mercurial/parser.py
author Matt Harbison <matt_harbison@yahoo.com>
Sun, 23 Dec 2018 21:54:56 -0500
changeset 41063 7250cbaabde0
parent 39087 0a2ce5b43574
child 42162 29798c9ba5c9
permissions -rw-r--r--
exthelper: support the option argument when registering a command Largefiles uses this 5th argument with push and pull, so this will be tested in the next commit. I assume the reason for unrolling and reforming the tuple in each finalxxxsetup() is to validate that something proper was passed in when registering. But it's better to explode when decorating than during the delayed actual registration.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     1
# parser.py - simple top-down operator precedence parser for mercurial
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     2
#
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     3
# Copyright 2010 Matt Mackall <mpm@selenic.com>
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     4
#
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms of the
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     6
# GNU General Public License version 2 or any later version.
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     7
11449
05af334bac05 parser: fix URL to effbot
Julian Cowley <julian@lava.net>
parents: 11412
diff changeset
     8
# see http://effbot.org/zone/simple-top-down-parsing.htm and
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
     9
# http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    10
# for background
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    11
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    12
# takes a tokenizer and elements
25655
b8b73652c1c9 parser: update documentation about tokenizer and elements
Yuya Nishihara <yuya@tcha.org>
parents: 25654
diff changeset
    13
# tokenizer is an iterator that returns (type, value, pos) tuples
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    14
# elements is a mapping of types to binding strength, primary, prefix, infix
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    15
# and suffix actions
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    16
# an action is a tree node name, a tree label, and an optional match
17500
8ac8db8dc346 en-us: labeled
timeless@mozdev.org
parents: 14701
diff changeset
    17
# __call__(program) parses program into a labeled tree
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    18
34152
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
    19
from __future__ import absolute_import, print_function
25963
7448df709b2e parser: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25818
diff changeset
    20
7448df709b2e parser: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 25818
diff changeset
    21
from .i18n import _
31491
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31362
diff changeset
    22
from . import (
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31362
diff changeset
    23
    error,
36576
7840d8bd0558 py3: byte-stringify ValueError of unescapestr() to reraise as ParseError
Yuya Nishihara <yuya@tcha.org>
parents: 34152
diff changeset
    24
    pycompat,
31491
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31362
diff changeset
    25
    util,
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31362
diff changeset
    26
)
37087
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36576
diff changeset
    27
from .utils import (
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36576
diff changeset
    28
    stringutil,
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36576
diff changeset
    29
)
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
    30
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    31
class parser(object):
25654
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    32
    def __init__(self, elements, methods=None):
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    33
        self._elements = elements
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    34
        self._methods = methods
13176
895f54a79c6e templater: use the parser.py parser to extend the templater syntax
Matt Mackall <mpm@selenic.com>
parents: 11449
diff changeset
    35
        self.current = None
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    36
    def _advance(self):
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    37
        'advance the tokenizer'
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    38
        t = self.current
25171
d647f97f88dd parsers: use 'next' instead of try/except
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20778
diff changeset
    39
        self.current = next(self._iter, None)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    40
        return t
25804
f0a77cb6316a parser: extract function that tests if next token may start new term
Yuya Nishihara <yuya@tcha.org>
parents: 25803
diff changeset
    41
    def _hasnewterm(self):
f0a77cb6316a parser: extract function that tests if next token may start new term
Yuya Nishihara <yuya@tcha.org>
parents: 25803
diff changeset
    42
        'True if next token may start new term'
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    43
        return any(self._elements[self.current[0]][1:3])
25802
cc741c76b26a parser: remove unused parameter 'pos' from _match()
Yuya Nishihara <yuya@tcha.org>
parents: 25801
diff changeset
    44
    def _match(self, m):
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    45
        'make sure the tokenizer matches an end condition'
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    46
        if self.current[0] != m:
14701
4b93bd041772 parsers: fix localization markup of parser errors
Mads Kiilerich <mads@kiilerich.com>
parents: 13665
diff changeset
    47
            raise error.ParseError(_("unexpected token: %s") % self.current[0],
11305
d4cafcb63f77 cleanups: undefined variables
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 11289
diff changeset
    48
                                   self.current[2])
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    49
        self._advance()
25803
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    50
    def _parseoperand(self, bind, m=None):
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    51
        'gather right-hand-side operand until an end condition or binding met'
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    52
        if m and self.current[0] == m:
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    53
            expr = None
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    54
        else:
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    55
            expr = self._parse(bind)
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    56
        if m:
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    57
            self._match(m)
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    58
        return expr
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    59
    def _parse(self, bind=0):
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
    60
        token, value, pos = self._advance()
25816
43a8a87fc175 parser: resolve ambiguity where both prefix and primary actions are defined
Yuya Nishihara <yuya@tcha.org>
parents: 25815
diff changeset
    61
        # handle prefix rules on current token, take as primary if unambiguous
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    62
        primary, prefix = self._elements[token][1:3]
