mparser: fix precedence of arithmetic operators
The arithmetic operators are now split into two groups: * The add/sub group: +, - * The mul/div group: *, /, % All operators within the same group are left-associative and have equal precedence. The mul/div group has a higher precedence than the add/sub group, as one would expect. Previously every operator had a different precedence and was right-associative, which resulted in surprising behavior. This is a potentially breaking change for projects that relied on the old incorrect behavior. Fixes #6870
This commit is contained in:
parent
d84daf3e95
commit
2cfbb36a84
|
@ -489,6 +489,13 @@ class Parser:
|
|||
return True
|
||||
return False
|
||||
|
||||
def accept_any(self, tids: T.Sequence[str]) -> str:
|
||||
tid = self.current.tid
|
||||
if tid in tids:
|
||||
self.getsym()
|
||||
return tid
|
||||
return ''
|
||||
|
||||
def expect(self, s: str) -> bool:
|
||||
if self.accept(s):
|
||||
return True
|
||||
|
@ -562,36 +569,35 @@ class Parser:
|
|||
return left
|
||||
|
||||
def e5(self) -> BaseNode:
|
||||
return self.e5add()
|
||||
return self.e5addsub()
|
||||
|
||||
def e5add(self) -> BaseNode:
|
||||
left = self.e5sub()
|
||||
if self.accept('plus'):
|
||||
return ArithmeticNode('add', left, self.e5add())
|
||||
def e5addsub(self) -> BaseNode:
|
||||
op_map = {
|
||||
'plus': 'add',
|
||||
'dash': 'sub',
|
||||
}
|
||||
left = self.e5muldiv()
|
||||
while True:
|
||||
op = self.accept_any(tuple(op_map.keys()))
|
||||
if op:
|
||||
left = ArithmeticNode(op_map[op], left, self.e5muldiv())
|
||||
else:
|
||||
break
|
||||
return left
|
||||
|
||||
def e5sub(self) -> BaseNode:
|
||||
left = self.e5mod()
|
||||
if self.accept('dash'):
|
||||
return ArithmeticNode('sub', left, self.e5sub())
|
||||
return left
|
||||
|
||||
def e5mod(self) -> BaseNode:
|
||||
left = self.e5mul()
|
||||
if self.accept('percent'):
|
||||
return ArithmeticNode('mod', left, self.e5mod())
|
||||
return left
|
||||
|
||||
def e5mul(self) -> BaseNode:
|
||||
left = self.e5div()
|
||||
if self.accept('star'):
|
||||
return ArithmeticNode('mul', left, self.e5mul())
|
||||
return left
|
||||
|
||||
def e5div(self) -> BaseNode:
|
||||
def e5muldiv(self) -> BaseNode:
|
||||
op_map = {
|
||||
'percent': 'mod',
|
||||
'star': 'mul',
|
||||
'fslash': 'div',
|
||||
}
|
||||
left = self.e6()
|
||||
if self.accept('fslash'):
|
||||
return ArithmeticNode('div', left, self.e5div())
|
||||
while True:
|
||||
op = self.accept_any(tuple(op_map.keys()))
|
||||
if op:
|
||||
left = ArithmeticNode(op_map[op], left, self.e6())
|
||||
else:
|
||||
break
|
||||
return left
|
||||
|
||||
def e6(self) -> BaseNode:
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
project('arithmetic operators')
|
||||
assert(5 - 3 - 1 == 1)
|
||||
assert(5 - (3 - 1) == 3)
|
||||
assert(5 - 1 * 3 - 3 == -1)
|
||||
assert(420 - 300 - 51 == 69)
|
||||
assert(1000 / 2 / 2 / 2 == 125)
|
||||
assert(4 * 9 / 3 % 8 - 3 - 10 / 2 == -4)
|
||||
assert(94 - 30 + (2 - (40 - 6 + 7) - 9) - 10 == 6)
|
Loading…
Reference in New Issue