comparison mercurial/revsetlang.py @ 41219:e5b227f41e4a

revset: extract parsing logic out of formatspec We want to be able to perform better handling of some input when running revset (eg: `repo.revs("%ld", somerevs)`). The first step is to be able to access some of the parsed content before it gets substituted. There are many possible different substitutions, we'll add support for them gradually. In this changeset we support none, we just split some logic in a sub function as a preparatory step.
author Boris Feld <boris.feld@octobus.net>
date Fri, 04 Jan 2019 02:29:04 +0100
parents 24a1f67bb75a
children 8d26026b3335
comparison
equal deleted inserted replaced
41218:24a1f67bb75a 41219:e5b227f41e4a
664 >>> formatspec(b'sort(%r, %ps)', b':', [b'desc', b'user']) 664 >>> formatspec(b'sort(%r, %ps)', b':', [b'desc', b'user'])
665 "sort((:), 'desc', 'user')" 665 "sort((:), 'desc', 'user')"
666 >>> formatspec(b'%ls', [b'a', b"'"]) 666 >>> formatspec(b'%ls', [b'a', b"'"])
667 "_list('a\\\\x00\\\\'')" 667 "_list('a\\\\x00\\\\'')"
668 ''' 668 '''
669 parsed = _parseargs(expr, args)
670 ret = []
671 for t, arg in parsed:
672 if t is None:
673 ret.append(arg)
674 else:
675 raise error.ProgrammingError("unknown revspec item type: %r" % t)
676 return b''.join(ret)
677
678 def _parseargs(expr, args):
679 """parse the expression and replace all inexpensive args
680
681 return a list of tuple [(arg-type, arg-value)]
682
683 Arg-type can be:
684 * None: a string ready to be concatenated into a final spec
685 """
669 expr = pycompat.bytestr(expr) 686 expr = pycompat.bytestr(expr)
670 argiter = iter(args) 687 argiter = iter(args)
671 ret = [] 688 ret = []
672 pos = 0 689 pos = 0
673 while pos < len(expr): 690 while pos < len(expr):
674 q = expr.find('%', pos) 691 q = expr.find('%', pos)
675 if q < 0: 692 if q < 0:
676 ret.append(expr[pos:]) 693 ret.append((None, expr[pos:]))
677 break 694 break
678 ret.append(expr[pos:q]) 695 ret.append((None, expr[pos:q]))
679 pos = q + 1 696 pos = q + 1
680 try: 697 try:
681 d = expr[pos] 698 d = expr[pos]
682 except IndexError: 699 except IndexError:
683 raise error.ParseError(_('incomplete revspec format character')) 700 raise error.ParseError(_('incomplete revspec format character'))
684 if d == '%': 701 if d == '%':
685 ret.append(d) 702 ret.append((None, d))
686 pos += 1 703 pos += 1
687 continue 704 continue
688 705
689 try: 706 try:
690 arg = next(argiter) 707 arg = next(argiter)
691 except StopIteration: 708 except StopIteration:
692 raise error.ParseError(_('missing argument for revspec')) 709 raise error.ParseError(_('missing argument for revspec'))
693 f = _formatlistfuncs.get(d) 710 f = _formatlistfuncs.get(d)
694 if f: 711 if f:
695 # a list of some type 712 # a list of some type, might be expensive, do not replace
696 pos += 1 713 pos += 1
697 try: 714 try:
698 d = expr[pos] 715 d = expr[pos]
699 except IndexError: 716 except IndexError:
700 raise error.ParseError(_('incomplete revspec format character')) 717 raise error.ParseError(_('incomplete revspec format character'))
701 try: 718 try:
702 ret.append(f(list(arg), d)) 719 ret.append((None, f(list(arg), d)))
703 except (TypeError, ValueError): 720 except (TypeError, ValueError):
704 raise error.ParseError(_('invalid argument for revspec')) 721 raise error.ParseError(_('invalid argument for revspec'))
705 else: 722 else:
723 # a single entry, not expensive, replace
706 try: 724 try:
707 ret.append(_formatargtype(d, arg)) 725 ret.append((None, _formatargtype(d, arg)))
708 except (TypeError, ValueError): 726 except (TypeError, ValueError):
709 raise error.ParseError(_('invalid argument for revspec')) 727 raise error.ParseError(_('invalid argument for revspec'))
710 pos += 1 728 pos += 1
711 729
712 try: 730 try:
713 next(argiter) 731 next(argiter)
714 raise error.ParseError(_('too many revspec arguments specified')) 732 raise error.ParseError(_('too many revspec arguments specified'))
715 except StopIteration: 733 except StopIteration:
716 pass 734 pass
717 return ''.join(ret) 735 return ret
718 736
719 def prettyformat(tree): 737 def prettyformat(tree):
720 return parser.prettyformat(tree, ('string', 'symbol')) 738 return parser.prettyformat(tree, ('string', 'symbol'))
721 739
722 def depth(tree): 740 def depth(tree):