comparison mercurial/parser.py @ 25815:e71e5629e006

parser: separate actions for primary expression and prefix operator This will allow us to define both a primary expression, ":", and a prefix operator, ":y". The ambiguity will be resolved by the next patch. Prefix actions in elements table are adjusted as follows: original prefix primary prefix ----------------- -------- ----------------- ("group", 1, ")") -> n/a ("group", 1, ")") ("negate", 19) -> n/a ("negate", 19) ("symbol",) -> "symbol" n/a
author Yuya Nishihara <yuya@tcha.org>
date Sun, 05 Jul 2015 12:02:13 +0900
parents f0a77cb6316a
children 43a8a87fc175
comparison
equal deleted inserted replaced
25814:dc1a49264628 25815:e71e5629e006
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, pos) tuples 13 # tokenizer is an iterator that returns (type, value, pos) tuples
14 # elements is a mapping of types to binding strength, prefix, infix and 14 # elements is a mapping of types to binding strength, primary, prefix, infix
15 # suffix actions 15 # and suffix actions
16 # an action is a tree node name, a tree label, and an optional match 16 # an action is a tree node name, a tree label, and an optional match
17 # __call__(program) parses program into a labeled tree 17 # __call__(program) parses program into a labeled tree
18 18
19 import error 19 import error
20 from i18n import _ 20 from i18n import _
29 t = self.current 29 t = self.current
30 self.current = next(self._iter, None) 30 self.current = next(self._iter, None)
31 return t 31 return t
32 def _hasnewterm(self): 32 def _hasnewterm(self):
33 'True if next token may start new term' 33 'True if next token may start new term'
34 return bool(self._elements[self.current[0]][1]) 34 return any(self._elements[self.current[0]][1:3])
35 def _match(self, m): 35 def _match(self, m):
36 'make sure the tokenizer matches an end condition' 36 'make sure the tokenizer matches an end condition'
37 if self.current[0] != m: 37 if self.current[0] != m:
38 raise error.ParseError(_("unexpected token: %s") % self.current[0], 38 raise error.ParseError(_("unexpected token: %s") % self.current[0],
39 self.current[2]) 39 self.current[2])
48 self._match(m) 48 self._match(m)
49 return expr 49 return expr
50 def _parse(self, bind=0): 50 def _parse(self, bind=0):
51 token, value, pos = self._advance() 51 token, value, pos = self._advance()
52 # handle prefix rules on current token 52 # handle prefix rules on current token
53 prefix = self._elements[token][1] 53 primary, prefix = self._elements[token][1:3]
54 if not prefix: 54 if primary:
55 expr = (primary, value)
56 elif prefix:
57 expr = (prefix[0], self._parseoperand(*prefix[1:]))
58 else:
55 raise error.ParseError(_("not a prefix: %s") % token, pos) 59 raise error.ParseError(_("not a prefix: %s") % token, pos)
56 if len(prefix) == 1:
57 expr = (prefix[0], value)
58 else:
59 expr = (prefix[0], self._parseoperand(*prefix[1:]))
60 # gather tokens until we meet a lower binding strength 60 # gather tokens until we meet a lower binding strength
61 while bind < self._elements[self.current[0]][0]: 61 while bind < self._elements[self.current[0]][0]:
62 token, value, pos = self._advance() 62 token, value, pos = self._advance()
63 infix, suffix = self._elements[token][2:] 63 infix, suffix = self._elements[token][3:]
64 # check for suffix - next token isn't a valid prefix 64 # check for suffix - next token isn't a valid prefix
65 if suffix and not self._hasnewterm(): 65 if suffix and not self._hasnewterm():
66 expr = (suffix[0], expr) 66 expr = (suffix[0], expr)
67 else: 67 else:
68 # handle infix rules 68 # handle infix rules