Show More
@@ -264,3 +264,108 b' class basealiasrules(object):' | |||
|
264 | 264 | def _getlist(tree): |
|
265 | 265 | """Extract a list of arguments from parsed tree""" |
|
266 | 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 | 2237 | def _parsealiasdecl(decl): |
|
2238 | 2238 | """Parse alias declaration ``decl`` |
|
2239 | 2239 | |
|
2240 | This returns ``(name, tree, args, errorstr)`` tuple: | |
|
2241 | ||
|
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') | |
|
2240 | >>> _parsealiasdecl('foo($1)') | |
|
2241 | ('func', ('symbol', 'foo'), ('symbol', '$1')) | |
|
2253 | 2242 | >>> _parsealiasdecl('foo bar') |
|
2254 | ('foo bar', None, None, 'at 4: invalid token') | |
|
2255 | >>> _parsealiasdecl('foo()') | |
|
2256 | ('foo', ('func', ('symbol', 'foo')), [], None) | |
|
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') | |
|
2243 | Traceback (most recent call last): | |
|
2244 | ... | |
|
2245 | ParseError: ('invalid token', 4) | |
|
2275 | 2246 | """ |
|
2276 | 2247 | p = parser.parser(elements) |
|
2277 | try: | |
|
2278 | tree, pos = p.parse(_tokenizealias(decl)) | |
|
2279 | if (pos != len(decl)): | |
|
2280 | raise error.ParseError(_('invalid token'), pos) | |
|
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")) | |
|
2248 | tree, pos = p.parse(_tokenizealias(decl)) | |
|
2249 | if pos != len(decl): | |
|
2250 | raise error.ParseError(_('invalid token'), pos) | |
|
2251 | return parser.simplifyinfixops(tree, ('list',)) | |
|
2309 | 2252 | |
|
2310 | 2253 | def _relabelaliasargs(tree, args): |
|
2311 | 2254 | if not isinstance(tree, tuple): |
@@ -2369,6 +2312,7 b' def _parsealiasdefn(defn, args):' | |||
|
2369 | 2312 | class _aliasrules(parser.basealiasrules): |
|
2370 | 2313 | """Parsing and expansion rule set of revset aliases""" |
|
2371 | 2314 | _section = _('revset alias') |
|
2315 | _parsedecl = staticmethod(_parsealiasdecl) | |
|
2372 | 2316 | _getlist = staticmethod(getlist) |
|
2373 | 2317 | |
|
2374 | 2318 | class revsetalias(object): |
@@ -2382,7 +2326,8 b' class revsetalias(object):' | |||
|
2382 | 2326 | h = heads(default) |
|
2383 | 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 | 2331 | if self.error: |
|
2387 | 2332 | self.error = _('failed to parse the declaration of revset alias' |
|
2388 | 2333 | ' "%s": %s') % (self.name, self.error) |
General Comments 0
You need to be logged in to leave comments.
Login now