mercurial/parser.py
author Jun Wu <quark@fb.com>
Wed, 20 Dec 2017 02:13:35 -0800
changeset 35460 8652ab4046e4
parent 34137 a8994d08e4a2
child 36547 7840d8bd0558
permissions -rw-r--r--
osutil: add a function to unblock signals Signals could be blocked by something like: #include <unistd.h> #include <signal.h> int main(int argc, char * const argv[]) { sigset_t set; sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); execv("/bin/hg", argv); return 0; } One of the problems is if SIGCHLD is blocked, chgserver would not reap zombie workers since it depends on SIGCHLD handler entirely. While it's the parent process to blame but it seems a good idea to just unblock the signal from hg. FWIW git does that for SIGPIPE already [1]. Unfortunately Python 2 does not reset or provide APIs to change signal masks. Therefore let's add one in osutil. Note: Python 3.3 introduced `signal.pthread_sigmask` which solves the problem. `sigprocmask` is part of POSIX [2] so there is no feature testing in `setup.py`. [1]: https://github.com/git/git/commit/7559a1be8a0afb10df41d25e4cf4c5285a5faef1 [2]: http://pubs.opengroup.org/onlinepubs/7908799/xsh/sigprocmask.html Differential Revision: https://phab.mercurial-scm.org/D1736
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
34137
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34131
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 _
31484
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31353
diff changeset
    22
from . import (
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
    23
    encoding,
31484
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31353
diff changeset
    24
    error,
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31353
diff changeset
    25
    util,
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31353
diff changeset
    26
)
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
    27
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    28
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
    29
    def __init__(self, elements, methods=None):
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    30
        self._elements = elements
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    31
        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
    32
        self.current = None
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    33
    def _advance(self):
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    34
        'advance the tokenizer'
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    35
        t = self.current
25171
d647f97f88dd parsers: use 'next' instead of try/except
Pierre-Yves David <pierre-yves.david@fb.com>
parents: 20778
diff changeset
    36
        self.current = next(self._iter, None)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    37
        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
    38
    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
    39
        '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
    40
        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
    41
    def _match(self, m):
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    42
        'make sure the tokenizer matches an end condition'
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    43
        if self.current[0] != m:
14701
4b93bd041772 parsers: fix localization markup of parser errors
Mads Kiilerich <mads@kiilerich.com>
parents: 13665
diff changeset
    44
            raise error.ParseError(_("unexpected token: %s") % self.current[0],
11305
d4cafcb63f77 cleanups: undefined variables
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 11289
diff changeset
    45
                                   self.current[2])
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    46
        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
    47
    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
    48
        '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
    49
        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
    50
            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
    51
        else:
b3004d273874 parser: factor out function that parses right-hand side of prefix/infix ops
Yuya Nishihara <yuya@tcha.org>
parents: 25802
diff changeset
    52
            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
    53
        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
    54
            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
    55
        return expr
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    56
    def _parse(self, bind=0):
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
    57
        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
    58
        # 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
    59
        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
    60
        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
    61
            expr = (primary, value)
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    62
        elif prefix:
e71e5629e006 parser: separate actions for primary expression and prefix operator
Yuya Nishihara <yuya@tcha.org>
parents: 25804
diff changeset
    63
            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
    64
        else:
14701
4b93bd041772 parsers: fix localization markup of parser errors
Mads Kiilerich <mads@kiilerich.com>
parents: 13665
diff changeset
    65
            raise error.ParseError(_("not a prefix: %s") % token, pos)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    66
        # gather tokens until we meet a lower binding strength
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    67
        while bind < self._elements[self.current[0]][0]:
11289
4215ce511134 revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents: 11278
diff changeset
    68
            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
    69
            # 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
    70
            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
    71
            if suffix and not (infix and self._hasnewterm()):
29767
e5b794063fd4 parser: remove unused binding parameter from suffix action
Yuya Nishihara <yuya@tcha.org>
parents: 29059
diff changeset
    72
                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
    73
            elif infix:
42ac9d1d1572 parser: reorder infix/suffix handling to be similar to prefix/primary flow
Yuya Nishihara <yuya@tcha.org>
parents: 25816
diff changeset
    74
                expr = (infix[0], expr, self._parseoperand(*infix[1:]))
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    75
            else:
