Mercurial > public > mercurial-scm > hg
comparison mercurial/revsetlang.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | c59eb1560c44 |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
20 ) | 20 ) |
21 from .utils import stringutil | 21 from .utils import stringutil |
22 | 22 |
23 elements = { | 23 elements = { |
24 # token-type: binding-strength, primary, prefix, infix, suffix | 24 # token-type: binding-strength, primary, prefix, infix, suffix |
25 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None), | 25 b"(": (21, None, (b"group", 1, b")"), (b"func", 1, b")"), None), |
26 "[": (21, None, None, ("subscript", 1, "]"), None), | 26 b"[": (21, None, None, (b"subscript", 1, b"]"), None), |
27 "#": (21, None, None, ("relation", 21), None), | 27 b"#": (21, None, None, (b"relation", 21), None), |
28 "##": (20, None, None, ("_concat", 20), None), | 28 b"##": (20, None, None, (b"_concat", 20), None), |
29 "~": (18, None, None, ("ancestor", 18), None), | 29 b"~": (18, None, None, (b"ancestor", 18), None), |
30 "^": (18, None, None, ("parent", 18), "parentpost"), | 30 b"^": (18, None, None, (b"parent", 18), b"parentpost"), |
31 "-": (5, None, ("negate", 19), ("minus", 5), None), | 31 b"-": (5, None, (b"negate", 19), (b"minus", 5), None), |
32 "::": ( | 32 b"::": ( |
33 17, | 33 17, |
34 "dagrangeall", | 34 b"dagrangeall", |
35 ("dagrangepre", 17), | 35 (b"dagrangepre", 17), |
36 ("dagrange", 17), | 36 (b"dagrange", 17), |
37 "dagrangepost", | 37 b"dagrangepost", |
38 ), | 38 ), |
39 "..": ( | 39 b"..": ( |
40 17, | 40 17, |
41 "dagrangeall", | 41 b"dagrangeall", |
42 ("dagrangepre", 17), | 42 (b"dagrangepre", 17), |
43 ("dagrange", 17), | 43 (b"dagrange", 17), |
44 "dagrangepost", | 44 b"dagrangepost", |
45 ), | 45 ), |
46 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"), | 46 b":": (15, b"rangeall", (b"rangepre", 15), (b"range", 15), b"rangepost"), |
47 "not": (10, None, ("not", 10), None, None), | 47 b"not": (10, None, (b"not", 10), None, None), |
48 "!": (10, None, ("not", 10), None, None), | 48 b"!": (10, None, (b"not", 10), None, None), |
49 "and": (5, None, None, ("and", 5), None), | 49 b"and": (5, None, None, (b"and", 5), None), |
50 "&": (5, None, None, ("and", 5), None), | 50 b"&": (5, None, None, (b"and", 5), None), |
51 "%": (5, None, None, ("only", 5), "onlypost"), | 51 b"%": (5, None, None, (b"only", 5), b"onlypost"), |
52 "or": (4, None, None, ("or", 4), None), | 52 b"or": (4, None, None, (b"or", 4), None), |
53 "|": (4, None, None, ("or", 4), None), | 53 b"|": (4, None, None, (b"or", 4), None), |
54 "+": (4, None, None, ("or", 4), None), | 54 b"+": (4, None, None, (b"or", 4), None), |
55 "=": (3, None, None, ("keyvalue", 3), None), | 55 b"=": (3, None, None, (b"keyvalue", 3), None), |
56 ",": (2, None, None, ("list", 2), None), | 56 b",": (2, None, None, (b"list", 2), None), |
57 ")": (0, None, None, None, None), | 57 b")": (0, None, None, None, None), |
58 "]": (0, None, None, None, None), | 58 b"]": (0, None, None, None, None), |
59 "symbol": (0, "symbol", None, None, None), | 59 b"symbol": (0, b"symbol", None, None, None), |
60 "string": (0, "string", None, None, None), | 60 b"string": (0, b"string", None, None, None), |
61 "end": (0, None, None, None, None), | 61 b"end": (0, None, None, None, None), |
62 } | 62 } |
63 | 63 |
64 keywords = {'and', 'or', 'not'} | 64 keywords = {b'and', b'or', b'not'} |
65 | 65 |
66 symbols = {} | 66 symbols = {} |
67 | 67 |
68 _quoteletters = {'"', "'"} | 68 _quoteletters = {b'"', b"'"} |
69 _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%")) | 69 _simpleopletters = set(pycompat.iterbytestr(b"()[]#:=,-|&+!~^%")) |
70 | 70 |
71 # default set of valid characters for the initial letter of symbols | 71 # default set of valid characters for the initial letter of symbols |
72 _syminitletters = set( | 72 _syminitletters = set( |
73 pycompat.iterbytestr( | 73 pycompat.iterbytestr( |
74 pycompat.sysbytes(string.ascii_letters) | 74 pycompat.sysbytes(string.ascii_letters) |
75 + pycompat.sysbytes(string.digits) | 75 + pycompat.sysbytes(string.digits) |
76 + '._@' | 76 + b'._@' |
77 ) | 77 ) |
78 ) | set(map(pycompat.bytechr, pycompat.xrange(128, 256))) | 78 ) | set(map(pycompat.bytechr, pycompat.xrange(128, 256))) |
79 | 79 |
80 # default set of valid characters for non-initial letters of symbols | 80 # default set of valid characters for non-initial letters of symbols |
81 _symletters = _syminitletters | set(pycompat.iterbytestr('-/')) | 81 _symletters = _syminitletters | set(pycompat.iterbytestr(b'-/')) |
82 | 82 |
83 | 83 |
84 def tokenize(program, lookup=None, syminitletters=None, symletters=None): | 84 def tokenize(program, lookup=None, syminitletters=None, symletters=None): |
85 ''' | 85 ''' |
86 Parse a revset statement into a stream of tokens | 86 Parse a revset statement into a stream of tokens |
102 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)] | 102 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)] |
103 | 103 |
104 ''' | 104 ''' |
105 if not isinstance(program, bytes): | 105 if not isinstance(program, bytes): |
106 raise error.ProgrammingError( | 106 raise error.ProgrammingError( |
107 'revset statement must be bytes, got %r' % program | 107 b'revset statement must be bytes, got %r' % program |
108 ) | 108 ) |
109 program = pycompat.bytestr(program) | 109 program = pycompat.bytestr(program) |
110 if syminitletters is None: | 110 if syminitletters is None: |
111 syminitletters = _syminitletters | 111 syminitletters = _syminitletters |
112 if symletters is None: | 112 if symletters is None: |
113 symletters = _symletters | 113 symletters = _symletters |
114 | 114 |
115 if program and lookup: | 115 if program and lookup: |
116 # attempt to parse old-style ranges first to deal with | 116 # attempt to parse old-style ranges first to deal with |
117 # things like old-tag which contain query metacharacters | 117 # things like old-tag which contain query metacharacters |
118 parts = program.split(':', 1) | 118 parts = program.split(b':', 1) |
119 if all(lookup(sym) for sym in parts if sym): | 119 if all(lookup(sym) for sym in parts if sym): |
120 if parts[0]: | 120 if parts[0]: |
121 yield ('symbol', parts[0], 0) | 121 yield (b'symbol', parts[0], 0) |
122 if len(parts) > 1: | 122 if len(parts) > 1: |
123 s = len(parts[0]) | 123 s = len(parts[0]) |
124 yield (':', None, s) | 124 yield (b':', None, s) |
125 if parts[1]: | 125 if parts[1]: |
126 yield ('symbol', parts[1], s + 1) | 126 yield (b'symbol', parts[1], s + 1) |
127 yield ('end', None, len(program)) | 127 yield (b'end', None, len(program)) |
128 return | 128 return |
129 | 129 |
130 pos, l = 0, len(program) | 130 pos, l = 0, len(program) |
131 while pos < l: | 131 while pos < l: |
132 c = program[pos] | 132 c = program[pos] |
133 if c.isspace(): # skip inter-token whitespace | 133 if c.isspace(): # skip inter-token whitespace |
134 pass | 134 pass |
135 elif ( | 135 elif ( |
136 c == ':' and program[pos : pos + 2] == '::' | 136 c == b':' and program[pos : pos + 2] == b'::' |
137 ): # look ahead carefully | 137 ): # look ahead carefully |
138 yield ('::', None, pos) | 138 yield (b'::', None, pos) |
139 pos += 1 # skip ahead | 139 pos += 1 # skip ahead |
140 elif ( | 140 elif ( |
141 c == '.' and program[pos : pos + 2] == '..' | 141 c == b'.' and program[pos : pos + 2] == b'..' |
142 ): # look ahead carefully | 142 ): # look ahead carefully |
143 yield ('..', None, pos) | 143 yield (b'..', None, pos) |
144 pos += 1 # skip ahead | 144 pos += 1 # skip ahead |
145 elif ( | 145 elif ( |
146 c == '#' and program[pos : pos + 2] == '##' | 146 c == b'#' and program[pos : pos + 2] == b'##' |
147 ): # look ahead carefully | 147 ): # look ahead carefully |
148 yield ('##', None, pos) | 148 yield (b'##', None, pos) |
149 pos += 1 # skip ahead | 149 pos += 1 # skip ahead |
150 elif c in _simpleopletters: # handle simple operators | 150 elif c in _simpleopletters: # handle simple operators |
151 yield (c, None, pos) | 151 yield (c, None, pos) |
152 elif ( | 152 elif ( |
153 c in _quoteletters | 153 c in _quoteletters |
154 or c == 'r' | 154 or c == b'r' |
155 and program[pos : pos + 2] in ("r'", 'r"') | 155 and program[pos : pos + 2] in (b"r'", b'r"') |
156 ): # handle quoted strings | 156 ): # handle quoted strings |
157 if c == 'r': | 157 if c == b'r': |
158 pos += 1 | 158 pos += 1 |
159 c = program[pos] | 159 c = program[pos] |
160 decode = lambda x: x | 160 decode = lambda x: x |
161 else: | 161 else: |
162 decode = parser.unescapestr | 162 decode = parser.unescapestr |
163 pos += 1 | 163 pos += 1 |
164 s = pos | 164 s = pos |
165 while pos < l: # find closing quote | 165 while pos < l: # find closing quote |
166 d = program[pos] | 166 d = program[pos] |
167 if d == '\\': # skip over escaped characters | 167 if d == b'\\': # skip over escaped characters |
168 pos += 2 | 168 pos += 2 |
169 continue | 169 continue |
170 if d == c: | 170 if d == c: |
171 yield ('string', decode(program[s:pos]), s) | 171 yield (b'string', decode(program[s:pos]), s) |
172 break | 172 break |
173 pos += 1 | 173 pos += 1 |
174 else: | 174 else: |
175 raise error.ParseError(_("unterminated string"), s) | 175 raise error.ParseError(_(b"unterminated string"), s) |
176 # gather up a symbol/keyword | 176 # gather up a symbol/keyword |
177 elif c in syminitletters: | 177 elif c in syminitletters: |
178 s = pos | 178 s = pos |
179 pos += 1 | 179 pos += 1 |
180 while pos < l: # find end of symbol | 180 while pos < l: # find end of symbol |
181 d = program[pos] | 181 d = program[pos] |
182 if d not in symletters: | 182 if d not in symletters: |
183 break | 183 break |
184 if d == '.' and program[pos - 1] == '.': # special case for .. | 184 if ( |
185 d == b'.' and program[pos - 1] == b'.' | |
186 ): # special case for .. | |
185 pos -= 1 | 187 pos -= 1 |
186 break | 188 break |
187 pos += 1 | 189 pos += 1 |
188 sym = program[s:pos] | 190 sym = program[s:pos] |
189 if sym in keywords: # operator keywords | 191 if sym in keywords: # operator keywords |
190 yield (sym, None, s) | 192 yield (sym, None, s) |
191 elif '-' in sym: | 193 elif b'-' in sym: |
192 # some jerk gave us foo-bar-baz, try to check if it's a symbol | 194 # some jerk gave us foo-bar-baz, try to check if it's a symbol |
193 if lookup and lookup(sym): | 195 if lookup and lookup(sym): |
194 # looks like a real symbol | 196 # looks like a real symbol |
195 yield ('symbol', sym, s) | 197 yield (b'symbol', sym, s) |
196 else: | 198 else: |
197 # looks like an expression | 199 # looks like an expression |
198 parts = sym.split('-') | 200 parts = sym.split(b'-') |
199 for p in parts[:-1]: | 201 for p in parts[:-1]: |
200 if p: # possible consecutive - | 202 if p: # possible consecutive - |
201 yield ('symbol', p, s) | 203 yield (b'symbol', p, s) |
202 s += len(p) | 204 s += len(p) |
203 yield ('-', None, s) | 205 yield (b'-', None, s) |
204 s += 1 | 206 s += 1 |
205 if parts[-1]: # possible trailing - | 207 if parts[-1]: # possible trailing - |
206 yield ('symbol', parts[-1], s) | 208 yield (b'symbol', parts[-1], s) |
207 else: | 209 else: |
208 yield ('symbol', sym, s) | 210 yield (b'symbol', sym, s) |
209 pos -= 1 | 211 pos -= 1 |
210 else: | 212 else: |
211 raise error.ParseError( | 213 raise error.ParseError( |
212 _("syntax error in revset '%s'") % program, pos | 214 _(b"syntax error in revset '%s'") % program, pos |
213 ) | 215 ) |
214 pos += 1 | 216 pos += 1 |
215 yield ('end', None, pos) | 217 yield (b'end', None, pos) |
216 | 218 |
217 | 219 |
218 # helpers | 220 # helpers |
219 | 221 |
220 _notset = object() | 222 _notset = object() |
221 | 223 |
222 | 224 |
223 def getsymbol(x): | 225 def getsymbol(x): |
224 if x and x[0] == 'symbol': | 226 if x and x[0] == b'symbol': |
225 return x[1] | 227 return x[1] |
226 raise error.ParseError(_('not a symbol')) | 228 raise error.ParseError(_(b'not a symbol')) |
227 | 229 |
228 | 230 |
229 def getstring(x, err): | 231 def getstring(x, err): |
230 if x and (x[0] == 'string' or x[0] == 'symbol'): | 232 if x and (x[0] == b'string' or x[0] == b'symbol'): |
231 return x[1] | 233 return x[1] |
232 raise error.ParseError(err) | 234 raise error.ParseError(err) |
233 | 235 |
234 | 236 |
235 def getinteger(x, err, default=_notset): | 237 def getinteger(x, err, default=_notset): |
249 | 251 |
250 | 252 |
251 def getlist(x): | 253 def getlist(x): |
252 if not x: | 254 if not x: |
253 return [] | 255 return [] |
254 if x[0] == 'list': | 256 if x[0] == b'list': |
255 return list(x[1:]) | 257 return list(x[1:]) |
256 return [x] | 258 return [x] |
257 | 259 |
258 | 260 |
259 def getrange(x, err): | 261 def getrange(x, err): |
260 if not x: | 262 if not x: |
261 raise error.ParseError(err) | 263 raise error.ParseError(err) |
262 op = x[0] | 264 op = x[0] |
263 if op == 'range': | 265 if op == b'range': |
264 return x[1], x[2] | 266 return x[1], x[2] |
265 elif op == 'rangepre': | 267 elif op == b'rangepre': |
266 return None, x[1] | 268 return None, x[1] |
267 elif op == 'rangepost': | 269 elif op == b'rangepost': |
268 return x[1], None | 270 return x[1], None |
269 elif op == 'rangeall': | 271 elif op == b'rangeall': |
270 return None, None | 272 return None, None |
271 raise error.ParseError(err) | 273 raise error.ParseError(err) |
272 | 274 |
273 | 275 |
274 def getintrange(x, err1, err2, deffirst=_notset, deflast=_notset): | 276 def getintrange(x, err1, err2, deffirst=_notset, deflast=_notset): |
275 """Get [first, last] integer range (both inclusive) from a parsed tree | 277 """Get [first, last] integer range (both inclusive) from a parsed tree |
276 | 278 |
277 If any of the sides omitted, and if no default provided, ParseError will | 279 If any of the sides omitted, and if no default provided, ParseError will |
278 be raised. | 280 be raised. |
279 """ | 281 """ |
280 if x and (x[0] == 'string' or x[0] == 'symbol'): | 282 if x and (x[0] == b'string' or x[0] == b'symbol'): |
281 n = getinteger(x, err1) | 283 n = getinteger(x, err1) |
282 return n, n | 284 return n, n |
283 a, b = getrange(x, err1) | 285 a, b = getrange(x, err1) |
284 return getinteger(a, err2, deffirst), getinteger(b, err2, deflast) | 286 return getinteger(a, err2, deffirst), getinteger(b, err2, deflast) |
285 | 287 |
294 def getargsdict(x, funcname, keys): | 296 def getargsdict(x, funcname, keys): |
295 return parser.buildargsdict( | 297 return parser.buildargsdict( |
296 getlist(x), | 298 getlist(x), |
297 funcname, | 299 funcname, |
298 parser.splitargspec(keys), | 300 parser.splitargspec(keys), |
299 keyvaluenode='keyvalue', | 301 keyvaluenode=b'keyvalue', |
300 keynode='symbol', | 302 keynode=b'symbol', |
301 ) | 303 ) |
302 | 304 |
303 | 305 |
304 # cache of {spec: raw parsed tree} built internally | 306 # cache of {spec: raw parsed tree} built internally |
305 _treecache = {} | 307 _treecache = {} |
318 | 320 |
319 >>> _build(b'f(_) and _', (b'string', b'1'), (b'symbol', b'2')) | 321 >>> _build(b'f(_) and _', (b'string', b'1'), (b'symbol', b'2')) |
320 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2')) | 322 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2')) |
321 """ | 323 """ |
322 template = _cachedtree(tmplspec) | 324 template = _cachedtree(tmplspec) |
323 return parser.buildtree(template, ('symbol', '_'), *repls) | 325 return parser.buildtree(template, (b'symbol', b'_'), *repls) |
324 | 326 |
325 | 327 |
326 def _match(patspec, tree): | 328 def _match(patspec, tree): |
327 """Test if a tree matches the given pattern statement; return the matches | 329 """Test if a tree matches the given pattern statement; return the matches |
328 | 330 |
331 [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')] | 333 [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')] |
332 >>> _match(b'f(_)', parse(b'f(1, 2)')) | 334 >>> _match(b'f(_)', parse(b'f(1, 2)')) |
333 """ | 335 """ |
334 pattern = _cachedtree(patspec) | 336 pattern = _cachedtree(patspec) |
335 return parser.matchtree( | 337 return parser.matchtree( |
336 pattern, tree, ('symbol', '_'), {'keyvalue', 'list'} | 338 pattern, tree, (b'symbol', b'_'), {b'keyvalue', b'list'} |
337 ) | 339 ) |
338 | 340 |
339 | 341 |
340 def _matchonly(revs, bases): | 342 def _matchonly(revs, bases): |
341 return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases)) | 343 return _match(b'ancestors(_) and not ancestors(_)', (b'and', revs, bases)) |
342 | 344 |
343 | 345 |
344 def _fixops(x): | 346 def _fixops(x): |
345 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be | 347 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be |
346 handled well by our simple top-down parser""" | 348 handled well by our simple top-down parser""" |
347 if not isinstance(x, tuple): | 349 if not isinstance(x, tuple): |
348 return x | 350 return x |
349 | 351 |
350 op = x[0] | 352 op = x[0] |
351 if op == 'parent': | 353 if op == b'parent': |
352 # x^:y means (x^) : y, not x ^ (:y) | 354 # x^:y means (x^) : y, not x ^ (:y) |
353 # x^: means (x^) :, not x ^ (:) | 355 # x^: means (x^) :, not x ^ (:) |
354 post = ('parentpost', x[1]) | 356 post = (b'parentpost', x[1]) |
355 if x[2][0] == 'dagrangepre': | 357 if x[2][0] == b'dagrangepre': |
356 return _fixops(('dagrange', post, x[2][1])) | 358 return _fixops((b'dagrange', post, x[2][1])) |
357 elif x[2][0] == 'dagrangeall': | 359 elif x[2][0] == b'dagrangeall': |
358 return _fixops(('dagrangepost', post)) | 360 return _fixops((b'dagrangepost', post)) |
359 elif x[2][0] == 'rangepre': | 361 elif x[2][0] == b'rangepre': |
360 return _fixops(('range', post, x[2][1])) | 362 return _fixops((b'range', post, x[2][1])) |
361 elif x[2][0] == 'rangeall': | 363 elif x[2][0] == b'rangeall': |
362 return _fixops(('rangepost', post)) | 364 return _fixops((b'rangepost', post)) |
363 elif op == 'or': | 365 elif op == b'or': |
364 # make number of arguments deterministic: | 366 # make number of arguments deterministic: |
365 # x + y + z -> (or x y z) -> (or (list x y z)) | 367 # x + y + z -> (or x y z) -> (or (list x y z)) |
366 return (op, _fixops(('list',) + x[1:])) | 368 return (op, _fixops((b'list',) + x[1:])) |
367 elif op == 'subscript' and x[1][0] == 'relation': | 369 elif op == b'subscript' and x[1][0] == b'relation': |
368 # x#y[z] ternary | 370 # x#y[z] ternary |
369 return _fixops(('relsubscript', x[1][1], x[1][2], x[2])) | 371 return _fixops((b'relsubscript', x[1][1], x[1][2], x[2])) |
370 | 372 |
371 return (op,) + tuple(_fixops(y) for y in x[1:]) | 373 return (op,) + tuple(_fixops(y) for y in x[1:]) |
372 | 374 |
373 | 375 |
374 def _analyze(x): | 376 def _analyze(x): |
375 if x is None: | 377 if x is None: |
376 return x | 378 return x |
377 | 379 |
378 op = x[0] | 380 op = x[0] |
379 if op == 'minus': | 381 if op == b'minus': |
380 return _analyze(_build('_ and not _', *x[1:])) | 382 return _analyze(_build(b'_ and not _', *x[1:])) |
381 elif op == 'only': | 383 elif op == b'only': |
382 return _analyze(_build('only(_, _)', *x[1:])) | 384 return _analyze(_build(b'only(_, _)', *x[1:])) |
383 elif op == 'onlypost': | 385 elif op == b'onlypost': |
384 return _analyze(_build('only(_)', x[1])) | 386 return _analyze(_build(b'only(_)', x[1])) |
385 elif op == 'dagrangeall': | 387 elif op == b'dagrangeall': |
386 raise error.ParseError(_("can't use '::' in this context")) | 388 raise error.ParseError(_(b"can't use '::' in this context")) |
387 elif op == 'dagrangepre': | 389 elif op == b'dagrangepre': |
388 return _analyze(_build('ancestors(_)', x[1])) | 390 return _analyze(_build(b'ancestors(_)', x[1])) |
389 elif op == 'dagrangepost': | 391 elif op == b'dagrangepost': |
390 return _analyze(_build('descendants(_)', x[1])) | 392 return _analyze(_build(b'descendants(_)', x[1])) |
391 elif op == 'negate': | 393 elif op == b'negate': |
392 s = getstring(x[1], _("can't negate that")) | 394 s = getstring(x[1], _(b"can't negate that")) |
393 return _analyze(('string', '-' + s)) | 395 return _analyze((b'string', b'-' + s)) |
394 elif op in ('string', 'symbol', 'smartset'): | 396 elif op in (b'string', b'symbol', b'smartset'): |
395 return x | 397 return x |
396 elif op == 'rangeall': | 398 elif op == b'rangeall': |
397 return (op, None) | 399 return (op, None) |
398 elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}: | 400 elif op in {b'or', b'not', b'rangepre', b'rangepost', b'parentpost'}: |
399 return (op, _analyze(x[1])) | 401 return (op, _analyze(x[1])) |
400 elif op == 'group': | 402 elif op == b'group': |
401 return _analyze(x[1]) | 403 return _analyze(x[1]) |
402 elif op in { | 404 elif op in { |
403 'and', | 405 b'and', |
404 'dagrange', | 406 b'dagrange', |
405 'range', | 407 b'range', |
406 'parent', | 408 b'parent', |
407 'ancestor', | 409 b'ancestor', |
408 'relation', | 410 b'relation', |
409 'subscript', | 411 b'subscript', |
410 }: | 412 }: |
411 ta = _analyze(x[1]) | 413 ta = _analyze(x[1]) |
412 tb = _analyze(x[2]) | 414 tb = _analyze(x[2]) |
413 return (op, ta, tb) | 415 return (op, ta, tb) |
414 elif op == 'relsubscript': | 416 elif op == b'relsubscript': |
415 ta = _analyze(x[1]) | 417 ta = _analyze(x[1]) |
416 tb = _analyze(x[2]) | 418 tb = _analyze(x[2]) |
417 tc = _analyze(x[3]) | 419 tc = _analyze(x[3]) |
418 return (op, ta, tb, tc) | 420 return (op, ta, tb, tc) |
419 elif op == 'list': | 421 elif op == b'list': |
420 return (op,) + tuple(_analyze(y) for y in x[1:]) | 422 return (op,) + tuple(_analyze(y) for y in x[1:]) |
421 elif op == 'keyvalue': | 423 elif op == b'keyvalue': |
422 return (op, x[1], _analyze(x[2])) | 424 return (op, x[1], _analyze(x[2])) |
423 elif op == 'func': | 425 elif op == b'func': |
424 return (op, x[1], _analyze(x[2])) | 426 return (op, x[1], _analyze(x[2])) |
425 raise ValueError('invalid operator %r' % op) | 427 raise ValueError(b'invalid operator %r' % op) |
426 | 428 |
427 | 429 |
428 def analyze(x): | 430 def analyze(x): |
429 """Transform raw parsed tree to evaluatable tree which can be fed to | 431 """Transform raw parsed tree to evaluatable tree which can be fed to |
430 optimize() or getset() | 432 optimize() or getset() |
438 def _optimize(x): | 440 def _optimize(x): |
439 if x is None: | 441 if x is None: |
440 return 0, x | 442 return 0, x |
441 | 443 |
442 op = x[0] | 444 op = x[0] |
443 if op in ('string', 'symbol', 'smartset'): | 445 if op in (b'string', b'symbol', b'smartset'): |
444 return 0.5, x # single revisions are small | 446 return 0.5, x # single revisions are small |
445 elif op == 'and': | 447 elif op == b'and': |
446 wa, ta = _optimize(x[1]) | 448 wa, ta = _optimize(x[1]) |
447 wb, tb = _optimize(x[2]) | 449 wb, tb = _optimize(x[2]) |
448 w = min(wa, wb) | 450 w = min(wa, wb) |
449 | 451 |
450 # (draft/secret/_notpublic() & ::x) have a fast path | 452 # (draft/secret/_notpublic() & ::x) have a fast path |
451 m = _match('_() & ancestors(_)', ('and', ta, tb)) | 453 m = _match(b'_() & ancestors(_)', (b'and', ta, tb)) |
452 if m and getsymbol(m[1]) in {'draft', 'secret', '_notpublic'}: | 454 if m and getsymbol(m[1]) in {b'draft', b'secret', b'_notpublic'}: |
453 return w, _build('_phaseandancestors(_, _)', m[1], m[2]) | 455 return w, _build(b'_phaseandancestors(_, _)', m[1], m[2]) |
454 | 456 |
455 # (::x and not ::y)/(not ::y and ::x) have a fast path | 457 # (::x and not ::y)/(not ::y and ::x) have a fast path |
456 m = _matchonly(ta, tb) or _matchonly(tb, ta) | 458 m = _matchonly(ta, tb) or _matchonly(tb, ta) |
457 if m: | 459 if m: |
458 return w, _build('only(_, _)', *m[1:]) | 460 return w, _build(b'only(_, _)', *m[1:]) |
459 | 461 |
460 m = _match('not _', tb) | 462 m = _match(b'not _', tb) |
461 if m: | 463 if m: |
462 return wa, ('difference', ta, m[1]) | 464 return wa, (b'difference', ta, m[1]) |
463 if wa > wb: | 465 if wa > wb: |
464 op = 'andsmally' | 466 op = b'andsmally' |
465 return w, (op, ta, tb) | 467 return w, (op, ta, tb) |
466 elif op == 'or': | 468 elif op == b'or': |
467 # fast path for machine-generated expression, that is likely to have | 469 # fast path for machine-generated expression, that is likely to have |
468 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()' | 470 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()' |
469 ws, ts, ss = [], [], [] | 471 ws, ts, ss = [], [], [] |
470 | 472 |
471 def flushss(): | 473 def flushss(): |
472 if not ss: | 474 if not ss: |
473 return | 475 return |
474 if len(ss) == 1: | 476 if len(ss) == 1: |
475 w, t = ss[0] | 477 w, t = ss[0] |
476 else: | 478 else: |
477 s = '\0'.join(t[1] for w, t in ss) | 479 s = b'\0'.join(t[1] for w, t in ss) |
478 y = _build('_list(_)', ('string', s)) | 480 y = _build(b'_list(_)', (b'string', s)) |
479 w, t = _optimize(y) | 481 w, t = _optimize(y) |
480 ws.append(w) | 482 ws.append(w) |
481 ts.append(t) | 483 ts.append(t) |
482 del ss[:] | 484 del ss[:] |
483 | 485 |
484 for y in getlist(x[1]): | 486 for y in getlist(x[1]): |
485 w, t = _optimize(y) | 487 w, t = _optimize(y) |
486 if t is not None and (t[0] == 'string' or t[0] == 'symbol'): | 488 if t is not None and (t[0] == b'string' or t[0] == b'symbol'): |
487 ss.append((w, t)) | 489 ss.append((w, t)) |
488 continue | 490 continue |
489 flushss() | 491 flushss() |
490 ws.append(w) | 492 ws.append(w) |
491 ts.append(t) | 493 ts.append(t) |
492 flushss() | 494 flushss() |
493 if len(ts) == 1: | 495 if len(ts) == 1: |
494 return ws[0], ts[0] # 'or' operation is fully optimized out | 496 return ws[0], ts[0] # 'or' operation is fully optimized out |
495 return max(ws), (op, ('list',) + tuple(ts)) | 497 return max(ws), (op, (b'list',) + tuple(ts)) |
496 elif op == 'not': | 498 elif op == b'not': |
497 # Optimize not public() to _notpublic() because we have a fast version | 499 # Optimize not public() to _notpublic() because we have a fast version |
498 if _match('public()', x[1]): | 500 if _match(b'public()', x[1]): |
499 o = _optimize(_build('_notpublic()')) | 501 o = _optimize(_build(b'_notpublic()')) |
500 return o[0], o[1] | 502 return o[0], o[1] |
501 else: | 503 else: |
502 o = _optimize(x[1]) | 504 o = _optimize(x[1]) |
503 return o[0], (op, o[1]) | 505 return o[0], (op, o[1]) |
504 elif op == 'rangeall': | 506 elif op == b'rangeall': |
505 return 1, x | 507 return 1, x |
506 elif op in ('rangepre', 'rangepost', 'parentpost'): | 508 elif op in (b'rangepre', b'rangepost', b'parentpost'): |
507 o = _optimize(x[1]) | 509 o = _optimize(x[1]) |
508 return o[0], (op, o[1]) | 510 return o[0], (op, o[1]) |
509 elif op in ('dagrange', 'range'): | 511 elif op in (b'dagrange', b'range'): |
510 wa, ta = _optimize(x[1]) | 512 wa, ta = _optimize(x[1]) |
511 wb, tb = _optimize(x[2]) | 513 wb, tb = _optimize(x[2]) |
512 return wa + wb, (op, ta, tb) | 514 return wa + wb, (op, ta, tb) |
513 elif op in ('parent', 'ancestor', 'relation', 'subscript'): | 515 elif op in (b'parent', b'ancestor', b'relation', b'subscript'): |
514 w, t = _optimize(x[1]) | 516 w, t = _optimize(x[1]) |
515 return w, (op, t, x[2]) | 517 return w, (op, t, x[2]) |
516 elif op == 'relsubscript': | 518 elif op == b'relsubscript': |
517 w, t = _optimize(x[1]) | 519 w, t = _optimize(x[1]) |
518 return w, (op, t, x[2], x[3]) | 520 return w, (op, t, x[2], x[3]) |
519 elif op == 'list': | 521 elif op == b'list': |
520 ws, ts = zip(*(_optimize(y) for y in x[1:])) | 522 ws, ts = zip(*(_optimize(y) for y in x[1:])) |
521 return sum(ws), (op,) + ts | 523 return sum(ws), (op,) + ts |
522 elif op == 'keyvalue': | 524 elif op == b'keyvalue': |
523 w, t = _optimize(x[2]) | 525 w, t = _optimize(x[2]) |
524 return w, (op, x[1], t) | 526 return w, (op, x[1], t) |
525 elif op == 'func': | 527 elif op == b'func': |
526 f = getsymbol(x[1]) | 528 f = getsymbol(x[1]) |
527 wa, ta = _optimize(x[2]) | 529 wa, ta = _optimize(x[2]) |
528 w = getattr(symbols.get(f), '_weight', 1) | 530 w = getattr(symbols.get(f), '_weight', 1) |
529 m = _match('commonancestors(_)', ta) | 531 m = _match(b'commonancestors(_)', ta) |
530 | 532 |
531 # Optimize heads(commonancestors(_)) because we have a fast version | 533 # Optimize heads(commonancestors(_)) because we have a fast version |
532 if f == 'heads' and m: | 534 if f == b'heads' and m: |
533 return w + wa, _build('_commonancestorheads(_)', m[1]) | 535 return w + wa, _build(b'_commonancestorheads(_)', m[1]) |
534 | 536 |
535 return w + wa, (op, x[1], ta) | 537 return w + wa, (op, x[1], ta) |
536 raise ValueError('invalid operator %r' % op) | 538 raise ValueError(b'invalid operator %r' % op) |
537 | 539 |
538 | 540 |
539 def optimize(tree): | 541 def optimize(tree): |
540 """Optimize evaluatable tree | 542 """Optimize evaluatable tree |
541 | 543 |
545 return newtree | 547 return newtree |
546 | 548 |
547 | 549 |
548 # the set of valid characters for the initial letter of symbols in | 550 # the set of valid characters for the initial letter of symbols in |
549 # alias declarations and definitions | 551 # alias declarations and definitions |
550 _aliassyminitletters = _syminitletters | {'$'} | 552 _aliassyminitletters = _syminitletters | {b'$'} |
551 | 553 |
552 | 554 |
553 def _parsewith(spec, lookup=None, syminitletters=None): | 555 def _parsewith(spec, lookup=None, syminitletters=None): |
554 """Generate a parse tree of given spec with given tokenizing options | 556 """Generate a parse tree of given spec with given tokenizing options |
555 | 557 |
562 >>> _parsewith(b'foo bar') | 564 >>> _parsewith(b'foo bar') |
563 Traceback (most recent call last): | 565 Traceback (most recent call last): |
564 ... | 566 ... |
565 ParseError: ('invalid token', 4) | 567 ParseError: ('invalid token', 4) |
566 """ | 568 """ |
567 if lookup and spec.startswith('revset(') and spec.endswith(')'): | 569 if lookup and spec.startswith(b'revset(') and spec.endswith(b')'): |
568 lookup = None | 570 lookup = None |
569 p = parser.parser(elements) | 571 p = parser.parser(elements) |
570 tree, pos = p.parse( | 572 tree, pos = p.parse( |
571 tokenize(spec, lookup=lookup, syminitletters=syminitletters) | 573 tokenize(spec, lookup=lookup, syminitletters=syminitletters) |
572 ) | 574 ) |
573 if pos != len(spec): | 575 if pos != len(spec): |
574 raise error.ParseError(_('invalid token'), pos) | 576 raise error.ParseError(_(b'invalid token'), pos) |
575 return _fixops(parser.simplifyinfixops(tree, ('list', 'or'))) | 577 return _fixops(parser.simplifyinfixops(tree, (b'list', b'or'))) |
576 | 578 |
577 | 579 |
578 class _aliasrules(parser.basealiasrules): | 580 class _aliasrules(parser.basealiasrules): |
579 """Parsing and expansion rule set of revset aliases""" | 581 """Parsing and expansion rule set of revset aliases""" |
580 | 582 |
581 _section = _('revset alias') | 583 _section = _(b'revset alias') |
582 | 584 |
583 @staticmethod | 585 @staticmethod |
584 def _parse(spec): | 586 def _parse(spec): |
585 """Parse alias declaration/definition ``spec`` | 587 """Parse alias declaration/definition ``spec`` |
586 | 588 |
590 """ | 592 """ |
591 return _parsewith(spec, syminitletters=_aliassyminitletters) | 593 return _parsewith(spec, syminitletters=_aliassyminitletters) |
592 | 594 |
593 @staticmethod | 595 @staticmethod |
594 def _trygetfunc(tree): | 596 def _trygetfunc(tree): |
595 if tree[0] == 'func' and tree[1][0] == 'symbol': | 597 if tree[0] == b'func' and tree[1][0] == b'symbol': |
596 return tree[1][1], getlist(tree[2]) | 598 return tree[1][1], getlist(tree[2]) |
597 | 599 |
598 | 600 |
599 def expandaliases(tree, aliases, warn=None): | 601 def expandaliases(tree, aliases, warn=None): |
600 """Expand aliases in a tree, aliases is a list of (name, value) tuples""" | 602 """Expand aliases in a tree, aliases is a list of (name, value) tuples""" |
602 tree = _aliasrules.expand(aliases, tree) | 604 tree = _aliasrules.expand(aliases, tree) |
603 # warn about problematic (but not referred) aliases | 605 # warn about problematic (but not referred) aliases |
604 if warn is not None: | 606 if warn is not None: |
605 for name, alias in sorted(aliases.iteritems()): | 607 for name, alias in sorted(aliases.iteritems()): |
606 if alias.error and not alias.warned: | 608 if alias.error and not alias.warned: |
607 warn(_('warning: %s\n') % (alias.error)) | 609 warn(_(b'warning: %s\n') % (alias.error)) |
608 alias.warned = True | 610 alias.warned = True |
609 return tree | 611 return tree |
610 | 612 |
611 | 613 |
612 def foldconcat(tree): | 614 def foldconcat(tree): |
613 """Fold elements to be concatenated by `##` | 615 """Fold elements to be concatenated by `##` |
614 """ | 616 """ |
615 if not isinstance(tree, tuple) or tree[0] in ( | 617 if not isinstance(tree, tuple) or tree[0] in ( |
616 'string', | 618 b'string', |
617 'symbol', | 619 b'symbol', |
618 'smartset', | 620 b'smartset', |
619 ): | 621 ): |
620 return tree | 622 return tree |
621 if tree[0] == '_concat': | 623 if tree[0] == b'_concat': |
622 pending = [tree] | 624 pending = [tree] |
623 l = [] | 625 l = [] |
624 while pending: | 626 while pending: |
625 e = pending.pop() | 627 e = pending.pop() |
626 if e[0] == '_concat': | 628 if e[0] == b'_concat': |
627 pending.extend(reversed(e[1:])) | 629 pending.extend(reversed(e[1:])) |
628 elif e[0] in ('string', 'symbol'): | 630 elif e[0] in (b'string', b'symbol'): |
629 l.append(e[1]) | 631 l.append(e[1]) |
630 else: | 632 else: |
631 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0]) | 633 msg = _(b"\"##\" can't concatenate \"%s\" element") % (e[0]) |
632 raise error.ParseError(msg) | 634 raise error.ParseError(msg) |
633 return ('string', ''.join(l)) | 635 return (b'string', b''.join(l)) |
634 else: | 636 else: |
635 return tuple(foldconcat(t) for t in tree) | 637 return tuple(foldconcat(t) for t in tree) |
636 | 638 |
637 | 639 |
638 def parse(spec, lookup=None): | 640 def parse(spec, lookup=None): |
640 return _parsewith(spec, lookup=lookup) | 642 return _parsewith(spec, lookup=lookup) |
641 except error.ParseError as inst: | 643 except error.ParseError as inst: |
642 if len(inst.args) > 1: # has location | 644 if len(inst.args) > 1: # has location |
643 loc = inst.args[1] | 645 loc = inst.args[1] |
644 # Remove newlines -- spaces are equivalent whitespace. | 646 # Remove newlines -- spaces are equivalent whitespace. |
645 spec = spec.replace('\n', ' ') | 647 spec = spec.replace(b'\n', b' ') |
646 # We want the caret to point to the place in the template that | 648 # We want the caret to point to the place in the template that |
647 # failed to parse, but in a hint we get a open paren at the | 649 # failed to parse, but in a hint we get a open paren at the |
648 # start. Therefore, we print "loc + 1" spaces (instead of "loc") | 650 # start. Therefore, we print "loc + 1" spaces (instead of "loc") |
649 # to line up the caret with the location of the error. | 651 # to line up the caret with the location of the error. |
650 inst.hint = spec + '\n' + ' ' * (loc + 1) + '^ ' + _('here') | 652 inst.hint = spec + b'\n' + b' ' * (loc + 1) + b'^ ' + _(b'here') |
651 raise | 653 raise |
652 | 654 |
653 | 655 |
654 def _quote(s): | 656 def _quote(s): |
655 r"""Quote a value in order to make it safe for the revset engine. | 657 r"""Quote a value in order to make it safe for the revset engine. |
661 >>> _quote(b'asdf\'') | 663 >>> _quote(b'asdf\'') |
662 "'asdf\\''" | 664 "'asdf\\''" |
663 >>> _quote(1) | 665 >>> _quote(1) |
664 "'1'" | 666 "'1'" |
665 """ | 667 """ |
666 return "'%s'" % stringutil.escapestr(pycompat.bytestr(s)) | 668 return b"'%s'" % stringutil.escapestr(pycompat.bytestr(s)) |
667 | 669 |
668 | 670 |
669 def _formatargtype(c, arg): | 671 def _formatargtype(c, arg): |
670 if c == 'd': | 672 if c == b'd': |
671 return '_rev(%d)' % int(arg) | 673 return b'_rev(%d)' % int(arg) |
672 elif c == 's': | 674 elif c == b's': |
673 return _quote(arg) | 675 return _quote(arg) |
674 elif c == 'r': | 676 elif c == b'r': |
675 if not isinstance(arg, bytes): | 677 if not isinstance(arg, bytes): |
676 raise TypeError | 678 raise TypeError |
677 parse(arg) # make sure syntax errors are confined | 679 parse(arg) # make sure syntax errors are confined |
678 return '(%s)' % arg | 680 return b'(%s)' % arg |
679 elif c == 'n': | 681 elif c == b'n': |
680 return _quote(node.hex(arg)) | 682 return _quote(node.hex(arg)) |
681 elif c == 'b': | 683 elif c == b'b': |
682 try: | 684 try: |
683 return _quote(arg.branch()) | 685 return _quote(arg.branch()) |
684 except AttributeError: | 686 except AttributeError: |
685 raise TypeError | 687 raise TypeError |
686 raise error.ParseError(_('unexpected revspec format character %s') % c) | 688 raise error.ParseError(_(b'unexpected revspec format character %s') % c) |
687 | 689 |
688 | 690 |
689 def _formatlistexp(s, t): | 691 def _formatlistexp(s, t): |
690 l = len(s) | 692 l = len(s) |
691 if l == 0: | 693 if l == 0: |
692 return "_list('')" | 694 return b"_list('')" |
693 elif l == 1: | 695 elif l == 1: |
694 return _formatargtype(t, s[0]) | 696 return _formatargtype(t, s[0]) |
695 elif t == 'd': | 697 elif t == b'd': |
696 return _formatintlist(s) | 698 return _formatintlist(s) |
697 elif t == 's': | 699 elif t == b's': |
698 return "_list(%s)" % _quote("\0".join(s)) | 700 return b"_list(%s)" % _quote(b"\0".join(s)) |
699 elif t == 'n': | 701 elif t == b'n': |
700 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s) | 702 return b"_hexlist('%s')" % b"\0".join(node.hex(a) for a in s) |
701 elif t == 'b': | 703 elif t == b'b': |
702 try: | 704 try: |
703 return "_list('%s')" % "\0".join(a.branch() for a in s) | 705 return b"_list('%s')" % b"\0".join(a.branch() for a in s) |
704 except AttributeError: | 706 except AttributeError: |
705 raise TypeError | 707 raise TypeError |
706 | 708 |
707 m = l // 2 | 709 m = l // 2 |
708 return '(%s or %s)' % (_formatlistexp(s[:m], t), _formatlistexp(s[m:], t)) | 710 return b'(%s or %s)' % (_formatlistexp(s[:m], t), _formatlistexp(s[m:], t)) |
709 | 711 |
710 | 712 |
711 def _formatintlist(data): | 713 def _formatintlist(data): |
712 try: | 714 try: |
713 l = len(data) | 715 l = len(data) |
714 if l == 0: | 716 if l == 0: |
715 return "_list('')" | 717 return b"_list('')" |
716 elif l == 1: | 718 elif l == 1: |
717 return _formatargtype('d', data[0]) | 719 return _formatargtype(b'd', data[0]) |
718 return "_intlist('%s')" % "\0".join('%d' % int(a) for a in data) | 720 return b"_intlist('%s')" % b"\0".join(b'%d' % int(a) for a in data) |
719 except (TypeError, ValueError): | 721 except (TypeError, ValueError): |
720 raise error.ParseError(_('invalid argument for revspec')) | 722 raise error.ParseError(_(b'invalid argument for revspec')) |
721 | 723 |
722 | 724 |
723 def _formatparamexp(args, t): | 725 def _formatparamexp(args, t): |
724 return ', '.join(_formatargtype(t, a) for a in args) | 726 return b', '.join(_formatargtype(t, a) for a in args) |
725 | 727 |
726 | 728 |
727 _formatlistfuncs = { | 729 _formatlistfuncs = { |
728 'l': _formatlistexp, | 730 b'l': _formatlistexp, |
729 'p': _formatparamexp, | 731 b'p': _formatparamexp, |
730 } | 732 } |
731 | 733 |
732 | 734 |
733 def formatspec(expr, *args): | 735 def formatspec(expr, *args): |
734 ''' | 736 ''' |
770 parsed = _parseargs(expr, args) | 772 parsed = _parseargs(expr, args) |
771 ret = [] | 773 ret = [] |
772 for t, arg in parsed: | 774 for t, arg in parsed: |
773 if t is None: | 775 if t is None: |
774 ret.append(arg) | 776 ret.append(arg) |
775 elif t == 'baseset': | 777 elif t == b'baseset': |
776 if isinstance(arg, set): | 778 if isinstance(arg, set): |
777 arg = sorted(arg) | 779 arg = sorted(arg) |
778 ret.append(_formatintlist(list(arg))) | 780 ret.append(_formatintlist(list(arg))) |
779 else: | 781 else: |
780 raise error.ProgrammingError("unknown revspec item type: %r" % t) | 782 raise error.ProgrammingError(b"unknown revspec item type: %r" % t) |
781 return b''.join(ret) | 783 return b''.join(ret) |
782 | 784 |
783 | 785 |
784 def spectree(expr, *args): | 786 def spectree(expr, *args): |
785 """similar to formatspec but return a parsed and optimized tree""" | 787 """similar to formatspec but return a parsed and optimized tree""" |
787 ret = [] | 789 ret = [] |
788 inputs = [] | 790 inputs = [] |
789 for t, arg in parsed: | 791 for t, arg in parsed: |
790 if t is None: | 792 if t is None: |
791 ret.append(arg) | 793 ret.append(arg) |
792 elif t == 'baseset': | 794 elif t == b'baseset': |
793 newtree = ('smartset', smartset.baseset(arg)) | 795 newtree = (b'smartset', smartset.baseset(arg)) |
794 inputs.append(newtree) | 796 inputs.append(newtree) |
795 ret.append("$") | 797 ret.append(b"$") |
796 else: | 798 else: |
797 raise error.ProgrammingError("unknown revspec item type: %r" % t) | 799 raise error.ProgrammingError(b"unknown revspec item type: %r" % t) |
798 expr = b''.join(ret) | 800 expr = b''.join(ret) |
799 tree = _parsewith(expr, syminitletters=_aliassyminitletters) | 801 tree = _parsewith(expr, syminitletters=_aliassyminitletters) |
800 tree = parser.buildtree(tree, ('symbol', '$'), *inputs) | 802 tree = parser.buildtree(tree, (b'symbol', b'$'), *inputs) |
801 tree = foldconcat(tree) | 803 tree = foldconcat(tree) |
802 tree = analyze(tree) | 804 tree = analyze(tree) |
803 tree = optimize(tree) | 805 tree = optimize(tree) |
804 return tree | 806 return tree |
805 | 807 |
816 expr = pycompat.bytestr(expr) | 818 expr = pycompat.bytestr(expr) |
817 argiter = iter(args) | 819 argiter = iter(args) |
818 ret = [] | 820 ret = [] |
819 pos = 0 | 821 pos = 0 |
820 while pos < len(expr): | 822 while pos < len(expr): |
821 q = expr.find('%', pos) | 823 q = expr.find(b'%', pos) |
822 if q < 0: | 824 if q < 0: |
823 ret.append((None, expr[pos:])) | 825 ret.append((None, expr[pos:])) |
824 break | 826 break |
825 ret.append((None, expr[pos:q])) | 827 ret.append((None, expr[pos:q])) |
826 pos = q + 1 | 828 pos = q + 1 |
827 try: | 829 try: |
828 d = expr[pos] | 830 d = expr[pos] |
829 except IndexError: | 831 except IndexError: |
830 raise error.ParseError(_('incomplete revspec format character')) | 832 raise error.ParseError(_(b'incomplete revspec format character')) |
831 if d == '%': | 833 if d == b'%': |
832 ret.append((None, d)) | 834 ret.append((None, d)) |
833 pos += 1 | 835 pos += 1 |
834 continue | 836 continue |
835 | 837 |
836 try: | 838 try: |
837 arg = next(argiter) | 839 arg = next(argiter) |
838 except StopIteration: | 840 except StopIteration: |
839 raise error.ParseError(_('missing argument for revspec')) | 841 raise error.ParseError(_(b'missing argument for revspec')) |
840 f = _formatlistfuncs.get(d) | 842 f = _formatlistfuncs.get(d) |
841 if f: | 843 if f: |
842 # a list of some type, might be expensive, do not replace | 844 # a list of some type, might be expensive, do not replace |
843 pos += 1 | 845 pos += 1 |
844 islist = d == 'l' | 846 islist = d == b'l' |
845 try: | 847 try: |
846 d = expr[pos] | 848 d = expr[pos] |
847 except IndexError: | 849 except IndexError: |
848 raise error.ParseError(_('incomplete revspec format character')) | 850 raise error.ParseError( |
849 if islist and d == 'd' and arg: | 851 _(b'incomplete revspec format character') |
852 ) | |
853 if islist and d == b'd' and arg: | |
850 # we don't create a baseset yet, because it come with an | 854 # we don't create a baseset yet, because it come with an |
851 # extra cost. If we are going to serialize it we better | 855 # extra cost. If we are going to serialize it we better |
852 # skip it. | 856 # skip it. |
853 ret.append(('baseset', arg)) | 857 ret.append((b'baseset', arg)) |
854 pos += 1 | 858 pos += 1 |
855 continue | 859 continue |
856 try: | 860 try: |
857 ret.append((None, f(list(arg), d))) | 861 ret.append((None, f(list(arg), d))) |
858 except (TypeError, ValueError): | 862 except (TypeError, ValueError): |
859 raise error.ParseError(_('invalid argument for revspec')) | 863 raise error.ParseError(_(b'invalid argument for revspec')) |
860 else: | 864 else: |
861 # a single entry, not expensive, replace | 865 # a single entry, not expensive, replace |
862 try: | 866 try: |
863 ret.append((None, _formatargtype(d, arg))) | 867 ret.append((None, _formatargtype(d, arg))) |
864 except (TypeError, ValueError): | 868 except (TypeError, ValueError): |
865 raise error.ParseError(_('invalid argument for revspec')) | 869 raise error.ParseError(_(b'invalid argument for revspec')) |
866 pos += 1 | 870 pos += 1 |
867 | 871 |
868 try: | 872 try: |
869 next(argiter) | 873 next(argiter) |
870 raise error.ParseError(_('too many revspec arguments specified')) | 874 raise error.ParseError(_(b'too many revspec arguments specified')) |
871 except StopIteration: | 875 except StopIteration: |
872 pass | 876 pass |
873 return ret | 877 return ret |
874 | 878 |
875 | 879 |
876 def prettyformat(tree): | 880 def prettyformat(tree): |
877 return parser.prettyformat(tree, ('string', 'symbol')) | 881 return parser.prettyformat(tree, (b'string', b'symbol')) |
878 | 882 |
879 | 883 |
880 def depth(tree): | 884 def depth(tree): |
881 if isinstance(tree, tuple): | 885 if isinstance(tree, tuple): |
882 return max(map(depth, tree)) + 1 | 886 return max(map(depth, tree)) + 1 |
883 else: | 887 else: |
884 return 0 | 888 return 0 |
885 | 889 |
886 | 890 |
887 def funcsused(tree): | 891 def funcsused(tree): |
888 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'): | 892 if not isinstance(tree, tuple) or tree[0] in (b'string', b'symbol'): |
889 return set() | 893 return set() |
890 else: | 894 else: |
891 funcs = set() | 895 funcs = set() |
892 for s in tree[1:]: | 896 for s in tree[1:]: |
893 funcs |= funcsused(s) | 897 funcs |= funcsused(s) |
894 if tree[0] == 'func': | 898 if tree[0] == b'func': |
895 funcs.add(tree[1][1]) | 899 funcs.add(tree[1][1]) |
896 return funcs | 900 return funcs |
897 | 901 |
898 | 902 |
899 _hashre = util.re.compile('[0-9a-fA-F]{1,40}$') | 903 _hashre = util.re.compile(b'[0-9a-fA-F]{1,40}$') |
900 | 904 |
901 | 905 |
902 def _ishashlikesymbol(symbol): | 906 def _ishashlikesymbol(symbol): |
903 """returns true if the symbol looks like a hash""" | 907 """returns true if the symbol looks like a hash""" |
904 return _hashre.match(symbol) | 908 return _hashre.match(symbol) |
917 [] | 921 [] |
918 """ | 922 """ |
919 if not tree: | 923 if not tree: |
920 return [] | 924 return [] |
921 | 925 |
922 if tree[0] == "symbol": | 926 if tree[0] == b"symbol": |
923 if _ishashlikesymbol(tree[1]): | 927 if _ishashlikesymbol(tree[1]): |
924 return [tree[1]] | 928 return [tree[1]] |
925 elif len(tree) >= 3: | 929 elif len(tree) >= 3: |
926 results = [] | 930 results = [] |
927 for subtree in tree[1:]: | 931 for subtree in tree[1:]: |