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:]: