comparison mercurial/parser.py @ 28908:7a772deffa12

parser: drop redundant comparison between alias declaration tree and pattern Since _getalias() explicitly tests the type and name of the pattern tree, we don't need to compare "a.tree == tree" for 'symbol', and "a.tree == tree[:2]" for 'func', where tree is either ('symbol', name) or ('func', ('symbol', name)). This change helps implementing better handling of template aliases. See the subsequent patches for details. The alias.tree field is removed as it is no longer used.
author Yuya Nishihara <yuya@tcha.org>
date Tue, 29 Mar 2016 16:50:16 +0900
parents 8d398155bfda
children edbffdc7f6a0
comparison
equal deleted inserted replaced
28907:66e647312d30 28908:7a772deffa12
230 return inst.args[0] 230 return inst.args[0]
231 231
232 class alias(object): 232 class alias(object):
233 """Parsed result of alias""" 233 """Parsed result of alias"""
234 234
235 def __init__(self, name, tree, args, err, replacement): 235 def __init__(self, name, args, err, replacement):
236 self.name = name 236 self.name = name
237 self.tree = tree
238 self.args = args 237 self.args = args
239 self.error = err 238 self.error = err
240 self.replacement = replacement 239 self.replacement = replacement
241 # whether own `error` information is already shown or not. 240 # whether own `error` information is already shown or not.
242 # this avoids showing same warning multiple times at each 241 # this avoids showing same warning multiple times at each
274 """Extract a list of arguments from parsed tree""" 273 """Extract a list of arguments from parsed tree"""
275 raise NotImplementedError 274 raise NotImplementedError
276 275
277 @classmethod 276 @classmethod
278 def _builddecl(cls, decl): 277 def _builddecl(cls, decl):
279 """Parse an alias declaration into ``(name, tree, args, errorstr)`` 278 """Parse an alias declaration into ``(name, args, errorstr)``
280 279
281 This function analyzes the parsed tree. The parsing rule is provided 280 This function analyzes the parsed tree. The parsing rule is provided
282 by ``_parse()``. 281 by ``_parse()``.
283 282
284 - ``name``: of declared alias (may be ``decl`` itself at error) 283 - ``name``: of declared alias (may be ``decl`` itself at error)
285 - ``tree``: parse result (or ``None`` at error)
286 - ``args``: list of argument names (or None for symbol declaration) 284 - ``args``: list of argument names (or None for symbol declaration)
287 - ``errorstr``: detail about detected error (or None) 285 - ``errorstr``: detail about detected error (or None)
288 286
289 >>> sym = lambda x: ('symbol', x) 287 >>> sym = lambda x: ('symbol', x)
290 >>> symlist = lambda *xs: ('list',) + tuple(sym(x) for x in xs) 288 >>> symlist = lambda *xs: ('list',) + tuple(sym(x) for x in xs)
322 >>> class aliasrules(basealiasrules): 320 >>> class aliasrules(basealiasrules):
323 ... _parse = staticmethod(parse) 321 ... _parse = staticmethod(parse)
324 ... _getlist = staticmethod(getlist) 322 ... _getlist = staticmethod(getlist)
325 >>> builddecl = aliasrules._builddecl 323 >>> builddecl = aliasrules._builddecl
326 >>> builddecl('foo') 324 >>> builddecl('foo')
327 ('foo', ('symbol', 'foo'), None, None) 325 ('foo', None, None)
328 >>> builddecl('$foo') 326 >>> builddecl('$foo')
329 ('$foo', None, None, "'$' not for alias arguments") 327 ('$foo', None, "'$' not for alias arguments")
330 >>> builddecl('foo::bar') 328 >>> builddecl('foo::bar')
331 ('foo::bar', None, None, 'invalid format') 329 ('foo::bar', None, 'invalid format')
332 >>> builddecl('foo()') 330 >>> builddecl('foo()')
333 ('foo', ('func', ('symbol', 'foo')), [], None) 331 ('foo', [], None)
334 >>> builddecl('$foo()') 332 >>> builddecl('$foo()')
335 ('$foo()', None, None, "'$' not for alias arguments") 333 ('$foo()', None, "'$' not for alias arguments")
336 >>> builddecl('foo($1, $2)') 334 >>> builddecl('foo($1, $2)')
337 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None) 335 ('foo', ['$1', '$2'], None)
338 >>> builddecl('foo(bar_bar, baz.baz)') 336 >>> builddecl('foo(bar_bar, baz.baz)')
339 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None) 337 ('foo', ['bar_bar', 'baz.baz'], None)
340 >>> builddecl('foo($1, $2, nested($1, $2))') 338 >>> builddecl('foo($1, $2, nested($1, $2))')
341 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list') 339 ('foo($1, $2, nested($1, $2))', None, 'invalid argument list')
342 >>> builddecl('foo(bar($1, $2))') 340 >>> builddecl('foo(bar($1, $2))')
343 ('foo(bar($1, $2))', None, None, 'invalid argument list') 341 ('foo(bar($1, $2))', None, 'invalid argument list')
344 >>> builddecl('foo("bar")') 342 >>> builddecl('foo("bar")')
345 ('foo("bar")', None, None, 'invalid argument list') 343 ('foo("bar")', None, 'invalid argument list')
346 >>> builddecl('foo($1, $2') 344 >>> builddecl('foo($1, $2')
347 ('foo($1, $2', None, None, 'at 10: unexpected token: end') 345 ('foo($1, $2', None, 'at 10: unexpected token: end')
348 >>> builddecl('foo("bar') 346 >>> builddecl('foo("bar')
349 ('foo("bar', None, None, 'at 5: unterminated string') 347 ('foo("bar', None, 'at 5: unterminated string')
350 >>> builddecl('foo($1, $2, $1)') 348 >>> builddecl('foo($1, $2, $1)')
351 ('foo', None, None, 'argument names collide with each other') 349 ('foo', None, 'argument names collide with each other')
352 """ 350 """
353 try: 351 try:
354 tree = cls._parse(decl) 352 tree = cls._parse(decl)
355 except error.ParseError as inst: 353 except error.ParseError as inst:
356 return (decl, None, None, parseerrordetail(inst)) 354 return (decl, None, parseerrordetail(inst))
357 355
358 if tree[0] == cls._symbolnode: 356 if tree[0] == cls._symbolnode:
359 # "name = ...." style 357 # "name = ...." style
360 name = tree[1] 358 name = tree[1]
361 if name.startswith('$'): 359 if name.startswith('$'):
362 return (decl, None, None, _("'$' not for alias arguments")) 360 return (decl, None, _("'$' not for alias arguments"))
363 return (name, tree, None, None) 361 return (name, None, None)
364 362
365 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode: 363 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode:
366 # "name(arg, ....) = ...." style 364 # "name(arg, ....) = ...." style
367 name = tree[1][1] 365 name = tree[1][1]
368 if name.startswith('$'): 366 if name.startswith('$'):
369 return (decl, None, None, _("'$' not for alias arguments")) 367 return (decl, None, _("'$' not for alias arguments"))
370 args = [] 368 args = []
371 for arg in cls._getlist(tree[2]): 369 for arg in cls._getlist(tree[2]):
372 if arg[0] != cls._symbolnode: 370 if arg[0] != cls._symbolnode:
373 return (decl, None, None, _("invalid argument list")) 371 return (decl, None, _("invalid argument list"))
374 args.append(arg[1]) 372 args.append(arg[1])
375 if len(args) != len(set(args)): 373 if len(args) != len(set(args)):
376 return (name, None, None, 374 return (name, None, _("argument names collide with each other"))
377 _("argument names collide with each other")) 375 return (name, args, None)
378 return (name, tree[:2], args, None) 376
379 377 return (decl, None, _("invalid format"))
380 return (decl, None, None, _("invalid format"))
381 378
382 @classmethod 379 @classmethod
383 def _relabelargs(cls, tree, args): 380 def _relabelargs(cls, tree, args):
384 """Mark alias arguments as ``_aliasarg``""" 381 """Mark alias arguments as ``_aliasarg``"""
385 if not isinstance(tree, tuple): 382 if not isinstance(tree, tuple):
447 444
448 @classmethod 445 @classmethod
449 def build(cls, decl, defn): 446 def build(cls, decl, defn):
450 """Parse an alias declaration and definition into an alias object""" 447 """Parse an alias declaration and definition into an alias object"""
451 repl = efmt = None 448 repl = efmt = None
452 name, tree, args, err = cls._builddecl(decl) 449 name, args, err = cls._builddecl(decl)
453 if err: 450 if err:
454 efmt = _('failed to parse the declaration of %(section)s ' 451 efmt = _('failed to parse the declaration of %(section)s '
455 '"%(name)s": %(error)s') 452 '"%(name)s": %(error)s')
456 else: 453 else:
457 try: 454 try:
460 err = parseerrordetail(inst) 457 err = parseerrordetail(inst)
461 efmt = _('failed to parse the definition of %(section)s ' 458 efmt = _('failed to parse the definition of %(section)s '
462 '"%(name)s": %(error)s') 459 '"%(name)s": %(error)s')
463 if err: 460 if err:
464 err = efmt % {'section': cls._section, 'name': name, 'error': err} 461 err = efmt % {'section': cls._section, 'name': name, 'error': err}
465 return alias(name, tree, args, err, repl) 462 return alias(name, args, err, repl)
466 463
467 @classmethod 464 @classmethod
468 def buildmap(cls, items): 465 def buildmap(cls, items):
469 """Parse a list of alias (name, replacement) pairs into a dict of 466 """Parse a list of alias (name, replacement) pairs into a dict of
470 alias objects""" 467 alias objects"""
482 if not isinstance(tree, tuple): 479 if not isinstance(tree, tuple):
483 return None 480 return None
484 if tree[0] == cls._symbolnode: 481 if tree[0] == cls._symbolnode:
485 name = tree[1] 482 name = tree[1]
486 a = aliases.get(name) 483 a = aliases.get(name)
487 if a and a.args is None and a.tree == tree: 484 if a and a.args is None:
488 return a 485 return a
489 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode: 486 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode:
490 name = tree[1][1] 487 name = tree[1][1]
491 a = aliases.get(name) 488 a = aliases.get(name)
492 if a and a.args is not None and a.tree == tree[:2]: 489 if a and a.args is not None:
493 return a 490 return a
494 return None 491 return None
495 492
496 @classmethod 493 @classmethod
497 def _expandargs(cls, tree, args): 494 def _expandargs(cls, tree, args):