25817
42ac9d1d1572 parser: reorder infix/suffix handling to be similar to prefix/primary flow
Yuya Nishihara <yuya@tcha.org>
parents: 25816
diff changeset
    76
                raise error.ParseError(_("not an infix: %s") % token, pos)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    77
        return expr
25654
af329a84310c parser: accept iterator of tokens instead of tokenizer function and program
Yuya Nishihara <yuya@tcha.org>
parents: 25306
diff changeset
    78
    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
    79
        '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
    80
        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
    81
        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
    82
        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
    83
        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
    84
        return res, pos
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    85
    def eval(self, tree):
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    86
        'recursively evaluate a parse tree using node methods'
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    87
        if not isinstance(tree, tuple):
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    88
            return tree
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    89
        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
    90
    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
    91
        '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
    92
        t = self.parse(tokeniter)
11274
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    93
        if self._methods:
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    94
            return self.eval(t)
77272d28b53f revset: introduce basic parser
Matt Mackall <mpm@selenic.com>
parents:
diff changeset
    95
        return t
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
    96
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
    97
def splitargspec(spec):
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
    98
    """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
    99
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   100
    >>> splitargspec(b'')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   101
    ([], None, [], None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   102
    >>> splitargspec(b'foo bar')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   103
    ([], None, ['foo', 'bar'], None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   104
    >>> 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
   105
    (['foo'], 'bar', ['baz'], 'qux')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   106
    >>> splitargspec(b'*foo')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   107
    ([], 'foo', [], None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   108
    >>> splitargspec(b'**foo')
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   109
    ([], None, [], 'foo')
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   110
    """
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   111
    optkey = None
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   112
    pre, sep, post = spec.partition('**')
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   113
    if sep:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   114
        posts = post.split()
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   115
        if not posts:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   116
            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
   117
        if len(posts) > 1:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   118
            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
   119
        optkey = posts[0]
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   120
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   121
    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
   122
    pres = pre.split()
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   123
    posts = post.split()
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   124
    if sep:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   125
        if not posts:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   126
            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
   127
        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
   128
    return [], None, pres, optkey
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   129
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   130
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
   131
    """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
   132
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   133
    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
   134
    where
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   135
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   136
    - ``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
   137
    - ``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
   138
    - ``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
   139
    - ``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
   140
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   141
    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
   142
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   143
    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
   144
    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
   145
    """
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   146
    poskeys, varkey, keys, optkey = argspec
30752
ffd324eaf994 parser: make buildargsdict() precompute position where keyword args start
Yuya Nishihara <yuya@tcha.org>
parents: 30332
diff changeset
   147
    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: 30332
diff changeset
   148
                   len(trees))
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   149
    if kwstart < len(poskeys):
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   150
        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
   151
                                 "arguments")
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   152
                               % {'func': funcname, 'nargs': len(poskeys)})
31920
a98540ea1e42 parser: verify excessive number of args excluding kwargs in buildargsdict()
Yuya Nishihara <yuya@tcha.org>
parents: 31484
diff changeset
   153
    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: 31484
diff changeset
   154
        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: 31484
diff changeset
   155
                                 "arguments")
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   156
                               % {'func': funcname,
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   157
                                  'nargs': len(poskeys) + len(keys)})
31922
0f41f1e3c75c parser: preserve order of keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 31921
diff changeset
   158
    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
   159
    # consume positional arguments
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   160
    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
   161
        args[k] = x
30753
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   162
    if varkey:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   163
        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
   164
    else:
c3a3896a9fa8 parser: extend buildargsdict() to support variable-length positional args
Yuya Nishihara <yuya@tcha.org>
parents: 30752
diff changeset
   165
        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
   166
            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
   167
    # 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
   168
    if optkey:
31922
0f41f1e3c75c parser: preserve order of keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 31921
diff changeset
   169
        args[optkey] = util.sortdict()
30752
ffd324eaf994 parser: make buildargsdict() precompute position where keyword args start
Yuya Nishihara <yuya@tcha.org>
parents: 30332
diff changeset
   170
    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
   171
        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
   172
            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
   173
                                   % {'func': funcname})
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   174
        k = x[1][1]
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   175
        if k in keys:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   176
            d = args
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   177
        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
   178
            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
   179
                                     "argument '%(key)s'")
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   180
                                   % {'func': funcname, 'key': k})
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   181
        else:
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   182
            d = args[optkey]
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   183
        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
   184
            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
   185
                                     "argument '%(key)s'")
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   186
                                   % {'func': funcname, 'key': k})
31921
2156934b7917 parser: extend buildargsdict() to support arbitrary number of **kwargs
Yuya Nishihara <yuya@tcha.org>
parents: 31920
diff changeset
   187
        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
   188
    return args
48919d246a47 revset: add function to build dict of positional and keyword arguments
Yuya Nishihara <yuya@tcha.org>
parents: 25655
diff changeset
   189
26231
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   190
def unescapestr(s):
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   191
    try:
31484
afb335353d28 util: wrap s.decode('string_escape') calls for future py3 compatibility
Yuya Nishihara <yuya@tcha.org>
parents: 31353
diff changeset
   192
        return util.unescapestr(s)
26231
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   193
    except ValueError as e:
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   194
        # mangle Python's exception into our format
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   195
        raise error.ParseError(str(e).lower())
87c9c562c37a parser: move unescape helper from templater
Yuya Nishihara <yuya@tcha.org>
parents: 25963
diff changeset
   196
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   197
def _brepr(obj):
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   198
    if isinstance(obj, bytes):
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   199
        return b"'%s'" % util.escapestr(obj)
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   200
    return encoding.strtolocal(repr(obj))
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   201
25254
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   202
def _prettyformat(tree, leafnodes, level, lines):
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   203
    if not isinstance(tree, tuple):
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   204
        lines.append((level, _brepr(tree)))
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   205
    elif tree[0] in leafnodes:
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   206
        rs = map(_brepr, tree[1:])
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   207
        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
   208
    else:
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   209
        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
   210
        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
   211
            _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
   212
        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
   213
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   214
def prettyformat(tree, leafnodes):
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   215
    lines = []
25254
060bdfef2517 parser: extract closure of prettyformat() to a top-level function
Yuya Nishihara <yuya@tcha.org>
parents: 25253
diff changeset
   216
    _prettyformat(tree, leafnodes, 0, lines)
25253
3f1a9b44b8c2 parser: move prettyformat() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 25171
diff changeset
   217
    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
   218
    return output
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   219
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   220
def simplifyinfixops(tree, targetnodes):
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   221
    """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
   222
34137
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   223
    >>> from . import pycompat
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   224
    >>> def f(tree):
34137
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   225
    ...     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: 34131
diff changeset
   226
    ...     print(pycompat.sysstr(s))
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   227
    >>> f((b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   228
    ...     (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   229
    ...       (b'symbol', b'1'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   230
    ...       (b'symbol', b'2')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   231
    ...     (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
   232
    (or
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   233
      (symbol '1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   234
      (symbol '2')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   235
      (symbol '3'))
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   236
    >>> f((b'func',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   237
    ...     (b'symbol', b'p1'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   238
    ...     (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   239
    ...       (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   240
    ...         (b'func',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   241
    ...           (b'symbol', b'sort'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   242
    ...           (b'list',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   243
    ...             (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   244
    ...               (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   245
    ...                 (b'symbol', b'1'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   246
    ...                 (b'symbol', b'2')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   247
    ...               (b'symbol', b'3')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   248
    ...             (b'negate',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   249
    ...               (b'symbol', b'rev')))),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   250
    ...         (b'and',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   251
    ...           (b'symbol', b'4'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   252
    ...           (b'group',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   253
    ...             (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   254
    ...               (b'or',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   255
    ...                 (b'symbol', b'5'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   256
    ...                 (b'symbol', b'6')),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   257
    ...               (b'symbol', b'7'))))),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   258
    ...       (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
   259
    (func
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   260
      (symbol 'p1')
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   261
      (or
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   262
        (func
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   263
          (symbol 'sort')
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   264
          (list
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   265
            (or
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   266
              (symbol '1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   267
              (symbol '2')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   268
              (symbol '3'))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   269
            (negate
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   270
              (symbol 'rev'))))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   271
        (and
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   272
          (symbol '4')
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   273
          (group
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   274
            (or
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   275
              (symbol '5')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   276
              (symbol '6')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   277
              (symbol '7'))))
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   278
        (symbol '8')))
25306
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   279
    """
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   280
    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
   281
        return tree
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   282
    op = tree[0]
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   283
    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
   284
        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
   285
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   286
    # 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
   287
    # 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
   288
    # 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
   289
    simplified = []
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   290
    x = tree
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   291
    while x[0] == op:
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   292
        l, r = x[1:]
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   293
        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
   294
        x = l
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   295
    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
   296
    simplified.append(op)
