Mercurial > hg
comparison mercurial/parser.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 29798c9ba5c9 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
22 from . import ( | 22 from . import ( |
23 error, | 23 error, |
24 pycompat, | 24 pycompat, |
25 util, | 25 util, |
26 ) | 26 ) |
27 from .utils import ( | 27 from .utils import stringutil |
28 stringutil, | 28 |
29 ) | |
30 | 29 |
31 class parser(object): | 30 class parser(object): |
32 def __init__(self, elements, methods=None): | 31 def __init__(self, elements, methods=None): |
33 self._elements = elements | 32 self._elements = elements |
34 self._methods = methods | 33 self._methods = methods |
35 self.current = None | 34 self.current = None |
35 | |
36 def _advance(self): | 36 def _advance(self): |
37 'advance the tokenizer' | 37 'advance the tokenizer' |
38 t = self.current | 38 t = self.current |
39 self.current = next(self._iter, None) | 39 self.current = next(self._iter, None) |
40 return t | 40 return t |
41 | |
41 def _hasnewterm(self): | 42 def _hasnewterm(self): |
42 'True if next token may start new term' | 43 'True if next token may start new term' |
43 return any(self._elements[self.current[0]][1:3]) | 44 return any(self._elements[self.current[0]][1:3]) |
45 | |
44 def _match(self, m): | 46 def _match(self, m): |
45 'make sure the tokenizer matches an end condition' | 47 'make sure the tokenizer matches an end condition' |
46 if self.current[0] != m: | 48 if self.current[0] != m: |
47 raise error.ParseError(_("unexpected token: %s") % self.current[0], | 49 raise error.ParseError( |
48 self.current[2]) | 50 _("unexpected token: %s") % self.current[0], self.current[2] |
51 ) | |
49 self._advance() | 52 self._advance() |
53 | |
50 def _parseoperand(self, bind, m=None): | 54 def _parseoperand(self, bind, m=None): |
51 'gather right-hand-side operand until an end condition or binding met' | 55 'gather right-hand-side operand until an end condition or binding met' |
52 if m and self.current[0] == m: | 56 if m and self.current[0] == m: |
53 expr = None | 57 expr = None |
54 else: | 58 else: |
55 expr = self._parse(bind) | 59 expr = self._parse(bind) |
56 if m: | 60 if m: |
57 self._match(m) | 61 self._match(m) |
58 return expr | 62 return expr |
63 | |
59 def _parse(self, bind=0): | 64 def _parse(self, bind=0): |
60 token, value, pos = self._advance() | 65 token, value, pos = self._advance() |
61 # handle prefix rules on current token, take as primary if unambiguous | 66 # handle prefix rules on current token, take as primary if unambiguous |
62 primary, prefix = self._elements[token][1:3] | 67 primary, prefix = self._elements[token][1:3] |
63 if primary and not (prefix and self._hasnewterm()): | 68 if primary and not (prefix and self._hasnewterm()): |
76 elif infix: | 81 elif infix: |
77 expr = (infix[0], expr, self._parseoperand(*infix[1:])) | 82 expr = (infix[0], expr, self._parseoperand(*infix[1:])) |
78 else: | 83 else: |
79 raise error.ParseError(_("not an infix: %s") % token, pos) | 84 raise error.ParseError(_("not an infix: %s") % token, pos) |
80 return expr | 85 return expr |
86 | |
81 def parse(self, tokeniter): | 87 def parse(self, tokeniter): |
82 'generate a parse tree from tokens' | 88 'generate a parse tree from tokens' |
83 self._iter = tokeniter | 89 self._iter = tokeniter |
84 self._advance() | 90 self._advance() |
85 res = self._parse() | 91 res = self._parse() |
86 token, value, pos = self.current | 92 token, value, pos = self.current |
87 return res, pos | 93 return res, pos |
94 | |
88 def eval(self, tree): | 95 def eval(self, tree): |
89 'recursively evaluate a parse tree using node methods' | 96 'recursively evaluate a parse tree using node methods' |
90 if not isinstance(tree, tuple): | 97 if not isinstance(tree, tuple): |
91 return tree | 98 return tree |
92 return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]]) | 99 return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]]) |
100 | |
93 def __call__(self, tokeniter): | 101 def __call__(self, tokeniter): |
94 'parse tokens into a parse tree and evaluate if methods given' | 102 'parse tokens into a parse tree and evaluate if methods given' |
95 t = self.parse(tokeniter) | 103 t = self.parse(tokeniter) |
96 if self._methods: | 104 if self._methods: |
97 return self.eval(t) | 105 return self.eval(t) |
98 return t | 106 return t |
107 | |
99 | 108 |
100 def splitargspec(spec): | 109 def splitargspec(spec): |
101 """Parse spec of function arguments into (poskeys, varkey, keys, optkey) | 110 """Parse spec of function arguments into (poskeys, varkey, keys, optkey) |
102 | 111 |
103 >>> splitargspec(b'') | 112 >>> splitargspec(b'') |
128 if not posts: | 137 if not posts: |
129 raise error.ProgrammingError('no *varkey name provided') | 138 raise error.ProgrammingError('no *varkey name provided') |
130 return pres, posts[0], posts[1:], optkey | 139 return pres, posts[0], posts[1:], optkey |
131 return [], None, pres, optkey | 140 return [], None, pres, optkey |
132 | 141 |
142 | |
133 def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode): | 143 def buildargsdict(trees, funcname, argspec, keyvaluenode, keynode): |
134 """Build dict from list containing positional and keyword arguments | 144 """Build dict from list containing positional and keyword arguments |
135 | 145 |
136 Arguments are specified by a tuple of ``(poskeys, varkey, keys, optkey)`` | 146 Arguments are specified by a tuple of ``(poskeys, varkey, keys, optkey)`` |
137 where | 147 where |
145 | 155 |
146 Invalid keywords, too few positional arguments, or too many positional | 156 Invalid keywords, too few positional arguments, or too many positional |
147 arguments are rejected, but missing keyword arguments are just omitted. | 157 arguments are rejected, but missing keyword arguments are just omitted. |
148 """ | 158 """ |
149 poskeys, varkey, keys, optkey = argspec | 159 poskeys, varkey, keys, optkey = argspec |
150 kwstart = next((i for i, x in enumerate(trees) | 160 kwstart = next( |
151 if x and x[0] == keyvaluenode), | 161 (i for i, x in enumerate(trees) if x and x[0] == keyvaluenode), |
152 len(trees)) | 162 len(trees), |
163 ) | |
153 if kwstart < len(poskeys): | 164 if kwstart < len(poskeys): |
154 raise error.ParseError(_("%(func)s takes at least %(nargs)d positional " | 165 raise error.ParseError( |
155 "arguments") | 166 _("%(func)s takes at least %(nargs)d positional " "arguments") |
156 % {'func': funcname, 'nargs': len(poskeys)}) | 167 % {'func': funcname, 'nargs': len(poskeys)} |
168 ) | |
157 if not varkey and kwstart > len(poskeys) + len(keys): | 169 if not varkey and kwstart > len(poskeys) + len(keys): |
158 raise error.ParseError(_("%(func)s takes at most %(nargs)d positional " | 170 raise error.ParseError( |
159 "arguments") | 171 _("%(func)s takes at most %(nargs)d positional " "arguments") |
160 % {'func': funcname, | 172 % {'func': funcname, 'nargs': len(poskeys) + len(keys)} |
161 'nargs': len(poskeys) + len(keys)}) | 173 ) |
162 args = util.sortdict() | 174 args = util.sortdict() |
163 # consume positional arguments | 175 # consume positional arguments |
164 for k, x in zip(poskeys, trees[:kwstart]): | 176 for k, x in zip(poskeys, trees[:kwstart]): |
165 args[k] = x | 177 args[k] = x |
166 if varkey: | 178 if varkey: |
167 args[varkey] = trees[len(args):kwstart] | 179 args[varkey] = trees[len(args) : kwstart] |
168 else: | 180 else: |
169 for k, x in zip(keys, trees[len(args):kwstart]): | 181 for k, x in zip(keys, trees[len(args) : kwstart]): |
170 args[k] = x | 182 args[k] = x |
171 # remainder should be keyword arguments | 183 # remainder should be keyword arguments |
172 if optkey: | 184 if optkey: |
173 args[optkey] = util.sortdict() | 185 args[optkey] = util.sortdict() |
174 for x in trees[kwstart:]: | 186 for x in trees[kwstart:]: |
175 if not x or x[0] != keyvaluenode or x[1][0] != keynode: | 187 if not x or x[0] != keyvaluenode or x[1][0] != keynode: |
176 raise error.ParseError(_("%(func)s got an invalid argument") | 188 raise error.ParseError( |
177 % {'func': funcname}) | 189 _("%(func)s got an invalid argument") % {'func': funcname} |
190 ) | |
178 k = x[1][1] | 191 k = x[1][1] |
179 if k in keys: | 192 if k in keys: |
180 d = args | 193 d = args |
181 elif not optkey: | 194 elif not optkey: |
182 raise error.ParseError(_("%(func)s got an unexpected keyword " | 195 raise error.ParseError( |
183 "argument '%(key)s'") | 196 _("%(func)s got an unexpected keyword " "argument '%(key)s'") |
184 % {'func': funcname, 'key': k}) | 197 % {'func': funcname, 'key': k} |
198 ) | |
185 else: | 199 else: |
186 d = args[optkey] | 200 d = args[optkey] |
187 if k in d: | 201 if k in d: |
188 raise error.ParseError(_("%(func)s got multiple values for keyword " | 202 raise error.ParseError( |
189 "argument '%(key)s'") | 203 _( |
190 % {'func': funcname, 'key': k}) | 204 "%(func)s got multiple values for keyword " |
205 "argument '%(key)s'" | |
206 ) | |
207 % {'func': funcname, 'key': k} | |
208 ) | |
191 d[k] = x[2] | 209 d[k] = x[2] |
192 return args | 210 return args |
211 | |
193 | 212 |
194 def unescapestr(s): | 213 def unescapestr(s): |
195 try: | 214 try: |
196 return stringutil.unescapestr(s) | 215 return stringutil.unescapestr(s) |
197 except ValueError as e: | 216 except ValueError as e: |
198 # mangle Python's exception into our format | 217 # mangle Python's exception into our format |
199 raise error.ParseError(pycompat.bytestr(e).lower()) | 218 raise error.ParseError(pycompat.bytestr(e).lower()) |
219 | |
200 | 220 |
201 def _prettyformat(tree, leafnodes, level, lines): | 221 def _prettyformat(tree, leafnodes, level, lines): |
202 if not isinstance(tree, tuple): | 222 if not isinstance(tree, tuple): |
203 lines.append((level, stringutil.pprint(tree))) | 223 lines.append((level, stringutil.pprint(tree))) |
204 elif tree[0] in leafnodes: | 224 elif tree[0] in leafnodes: |
208 lines.append((level, '(%s' % tree[0])) | 228 lines.append((level, '(%s' % tree[0])) |
209 for s in tree[1:]: | 229 for s in tree[1:]: |
210 _prettyformat(s, leafnodes, level + 1, lines) | 230 _prettyformat(s, leafnodes, level + 1, lines) |
211 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')] | 231 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')] |
212 | 232 |
233 | |
213 def prettyformat(tree, leafnodes): | 234 def prettyformat(tree, leafnodes): |
214 lines = [] | 235 lines = [] |
215 _prettyformat(tree, leafnodes, 0, lines) | 236 _prettyformat(tree, leafnodes, 0, lines) |
216 output = '\n'.join((' ' * l + s) for l, s in lines) | 237 output = '\n'.join((' ' * l + s) for l, s in lines) |
217 return output | 238 return output |
239 | |
218 | 240 |
219 def simplifyinfixops(tree, targetnodes): | 241 def simplifyinfixops(tree, targetnodes): |
220 """Flatten chained infix operations to reduce usage of Python stack | 242 """Flatten chained infix operations to reduce usage of Python stack |
221 | 243 |
222 >>> from . import pycompat | 244 >>> from . import pycompat |
293 x = l | 315 x = l |
294 simplified.append(simplifyinfixops(x, targetnodes)) | 316 simplified.append(simplifyinfixops(x, targetnodes)) |
295 simplified.append(op) | 317 simplified.append(op) |
296 return tuple(reversed(simplified)) | 318 return tuple(reversed(simplified)) |
297 | 319 |
320 | |
298 def _buildtree(template, placeholder, replstack): | 321 def _buildtree(template, placeholder, replstack): |
299 if template == placeholder: | 322 if template == placeholder: |
300 return replstack.pop() | 323 return replstack.pop() |
301 if not isinstance(template, tuple): | 324 if not isinstance(template, tuple): |
302 return template | 325 return template |
303 return tuple(_buildtree(x, placeholder, replstack) for x in template) | 326 return tuple(_buildtree(x, placeholder, replstack) for x in template) |
327 | |
304 | 328 |
305 def buildtree(template, placeholder, *repls): | 329 def buildtree(template, placeholder, *repls): |
306 """Create new tree by substituting placeholders by replacements | 330 """Create new tree by substituting placeholders by replacements |
307 | 331 |
308 >>> _ = (b'symbol', b'_') | 332 >>> _ = (b'symbol', b'_') |
320 r = _buildtree(template, placeholder, replstack) | 344 r = _buildtree(template, placeholder, replstack) |
321 if replstack: | 345 if replstack: |
322 raise error.ProgrammingError('too many replacements') | 346 raise error.ProgrammingError('too many replacements') |
323 return r | 347 return r |
324 | 348 |
349 | |
325 def _matchtree(pattern, tree, placeholder, incompletenodes, matches): | 350 def _matchtree(pattern, tree, placeholder, incompletenodes, matches): |
326 if pattern == tree: | 351 if pattern == tree: |
327 return True | 352 return True |
328 if not isinstance(pattern, tuple) or not isinstance(tree, tuple): | 353 if not isinstance(pattern, tuple) or not isinstance(tree, tuple): |
329 return False | 354 return False |
330 if pattern == placeholder and tree[0] not in incompletenodes: | 355 if pattern == placeholder and tree[0] not in incompletenodes: |
331 matches.append(tree) | 356 matches.append(tree) |
332 return True | 357 return True |
333 if len(pattern) != len(tree): | 358 if len(pattern) != len(tree): |
334 return False | 359 return False |
335 return all(_matchtree(p, x, placeholder, incompletenodes, matches) | 360 return all( |
336 for p, x in zip(pattern, tree)) | 361 _matchtree(p, x, placeholder, incompletenodes, matches) |
362 for p, x in zip(pattern, tree) | |
363 ) | |
364 | |
337 | 365 |
338 def matchtree(pattern, tree, placeholder=None, incompletenodes=()): | 366 def matchtree(pattern, tree, placeholder=None, incompletenodes=()): |
339 """If a tree matches the pattern, return a list of the tree and nodes | 367 """If a tree matches the pattern, return a list of the tree and nodes |
340 matched with the placeholder; Otherwise None | 368 matched with the placeholder; Otherwise None |
341 | 369 |
373 raise error.ProgrammingError('placeholder must be a node tuple') | 401 raise error.ProgrammingError('placeholder must be a node tuple') |
374 matches = [tree] | 402 matches = [tree] |
375 if _matchtree(pattern, tree, placeholder, incompletenodes, matches): | 403 if _matchtree(pattern, tree, placeholder, incompletenodes, matches): |
376 return matches | 404 return matches |
377 | 405 |
406 | |
378 def parseerrordetail(inst): | 407 def parseerrordetail(inst): |
379 """Compose error message from specified ParseError object | 408 """Compose error message from specified ParseError object |
380 """ | 409 """ |
381 if len(inst.args) > 1: | 410 if len(inst.args) > 1: |
382 return _('at %d: %s') % (inst.args[1], inst.args[0]) | 411 return _('at %d: %s') % (inst.args[1], inst.args[0]) |
383 else: | 412 else: |
384 return inst.args[0] | 413 return inst.args[0] |
414 | |
385 | 415 |
386 class alias(object): | 416 class alias(object): |
387 """Parsed result of alias""" | 417 """Parsed result of alias""" |
388 | 418 |
389 def __init__(self, name, args, err, replacement): | 419 def __init__(self, name, args, err, replacement): |
394 # whether own `error` information is already shown or not. | 424 # whether own `error` information is already shown or not. |
395 # this avoids showing same warning multiple times at each | 425 # this avoids showing same warning multiple times at each |
396 # `expandaliases`. | 426 # `expandaliases`. |
397 self.warned = False | 427 self.warned = False |
398 | 428 |
429 | |
399 class basealiasrules(object): | 430 class basealiasrules(object): |
400 """Parsing and expansion rule set of aliases | 431 """Parsing and expansion rule set of aliases |
401 | 432 |
402 This is a helper for fileset/revset/template aliases. A concrete rule set | 433 This is a helper for fileset/revset/template aliases. A concrete rule set |
403 should be made by sub-classing this and implementing class/static methods. | 434 should be made by sub-classing this and implementing class/static methods. |
406 | 437 |
407 # decl = defn | 438 # decl = defn |
408 h = heads(default) | 439 h = heads(default) |
409 b($1) = ancestors($1) - ancestors(default) | 440 b($1) = ancestors($1) - ancestors(default) |
410 """ | 441 """ |
442 | |
411 # typically a config section, which will be included in error messages | 443 # typically a config section, which will be included in error messages |
412 _section = None | 444 _section = None |
413 # tag of symbol node | 445 # tag of symbol node |
414 _symbolnode = 'symbol' | 446 _symbolnode = 'symbol' |
415 | 447 |
663 def _expand(cls, aliases, tree, expanding, cache): | 695 def _expand(cls, aliases, tree, expanding, cache): |
664 if not isinstance(tree, tuple): | 696 if not isinstance(tree, tuple): |
665 return tree | 697 return tree |
666 r = cls._getalias(aliases, tree) | 698 r = cls._getalias(aliases, tree) |
667 if r is None: | 699 if r is None: |
668 return tuple(cls._expand(aliases, t, expanding, cache) | 700 return tuple( |
669 for t in tree) | 701 cls._expand(aliases, t, expanding, cache) for t in tree |
702 ) | |
670 a, l = r | 703 a, l = r |
671 if a.error: | 704 if a.error: |
672 raise error.Abort(a.error) | 705 raise error.Abort(a.error) |
673 if a in expanding: | 706 if a in expanding: |
674 raise error.ParseError(_('infinite expansion of %(section)s ' | 707 raise error.ParseError( |
675 '"%(name)s" detected') | 708 _('infinite expansion of %(section)s ' '"%(name)s" detected') |
676 % {'section': cls._section, 'name': a.name}) | 709 % {'section': cls._section, 'name': a.name} |
710 ) | |
677 # get cacheable replacement tree by expanding aliases recursively | 711 # get cacheable replacement tree by expanding aliases recursively |
678 expanding.append(a) | 712 expanding.append(a) |
679 if a.name not in cache: | 713 if a.name not in cache: |
680 cache[a.name] = cls._expand(aliases, a.replacement, expanding, | 714 cache[a.name] = cls._expand( |
681 cache) | 715 aliases, a.replacement, expanding, cache |
716 ) | |
682 result = cache[a.name] | 717 result = cache[a.name] |
683 expanding.pop() | 718 expanding.pop() |
684 if a.args is None: | 719 if a.args is None: |
685 return result | 720 return result |
686 # substitute function arguments in replacement tree | 721 # substitute function arguments in replacement tree |
687 if len(l) != len(a.args): | 722 if len(l) != len(a.args): |
688 raise error.ParseError(_('invalid number of arguments: %d') | 723 raise error.ParseError( |
689 % len(l)) | 724 _('invalid number of arguments: %d') % len(l) |
725 ) | |
690 l = [cls._expand(aliases, t, [], cache) for t in l] | 726 l = [cls._expand(aliases, t, [], cache) for t in l] |
691 return cls._expandargs(result, dict(zip(a.args, l))) | 727 return cls._expandargs(result, dict(zip(a.args, l))) |
692 | 728 |
693 @classmethod | 729 @classmethod |
694 def expand(cls, aliases, tree): | 730 def expand(cls, aliases, tree): |