25816
43a8a87fc175 parser: resolve ambiguity where both prefix and primary actions are defined
Yuya Nishihara <yuya@tcha.org>
parents: 25815
diff changeset
    63
        if primary and not (prefix and self._hasnewterm()):
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    64
            expr = (primary, value)
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    65
        elif prefix:
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    66
            expr = (prefix[0], self._parseoperand(*prefix[1:]))
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    67
        else:
14701
4b93bd041772 parsers: fix localization markup of parser errors
Mads Kiilerich <mads@kiilerich.com>
parents: 13665
diff changeset
    68
            raise error.ParseError(_("not a prefix: %s") % token, pos)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    69
        # gather tokens until we meet a lower binding strength
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    70
        while bind < self._elements[self.current[0]][0]:
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
    71
            token, value, pos = self._advance()
25817
42ac9d1d1572 parser: reorder infix/suffix handling to be similar to prefix/primary flow
Yuya Nishihara <yuya@tcha.org>
parents: 25816
diff changeset
    72
            # handle infix rules, take as suffix if unambiguous
25815
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    73
            infix, suffix = self._elements[token][3:]
25818
455190fb4e51 parser: take suffix action if no infix action is defined
Yuya Nishihara <yuya@tcha.org>
parents: 25817
diff changeset
    74
            if suffix and not (infix and self._hasnewterm()):
29778
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29059
diff changeset
    75
                expr = (suffix, expr)
25817
42ac9d1d1572 parser: reorder infix/suffix handling to be similar to prefix/primary flow
Yuya Nishihara <yuya@tcha.org>
parents: 25816
diff changeset
    76
            elif infix:
42ac9d1d1572 parser: reorder infix/suffix handling to be similar to prefix/primary flow
Yuya Nishihara <yuya@tcha.org>
parents: 25816
diff changeset
    77
                expr = (infix[0], expr, self._parseoperand(*infix[1:]))
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    78
            else:
25817
42ac9d1d1572 parser: reorder infix/suffix handling to be similar to prefix/primary flow
Yuya Nishihara <yuya@tcha.org>
parents: 25816
diff changeset
    79
                raise error.ParseError(_("not an infix: %s") % token, pos)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    80
        return expr
25654
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    81
    def parse(self, tokeniter):
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    82
        'generate a parse tree from tokens'
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    83
        self._iter = tokeniter
13176
895f54a79c6e templater: use the parser.py parser to extend the templater syntax
Matt Mackall <mpm@selenic.com>
parents: 11449
diff changeset
    84
        self._advance()
13665
e798e430c5e5 revset: report a parse error if a revset is not parsed completely (issue2654)
Bernhard Leiner <bleiner@gmail.com>
parents: 13176
diff changeset
    85
        res = self._parse()
e798e430c5e5 revset: report a parse error if a revset is not parsed completely (issue2654)
Bernhard Leiner <bleiner@gmail.com>
parents: 13176
diff changeset
    86
        token, value, pos = self.current
e798e430c5e5 revset: report a parse error if a revset is not parsed completely (issue2654)
Bernhard Leiner <bleiner@gmail.com>
parents: 13176
diff changeset
    87
        return res, pos
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    88
    def eval(self, tree):
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    89
        'recursively evaluate a parse tree using node methods'
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    90
        if not isinstance(tree, tuple):
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    91
            return tree
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    92
        return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]])
25654
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    93
    def __call__(self, tokeniter):
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    94
        'parse tokens into a parse tree and evaluate if methods given'
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    95
        t = self.parse(tokeniter)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    96
        if self._methods:
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    97
            return self.eval(t)
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    98
        return t
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
    99
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   100
def splitargspec(spec):
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   101
    """Parse spec of function arguments into (poskeys, varkey, keys, optkey)
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   102
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   103
    >>> splitargspec(b'')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   104
    ([], None, [], None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   105
    >>> splitargspec(b'foo bar')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   106
    ([], None, ['foo', 'bar'], None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   107
    >>> splitargspec(b'foo *bar baz **qux')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   108
    (['foo'], 'bar', ['baz'], 'qux')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   109
    >>> splitargspec(b'*foo')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   110
    ([], 'foo', [], None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   111
    >>> splitargspec(b'**foo')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   112
    ([], None, [], 'foo')
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   113
    """
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   114
    optkey = None
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   115
    pre, sep, post = spec.partition('**')
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   116
    if sep:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   117
        posts = post.split()
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   118
        if not posts:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   119
            raise error.ProgrammingError('no **optkey name provided')
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   120
        if len(posts) > 1:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   121
            raise error.ProgrammingError('excessive **optkey names provided')
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   122
        optkey = posts[0]
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   123
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   124
    pre, sep, post = pre.partition('*')
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   125
    pres = pre.split()
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   126
    posts = post.split()
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   127
    if sep:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   128
        if not posts:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   129
            raise error.ProgrammingError('no *varkey name provided')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   130
        return pres, posts[0], posts[1:], optkey
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   131
    return [], None, pres, optkey
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   132
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   133
def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode):
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   134
    """Build dict from list containing positional and keyword arguments
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   135
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   136
    Arguments are specified by a tuple of ``(poskeys, varkey, keys, optkey)``
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   137
    where
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   138
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   139
    - ``poskeys``: list of names of positional arguments
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   140
    - ``varkey``: optional argument name that takes up remainder
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   141
    - ``keys``: list of names that can be either positional or keyword arguments
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   142
    - ``optkey``: optional argument name that takes up excess keyword arguments
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   143
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   144
    If ``varkey`` specified, all ``keys`` must be given as keyword arguments.
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   145
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   146
    Invalid keywords, too few positional arguments, or too many positional
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   147
    arguments are rejected, but missing keyword arguments are just omitted.
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   148
    """
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   149
    poskeys, varkey, keys, optkey = argspec
