# HG changeset patch # User Yuya Nishihara # Date 1456751744 -32400 # Node ID 4bf9ed7a260e377004e3344e941838c76972881a # Parent 0f59674dc9ffd2e336462c0013e15b5fbbc3b439 parser: move functions that process alias expansion to rule-set class They will be commonly used by revset and templater. It isn't easy to understand how _expand() works, so I'll add comments by a follow-up patch. The local variable 'alias' is renamed to 'a' to avoid shadowing the global 'alias' class. diff -r 0f59674dc9ff -r 4bf9ed7a260e mercurial/parser.py --- a/mercurial/parser.py Mon Feb 29 22:10:48 2016 +0900 +++ b/mercurial/parser.py Mon Feb 29 22:15:44 2016 +0900 @@ -472,3 +472,73 @@ a = cls.build(decl, defn) aliases[a.name] = a return aliases + + @classmethod + def _getalias(cls, aliases, tree): + """If tree looks like an unexpanded alias, return it. Return None + otherwise. + """ + if not isinstance(tree, tuple): + return None + if tree[0] == cls._symbolnode: + name = tree[1] + a = aliases.get(name) + if a and a.args is None and a.tree == tree: + return a + if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode: + name = tree[1][1] + a = aliases.get(name) + if a and a.args is not None and a.tree == tree[:2]: + return a + return None + + @classmethod + def _expandargs(cls, tree, args): + """Replace _aliasarg instances with the substitution value of the + same name in args, recursively. + """ + if not isinstance(tree, tuple): + return tree + if tree[0] == '_aliasarg': + sym = tree[1] + return args[sym] + return tuple(cls._expandargs(t, args) for t in tree) + + @classmethod + def _expand(cls, aliases, tree, expanding, cache): + if not isinstance(tree, tuple): + return tree + a = cls._getalias(aliases, tree) + if a is not None: + if a.error: + raise error.Abort(a.error) + if a in expanding: + raise error.ParseError(_('infinite expansion of %(section)s ' + '"%(name)s" detected') + % {'section': cls._section, + 'name': a.name}) + expanding.append(a) + if a.name not in cache: + cache[a.name] = cls._expand(aliases, a.replacement, expanding, + cache) + result = cache[a.name] + expanding.pop() + if a.args is not None: + l = cls._getlist(tree[2]) + if len(l) != len(a.args): + raise error.ParseError(_('invalid number of arguments: %d') + % len(l)) + l = [cls._expand(aliases, t, [], cache) for t in l] + result = cls._expandargs(result, dict(zip(a.args, l))) + else: + result = tuple(cls._expand(aliases, t, expanding, cache) + for t in tree) + return result + + @classmethod + def expand(cls, aliases, tree): + """Expand aliases in tree, recursively. + + 'aliases' is a dictionary mapping user defined aliases to alias objects. + """ + return cls._expand(aliases, tree, [], {}) diff -r 0f59674dc9ff -r 4bf9ed7a260e mercurial/revset.py --- a/mercurial/revset.py Mon Feb 29 22:10:48 2016 +0900 +++ b/mercurial/revset.py Mon Feb 29 22:15:44 2016 +0900 @@ -2256,72 +2256,9 @@ _parse = staticmethod(_parsealias) _getlist = staticmethod(getlist) -def _getalias(aliases, tree): - """If tree looks like an unexpanded alias, return it. Return None - otherwise. - """ - if not isinstance(tree, tuple): - return None - if tree[0] == 'symbol': - name = tree[1] - alias = aliases.get(name) - if alias and alias.args is None and alias.tree == tree: - return alias - if tree[0] == 'func' and tree[1][0] == 'symbol': - name = tree[1][1] - alias = aliases.get(name) - if alias and alias.args is not None and alias.tree == tree[:2]: - return alias - return None - -def _expandargs(tree, args): - """Replace _aliasarg instances with the substitution value of the - same name in args, recursively. - """ - if not isinstance(tree, tuple): - return tree - if tree[0] == '_aliasarg': - sym = tree[1] - return args[sym] - return tuple(_expandargs(t, args) for t in tree) - -def _expandaliases(aliases, tree, expanding, cache): - """Expand aliases in tree, recursively. - - 'aliases' is a dictionary mapping user defined aliases to - alias objects. - """ - if not isinstance(tree, tuple): - # Do not expand raw strings - return tree - alias = _getalias(aliases, tree) - if alias is not None: - if alias.error: - raise error.Abort(alias.error) - if alias in expanding: - raise error.ParseError(_('infinite expansion of revset alias "%s" ' - 'detected') % alias.name) - expanding.append(alias) - if alias.name not in cache: - cache[alias.name] = _expandaliases(aliases, alias.replacement, - expanding, cache) - result = cache[alias.name] - expanding.pop() - if alias.args is not None: - l = getlist(tree[2]) - if len(l) != len(alias.args): - raise error.ParseError( - _('invalid number of arguments: %d') % len(l)) - l = [_expandaliases(aliases, a, [], cache) for a in l] - result = _expandargs(result, dict(zip(alias.args, l))) - else: - result = tuple(_expandaliases(aliases, t, expanding, cache) - for t in tree) - return result - def findaliases(ui, tree, showwarning=None): aliases = _aliasrules.buildmap(ui.configitems('revsetalias')) - tree = _expandaliases(aliases, tree, [], {}) + tree = _aliasrules.expand(aliases, tree) if showwarning: # warn about problematic (but not referred) aliases for name, alias in sorted(aliases.iteritems()):