CSC 533: Programming Languages
Spring 2021

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. 20 were provided in HW1. To keep the interpreter simple, it assumed that there is whitespace (spaces, tabs and/or blank lines) between individual tokens in the language. There can be no spaces between the characters in an identifier, integer, or string, however. 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. Every variable must be declared before it can be used, and the language is strongly typed. Each variable is given a type when declared and can only be assigned values of that type. Assigning a value of the incorrect type should result in a run-time error.

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                #chars            applied to a string                  #chars "foo" → 3
 string indexing              @index            applied to a string and integer      "foo" @index 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: variable declaration, assignment, output statement, compound statement, if statement, while loop, and repeat loop. Simple subroutines (with typed parameters and no return value) can also be declared and called (more detail in a later assignment).

SAMPLE CODE (output in red)
  >>> int num = 99 ;
  >>> output num ;
  99
  >>> int expr1 = ( 10 * 2 ) + 1 ;
  >>> int expr2 = 1 + ( 10 * 2 ) ;
  >>> output expr1 == expr2 ;
  true
  >>> str w1 = "foo" ;
  >>> output w1 ;
  foo
  >>> boo flag = true ;
  >>> output flag ;
  true
  >>> output "foo" != "foo" ;
  false
  >>> output 3 > 2 ;
  true
  >>> boo test = "smith" < "smooth" ;
  >>> output test ;
  true
  >>> repeat 3 {
          output "Hello" ;
      } 
  Hello
  Hello
  Hello
  >>> int count = 4 ;
      while count > 0 {
          output count ;
          count = ( count - 1 ) ;
      }
  4
  3
  2
  1
  >>> str word = "banana" ;
  >>> str reverse = "" ;
  >>> int i = 0 ;
  >>> while i < ( #chars word ) {
          reverse = ( word @index i ) + reverse ;
          i = i + 1 ;
      }
  >>> output reverse ;
  ananab
  >>> boo check = true ;
  >>> boo opp = not check ;
  >>> output opp ;
  false
  >>> output ( check and opp ) , ( check or opp ) ;
  false true
  >>> if ( not ( check and opp ) ) {
          output "one" ;
          output ;
          output "two" ;
      }
      else {
          output "three" ;
          output ;
          output "four" ;
      }
  one
  
  two
  >>> int blen = #chars "banana" ;
  >>> if blen < 5 { }
      else { output "long" ; }
  long
  >>> int num = 6 ;
  >>> output "Hailstone" , "sequence" , "from" , num ;
  Hailstone sequence from 6
  >>> while num != 1 {
          if ( num % 2 ) == 0  {
              num = num / 2 ;
          }
          else {
              num = ( 3 * num ) + 1 ;
          }
          output num ;
      }	
  3
  10
  5
  16
  8
  4
  2
  1

An incomplete version of the SILLY interpreter is provided for you via the following classes/files:

As provided, the interpreter runs a subset of the SILLY language. Variable declarations, assignments and output statements are implemented, although output statements are currently limited to displaying only a single value (instead of an arbitrary sequence of comma-separated values). Repeat and while loops are implemented, but if statements are not. The three data types (numbers, strings and Booleans) are implemented, as are most of the operators that can be used in expressions. Missing are the Boolean operators (not, and and or). So, you should be able to test the interpreter out on basic statements (such as in the left column above). 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. Currently, the interpreter does no checking for run-time errors. This means that some illegal actions will be allowed (e.g., redeclaring an existing variable, assigning a value of the wrong type to a variable), while other actions will crash the program without producing a reasonable error message (e.g., a type mismatch in an expression). You are to add run-time error checking to the existing classes so that any run-time error throws an exception of the form "RUN-TIME ERROR: error description" where "error description" is a meaningful description of the error. For example, attempting to assign a value to an undeclared variable might produce "RUN-TIME ERROR: Variable in assignment is not declared". Similarly, attempting to assign a value of the wrong type might produce "RUN-TIME ERROR: type mismatch in assignment". Hint: run-time checking will need to occur in the execute method of each Statement class, as well as in the evaluate methods of the Expression and Term classes.
  2. Currently, most of the operators are implemented so you can test the interpreter using simple expressions. However, the Boolean operators not, and and or are not. Make the appropriate additions to the appropriate classes so that these operators are recognized and evaluated correctly. A runtime exception should be thrown if any Boolean operator is applied to a non-Boolean value.
  3. The current definition of the output statement is incomplete in that it only supports printing a single expression. Modify the Output class so that multiple (or no) expressions, separated by commas, can be printed in a single statement. Note: this requires storing all the expressions when an output statement is created, since the expressions cannot be evaluated until the statement is executed. Be sure to update the toString method so that it accommodates the full format.
  4. Implement an If class that defines if statements. This class will have significant similarities to the While class due to their structural similarity (both contain a compound statement that is controlled by a Boolean test). However, an if statement has an else case that must also be handled. You will need to update the Token and Statement classes to recognize this new type of statement. Also, be sure to catch any syntax and run-time errors that might occur in your if statement, and implement the toString method as required.

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.