Compiler Design

Delta: An Incremental Compiler
Step 20: Ternary Conditional Operator

Description

Add support for the ternary conditional operator. It has the following syntax:

\( \; \; \; \; \texttt{\{} \; \textit{exp}_1 \; \texttt{,} \; \textit{exp}_2 \; \texttt{,} \; \textit{exp}_3 \; \texttt{\}} \)

If the first operand evaluates to true (any non-zero value), the overall result would be the evaluation of the second operand. On the other hand, if the first operand evaluates to false (zero), the overall result would be the evaluation of the third operand. Note that the first operand is always evaluated but between the second and third operands only one is ever evaluated and never both of them.

The ternary conditional operator can be used as part of any expression and has the same precedence as the parenthesis.

Example

The Delta program:

var x;
x = 3;
{ x - 2, x + x, x * x } + 1

should produce the following WAT code:

(module
  (func
    (export "_start")
    (result i32)
    (local $x i32)
    i32.const 3
    local.set $x
    local.get $x
    i32.const 2
    i32.sub
    if (result i32)
    local.get $x
    local.get $x
    i32.add
    else
    local.get $x
    local.get $x
    i32.mul
    end
    i32.const 1
    i32.add
  )
)

The above WAT function’s return value should be:

7

Unit Tests

# File: tests/test_20_ternary_conditional.py

from unittest import TestCase
from delta import Compiler, SyntaxMistake
from wasmtime._trap import Trap


class TestTernaryConditional(TestCase):

    def setUp(self):
        self.c = Compiler('program_start')

    def test_syntax_mistake(self):
        with self.assertRaises(SyntaxMistake):
            self.c.realize('{1, 2}')

    def test_ternary1(self):
        self.assertEqual(1,
                         self.c.realize('{true, 1, 2}'))

    def test_ternary2(self):
        self.assertEqual(2,
                         self.c.realize('{false, 1, 2}'))

    def test_ternary3(self):
        self.assertEqual(1,
                         self.c.realize('''
                         var a;
                         a = true;
                         a = {a, 1, 2};
                         a'''))

    def test_ternary4(self):
        self.assertEqual(2,
                         self.c.realize('''
                         var a;
                         a = false;
                         a = {a, 1, 2};
                         a'''))

    def test_ternary5(self):
        self.assertEqual(7,
                         self.c.realize('''
                         var x;
                         x = 3;
                         { x - 2, x + x, x * x } + 1
                         '''))

    def test_ternary6(self):
        self.assertEqual(10,
                         self.c.realize('''
                         var x;
                         x = 3;
                         { x - 3, x + x, x * x } + 1
                         '''))

    def test_ternary7(self):
        self.assertEqual(4,
                         self.c.realize('''
                         var x;
                         x = 3;
                         if {x - 3, false, true} {
                            x = x + 1;
                         } else {
                            x = x - 1;
                         }
                         x
                         '''))

    def test_ternary8(self):
        self.assertEqual(2,
                         self.c.realize('''
                         var x;
                         x = 3;
                         if !{x - 3, false, true} {
                            x = x + 1;
                         } else {
                            x = x - 1;
                         }
                         x
                         '''))

    def test_ternary9(self):
        self.assertEqual(8,
                         self.c.realize('''
                         var x, y, z;
                         x = 2;
                         y = x * 2 - 1;
                         z = x + y;
                         { {!z, false, true},
                           {x, {!y, x + z, z + y}, z},
                           {y, z, x}
                         }
                         '''))

    def test_ternary10(self):
        self.assertEqual(1,
                         self.c.realize('''
                         var t, u;
                         t = false;
                         u = true;
                         {t, u/t, u}
                         '''))

    def test_ternary11(self):
        self.assertEqual(1,
                         self.c.realize('''
                         var a, b;
                         a = 0;
                         {a, 1 / a, {!a, 1, 1 / a}}
                         '''))

    def test_ternary_runtime_error(self):
        with self.assertRaises(Trap):
            self.c.realize('''
            var t, u;
            t = false;
            u = true;
            {!t, u/t, u}
            ''')