Patchwork [4,of,7] parser: move functions that process alias expansion to rule-set class

login
register
mail settings
Submitter Yuya Nishihara
Date April 13, 2016, 3:55 p.m.
Message ID <f71d1db9f483558c881e.1460562916@mimosa>
Download mbox | patch
Permalink /patch/14583/
State Accepted
Commit 4bf9ed7a260e377004e3344e941838c76972881a
Headers show

Comments

Yuya Nishihara - April 13, 2016, 3:55 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1456751744 -32400
#      Mon Feb 29 22:15:44 2016 +0900
# Node ID f71d1db9f483558c881e55f0f20b20a97b727cd4
# Parent  d19490b6a3b411b1c5321949c57ea4cc2b4fcd36
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.

Patch

diff --git a/mercurial/parser.py b/mercurial/parser.py
--- a/mercurial/parser.py
+++ b/mercurial/parser.py
@@ -472,3 +472,73 @@  class basealiasrules(object):
             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 --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -2256,72 +2256,9 @@  class _aliasrules(parser.basealiasrules)
     _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()):