Repository: mesonbuild/meson

Issue:
Unexpected value of arithmetic expression
**Describe the bug**
The result fo arithmetic expression is unexpected.

**To Reproduce**
```
project('test', 'c',  meson_version: '== 0.52.1')                                                                                                                                              
a = 3                                                                                                                                                                                          
b = 1                                                                                                                                                                                          
c = 1                                                                                                                                                                                          
d = a - b - c                                                                                                                                                                                  
message('d=@0@'.format(d))
```
produces `Message: d=3`

**Expected behavior**
`Message: d=1`

However the following works as expected (just added parentheses):
```
project('test', 'c',  meson_version: '== 0.52.1')                                                                                                                                              
a = 3                                                                                                                                                                                          
b = 1                                                                                                                                                                                          
c = 1                                                                                                                                                                                          
d = (a - b) - c                                                                                                                                                                                  
message('d=@0@'.format(d))
```

**system parameters**
* plain native build
* Linux Manjaro
* Python 3.8.1
* meson version: 0.52.1
* ninja version: 1.10.0



Discussion:
Confirmed broken in latest git as well... This is awful. I'm honestly shocked this hasn't been caught until now.

It looks like the expression is being interpreted as `a - (b - c)`. I'm not very familiar with meson's parsing code, but I'll see what I can do...
I think I fixed this. I'll send a PR when I've added some tests and made sure it doesn't break anything.

```diff
     def e5sub(self) -> BaseNode:
         left = self.e5mod()
-        if self.accept('dash'):
-            return ArithmeticNode('sub', left, self.e5sub())
+        while self.accept('dash'):
+            left = ArithmeticNode('sub', left, self.e5mod())
         return left
```

Resolving patch:
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -489,6 +489,13 @@ def accept(self, s: str) -> bool:
             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 @@ def e4(self) -> BaseNode:
         return left
 
     def e5(self) -> BaseNode:
-        return self.e5add()
-
-    def e5add(self) -> BaseNode:
-        left = self.e5sub()
-        if self.accept('plus'):
-            return ArithmeticNode('add', left, self.e5add())
-        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 self.e5addsub()
+
+    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 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:


Test patch:
diff --git a/test cases/common/230 arithmetic operators/meson.build b/test cases/common/230 arithmetic operators/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/common/230 arithmetic operators/meson.build	
@@ -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)