mercurial/revsetlang.py
author Gregory Szorc <gregory.szorc@gmail.com>
Sat, 10 Mar 2018 14:00:40 -0800
changeset 36867 a755fd3b7146
parent 36691 1b179d151578
child 37084 f0b6fbea00cf
permissions -rw-r--r--
hgweb: expose URL scheme and REMOTE_* attributes These are consulted by the HTTP wire protocol handler by reading from the env dict. Let's expose them as attributes instead. With the wire protocol handler updates to use the new attributes, we no longer have any consumers of the legacy wsgirequest type in the wire protocol code (outside of a proxied call to the permissions checker). So, we remove most references to it. Differential Revision: https://phab.mercurial-scm.org/D2783
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),
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
    24
    "[": (21, None, None, ("subscript", 1, "]"), None),
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
    25
    "#": (21, None, None, ("relation", 21), None),
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    26
    "##": (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
    27
    "~": (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
    28
    "^": (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
    29
    "-": (5, None, ("negate", 19), ("minus", 5), None),
35542
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
    30
    "::": (17, "dagrangeall", ("dagrangepre", 17), ("dagrange", 17),
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
    31
           "dagrangepost"),
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
    32
    "..": (17, "dagrangeall", ("dagrangepre", 17), ("dagrange", 17),
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
    33
           "dagrangepost"),
29767
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
    34
    ":": (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
    35
    "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
    36
    "!": (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
    37
    "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
    38
    "&": (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
    39
    "%": (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
    40
    "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
    41
    "|": (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
    42
    "+": (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
    43
    "=": (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
    44
    ",": (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
    45
    ")": (0, None, None, None, None),
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
    46
    "]": (0, None, None, None, None),
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    47
    "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
    48
    "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
    49
    "end": (0, None, None, None, None),
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    50
}
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    51
32291
bd872f64a8ba cleanup: use set literals
Martin von Zweigbergk <martinvonz@google.com>
parents: 31997
diff changeset
    52
keywords = {'and', 'or', 'not'}
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    53
34273
b0790bebfcf8 revset: move weight information to predicate
Jun Wu <quark@fb.com>
parents: 34272
diff changeset
    54
symbols = {}
b0790bebfcf8 revset: move weight information to predicate
Jun Wu <quark@fb.com>
parents: 34272
diff changeset
    55
32291
bd872f64a8ba cleanup: use set literals
Martin von Zweigbergk <martinvonz@google.com>
parents: 31997
diff changeset
    56
_quoteletters = {'"', "'"}
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
    57
_simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%"))
31384
fac5cd3b8673 py3: make set of revset operators and quotes in bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31383
diff changeset
    58
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    59
# 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
    60
_syminitletters = set(pycompat.iterbytestr(
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    61
    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
    62
    string.digits.encode('ascii') +
7556fe09cc48 py3: convert set of revset initial symbols back to bytes
Yuya Nishihara <yuya@tcha.org>
parents: 31355
diff changeset
    63
    '._@')) | 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
    64
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    65
# 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
    66
_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
    67
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    68
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
    69
    '''
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    70
    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
    71
23842
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    72
    ``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
    73
    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
    74
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    75
    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
    76
    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
    77
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    78
    ``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
    79
    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
    80
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    81
    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
    82
    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
    83
17886
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    84
    Check that @ is a valid unquoted token character (issue3686):
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
    85
    >>> list(tokenize(b"@::"))
17886
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    86
    [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    87
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    88
    '''
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
    89
    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
    90
    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
    91
        syminitletters = _syminitletters
91dbb98b3513 revset: make tokenize extensible to parse alias declarations and definitions
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23836
diff changeset
    92
    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
    93
        symletters = _symletters
17886
d8905e2c1301 revset: accept @ in unquoted symbols (issue3686)
Matt Mackall <mpm@selenic.com>
parents: 17829
diff changeset
    94
25902
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
    95
    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
    96
        # 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
    97
        # 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
    98
        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
    99
        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
   100
            if parts[0]:
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   101
                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
   102
            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
   103
                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
   104
                yield (':', None, s)
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   105
                if parts[1]:
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   106
                    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
   107
            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
   108
            return
5214cbdc37e5 revset: port parsing rule of old-style ranges from scmutil.revrange()
Yuya Nishihara <yuya@tcha.org>
parents: 25819
diff changeset
   109
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   110
    pos, l = 0, len(program)
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   111
    while pos < l:
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   112
        c = program[pos]
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   113
        if c.isspace(): # skip inter-token whitespace
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   114
            pass
11278
7df88cdf47fd revset: add support for prefix and suffix versions of : and ::
Matt Mackall <mpm@selenic.com>
parents: 11275
diff changeset
   115
        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
   116
            yield ('::', None, pos)
11278
7df88cdf47fd revset: add support for prefix and suffix versions of : and ::
Matt Mackall <mpm@selenic.com>
parents: 11275
diff changeset
   117
            pos += 1 # skip ahead
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   118
        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
   119
            yield ('..', None, pos)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   120
            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
   121
        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
   122
            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
   123
            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
   124
        elif c in _simpleopletters: # handle simple operators
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   125
            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
   126
        elif (c in _quoteletters or c == 'r' and
12408
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   127
              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
   128
            if c == 'r':
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   129
                pos += 1
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   130
                c = program[pos]
12408
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   131
                decode = lambda x: x
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   132
            else:
26232
43f9976346e9 revset: handle error of string unescaping
Yuya Nishihara <yuya@tcha.org>
parents: 26212
diff changeset
   133
                decode = parser.unescapestr
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   134
            pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   135
            s = pos
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   136
            while pos < l: # find closing quote
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   137
                d = program[pos]
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   138
                if d == '\\': # skip over escaped characters
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   139
                    pos += 2
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   140
                    continue
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   141
                if d == c:
12408
78a97859b90d revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents: 12401
diff changeset
   142
                    yield ('string', decode(program[s:pos]), s)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   143
                    break
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   144
                pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   145
            else:
11383
de544774ebea revset: all your error messages are belong to _
Martin Geisler <mg@lazybytes.net>
parents: 11349
diff changeset
   146
                raise error.ParseError(_("unterminated string"), s)
16683
525fdb738975 cleanup: eradicate long lines
Brodie Rao <brodie@sf.io>
parents: 16661
diff changeset
   147
        # 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
   148
        elif c in syminitletters:
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   149
            s = pos
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   150
            pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   151
            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
   152
                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
   153
                if d not in symletters:
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   154
                    break
31441
80c8a6db450d py3: use bytestr wrapper in revsetlang.tokenize()
Yuya Nishihara <yuya@tcha.org>
parents: 31440
diff changeset
   155
                if d == '.' and program[pos - 1] == '.': # special case for ..
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   156
                    pos -= 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   157
                    break
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   158
                pos += 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   159
            sym = program[s:pos]
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   160
            if sym in keywords: # operator keywords
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   161
                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
   162
            elif '-' in sym:
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   163
                # 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
   164
                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
   165
                    # 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
   166
                    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
   167
                else:
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   168
                    # 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
   169
                    parts = sym.split('-')
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   170
                    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
   171
                        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
   172
                            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
   173
                        s += len(p)
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   174
                        yield ('-', None, pos)
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   175
                        s += 1
403f1f73d30f revset: try to handle hyphenated symbols if lookup callback is available
Matt Mackall <mpm@selenic.com>
parents: 20779
diff changeset
   176
                    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
   177
                        yield ('symbol', parts[-1], s)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   178
            else:
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   179
                yield ('symbol', sym, s)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   180
            pos -= 1
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   181
        else:
24708
fb47816e1a9c revsets: more informative syntax error message
Ryan McElroy <rmcelroy@fb.com>
parents: 24518
diff changeset
   182
            raise error.ParseError(_("syntax error in revset '%s'") %
fb47816e1a9c revsets: more informative syntax error message
Ryan McElroy <rmcelroy@fb.com>
parents: 24518
diff changeset
   183
                                   program, pos)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   184
        pos += 1
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   185
    yield ('end', None, pos)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   186
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   187
# helpers
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   188
30802
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   189
_notset = object()
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   190
29441
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   191
def getsymbol(x):
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   192
    if x and x[0] == 'symbol':
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   193
        return x[1]
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   194
    raise error.ParseError(_('not a symbol'))
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   195
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   196
def getstring(x, err):
11406
42408cd43f55 revset: fix up contains/getstring when no args passed
Matt Mackall <mpm@selenic.com>
parents: 11404
diff changeset
   197
    if x and (x[0] == 'string' or x[0] == 'symbol'):
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   198
        return x[1]
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11284
diff changeset
   199
    raise error.ParseError(err)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   200
30802
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   201
def getinteger(x, err, default=_notset):
5eb3e4568c94 revset: add default value to getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30801
diff changeset
   202
    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
   203
        return default
30801
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   204
    try:
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   205
        return int(getstring(x, err))
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   206
    except ValueError:
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   207
        raise error.ParseError(err)
67ee7874e53b revset: factor out getinteger() helper
Yuya Nishihara <yuya@tcha.org>
parents: 30800
diff changeset
   208
31997
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   209
def getboolean(x, err):
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   210
    value = util.parsebool(getsymbol(x))
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   211
    if value is not None:
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   212
        return value
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   213
    raise error.ParseError(err)
11f501f0a213 revsetlang: add a getboolean helper function
Denis Laxalde <denis@laxalde.org>
parents: 31800
diff changeset
   214
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   215
def getlist(x):
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   216
    if not x:
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   217
        return []
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   218
    if x[0] == 'list':
27987
b19d8d5d6b51 revset: flatten chained 'list' operations (aka function args) (issue5072)
Yuya Nishihara <yuya@tcha.org>
parents: 27945
diff changeset
   219
        return list(x[1:])
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   220
    return [x]
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   221
30804
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   222
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
   223
    if not x:
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   224
        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
   225
    op = x[0]
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   226
    if op == 'range':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   227
        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
   228
    elif op == 'rangepre':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   229
        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
   230
    elif op == 'rangepost':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   231
        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
   232
    elif op == 'rangeall':
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   233
        return None, None
4227f80f72b2 revset: abuse x:y syntax to specify line range of followlines()
Yuya Nishihara <yuya@tcha.org>
parents: 30803
diff changeset
   234
    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
   235
11339
744d5b73f776 revset: improve filter argument handling
Matt Mackall <mpm@selenic.com>
parents: 11304
diff changeset
   236
def getargs(x, min, max, err):
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   237
    l = getlist(x)
16161
5a627b49b4d9 graphlog: paths/-I/-X handling requires a new revset
Patrick Mezard <patrick@mezard.eu>
parents: 16096
diff changeset
   238
    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
   239
        raise error.ParseError(err)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   240
    return l
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   241
25767
026105c442d7 revset: rename getkwargs() to getargsdict()
Yuya Nishihara <yuya@tcha.org>
parents: 25766
diff changeset
   242
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
   243
    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
   244
                                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
   245
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   246
# cache of {spec: raw parsed tree} built internally
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   247
_treecache = {}
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   248
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   249
def _cachedtree(spec):
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   250
    # thread safe because parse() is reentrant and dict.__setitem__() is atomic
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   251
    tree = _treecache.get(spec)
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   252
    if tree is None:
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   253
        _treecache[spec] = tree = parse(spec)
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   254
    return tree
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   255
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   256
def _build(tmplspec, *repls):
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   257
    """Create raw parsed tree from a template revset statement
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   258
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   259
    >>> _build(b'f(_) and _', (b'string', b'1'), (b'symbol', b'2'))
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   260
    ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2'))
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   261
    """
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   262
    template = _cachedtree(tmplspec)
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   263
    return parser.buildtree(template, ('symbol', '_'), *repls)
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   264
34046
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   265
def _match(patspec, tree):
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   266
    """Test if a tree matches the given pattern statement; return the matches
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   267
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   268
    >>> _match(b'f(_)', parse(b'f()'))
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   269
    >>> _match(b'f(_)', parse(b'f(1)'))
34046
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   270
    [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')]
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   271
    >>> _match(b'f(_)', parse(b'f(1, 2)'))
34046
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   272
    """
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   273
    pattern = _cachedtree(patspec)
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   274
    return parser.matchtree(pattern, tree, ('symbol', '_'),
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   275
                            {'keyvalue', 'list'})
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   276
29117
7828cadd2873 revset: construct arguments of only() against matched tree
Yuya Nishihara <yuya@tcha.org>
parents: 29116
diff changeset
   277
def _matchonly(revs, bases):
34046
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   278
    return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases))
29116
0c9b05dae010 revset: unnest isonly() closure from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29098
diff changeset
   279
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   280
def _fixops(x):
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   281
    """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
   282
    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
   283
    if not isinstance(x, tuple):
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   284
        return x
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   285
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   286
    op = x[0]
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   287
    if op == 'parent':
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   288
        # 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
   289
        # 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
   290
        post = ('parentpost', x[1])
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   291
        if x[2][0] == 'dagrangepre':
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   292
            return _fixops(('dagrange', post, x[2][1]))
35542
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
   293
        elif x[2][0] == 'dagrangeall':
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
   294
            return _fixops(('dagrangepost', post))
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   295
        elif x[2][0] == 'rangepre':
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   296
            return _fixops(('range', post, x[2][1]))
29770
9c51a5de76db revset: also parse x^: as (x^):
Yuya Nishihara <yuya@tcha.org>
parents: 29769
diff changeset
   297
        elif x[2][0] == 'rangeall':
9c51a5de76db revset: also parse x^: as (x^):
Yuya Nishihara <yuya@tcha.org>
parents: 29769
diff changeset
   298
            return _fixops(('rangepost', post))
29929
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   299
    elif op == 'or':
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   300
        # make number of arguments deterministic:
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   301
        # 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
   302
        return (op, _fixops(('list',) + x[1:]))
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
   303
    elif op == 'subscript' and x[1][0] == 'relation':
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
   304
        # x#y[z] ternary
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
   305
        return _fixops(('relsubscript', x[1][1], x[1][2], x[2]))
29769
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   306
abe4eecc3253 revset: resolve ambiguity of x^:y before alias expansion
Yuya Nishihara <yuya@tcha.org>
parents: 29767
diff changeset
   307
    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
   308
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   309
def _analyze(x):
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   310
    if x is None:
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   311
        return x
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   312
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   313
    op = x[0]
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   314
    if op == 'minus':
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   315
        return _analyze(_build('_ and not _', *x[1:]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   316
    elif op == 'only':
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   317
        return _analyze(_build('only(_, _)', *x[1:]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   318
    elif op == 'onlypost':
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   319
        return _analyze(_build('only(_)', x[1]))
35542
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
   320
    elif op == 'dagrangeall':
beb667c9880f revset: parse x^:: as (x^):: (issue5764)
Yuya Nishihara <yuya@tcha.org>
parents: 35494
diff changeset
   321
        raise error.ParseError(_("can't use '::' in this context"))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   322
    elif op == 'dagrangepre':
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   323
        return _analyze(_build('ancestors(_)', x[1]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   324
    elif op == 'dagrangepost':
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   325
        return _analyze(_build('descendants(_)', x[1]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   326
    elif op == 'negate':
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   327
        s = getstring(x[1], _("can't negate that"))
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   328
        return _analyze(('string', '-' + s))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   329
    elif op in ('string', 'symbol'):
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   330
        return x
30803
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   331
    elif op == 'rangeall':
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   332
        return (op, None)
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   333
    elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}:
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   334
        return (op, _analyze(x[1]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   335
    elif op == 'group':
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   336
        return _analyze(x[1])
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   337
    elif op in {'and', 'dagrange', 'range', 'parent', 'ancestor', 'relation',
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   338
                'subscript'}:
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   339
        ta = _analyze(x[1])
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   340
        tb = _analyze(x[2])
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   341
        return (op, ta, tb)
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
   342
    elif op == 'relsubscript':
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   343
        ta = _analyze(x[1])
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   344
        tb = _analyze(x[2])
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   345
        tc = _analyze(x[3])
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   346
        return (op, ta, tb, tc)
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   347
    elif op == 'list':
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   348
        return (op,) + tuple(_analyze(y) for y in x[1:])
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   349
    elif op == 'keyvalue':
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   350
        return (op, x[1], _analyze(x[2]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   351
    elif op == 'func':
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   352
        return (op, x[1], _analyze(x[2]))
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   353
    raise ValueError('invalid operator %r' % op)
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   354
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   355
def analyze(x):
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   356
    """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
   357
    optimize() or getset()
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   358
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   359
    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
   360
    defined in methods or symbols table respectively.
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   361
    """
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   362
    return _analyze(x)
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   363
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   364
def _optimize(x):
13031
3da456d0c885 code style: prefer 'is' and 'is not' tests with singletons
Martin Geisler <mg@aragost.com>
parents: 12936
diff changeset
   365
    if x is None:
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   366
        return 0, x
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   367
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   368
    op = x[0]
29904
e4b4168a4f1c revset: extract tree transformation from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29898
diff changeset
   369
    if op in ('string', 'symbol'):
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   370
        return 0.5, x # single revisions are small
16859
eeb464ed7275 revset: drop unreachable code
Bryan O'Sullivan <bryano@fb.com>
parents: 16838
diff changeset
   371
    elif op == 'and':
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   372
        wa, ta = _optimize(x[1])
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   373
        wb, tb = _optimize(x[2])
29116
0c9b05dae010 revset: unnest isonly() closure from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29098
diff changeset
   374
        w = min(wa, wb)
20499
2efd608473fb revset: optimize missing ancestor expressions
Siddharth Agarwal <sid0@fb.com>
parents: 20498
diff changeset
   375
34065
c6c8a52e28c9 revset: optimize "draft() & ::x" pattern
Jun Wu <quark@fb.com>
parents: 34047
diff changeset
   376
        # (draft/secret/_notpublic() & ::x) have a fast path
c6c8a52e28c9 revset: optimize "draft() & ::x" pattern
Jun Wu <quark@fb.com>
parents: 34047
diff changeset
   377
        m = _match('_() & ancestors(_)', ('and', ta, tb))
c6c8a52e28c9 revset: optimize "draft() & ::x" pattern
Jun Wu <quark@fb.com>
parents: 34047
diff changeset
   378
        if m and getsymbol(m[1]) in {'draft', 'secret', '_notpublic'}:
c6c8a52e28c9 revset: optimize "draft() & ::x" pattern
Jun Wu <quark@fb.com>
parents: 34047
diff changeset
   379
            return w, _build('_phaseandancestors(_, _)', m[1], m[2])
c6c8a52e28c9 revset: optimize "draft() & ::x" pattern
Jun Wu <quark@fb.com>
parents: 34047
diff changeset
   380
20499
2efd608473fb revset: optimize missing ancestor expressions
Siddharth Agarwal <sid0@fb.com>
parents: 20498
diff changeset
   381
        # (::x and not ::y)/(not ::y and ::x) have a fast path
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   382
        m = _matchonly(ta, tb) or _matchonly(tb, ta)
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   383
        if m:
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   384
            return w, _build('only(_, _)', *m[1:])
20499
2efd608473fb revset: optimize missing ancestor expressions
Siddharth Agarwal <sid0@fb.com>
parents: 20498
diff changeset
   385
34046
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   386
        m = _match('not _', tb)
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   387
        if m:
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   388
            return wa, ('difference', ta, m[1])
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   389
        if wa > wb:
34020
37b82485097f revset: do not flip "and" arguments when optimizing
Jun Wu <quark@fb.com>
parents: 34016
diff changeset
   390
            op = 'andsmally'
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   391
        return w, (op, ta, tb)
11279
62ccf4cd6e7f revset: optimize the parse tree directly
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
   392
    elif op == 'or':
25343
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   393
        # 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
   394
        # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   395
        ws, ts, ss = [], [], []
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   396
        def flushss():
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   397
            if not ss:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   398
                return
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   399
            if len(ss) == 1:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   400
                w, t = ss[0]
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   401
            else:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   402
                s = '\0'.join(t[1] for w, t in ss)
34044
b862e6fca7ac revsetlang: build optimized tree by helper function
Yuya Nishihara <yuya@tcha.org>
parents: 34020
diff changeset
   403
                y = _build('_list(_)', ('string', s))
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   404
                w, t = _optimize(y)
25343
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   405
            ws.append(w)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   406
            ts.append(t)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   407
            del ss[:]
29929
b3845cab4ddc revset: wrap arguments of 'or' by 'list' node
Yuya Nishihara <yuya@tcha.org>
parents: 29922
diff changeset
   408
        for y in getlist(x[1]):
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   409
            w, t = _optimize(y)
25996
b12e00a05d57 revset: prevent crash caused by empty group expression while optimizing "or"
Yuya Nishihara <yuya@tcha.org>
parents: 25995
diff changeset
   410
            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
   411
                ss.append((w, t))
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   412
                continue
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   413
            flushss()
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   414
            ws.append(w)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   415
            ts.append(t)
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   416
        flushss()
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   417
        if len(ts) == 1:
7fbef7932af9 revset: optimize 'or' operation of trivial revisions to a list
Yuya Nishihara <yuya@tcha.org>
parents: 25342
diff changeset
   418
            return ws[0], ts[0] # 'or' operation is fully optimized out
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   419
        return max(ws), (op, ('list',) + tuple(ts))
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   420
    elif op == 'not':
25191
08d1ef09ed37 revset: optimize not public revset
Laurent Charignon <lcharignon@fb.com>
parents: 25149
diff changeset
   421
        # Optimize not public() to _notpublic() because we have a fast version
34046
f23cbca9b277 revsetlang: match tree by helper function on optimize
Yuya Nishihara <yuya@tcha.org>
parents: 34044
diff changeset
   422
        if _match('public()', x[1]):
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   423
            o = _optimize(_build('_notpublic()'))
25191
08d1ef09ed37 revset: optimize not public revset
Laurent Charignon <lcharignon@fb.com>
parents: 25149
diff changeset
   424
            return o[0], o[1]
08d1ef09ed37 revset: optimize not public revset
Laurent Charignon <lcharignon@fb.com>
parents: 25149
diff changeset
   425
        else:
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   426
            o = _optimize(x[1])
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   427
            return o[0], (op, o[1])
30803
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   428
    elif op == 'rangeall':
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   429
        return 1, x
30803
d389f19f14aa revset: do not transform range* operators in parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 30802
diff changeset
   430
    elif op in ('rangepre', 'rangepost', 'parentpost'):
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   431
        o = _optimize(x[1])
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   432
        return o[0], (op, o[1])
33415
371f59c6a89e revset: do not compute weight for integer literal argument
Yuya Nishihara <yuya@tcha.org>
parents: 33336
diff changeset
   433
    elif op in ('dagrange', 'range'):
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   434
        wa, ta = _optimize(x[1])
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   435
        wb, tb = _optimize(x[2])
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   436
        return wa + wb, (op, ta, tb)
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
   437
    elif op in ('parent', 'ancestor', 'relation', 'subscript'):
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   438
        w, t = _optimize(x[1])
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   439
        return w, (op, t, x[2])
33416
9467d5337292 revset: add experimental relation and subscript operators
Yuya Nishihara <yuya@tcha.org>
parents: 33415
diff changeset
   440
    elif op == 'relsubscript':
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   441
        w, t = _optimize(x[1])
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   442
        return w, (op, t, x[2], x[3])
27987
b19d8d5d6b51 revset: flatten chained 'list' operations (aka function args) (issue5072)
Yuya Nishihara <yuya@tcha.org>
parents: 27945
diff changeset
   443
    elif op == 'list':
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   444
        ws, ts = zip(*(_optimize(y) 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
   445
        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
   446
    elif op == 'keyvalue':
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   447
        w, t = _optimize(x[2])
29766
5004ef47f437 revset: fix keyword arguments to go through optimization process
Yuya Nishihara <yuya@tcha.org>
parents: 29646
diff changeset
   448
        return w, (op, x[1], t)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   449
    elif op == 'func':
29441
9e8d258708bb revset: check invalid function syntax "func-name"() explicitly
Yuya Nishihara <yuya@tcha.org>
parents: 29425
diff changeset
   450
        f = getsymbol(x[1])
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   451
        wa, ta = _optimize(x[2])
34273
b0790bebfcf8 revset: move weight information to predicate
Jun Wu <quark@fb.com>
parents: 34272
diff changeset
   452
        w = getattr(symbols.get(f), '_weight', 1)
34011
1b28525e6698 revset: remove order information from tree (API)
Jun Wu <quark@fb.com>
parents: 34010
diff changeset
   453
        return w + wa, (op, x[1], ta)
29896
104914b03b83 revset: make optimize() reject unknown operators
Yuya Nishihara <yuya@tcha.org>
parents: 29814
diff changeset
   454
    raise ValueError('invalid operator %r' % op)
11275
c9ce8ecd6ca1 revset: introduce revset core
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
   455
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   456
def optimize(tree):
29905
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   457
    """Optimize evaluatable tree
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   458
371c2a39eead revset: make analyze() a separate step from optimize()
Yuya Nishihara <yuya@tcha.org>
parents: 29904
diff changeset
   459
    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
   460
    """
34272
53fb09c73ba8 revset: remove "small" argument from "_optimize"
Jun Wu <quark@fb.com>
parents: 34131
diff changeset
   461
    _weight, newtree = _optimize(tree)
29119
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   462
    return newtree
a032ebea4e97 revset: factor out public optimize() function from recursion
Yuya Nishihara <yuya@tcha.org>
parents: 29118
diff changeset
   463
23845
0a7fd54d4e60 revset: introduce "_parsealiasdecl" to parse alias declarations strictly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23844
diff changeset
   464
# 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
   465
# alias declarations and definitions
34069
e18119b1ad5d py3: fix mixed bytes/unicode in revsetlang._aliassyminitletters
Yuya Nishihara <yuya@tcha.org>
parents: 34065
diff changeset
   466
_aliassyminitletters = _syminitletters | {'$'}
23845
0a7fd54d4e60 revset: introduce "_parsealiasdecl" to parse alias declarations strictly
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23844
diff changeset
   467
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   468
def _parsewith(spec, lookup=None, syminitletters=None):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   469
    """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
   470
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   471
    >>> _parsewith(b'foo($1)', syminitletters=_aliassyminitletters)
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   472
    ('func', ('symbol', 'foo'), ('symbol', '$1'))
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   473
    >>> _parsewith(b'$1')
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   474
    Traceback (most recent call last):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   475
      ...
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   476
    ParseError: ("syntax error in revset '$1'", 0)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   477
    >>> _parsewith(b'foo bar')
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   478
    Traceback (most recent call last):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   479
      ...
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   480
    ParseError: ('invalid token', 4)
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   481
    """
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   482
    p = parser.parser(elements)
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   483
    tree, pos = p.parse(tokenize(spec, lookup=lookup,
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   484
                                 syminitletters=syminitletters))
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   485
    if pos != len(spec):
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   486
        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
   487
    return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
29073
81bac118f9e2 revset: factor out common parsing function
Yuya Nishihara <yuya@tcha.org>
parents: 29072
diff changeset
   488
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28869
diff changeset
   489
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
   490
    """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
   491
    _section = _('revset alias')
29074
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   492
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   493
    @staticmethod
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   494
    def _parse(spec):
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   495
        """Parse alias declaration/definition ``spec``
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   496
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   497
        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
   498
        (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
   499
        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
   500
        """
e7c679738503 revset: define _parsealias() in _aliasrules class
Yuya Nishihara <yuya@tcha.org>
parents: 29073
diff changeset
   501
        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
   502
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   503
    @staticmethod
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   504
    def _trygetfunc(tree):
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   505
        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
   506
            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
   507
33336
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   508
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
   509
    """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
   510
    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
   511
    tree = _aliasrules.expand(aliases, tree)
29922
ae933e3e2226 revset: remove showwarning option from expandaliases()
Yuya Nishihara <yuya@tcha.org>
parents: 29906
diff changeset
   512
    # 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
   513
    if warn is not None:
4672db164c98 revset: make repo.anyrevs accept customized alias override (API)
Jun Wu <quark@fb.com>
parents: 32913
diff changeset
   514
        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
   515
            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
   516
                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
   517
                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
   518
    return tree
14098
9f5a0acb0056 revset aliases
Alexander Solovyov <alexander@solovyov.net>
parents: 14073
diff changeset
   519
23742
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   520
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
   521
    """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
   522
    """
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   523
    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
   524
        return tree
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   525
    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
   526
        pending = [tree]
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   527
        l = []
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   528
        while pending:
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   529
            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
   530
            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
   531
                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
   532
            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
   533
                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
   534
            else:
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   535
                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
   536
                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
   537
        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
   538
    else:
3a4d8a6ce432 revset: introduce new operator "##" to concatenate strings/symbols at runtime
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 23729
diff changeset
   539
        return tuple(foldconcat(t) for t in tree)
14098
9f5a0acb0056 revset aliases
Alexander Solovyov <alexander@solovyov.net>
parents: 14073
diff changeset
   540
20779
ffc2295c6b80 revset: pass a lookup function to the tokenizer
Matt Mackall <mpm@selenic.com>
parents: 20754
diff changeset
   541
def parse(spec, lookup=None):
36685
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   542
    try:
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   543
        return _parsewith(spec, lookup=lookup)
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   544
    except error.ParseError as inst:
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   545
        if len(inst.args) > 1:  # has location
36691
1b179d151578 templater: fix position of terminator character in error message
Yuya Nishihara <yuya@tcha.org>
parents: 36685
diff changeset
   546
            loc = inst.args[1]
36685
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   547
            # Remove newlines -- spaces are equivalent whitespace.
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   548
            spec = spec.replace('\n', ' ')
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   549
            # We want the caret to point to the place in the template that
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   550
            # failed to parse, but in a hint we get a open paren at the
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   551
            # start. Therefore, we print "loc + 1" spaces (instead of "loc")
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   552
            # to line up the caret with the location of the error.
36691
1b179d151578 templater: fix position of terminator character in error message
Yuya Nishihara <yuya@tcha.org>
parents: 36685
diff changeset
   553
            inst.hint = spec + '\n' + ' ' * (loc + 1) + '^ ' + _('here')
36685
2a258985ffeb revsetlang: add a hint for more useful parse errors
Ryan McElroy <rmcelroy@fb.com>
parents: 35882
diff changeset
   554
        raise
20779
ffc2295c6b80 revset: pass a lookup function to the tokenizer
Matt Mackall <mpm@selenic.com>
parents: 20754
diff changeset
   555
31604
7eac6fcf2ffa revsetlang: move quoting function to not be a closure
Augie Fackler <augie@google.com>
parents: 31603
diff changeset
   556
def _quote(s):
31605
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   557
    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
   558
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   559
    >>> _quote(b'asdf')
31605
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   560
    "'asdf'"
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   561
    >>> _quote(b"asdf'\"")
31605
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   562
    '\'asdf\\\'"\''
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   563
    >>> _quote(b'asdf\'')
31606
0b3eb280564b revsetlang: perform quoting using ui.escapestr instead of repr()
Augie Fackler <augie@google.com>
parents: 31605
diff changeset
   564
    "'asdf\\''"
31605
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   565
    >>> _quote(1)
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   566
    "'1'"
0b94c19b641c revsetlang: add docstring with some tests to _quote
Augie Fackler <augie@google.com>
parents: 31604
diff changeset
   567
    """
31636
f3b151278655 revsetlang: fix _quote on int on python3
Augie Fackler <raf@durin42.com>
parents: 31606
diff changeset
   568
    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
   569
35596
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   570
def _formatargtype(c, arg):
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   571
    if c == 'd':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   572
        return '%d' % int(arg)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   573
    elif c == 's':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   574
        return _quote(arg)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   575
    elif c == 'r':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   576
        parse(arg) # make sure syntax errors are confined
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   577
        return '(%s)' % arg
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   578
    elif c == 'n':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   579
        return _quote(node.hex(arg))
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   580
    elif c == 'b':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   581
        try:
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   582
            return _quote(arg.branch())
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   583
        except AttributeError:
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   584
            raise TypeError
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   585
    raise error.ParseError(_('unexpected revspec format character %s') % c)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   586
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   587
def _formatlistexp(s, t):
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   588
    l = len(s)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   589
    if l == 0:
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   590
        return "_list('')"
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   591
    elif l == 1:
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   592
        return _formatargtype(t, s[0])
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   593
    elif t == 'd':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   594
        return "_intlist('%s')" % "\0".join('%d' % int(a) for a in s)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   595
    elif t == 's':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   596
        return "_list(%s)" % _quote("\0".join(s))
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   597
    elif t == 'n':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   598
        return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   599
    elif t == 'b':
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   600
        try:
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   601
            return "_list('%s')" % "\0".join(a.branch() for a in s)
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   602
        except AttributeError:
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   603
            raise TypeError
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   604
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   605
    m = l // 2
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   606
    return '(%s or %s)' % (_formatlistexp(s[:m], t), _formatlistexp(s[m:], t))
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   607
35597
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   608
def _formatparamexp(args, t):
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   609
    return ', '.join(_formatargtype(t, a) for a in args)
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   610
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   611
_formatlistfuncs = {
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   612
    'l': _formatlistexp,
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   613
    'p': _formatparamexp,
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   614
}
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   615
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   616
def formatspec(expr, *args):
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   617
    '''
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   618
    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
   619
    escapes arguments appropriately. Aliases are intentionally ignored
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   620
    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
   621
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   622
    Supported arguments:
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   623
15266
8bea39ca9acb revset: add %r for embedded revset support to formatspec
Matt Mackall <mpm@selenic.com>
parents: 15153
diff changeset
   624
    %r = revset expression, parenthesized
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   625
    %d = int(arg), no quoting
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   626
    %s = string(arg), escaped and single-quoted
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   627
    %b = arg.branch(), escaped and single-quoted
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   628
    %n = hex(arg), single-quoted
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   629
    %% = a literal '%'
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   630
35597
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   631
    Prefixing the type with 'l' specifies a parenthesized list of that type,
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   632
    and 'p' specifies a list of function parameters of that type.
15140
353a1ba928f6 revset: add 'l' flag to formatspec for args
Matt Mackall <mpm@selenic.com>
parents: 15138
diff changeset
   633
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   634
    >>> formatspec(b'%r:: and %lr', b'10 or 11', (b"this()", b"that()"))
15268
bd5103819c2e revset: fix %r handling in formatspec
Matt Mackall <mpm@selenic.com>
parents: 15266
diff changeset
   635
    '(10 or 11):: and ((this()) or (that()))'
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   636
    >>> formatspec(b'%d:: and not %d::', 10, 20)
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   637
    '10:: and not 20::'
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   638
    >>> formatspec(b'%ld or %ld', [], [1])
15898
6902e13ddd03 revset: optimize building large lists in formatrevspec
Matt Mackall <mpm@selenic.com>
parents: 15837
diff changeset
   639
    "_list('') or 1"
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   640
    >>> formatspec(b'keyword(%s)', b'foo\\xe9')
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   641
    "keyword('foo\\\\xe9')"
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   642
    >>> b = lambda: b'default'
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   643
    >>> b.branch = b
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   644
    >>> formatspec(b'branch(%b)', b)
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   645
    "branch('default')"
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34069
diff changeset
   646
    >>> formatspec(b'root(%ls)', [b'a', b'b', b'c', b'd'])
35595
91201737d07a revsetlang: fix quoting of %ls string
Yuya Nishihara <yuya@tcha.org>
parents: 35594
diff changeset
   647
    "root(_list('a\\\\x00b\\\\x00c\\\\x00d'))"
35597
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   648
    >>> formatspec(b'sort(%r, %ps)', b':', [b'desc', b'user'])
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   649
    "sort((:), 'desc', 'user')"
35822
4269971b0d26 revsetlang: fix a doctest example on Python 3
Augie Fackler <augie@google.com>
parents: 35597
diff changeset
   650
    >>> formatspec(b'%ls', [b'a', b"'"])
35595
91201737d07a revsetlang: fix quoting of %ls string
Yuya Nishihara <yuya@tcha.org>
parents: 35594
diff changeset
   651
    "_list('a\\\\x00\\\\'')"
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   652
    '''
31440
f784ba187089 py3: use bytestr wrapper in revsetlang.formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 31385
diff changeset
   653
    expr = pycompat.bytestr(expr)
35560
3a8810c1b9bc revsetlang: use iterator to track current argument in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35559
diff changeset
   654
    argiter = iter(args)
35557
2df8d12f23bc revsetlang: avoid string concatenation in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35542
diff changeset
   655
    ret = []
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   656
    pos = 0
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   657
    while pos < len(expr):
35558
dfc628611144 revsetlang: use str.find() to scan expr in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35557
diff changeset
   658
        q = expr.find('%', pos)
dfc628611144 revsetlang: use str.find() to scan expr in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35557
diff changeset
   659
        if q < 0:
dfc628611144 revsetlang: use str.find() to scan expr in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35557
diff changeset
   660
            ret.append(expr[pos:])
dfc628611144 revsetlang: use str.find() to scan expr in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35557
diff changeset
   661
            break
dfc628611144 revsetlang: use str.find() to scan expr in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35557
diff changeset
   662
        ret.append(expr[pos:q])
dfc628611144 revsetlang: use str.find() to scan expr in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35557
diff changeset
   663
        pos = q + 1
35593
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   664
        try:
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   665
            d = expr[pos]
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   666
        except IndexError:
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   667
            raise error.ParseError(_('incomplete revspec format character'))
35559
a480551bd1b4 revsetlang: unnest "if True" in formatrevspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35558
diff changeset
   668
        if d == '%':
a480551bd1b4 revsetlang: unnest "if True" in formatrevspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35558
diff changeset
   669
            ret.append(d)
35592
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   670
            pos += 1
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   671
            continue
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   672
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   673
        try:
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   674
            arg = next(argiter)
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   675
        except StopIteration:
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   676
            raise error.ParseError(_('missing argument for revspec'))
35597
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   677
        f = _formatlistfuncs.get(d)
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   678
        if f:
35559
a480551bd1b4 revsetlang: unnest "if True" in formatrevspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35558
diff changeset
   679
            # a list of some type
a480551bd1b4 revsetlang: unnest "if True" in formatrevspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35558
diff changeset
   680
            pos += 1
35593
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   681
            try:
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   682
                d = expr[pos]
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   683
            except IndexError:
850cd045b1df revsetlang: check incomplete revspec format character
Yuya Nishihara <yuya@tcha.org>
parents: 35592
diff changeset
   684
                raise error.ParseError(_('incomplete revspec format character'))
35594
468d7a1f6633 revsetlang: catch invalid value passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35593
diff changeset
   685
            try:
35597
ed0902e8053e revsetlang: add %p specifier to format list of function arguments
Yuya Nishihara <yuya@tcha.org>
parents: 35596
diff changeset
   686
                ret.append(f(list(arg), d))
35594
468d7a1f6633 revsetlang: catch invalid value passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35593
diff changeset
   687
            except (TypeError, ValueError):
468d7a1f6633 revsetlang: catch invalid value passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35593
diff changeset
   688
                raise error.ParseError(_('invalid argument for revspec'))
35559
a480551bd1b4 revsetlang: unnest "if True" in formatrevspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35558
diff changeset
   689
        else:
35594
468d7a1f6633 revsetlang: catch invalid value passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35593
diff changeset
   690
            try:
35596
a57acea31b3b revsetlang: unnest inner functions from formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35595
diff changeset
   691
                ret.append(_formatargtype(d, arg))
35594
468d7a1f6633 revsetlang: catch invalid value passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35593
diff changeset
   692
            except (TypeError, ValueError):
468d7a1f6633 revsetlang: catch invalid value passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35593
diff changeset
   693
                raise error.ParseError(_('invalid argument for revspec'))
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   694
        pos += 1
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   695
35592
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   696
    try:
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   697
        next(argiter)
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   698
        raise error.ParseError(_('too many revspec arguments specified'))
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   699
    except StopIteration:
0fd617afebc0 revsetlang: check number of arguments passed to formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35591
diff changeset
   700
        pass
35557
2df8d12f23bc revsetlang: avoid string concatenation in formatspec()
Yuya Nishihara <yuya@tcha.org>
parents: 35542
diff changeset
   701
    return ''.join(ret)
14901
a773119f30ba revset: add formatspec convenience query builder
Matt Mackall <mpm@selenic.com>
parents: 14900
diff changeset
   702
16218
81a1a00f5738 debugrevspec: pretty print output
Patrick Mezard <patrick@mezard.eu>
parents: 16185
diff changeset
   703
def prettyformat(tree):
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25251
diff changeset
   704
    return parser.prettyformat(tree, ('string', 'symbol'))
16218
81a1a00f5738 debugrevspec: pretty print output
Patrick Mezard <patrick@mezard.eu>
parents: 16185
diff changeset
   705
19719
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   706
def depth(tree):
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   707
    if isinstance(tree, tuple):
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   708
        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
   709
    else:
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   710
        return 0
2f9d5c5256ea revset: add helper function to get revset parse tree depth
Alexander Plavin <alexander@plav.in>
parents: 19706
diff changeset
   711
19720
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   712
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
   713
    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
   714
        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
   715
    else:
f0b992a9be9c revset: add helper function to get functions used in a revset parse tree
Alexander Plavin <alexander@plav.in>
parents: 19719
diff changeset
   716
        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
   717
        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
   718
            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
   719
        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
   720
            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
   721
        return funcs
35494
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   722
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   723
_hashre = util.re.compile('[0-9a-fA-F]{1,40}$')
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   724
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   725
def _ishashlikesymbol(symbol):
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   726
    """returns true if the symbol looks like a hash"""
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   727
    return _hashre.match(symbol)
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   728
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   729
def gethashlikesymbols(tree):
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   730
    """returns the list of symbols of the tree that look like hashes
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   731
35882
87416288be98 tests: make doctest py3-compatible again
Yuya Nishihara <yuya@tcha.org>
parents: 35822
diff changeset
   732
    >>> gethashlikesymbols(parse(b'3::abe3ff'))
35494
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   733
    ['3', 'abe3ff']
35882
87416288be98 tests: make doctest py3-compatible again
Yuya Nishihara <yuya@tcha.org>
parents: 35822
diff changeset
   734
    >>> gethashlikesymbols(parse(b'precursors(.)'))
35494
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   735
    []
35882
87416288be98 tests: make doctest py3-compatible again
Yuya Nishihara <yuya@tcha.org>
parents: 35822
diff changeset
   736
    >>> gethashlikesymbols(parse(b'precursors(34)'))
35494
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   737
    ['34']
35882
87416288be98 tests: make doctest py3-compatible again
Yuya Nishihara <yuya@tcha.org>
parents: 35822
diff changeset
   738
    >>> gethashlikesymbols(parse(b'abe3ffZ'))
35494
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   739
    []
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   740
    """
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   741
    if not tree:
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   742
        return []
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   743
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   744
    if tree[0] == "symbol":
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   745
        if _ishashlikesymbol(tree[1]):
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   746
            return [tree[1]]
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   747
    elif len(tree) >= 3:
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   748
        results = []
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   749
        for subtree in tree[1:]:
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   750
            results += gethashlikesymbols(subtree)
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   751
        return results
dd911f95cbda revsetlang: add utility function to return hash like symbols from the tree
Pulkit Goyal <7895pulkit@gmail.com>
parents: 34273
diff changeset
   752
    return []