30752
ffd324eaf994 parser: make buildargsdict() precompute position where keyword args start
Yuya Nishihara <yuya@tcha.org>
parents: 30342
diff changeset
   150
    kwstart = next((i for i, x in enumerate(trees) if x[0] == keyvaluenode),
ffd324eaf994 parser: make buildargsdict() precompute position where keyword args start
Yuya Nishihara <yuya@tcha.org>
parents: 30342
diff changeset
   151
                   len(trees))
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   152
    if kwstart < len(poskeys):
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   153
        raise error.ParseError(_("%(func)s takes at least %(nargs)d positional "
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   154
                                 "arguments")
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   155
                               % {'func': funcname, 'nargs': len(poskeys)})
31920
a98540ea1e42 parser: verify excessive number of args excluding kwargs in buildargsdict()
Yuya Nishihara <yuya@tcha.org>
parents: 31491
diff changeset
   156
    if not varkey and kwstart > len(poskeys) + len(keys):
a98540ea1e42 parser: verify excessive number of args excluding kwargs in buildargsdict()
Yuya Nishihara <yuya@tcha.org>
parents: 31491
diff changeset
   157
        raise error.ParseError(_("%(func)s takes at most %(nargs)d positional "
a98540ea1e42 parser: verify excessive number of args excluding kwargs in buildargsdict()
Yuya Nishihara <yuya@tcha.org>
parents: 31491
diff changeset
   158
                                 "arguments")
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   159
                               % {'func': funcname,
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   160
                                  'nargs': len(poskeys) + len(keys)})
31922
0f41f1e3c75c parser: preserve order of keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 31921
diff changeset
   161
    args = util.sortdict()
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   162
    # consume positional arguments
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   163
    for k, x in zip(poskeys, trees[:kwstart]):
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   164
        args[k] = x
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   165
    if varkey:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   166
        args[varkey] = trees[len(args):kwstart]
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   167
    else:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   168
        for k, x in zip(keys, trees[len(args):kwstart]):
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   169
            args[k] = x
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   170
    # remainder should be keyword arguments
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   171
    if optkey:
31922
0f41f1e3c75c parser: preserve order of keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 31921
diff changeset
   172
        args[optkey] = util.sortdict()
30752
ffd324eaf994 parser: make buildargsdict() precompute position where keyword args start
Yuya Nishihara <yuya@tcha.org>
parents: 30342
diff changeset
   173
    for x in trees[kwstart:]:
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   174
        if x[0] != keyvaluenode or x[1][0] != keynode:
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   175
            raise error.ParseError(_("%(func)s got an invalid argument")
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   176
                                   % {'func': funcname})
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   177
        k = x[1][1]
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   178
        if k in keys:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   179
            d = args
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   180
        elif not optkey:
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   181
            raise error.ParseError(_("%(func)s got an unexpected keyword "
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   182
                                     "argument '%(key)s'")
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   183
                                   % {'func': funcname, 'key': k})
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   184
        else:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   185
            d = args[optkey]
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   186
        if k in d:
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   187
            raise error.ParseError(_("%(func)s got multiple values for keyword "
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   188
                                     "argument '%(key)s'")
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   189
                                   % {'func': funcname, 'key': k})
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   190
        d[k] = x[2]
25705
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   191
    return args
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   192
26231
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   193
def unescapestr(s):
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   194
    try:
37087
f0b6fbea00cf stringutil: bulk-replace call sites to point to new module
Yuya Nishihara <yuya@tcha.org>
parents: 36576
diff changeset
   195
        return stringutil.unescapestr(s)
26231
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   196
    except ValueError as e:
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   197
        # mangle Python's exception into our format
36576
7840d8bd0558 py3: byte-stringify ValueError of unescapestr() to reraise as ParseError
Yuya Nishihara <yuya@tcha.org>
parents: 34152
diff changeset
   198
        raise error.ParseError(pycompat.bytestr(e).lower())
26231
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   199
25254
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   200
def _prettyformat(tree, leafnodes, level, lines):
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   201
    if not isinstance(tree, tuple):
