CSC 533: Programming Languages
Spring 2023

HW2: Implementing a Simple Interpreter


For this assignment, you are to complete an interpreter for a subset of the SILLY (Simple, Interpreted, Limited Language for You) programming language. Recall that the syntax rules for SILLY v. 22 were provided in HW1. There can be no spaces between the characters in an identifier, integer, or string. Otherwise, whitespace is allowed between tokens. The SILLY language is case-sensitive, so variables a and A are considered unique.

There are three data types in SILLY: integer, string, and Boolean. The language is strongly typed, with any type mismatches resulting in either syntax or runtime errors. A variable must be declared (with its type) and assigned a value the first time it appears. The type for that variable is then set and cannot be changed.

There are a number of operators in SILLY for building expressions, as listed below:

 DESCRIPTION                  OPERATOR(S)       OPERAND(S)                           EXAMPLE
 ------------------------     -----------       -------------------------------      ----------------------------
 standard math operations     + - * / % ^       applied to 2 integers                (4 * 7) → 28
 string concatenation         +                 applied to 2 strings                 ("foo" + "bar") → "foobar"
 string length                #                 applied to a string                  (# "foo") → 3 
 string indexing              @                 applied to a string and integer      ("foo" @ 0) → "f" 
 Boolean negation             not               applied to a Boolean                 (not true) → false
 Boolean connectives          and or            applied to 2 Booleans                (true or false) → true
 comparison operators         == != < > <= >=   applied to two value of same type    (3 <= 4) → true
 

Note that the '+' operator can be applied to two numbers (addition) or to two strings (concatenation). The comparison operators can be applied to any two values of the same type. Integers are ordered numerically (e.g., 2 < 3); strings are ordered alphabetically (e.g., "bar" < "foo"); and Booleans are ordered truthfully (e.g., false < true). Any type mismatch within an expression (e.g., applying the '*' operator to two string values or comparing values of different types) should result in a run-time error with a descriptive message.

SILLY provides a number of different statement types, including declaration, assignment, output, if and while. Sample interpretations of SILLY code are provided below, with user input prefaced by ">>>" and the interpreter's output highlighted in red:

SAMPLE CODE (output in red)
>>> int x = 9
>>> output x
9
>>> int y = (2 * x)
>>> output (x + y)
27
>>> str word = "foo"
>>> output word
foo
>>> output ((word + "bar") + (word + "biz"))
foobarfoobiz
>>> boo flag = true
>>> output flag
true
>>> output (not flag)
false
>>> if (x < y) then {
        output "ok"
        output "still-ok"
    } noelse
ok
still-ok
>>> if (x > y) then {
        output "not-ok"
        output "still-not-ok"
    } noelse
>>> int count = 5
>>> while (count > 0) do {
        output count
        count = (count - 1)
    }
5
4
3
2
1
>>> boo stop = false
>>> int num = 2
>>> while (not stop) do {
        output num
        num = (2 * num)
        if (num > 1000) then {
            stop = true
        } noelse
    }
2
4
8
16
32
64
128
256
512
>>> output {1, (15 / 3), (true or false), ("ab" + "cd")}
1 5 true abcd
>>> str text = "banana"
>>> output {(# text), (text @ 0)}
6 b
>>> str reverse = ""
>>> int i = 0
>>> while (i < (# text)) do {
        reverse = ((text @ i) + reverse)
        i = (i + 1)
    }
>>> output {text, reverse}
banana ananab 
>>> int grade = 88
>>> if (grade >= 90) then {
        output "excellent"
    } else {
        if (grade >= 75) then {
            output "good"
        } else {
            output "needs_work"
        } 
    }
good
>>> int current = 5
>>> while (current != 1) do {
        output current
        if ((current % 2) == 0) then {
            current = (current / 2)
        }
        else {
            current = ((3 * current) + 1)
        }
    }
5
16
8
4
2
>>> output {1, "STUCK"}
1 STUCK
>>> int value = 2
>>> do {
        output value
        value = (2 * value)
    } until (value > 1000)
2
4
8
16
32
64
128
256
512

An incomplete version of the SILLY interpreter is provided for you via the following classes/files (which are also downloadable as SILLY.zip):

As provided, the interpreter runs a subset of the SILLY language. Variable declarations, assignments, while loops and compound statements are fully implemented. Output statements, if statements and expressions are partially implemented. Note that statements in the left column above will execute given the existing code; statements in the right column depend on language features that you will be implementing in this assignment.

For this assignment, you will need to make the following modifications/additions:

  1. Within the Expression class, all the required operators are defined except for two string operators: # and @. Modify the Expression constructor so that expressions involving these two operators can be read in and stored. Then, modify the evaluate method so that the operators are evaluated as specified.
  2. Implement the do statement in a class named Do. This class should be very similar to the While class. Note, however, that the test for a do statement appears at the end of the statement and specifies a stopping condition. As a result, the code in the body of a do statement will always be executed at least once. Be sure to implement all the required methods of the class, including toString.
  3. The current definition of the output statement is incomplete in that it can only display a single expression. Modify the Output class so that it also supports multiple output expressions enclosed in curly braces and separated by commas. This will require adding field(s) and modifying the constructor (to be able to read and store an arbitrary number of expressions), the execute method (to display all the expressions on a single line), and the toString method (to display all formats). Note: the current format for outputting a single expression should still be supported.
  4. The current definition of the if statement is incomplete in that it does not support the else case. Modify the If class so that it can handle this additional case when it appears. This will require adding field(s) and modifying the constructor (to be able to read and store the statements associated with the else case), the execute method (to execute those statements when the test fails), and the toString method (to display all formats).

Note that these additions/modifications may also require updating other files in the project so that all new operators, keywords and statements are recognized. All syntax errors (e.g., malformed expressions) and runtime errors (e.g., type mismatches) must be caught by your code and result in meaningful error messages. To make testing your code easier, you can enter your test statements into a text file then execute them using the provided (Batch.java) class. When executed, the batch interpreter prompts the user for a file name, reads SILLY statements from the file, and executes them. By default, the text file is assumed to be in the top-level directory of the project.