Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/revset.py @ 29934:90455e7bf543
revset: infer ordering flag to teach if operation should define/follow order
New flag 'order' is the hint to determine if a function or operation can
enforce its ordering requirement or take the ordering already defined. It
will be used to fix a couple of ordering bugs, such as:
a) 'x & (y | z)' disregards the order of 'x' (issue5100)
b) 'x & y:z' is listed from 'y' to 'z'
c) 'x & y' can be rewritten as 'y & x' if weight(x) > weight(y)
(a) and (b) are bugs of the revset core. Before this, there was no way to
tell if 'orset()' and 'rangeset()' can enforce its ordering. These bugs
could be addressed by overriding __and__() of the initial set to take the
ordering of the other set:
class fullreposet:
def __and__(self, other):
# allow other to enforce its ordering
return other
but it would expose (c), which is a hidden bug of optimize(). So, in either
ways, optimize() have to know the current ordering requirement. Otherwise,
it couldn't rewrite expressions by weights with no output change, nor tell
how a revset function or operation should order the entries.
'order' is tri-state. It starts with 'define', and shifts to 'follow' by
'x & y'. It changes back to 'define' on function call 'f(x)' or function-like
operation 'x (f) y' because 'f' may have its own ordering requirement for 'x'
and 'y'. The state 'any' will allow us to avoid extra cost that would be
necessary to constrain ordering where it isn't important, 'not x'.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Tue, 16 Feb 2016 22:02:16 +0900 |
parents | b3845cab4ddc |
children | d2d1be3009ca |
comparison
equal
deleted
inserted
replaced
29933:b3845cab4ddc | 29934:90455e7bf543 |
---|---|
2308 "ancestor": ancestorspec, | 2308 "ancestor": ancestorspec, |
2309 "parent": parentspec, | 2309 "parent": parentspec, |
2310 "parentpost": p1, | 2310 "parentpost": p1, |
2311 } | 2311 } |
2312 | 2312 |
2313 # Constants for ordering requirement, used in _analyze(): | |
2314 # | |
2315 # If 'define', any nested functions and operations can change the ordering of | |
2316 # the entries in the set. If 'follow', any nested functions and operations | |
2317 # should take the ordering specified by the first operand to the '&' operator. | |
2318 # | |
2319 # For instance, | |
2320 # | |
2321 # X & (Y | Z) | |
2322 # ^ ^^^^^^^ | |
2323 # | follow | |
2324 # define | |
2325 # | |
2326 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order | |
2327 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't. | |
2328 # | |
2329 # 'any' means the order doesn't matter. For instance, | |
2330 # | |
2331 # X & !Y | |
2332 # ^ | |
2333 # any | |
2334 # | |
2335 # 'y()' can either enforce its ordering requirement or take the ordering | |
2336 # specified by 'x()' because 'not()' doesn't care the order. | |
2337 # | |
2338 # Transition of ordering requirement: | |
2339 # | |
2340 # 1. starts with 'define' | |
2341 # 2. shifts to 'follow' by 'x & y' | |
2342 # 3. changes back to 'define' on function call 'f(x)' or function-like | |
2343 # operation 'x (f) y' because 'f' may have its own ordering requirement | |
2344 # for 'x' and 'y' (e.g. 'first(x)') | |
2345 # | |
2346 anyorder = 'any' # don't care the order | |
2347 defineorder = 'define' # should define the order | |
2348 followorder = 'follow' # must follow the current order | |
2349 | |
2350 # transition table for 'x & y', from the current expression 'x' to 'y' | |
2351 _tofolloworder = { | |
2352 anyorder: anyorder, | |
2353 defineorder: followorder, | |
2354 followorder: followorder, | |
2355 } | |
2356 | |
2313 def _matchonly(revs, bases): | 2357 def _matchonly(revs, bases): |
2314 """ | 2358 """ |
2315 >>> f = lambda *args: _matchonly(*map(parse, args)) | 2359 >>> f = lambda *args: _matchonly(*map(parse, args)) |
2316 >>> f('ancestors(A)', 'not ancestors(B)') | 2360 >>> f('ancestors(A)', 'not ancestors(B)') |
2317 ('list', ('symbol', 'A'), ('symbol', 'B')) | 2361 ('list', ('symbol', 'A'), ('symbol', 'B')) |
2347 # x + y + z -> (or x y z) -> (or (list x y z)) | 2391 # x + y + z -> (or x y z) -> (or (list x y z)) |
2348 return (op, _fixops(('list',) + x[1:])) | 2392 return (op, _fixops(('list',) + x[1:])) |
2349 | 2393 |
2350 return (op,) + tuple(_fixops(y) for y in x[1:]) | 2394 return (op,) + tuple(_fixops(y) for y in x[1:]) |
2351 | 2395 |
2352 def _analyze(x): | 2396 def _analyze(x, order): |
2353 if x is None: | 2397 if x is None: |
2354 return x | 2398 return x |
2355 | 2399 |
2356 op = x[0] | 2400 op = x[0] |
2357 if op == 'minus': | 2401 if op == 'minus': |
2358 return _analyze(('and', x[1], ('not', x[2]))) | 2402 return _analyze(('and', x[1], ('not', x[2])), order) |
2359 elif op == 'only': | 2403 elif op == 'only': |
2360 t = ('func', ('symbol', 'only'), ('list', x[1], x[2])) | 2404 t = ('func', ('symbol', 'only'), ('list', x[1], x[2])) |
2361 return _analyze(t) | 2405 return _analyze(t, order) |
2362 elif op == 'onlypost': | 2406 elif op == 'onlypost': |
2363 return _analyze(('func', ('symbol', 'only'), x[1])) | 2407 return _analyze(('func', ('symbol', 'only'), x[1]), order) |
2364 elif op == 'dagrangepre': | 2408 elif op == 'dagrangepre': |
2365 return _analyze(('func', ('symbol', 'ancestors'), x[1])) | 2409 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order) |
2366 elif op == 'dagrangepost': | 2410 elif op == 'dagrangepost': |
2367 return _analyze(('func', ('symbol', 'descendants'), x[1])) | 2411 return _analyze(('func', ('symbol', 'descendants'), x[1]), order) |
2368 elif op == 'rangeall': | 2412 elif op == 'rangeall': |
2369 return _analyze(('range', ('string', '0'), ('string', 'tip'))) | 2413 return _analyze(('range', ('string', '0'), ('string', 'tip')), order) |
2370 elif op == 'rangepre': | 2414 elif op == 'rangepre': |
2371 return _analyze(('range', ('string', '0'), x[1])) | 2415 return _analyze(('range', ('string', '0'), x[1]), order) |
2372 elif op == 'rangepost': | 2416 elif op == 'rangepost': |
2373 return _analyze(('range', x[1], ('string', 'tip'))) | 2417 return _analyze(('range', x[1], ('string', 'tip')), order) |
2374 elif op == 'negate': | 2418 elif op == 'negate': |
2375 s = getstring(x[1], _("can't negate that")) | 2419 s = getstring(x[1], _("can't negate that")) |
2376 return _analyze(('string', '-' + s)) | 2420 return _analyze(('string', '-' + s), order) |
2377 elif op in ('string', 'symbol'): | 2421 elif op in ('string', 'symbol'): |
2378 return x | 2422 return x |
2379 elif op == 'and': | 2423 elif op == 'and': |
2380 ta = _analyze(x[1]) | 2424 ta = _analyze(x[1], order) |
2381 tb = _analyze(x[2]) | 2425 tb = _analyze(x[2], _tofolloworder[order]) |
2382 return (op, ta, tb) | 2426 return (op, ta, tb) |
2383 elif op == 'or': | 2427 elif op == 'or': |
2384 return (op, _analyze(x[1])) | 2428 return (op, _analyze(x[1], order)) |
2385 elif op == 'not': | 2429 elif op == 'not': |
2386 return (op, _analyze(x[1])) | 2430 return (op, _analyze(x[1], anyorder)) |
2387 elif op == 'parentpost': | 2431 elif op == 'parentpost': |
2388 return (op, _analyze(x[1])) | 2432 return (op, _analyze(x[1], defineorder)) |
2389 elif op == 'group': | 2433 elif op == 'group': |
2390 return _analyze(x[1]) | 2434 return _analyze(x[1], order) |
2391 elif op in ('dagrange', 'range', 'parent', 'ancestor'): | 2435 elif op in ('dagrange', 'range', 'parent', 'ancestor'): |
2392 ta = _analyze(x[1]) | 2436 ta = _analyze(x[1], defineorder) |
2393 tb = _analyze(x[2]) | 2437 tb = _analyze(x[2], defineorder) |
2394 return (op, ta, tb) | 2438 return (op, ta, tb) |
2395 elif op == 'list': | 2439 elif op == 'list': |
2396 return (op,) + tuple(_analyze(y) for y in x[1:]) | 2440 return (op,) + tuple(_analyze(y, order) for y in x[1:]) |
2397 elif op == 'keyvalue': | 2441 elif op == 'keyvalue': |
2398 return (op, x[1], _analyze(x[2])) | 2442 return (op, x[1], _analyze(x[2], order)) |
2399 elif op == 'func': | 2443 elif op == 'func': |
2400 return (op, x[1], _analyze(x[2])) | 2444 return (op, x[1], _analyze(x[2], defineorder)) |
2401 raise ValueError('invalid operator %r' % op) | 2445 raise ValueError('invalid operator %r' % op) |
2402 | 2446 |
2403 def analyze(x): | 2447 def analyze(x, order=defineorder): |
2404 """Transform raw parsed tree to evaluatable tree which can be fed to | 2448 """Transform raw parsed tree to evaluatable tree which can be fed to |
2405 optimize() or getset() | 2449 optimize() or getset() |
2406 | 2450 |
2407 All pseudo operations should be mapped to real operations or functions | 2451 All pseudo operations should be mapped to real operations or functions |
2408 defined in methods or symbols table respectively. | 2452 defined in methods or symbols table respectively. |
2409 """ | 2453 |
2410 return _analyze(x) | 2454 'order' specifies how the current expression 'x' is ordered (see the |
2455 constants defined above.) | |
2456 """ | |
2457 return _analyze(x, order) | |
2411 | 2458 |
2412 def _optimize(x, small): | 2459 def _optimize(x, small): |
2413 if x is None: | 2460 if x is None: |
2414 return 0, x | 2461 return 0, x |
2415 | 2462 |