revsetlang: match tree by helper function on optimize
authorYuya Nishihara <yuya@tcha.org>
Wed, 17 Feb 2016 21:40:59 +0900
changeset 34046 f23cbca9b277
parent 34045 79681d8ee587
child 34047 b2c691d75d93
revsetlang: match tree by helper function on optimize This should make optimize() more readable and less error-prone, but it doubles the parsing cost. (original) $ python -m timeit -n10000 -s 'from mercurial import revsetlang as L' \ 'L.optimize(L.analyze(L.parse("ancestors(x) and not ancestors(y)")))' 10000 loops, best of 3: 79.3 usec per loop (this patch) $ python -m timeit -n10000 -s 'from mercurial import revsetlang as L' \ 'L._treecache.clear(); \ L.optimize(L.analyze(L.parse("ancestors(x) and not ancestors(y)")))' 10000 loops, best of 3: 201 usec per loop
mercurial/revsetlang.py
--- a/mercurial/revsetlang.py	Wed Feb 17 21:31:09 2016 +0900
+++ b/mercurial/revsetlang.py	Wed Feb 17 21:40:59 2016 +0900
@@ -258,6 +258,18 @@
     template = _cachedtree(tmplspec)
     return parser.buildtree(template, ('symbol', '_'), *repls)
 
+def _match(patspec, tree):
+    """Test if a tree matches the given pattern statement; return the matches
+
+    >>> _match('f(_)', parse('f()'))
+    >>> _match('f(_)', parse('f(1)'))
+    [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')]
+    >>> _match('f(_)', parse('f(1, 2)'))
+    """
+    pattern = _cachedtree(patspec)
+    return parser.matchtree(pattern, tree, ('symbol', '_'),
+                            {'keyvalue', 'list'})
+
 def _isnamedfunc(x, funcname):
     """Check if given tree matches named function"""
     return x and x[0] == 'func' and getsymbol(x[1]) == funcname
@@ -278,15 +290,7 @@
     return x[2]
 
 def _matchonly(revs, bases):
-    """
-    >>> f = lambda *args: _matchonly(*map(parse, args))
-    >>> f('ancestors(A)', 'not ancestors(B)')
-    ('list', ('symbol', 'A'), ('symbol', 'B'))
-    """
-    ta = _matchnamedfunc(revs, 'ancestors')
-    tb = bases and bases[0] == 'not' and _matchnamedfunc(bases[1], 'ancestors')
-    if _isposargs(ta, 1) and _isposargs(tb, 1):
-        return ('list', ta, tb)
+    return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases))
 
 def _fixops(x):
     """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
@@ -389,8 +393,9 @@
         if m:
             return w, _build('only(_, _)', *m[1:])
 
-        if tb is not None and tb[0] == 'not':
-            return wa, ('difference', ta, tb[1])
+        m = _match('not _', tb)
+        if m:
+            return wa, ('difference', ta, m[1])
         if wa > wb:
             op = 'andsmally'
         return w, (op, ta, tb)
@@ -424,7 +429,7 @@
         return max(ws), (op, ('list',) + tuple(ts))
     elif op == 'not':
         # Optimize not public() to _notpublic() because we have a fast version
-        if x[1][:3] == ('func', ('symbol', 'public'), None):
+        if _match('public()', x[1]):
             o = _optimize(_build('_notpublic()'), not small)
             return o[0], o[1]
         else: