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 |