Show More
@@ -43,6 +43,9 b' def getmatch(mctx, x):' | |||||
43 | raise error.ParseError(_("missing argument")) |
|
43 | raise error.ParseError(_("missing argument")) | |
44 | return methods[x[0]](mctx, *x[1:]) |
|
44 | return methods[x[0]](mctx, *x[1:]) | |
45 |
|
45 | |||
|
46 | def getmatchwithstatus(mctx, x, hint): | |||
|
47 | return getmatch(mctx, x) | |||
|
48 | ||||
46 | def stringmatch(mctx, x): |
|
49 | def stringmatch(mctx, x): | |
47 | return mctx.matcher([x]) |
|
50 | return mctx.matcher([x]) | |
48 |
|
51 | |||
@@ -443,6 +446,7 b' def subrepo(mctx, x):' | |||||
443 | return mctx.predicate(sstate.__contains__, predrepr='subrepo') |
|
446 | return mctx.predicate(sstate.__contains__, predrepr='subrepo') | |
444 |
|
447 | |||
445 | methods = { |
|
448 | methods = { | |
|
449 | 'withstatus': getmatchwithstatus, | |||
446 | 'string': stringmatch, |
|
450 | 'string': stringmatch, | |
447 | 'symbol': stringmatch, |
|
451 | 'symbol': stringmatch, | |
448 | 'kindpat': kindpatmatch, |
|
452 | 'kindpat': kindpatmatch, |
@@ -171,6 +171,82 b' def _analyze(x):' | |||||
171 | return (op, x[1], ta) |
|
171 | return (op, x[1], ta) | |
172 | raise error.ProgrammingError('invalid operator %r' % op) |
|
172 | raise error.ProgrammingError('invalid operator %r' % op) | |
173 |
|
173 | |||
|
174 | def _insertstatushints(x): | |||
|
175 | """Insert hint nodes where status should be calculated (first path) | |||
|
176 | ||||
|
177 | This works in bottom-up way, summing up status names and inserting hint | |||
|
178 | nodes at 'and' and 'or' as needed. Thus redundant hint nodes may be left. | |||
|
179 | ||||
|
180 | Returns (status-names, new-tree) at the given subtree, where status-names | |||
|
181 | is a sum of status names referenced in the given subtree. | |||
|
182 | """ | |||
|
183 | if x is None: | |||
|
184 | return (), x | |||
|
185 | ||||
|
186 | op = x[0] | |||
|
187 | if op in {'string', 'symbol', 'kindpat'}: | |||
|
188 | return (), x | |||
|
189 | if op == 'not': | |||
|
190 | h, t = _insertstatushints(x[1]) | |||
|
191 | return h, (op, t) | |||
|
192 | if op == 'and': | |||
|
193 | ha, ta = _insertstatushints(x[1]) | |||
|
194 | hb, tb = _insertstatushints(x[2]) | |||
|
195 | hr = ha + hb | |||
|
196 | if ha and hb: | |||
|
197 | return hr, ('withstatus', (op, ta, tb), ('string', ' '.join(hr))) | |||
|
198 | return hr, (op, ta, tb) | |||
|
199 | if op == 'or': | |||
|
200 | hs, ts = zip(*(_insertstatushints(y) for y in x[1:])) | |||
|
201 | hr = sum(hs, ()) | |||
|
202 | if sum(bool(h) for h in hs) > 1: | |||
|
203 | return hr, ('withstatus', (op,) + ts, ('string', ' '.join(hr))) | |||
|
204 | return hr, (op,) + ts | |||
|
205 | if op == 'list': | |||
|
206 | hs, ts = zip(*(_insertstatushints(y) for y in x[1:])) | |||
|
207 | return sum(hs, ()), (op,) + ts | |||
|
208 | if op == 'func': | |||
|
209 | f = getsymbol(x[1]) | |||
|
210 | # don't propagate 'ha' crossing a function boundary | |||
|
211 | ha, ta = _insertstatushints(x[2]) | |||
|
212 | if getattr(symbols.get(f), '_callstatus', False): | |||
|
213 | return (f,), ('withstatus', (op, x[1], ta), ('string', f)) | |||
|
214 | return (), (op, x[1], ta) | |||
|
215 | raise error.ProgrammingError('invalid operator %r' % op) | |||
|
216 | ||||
|
217 | def _mergestatushints(x, instatus): | |||
|
218 | """Remove redundant status hint nodes (second path) | |||
|
219 | ||||
|
220 | This is the top-down path to eliminate inner hint nodes. | |||
|
221 | """ | |||
|
222 | if x is None: | |||
|
223 | return x | |||
|
224 | ||||
|
225 | op = x[0] | |||
|
226 | if op == 'withstatus': | |||
|
227 | if instatus: | |||
|
228 | # drop redundant hint node | |||
|
229 | return _mergestatushints(x[1], instatus) | |||
|
230 | t = _mergestatushints(x[1], instatus=True) | |||
|
231 | return (op, t, x[2]) | |||
|
232 | if op in {'string', 'symbol', 'kindpat'}: | |||
|
233 | return x | |||
|
234 | if op == 'not': | |||
|
235 | t = _mergestatushints(x[1], instatus) | |||
|
236 | return (op, t) | |||
|
237 | if op == 'and': | |||
|
238 | ta = _mergestatushints(x[1], instatus) | |||
|
239 | tb = _mergestatushints(x[2], instatus) | |||
|
240 | return (op, ta, tb) | |||
|
241 | if op in {'list', 'or'}: | |||
|
242 | ts = tuple(_mergestatushints(y, instatus) for y in x[1:]) | |||
|
243 | return (op,) + ts | |||
|
244 | if op == 'func': | |||
|
245 | # don't propagate 'instatus' crossing a function boundary | |||
|
246 | ta = _mergestatushints(x[2], instatus=False) | |||
|
247 | return (op, x[1], ta) | |||
|
248 | raise error.ProgrammingError('invalid operator %r' % op) | |||
|
249 | ||||
174 | def analyze(x): |
|
250 | def analyze(x): | |
175 | """Transform raw parsed tree to evaluatable tree which can be fed to |
|
251 | """Transform raw parsed tree to evaluatable tree which can be fed to | |
176 | optimize() or getmatch() |
|
252 | optimize() or getmatch() | |
@@ -178,7 +254,9 b' def analyze(x):' | |||||
178 | All pseudo operations should be mapped to real operations or functions |
|
254 | All pseudo operations should be mapped to real operations or functions | |
179 | defined in methods or symbols table respectively. |
|
255 | defined in methods or symbols table respectively. | |
180 | """ |
|
256 | """ | |
181 |
|
|
257 | t = _analyze(x) | |
|
258 | _h, t = _insertstatushints(t) | |||
|
259 | return _mergestatushints(t, instatus=False) | |||
182 |
|
260 | |||
183 | def _optimizeandops(op, ta, tb): |
|
261 | def _optimizeandops(op, ta, tb): | |
184 | if tb is not None and tb[0] == 'not': |
|
262 | if tb is not None and tb[0] == 'not': | |
@@ -205,6 +283,9 b' def _optimize(x):' | |||||
205 | return 0, x |
|
283 | return 0, x | |
206 |
|
284 | |||
207 | op = x[0] |
|
285 | op = x[0] | |
|
286 | if op == 'withstatus': | |||
|
287 | w, t = _optimize(x[1]) | |||
|
288 | return w, (op, t, x[2]) | |||
208 | if op in {'string', 'symbol'}: |
|
289 | if op in {'string', 'symbol'}: | |
209 | return WEIGHT_CHECK_FILENAME, x |
|
290 | return WEIGHT_CHECK_FILENAME, x | |
210 | if op == 'kindpat': |
|
291 | if op == 'kindpat': |
@@ -24,7 +24,9 b' def _compile(tree):' | |||||
24 | if not tree: |
|
24 | if not tree: | |
25 | raise error.ParseError(_("missing argument")) |
|
25 | raise error.ParseError(_("missing argument")) | |
26 | op = tree[0] |
|
26 | op = tree[0] | |
27 | if op in {'symbol', 'string', 'kindpat'}: |
|
27 | if op == 'withstatus': | |
|
28 | return _compile(tree[1]) | |||
|
29 | elif op in {'symbol', 'string', 'kindpat'}: | |||
28 | name = filesetlang.getpattern(tree, {'path'}, _('invalid file pattern')) |
|
30 | name = filesetlang.getpattern(tree, {'path'}, _('invalid file pattern')) | |
29 | if name.startswith('**'): # file extension test, ex. "**.tar.gz" |
|
31 | if name.startswith('**'): # file extension test, ex. "**.tar.gz" | |
30 | ext = name[2:] |
|
32 | ext = name[2:] |
@@ -175,18 +175,22 b' Show parsed tree at stages:' | |||||
175 | (func |
|
175 | (func | |
176 | (symbol 'grep') |
|
176 | (symbol 'grep') | |
177 | (string 'b')) |
|
177 | (string 'b')) | |
178 | (func |
|
178 | (withstatus | |
179 | (symbol 'clean') |
|
179 | (func | |
180 | None))) |
|
180 | (symbol 'clean') | |
|
181 | None) | |||
|
182 | (string 'clean')))) | |||
181 | * optimized: |
|
183 | * optimized: | |
182 | (or |
|
184 | (or | |
183 | (patterns |
|
185 | (patterns | |
184 | (symbol 'a1') |
|
186 | (symbol 'a1') | |
185 | (symbol 'a2')) |
|
187 | (symbol 'a2')) | |
186 | (and |
|
188 | (and | |
187 | (func |
|
189 | (withstatus | |
188 | (symbol 'clean') |
|
190 | (func | |
189 | None) |
|
191 | (symbol 'clean') | |
|
192 | None) | |||
|
193 | (string 'clean')) | |||
190 | (func |
|
194 | (func | |
191 | (symbol 'grep') |
|
195 | (symbol 'grep') | |
192 | (string 'b')))) |
|
196 | (string 'b')))) | |
@@ -374,6 +378,156 b' Test files status in different revisions' | |||||
374 | b2 |
|
378 | b2 | |
375 | c1 |
|
379 | c1 | |
376 |
|
380 | |||
|
381 | Test insertion of status hints | |||
|
382 | ||||
|
383 | $ fileset -p optimized 'added()' | |||
|
384 | * optimized: | |||
|
385 | (withstatus | |||
|
386 | (func | |||
|
387 | (symbol 'added') | |||
|
388 | None) | |||
|
389 | (string 'added')) | |||
|
390 | c1 | |||
|
391 | ||||
|
392 | $ fileset -p optimized 'a* & removed()' | |||
|
393 | * optimized: | |||
|
394 | (and | |||
|
395 | (symbol 'a*') | |||
|
396 | (withstatus | |||
|
397 | (func | |||
|
398 | (symbol 'removed') | |||
|
399 | None) | |||
|
400 | (string 'removed'))) | |||
|
401 | a2 | |||
|
402 | ||||
|
403 | $ fileset -p optimized 'a* - removed()' | |||
|
404 | * optimized: | |||
|
405 | (minus | |||
|
406 | (symbol 'a*') | |||
|
407 | (withstatus | |||
|
408 | (func | |||
|
409 | (symbol 'removed') | |||
|
410 | None) | |||
|
411 | (string 'removed'))) | |||
|
412 | a1 | |||
|
413 | ||||
|
414 | $ fileset -p analyzed -p optimized '(added() + removed()) - a*' | |||
|
415 | * analyzed: | |||
|
416 | (and | |||
|
417 | (withstatus | |||
|
418 | (or | |||
|
419 | (func | |||
|
420 | (symbol 'added') | |||
|
421 | None) | |||
|
422 | (func | |||
|
423 | (symbol 'removed') | |||
|
424 | None)) | |||
|
425 | (string 'added removed')) | |||
|
426 | (not | |||
|
427 | (symbol 'a*'))) | |||
|
428 | * optimized: | |||
|
429 | (and | |||
|
430 | (not | |||
|
431 | (symbol 'a*')) | |||
|
432 | (withstatus | |||
|
433 | (or | |||
|
434 | (func | |||
|
435 | (symbol 'added') | |||
|
436 | None) | |||
|
437 | (func | |||
|
438 | (symbol 'removed') | |||
|
439 | None)) | |||
|
440 | (string 'added removed'))) | |||
|
441 | c1 | |||
|
442 | ||||
|
443 | $ fileset -p optimized 'a* + b* + added() + unknown()' | |||
|
444 | * optimized: | |||
|
445 | (withstatus | |||
|
446 | (or | |||
|
447 | (patterns | |||
|
448 | (symbol 'a*') | |||
|
449 | (symbol 'b*')) | |||
|
450 | (func | |||
|
451 | (symbol 'added') | |||
|
452 | None) | |||
|
453 | (func | |||
|
454 | (symbol 'unknown') | |||
|
455 | None)) | |||
|
456 | (string 'added unknown')) | |||
|
457 | a1 | |||
|
458 | a2 | |||
|
459 | b1 | |||
|
460 | b2 | |||
|
461 | c1 | |||
|
462 | c3 | |||
|
463 | ||||
|
464 | $ fileset -p analyzed -p optimized 'removed() & missing() & a*' | |||
|
465 | * analyzed: | |||
|
466 | (and | |||
|
467 | (withstatus | |||
|
468 | (and | |||
|
469 | (func | |||
|
470 | (symbol 'removed') | |||
|
471 | None) | |||
|
472 | (func | |||
|
473 | (symbol 'missing') | |||
|
474 | None)) | |||
|
475 | (string 'removed missing')) | |||
|
476 | (symbol 'a*')) | |||
|
477 | * optimized: | |||
|
478 | (and | |||
|
479 | (symbol 'a*') | |||
|
480 | (withstatus | |||
|
481 | (and | |||
|
482 | (func | |||
|
483 | (symbol 'removed') | |||
|
484 | None) | |||
|
485 | (func | |||
|
486 | (symbol 'missing') | |||
|
487 | None)) | |||
|
488 | (string 'removed missing'))) | |||
|
489 | ||||
|
490 | $ fileset -p optimized 'clean() & revs(0, added())' | |||
|
491 | * optimized: | |||
|
492 | (and | |||
|
493 | (withstatus | |||
|
494 | (func | |||
|
495 | (symbol 'clean') | |||
|
496 | None) | |||
|
497 | (string 'clean')) | |||
|
498 | (func | |||
|
499 | (symbol 'revs') | |||
|
500 | (list | |||
|
501 | (symbol '0') | |||
|
502 | (withstatus | |||
|
503 | (func | |||
|
504 | (symbol 'added') | |||
|
505 | None) | |||
|
506 | (string 'added'))))) | |||
|
507 | b1 | |||
|
508 | ||||
|
509 | $ fileset -p optimized 'clean() & status(null, 0, b* & added())' | |||
|
510 | * optimized: | |||
|
511 | (and | |||
|
512 | (withstatus | |||
|
513 | (func | |||
|
514 | (symbol 'clean') | |||
|
515 | None) | |||
|
516 | (string 'clean')) | |||
|
517 | (func | |||
|
518 | (symbol 'status') | |||
|
519 | (list | |||
|
520 | (symbol 'null') | |||
|
521 | (symbol '0') | |||
|
522 | (and | |||
|
523 | (symbol 'b*') | |||
|
524 | (withstatus | |||
|
525 | (func | |||
|
526 | (symbol 'added') | |||
|
527 | None) | |||
|
528 | (string 'added')))))) | |||
|
529 | b1 | |||
|
530 | ||||
377 | Test files properties |
|
531 | Test files properties | |
378 |
|
532 | |||
379 | >>> open('bin', 'wb').write(b'\0a') and None |
|
533 | >>> open('bin', 'wb').write(b'\0a') and None |
General Comments 0
You need to be logged in to leave comments.
Login now