39087
0a2ce5b43574 parser: replace bespoke _brepr with stringutil.pprint
Augie Fackler <augie@google.com>
parents: 37087
diff changeset
   202
        lines.append((level, stringutil.pprint(tree)))
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   203
    elif tree[0] in leafnodes:
39087
0a2ce5b43574 parser: replace bespoke _brepr with stringutil.pprint
Augie Fackler <augie@google.com>
parents: 37087
diff changeset
   204
        rs = map(stringutil.pprint, tree[1:])
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   205
        lines.append((level, '(%s %s)' % (tree[0], ' '.join(rs))))
25254
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   206
    else:
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   207
        lines.append((level, '(%s' % tree[0]))
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   208
        for s in tree[1:]:
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   209
            _prettyformat(s, leafnodes, level + 1, lines)
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   210
        lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   211
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   212
def prettyformat(tree, leafnodes):
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   213
    lines = []
25254
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   214
    _prettyformat(tree, leafnodes, 0, lines)
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   215
    output = '\n'.join(('  ' * l + s) for l, s in lines)
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   216
    return output
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   217
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   218
def simplifyinfixops(tree, targetnodes):
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   219
    """Flatten chained infix operations to reduce usage of Python stack
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   220
34152
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   221
    >>> from . import pycompat
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   222
    >>> def f(tree):
34152
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   223
    ...     s = prettyformat(simplifyinfixops(tree, (b'or',)), (b'symbol',))
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   224
    ...     print(pycompat.sysstr(s))
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   225
    >>> f((b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   226
    ...     (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   227
    ...       (b'symbol', b'1'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   228
    ...       (b'symbol', b'2')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   229
    ...     (b'symbol', b'3')))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   230
    (or
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   231
      (symbol '1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   232
      (symbol '2')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   233
      (symbol '3'))
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   234
    >>> f((b'func',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   235
    ...     (b'symbol', b'p1'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   236
    ...     (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   237
    ...       (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   238
    ...         (b'func',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   239
    ...           (b'symbol', b'sort'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   240
    ...           (b'list',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   241
    ...             (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   242
    ...               (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   243
    ...                 (b'symbol', b'1'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   244
    ...                 (b'symbol', b'2')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   245
    ...               (b'symbol', b'3')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   246
    ...             (b'negate',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   247
    ...               (b'symbol', b'rev')))),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   248
    ...         (b'and',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   249
    ...           (b'symbol', b'4'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   250
    ...           (b'group',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   251
    ...             (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   252
    ...               (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   253
    ...                 (b'symbol', b'5'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   254
    ...                 (b'symbol', b'6')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   255
    ...               (b'symbol', b'7'))))),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   256
    ...       (b'symbol', b'8'))))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   257
    (func
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   258
      (symbol 'p1')
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   259
      (or
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   260
        (func
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   261
          (symbol 'sort')
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   262
          (list
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   263
            (or
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   264
              (symbol '1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   265
              (symbol '2')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   266
              (symbol '3'))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   267
            (negate
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   268
              (symbol 'rev'))))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   269
        (and
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   270
          (symbol '4')
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   271
          (group
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   272
            (or
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   273
              (symbol '5')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   274
              (symbol '6')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   275
              (symbol '7'))))
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   276
        (symbol '8')))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   277
    """
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   278
    if not isinstance(tree, tuple):
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   279
        return tree
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   280
    op = tree[0]
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   281
    if op not in targetnodes:
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   282
        return (op,) + tuple(simplifyinfixops(x, targetnodes) for x in tree[1:])
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   283
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   284
    # walk down left nodes taking each right node. no recursion to left nodes
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   285
    # because infix operators are left-associative, i.e. left tree is deep.
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   286
    # e.g. '1 + 2 + 3' -> (+ (+ 1 2) 3) -> (+ 1 2 3)
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   287
    simplified = []
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   288
    x = tree
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   289
    while x[0] == op:
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   290
        l, r = x[1:]
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   291
        simplified.append(simplifyinfixops(r, targetnodes))
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   292
        x = l
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   293
    simplified.append(simplifyinfixops(x, targetnodes))
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   294
    simplified.append(op)
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   295
    return tuple(reversed(simplified))
