mercurial/revsetlang.py
author Martin von Zweigbergk <martinvonz@google.com>
Sun, 09 Jul 2017 15:19:27 -0700
changeset 33378 adf95bfb423a
parent 33336 4672db164c98
child 33415 371f59c6a89e
permissions -rw-r--r--
match: make nevermatcher an exact matcher and a prefix matcher The m.isexact() and m.prefix() methods are used by callers to determine whether m.files() can be used for fast paths. It seems safe to let callers to any fast paths it can that rely on the empty m.files().
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
31024
0b8356705de6 revset: split language services to revsetlang module (API)
Yuya Nishihara <yuya@tcha.org>
parents: 31017
diff changeset
     1
# revsetlang.py - parser, tokenizer and utility for revision set language
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     2
#
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     3
# Copyright 2010 Matt Mackall <mpm@selenic.com>
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     4
#
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms of the
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     6
# GNU General Public License version 2 or any later version.
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     7
25971
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
     8
from __future__ import absolute_import
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
     9
30071
2def3d55b1b9 revset: build _syminitletters from a saner source: the string module
Augie Fackler <augie@google.com>
parents: 30056
diff changeset
    10
import string
25971
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    11
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    12
from .i18n import _
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    13
from . import (
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    14
    error,
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    15
    node,
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    16
    parser,
30071
2def3d55b1b9 revset: build _syminitletters from a saner source: the string module
Augie Fackler <augie@google.com>
parents: 30056
diff changeset
    17
    pycompat,
31606
0b3eb280564b revsetlang: perform quoting using ui.escapestr instead of repr()
Augie Fackler <augie@google.com>
parents: 31605
diff changeset
    18
    util,
25971
e9cd028f2dff revset: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25929
diff changeset
    19
)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    20
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    21
elements = {
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    22
    # token-type: binding-strength, primary, prefix, infix, suffix
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    23
    "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    24
    "##": (20, None, None, ("_concat", 20), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    25
    "~": (18, None, None, ("ancestor", 18), None),
29767
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
    26
    "^": (18, None, None, ("parent", 18), "parentpost"),
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    27
    "-": (5, None, ("negate", 19), ("minus", 5), None),
29767
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
    28
    "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
    29
    "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
    30
    ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    31
    "not": (10, None, ("not", 10), None, None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    32
    "!": (10, None, ("not", 10), None, None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    33
    "and": (5, None, None, ("and", 5), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    34
    "&": (5, None, None, ("and", 5), None),
29767
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
    35
    "%": (5, None, None, ("only", 5), "onlypost"),
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    36
    "or": (4, None, None, ("or", 4), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    37
    "|": (4, None, None, ("or", 4), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    38
    "+": (4, None, None, ("or", 4), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    39
    "=": (3, None, None, ("keyvalue", 3), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    40
    ",": (2, None, None, ("list", 2), None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    41
    ")": (0, None, None, None, None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    42
    "symbol": (0, "symbol", None, None, None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    43
    "string": (0, "string", None, None, None),
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    44
    "end": (0, None, None, None, None),
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    45
}
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    46
32291
bd872f64a8ba cleanup: use set literals
Martin von Zweigbergk <martinvonz@google.com>
parents: 31997
diff changeset
    47
keywords = {'and', 'or', 'not'}
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    48
32291
bd872f64a8ba cleanup: use set literals
Martin von Zweigbergk <martinvonz@google.com>
parents: 31997
diff changeset
    49
_quoteletters = {'"', "'"}
31384
fac5cd3b8673 py3: make set of revset operators and quotes in bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31383
diff changeset
    50
_simpleopletters = set(pycompat.iterbytestr("():=,-|&+!~^%"))
fac5cd3b8673 py3: make set of revset operators and quotes in bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31383
diff changeset
    51
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    52
# default set of valid characters for the initial letter of symbols
31383
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    53
_syminitletters = set(pycompat.iterbytestr(
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    54
    string.ascii_letters.encode('ascii') +
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    55
    string.digits.encode('ascii') +
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    56
    '._@')) | set(map(pycompat.bytechr, xrange(128, 256)))
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    57
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    58
# default set of valid characters for non-initial letters of symbols
31383
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    59
_symletters = _syminitletters | set(pycompat.iterbytestr('-/'))
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    60
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    61
def tokenize(program, lookup=None, syminitletters=None, symletters=None):
17886
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    62
    '''
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    63
    Parse a revset statement into a stream of tokens
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    64
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    65
    ``syminitletters`` is the set of valid characters for the initial
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    66
    letter of symbols.
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    67
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    68
    By default, character ``c`` is recognized as valid for initial
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    69
    letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    70
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    71
    ``symletters`` is the set of valid characters for non-initial
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    72
    letters of symbols.
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    73
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    74
    By default, character ``c`` is recognized as valid for non-initial
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    75
    letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    76
17886
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    77
    Check that @ is a valid unquoted token character (issue3686):
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    78
    >>> list(tokenize("@::"))
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    79
    [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    80
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    81
    '''
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
    82
    program = pycompat.bytestr(program)
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    83
    if syminitletters is None:
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    84
        syminitletters = _syminitletters
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    85
    if symletters is None:
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    86
        symletters = _symletters
17886
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    87
25902
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    88
    if program and lookup:
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    89
        # attempt to parse old-style ranges first to deal with
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    90
        # things like old-tag which contain query metacharacters
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    91
        parts = program.split(':', 1)
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    92
        if all(lookup(sym) for sym in parts if sym):
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    93
            if parts[0]:
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    94
                yield ('symbol', parts[0], 0)
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    95
            if len(parts) > 1:
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    96
                s = len(parts[0])
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    97
                yield (':', None, s)
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    98
                if parts[1]:
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    99
                    yield ('symbol', parts[1], s + 1)
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   100
            yield ('end', None, len(program))
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   101
            return
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   102
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   103
    pos, l = 0, len(program)
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   104
    while pos < l:
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   105
        c = program[pos]
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   106
        if c.isspace(): # skip inter-token whitespace
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   107
            pass
11278
7df88cdf47fd revset: add support for prefix and suffix versions of : and ::
Matt Mackall <mpm@selenic.com>
parents: 11275
diff changeset
   108
        elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   109
            yield ('::', None, pos)
11278
7df88cdf47fd revset: add support for prefix and suffix versions of : and ::
Matt Mackall <mpm@selenic.com>
parents: 11275
diff changeset
   110
            pos += 1 # skip ahead
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   111
        elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   112
            yield ('..', None, pos)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   113
            pos += 1 # skip ahead
23742
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   114
        elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   115
            yield ('##', None, pos)
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   116
            pos += 1 # skip ahead
31384
fac5cd3b8673 py3: make set of revset operators and quotes in bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31383
diff changeset
   117
        elif c in _simpleopletters: # handle simple operators
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   118
            yield (c, None, pos)
31384
fac5cd3b8673 py3: make set of revset operators and quotes in bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31383
diff changeset
   119
        elif (c in _quoteletters or c == 'r' and
12408
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   120
              program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   121
            if c == 'r':
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   122
                pos += 1
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   123
                c = program[pos]
12408
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   124
                decode = lambda x: x
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   125
            else:
26232
43f9976346e9 revset: handle error of string unescaping
Yuya Nishihara <yuya@tcha.org>
parents: 26212
diff changeset
   126
                decode = parser.unescapestr
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   127
            pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   128
            s = pos
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   129
            while pos < l: # find closing quote
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   130
                d = program[pos]
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   131
                if d == '\\': # skip over escaped characters
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   132
                    pos += 2
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   133
                    continue
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   134
                if d == c:
12408
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   135
                    yield ('string', decode(program[s:pos]), s)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   136
                    break
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   137
                pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   138
            else:
11383
de544774ebea revset: all your error messages are belong to _
Martin Geisler <mg@lazybytes.net>
parents: 11349
diff changeset
   139
                raise error.ParseError(_("unterminated string"), s)
16683
525fdb738975 cleanup: eradicate long lines
Brodie Rao <brodie@sf.io>
parents: 16661
diff changeset
   140
        # gather up a symbol/keyword
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
   141
        elif c in syminitletters:
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   142
            s = pos
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   143
            pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   144
            while pos < l: # find end of symbol
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   145
                d = program[pos]
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
   146
                if d not in symletters:
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   147
                    break
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   148
                if d == '.' and program[pos - 1] == '.': # special case for ..
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   149
                    pos -= 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   150
                    break
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   151
                pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   152
            sym = program[s:pos]
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   153
            if sym in keywords: # operator keywords
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   154
                yield (sym, None, s)
20780
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   155
            elif '-' in sym:
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   156
                # some jerk gave us foo-bar-baz, try to check if it's a symbol
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   157
                if lookup and lookup(sym):
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   158
                    # looks like a real symbol
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   159
                    yield ('symbol', sym, s)
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   160
                else:
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   161
                    # looks like an expression
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   162
                    parts = sym.split('-')
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   163
                    for p in parts[:-1]:
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   164
                        if p: # possible consecutive -
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   165
                            yield ('symbol', p, s)
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   166
                        s += len(p)
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   167
                        yield ('-', None, pos)
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   168
                        s += 1
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   169
                    if parts[-1]: # possible trailing -
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   170
                        yield ('symbol', parts[-1], s)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   171
            else:
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   172
                yield ('symbol', sym, s)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   173
            pos -= 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   174
        else:
24708
fb47816e1a9c revsets: more informative syntax error message
Ryan McElroy <rmcelroy@fb.com>
parents: 24518
diff changeset
   175
            raise error.ParseError(_("syntax error in revset '%s'") %
fb47816e1a9c revsets: more informative syntax error message
Ryan McElroy <rmcelroy@fb.com>
parents: 24518
diff changeset
   176
                                   program, pos)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   177
        pos += 1
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   178
    yield ('end', None, pos)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   179
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   180
# helpers
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   181
30802
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   182
_notset = object()
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   183
29441
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   184
def getsymbol(x):
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   185
    if x and x[0] == 'symbol':
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   186
        return x[1]
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   187
    raise error.ParseError(_('not a symbol'))
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   188
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   189
def getstring(x, err):
11406
42408cd43f55 revset: fix up contains/getstring when no args passed
Matt Mackall <mpm@selenic.com>
parents: 11404
diff changeset
   190
    if x and (x[0] == 'string' or x[0] == 'symbol'):
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   191
        return x[1]
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   192
    raise error.ParseError(err)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   193
30802
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   194
def getinteger(x, err, default=_notset):
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   195
    if not x and default is not _notset:
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   196
        return default
30801
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   197
    try:
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   198
        return int(getstring(x, err))
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   199
    except ValueError:
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   200
        raise error.ParseError(err)
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   201
31997
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   202
def getboolean(x, err):
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   203
    value = util.parsebool(getsymbol(x))
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   204
    if value is not None:
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   205
        return value
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   206
    raise error.ParseError(err)
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   207
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   208
def getlist(x):
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   209
    if not x:
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   210
        return []
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   211
    if x[0] == 'list':
27987
b19d8d5d6b51 revset: flatten chained 'list' operations (aka function args) (issue5072)
Yuya Nishihara <yuya@tcha.org>
parents: 27945
diff changeset
   212
        return list(x[1:])
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   213
    return [x]
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   214
30804
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   215
def getrange(x, err):
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   216
    if not x:
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   217
        raise error.ParseError(err)
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   218
    op = x[0]
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   219
    if op == 'range':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   220
        return x[1], x[2]
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   221
    elif op == 'rangepre':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   222
        return None, x[1]
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   223
    elif op == 'rangepost':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   224
        return x[1], None
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   225
    elif op == 'rangeall':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   226
        return None, None
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   227
    raise error.ParseError(err)
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   228
11339
744d5b73f776 revset: improve filter argument handling
Matt Mackall <mpm@selenic.com>
parents: 11304
diff changeset
   229
def getargs(x, min, max, err):
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   230
    l = getlist(x)
16161
5a627b49b4d9 graphlog: paths/-I/-X handling requires a new revset
Patrick Mezard <patrick@mezard.eu>
parents: 16096
diff changeset
   231
    if len(l) < min or (max >= 0 and len(l) > max):
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   232
        raise error.ParseError(err)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   233
    return l
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   234
25767
026105c442d7 revset: rename getkwargs() to getargsdict()
Yuya Nishihara <yuya@tcha.org>
parents: 25766
diff changeset
   235
def getargsdict(x, funcname, keys):
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30719
diff changeset
   236
    return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys),
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25704
diff changeset
   237
                                keyvaluenode='keyvalue', keynode='symbol')
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25704
diff changeset
   238
32912
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   239
def _isnamedfunc(x, funcname):
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   240
    """Check if given tree matches named function"""
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   241
    return x and x[0] == 'func' and getsymbol(x[1]) == funcname
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   242
32913
3292c0df64f7 revsetlang: check arguments passed to ancestors() before optimizing to only()
Yuya Nishihara <yuya@tcha.org>
parents: 32912
diff changeset
   243
def _isposargs(x, n):
3292c0df64f7 revsetlang: check arguments passed to ancestors() before optimizing to only()
Yuya Nishihara <yuya@tcha.org>
parents: 32912
diff changeset
   244
    """Check if given tree is n-length list of positional arguments"""
3292c0df64f7 revsetlang: check arguments passed to ancestors() before optimizing to only()
Yuya Nishihara <yuya@tcha.org>
parents: 32912
diff changeset
   245
    l = getlist(x)
3292c0df64f7 revsetlang: check arguments passed to ancestors() before optimizing to only()
Yuya Nishihara <yuya@tcha.org>
parents: 32912
diff changeset
   246
    return len(l) == n and all(y and y[0] != 'keyvalue' for y in l)
3292c0df64f7 revsetlang: check arguments passed to ancestors() before optimizing to only()
Yuya Nishihara <yuya@tcha.org>
parents: 32912
diff changeset
   247
32912
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   248
def _matchnamedfunc(x, funcname):
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   249
    """Return args tree if given tree matches named function; otherwise None
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   250
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   251
    This can't be used for testing a nullary function since its args tree
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   252
    is also None. Use _isnamedfunc() instead.
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   253
    """
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   254
    if not _isnamedfunc(x, funcname):
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   255
        return
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   256
    return x[2]
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   257
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   258
# Constants for ordering requirement, used in _analyze():
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   259
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   260
# If 'define', any nested functions and operations can change the ordering of
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   261
# the entries in the set. If 'follow', any nested functions and operations
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   262
# should take the ordering specified by the first operand to the '&' operator.
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   263
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   264
# For instance,
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   265
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   266
#   X & (Y | Z)
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   267
#   ^   ^^^^^^^
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   268
#   |   follow
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   269
#   define
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   270
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   271
# will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   272
# of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   273
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   274
# 'any' means the order doesn't matter. For instance,
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   275
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   276
#   X & !Y
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   277
#        ^
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   278
#        any
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   279
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   280
# 'y()' can either enforce its ordering requirement or take the ordering
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   281
# specified by 'x()' because 'not()' doesn't care the order.
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   282
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   283
# Transition of ordering requirement:
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   284
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   285
# 1. starts with 'define'
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   286
# 2. shifts to 'follow' by 'x & y'
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   287
# 3. changes back to 'define' on function call 'f(x)' or function-like
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   288
#    operation 'x (f) y' because 'f' may have its own ordering requirement
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   289
#    for 'x' and 'y' (e.g. 'first(x)')
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   290
#
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   291
anyorder = 'any'        # don't care the order
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   292
defineorder = 'define'  # should define the order
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   293
followorder = 'follow'  # must follow the current order
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   294
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   295
# transition table for 'x & y', from the current expression 'x' to 'y'
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   296
_tofolloworder = {
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   297
    anyorder: anyorder,
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   298
    defineorder: followorder,
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   299
    followorder: followorder,
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   300
}
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   301
29117
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   302
def _matchonly(revs, bases):
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   303
    """
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   304
    >>> f = lambda *args: _matchonly(*map(parse, args))
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   305
    >>> f('ancestors(A)', 'not ancestors(B)')
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   306
    ('list', ('symbol', 'A'), ('symbol', 'B'))
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   307
    """
32912
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   308
    ta = _matchnamedfunc(revs, 'ancestors')
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   309
    tb = bases and bases[0] == 'not' and _matchnamedfunc(bases[1], 'ancestors')
32913
3292c0df64f7 revsetlang: check arguments passed to ancestors() before optimizing to only()
Yuya Nishihara <yuya@tcha.org>
parents: 32912
diff changeset
   310
    if _isposargs(ta, 1) and _isposargs(tb, 1):
32912
c808507cfbf0 revsetlang: factor out helper to match ancestors() in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 32291
diff changeset
   311
        return ('list', ta, tb)
29116
0c9b05dae010 revset: unnest isonly() closure from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29098
diff changeset
   312
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   313
def _fixops(x):
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   314
    """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   315
    handled well by our simple top-down parser"""
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   316
    if not isinstance(x, tuple):
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   317
        return x
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   318
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   319
    op = x[0]
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   320
    if op == 'parent':
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   321
        # x^:y means (x^) : y, not x ^ (:y)
29770
9c51a5de76db revset: also parse x^: as (x^):
Yuya Nishihara <yuya@tcha.org>
parents: 29769
diff changeset
   322
        # x^:  means (x^) :,   not x ^ (:)
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   323
        post = ('parentpost', x[1])
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   324
        if x[2][0] == 'dagrangepre':
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   325
            return _fixops(('dagrange', post, x[2][1]))
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   326
        elif x[2][0] == 'rangepre':
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   327
            return _fixops(('range', post, x[2][1]))
29770
9c51a5de76db revset: also parse x^: as (x^):
Yuya Nishihara <yuya@tcha.org>
parents: 29769
diff changeset
   328
        elif x[2][0] == 'rangeall':
9c51a5de76db revset: also parse x^: as (x^):
Yuya Nishihara <yuya@tcha.org>
parents: 29769
diff changeset
   329
            return _fixops(('rangepost', post))
29929
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   330
    elif op == 'or':
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   331
        # make number of arguments deterministic:
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   332
        # x + y + z -> (or x y z) -> (or (list x y z))
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   333
        return (op, _fixops(('list',) + x[1:]))
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   334
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   335
    return (op,) + tuple(_fixops(y) for y in x[1:])
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   336
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   337
def _analyze(x, order):
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   338
    if x is None:
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   339
        return x
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   340
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   341
    op = x[0]
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   342
    if op == 'minus':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   343
        return _analyze(('and', x[1], ('not', x[2])), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   344
    elif op == 'only':
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   345
        t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   346
        return _analyze(t, order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   347
    elif op == 'onlypost':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   348
        return _analyze(('func', ('symbol', 'only'), x[1]), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   349
    elif op == 'dagrangepre':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   350
        return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   351
    elif op == 'dagrangepost':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   352
        return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   353
    elif op == 'negate':
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   354
        s = getstring(x[1], _("can't negate that"))
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   355
        return _analyze(('string', '-' + s), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   356
    elif op in ('string', 'symbol'):
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   357
        return x
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   358
    elif op == 'and':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   359
        ta = _analyze(x[1], order)
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   360
        tb = _analyze(x[2], _tofolloworder[order])
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   361
        return (op, ta, tb, order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   362
    elif op == 'or':
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   363
        return (op, _analyze(x[1], order), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   364
    elif op == 'not':
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   365
        return (op, _analyze(x[1], anyorder), order)
30803
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   366
    elif op == 'rangeall':
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   367
        return (op, None, order)
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   368
    elif op in ('rangepre', 'rangepost', 'parentpost'):
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   369
        return (op, _analyze(x[1], defineorder), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   370
    elif op == 'group':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   371
        return _analyze(x[1], order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   372
    elif op in ('dagrange', 'range', 'parent', 'ancestor'):
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   373
        ta = _analyze(x[1], defineorder)
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   374
        tb = _analyze(x[2], defineorder)
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   375
        return (op, ta, tb, order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   376
    elif op == 'list':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   377
        return (op,) + tuple(_analyze(y, order) for y in x[1:])
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   378
    elif op == 'keyvalue':
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   379
        return (op, x[1], _analyze(x[2], order))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   380
    elif op == 'func':
29943
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   381
        f = getsymbol(x[1])
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   382
        d = defineorder
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   383
        if f == 'present':
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   384
            # 'present(set)' is known to return the argument set with no
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   385
            # modification, so forward the current order to its argument
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   386
            d = order
80c86b9bb40b revset: forward ordering requirement to argument of present()
Yuya Nishihara <yuya@tcha.org>
parents: 29935
diff changeset
   387
        return (op, x[1], _analyze(x[2], d), order)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   388
    raise ValueError('invalid operator %r' % op)
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   389
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   390
def analyze(x, order=defineorder):
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   391
    """Transform raw parsed tree to evaluatable tree which can be fed to
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   392
    optimize() or getset()
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   393
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   394
    All pseudo operations should be mapped to real operations or functions
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   395
    defined in methods or symbols table respectively.
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   396
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   397
    'order' specifies how the current expression 'x' is ordered (see the
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   398
    constants defined above.)
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   399
    """
29930
90455e7bf543 revset: infer ordering flag to teach if operation should define/follow order
Yuya Nishihara <yuya@tcha.org>
parents: 29929
diff changeset
   400
    return _analyze(x, order)
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   401
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   402
def _optimize(x, small):
13031
3da456d0c885 code style: prefer 'is' and 'is not' tests with singletons
Martin Geisler <mg@aragost.com>
parents: 12936
diff changeset
   403
    if x is None:
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   404
        return 0, x
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   405
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   406
    smallbonus = 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   407
    if small:
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   408
        smallbonus = .5
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   409
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   410
    op = x[0]
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   411
    if op in ('string', 'symbol'):
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   412
        return smallbonus, x # single revisions are small
16859
eeb464ed7275 revset: drop unreachable code
Bryan O'Sullivan <bryano@fb.com>
parents: 16838
diff changeset
   413
    elif op == 'and':
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   414
        wa, ta = _optimize(x[1], True)
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   415
        wb, tb = _optimize(x[2], True)
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   416
        order = x[3]
29116
0c9b05dae010 revset: unnest isonly() closure from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29098
diff changeset
   417
        w = min(wa, wb)
20499
2efd608473fb revset: optimize missing ancestor expressions
Siddharth Agarwal <sid0@fb.com>
parents: 20498
diff changeset
   418
2efd608473fb revset: optimize missing ancestor expressions
Siddharth Agarwal <sid0@fb.com>
parents: 20498
diff changeset
   419
        # (::x and not ::y)/(not ::y and ::x) have a fast path
29117
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   420
        tm = _matchonly(ta, tb) or _matchonly(tb, ta)
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   421
        if tm:
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   422
            return w, ('func', ('symbol', 'only'), tm, order)
20499
2efd608473fb revset: optimize missing ancestor expressions
Siddharth Agarwal <sid0@fb.com>
parents: 20498
diff changeset
   423
28217
d2ac8b57a75d revset: use smartset minus operator
Durham Goode <durham@fb.com>
parents: 28139
diff changeset
   424
        if tb is not None and tb[0] == 'not':
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   425
            return wa, ('difference', ta, tb[1], order)
28217
d2ac8b57a75d revset: use smartset minus operator
Durham Goode <durham@fb.com>
parents: 28139
diff changeset
   426
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   427
        if wa > wb:
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   428
            return w, (op, tb, ta, order)
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   429
        return w, (op, ta, tb, order)
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   430
    elif op == 'or':
25343
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   431
        # fast path for machine-generated expression, that is likely to have
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   432
        # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   433
        order = x[2]
25343
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   434
        ws, ts, ss = [], [], []
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   435
        def flushss():
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   436
            if not ss:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   437
                return
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   438
            if len(ss) == 1:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   439
                w, t = ss[0]
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   440
            else:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   441
                s = '\0'.join(t[1] for w, t in ss)
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   442
                y = ('func', ('symbol', '_list'), ('string', s), order)
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   443
                w, t = _optimize(y, False)
25343
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   444
            ws.append(w)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   445
            ts.append(t)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   446
            del ss[:]
29929
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   447
        for y in getlist(x[1]):
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   448
            w, t = _optimize(y, False)
25996
b12e00a05d57 revset: prevent crash caused by empty group expression while optimizing "or"
Yuya Nishihara <yuya@tcha.org>
parents: 25995
diff changeset
   449
            if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
25343
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   450
                ss.append((w, t))
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   451
                continue
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   452
            flushss()
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   453
            ws.append(w)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   454
            ts.append(t)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   455
        flushss()
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   456
        if len(ts) == 1:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   457
            return ws[0], ts[0] # 'or' operation is fully optimized out
31800
c63cb2d10d6d revsetlang: enable optimization of 'x + y' expression
Yuya Nishihara <yuya@tcha.org>
parents: 31636
diff changeset
   458
        if order != defineorder:
c63cb2d10d6d revsetlang: enable optimization of 'x + y' expression
Yuya Nishihara <yuya@tcha.org>
parents: 31636
diff changeset
   459
            # reorder by weight only when f(a + b) == f(b + a)
c63cb2d10d6d revsetlang: enable optimization of 'x + y' expression
Yuya Nishihara <yuya@tcha.org>
parents: 31636
diff changeset
   460
            ts = [wt[1] for wt in sorted(zip(ws, ts), key=lambda wt: wt[0])]
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   461
        return max(ws), (op, ('list',) + tuple(ts), order)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   462
    elif op == 'not':
25191
08d1ef09ed37 revset: optimize not public revset
Laurent Charignon <lcharignon@fb.com>
parents: 25149
diff changeset
   463
        # Optimize not public() to _notpublic() because we have a fast version
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   464
        if x[1][:3] == ('func', ('symbol', 'public'), None):
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   465
            order = x[1][3]
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   466
            newsym = ('func', ('symbol', '_notpublic'), None, order)
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   467
            o = _optimize(newsym, not small)
25191
08d1ef09ed37 revset: optimize not public revset
Laurent Charignon <lcharignon@fb.com>
parents: 25149
diff changeset
   468
            return o[0], o[1]
08d1ef09ed37 revset: optimize not public revset
Laurent Charignon <lcharignon@fb.com>
parents: 25149
diff changeset
   469
        else:
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   470
            o = _optimize(x[1], not small)
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   471
            order = x[2]
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   472
            return o[0], (op, o[1], order)
30803
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   473
    elif op == 'rangeall':
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   474
        return smallbonus, x
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   475
    elif op in ('rangepre', 'rangepost', 'parentpost'):
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   476
        o = _optimize(x[1], small)
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   477
        order = x[2]
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   478
        return o[0], (op, o[1], order)
29898
7203400f91b0 revset: do not partial-match operator and function names in optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29897
diff changeset
   479
    elif op in ('dagrange', 'range', 'parent', 'ancestor'):
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   480
        wa, ta = _optimize(x[1], small)
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   481
        wb, tb = _optimize(x[2], small)
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   482
        order = x[3]
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   483
        return wa + wb, (op, ta, tb, order)
27987
b19d8d5d6b51 revset: flatten chained 'list' operations (aka function args) (issue5072)
Yuya Nishihara <yuya@tcha.org>
parents: 27945
diff changeset
   484
    elif op == 'list':
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   485
        ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
27987
b19d8d5d6b51 revset: flatten chained 'list' operations (aka function args) (issue5072)
Yuya Nishihara <yuya@tcha.org>
parents: 27945
diff changeset
   486
        return sum(ws), (op,) + ts
29766
5004ef47f437 revset: fix keyword arguments to go through optimization process
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
   487
    elif op == 'keyvalue':
5004ef47f437 revset: fix keyword arguments to go through optimization process
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
   488
        w, t = _optimize(x[2], small)
5004ef47f437 revset: fix keyword arguments to go through optimization process
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
   489
        return w, (op, x[1], t)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   490
    elif op == 'func':
29441
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   491
        f = getsymbol(x[1])
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   492
        wa, ta = _optimize(x[2], small)
29898
7203400f91b0 revset: do not partial-match operator and function names in optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29897
diff changeset
   493
        if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
30205
b4074417b661 revset: optimize for destination() being "inefficient"
Mads Kiilerich <madski@unity3d.com>
parents: 30179
diff changeset
   494
                 'keyword', 'outgoing', 'user', 'destination'):
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   495
            w = 10 # slow
29898
7203400f91b0 revset: do not partial-match operator and function names in optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29897
diff changeset
   496
        elif f in ('modifies', 'adds', 'removes'):
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   497
            w = 30 # slower
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   498
        elif f == "contains":
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   499
            w = 100 # very slow
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   500
        elif f == "ancestor":
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   501
            w = 1 * smallbonus
30700
323f0c4b43f4 revset: categorize wdir() as very fast function
Yuya Nishihara <yuya@tcha.org>
parents: 30699
diff changeset
   502
        elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'):
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   503
            w = 0
29898
7203400f91b0 revset: do not partial-match operator and function names in optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29897
diff changeset
   504
        elif f == "sort":
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   505
            w = 10 # assume most sorts look at changelog
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   506
        else:
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   507
            w = 1
29932
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   508
        order = x[3]
09a84e747c88 revset: pass around ordering flags to operations
Yuya Nishihara <yuya@tcha.org>
parents: 29931
diff changeset
   509
        return w + wa, (op, x[1], ta, order)
29896
104914b03b83 revset: make optimize() reject unknown operators
Yuya Nishihara <yuya@tcha.org>
parents: 29814
diff changeset
   510
    raise ValueError('invalid operator %r' % op)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   511
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   512
def optimize(tree):
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   513
    """Optimize evaluatable tree
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   514
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   515
    All pseudo operations should be transformed beforehand.
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   516
    """
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   517
    _weight, newtree = _optimize(tree, small=True)
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   518
    return newtree
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   519
23845
0a7fd54d4e60 revset: introduce "_parsealiasdecl" to parse alias declarations strictly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23844
diff changeset
   520
# the set of valid characters for the initial letter of symbols in
0a7fd54d4e60 revset: introduce "_parsealiasdecl" to parse alias declarations strictly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23844
diff changeset
   521
# alias declarations and definitions
30071
2def3d55b1b9 revset: build _syminitletters from a saner source: the string module
Augie Fackler <augie@google.com>
parents: 30056
diff changeset
   522
_aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
23845
0a7fd54d4e60 revset: introduce "_parsealiasdecl" to parse alias declarations strictly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23844
diff changeset
   523
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   524
def _parsewith(spec, lookup=None, syminitletters=None):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   525
    """Generate a parse tree of given spec with given tokenizing options
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   526
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   527
    >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   528
    ('func', ('symbol', 'foo'), ('symbol', '$1'))
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   529
    >>> _parsewith('$1')
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   530
    Traceback (most recent call last):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   531
      ...
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   532
    ParseError: ("syntax error in revset '$1'", 0)
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   533
    >>> _parsewith('foo bar')
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   534
    Traceback (most recent call last):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   535
      ...
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   536
    ParseError: ('invalid token', 4)
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   537
    """
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   538
    p = parser.parser(elements)
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   539
    tree, pos = p.parse(tokenize(spec, lookup=lookup,
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   540
                                 syminitletters=syminitletters))
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   541
    if pos != len(spec):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   542
        raise error.ParseError(_('invalid token'), pos)
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   543
    return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   544
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28869
diff changeset
   545
class _aliasrules(parser.basealiasrules):
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28869
diff changeset
   546
    """Parsing and expansion rule set of revset aliases"""
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28869
diff changeset
   547
    _section = _('revset alias')
29074
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   548
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   549
    @staticmethod
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   550
    def _parse(spec):
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   551
        """Parse alias declaration/definition ``spec``
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   552
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   553
        This allows symbol names to use also ``$`` as an initial letter
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   554
        (for backward compatibility), and callers of this function should
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   555
        examine whether ``$`` is used also for unexpected symbols or not.
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   556
        """
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   557
        return _parsewith(spec, syminitletters=_aliassyminitletters)
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   558
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   559
    @staticmethod
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   560
    def _trygetfunc(tree):
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   561
        if tree[0] == 'func' and tree[1][0] == 'symbol':
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   562
            return tree[1][1], getlist(tree[2])
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28869
diff changeset
   563
33336
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   564
def expandaliases(tree, aliases, warn=None):
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   565
    """Expand aliases in a tree, aliases is a list of (name, value) tuples"""
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   566
    aliases = _aliasrules.buildmap(aliases)
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28894
diff changeset
   567
    tree = _aliasrules.expand(aliases, tree)
29922
ae933e3e2226 revset: remove showwarning option from expandaliases()
Yuya Nishihara <yuya@tcha.org>
parents: 29906
diff changeset
   568
    # warn about problematic (but not referred) aliases
33336
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   569
    if warn is not None:
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   570
        for name, alias in sorted(aliases.iteritems()):
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   571
            if alias.error and not alias.warned:
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   572
                warn(_('warning: %s\n') % (alias.error))
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   573
                alias.warned = True
23725
6a81f88758aa revset: delay showing parse error for the revset alias until it is referred
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23720
diff changeset
   574
    return tree
14098
9f5a0acb0056 revset aliases
Alexander Solovyov <alexander@solovyov.net>
parents: 14073
diff changeset
   575
23742
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   576
def foldconcat(tree):
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   577
    """Fold elements to be concatenated by `##`
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   578
    """
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   579
    if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   580
        return tree
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   581
    if tree[0] == '_concat':
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   582
        pending = [tree]
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   583
        l = []
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   584
        while pending:
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   585
            e = pending.pop()
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   586
            if e[0] == '_concat':
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   587
                pending.extend(reversed(e[1:]))
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   588
            elif e[0] in ('string', 'symbol'):
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   589
                l.append(e[1])
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   590
            else:
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   591
                msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   592
                raise error.ParseError(msg)
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   593
        return ('string', ''.join(l))
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   594
    else:
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   595
        return tuple(foldconcat(t) for t in tree)
14098
9f5a0acb0056 revset aliases
Alexander Solovyov <alexander@solovyov.net>
parents: 14073
diff changeset
   596
20779
ffc2295c6b80 revset: pass a lookup function to the tokenizer
Matt Mackall <mpm@selenic.com>
parents: 20754
diff changeset
   597
def parse(spec, lookup=None):
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   598
    return _parsewith(spec, lookup=lookup)
20779
ffc2295c6b80 revset: pass a lookup function to the tokenizer
Matt Mackall <mpm@selenic.com>
parents: 20754
diff changeset
   599
31604
7eac6fcf2ffa revsetlang: move quoting function to not be a closure
Augie Fackler <augie@google.com>
parents: 31603
diff changeset
   600
def _quote(s):
31605
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   601
    r"""Quote a value in order to make it safe for the revset engine.
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   602
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   603
    >>> _quote('asdf')
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   604
    "'asdf'"
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   605
    >>> _quote("asdf'\"")
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   606
    '\'asdf\\\'"\''
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   607
    >>> _quote('asdf\'')
31606
0b3eb280564b revsetlang: perform quoting using ui.escapestr instead of repr()
Augie Fackler <augie@google.com>
parents: 31605
diff changeset
   608
    "'asdf\\''"
31605
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   609
    >>> _quote(1)
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   610
    "'1'"
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   611
    """
31636
f3b151278655 revsetlang: fix _quote on int on python3
Augie Fackler <raf@durin42.com>
parents: 31606
diff changeset
   612
    return "'%s'" % util.escapestr(pycompat.bytestr(s))
31604
7eac6fcf2ffa revsetlang: move quoting function to not be a closure
Augie Fackler <augie@google.com>
parents: 31603
diff changeset
   613
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   614
def formatspec(expr, *args):
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   615
    '''
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   616
    This is a convenience function for using revsets internally, and
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   617
    escapes arguments appropriately. Aliases are intentionally ignored
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   618
    so that intended expression behavior isn't accidentally subverted.
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   619
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   620
    Supported arguments:
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   621
15266
8bea39ca9acb revset: add %r for embedded revset support to formatspec
Matt Mackall <mpm@selenic.com>
parents: 15153
diff changeset
   622
    %r = revset expression, parenthesized
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   623
    %d = int(arg), no quoting
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   624
    %s = string(arg), escaped and single-quoted
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   625
    %b = arg.branch(), escaped and single-quoted
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   626
    %n = hex(arg), single-quoted
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   627
    %% = a literal '%'
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   628
15266
8bea39ca9acb revset: add %r for embedded revset support to formatspec
Matt Mackall <mpm@selenic.com>
parents: 15153
diff changeset
   629
    Prefixing the type with 'l' specifies a parenthesized list of that type.
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   630
15268
bd5103819c2e revset: fix %r handling in formatspec
Matt Mackall <mpm@selenic.com>
parents: 15266
diff changeset
   631
    >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
bd5103819c2e revset: fix %r handling in formatspec
Matt Mackall <mpm@selenic.com>
parents: 15266
diff changeset
   632
    '(10 or 11):: and ((this()) or (that()))'
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   633
    >>> formatspec('%d:: and not %d::', 10, 20)
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   634
    '10:: and not 20::'
15325
cdf1daa3b83f revset: deal with empty lists in formatspec
Matt Mackall <mpm@selenic.com>
parents: 15268
diff changeset
   635
    >>> formatspec('%ld or %ld', [], [1])
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   636
    "_list('') or 1"
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   637
    >>> formatspec('keyword(%s)', 'foo\\xe9')
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   638
    "keyword('foo\\\\xe9')"
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   639
    >>> b = lambda: 'default'
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   640
    >>> b.branch = b
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   641
    >>> formatspec('branch(%b)', b)
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   642
    "branch('default')"
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   643
    >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   644
    "root(_list('a\\x00b\\x00c\\x00d'))"
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   645
    '''
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   646
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   647
    def argtype(c, arg):
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   648
        if c == 'd':
31603
fbe8502c4480 revsetlang: portably bytestring-ify another pair of int() calls
Augie Fackler <augie@google.com>
parents: 31565
diff changeset
   649
            return '%d' % int(arg)
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   650
        elif c == 's':
31604
7eac6fcf2ffa revsetlang: move quoting function to not be a closure
Augie Fackler <augie@google.com>
parents: 31603
diff changeset
   651
            return _quote(arg)
15266
8bea39ca9acb revset: add %r for embedded revset support to formatspec
Matt Mackall <mpm@selenic.com>
parents: 15153
diff changeset
   652
        elif c == 'r':
8bea39ca9acb revset: add %r for embedded revset support to formatspec
Matt Mackall <mpm@selenic.com>
parents: 15153
diff changeset
   653
            parse(arg) # make sure syntax errors are confined
8bea39ca9acb revset: add %r for embedded revset support to formatspec
Matt Mackall <mpm@selenic.com>
parents: 15153
diff changeset
   654
            return '(%s)' % arg
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   655
        elif c == 'n':
31604
7eac6fcf2ffa revsetlang: move quoting function to not be a closure
Augie Fackler <augie@google.com>
parents: 31603
diff changeset
   656
            return _quote(node.hex(arg))
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   657
        elif c == 'b':
31604
7eac6fcf2ffa revsetlang: move quoting function to not be a closure
Augie Fackler <augie@google.com>
parents: 31603
diff changeset
   658
            return _quote(arg.branch())
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   659
15595
a585d78e7b2f revset: balance %l or-expressions (issue3129)
Matt Mackall <mpm@selenic.com>
parents: 15532
diff changeset
   660
    def listexp(s, t):
a585d78e7b2f revset: balance %l or-expressions (issue3129)
Matt Mackall <mpm@selenic.com>
parents: 15532
diff changeset
   661
        l = len(s)
a585d78e7b2f revset: balance %l or-expressions (issue3129)
Matt Mackall <mpm@selenic.com>
parents: 15532
diff changeset
   662
        if l == 0:
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   663
            return "_list('')"
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   664
        elif l == 1:
15595
a585d78e7b2f revset: balance %l or-expressions (issue3129)
Matt Mackall <mpm@selenic.com>
parents: 15532
diff changeset
   665
            return argtype(t, s[0])
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   666
        elif t == 'd':
31565
553ad16b274f revsetlang: portably turn int into bytestring
Augie Fackler <augie@google.com>
parents: 31441
diff changeset
   667
            return "_intlist('%s')" % "\0".join('%d' % int(a) for a in s)
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   668
        elif t == 's':
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   669
            return "_list('%s')" % "\0".join(s)
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   670
        elif t == 'n':
20569
0d4be103c734 revset: added _hexlist method to replace _list for %ln
Lucas Moscovicz <lmoscovicz@fb.com>
parents: 20566
diff changeset
   671
            return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   672
        elif t == 'b':
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   673
            return "_list('%s')" % "\0".join(a.branch() for a in s)
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   674
15791
a814f8fcc65a Use explicit integer division
Martin Geisler <mg@aragost.com>
parents: 15726
diff changeset
   675
        m = l // 2
15595
a585d78e7b2f revset: balance %l or-expressions (issue3129)
Matt Mackall <mpm@selenic.com>
parents: 15532
diff changeset
   676
        return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
a585d78e7b2f revset: balance %l or-expressions (issue3129)
Matt Mackall <mpm@selenic.com>
parents: 15532
diff changeset
   677
31440
f784ba187089 py3: use bytestr wrapper in revsetlang.formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 31385
diff changeset
   678
    expr = pycompat.bytestr(expr)
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   679
    ret = ''
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   680
    pos = 0
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   681
    arg = 0
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   682
    while pos < len(expr):
31440
f784ba187089 py3: use bytestr wrapper in revsetlang.formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 31385
diff changeset
   683
        c = expr[pos]
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   684
        if c == '%':
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   685
            pos += 1
31440
f784ba187089 py3: use bytestr wrapper in revsetlang.formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 31385
diff changeset
   686
            d = expr[pos]
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   687
            if d == '%':
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   688
                ret += d
15268
bd5103819c2e revset: fix %r handling in formatspec
Matt Mackall <mpm@selenic.com>
parents: 15266
diff changeset
   689
            elif d in 'dsnbr':
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   690
                ret += argtype(d, args[arg])
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   691
                arg += 1
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   692
            elif d == 'l':
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   693
                # a list of some type
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   694
                pos += 1
31440
f784ba187089 py3: use bytestr wrapper in revsetlang.formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 31385
diff changeset
   695
                d = expr[pos]
15596
2555f441a32f merge with stable
Matt Mackall <mpm@selenic.com>
parents: 15595
diff changeset
   696
                ret += listexp(list(args[arg]), d)
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   697
                arg += 1
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   698
            else:
29389
98e8313dcd9e i18n: translate abort messages
liscju <piotr.listkiewicz@gmail.com>
parents: 29365
diff changeset
   699
                raise error.Abort(_('unexpected revspec format character %s')
98e8313dcd9e i18n: translate abort messages
liscju <piotr.listkiewicz@gmail.com>
parents: 29365
diff changeset
   700
                                  % d)
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   701
        else:
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   702
            ret += c
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   703
        pos += 1
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   704
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   705
    return ret
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   706
16218
81a1a00f5738 debugrevspec: pretty print output
Patrick Mezard <patrick@mezard.eu>
parents: 16185
diff changeset
   707
def prettyformat(tree):
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25251
diff changeset
   708
    return parser.prettyformat(tree, ('string', 'symbol'))
16218
81a1a00f5738 debugrevspec: pretty print output
Patrick Mezard <patrick@mezard.eu>
parents: 16185
diff changeset
   709
19719
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   710
def depth(tree):
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   711
    if isinstance(tree, tuple):
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   712
        return max(map(depth, tree)) + 1
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   713
    else:
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   714
        return 0
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   715
19720
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   716
def funcsused(tree):
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   717
    if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   718
        return set()
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   719
    else:
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   720
        funcs = set()
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   721
        for s in tree[1:]:
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   722
            funcs |= funcsused(s)
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   723
        if tree[0] == 'func':
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   724
            funcs.add(tree[1][1])
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   725
        return funcs