Repository: mesonbuild/meson

Issue:
Ternary if
Im really want see ternary IF in meson.
1. This is readable
2. This is widespread common operation
3. Reduce size of script

I prefer c ternary if form:
`variable =  (condition) ? true : false`
but are be happy with python form
`variable = true if condition else false`



Discussion:
Ternary operator is nice, but on the other hand it seems to always invite abuse which is not particularly nice. Also the Python form is just plain confusing, especially to newcomers but also to seasoned developers.

maybe disable recursion definition ? all the evil of the construction like `a = x?x:y?z:z?b:c`

```
variable = true if condition else false
```

Would be quite nice, I think. It's a very common idiom in build files. I have hundreds of lines of:

```
if condition
  conf.set('FOOBAR', true)
endif
```

or

```
if condition
  conf.set('FOOBAR', true)
else
  conf.set('FOOBAR', false)
endif
```

I think removing recursion in this would be a good idea too. It would get rid of all the anti-patterns associated with ternary operators and simplify the implementation.


Resolving patch:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1323,6 +1323,8 @@ def evaluate_statement(self, cur):
             return self.evaluate_plusassign(cur)
         elif isinstance(cur, mparser.IndexNode):
             return self.evaluate_indexing(cur)
+        elif isinstance(cur, mparser.TernaryNode):
+            return self.evaluate_ternary(cur)
         elif self.is_elementary_type(cur):
             return cur
         else:
@@ -2401,6 +2403,16 @@ def evaluate_if(self, node):
         if not isinstance(node.elseblock, mparser.EmptyNode):
             self.evaluate_codeblock(node.elseblock)
 
+    def evaluate_ternary(self, node):
+        assert(isinstance(node, mparser.TernaryNode))
+        result = self.evaluate_statement(node.condition)
+        if not isinstance(result, bool):
+            raise InterpreterException('Ternary condition is not boolean.')
+        if result:
+            return self.evaluate_statement(node.trueblock)
+        else:
+            return self.evaluate_statement(node.falseblock)
+
     def evaluate_foreach(self, node):
         assert(isinstance(node, mparser.ForeachClauseNode))
         varname = node.varname.value
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -67,6 +67,7 @@ def __init__(self):
             ('lt', re.compile(r'<')),
             ('ge', re.compile(r'>=')),
             ('gt', re.compile(r'>')),
+            ('questionmark', re.compile(r'\?')),
         ]
 
     def lex(self, code):
@@ -282,6 +283,14 @@ def __init__(self, lineno, colno, condition, block):
         self.condition = condition
         self.block = block
 
+class TernaryNode():
+    def __init__(self, lineno, colno, condition, trueblock, falseblock):
+        self.lineno = lineno
+        self.colno = colno
+        self.condition = condition
+        self.trueblock = trueblock
+        self.falseblock = falseblock
+
 class ArgumentNode():
     def __init__(self, token):
         self.lineno = token.lineno
@@ -344,6 +353,7 @@ class Parser:
     def __init__(self, code):
         self.stream = Lexer().lex(code)
         self.getsym()
+        self.in_ternary = False
 
     def getsym(self):
         try:
@@ -383,6 +393,16 @@ def e1(self):
                 raise ParseException('Assignment target must be an id.',
                                      left.lineno, left.colno)
             return AssignmentNode(left.lineno, left.colno, left.value, value)
+        elif self.accept('questionmark'):
+            if self.in_ternary:
+                raise ParseException('Nested ternary operators are not allowed.',
+                                     left.lineno, left.colno)
+            self.in_ternary = True
+            trueblock = self.e1()
+            self.expect('colon')
+            falseblock = self.e1()
+            self.in_ternary = False
+            return TernaryNode(left.lineno, left.colno, left, trueblock, falseblock)
         return left
 
     def e2(self):


Test patch:
diff --git a/test cases/common/116 ternary/meson.build b/test cases/common/116 ternary/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/common/116 ternary/meson.build	
@@ -0,0 +1,7 @@
+project('ternary operator', 'c')
+
+one = true ? 1 : error('False branch should not be evaluated')
+two = false ? error('True branch should not be evaluated.') : 2
+
+assert(one == 1, 'Return value from ternary true is wrong.')
+assert(two == 2, 'Return value from ternary false is wrong.')
diff --git a/test cases/failing/30 nested ternary/meson.build b/test cases/failing/30 nested ternary/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/failing/30 nested ternary/meson.build	
@@ -0,0 +1,3 @@
+project('nested ternary', 'c')
+
+x = true ? (false ? 1 : 0) : 2