28720
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   296
34060
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   297
def _buildtree(template, placeholder, replstack):
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   298
    if template == placeholder:
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   299
        return replstack.pop()
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   300
    if not isinstance(template, tuple):
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   301
        return template
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   302
    return tuple(_buildtree(x, placeholder, replstack) for x in template)
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   303
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   304
def buildtree(template, placeholder, *repls):
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   305
    """Create new tree by substituting placeholders by replacements
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   306
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   307
    >>> _ = (b'symbol', b'_')
34060
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   308
    >>> def f(template, *repls):
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   309
    ...     return buildtree(template, _, *repls)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   310
    >>> f((b'func', (b'symbol', b'only'), (b'list', _, _)),
34060
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   311
    ...   ('symbol', '1'), ('symbol', '2'))
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   312
    ('func', ('symbol', 'only'), ('list', ('symbol', '1'), ('symbol', '2')))
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   313
    >>> f((b'and', _, (b'not', _)), (b'symbol', b'1'), (b'symbol', b'2'))
34060
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   314
    ('and', ('symbol', '1'), ('not', ('symbol', '2')))
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   315
    """
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   316
    if not isinstance(placeholder, tuple):
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   317
        raise error.ProgrammingError('placeholder must be a node tuple')
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   318
    replstack = list(reversed(repls))
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   319
    r = _buildtree(template, placeholder, replstack)
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   320
    if replstack:
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   321
        raise error.ProgrammingError('too many replacements')
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   322
    return r
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   323
34062
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   324
def _matchtree(pattern, tree, placeholder, incompletenodes, matches):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   325
    if pattern == tree:
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   326
        return True
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   327
    if not isinstance(pattern, tuple) or not isinstance(tree, tuple):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   328
        return False
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   329
    if pattern == placeholder and tree[0] not in incompletenodes:
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   330
        matches.append(tree)
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   331
        return True
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   332
    if len(pattern) != len(tree):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   333
        return False
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   334
    return all(_matchtree(p, x, placeholder, incompletenodes, matches)
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   335
               for p, x in zip(pattern, tree))
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   336
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   337
def matchtree(pattern, tree, placeholder=None, incompletenodes=()):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   338
    """If a tree matches the pattern, return a list of the tree and nodes
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   339
    matched with the placeholder; Otherwise None
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   340
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   341
    >>> def f(pattern, tree):
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   342
    ...     m = matchtree(pattern, tree, _, {b'keyvalue', b'list'})
34062
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   343
    ...     if m:
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   344
    ...         return m[1:]
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   345
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   346
    >>> _ = (b'symbol', b'_')
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   347
    >>> f((b'func', (b'symbol', b'ancestors'), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   348
    ...   (b'func', (b'symbol', b'ancestors'), (b'symbol', b'1')))
34062
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   349
    [('symbol', '1')]
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   350
    >>> f((b'func', (b'symbol', b'ancestors'), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   351
    ...   (b'func', (b'symbol', b'ancestors'), None))
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   352
    >>> f((b'range', (b'dagrange', _, _), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   353
    ...   (b'range',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   354
    ...     (b'dagrange', (b'symbol', b'1'), (b'symbol', b'2')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   355
    ...     (b'symbol', b'3')))
34062
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   356
    [('symbol', '1'), ('symbol', '2'), ('symbol', '3')]
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   357
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   358
    The placeholder does not match the specified incomplete nodes because
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   359
    an incomplete node (e.g. argument list) cannot construct an expression.
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   360
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   361
    >>> f((b'func', (b'symbol', b'ancestors'), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   362
    ...   (b'func', (b'symbol', b'ancestors'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   363
    ...     (b'list', (b'symbol', b'1'), (b'symbol', b'2'))))
34062
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   364
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   365
    The placeholder may be omitted, but which shouldn't match a None node.
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   366
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   367
    >>> _ = None
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   368
    >>> f((b'func', (b'symbol', b'ancestors'), None),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   369
    ...   (b'func', (b'symbol', b'ancestors'), (b'symbol', b'0')))
34062
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   370
    """
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   371
    if placeholder is not None and not isinstance(placeholder, tuple):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   372
        raise error.ProgrammingError('placeholder must be a node tuple')
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   373
    matches = [tree]
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   374
    if _matchtree(pattern, tree, placeholder, incompletenodes, matches):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   375
        return matches
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34060
diff changeset
   376
28720
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   377
def parseerrordetail(inst):
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   378
    """Compose error message from specified ParseError object
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   379
    """
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   380
    if len(inst.args) > 1:
31362
089e37802fb3 parser: use %d instead of %s for interpolating error position
Augie Fackler <augie@google.com>
parents: 30753
diff changeset
   381
        return _('at %d: %s') % (inst.args[1], inst.args[0])
28720
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   382
    else:
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   383
        return inst.args[0]
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   384
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   385
class alias(object):
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   386
    """Parsed result of alias"""
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   387
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   388
    def __init__(self, name, args, err, replacement):
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   389
        self.name = name
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   390
        self.args = args
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   391
        self.error = err
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   392
        self.replacement = replacement
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   393
        # whether own `error` information is already shown or not.
28898
8d398155bfda revset: rename findaliases() to expandaliases()
Yuya Nishihara <yuya@tcha.org>
parents: 28897
diff changeset
   394
        # this avoids showing same warning multiple times at each
