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
This commit is contained in:
Yuya Nishihara 2016-02-17 21:40:59 +09:00
parent 27c162ca6b
commit 48edce65ce

View File

@ -258,6 +258,18 @@ def _build(tmplspec, *repls):
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 @@ def _matchnamedfunc(x, funcname):
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 @@ def _optimize(x, small):
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 @@ def _optimize(x, small):
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: