Mercurial > public > mercurial-scm > hg
comparison mercurial/parser.py @ 28910:1203159c8928
parser: factor out _trygetfunc() that extracts function name and arguments
This provides a customization point for templater. In templater, there are
two ways to call a unary function: func(x) and x|func. They are processed
differently in templater due to historical reasons, but they should be
handled in the same way while expanding aliases. In short, x|func should be
processed as syntactic sugar for func(x).
_funcnode and _getlist() are replaced by _trygetfunc().
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Tue, 29 Mar 2016 17:27:34 +0900 |
parents | edbffdc7f6a0 |
children | dbed4c4f48ae |
comparison
equal
deleted
inserted
replaced
28909:edbffdc7f6a0 | 28910:1203159c8928 |
---|---|
254 h = heads(default) | 254 h = heads(default) |
255 b($1) = ancestors($1) - ancestors(default) | 255 b($1) = ancestors($1) - ancestors(default) |
256 """ | 256 """ |
257 # typically a config section, which will be included in error messages | 257 # typically a config section, which will be included in error messages |
258 _section = None | 258 _section = None |
259 # tags of symbol and function nodes | 259 # tag of symbol node |
260 _symbolnode = 'symbol' | 260 _symbolnode = 'symbol' |
261 _funcnode = 'func' | |
262 | 261 |
263 def __new__(cls): | 262 def __new__(cls): |
264 raise TypeError("'%s' is not instantiatable" % cls.__name__) | 263 raise TypeError("'%s' is not instantiatable" % cls.__name__) |
265 | 264 |
266 @staticmethod | 265 @staticmethod |
267 def _parse(spec): | 266 def _parse(spec): |
268 """Parse an alias name, arguments and definition""" | 267 """Parse an alias name, arguments and definition""" |
269 raise NotImplementedError | 268 raise NotImplementedError |
270 | 269 |
271 @staticmethod | 270 @staticmethod |
272 def _getlist(tree): | 271 def _trygetfunc(tree): |
273 """Extract a list of arguments from parsed tree""" | 272 """Return (name, args) if tree is a function; otherwise None""" |
274 raise NotImplementedError | 273 raise NotImplementedError |
275 | 274 |
276 @classmethod | 275 @classmethod |
277 def _builddecl(cls, decl): | 276 def _builddecl(cls, decl): |
278 """Parse an alias declaration into ``(name, args, errorstr)`` | 277 """Parse an alias declaration into ``(name, args, errorstr)`` |
309 >>> def parse(expr): | 308 >>> def parse(expr): |
310 ... x = parsemap[expr] | 309 ... x = parsemap[expr] |
311 ... if isinstance(x, Exception): | 310 ... if isinstance(x, Exception): |
312 ... raise x | 311 ... raise x |
313 ... return x | 312 ... return x |
314 >>> def getlist(tree): | 313 >>> def trygetfunc(tree): |
315 ... if not tree: | 314 ... if not tree or tree[0] != 'func' or tree[1][0] != 'symbol': |
316 ... return [] | 315 ... return None |
317 ... if tree[0] == 'list': | 316 ... if not tree[2]: |
318 ... return list(tree[1:]) | 317 ... return tree[1][1], [] |
319 ... return [tree] | 318 ... if tree[2][0] == 'list': |
319 ... return tree[1][1], list(tree[2][1:]) | |
320 ... return tree[1][1], [tree[2]] | |
320 >>> class aliasrules(basealiasrules): | 321 >>> class aliasrules(basealiasrules): |
321 ... _parse = staticmethod(parse) | 322 ... _parse = staticmethod(parse) |
322 ... _getlist = staticmethod(getlist) | 323 ... _trygetfunc = staticmethod(trygetfunc) |
323 >>> builddecl = aliasrules._builddecl | 324 >>> builddecl = aliasrules._builddecl |
324 >>> builddecl('foo') | 325 >>> builddecl('foo') |
325 ('foo', None, None) | 326 ('foo', None, None) |
326 >>> builddecl('$foo') | 327 >>> builddecl('$foo') |
327 ('$foo', None, "'$' not for alias arguments") | 328 ('$foo', None, "'$' not for alias arguments") |
358 name = tree[1] | 359 name = tree[1] |
359 if name.startswith('$'): | 360 if name.startswith('$'): |
360 return (decl, None, _("'$' not for alias arguments")) | 361 return (decl, None, _("'$' not for alias arguments")) |
361 return (name, None, None) | 362 return (name, None, None) |
362 | 363 |
363 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode: | 364 func = cls._trygetfunc(tree) |
365 if func: | |
364 # "name(arg, ....) = ...." style | 366 # "name(arg, ....) = ...." style |
365 name = tree[1][1] | 367 name, args = func |
366 if name.startswith('$'): | 368 if name.startswith('$'): |
367 return (decl, None, _("'$' not for alias arguments")) | 369 return (decl, None, _("'$' not for alias arguments")) |
368 args = [] | 370 if any(t[0] != cls._symbolnode for t in args): |
369 for arg in cls._getlist(tree[2]): | 371 return (decl, None, _("invalid argument list")) |
370 if arg[0] != cls._symbolnode: | |
371 return (decl, None, _("invalid argument list")) | |
372 args.append(arg[1]) | |
373 if len(args) != len(set(args)): | 372 if len(args) != len(set(args)): |
374 return (name, None, _("argument names collide with each other")) | 373 return (name, None, _("argument names collide with each other")) |
375 return (name, args, None) | 374 return (name, [t[1] for t in args], None) |
376 | 375 |
377 return (decl, None, _("invalid format")) | 376 return (decl, None, _("invalid format")) |
378 | 377 |
379 @classmethod | 378 @classmethod |
380 def _relabelargs(cls, tree, args): | 379 def _relabelargs(cls, tree, args): |
409 ... '$10 or baz': ('or', ('symbol', '$10'), ('symbol', 'baz')), | 408 ... '$10 or baz': ('or', ('symbol', '$10'), ('symbol', 'baz')), |
410 ... '"$1" or "foo"': ('or', ('string', '$1'), ('string', 'foo')), | 409 ... '"$1" or "foo"': ('or', ('string', '$1'), ('string', 'foo')), |
411 ... } | 410 ... } |
412 >>> class aliasrules(basealiasrules): | 411 >>> class aliasrules(basealiasrules): |
413 ... _parse = staticmethod(parsemap.__getitem__) | 412 ... _parse = staticmethod(parsemap.__getitem__) |
414 ... _getlist = staticmethod(lambda x: []) | 413 ... _trygetfunc = staticmethod(lambda x: None) |
415 >>> builddefn = aliasrules._builddefn | 414 >>> builddefn = aliasrules._builddefn |
416 >>> def pprint(tree): | 415 >>> def pprint(tree): |
417 ... print prettyformat(tree, ('_aliasarg', 'string', 'symbol')) | 416 ... print prettyformat(tree, ('_aliasarg', 'string', 'symbol')) |
418 >>> args = ['$1', '$2', 'foo'] | 417 >>> args = ['$1', '$2', 'foo'] |
419 >>> pprint(builddefn('$1 or foo', args)) | 418 >>> pprint(builddefn('$1 or foo', args)) |
481 if tree[0] == cls._symbolnode: | 480 if tree[0] == cls._symbolnode: |
482 name = tree[1] | 481 name = tree[1] |
483 a = aliases.get(name) | 482 a = aliases.get(name) |
484 if a and a.args is None: | 483 if a and a.args is None: |
485 return a, None | 484 return a, None |
486 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode: | 485 func = cls._trygetfunc(tree) |
487 name = tree[1][1] | 486 if func: |
487 name, args = func | |
488 a = aliases.get(name) | 488 a = aliases.get(name) |
489 if a and a.args is not None: | 489 if a and a.args is not None: |
490 return a, cls._getlist(tree[2]) | 490 return a, args |
491 return None | 491 return None |
492 | 492 |
493 @classmethod | 493 @classmethod |
494 def _expandargs(cls, tree, args): | 494 def _expandargs(cls, tree, args): |
495 """Replace _aliasarg instances with the substitution value of the | 495 """Replace _aliasarg instances with the substitution value of the |