8d398155bfda revset: rename findaliases() to expandaliases()
Yuya Nishihara <yuya@tcha.org>
parents: 28897
diff changeset
   395
        # `expandaliases`.
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   396
        self.warned = False
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   397
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   398
class basealiasrules(object):
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   399
    """Parsing and expansion rule set of aliases
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   400
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   401
    This is a helper for fileset/revset/template aliases. A concrete rule set
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   402
    should be made by sub-classing this and implementing class/static methods.
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   403
30342
318a24b52eeb spelling: fixes of non-dictionary words
Mads Kiilerich <madski@unity3d.com>
parents: 29778
diff changeset
   404
    It supports alias expansion of symbol and function-call styles::
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   405
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   406
        # decl = defn
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   407
        h = heads(default)
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   408
        b($1) = ancestors($1) - ancestors(default)
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   409
    """
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   410
    # typically a config section, which will be included in error messages
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   411
    _section = None
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   412
    # tag of symbol node
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   413
    _symbolnode = 'symbol'
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   414
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   415
    def __new__(cls):
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   416
        raise TypeError("'%s' is not instantiatable" % cls.__name__)
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   417
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   418
    @staticmethod
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   419
    def _parse(spec):
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   420
        """Parse an alias name, arguments and definition"""
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   421
        raise NotImplementedError
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   422
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   423
    @staticmethod
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   424
    def _trygetfunc(tree):
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   425
        """Return (name, args) if tree is a function; otherwise None"""
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   426
        raise NotImplementedError
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   427
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   428
    @classmethod
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   429
    def _builddecl(cls, decl):
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   430
        """Parse an alias declaration into ``(name, args, errorstr)``
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   431
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   432
        This function analyzes the parsed tree. The parsing rule is provided
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   433
        by ``_parse()``.
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   434
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   435
        - ``name``: of declared alias (may be ``decl`` itself at error)
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   436
        - ``args``: list of argument names (or None for symbol declaration)
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   437
        - ``errorstr``: detail about detected error (or None)
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   438
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   439
        >>> sym = lambda x: (b'symbol', x)
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   440
        >>> symlist = lambda *xs: (b'list',) + tuple(sym(x) for x in xs)
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   441
        >>> func = lambda n, a: (b'func', sym(n), a)
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   442
        >>> parsemap = {
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   443
        ...     b'foo': sym(b'foo'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   444
        ...     b'$foo': sym(b'$foo'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   445
        ...     b'foo::bar': (b'dagrange', sym(b'foo'), sym(b'bar')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   446
        ...     b'foo()': func(b'foo', None),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   447
        ...     b'$foo()': func(b'$foo', None),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   448
        ...     b'foo($1, $2)': func(b'foo', symlist(b'$1', b'$2')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   449
        ...     b'foo(bar_bar, baz.baz)':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   450
        ...         func(b'foo', symlist(b'bar_bar', b'baz.baz')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   451
        ...     b'foo(bar($1, $2))':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   452
        ...         func(b'foo', func(b'bar', symlist(b'$1', b'$2'))),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   453
        ...     b'foo($1, $2, nested($1, $2))':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   454
        ...         func(b'foo', (symlist(b'$1', b'$2') +
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   455
        ...                      (func(b'nested', symlist(b'$1', b'$2')),))),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   456
        ...     b'foo("bar")': func(b'foo', (b'string', b'bar')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   457
        ...     b'foo($1, $2': error.ParseError(b'unexpected token: end', 10),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   458
        ...     b'foo("bar': error.ParseError(b'unterminated string', 5),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   459
        ...     b'foo($1, $2, $1)': func(b'foo', symlist(b'$1', b'$2', b'$1')),
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   460
        ... }
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   461
        >>> def parse(expr):
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   462
        ...     x = parsemap[expr]
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   463
        ...     if isinstance(x, Exception):
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   464
        ...         raise x
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   465
        ...     return x
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   466
        >>> def trygetfunc(tree):
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   467
        ...     if not tree or tree[0] != b'func' or tree[1][0] != b'symbol':
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   468
        ...         return None
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   469
        ...     if not tree[2]:
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   470
        ...         return tree[1][1], []
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   471
        ...     if tree[2][0] == b'list':
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   472
        ...         return tree[1][1], list(tree[2][1:])
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   473
        ...     return tree[1][1], [tree[2]]
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   474
        >>> class aliasrules(basealiasrules):
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   475
        ...     _parse = staticmethod(parse)
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   476
        ...     _trygetfunc = staticmethod(trygetfunc)
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   477
        >>> builddecl = aliasrules._builddecl
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   478
        >>> builddecl(b'foo')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   479
        ('foo', None, None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   480
        >>> builddecl(b'$foo')
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   481
        ('$foo', None, "invalid symbol '$foo'")
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   482
        >>> builddecl(b'foo::bar')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   483
        ('foo::bar', None, 'invalid format')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   484
        >>> builddecl(b'foo()')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   485
        ('foo', [], None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   486
        >>> builddecl(b'$foo()')
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   487
        ('$foo()', None, "invalid function '$foo'")
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   488
        >>> builddecl(b'foo($1, $2)')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   489
        ('foo', ['$1', '$2'], None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   490
        >>> builddecl(b'foo(bar_bar, baz.baz)')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   491
        ('foo', ['bar_bar', 'baz.baz'], None)
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   492
        >>> builddecl(b'foo($1, $2, nested($1, $2))')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   493
        ('foo($1, $2, nested($1, $2))', None, 'invalid argument list')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   494
        >>> builddecl(b'foo(bar($1, $2))')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   495
        ('foo(bar($1, $2))', None, 'invalid argument list')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   496
        >>> builddecl(b'foo("bar")')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   497
        ('foo("bar")', None, 'invalid argument list')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   498
        >>> builddecl(b'foo($1, $2')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   499
        ('foo($1, $2', None, 'at 10: unexpected token: end')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   500
        >>> builddecl(b'foo("bar')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   501
        ('foo("bar', None, 'at 5: unterminated string')
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   502
        >>> builddecl(b'foo($1, $2, $1)')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   503
        ('foo', None, 'argument names collide with each other')
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   504
        """
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   505
        try:
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   506
            tree = cls._parse(decl)
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   507
        except error.ParseError as inst:
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   508
            return (decl, None, parseerrordetail(inst))
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   509
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   510
        if tree[0] == cls._symbolnode:
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   511
            # "name = ...." style
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   512
            name = tree[1]
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   513
            if name.startswith('$'):
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   514
                return (decl, None, _("invalid symbol '%s'") % name)
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   515
            return (name, None, None)
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   516
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   517
        func = cls._trygetfunc(tree)
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   518
        if func:
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   519
            # "name(arg, ....) = ...." style
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   520
            name, args = func
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   521
            if name.startswith('$'):
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   522
                return (decl, None, _("invalid function '%s'") % name)
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   523
            if any(t[0] != cls._symbolnode for t in args):
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   524
                return (decl, None, _("invalid argument list"))
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   525
            if len(args) != len(set(args)):
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   526
                return (name, None, _("argument names collide with each other"))
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   527
            return (name, [t[1] for t in args], None)
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   528
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   529
        return (decl, None, _("invalid format"))
