comparison mercurial/revset.py @ 23993:eedade006458

revset: introduce _parsealiasdefn to parse alias definitions strictly This patch introduces "_parsealiasdefn" to parse alias definitions strictly. For example, it can avoid problems below, which current implementation can't. - the shorter name argument breaks referring the longer name one in the definition, if the former is completely prefix of the latter for example, the alias definition "foo($1, $10) = $1 or $10" is parsed as "_aliasarg('$1') or _aliasarg('$1')0" and causes parse error, because tail "0" of "_aliasarg('$1')0" is invalid. - argument names in the quoted string are broken for example, the definition "foo($1) = $1 or desc('$1')" is parsed as "_aliasarg('$1') or desc('_aliasarg(\'$1\')')" and causes unexpected description matching against not '$1' but '_aliasarg(\'$1\')'. To decrease complication of patch, current implementation for alias definitions is replaced by "_parsealiasdefn" in the subsequent patch. This patch just introduces it. This patch defines "_parsealiasdefn" not as a method of "revsetalias" class but as a one of "revset" module, because of ease of testing by doctest.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Mon, 02 Feb 2015 23:07:04 +0900
parents eeb5d5ab14a6
children 8a2156780839
comparison
equal deleted inserted replaced
23992:db85e454fccc 23993:eedade006458
2240 2240
2241 return (decl, None, None, _("invalid format")) 2241 return (decl, None, None, _("invalid format"))
2242 except error.ParseError, inst: 2242 except error.ParseError, inst:
2243 return (decl, None, None, parseerrordetail(inst)) 2243 return (decl, None, None, parseerrordetail(inst))
2244 2244
2245 def _parsealiasdefn(defn, args):
2246 """Parse alias definition ``defn``
2247
2248 This function also replaces alias argument references in the
2249 specified definition by ``_aliasarg(ARGNAME)``.
2250
2251 ``args`` is a list of alias argument names, or None if the alias
2252 is declared as a symbol.
2253
2254 This returns "tree" as parsing result.
2255
2256 >>> args = ['$1', '$2', 'foo']
2257 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2258 (or
2259 (func
2260 ('symbol', '_aliasarg')
2261 ('string', '$1'))
2262 (func
2263 ('symbol', '_aliasarg')
2264 ('string', 'foo')))
2265 >>> try:
2266 ... _parsealiasdefn('$1 or $bar', args)
2267 ... except error.ParseError, inst:
2268 ... print parseerrordetail(inst)
2269 at 6: '$' not for alias arguments
2270 >>> args = ['$1', '$10', 'foo']
2271 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2272 (or
2273 (func
2274 ('symbol', '_aliasarg')
2275 ('string', '$10'))
2276 ('symbol', 'foobar'))
2277 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2278 (or
2279 ('string', '$1')
2280 ('string', 'foo'))
2281 """
2282 def tokenizedefn(program, lookup=None):
2283 if args:
2284 argset = set(args)
2285 else:
2286 argset = set()
2287
2288 for t, value, pos in _tokenizealias(program, lookup=lookup):
2289 if t == 'symbol':
2290 if value in argset:
2291 # emulate tokenization of "_aliasarg('ARGNAME')":
2292 # "_aliasarg()" is an unknown symbol only used separate
2293 # alias argument placeholders from regular strings.
2294 yield ('symbol', '_aliasarg', pos)
2295 yield ('(', None, pos)
2296 yield ('string', value, pos)
2297 yield (')', None, pos)
2298 continue
2299 elif value.startswith('$'):
2300 raise error.ParseError(_("'$' not for alias arguments"),
2301 pos)
2302 yield (t, value, pos)
2303
2304 p = parser.parser(tokenizedefn, elements)
2305 tree, pos = p.parse(defn)
2306 if pos != len(defn):
2307 raise error.ParseError(_('invalid token'), pos)
2308 return tree
2309
2245 class revsetalias(object): 2310 class revsetalias(object):
2246 # whether own `error` information is already shown or not. 2311 # whether own `error` information is already shown or not.
2247 # this avoids showing same warning multiple times at each `findaliases`. 2312 # this avoids showing same warning multiple times at each `findaliases`.
2248 warned = False 2313 warned = False
2249 2314