mercurial/parser.py
changeset 28910 1203159c8928
parent 28909 edbffdc7f6a0
child 29058 dbed4c4f48ae
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