Mercurial > public > mercurial-scm > hg-stable
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]) |