c87b05925054 parser: add helper to reduce nesting of chained infix operations
Yuya Nishihara <yuya@tcha.org>
parents: 25254
diff changeset
   297
    return tuple(reversed(simplified))
28720
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   298
34043
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   299
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
   300
    if template == placeholder:
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   301
        return replstack.pop()
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   302
    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
   303
        return template
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   304
    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
   305
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   306
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
   307
    """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
   308
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   309
    >>> _ = (b'symbol', b'_')
34043
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   310
    >>> def f(template, *repls):
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   311
    ...     return buildtree(template, _, *repls)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   312
    >>> f((b'func', (b'symbol', b'only'), (b'list', _, _)),
34043
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   313
    ...   ('symbol', '1'), ('symbol', '2'))
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   314
    ('func', ('symbol', 'only'), ('list', ('symbol', '1'), ('symbol', '2')))
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   315
    >>> f((b'and', _, (b'not', _)), (b'symbol', b'1'), (b'symbol', b'2'))
34043
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   316
    ('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
   317
    """
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   318
    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
   319
        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
   320
    replstack = list(reversed(repls))
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   321
    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
   322
    if replstack:
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   323
        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
   324
    return r
90896b61fe26 parser: add helper function that constructs parsed tree from template
Yuya Nishihara <yuya@tcha.org>
parents: 31922
diff changeset
   325
34045
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   326
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: 34043
diff changeset
   327
    if pattern == tree:
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   328
        return True
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   329
    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: 34043
diff changeset
   330
        return False
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   331
    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: 34043
diff changeset
   332
        matches.append(tree)
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   333
        return True
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   334
    if len(pattern) != len(tree):
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   335
        return False
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   336
    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: 34043
diff changeset
   337
               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: 34043
diff changeset
   338
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   339
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: 34043
diff changeset
   340
    """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: 34043
diff changeset
   341
    matched with the placeholder; Otherwise None
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   342
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   343
    >>> def f(pattern, tree):
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   344
    ...     m = matchtree(pattern, tree, _, {b'keyvalue', b'list'})
34045
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   345
    ...     if m:
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   346
    ...         return m[1:]
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   347
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   348
    >>> _ = (b'symbol', b'_')
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   349
    >>> f((b'func', (b'symbol', b'ancestors'), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   350
    ...   (b'func', (b'symbol', b'ancestors'), (b'symbol', b'1')))
34045
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   351
    [('symbol', '1')]
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   352
    >>> f((b'func', (b'symbol', b'ancestors'), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   353
    ...   (b'func', (b'symbol', b'ancestors'), None))
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   354
    >>> f((b'range', (b'dagrange', _, _), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   355
    ...   (b'range',
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   356
    ...     (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: 34073
diff changeset
   357
    ...     (b'symbol', b'3')))
34045
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   358
    [('symbol', '1'), ('symbol', '2'), ('symbol', '3')]
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   359
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   360
    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: 34043
diff changeset
   361
    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: 34043
diff changeset
   362
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   363
    >>> f((b'func', (b'symbol', b'ancestors'), _),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   364
    ...   (b'func', (b'symbol', b'ancestors'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   365
    ...     (b'list', (b'symbol', b'1'), (b'symbol', b'2'))))
34045
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   366
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   367
    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: 34043
diff changeset
   368
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   369
    >>> _ = None
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   370
    >>> 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: 34073
diff changeset
   371
    ...   (b'func', (b'symbol', b'ancestors'), (b'symbol', b'0')))
34045
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   372
    """
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   373
    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: 34043
diff changeset
   374
        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: 34043
diff changeset
   375
    matches = [tree]
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   376
    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: 34043
diff changeset
   377
        return matches
79681d8ee587 parser: add helper function to test if pattern matches parsed tree
Yuya Nishihara <yuya@tcha.org>
parents: 34043
diff changeset
   378
28720
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   379
def parseerrordetail(inst):
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   380
    """Compose error message from specified ParseError object
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   381
    """
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   382
    if len(inst.args) > 1:
31353
089e37802fb3 parser: use %d instead of %s for interpolating error position
Augie Fackler <augie@google.com>
parents: 30753
diff changeset
   383
        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
   384
    else:
639e0f1e8ffa parser: move parsererrordetail() function from revset module
Yuya Nishihara <yuya@tcha.org>
parents: 26231
diff changeset
   385
        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
   386
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   387
class alias(object):
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   388
    """Parsed result of alias"""
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   389
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   390
    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
   391
        self.name = name
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   392
        self.args = args
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   393
        self.error = err
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   394
        self.replacement = replacement
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   395
        # 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
   396
        # this avoids showing same warning multiple times at each
8d398155bfda revset: rename findaliases() to expandaliases()
Yuya Nishihara <yuya@tcha.org>
parents: 28897
diff changeset
   397
        # `expandaliases`.
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   398
        self.warned = False
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   399
28870
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   400
class basealiasrules(object):
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   401
    """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
   402
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   403
    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
   404
    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
   405
30332
318a24b52eeb spelling: fixes of non-dictionary words
Mads Kiilerich <madski@unity3d.com>
parents: 29767
diff changeset
   406
    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
   407
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   408
        # decl = defn
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   409
        h = heads(default)
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   410
        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
   411
    """
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   412
    # 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
   413
    _section = None
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   414
    # 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
   415
    _symbolnode = 'symbol'
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   416
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   417
    def __new__(cls):
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   418
        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
   419
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   420
    @staticmethod
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   421
    def _parse(spec):
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   422
        """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
   423
        raise NotImplementedError
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   424
475dad3432fd parser: add stub class that will host alias parsing and expansion
Yuya Nishihara <yuya@tcha.org>
parents: 28720
diff changeset
   425
    @staticmethod
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   426
    def _trygetfunc(tree):
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   427
        """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
   428
        raise NotImplementedError
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   429
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   430
    @classmethod
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   431
    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
   432
        """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
   433
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   434
        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
   435
        by ``_parse()``.
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   436
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   437
        - ``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
   438
        - ``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
   439
        - ``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
   440
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   441
        >>> sym = lambda x: (b'symbol', x)
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   442
        >>> 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: 34073
diff changeset
   443
        >>> 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
   444
        >>> parsemap = {
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   445
        ...     b'foo': sym(b'foo'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   446
        ...     b'$foo': sym(b'$foo'),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   447
        ...     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: 34073
diff changeset
   448
        ...     b'foo()': func(b'foo', None),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   449
        ...     b'$foo()': func(b'$foo', None),
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   450
        ...     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: 34073
diff changeset
   451
        ...     b'foo(bar_bar, baz.baz)':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   452
        ...         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: 34073
diff changeset
   453
        ...     b'foo(bar($1, $2))':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   454
        ...         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: 34073
diff changeset
   455
        ...     b'foo($1, $2, nested($1, $2))':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   456
        ...         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: 34073
diff changeset
   457
        ...                      (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: 34073
diff changeset
   458
        ...     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: 34073
diff changeset
   459
        ...     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: 34073
diff changeset
   460
        ...     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: 34073
diff changeset
   461
        ...     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
   462
        ... }
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   463
        >>> def parse(expr):
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   464
        ...     x = parsemap[expr]
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   465
        ...     if isinstance(x, Exception):
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   466
        ...         raise x
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   467
        ...     return x
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   468
        >>> def trygetfunc(tree):
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   469
        ...     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
   470
        ...         return None
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   471
        ...     if not tree[2]:
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], []
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   473
        ...     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
   474
        ...         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
   475
        ...     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
   476
        >>> class aliasrules(basealiasrules):
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   477
        ...     _parse = staticmethod(parse)
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   478
        ...     _trygetfunc = staticmethod(trygetfunc)
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   479
        >>> builddecl = aliasrules._builddecl
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   480
        >>> builddecl(b'foo')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   481
        ('foo', None, None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   482
        >>> builddecl(b'$foo')
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   483
        ('$foo', None, "invalid symbol '$foo'")
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   484
        >>> 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
   485
        ('foo::bar', None, 'invalid format')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   486
        >>> builddecl(b'foo()')
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   487
        ('foo', [], None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   488
        >>> builddecl(b'$foo()')
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   489
        ('$foo()', None, "invalid function '$foo'")
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   490
        >>> 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
   491
        ('foo', ['$1', '$2'], None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   492
        >>> 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
   493
        ('foo', ['bar_bar', 'baz.baz'], None)
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   494
        >>> 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
   495
        ('foo($1, $2, nested($1, $2))', None, 'invalid argument list')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   496
        >>> 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
   497
        ('foo(bar($1, $2))', None, 'invalid argument list')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   498
        >>> 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
   499
        ('foo("bar")', None, 'invalid argument list')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   500
        >>> 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
   501
        ('foo($1, $2', None, 'at 10: unexpected token: end')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   502
        >>> 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
   503
        ('foo("bar', None, 'at 5: unterminated string')
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   504
        >>> 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
   505
        ('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
   506
        """
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   507
        try:
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   508
            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
   509
        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
   510
            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
   511
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   512
        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
   513
            # "name = ...." style
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   514
            name = tree[1]
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   515
            if name.startswith('$'):
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   516
                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
   517
            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
   518
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   519
        func = cls._trygetfunc(tree)
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   520
        if func:
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   521
            # "name(arg, ....) = ...." style
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   522
            name, args = func
