comparison mercurial/revsetlang.py @ 34063:f23cbca9b277

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
author Yuya Nishihara <yuya@tcha.org>
date Wed, 17 Feb 2016 21:40:59 +0900
parents b862e6fca7ac
children b2c691d75d93
comparison
equal deleted inserted replaced
34062:79681d8ee587 34063:f23cbca9b277
256 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2')) 256 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2'))
257 """ 257 """
258 template = _cachedtree(tmplspec) 258 template = _cachedtree(tmplspec)
259 return parser.buildtree(template, ('symbol', '_'), *repls) 259 return parser.buildtree(template, ('symbol', '_'), *repls)
260 260
261 def _match(patspec, tree):
262 """Test if a tree matches the given pattern statement; return the matches
263
264 >>> _match('f(_)', parse('f()'))
265 >>> _match('f(_)', parse('f(1)'))
266 [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')]
267 >>> _match('f(_)', parse('f(1, 2)'))
268 """
269 pattern = _cachedtree(patspec)
270 return parser.matchtree(pattern, tree, ('symbol', '_'),
271 {'keyvalue', 'list'})
272
261 def _isnamedfunc(x, funcname): 273 def _isnamedfunc(x, funcname):
262 """Check if given tree matches named function""" 274 """Check if given tree matches named function"""
263 return x and x[0] == 'func' and getsymbol(x[1]) == funcname 275 return x and x[0] == 'func' and getsymbol(x[1]) == funcname
264 276
265 def _isposargs(x, n): 277 def _isposargs(x, n):
276 if not _isnamedfunc(x, funcname): 288 if not _isnamedfunc(x, funcname):
277 return 289 return
278 return x[2] 290 return x[2]
279 291
280 def _matchonly(revs, bases): 292 def _matchonly(revs, bases):
281 """ 293 return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases))
282 >>> f = lambda *args: _matchonly(*map(parse, args))
283 >>> f('ancestors(A)', 'not ancestors(B)')
284 ('list', ('symbol', 'A'), ('symbol', 'B'))
285 """
286 ta = _matchnamedfunc(revs, 'ancestors')
287 tb = bases and bases[0] == 'not' and _matchnamedfunc(bases[1], 'ancestors')
288 if _isposargs(ta, 1) and _isposargs(tb, 1):
289 return ('list', ta, tb)
290 294
291 def _fixops(x): 295 def _fixops(x):
292 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be 296 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
293 handled well by our simple top-down parser""" 297 handled well by our simple top-down parser"""
294 if not isinstance(x, tuple): 298 if not isinstance(x, tuple):
387 # (::x and not ::y)/(not ::y and ::x) have a fast path 391 # (::x and not ::y)/(not ::y and ::x) have a fast path
388 m = _matchonly(ta, tb) or _matchonly(tb, ta) 392 m = _matchonly(ta, tb) or _matchonly(tb, ta)
389 if m: 393 if m:
390 return w, _build('only(_, _)', *m[1:]) 394 return w, _build('only(_, _)', *m[1:])
391 395
392 if tb is not None and tb[0] == 'not': 396 m = _match('not _', tb)
393 return wa, ('difference', ta, tb[1]) 397 if m:
398 return wa, ('difference', ta, m[1])
394 if wa > wb: 399 if wa > wb:
395 op = 'andsmally' 400 op = 'andsmally'
396 return w, (op, ta, tb) 401 return w, (op, ta, tb)
397 elif op == 'or': 402 elif op == 'or':
398 # fast path for machine-generated expression, that is likely to have 403 # fast path for machine-generated expression, that is likely to have
422 if len(ts) == 1: 427 if len(ts) == 1:
423 return ws[0], ts[0] # 'or' operation is fully optimized out 428 return ws[0], ts[0] # 'or' operation is fully optimized out
424 return max(ws), (op, ('list',) + tuple(ts)) 429 return max(ws), (op, ('list',) + tuple(ts))
425 elif op == 'not': 430 elif op == 'not':
426 # Optimize not public() to _notpublic() because we have a fast version 431 # Optimize not public() to _notpublic() because we have a fast version
427 if x[1][:3] == ('func', ('symbol', 'public'), None): 432 if _match('public()', x[1]):
428 o = _optimize(_build('_notpublic()'), not small) 433 o = _optimize(_build('_notpublic()'), not small)
429 return o[0], o[1] 434 return o[0], o[1]
430 else: 435 else:
431 o = _optimize(x[1], not small) 436 o = _optimize(x[1], not small)
432 return o[0], (op, o[1]) 437 return o[0], (op, o[1])