changeset 28910:1203159c8928

parser: factor out _trygetfunc() that extracts function name and arguments This provides a customization point for templater. In templater, there are two ways to call a unary function: func(x) and x|func. They are processed differently in templater due to historical reasons, but they should be handled in the same way while expanding aliases. In short, x|func should be processed as syntactic sugar for func(x). _funcnode and _getlist() are replaced by _trygetfunc().
author Yuya Nishihara <yuya@tcha.org>
date Tue, 29 Mar 2016 17:27:34 +0900
parents edbffdc7f6a0
children 35da19348143
files mercurial/parser.py mercurial/revset.py
diffstat 2 files changed, 28 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/parser.py	Tue Mar 29 17:21:11 2016 +0900
+++ b/mercurial/parser.py	Tue Mar 29 17:27:34 2016 +0900
@@ -256,9 +256,8 @@
     """
     # typically a config section, which will be included in error messages
     _section = None
-    # tags of symbol and function nodes
+    # tag of symbol node
     _symbolnode = 'symbol'
-    _funcnode = 'func'
 
     def __new__(cls):
         raise TypeError("'%s' is not instantiatable" % cls.__name__)
@@ -269,8 +268,8 @@
         raise NotImplementedError
 
     @staticmethod
-    def _getlist(tree):
-        """Extract a list of arguments from parsed tree"""
+    def _trygetfunc(tree):
+        """Return (name, args) if tree is a function; otherwise None"""
         raise NotImplementedError
 
     @classmethod
@@ -311,15 +310,17 @@
         ...     if isinstance(x, Exception):
         ...         raise x
         ...     return x
-        >>> def getlist(tree):
-        ...     if not tree:
-        ...         return []
-        ...     if tree[0] == 'list':
-        ...         return list(tree[1:])
-        ...     return [tree]
+        >>> def trygetfunc(tree):
+        ...     if not tree or tree[0] != 'func' or tree[1][0] != 'symbol':
+        ...         return None
+        ...     if not tree[2]:
+        ...         return tree[1][1], []
+        ...     if tree[2][0] == 'list':
+        ...         return tree[1][1], list(tree[2][1:])
+        ...     return tree[1][1], [tree[2]]
         >>> class aliasrules(basealiasrules):
         ...     _parse = staticmethod(parse)
-        ...     _getlist = staticmethod(getlist)
+        ...     _trygetfunc = staticmethod(trygetfunc)
         >>> builddecl = aliasrules._builddecl
         >>> builddecl('foo')
         ('foo', None, None)
@@ -360,19 +361,17 @@
                 return (decl, None, _("'$' not for alias arguments"))
             return (name, None, None)
 
-        if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode:
+        func = cls._trygetfunc(tree)
+        if func:
             # "name(arg, ....) = ...." style
-            name = tree[1][1]
+            name, args = func
             if name.startswith('$'):
                 return (decl, None, _("'$' not for alias arguments"))
-            args = []
-            for arg in cls._getlist(tree[2]):
-                if arg[0] != cls._symbolnode:
-                    return (decl, None, _("invalid argument list"))
-                args.append(arg[1])
+            if any(t[0] != cls._symbolnode for t in args):
+                return (decl, None, _("invalid argument list"))
             if len(args) != len(set(args)):
                 return (name, None, _("argument names collide with each other"))
-            return (name, args, None)
+            return (name, [t[1] for t in args], None)
 
         return (decl, None, _("invalid format"))
 
@@ -411,7 +410,7 @@
         ... }
         >>> class aliasrules(basealiasrules):
         ...     _parse = staticmethod(parsemap.__getitem__)
-        ...     _getlist = staticmethod(lambda x: [])
+        ...     _trygetfunc = staticmethod(lambda x: None)
         >>> builddefn = aliasrules._builddefn
         >>> def pprint(tree):
         ...     print prettyformat(tree, ('_aliasarg', 'string', 'symbol'))
@@ -483,11 +482,12 @@
             a = aliases.get(name)
             if a and a.args is None:
                 return a, None
-        if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode:
-            name = tree[1][1]
+        func = cls._trygetfunc(tree)
+        if func:
+            name, args = func
             a = aliases.get(name)
             if a and a.args is not None:
-                return a, cls._getlist(tree[2])
+                return a, args
         return None
 
     @classmethod
--- a/mercurial/revset.py	Tue Mar 29 17:21:11 2016 +0900
+++ b/mercurial/revset.py	Tue Mar 29 17:27:34 2016 +0900
@@ -2254,7 +2254,11 @@
     """Parsing and expansion rule set of revset aliases"""
     _section = _('revset alias')
     _parse = staticmethod(_parsealias)
-    _getlist = staticmethod(getlist)
+
+    @staticmethod
+    def _trygetfunc(tree):
+        if tree[0] == 'func' and tree[1][0] == 'symbol':
+            return tree[1][1], getlist(tree[2])
 
 def expandaliases(ui, tree, showwarning=None):
     aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))