comparison mercurial/revsetlang.py @ 41222:8aca89a694d4

revset: introduce an API that avoids `formatspec` input serialization Instead of having the data fully serialized, the input can be directly inserted in the tree at a later stage. Just using it for simple "%ld" case provide a significant boost. For example here are the impact on a sample discovery run between two pypy repositories with arbitrary differences (using hg perfdiscovery). $ hg perfdiscovery before: ! wall 0.700435 comb 0.710000 user 0.700000 sys 0.010000 (median of 15) after: ! wall 0.501305 comb 0.510000 user 0.490000 sys 0.020000 (median of 20)
author Boris Feld <boris.feld@octobus.net>
date Fri, 04 Jan 2019 13:41:21 +0100
parents 73203cdfe3fe
children 26b0a7514f01
comparison
equal deleted inserted replaced
41221:73203cdfe3fe 41222:8aca89a694d4
331 elif op == 'dagrangepost': 331 elif op == 'dagrangepost':
332 return _analyze(_build('descendants(_)', x[1])) 332 return _analyze(_build('descendants(_)', x[1]))
333 elif op == 'negate': 333 elif op == 'negate':
334 s = getstring(x[1], _("can't negate that")) 334 s = getstring(x[1], _("can't negate that"))
335 return _analyze(('string', '-' + s)) 335 return _analyze(('string', '-' + s))
336 elif op in ('string', 'symbol'): 336 elif op in ('string', 'symbol', 'smartset'):
337 return x 337 return x
338 elif op == 'rangeall': 338 elif op == 'rangeall':
339 return (op, None) 339 return (op, None)
340 elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}: 340 elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}:
341 return (op, _analyze(x[1])) 341 return (op, _analyze(x[1]))
371 def _optimize(x): 371 def _optimize(x):
372 if x is None: 372 if x is None:
373 return 0, x 373 return 0, x
374 374
375 op = x[0] 375 op = x[0]
376 if op in ('string', 'symbol'): 376 if op in ('string', 'symbol', 'smartset'):
377 return 0.5, x # single revisions are small 377 return 0.5, x # single revisions are small
378 elif op == 'and': 378 elif op == 'and':
379 wa, ta = _optimize(x[1]) 379 wa, ta = _optimize(x[1])
380 wb, tb = _optimize(x[2]) 380 wb, tb = _optimize(x[2])
381 w = min(wa, wb) 381 w = min(wa, wb)
533 return tree 533 return tree
534 534
535 def foldconcat(tree): 535 def foldconcat(tree):
536 """Fold elements to be concatenated by `##` 536 """Fold elements to be concatenated by `##`
537 """ 537 """
538 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'): 538 if (not isinstance(tree, tuple)
539 or tree[0] in ('string', 'symbol', 'smartset')):
539 return tree 540 return tree
540 if tree[0] == '_concat': 541 if tree[0] == '_concat':
541 pending = [tree] 542 pending = [tree]
542 l = [] 543 l = []
543 while pending: 544 while pending:
688 arg = sorted(arg) 689 arg = sorted(arg)
689 ret.append(_formatintlist(list(arg))) 690 ret.append(_formatintlist(list(arg)))
690 else: 691 else:
691 raise error.ProgrammingError("unknown revspec item type: %r" % t) 692 raise error.ProgrammingError("unknown revspec item type: %r" % t)
692 return b''.join(ret) 693 return b''.join(ret)
694
695 def spectree(expr, *args):
696 """similar to formatspec but return a parsed and optimized tree"""
697 parsed = _parseargs(expr, args)
698 ret = []
699 inputs = []
700 for t, arg in parsed:
701 if t is None:
702 ret.append(arg)
703 elif t == 'baseset':
704 newtree = ('smartset', smartset.baseset(arg))
705 inputs.append(newtree)
706 ret.append("$")
707 else:
708 raise error.ProgrammingError("unknown revspec item type: %r" % t)
709 expr = b''.join(ret)
710 tree = _parsewith(expr, syminitletters=_aliassyminitletters)
711 tree = parser.buildtree(tree, ('symbol', '$'), *inputs)
712 tree = foldconcat(tree)
713 tree = analyze(tree)
714 tree = optimize(tree)
715 return tree
693 716
694 def _parseargs(expr, args): 717 def _parseargs(expr, args):
695 """parse the expression and replace all inexpensive args 718 """parse the expression and replace all inexpensive args
696 719
697 return a list of tuple [(arg-type, arg-value)] 720 return a list of tuple [(arg-type, arg-value)]