##// END OF EJS Templates
parser: move alias declaration parser to common rule-set class...
Yuya Nishihara -
r28871:6d6201fc default
parent child Browse files
Show More
@@ -264,3 +264,108 b' class basealiasrules(object):'
264 def _getlist(tree):
264 def _getlist(tree):
265 """Extract a list of arguments from parsed tree"""
265 """Extract a list of arguments from parsed tree"""
266 raise NotImplementedError
266 raise NotImplementedError
267
268 @classmethod
269 def _builddecl(cls, decl):
270 """Parse an alias declaration into ``(name, tree, args, errorstr)``
271
272 This function analyzes the parsed tree. The parsing rule is provided
273 by ``_parsedecl()``.
274
275 - ``name``: of declared alias (may be ``decl`` itself at error)
276 - ``tree``: parse result (or ``None`` at error)
277 - ``args``: list of argument names (or None for symbol declaration)
278 - ``errorstr``: detail about detected error (or None)
279
280 >>> sym = lambda x: ('symbol', x)
281 >>> symlist = lambda *xs: ('list',) + tuple(sym(x) for x in xs)
282 >>> func = lambda n, a: ('func', sym(n), a)
283 >>> parsemap = {
284 ... 'foo': sym('foo'),
285 ... '$foo': sym('$foo'),
286 ... 'foo::bar': ('dagrange', sym('foo'), sym('bar')),
287 ... 'foo()': func('foo', None),
288 ... '$foo()': func('$foo', None),
289 ... 'foo($1, $2)': func('foo', symlist('$1', '$2')),
290 ... 'foo(bar_bar, baz.baz)':
291 ... func('foo', symlist('bar_bar', 'baz.baz')),
292 ... 'foo(bar($1, $2))':
293 ... func('foo', func('bar', symlist('$1', '$2'))),
294 ... 'foo($1, $2, nested($1, $2))':
295 ... func('foo', (symlist('$1', '$2') +
296 ... (func('nested', symlist('$1', '$2')),))),
297 ... 'foo("bar")': func('foo', ('string', 'bar')),
298 ... 'foo($1, $2': error.ParseError('unexpected token: end', 10),
299 ... 'foo("bar': error.ParseError('unterminated string', 5),
300 ... 'foo($1, $2, $1)': func('foo', symlist('$1', '$2', '$1')),
301 ... }
302 >>> def parse(expr):
303 ... x = parsemap[expr]
304 ... if isinstance(x, Exception):
305 ... raise x
306 ... return x
307 >>> def getlist(tree):
308 ... if not tree:
309 ... return []
310 ... if tree[0] == 'list':
311 ... return list(tree[1:])
312 ... return [tree]
313 >>> class aliasrules(basealiasrules):
314 ... _parsedecl = staticmethod(parse)
315 ... _getlist = staticmethod(getlist)
316 >>> builddecl = aliasrules._builddecl
317 >>> builddecl('foo')
318 ('foo', ('symbol', 'foo'), None, None)
319 >>> builddecl('$foo')
320 ('$foo', None, None, "'$' not for alias arguments")
321 >>> builddecl('foo::bar')
322 ('foo::bar', None, None, 'invalid format')
323 >>> builddecl('foo()')
324 ('foo', ('func', ('symbol', 'foo')), [], None)
325 >>> builddecl('$foo()')
326 ('$foo()', None, None, "'$' not for alias arguments")
327 >>> builddecl('foo($1, $2)')
328 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
329 >>> builddecl('foo(bar_bar, baz.baz)')
330 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
331 >>> builddecl('foo($1, $2, nested($1, $2))')
332 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
333 >>> builddecl('foo(bar($1, $2))')
334 ('foo(bar($1, $2))', None, None, 'invalid argument list')
335 >>> builddecl('foo("bar")')
336 ('foo("bar")', None, None, 'invalid argument list')
337 >>> builddecl('foo($1, $2')
338 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
339 >>> builddecl('foo("bar')
340 ('foo("bar', None, None, 'at 5: unterminated string')
341 >>> builddecl('foo($1, $2, $1)')
342 ('foo', None, None, 'argument names collide with each other')
343 """
344 try:
345 tree = cls._parsedecl(decl)
346 except error.ParseError as inst:
347 return (decl, None, None, parseerrordetail(inst))
348
349 if tree[0] == cls._symbolnode:
350 # "name = ...." style
351 name = tree[1]
352 if name.startswith('$'):
353 return (decl, None, None, _("'$' not for alias arguments"))
354 return (name, tree, None, None)
355
356 if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode:
357 # "name(arg, ....) = ...." style
358 name = tree[1][1]
359 if name.startswith('$'):
360 return (decl, None, None, _("'$' not for alias arguments"))
361 args = []
362 for arg in cls._getlist(tree[2]):
363 if arg[0] != cls._symbolnode:
364 return (decl, None, None, _("invalid argument list"))
365 args.append(arg[1])
366 if len(args) != len(set(args)):
367 return (name, None, None,
368 _("argument names collide with each other"))
369 return (name, tree[:2], args, None)
370
371 return (decl, None, None, _("invalid format"))
@@ -2237,75 +2237,18 b' def _tokenizealias(program, lookup=None)'
2237 def _parsealiasdecl(decl):
2237 def _parsealiasdecl(decl):
2238 """Parse alias declaration ``decl``
2238 """Parse alias declaration ``decl``
2239
2239
2240 This returns ``(name, tree, args, errorstr)`` tuple:
2240 >>> _parsealiasdecl('foo($1)')
2241
2241 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2242 - ``name``: of declared alias (may be ``decl`` itself at error)
2243 - ``tree``: parse result (or ``None`` at error)
2244 - ``args``: list of alias argument names (or None for symbol declaration)
2245 - ``errorstr``: detail about detected error (or None)
2246
2247 >>> _parsealiasdecl('foo')
2248 ('foo', ('symbol', 'foo'), None, None)
2249 >>> _parsealiasdecl('$foo')
2250 ('$foo', None, None, "'$' not for alias arguments")
2251 >>> _parsealiasdecl('foo::bar')
2252 ('foo::bar', None, None, 'invalid format')
2253 >>> _parsealiasdecl('foo bar')
2242 >>> _parsealiasdecl('foo bar')
2254 ('foo bar', None, None, 'at 4: invalid token')
2243 Traceback (most recent call last):
2255 >>> _parsealiasdecl('foo()')
2244 ...
2256 ('foo', ('func', ('symbol', 'foo')), [], None)
2245 ParseError: ('invalid token', 4)
2257 >>> _parsealiasdecl('$foo()')
2258 ('$foo()', None, None, "'$' not for alias arguments")
2259 >>> _parsealiasdecl('foo($1, $2)')
2260 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2261 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2262 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2263 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2264 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2265 >>> _parsealiasdecl('foo(bar($1, $2))')
2266 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2267 >>> _parsealiasdecl('foo("string")')
2268 ('foo("string")', None, None, 'invalid argument list')
2269 >>> _parsealiasdecl('foo($1, $2')
2270 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2271 >>> _parsealiasdecl('foo("string')
2272 ('foo("string', None, None, 'at 5: unterminated string')
2273 >>> _parsealiasdecl('foo($1, $2, $1)')
2274 ('foo', None, None, 'argument names collide with each other')
2275 """
2246 """
2276 p = parser.parser(elements)
2247 p = parser.parser(elements)
2277 try:
2248 tree, pos = p.parse(_tokenizealias(decl))
2278 tree, pos = p.parse(_tokenizealias(decl))
2249 if pos != len(decl):
2279 if (pos != len(decl)):
2250 raise error.ParseError(_('invalid token'), pos)
2280 raise error.ParseError(_('invalid token'), pos)
2251 return parser.simplifyinfixops(tree, ('list',))
2281 tree = parser.simplifyinfixops(tree, ('list',))
2282 except error.ParseError as inst:
2283 return (decl, None, None, parser.parseerrordetail(inst))
2284
2285 if True: # XXX to be removed
2286 if tree[0] == 'symbol':
2287 # "name = ...." style
2288 name = tree[1]
2289 if name.startswith('$'):
2290 return (decl, None, None, _("'$' not for alias arguments"))
2291 return (name, tree, None, None)
2292
2293 if tree[0] == 'func' and tree[1][0] == 'symbol':
2294 # "name(arg, ....) = ...." style
2295 name = tree[1][1]
2296 if name.startswith('$'):
2297 return (decl, None, None, _("'$' not for alias arguments"))
2298 args = []
2299 for arg in getlist(tree[2]):
2300 if arg[0] != 'symbol':
2301 return (decl, None, None, _("invalid argument list"))
2302 args.append(arg[1])
2303 if len(args) != len(set(args)):
2304 return (name, None, None,
2305 _("argument names collide with each other"))
2306 return (name, tree[:2], args, None)
2307
2308 return (decl, None, None, _("invalid format"))
2309
2252
2310 def _relabelaliasargs(tree, args):
2253 def _relabelaliasargs(tree, args):
2311 if not isinstance(tree, tuple):
2254 if not isinstance(tree, tuple):
@@ -2369,6 +2312,7 b' def _parsealiasdefn(defn, args):'
2369 class _aliasrules(parser.basealiasrules):
2312 class _aliasrules(parser.basealiasrules):
2370 """Parsing and expansion rule set of revset aliases"""
2313 """Parsing and expansion rule set of revset aliases"""
2371 _section = _('revset alias')
2314 _section = _('revset alias')
2315 _parsedecl = staticmethod(_parsealiasdecl)
2372 _getlist = staticmethod(getlist)
2316 _getlist = staticmethod(getlist)
2373
2317
2374 class revsetalias(object):
2318 class revsetalias(object):
@@ -2382,7 +2326,8 b' class revsetalias(object):'
2382 h = heads(default)
2326 h = heads(default)
2383 b($1) = ancestors($1) - ancestors(default)
2327 b($1) = ancestors($1) - ancestors(default)
2384 '''
2328 '''
2385 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2329 r = _aliasrules._builddecl(name)
2330 self.name, self.tree, self.args, self.error = r
2386 if self.error:
2331 if self.error:
2387 self.error = _('failed to parse the declaration of revset alias'
2332 self.error = _('failed to parse the declaration of revset alias'
2388 ' "%s": %s') % (self.name, self.error)
2333 ' "%s": %s') % (self.name, self.error)
General Comments 0
You need to be logged in to leave comments. Login now