28872
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   530
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   531
    @classmethod
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   532
    def _relabelargs(cls, tree, args):
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   533
        """Mark alias arguments as ``_aliasarg``"""
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   534
        if not isinstance(tree, tuple):
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   535
            return tree
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   536
        op = tree[0]
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   537
        if op != cls._symbolnode:
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   538
            return (op,) + tuple(cls._relabelargs(x, args) for x in tree[1:])
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   539
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   540
        assert len(tree) == 2
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   541
        sym = tree[1]
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   542
        if sym in args:
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   543
            op = '_aliasarg'
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   544
        elif sym.startswith('$'):
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   545
            raise error.ParseError(_("invalid symbol '%s'") % sym)
28872
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   546
        return (op, sym)
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   547
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   548
    @classmethod
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   549
    def _builddefn(cls, defn, args):
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   550
        """Parse an alias definition into a tree and marks substitutions
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   551
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   552
        This function marks alias argument references as ``_aliasarg``. The
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   553
        parsing rule is provided by ``_parse()``.
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   554
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   555
        ``args`` is a list of alias argument names, or None if the alias
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   556
        is declared as a symbol.
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   557
34152
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   558
        >>> from . import pycompat
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   559
        >>> parsemap = {
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   560
        ...     b'$1 or foo': (b'or', (b'symbol', b'$1'), (b'symbol', b'foo')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   561
        ...     b'$1 or $bar':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   562
        ...         (b'or', (b'symbol', b'$1'), (b'symbol', b'$bar')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   563
        ...     b'$10 or baz':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   564
        ...         (b'or', (b'symbol', b'$10'), (b'symbol', b'baz')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   565
        ...     b'"$1" or "foo"':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   566
        ...         (b'or', (b'string', b'$1'), (b'string', b'foo')),
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   567
        ... }
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   568
        >>> class aliasrules(basealiasrules):
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   569
        ...     _parse = staticmethod(parsemap.__getitem__)
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   570
        ...     _trygetfunc = staticmethod(lambda x: None)
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   571
        >>> builddefn = aliasrules._builddefn
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   572
        >>> def pprint(tree):
34152
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   573
        ...     s = prettyformat(tree, (b'_aliasarg', b'string', b'symbol'))
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   574
        ...     print(pycompat.sysstr(s))
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   575
        >>> args = [b'$1', b'$2', b'foo']
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   576
        >>> pprint(builddefn(b'$1 or foo', args))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   577
        (or
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   578
          (_aliasarg '$1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   579
          (_aliasarg 'foo'))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   580
        >>> try:
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   581
        ...     builddefn(b'$1 or $bar', args)
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   582
        ... except error.ParseError as inst:
34152
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34146
diff changeset
   583
        ...     print(pycompat.sysstr(parseerrordetail(inst)))
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   584
        invalid symbol '$bar'
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   585
        >>> args = [b'$1', b'$10', b'foo']
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   586
        >>> pprint(builddefn(b'$10 or baz', args))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   587
        (or
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   588
          (_aliasarg '$10')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   589
          (symbol 'baz'))
34146
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34090
diff changeset
   590
        >>> pprint(builddefn(b'"$1" or "foo"', args))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   591
        (or
34090
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   592
          (string '$1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34062
diff changeset
   593
          (string 'foo'))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   594
        """
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   595
        tree = cls._parse(defn)
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   596
        if args:
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   597
            args = set(args)
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   598
        else:
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   599
            args = set()
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   600
        return cls._relabelargs(tree, args)
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   601
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   602
    @classmethod
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   603
    def build(cls, decl, defn):
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   604
        """Parse an alias declaration and definition into an alias object"""
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   605
        repl = efmt = None
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   606
        name, args, err = cls._builddecl(decl)
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   607
        if err:
