Compiler Design

Delta: An Incremental Compiler
Step 21: Case Statement

Description

Add support for the case statement. It has the following syntax:

\( \; \; \; \; \texttt{case} \; \textit{expression}_0 \; \texttt{\{} \)
\( \; \; \; \; \; \; \; \; \texttt{when} \; \textit{expression}_1 \; \texttt{\{}\)
\( \; \; \; \; \; \; \; \; \; \; \; \; \textit{statements}_1 \)
\( \; \; \; \; \; \; \; \; \texttt{\}} \)
\( \; \; \; \; \; \; \; \; \texttt{when} \; \textit{expression}_2 \; \texttt{\{}\)
\( \; \; \; \; \; \; \; \; \; \; \; \; \textit{statements}_2 \)
\( \; \; \; \; \; \; \; \; \texttt{\}} \)
\( \; \; \; \; \; \; \; \; \vdots \)
\( \; \; \; \; \; \; \; \; \texttt{when} \; \textit{expression}_n \; \texttt{\{}\)
\( \; \; \; \; \; \; \; \; \; \; \; \; \textit{statements}_n \)
\( \; \; \; \; \; \; \; \; \texttt{\}} \)
\( \; \; \; \; \; \; \; \; \texttt{else} \; \texttt{\{}\)
\( \; \; \; \; \; \; \; \; \; \; \; \; \textit{statements}_{n+1} \)
\( \; \; \; \; \; \; \; \; \texttt{\}} \)
\( \; \; \; \; \texttt{\} } \)

The case statement can have zero or more when clauses. The else clause is optional, but must go at the end if present.

The case statement described above is equivalent to:

\( \; \; \; \; \texttt{if} \; \textit{expression}_0 \; \texttt{==} \; \textit{expression}_1 \texttt{\{} \; \)
\( \; \; \; \; \; \; \; \; \textit{statements}_1 \)
\( \; \; \; \; \texttt{\} } \)
\( \; \; \; \; \texttt{else if} \; \textit{expression}_0 \; \texttt{==} \; \textit{expression}_2 \; \texttt{\{} \; \)
\( \; \; \; \; \; \; \; \; \textit{statements}_2 \)
\( \; \; \; \; \texttt{\} } \)
\( \; \; \; \; \vdots \)
\( \; \; \; \; \texttt{else if} \; \textit{expression}_0 \; \texttt{==} \; \textit{expression}_n \; \texttt{\{} \; \)
\( \; \; \; \; \; \; \; \; \textit{statements}_n \)
\( \; \; \; \; \texttt{\} } \)
\( \; \; \; \; \texttt{else \{} \; \)
\( \; \; \; \; \; \; \; \; \textit{statements}_{n+1} \)
\( \; \; \; \; \texttt{\} } \)

Make sure to consider case and when as Delta reserved keywords from now on.

Example

The Delta program:

var x, y;
x = 3;
y = 0;
case x {
    when 1 {
        y = 10;
    }
    when 2 {
        y = 20;
    }
    when 3 {
        y = 30;
    }
    else {
        y = 40;
    }
}
y

should produce the following WAT code:

(module
  (func
    (export "_start")
    (result i32)
    (local $x i32)
    (local $y i32)
    i32.const 3
    local.set $x
    i32.const 0
    local.set $y
    local.get $x
    i32.const 1
    i32.eq
    if
    i32.const 10
    local.set $y
    else
    local.get $x
    i32.const 2
    i32.eq
    if
    i32.const 20
    local.set $y
    else
    local.get $x
    i32.const 3
    i32.eq
    if
    i32.const 30
    local.set $y
    else
    i32.const 40
    local.set $y
    end
    end
    end
    local.get $y
  )
)

The above WAT function’s return value should be:

30

Unit Tests

# File: tests/test_21_case.py

from unittest import TestCase
from delta import Compiler, SyntaxMistake
from delta.semantics import SemanticMistake


class TestCaseWhen(TestCase):

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

    def test_syntax_mistake(self):
        with self.assertRaises(SyntaxMistake):
            self.c.realize('case {}')

    def test_semantic_mistake(self):
        with self.assertRaises(SemanticMistake):
            self.c.realize('var case; 0')

    def test_case1(self):
        self.assertEqual(0,
                         self.c.realize('''
                         var x;
                         case 0 { }
                         x
                         '''))

    def test_case2(self):
        self.assertEqual(0,
                         self.c.realize('''
                         var x;
                         case 0 {
                            when 0 { }
                            when 1 { }
                            when 2 { }
                            when 3 { }
                            when 4 { }
                            when 5 { }
                            else { }
                         }
                         x
                         '''))

    def test_case3(self):
        self.assertEqual(1,
                         self.c.realize('''
                         var x;
                         case true {
                            when true {
                                x = 1;
                            }
                            when false {
                                x = 2;
                            }
                         }
                         x
                         '''))

    def test_case4(self):
        self.assertEqual(2,
                         self.c.realize('''
                         var x;
                         case false {
                            when true {
                                x = 1;
                            }
                            when false {
                                x = 2;
                            }
                         }
                         x
                         '''))

    def test_case5(self):
        self.assertEqual(40,
                         self.c.realize('''
                         var x, y;
                         x = 0;
                         y = 0;
                         case x {
                             when 1 {
                                 y = 10;
                             }
                             when 2 {
                                 y = 20;
                             }
                             when 3 {
                                 y = 30;
                             }
                             else {
                                 y = 40;
                             }
                         }
                         y
                         '''))

    def test_case6(self):
        self.assertEqual(0,
                         self.c.realize('''
                         var x, y;
                         x = 0;
                         y = 0;
                         case x {
                             when 1 {
                                 y = 10;
                             }
                             when 2 {
                                 y = 20;
                             }
                             when 3 {
                                 y = 30;
                             }
                         }
                         y
                         '''))

    def test_case7(self):
        self.assertEqual(30,
                         self.c.realize('''
                         var x, y;
                         x = 3;
                         y = 0;
                         case x {
                             when 1 {
                                 y = 10;
                             }
                             when 2 {
                                 y = 20;
                             }
                             when 3 {
                                 y = 30;
                             }
                             else {
                                 y = 40;
                             }
                         }
                         y
                         '''))

    def test_case8(self):
        self.assertEqual(333,
                         self.c.realize('''
                         var x, y, z;
                         x = 2;
                         y = 4;
                         z = 0;
                         case x + y * z {
                             when 3 - 2 {
                                 z = 100;
                             }
                             when 5 - 2 {
                                 z = 200;
                             }
                             when 1 + 1 {
                                 z = 300;
                                 case y - 1 {
                                     when x {
                                        y = y + 0;
                                        z = z + 10;
                                     }
                                     when x + 1 {
                                        y = y + 1;
                                        z = z + 10;
                                     }
                                     when x + 2 {
                                        y = y + 2;
                                        z = z + 20;
                                     }
                                     when x + 3 {
                                        y = y + 3;
                                        z = z + 30;
                                     }
                                     else {
                                        y = y + 4;
                                        z = z + 40;
                                     }
                                 }
                                 z = z + 18;
                             }
                             else {
                                 z = 400;
                             }
                         }
                         y + z
                         '''))