Mercurial > public > mercurial-scm > hg
comparison mercurial/fileset.py @ 14551:68d814a3cefd
fileset: basic pattern and boolean support
debugfileset can now generate file lists for things like:
"* and not hg*"
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 08 Jun 2011 13:44:41 -0500 |
parents | 85fe676c27e9 |
children | 68db17047637 |
comparison
equal
deleted
inserted
replaced
14550:2425a3536396 | 14551:68d814a3cefd |
---|---|
3 # Copyright 2010 Matt Mackall <mpm@selenic.com> | 3 # Copyright 2010 Matt Mackall <mpm@selenic.com> |
4 # | 4 # |
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 import parser, error | 8 import parser, error, match |
9 from i18n import _ | 9 from i18n import _ |
10 | 10 |
11 elements = { | 11 elements = { |
12 "(": (20, ("group", 1, ")"), ("func", 1, ")")), | 12 "(": (20, ("group", 1, ")"), ("func", 1, ")")), |
13 "-": (5, ("negate", 19), ("minus", 5)), | 13 "-": (5, ("negate", 19), ("minus", 5)), |
24 "string": (0, ("string",), None), | 24 "string": (0, ("string",), None), |
25 "end": (0, None, None), | 25 "end": (0, None, None), |
26 } | 26 } |
27 | 27 |
28 keywords = set(['and', 'or', 'not']) | 28 keywords = set(['and', 'or', 'not']) |
29 | |
30 globchars = ".*{}[]?/\\" | |
29 | 31 |
30 def tokenize(program): | 32 def tokenize(program): |
31 pos, l = 0, len(program) | 33 pos, l = 0, len(program) |
32 while pos < l: | 34 while pos < l: |
33 c = program[pos] | 35 c = program[pos] |
54 yield ('string', decode(program[s:pos]), s) | 56 yield ('string', decode(program[s:pos]), s) |
55 break | 57 break |
56 pos += 1 | 58 pos += 1 |
57 else: | 59 else: |
58 raise error.ParseError(_("unterminated string"), s) | 60 raise error.ParseError(_("unterminated string"), s) |
59 elif c.isalnum() or c in '.*{}[]?' or ord(c) > 127: | 61 elif c.isalnum() or c in globchars or ord(c) > 127: |
60 # gather up a symbol/keyword | 62 # gather up a symbol/keyword |
61 s = pos | 63 s = pos |
62 pos += 1 | 64 pos += 1 |
63 while pos < l: # find end of symbol | 65 while pos < l: # find end of symbol |
64 d = program[pos] | 66 d = program[pos] |
65 if not (d.isalnum() or d in ".*{}[]?," or ord(d) > 127): | 67 if not (d.isalnum() or d in globchars or ord(d) > 127): |
66 break | 68 break |
67 pos += 1 | 69 pos += 1 |
68 sym = program[s:pos] | 70 sym = program[s:pos] |
69 if sym in keywords: # operator keywords | 71 if sym in keywords: # operator keywords |
70 yield (sym, None, s) | 72 yield (sym, None, s) |
76 pos += 1 | 78 pos += 1 |
77 yield ('end', None, pos) | 79 yield ('end', None, pos) |
78 | 80 |
79 parse = parser.parser(tokenize, elements).parse | 81 parse = parser.parser(tokenize, elements).parse |
80 | 82 |
83 def getstring(x, err): | |
84 if x and (x[0] == 'string' or x[0] == 'symbol'): | |
85 return x[1] | |
86 raise error.ParseError(err) | |
87 | |
88 def getset(mctx, x): | |
89 if not x: | |
90 raise error.ParseError(_("missing argument")) | |
91 return methods[x[0]](mctx, *x[1:]) | |
92 | |
93 def stringset(mctx, x): | |
94 m = mctx.matcher([x]) | |
95 return [f for f in mctx.subset if m(f)] | |
96 | |
97 def andset(mctx, x, y): | |
98 return getset(mctx.narrow(getset(mctx, x)), y) | |
99 | |
100 def orset(mctx, x, y): | |
101 # needs optimizing | |
102 xl = getset(mctx, x) | |
103 yl = getset(mctx, y) | |
104 return xl + [f for f in yl if f not in xl] | |
105 | |
106 def notset(mctx, x): | |
107 s = set(getset(mctx, x)) | |
108 return [r for r in mctx.subset if r not in s] | |
109 | |
110 def listset(mctx, a, b): | |
111 raise error.ParseError(_("can't use a list in this context")) | |
112 | |
113 methods = { | |
114 'string': stringset, | |
115 'symbol': stringset, | |
116 'and': andset, | |
117 'or': orset, | |
118 'list': listset, | |
119 'group': getset, | |
120 'not': notset | |
121 } | |
122 | |
123 class matchctx(object): | |
124 def __init__(self, ctx, matchfn, subset=None): | |
125 self.ctx = ctx | |
126 self.matchfn = matchfn | |
127 self.subset = subset | |
128 if subset is None: | |
129 self.subset = ctx.walk(matchfn([])) # optimize this later | |
130 def matcher(self, pattern): | |
131 return self.matchfn(pattern) | |
132 def filter(self, files): | |
133 return [f for f in files if f in self.subset] | |
134 def narrow(self, files): | |
135 return matchctx(self.ctx, self.matchfn, | |
136 self.filter(files)) | |
137 | |
138 def getfileset(ctx, matchfn, expr): | |
139 tree, pos = parse(expr) | |
140 if (pos != len(expr)): | |
141 raise error.ParseError("invalid token", pos) | |
142 return getset(matchctx(ctx, matchfn), tree) |