28871
6d6201fc5aae parser: move alias declaration parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28870
diff changeset
   523
            if name.startswith('$'):
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   524
                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
   525
            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
   526
                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
   527
            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
   528
                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
   529
            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
   530
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   531
        return (decl, None, _("invalid format"))
28872
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   532
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   533
    @classmethod
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   534
    def _relabelargs(cls, tree, args):
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   535
        """Mark alias arguments as ``_aliasarg``"""
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   536
        if not isinstance(tree, tuple):
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   537
            return tree
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   538
        op = tree[0]
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   539
        if op != cls._symbolnode:
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   540
            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
   541
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   542
        assert len(tree) == 2
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   543
        sym = tree[1]
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   544
        if sym in args:
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   545
            op = '_aliasarg'
5f31d2248745 parser: move _relabelaliasargs() to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28871
diff changeset
   546
        elif sym.startswith('$'):
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   547
            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
   548
        return (op, sym)
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   549
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   550
    @classmethod
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   551
    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
   552
        """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
   553
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   554
        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
   555
        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
   556
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   557
        ``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
   558
        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
   559
34137
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   560
        >>> from . import pycompat
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   561
        >>> parsemap = {
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   562
        ...     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: 34073
diff changeset
   563
        ...     b'$1 or $bar':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   564
        ...         (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: 34073
diff changeset
   565
        ...     b'$10 or baz':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   566
        ...         (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: 34073
diff changeset
   567
        ...     b'"$1" or "foo"':
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   568
        ...         (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
   569
        ... }
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   570
        >>> class aliasrules(basealiasrules):
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   571
        ...     _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
   572
        ...     _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
   573
        >>> builddefn = aliasrules._builddefn
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   574
        >>> def pprint(tree):
34137
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   575
        ...     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: 34131
diff changeset
   576
        ...     print(pycompat.sysstr(s))
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   577
        >>> args = [b'$1', b'$2', b'foo']
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   578
        >>> 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
   579
        (or
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   580
          (_aliasarg '$1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   581
          (_aliasarg 'foo'))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   582
        >>> try:
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   583
        ...     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
   584
        ... except error.ParseError as inst:
34137
a8994d08e4a2 doctest: use print_function and convert bytes to unicode where needed
Yuya Nishihara <yuya@tcha.org>
parents: 34131
diff changeset
   585
        ...     print(pycompat.sysstr(parseerrordetail(inst)))
29058
dbed4c4f48ae parser: rephrase "'$' not for alias arguments" message
Yuya Nishihara <yuya@tcha.org>
parents: 28910
diff changeset
   586
        invalid symbol '$bar'
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   587
        >>> args = [b'$1', b'$10', b'foo']
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   588
        >>> 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
   589
        (or
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   590
          (_aliasarg '$10')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   591
          (symbol 'baz'))
34131
0fa781320203 doctest: bulk-replace string literals with b'' for Python 3
Yuya Nishihara <yuya@tcha.org>
parents: 34073
diff changeset
   592
        >>> 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
   593
        (or
34073
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   594
          (string '$1')
7bbc4e113e5f parser: stabilize output of prettyformat() by using byte-safe repr()
Yuya Nishihara <yuya@tcha.org>
parents: 34045
diff changeset
   595
          (string 'foo'))
28873
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   596
        """
