##// END OF EJS Templates
revset: add support for prefix and suffix versions of : and ::
Matt Mackall -
r11278:7df88cdf default
parent child Browse files
Show More
@@ -1,79 +1,88 b''
1 # parser.py - simple top-down operator precedence parser for mercurial
1 # parser.py - simple top-down operator precedence parser for mercurial
2 #
2 #
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 # see http://effbot.org/zone/simple-top-down-parsing.txt and
8 # see http://effbot.org/zone/simple-top-down-parsing.txt and
9 # http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
9 # http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
10 # for background
10 # for background
11
11
12 # takes a tokenizer and elements
12 # takes a tokenizer and elements
13 # tokenizer is an iterator that returns type, value pairs
13 # tokenizer is an iterator that returns type, value pairs
14 # elements is a mapping of types to binding strength, prefix and infix actions
14 # elements is a mapping of types to binding strength, prefix and infix actions
15 # an action is a tree node name, a tree label, and an optional match
15 # an action is a tree node name, a tree label, and an optional match
16 # __call__(program) parses program into a labelled tree
16 # __call__(program) parses program into a labelled tree
17
17
18 class parser(object):
18 class parser(object):
19 def __init__(self, tokenizer, elements, methods=None):
19 def __init__(self, tokenizer, elements, methods=None):
20 self._tokenizer = tokenizer
20 self._tokenizer = tokenizer
21 self._elements = elements
21 self._elements = elements
22 self._methods = methods
22 self._methods = methods
23 def _advance(self):
23 def _advance(self):
24 'advance the tokenizer'
24 'advance the tokenizer'
25 t = self.current
25 t = self.current
26 self.current = self._iter.next()
26 try:
27 self.current = self._iter.next()
28 except StopIteration:
29 pass
27 return t
30 return t
28 def _match(self, m):
31 def _match(self, m):
29 'make sure the tokenizer matches an end condition'
32 'make sure the tokenizer matches an end condition'
30 if self.current[0] != m:
33 if self.current[0] != m:
31 raise SyntaxError(self.current)
34 raise SyntaxError(self.current)
32 self._advance()
35 self._advance()
33 def _parse(self, bind=0):
36 def _parse(self, bind=0):
34 token, value = self._advance()
37 token, value = self._advance()
35 # handle prefix rules on current token
38 # handle prefix rules on current token
36 prefix = self._elements[token][1]
39 prefix = self._elements[token][1]
37 if not prefix:
40 if not prefix:
38 raise SyntaxError("not a prefix: %s" % token)
41 raise SyntaxError("not a prefix: %s" % token)
39 if len(prefix) == 1:
42 if len(prefix) == 1:
40 expr = (prefix[0], value)
43 expr = (prefix[0], value)
41 else:
44 else:
42 if len(prefix) > 2 and prefix[2] == self.current[0]:
45 if len(prefix) > 2 and prefix[2] == self.current[0]:
43 self._match(prefix[2])
46 self._match(prefix[2])
44 expr = (prefix[0], None)
47 expr = (prefix[0], None)
45 else:
48 else:
46 expr = (prefix[0], self._parse(prefix[1]))
49 expr = (prefix[0], self._parse(prefix[1]))
47 if len(prefix) > 2:
50 if len(prefix) > 2:
48 self._match(prefix[2])
51 self._match(prefix[2])
49 # gather tokens until we meet a lower binding strength
52 # gather tokens until we meet a lower binding strength
50 while bind < self._elements[self.current[0]][0]:
53 while bind < self._elements[self.current[0]][0]:
51 token, value = self._advance()
54 token, value = self._advance()
52 # handle infix rules
55 e = self._elements[token]
53 infix = self._elements[token][2]
56 # check for suffix - next token isn't a valid prefix
54 if len(infix) == 3 and infix[2] == self.current[0]:
57 if len(e) == 4 and not self._elements[self.current[0]][1]:
55 self._match(infix[2])
58 suffix = e[3]
56 expr = (infix[0], expr, (None))
59 expr = (suffix[0], expr)
57 else:
60 else:
58 if not infix[0]:
61 # handle infix rules
59 raise SyntaxError("not an infix")
62 infix = self._elements[token][2]
60 expr = (infix[0], expr, self._parse(infix[1]))
63 if len(infix) == 3 and infix[2] == self.current[0]:
61 if len(infix) == 3:
62 self._match(infix[2])
64 self._match(infix[2])
65 expr = (infix[0], expr, (None))
66 else:
67 if not infix[0]:
68 raise SyntaxError("not an infix")
69 expr = (infix[0], expr, self._parse(infix[1]))
70 if len(infix) == 3:
71 self._match(infix[2])
63 return expr
72 return expr
64 def parse(self, message):
73 def parse(self, message):
65 'generate a parse tree from a message'
74 'generate a parse tree from a message'
66 self._iter = self._tokenizer(message)
75 self._iter = self._tokenizer(message)
67 self.current = self._iter.next()
76 self.current = self._iter.next()
68 return self._parse()
77 return self._parse()
69 def eval(self, tree):
78 def eval(self, tree):
70 'recursively evaluate a parse tree using node methods'
79 'recursively evaluate a parse tree using node methods'
71 if not isinstance(tree, tuple):
80 if not isinstance(tree, tuple):
72 return tree
81 return tree
73 return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]])
82 return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]])
74 def __call__(self, message):
83 def __call__(self, message):
75 'parse a message into a parse tree and evaluate if methods given'
84 'parse a message into a parse tree and evaluate if methods given'
76 t = self.parse(message)
85 t = self.parse(message)
77 if self._methods:
86 if self._methods:
78 return self.eval(t)
87 return self.eval(t)
79 return t
88 return t
@@ -1,530 +1,546 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
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 re
8 import re
9 import parser, util, hg
9 import parser, util, hg
10 import match as _match
10 import match as _match
11
11
12 elements = {
12 elements = {
13 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
13 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
14 "-": (19, ("negate", 19), ("minus", 19)),
14 "-": (19, ("negate", 19), ("minus", 19)),
15 "..": (17, None, ("dagrange", 17)),
15 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
16 ":": (15, None, ("range", 15)),
16 ("dagrangepost", 17)),
17 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
18 ("dagrangepost", 17)),
19 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
17 "not": (10, ("not", 10)),
20 "not": (10, ("not", 10)),
18 "!": (10, ("not", 10)),
21 "!": (10, ("not", 10)),
19 "and": (5, None, ("and", 5)),
22 "and": (5, None, ("and", 5)),
20 "&": (5, None, ("and", 5)),
23 "&": (5, None, ("and", 5)),
21 "or": (4, None, ("or", 4)),
24 "or": (4, None, ("or", 4)),
22 "|": (4, None, ("or", 4)),
25 "|": (4, None, ("or", 4)),
23 "+": (4, None, ("or", 4)),
26 "+": (4, None, ("or", 4)),
24 ",": (2, None, ("list", 2)),
27 ",": (2, None, ("list", 2)),
25 ")": (0, None, None),
28 ")": (0, None, None),
26 "symbol": (0, ("symbol",), None),
29 "symbol": (0, ("symbol",), None),
27 "string": (0, ("string",), None),
30 "string": (0, ("string",), None),
28 "end": (0, None, None),
31 "end": (0, None, None),
29 }
32 }
30
33
31 keywords = set(['and', 'or', 'not'])
34 keywords = set(['and', 'or', 'not'])
32
35
33 def tokenize(program):
36 def tokenize(program):
34 pos, l = 0, len(program)
37 pos, l = 0, len(program)
35 while pos < l:
38 while pos < l:
36 c = program[pos]
39 c = program[pos]
37 if c.isspace(): # skip inter-token whitespace
40 if c.isspace(): # skip inter-token whitespace
38 pass
41 pass
39 elif c in "():,-|&+!": # handle simple operators
42 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
40 yield (c, None)
43 yield ('::', None)
44 pos += 1 # skip ahead
41 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
45 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
42 yield ('..', None)
46 yield ('..', None)
43 pos += 1 # skip ahead
47 pos += 1 # skip ahead
48 elif c in "():,-|&+!": # handle simple operators
49 yield (c, None)
44 elif c in '"\'': # handle quoted strings
50 elif c in '"\'': # handle quoted strings
45 pos += 1
51 pos += 1
46 s = pos
52 s = pos
47 while pos < l: # find closing quote
53 while pos < l: # find closing quote
48 d = program[pos]
54 d = program[pos]
49 if d == '\\': # skip over escaped characters
55 if d == '\\': # skip over escaped characters
50 pos += 2
56 pos += 2
51 continue
57 continue
52 if d == c:
58 if d == c:
53 yield ('string', program[s:pos].decode('string-escape'))
59 yield ('string', program[s:pos].decode('string-escape'))
54 break
60 break
55 pos += 1
61 pos += 1
56 else:
62 else:
57 raise "unterminated string"
63 raise "unterminated string"
58 elif c.isalnum() or c in '.': # gather up a symbol/keyword
64 elif c.isalnum() or c in '.': # gather up a symbol/keyword
59 s = pos
65 s = pos
60 pos += 1
66 pos += 1
61 while pos < l: # find end of symbol
67 while pos < l: # find end of symbol
62 d = program[pos]
68 d = program[pos]
63 if not (d.isalnum() or d in "._"):
69 if not (d.isalnum() or d in "._"):
64 break
70 break
65 if d == '.' and program[pos - 1] == '.': # special case for ..
71 if d == '.' and program[pos - 1] == '.': # special case for ..
66 pos -= 1
72 pos -= 1
67 break
73 break
68 pos += 1
74 pos += 1
69 sym = program[s:pos]
75 sym = program[s:pos]
70 if sym in keywords: # operator keywords
76 if sym in keywords: # operator keywords
71 yield (sym, None)
77 yield (sym, None)
72 else:
78 else:
73 yield ('symbol', sym)
79 yield ('symbol', sym)
74 pos -= 1
80 pos -= 1
75 else:
81 else:
76 raise "syntax error at %d" % pos
82 raise "syntax error at %d" % pos
77 pos += 1
83 pos += 1
78 yield ('end', None)
84 yield ('end', None)
79
85
80 # helpers
86 # helpers
81
87
82 def getstring(x, err):
88 def getstring(x, err):
83 if x[0] == 'string' or x[0] == 'symbol':
89 if x[0] == 'string' or x[0] == 'symbol':
84 return x[1]
90 return x[1]
85 raise err
91 raise err
86
92
87 def getlist(x):
93 def getlist(x):
88 if not x:
94 if not x:
89 return []
95 return []
90 if x[0] == 'list':
96 if x[0] == 'list':
91 return getlist(x[1]) + [x[2]]
97 return getlist(x[1]) + [x[2]]
92 return [x]
98 return [x]
93
99
94 def getpair(x, err):
100 def getpair(x, err):
95 l = getlist(x)
101 l = getlist(x)
96 if len(l) != 2:
102 if len(l) != 2:
97 raise err
103 raise err
98 return l
104 return l
99
105
100 def getset(repo, subset, x):
106 def getset(repo, subset, x):
101 if not x:
107 if not x:
102 raise "missing argument"
108 raise "missing argument"
103 return methods[x[0]](repo, subset, *x[1:])
109 return methods[x[0]](repo, subset, *x[1:])
104
110
105 # operator methods
111 # operator methods
106
112
107 def negate(repo, subset, x):
113 def negate(repo, subset, x):
108 return getset(repo, subset,
114 return getset(repo, subset,
109 ('string', '-' + getstring(x, "can't negate that")))
115 ('string', '-' + getstring(x, "can't negate that")))
110
116
111 def stringset(repo, subset, x):
117 def stringset(repo, subset, x):
112 x = repo[x].rev()
118 x = repo[x].rev()
113 if x in subset:
119 if x in subset:
114 return [x]
120 return [x]
115 return []
121 return []
116
122
117 def symbolset(repo, subset, x):
123 def symbolset(repo, subset, x):
118 if x in symbols:
124 if x in symbols:
119 raise "can't use %s here" % x
125 raise "can't use %s here" % x
120 return stringset(repo, subset, x)
126 return stringset(repo, subset, x)
121
127
122 def rangeset(repo, subset, x, y):
128 def rangeset(repo, subset, x, y):
123 m = getset(repo, subset, x)[0]
129 m = getset(repo, subset, x)[0]
124 n = getset(repo, subset, y)[-1]
130 n = getset(repo, subset, y)[-1]
125 if m < n:
131 if m < n:
126 return range(m, n + 1)
132 return range(m, n + 1)
127 return range(m, n - 1, -1)
133 return range(m, n - 1, -1)
128
134
135 def rangepreset(repo, subset, x):
136 return range(0, getset(repo, subset, x)[-1] + 1)
137
138 def rangepostset(repo, subset, x):
139 return range(getset(repo, subset, x)[0], len(repo))
140
129 def dagrangeset(repo, subset, x, y):
141 def dagrangeset(repo, subset, x, y):
130 return andset(repo, subset,
142 return andset(repo, subset,
131 ('func', ('symbol', 'descendants'), x),
143 ('func', ('symbol', 'descendants'), x),
132 ('func', ('symbol', 'ancestors'), y))
144 ('func', ('symbol', 'ancestors'), y))
133
145
134 def andset(repo, subset, x, y):
146 def andset(repo, subset, x, y):
135 if weight(x, True) > weight(y, True):
147 if weight(x, True) > weight(y, True):
136 x, y = y, x
148 x, y = y, x
137 return getset(repo, getset(repo, subset, x), y)
149 return getset(repo, getset(repo, subset, x), y)
138
150
139 def orset(repo, subset, x, y):
151 def orset(repo, subset, x, y):
140 if weight(y, False) < weight(x, False):
152 if weight(y, False) < weight(x, False):
141 x, y = y, x
153 x, y = y, x
142 s = set(getset(repo, subset, x))
154 s = set(getset(repo, subset, x))
143 s |= set(getset(repo, [r for r in subset if r not in s], y))
155 s |= set(getset(repo, [r for r in subset if r not in s], y))
144 return [r for r in subset if r in s]
156 return [r for r in subset if r in s]
145
157
146 def notset(repo, subset, x):
158 def notset(repo, subset, x):
147 s = set(getset(repo, subset, x))
159 s = set(getset(repo, subset, x))
148 return [r for r in subset if r not in s]
160 return [r for r in subset if r not in s]
149
161
150 def minusset(repo, subset, x, y):
162 def minusset(repo, subset, x, y):
151 if weight(x, True) > weight(y, True):
163 if weight(x, True) > weight(y, True):
152 return getset(repo, notset(repo, subset, y), x)
164 return getset(repo, notset(repo, subset, y), x)
153 return notset(repo, getset(repo, subset, x), y)
165 return notset(repo, getset(repo, subset, x), y)
154
166
155 def listset(repo, subset, a, b):
167 def listset(repo, subset, a, b):
156 raise "can't use a list in this context"
168 raise "can't use a list in this context"
157
169
158 def func(repo, subset, a, b):
170 def func(repo, subset, a, b):
159 if a[0] == 'symbol' and a[1] in symbols:
171 if a[0] == 'symbol' and a[1] in symbols:
160 return symbols[a[1]](repo, subset, b)
172 return symbols[a[1]](repo, subset, b)
161 raise "that's not a function: %s" % a[1]
173 raise "that's not a function: %s" % a[1]
162
174
163 # functions
175 # functions
164
176
165 def p1(repo, subset, x):
177 def p1(repo, subset, x):
166 ps = set()
178 ps = set()
167 cl = repo.changelog
179 cl = repo.changelog
168 for r in getset(repo, subset, x):
180 for r in getset(repo, subset, x):
169 ps.add(cl.parentrevs(r)[0])
181 ps.add(cl.parentrevs(r)[0])
170 return [r for r in subset if r in ps]
182 return [r for r in subset if r in ps]
171
183
172 def p2(repo, subset, x):
184 def p2(repo, subset, x):
173 ps = set()
185 ps = set()
174 cl = repo.changelog
186 cl = repo.changelog
175 for r in getset(repo, subset, x):
187 for r in getset(repo, subset, x):
176 ps.add(cl.parentrevs(r)[1])
188 ps.add(cl.parentrevs(r)[1])
177 return [r for r in subset if r in ps]
189 return [r for r in subset if r in ps]
178
190
179 def parents(repo, subset, x):
191 def parents(repo, subset, x):
180 ps = set()
192 ps = set()
181 cl = repo.changelog
193 cl = repo.changelog
182 for r in getset(repo, subset, x):
194 for r in getset(repo, subset, x):
183 ps.update(cl.parentrevs(r))
195 ps.update(cl.parentrevs(r))
184 return [r for r in subset if r in ps]
196 return [r for r in subset if r in ps]
185
197
186 def maxrev(repo, subset, x):
198 def maxrev(repo, subset, x):
187 s = getset(repo, subset, x)
199 s = getset(repo, subset, x)
188 if s:
200 if s:
189 m = max(s)
201 m = max(s)
190 if m in subset:
202 if m in subset:
191 return [m]
203 return [m]
192 return []
204 return []
193
205
194 def limit(repo, subset, x):
206 def limit(repo, subset, x):
195 l = getpair(x, "limit wants two args")
207 l = getpair(x, "limit wants two args")
196 try:
208 try:
197 lim = int(getstring(l[1], "limit wants a number"))
209 lim = int(getstring(l[1], "limit wants a number"))
198 except ValueError:
210 except ValueError:
199 raise "wants a number"
211 raise "wants a number"
200 return getset(repo, subset, l[0])[:lim]
212 return getset(repo, subset, l[0])[:lim]
201
213
202 def children(repo, subset, x):
214 def children(repo, subset, x):
203 cs = set()
215 cs = set()
204 cl = repo.changelog
216 cl = repo.changelog
205 s = set(getset(repo, subset, x))
217 s = set(getset(repo, subset, x))
206 for r in xrange(0, len(repo)):
218 for r in xrange(0, len(repo)):
207 for p in cl.parentrevs(r):
219 for p in cl.parentrevs(r):
208 if p in s:
220 if p in s:
209 cs.add(r)
221 cs.add(r)
210 return [r for r in subset if r in cs]
222 return [r for r in subset if r in cs]
211
223
212 def branch(repo, subset, x):
224 def branch(repo, subset, x):
213 s = getset(repo, range(len(repo)), x)
225 s = getset(repo, range(len(repo)), x)
214 b = set()
226 b = set()
215 for r in s:
227 for r in s:
216 b.add(repo[r].branch())
228 b.add(repo[r].branch())
217 s = set(s)
229 s = set(s)
218 return [r for r in subset if r in s or repo[r].branch() in b]
230 return [r for r in subset if r in s or repo[r].branch() in b]
219
231
220 def ancestor(repo, subset, x):
232 def ancestor(repo, subset, x):
221 l = getpair(x, "ancestor wants two args")
233 l = getpair(x, "ancestor wants two args")
222 a = getset(repo, subset, l[0])
234 a = getset(repo, subset, l[0])
223 b = getset(repo, subset, l[1])
235 b = getset(repo, subset, l[1])
224 if len(a) > 1 or len(b) > 1:
236 if len(a) > 1 or len(b) > 1:
225 raise "arguments to ancestor must be single revisions"
237 raise "arguments to ancestor must be single revisions"
226 return [repo[a[0]].ancestor(repo[b[0]]).rev()]
238 return [repo[a[0]].ancestor(repo[b[0]]).rev()]
227
239
228 def ancestors(repo, subset, x):
240 def ancestors(repo, subset, x):
229 args = getset(repo, range(len(repo)), x)
241 args = getset(repo, range(len(repo)), x)
230 s = set(repo.changelog.ancestors(*args)) | set(args)
242 s = set(repo.changelog.ancestors(*args)) | set(args)
231 return [r for r in subset if r in s]
243 return [r for r in subset if r in s]
232
244
233 def descendants(repo, subset, x):
245 def descendants(repo, subset, x):
234 args = getset(repo, range(len(repo)), x)
246 args = getset(repo, range(len(repo)), x)
235 s = set(repo.changelog.descendants(*args)) | set(args)
247 s = set(repo.changelog.descendants(*args)) | set(args)
236 return [r for r in subset if r in s]
248 return [r for r in subset if r in s]
237
249
238 def follow(repo, subset, x):
250 def follow(repo, subset, x):
239 if x:
251 if x:
240 raise "follow takes no args"
252 raise "follow takes no args"
241 p = repo['.'].rev()
253 p = repo['.'].rev()
242 s = set(repo.changelog.ancestors(p)) | set([p])
254 s = set(repo.changelog.ancestors(p)) | set([p])
243 return [r for r in subset if r in s]
255 return [r for r in subset if r in s]
244
256
245 def date(repo, subset, x):
257 def date(repo, subset, x):
246 ds = getstring(x, 'date wants a string')
258 ds = getstring(x, 'date wants a string')
247 dm = util.matchdate(ds)
259 dm = util.matchdate(ds)
248 return [r for r in subset if dm(repo[r].date()[0])]
260 return [r for r in subset if dm(repo[r].date()[0])]
249
261
250 def keyword(repo, subset, x):
262 def keyword(repo, subset, x):
251 kw = getstring(x, "keyword wants a string").lower()
263 kw = getstring(x, "keyword wants a string").lower()
252 l = []
264 l = []
253 for r in subset:
265 for r in subset:
254 c = repo[r]
266 c = repo[r]
255 t = " ".join(c.files() + [c.user(), c.description()])
267 t = " ".join(c.files() + [c.user(), c.description()])
256 if kw in t.lower():
268 if kw in t.lower():
257 l.append(r)
269 l.append(r)
258 return l
270 return l
259
271
260 def grep(repo, subset, x):
272 def grep(repo, subset, x):
261 gr = re.compile(getstring(x, "grep wants a string"))
273 gr = re.compile(getstring(x, "grep wants a string"))
262 l = []
274 l = []
263 for r in subset:
275 for r in subset:
264 c = repo[r]
276 c = repo[r]
265 for e in c.files() + [c.user(), c.description()]:
277 for e in c.files() + [c.user(), c.description()]:
266 if gr.search(e):
278 if gr.search(e):
267 l.append(r)
279 l.append(r)
268 continue
280 continue
269 return l
281 return l
270
282
271 def author(repo, subset, x):
283 def author(repo, subset, x):
272 n = getstring(x, "author wants a string").lower()
284 n = getstring(x, "author wants a string").lower()
273 return [r for r in subset if n in repo[r].user().lower()]
285 return [r for r in subset if n in repo[r].user().lower()]
274
286
275 def hasfile(repo, subset, x):
287 def hasfile(repo, subset, x):
276 pat = getstring(x, "file wants a pattern")
288 pat = getstring(x, "file wants a pattern")
277 m = _match.match(repo.root, repo.getcwd(), [pat])
289 m = _match.match(repo.root, repo.getcwd(), [pat])
278 s = []
290 s = []
279 for r in subset:
291 for r in subset:
280 for f in repo[r].files():
292 for f in repo[r].files():
281 if m(f):
293 if m(f):
282 s.append(r)
294 s.append(r)
283 continue
295 continue
284 return s
296 return s
285
297
286 def contains(repo, subset, x):
298 def contains(repo, subset, x):
287 pat = getstring(x, "file wants a pattern")
299 pat = getstring(x, "file wants a pattern")
288 m = _match.match(repo.root, repo.getcwd(), [pat])
300 m = _match.match(repo.root, repo.getcwd(), [pat])
289 s = []
301 s = []
290 if m.files() == [pat]:
302 if m.files() == [pat]:
291 for r in subset:
303 for r in subset:
292 if pat in repo[r]:
304 if pat in repo[r]:
293 s.append(r)
305 s.append(r)
294 continue
306 continue
295 else:
307 else:
296 for r in subset:
308 for r in subset:
297 c = repo[r]
309 c = repo[r]
298 for f in repo[r].manifest():
310 for f in repo[r].manifest():
299 if m(f):
311 if m(f):
300 s.append(r)
312 s.append(r)
301 continue
313 continue
302 return s
314 return s
303
315
304 def checkstatus(repo, subset, pat, field):
316 def checkstatus(repo, subset, pat, field):
305 m = _match.match(repo.root, repo.getcwd(), [pat])
317 m = _match.match(repo.root, repo.getcwd(), [pat])
306 s = []
318 s = []
307 fast = (m.files() == [pat])
319 fast = (m.files() == [pat])
308 for r in subset:
320 for r in subset:
309 c = repo[r]
321 c = repo[r]
310 if fast:
322 if fast:
311 if pat not in c.files():
323 if pat not in c.files():
312 continue
324 continue
313 else:
325 else:
314 for f in c.files():
326 for f in c.files():
315 if m(f):
327 if m(f):
316 break
328 break
317 else:
329 else:
318 continue
330 continue
319 files = repo.status(c.p1().node(), c.node())[field]
331 files = repo.status(c.p1().node(), c.node())[field]
320 if fast:
332 if fast:
321 if pat in files:
333 if pat in files:
322 s.append(r)
334 s.append(r)
323 continue
335 continue
324 else:
336 else:
325 for f in files:
337 for f in files:
326 if m(f):
338 if m(f):
327 s.append(r)
339 s.append(r)
328 continue
340 continue
329 return s
341 return s
330
342
331 def modifies(repo, subset, x):
343 def modifies(repo, subset, x):
332 pat = getstring(x, "modifies wants a pattern")
344 pat = getstring(x, "modifies wants a pattern")
333 return checkstatus(repo, subset, pat, 0)
345 return checkstatus(repo, subset, pat, 0)
334
346
335 def adds(repo, subset, x):
347 def adds(repo, subset, x):
336 pat = getstring(x, "adds wants a pattern")
348 pat = getstring(x, "adds wants a pattern")
337 return checkstatus(repo, subset, pat, 1)
349 return checkstatus(repo, subset, pat, 1)
338
350
339 def removes(repo, subset, x):
351 def removes(repo, subset, x):
340 pat = getstring(x, "removes wants a pattern")
352 pat = getstring(x, "removes wants a pattern")
341 return checkstatus(repo, subset, pat, 2)
353 return checkstatus(repo, subset, pat, 2)
342
354
343 def merge(repo, subset, x):
355 def merge(repo, subset, x):
344 if x:
356 if x:
345 raise "merge takes no args"
357 raise "merge takes no args"
346 cl = repo.changelog
358 cl = repo.changelog
347 return [r for r in subset if cl.parentrevs(r)[1] != -1]
359 return [r for r in subset if cl.parentrevs(r)[1] != -1]
348
360
349 def closed(repo, subset, x):
361 def closed(repo, subset, x):
350 return [r for r in subset if repo[r].extra('close')]
362 return [r for r in subset if repo[r].extra('close')]
351
363
352 def head(repo, subset, x):
364 def head(repo, subset, x):
353 hs = set()
365 hs = set()
354 for b, ls in repo.branchmap().iteritems():
366 for b, ls in repo.branchmap().iteritems():
355 hs.update(repo[h].rev() for h in ls)
367 hs.update(repo[h].rev() for h in ls)
356 return [r for r in subset if r in hs]
368 return [r for r in subset if r in hs]
357
369
358 def reverse(repo, subset, x):
370 def reverse(repo, subset, x):
359 l = getset(repo, subset, x)
371 l = getset(repo, subset, x)
360 l.reverse()
372 l.reverse()
361 return l
373 return l
362
374
363 def sort(repo, subset, x):
375 def sort(repo, subset, x):
364 l = getlist(x)
376 l = getlist(x)
365 keys = "rev"
377 keys = "rev"
366 if len(l) == 2:
378 if len(l) == 2:
367 keys = getstring(l[1], "sort spec must be a string")
379 keys = getstring(l[1], "sort spec must be a string")
368
380
369 s = l[0]
381 s = l[0]
370 keys = keys.split()
382 keys = keys.split()
371 l = []
383 l = []
372 def invert(s):
384 def invert(s):
373 return "".join(chr(255 - ord(c)) for c in s)
385 return "".join(chr(255 - ord(c)) for c in s)
374 for r in getset(repo, subset, s):
386 for r in getset(repo, subset, s):
375 c = repo[r]
387 c = repo[r]
376 e = []
388 e = []
377 for k in keys:
389 for k in keys:
378 if k == 'rev':
390 if k == 'rev':
379 e.append(r)
391 e.append(r)
380 elif k == '-rev':
392 elif k == '-rev':
381 e.append(-r)
393 e.append(-r)
382 elif k == 'branch':
394 elif k == 'branch':
383 e.append(c.branch())
395 e.append(c.branch())
384 elif k == '-branch':
396 elif k == '-branch':
385 e.append(invert(c.branch()))
397 e.append(invert(c.branch()))
386 elif k == 'desc':
398 elif k == 'desc':
387 e.append(c.description())
399 e.append(c.description())
388 elif k == '-desc':
400 elif k == '-desc':
389 e.append(invert(c.description()))
401 e.append(invert(c.description()))
390 elif k in 'user author':
402 elif k in 'user author':
391 e.append(c.user())
403 e.append(c.user())
392 elif k in '-user -author':
404 elif k in '-user -author':
393 e.append(invert(c.user()))
405 e.append(invert(c.user()))
394 elif k == 'date':
406 elif k == 'date':
395 e.append(c.date()[0])
407 e.append(c.date()[0])
396 elif k == '-date':
408 elif k == '-date':
397 e.append(-c.date()[0])
409 e.append(-c.date()[0])
398 else:
410 else:
399 raise "unknown sort key %r" % k
411 raise "unknown sort key %r" % k
400 e.append(r)
412 e.append(r)
401 l.append(e)
413 l.append(e)
402 l.sort()
414 l.sort()
403 return [e[-1] for e in l]
415 return [e[-1] for e in l]
404
416
405 def getall(repo, subset, x):
417 def getall(repo, subset, x):
406 return subset
418 return subset
407
419
408 def heads(repo, subset, x):
420 def heads(repo, subset, x):
409 s = getset(repo, subset, x)
421 s = getset(repo, subset, x)
410 ps = set(parents(repo, subset, x))
422 ps = set(parents(repo, subset, x))
411 return [r for r in s if r not in ps]
423 return [r for r in s if r not in ps]
412
424
413 def roots(repo, subset, x):
425 def roots(repo, subset, x):
414 s = getset(repo, subset, x)
426 s = getset(repo, subset, x)
415 cs = set(children(repo, subset, x))
427 cs = set(children(repo, subset, x))
416 return [r for r in s if r not in cs]
428 return [r for r in s if r not in cs]
417
429
418 def outgoing(repo, subset, x):
430 def outgoing(repo, subset, x):
419 l = getlist(x)
431 l = getlist(x)
420 if len(l) == 1:
432 if len(l) == 1:
421 dest = getstring(l[0], "outgoing wants a repo path")
433 dest = getstring(l[0], "outgoing wants a repo path")
422 else:
434 else:
423 dest = ''
435 dest = ''
424 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
436 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
425 dest, branches = hg.parseurl(dest)
437 dest, branches = hg.parseurl(dest)
426 other = hg.repository(hg.remoteui(repo, {}), dest)
438 other = hg.repository(hg.remoteui(repo, {}), dest)
427 repo.ui.pushbuffer()
439 repo.ui.pushbuffer()
428 o = repo.findoutgoing(other)
440 o = repo.findoutgoing(other)
429 repo.ui.popbuffer()
441 repo.ui.popbuffer()
430 cl = repo.changelog
442 cl = repo.changelog
431 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, None)[0]])
443 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, None)[0]])
432 print 'out', dest, o
444 print 'out', dest, o
433 return [r for r in subset if r in o]
445 return [r for r in subset if r in o]
434
446
435 symbols = {
447 symbols = {
436 "ancestor": ancestor,
448 "ancestor": ancestor,
437 "ancestors": ancestors,
449 "ancestors": ancestors,
438 "descendants": descendants,
450 "descendants": descendants,
439 "follow": follow,
451 "follow": follow,
440 "merge": merge,
452 "merge": merge,
441 "reverse": reverse,
453 "reverse": reverse,
442 "sort": sort,
454 "sort": sort,
443 "branch": branch,
455 "branch": branch,
444 "keyword": keyword,
456 "keyword": keyword,
445 "author": author,
457 "author": author,
446 "user": author,
458 "user": author,
447 "date": date,
459 "date": date,
448 "grep": grep,
460 "grep": grep,
449 "p1": p1,
461 "p1": p1,
450 "p2": p2,
462 "p2": p2,
451 "parents": parents,
463 "parents": parents,
452 "children": children,
464 "children": children,
453 "max": maxrev,
465 "max": maxrev,
454 "limit": limit,
466 "limit": limit,
455 "file": hasfile,
467 "file": hasfile,
456 "contains": contains,
468 "contains": contains,
457 "heads": heads,
469 "heads": heads,
458 "roots": roots,
470 "roots": roots,
459 "all": getall,
471 "all": getall,
460 "closed": closed,
472 "closed": closed,
461 "head": head,
473 "head": head,
462 "modifies": modifies,
474 "modifies": modifies,
463 "adds": adds,
475 "adds": adds,
464 "removes": removes,
476 "removes": removes,
465 "outgoing": outgoing,
477 "outgoing": outgoing,
466 }
478 }
467
479
468 methods = {
480 methods = {
469 "negate": negate,
481 "negate": negate,
470 "minus": minusset,
482 "minus": minusset,
471 "range": rangeset,
483 "range": rangeset,
484 "rangepre": rangepreset,
485 "rangepost": rangepostset,
472 "dagrange": dagrangeset,
486 "dagrange": dagrangeset,
487 "dagrangepre": ancestors,
488 "dagrangepost": descendants,
473 "string": stringset,
489 "string": stringset,
474 "symbol": symbolset,
490 "symbol": symbolset,
475 "and": andset,
491 "and": andset,
476 "or": orset,
492 "or": orset,
477 "not": notset,
493 "not": notset,
478 "list": listset,
494 "list": listset,
479 "func": func,
495 "func": func,
480 "group": lambda r, s, x: getset(r, s, x),
496 "group": lambda r, s, x: getset(r, s, x),
481 }
497 }
482
498
483 def weight(x, small):
499 def weight(x, small):
484 smallbonus = 1
500 smallbonus = 1
485 if small:
501 if small:
486 smallbonus = .5
502 smallbonus = .5
487
503
488 op = x[0]
504 op = x[0]
489 if op in 'string symbol negate':
505 if op in 'string symbol negate':
490 return smallbonus # single revisions are small
506 return smallbonus # single revisions are small
491 elif op == 'and' or op == 'dagrange':
507 elif op == 'and' or op == 'dagrange':
492 return min(weight(x[1], True), weight(x[2], True))
508 return min(weight(x[1], True), weight(x[2], True))
493 elif op in 'or -':
509 elif op in 'or -':
494 return max(weight(x[1], False), weight(x[2], False))
510 return max(weight(x[1], False), weight(x[2], False))
495 elif op == 'not':
511 elif op == 'not':
496 return weight(x[1], not small)
512 return weight(x[1], not small)
497 elif op == 'group':
513 elif op == 'group':
498 return weight(x[1], small)
514 return weight(x[1], small)
499 elif op == 'range':
515 elif op == 'range':
500 return weight(x[1], small) + weight(x[2], small)
516 return weight(x[1], small) + weight(x[2], small)
501 elif op == 'func':
517 elif op == 'func':
502 f = getstring(x[1], "not a symbol")
518 f = getstring(x[1], "not a symbol")
503 if f in "grep date user author keyword branch file":
519 if f in "grep date user author keyword branch file":
504 return 10 # slow
520 return 10 # slow
505 elif f in "modifies adds removes":
521 elif f in "modifies adds removes":
506 return 30 # slower
522 return 30 # slower
507 elif f == "contains":
523 elif f == "contains":
508 return 100 # very slow
524 return 100 # very slow
509 elif f == "ancestor":
525 elif f == "ancestor":
510 return (weight(x[1][1], small) +
526 return (weight(x[1][1], small) +
511 weight(x[1][2], small)) * smallbonus
527 weight(x[1][2], small)) * smallbonus
512 elif f == "reverse limit":
528 elif f == "reverse limit":
513 return weight(x[1], small)
529 return weight(x[1], small)
514 elif f in "sort":
530 elif f in "sort":
515 base = x[1]
531 base = x[1]
516 spec = "rev"
532 spec = "rev"
517 if x[1][0] == 'list':
533 if x[1][0] == 'list':
518 base = x[1][1]
534 base = x[1][1]
519 spec = x[1][2]
535 spec = x[1][2]
520 return max(weight(base, small), 10)
536 return max(weight(base, small), 10)
521 else:
537 else:
522 return 1
538 return 1
523
539
524 parse = parser.parser(tokenize, elements).parse
540 parse = parser.parser(tokenize, elements).parse
525
541
526 def match(spec):
542 def match(spec):
527 tree = parse(spec)
543 tree = parse(spec)
528 def mfunc(repo, subset):
544 def mfunc(repo, subset):
529 return getset(repo, subset, tree)
545 return getset(repo, subset, tree)
530 return mfunc
546 return mfunc
General Comments 0
You need to be logged in to leave comments. Login now