29059
8eba4cdcfd81 parser: shorten prefix of alias parsing errors
Yuya Nishihara <yuya@tcha.org>
parents: 29058
diff changeset
   608
            efmt = _('bad declaration of %(section)s "%(name)s": %(error)s')
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   609
        else:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   610
            try:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   611
                repl = cls._builddefn(defn, args)
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   612
            except error.ParseError as inst:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   613
                err = parseerrordetail(inst)
29059
8eba4cdcfd81 parser: shorten prefix of alias parsing errors
Yuya Nishihara <yuya@tcha.org>
parents: 29058
diff changeset
   614
                efmt = _('bad definition of %(section)s "%(name)s": %(error)s')
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   615
        if err:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   616
            err = efmt % {'section': cls._section, 'name': name, 'error': err}
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   617
        return alias(name, args, err, repl)
28893
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   618
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   619
    @classmethod
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   620
    def buildmap(cls, items):
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   621
        """Parse a list of alias (name, replacement) pairs into a dict of
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   622
        alias objects"""
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   623
        aliases = {}
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   624
        for decl, defn in items:
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   625
            a = cls.build(decl, defn)
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   626
            aliases[a.name] = a
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   627
        return aliases
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   628
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   629
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   630
    def _getalias(cls, aliases, tree):
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   631
        """If tree looks like an unexpanded alias, return (alias, pattern-args)
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   632
        pair. Return None otherwise.
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   633
        """
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   634
        if not isinstance(tree, tuple):
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   635
            return None
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   636
        if tree[0] == cls._symbolnode:
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   637
            name = tree[1]
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   638
            a = aliases.get(name)
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   639
            if a and a.args is None:
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   640
                return a, None
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   641
        func = cls._trygetfunc(tree)
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   642
        if func:
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   643
            name, args = func
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   644
            a = aliases.get(name)
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   645
            if a and a.args is not None:
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   646
                return a, args
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   647
        return None
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   648
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   649
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   650
    def _expandargs(cls, tree, args):
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   651
        """Replace _aliasarg instances with the substitution value of the
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   652
        same name in args, recursively.
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   653
        """
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   654
        if not isinstance(tree, tuple):
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   655
            return tree
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   656
        if tree[0] == '_aliasarg':
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   657
            sym = tree[1]
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   658
            return args[sym]
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   659
        return tuple(cls._expandargs(t, args) for t in tree)
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   660
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   661
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   662
    def _expand(cls, aliases, tree, expanding, cache):
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   663
        if not isinstance(tree, tuple):
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   664
            return tree
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   665
        r = cls._getalias(aliases, tree)
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   666
        if r is None:
28896
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   667
            return tuple(cls._expand(aliases, t, expanding, cache)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   668
                         for t in tree)
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   669
        a, l = r
28896
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   670
        if a.error:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   671
            raise error.Abort(a.error)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   672
        if a in expanding:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   673
            raise error.ParseError(_('infinite expansion of %(section)s '
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   674
                                     '"%(name)s" detected')
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   675
                                   % {'section': cls._section, 'name': a.name})
28897
c1f254138f44 parser: add short comment how aliases are expanded in phases
Yuya Nishihara <yuya@tcha.org>
parents: 28896
diff changeset
   676
        # get cacheable replacement tree by expanding aliases recursively
28896
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   677
        expanding.append(a)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   678
        if a.name not in cache:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   679
            cache[a.name] = cls._expand(aliases, a.replacement, expanding,
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   680
                                        cache)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   681
        result = cache[a.name]
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   682
        expanding.pop()
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   683
        if a.args is None:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   684
            return result
28897
c1f254138f44 parser: add short comment how aliases are expanded in phases
Yuya Nishihara <yuya@tcha.org>
parents: 28896
diff changeset
   685
        # substitute function arguments in replacement tree
28896
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   686
        if len(l) != len(a.args):
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   687
            raise error.ParseError(_('invalid number of arguments: %d')
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   688
                                   % len(l))
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   689
        l = [cls._expand(aliases, t, [], cache) for t in l]
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   690
        return cls._expandargs(result, dict(zip(a.args, l)))
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   691
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   692
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   693
    def expand(cls, aliases, tree):
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   694
        """Expand aliases in tree, recursively.
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   695
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   696
        'aliases' is a dictionary mapping user defined aliases to alias objects.
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   697
        """
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   698
        return cls._expand(aliases, tree, [], {})