28875
2e9f5453ab5a parser: unify parser function of alias declaration and definition
Yuya Nishihara <yuya@tcha.org>
parents: 28873
diff changeset
   597
        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
   598
        if args:
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   599
            args = set(args)
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   600
        else:
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   601
            args = set()
2ca3b7c563f3 parser: move alias definition parser to common rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28872
diff changeset
   602
        return cls._relabelargs(tree, args)
28892
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   603
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   604
    @classmethod
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   605
    def build(cls, decl, defn):
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   606
        """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
   607
        repl = efmt = None
28908
7a772deffa12 parser: drop redundant comparison between alias declaration tree and pattern
Yuya Nishihara <yuya@tcha.org>
parents: 28898
diff changeset
   608
        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
   609
        if err:
29059
8eba4cdcfd81 parser: shorten prefix of alias parsing errors
Yuya Nishihara <yuya@tcha.org>
parents: 29058
diff changeset
   610
            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
   611
        else:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   612
            try:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   613
                repl = cls._builddefn(defn, args)
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   614
            except error.ParseError as inst:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   615
                err = parseerrordetail(inst)
29059
8eba4cdcfd81 parser: shorten prefix of alias parsing errors
Yuya Nishihara <yuya@tcha.org>
parents: 29058
diff changeset
   616
                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
   617
        if err:
0c135f37c6f8 parser: construct alias object by rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28875
diff changeset
   618
            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
   619
        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
   620
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   621
    @classmethod
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   622
    def buildmap(cls, items):
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   623
        """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
   624
        alias objects"""
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   625
        aliases = {}
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   626
        for decl, defn in items:
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   627
            a = cls.build(decl, defn)
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   628
            aliases[a.name] = a
ee11167fe1da parser: extract helper that creates a dict of aliases
Yuya Nishihara <yuya@tcha.org>
parents: 28892
diff changeset
   629
        return aliases
28895
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   630
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   631
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   632
    def _getalias(cls, aliases, tree):
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   633
        """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
   634
        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
   635
        """
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   636
        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
   637
            return None
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   638
        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
   639
            name = tree[1]
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   640
            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
   641
            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
   642
                return a, None
28910
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   643
        func = cls._trygetfunc(tree)
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   644
        if func:
1203159c8928 parser: factor out _trygetfunc() that extracts function name and arguments
Yuya Nishihara <yuya@tcha.org>
parents: 28909
diff changeset
   645
            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
   646
            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
   647
            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
   648
                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
   649
        return None
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   650
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   651
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   652
    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
   653
        """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
   654
        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
   655
        """
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   656
        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
   657
            return tree
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   658
        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
   659
            sym = tree[1]
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   660
            return args[sym]
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   661
        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
   662
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   663
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   664
    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
   665
        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
   666
            return tree
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   667
        r = cls._getalias(aliases, tree)
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   668
        if r is None:
28896
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   669
            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
   670
                         for t in tree)
28909
edbffdc7f6a0 parser: make _getalias() return (alias, pattern-args) pair
Yuya Nishihara <yuya@tcha.org>
parents: 28908
diff changeset
   671
        a, l = r
28896
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   672
        if a.error:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   673
            raise error.Abort(a.error)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   674
        if a in expanding:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   675
            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
   676
                                     '"%(name)s" detected')
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   677
                                   % {'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
   678
        # 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
   679
        expanding.append(a)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   680
        if a.name not in cache:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   681
            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
   682
                                        cache)
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   683
        result = cache[a.name]
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   684
        expanding.pop()
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   685
        if a.args is None:
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   686
            return result
28897
c1f254138f44 parser: add short comment how aliases are expanded in phases
Yuya Nishihara <yuya@tcha.org>
parents: 28896
diff changeset
   687
        # 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
   688
        if len(l) != len(a.args):
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   689
            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
   690
                                   % len(l))
4c76a032ec7e parser: reorder alias expansion routine to return early
Yuya Nishihara <yuya@tcha.org>
parents: 28895
diff changeset
   691
        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
   692
        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
   693
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   694
    @classmethod
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   695
    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
   696
        """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
   697
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   698
        '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
   699
        """
4bf9ed7a260e parser: move functions that process alias expansion to rule-set class
Yuya Nishihara <yuya@tcha.org>
parents: 28893
diff changeset
   700
        return cls._expand(aliases, tree, [], {})