CSC 533: Programming Languages
Spring 2020

HW2: Implementing a Simple Interpreter


For this assignment, you are to complete an interpreter for a simple, procedural programming language named SILLY (Simple, Interpreted, Limited Language for You). The grammar rules for the initial subset of SILLY v. 19.0 are as follows:

<statement> --> <vardec> | <assignment> | <output> | <repeat> | <while> | <if> <vardec> --> 'var' <assignment> <assignment> --> <identifier> '=' <expression> ';' <output> --> 'output' [ <expression> { ',' <expression> } ] ';' <repeat> --> 'repeat' <expression> 'times' { <statement> } 'end' <while> --> 'while' <expression> 'do' { <statement> } 'end' <if> --> 'if' <expression> 'then' { <statement> } [ 'else' { <statement> } ] 'end' <expression> --> <unary_op> <term> | <term> { <binary_op> <term> } <term> --> <identifier> | <integer> | <string> | <boolean> <identifier> --> <letter> { ( <letter> | <digit> ) } <integer> --> [ '-' ] <digit> { <digit> } <string> --> '"' { <char> } '"' <boolean> --> 'true' | 'false' <letter> --> 'a' | 'b' | 'c' | ... | 'z' | 'A' | 'B' | 'C' | ... | 'Z' <digit> --> '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' <char> --> any non-whitespace character other than '"' <unary_op> --> 'not' | '#' <binary_op> --> '+' | '-' | '*' | '/' | '%' | '^' | '@' | 'and' | 'or' | '==' | '!=' | '>' | '>=' | '<' | '<='

There are no spaces between the characters in an identifier, integer, or string. Otherwise, tokens in the other rules are all separated by whitespace. 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 interpreter relies heavily on run-time type checking when evaluating expressions. Every variable must be declared and initialized before it can be used in expressions or assignment. Referencing an undeclared variable (e.g., in an expression) will result in a run-time exception, as will redeclaring a variable that already exists. In addition, all operators must be applied to values of the correct types (see below). Any type mismatch in an expression will result in a runtime exception.

 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 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"); Booleans are ordered truthfully (e.g., false < true). Also note that all binary operators have the same precedence and are left associative. That means that you can chain together binary operations, but evaluation is left-to-right regardless of the operators. Thus, "1 + 2 == 3" is a valid expression, but "3 == 1 + 2" is not. The expression that controls a repeat statement must evaluate to an integer value, while the expression that controls while and if statements must evaluate to a Boolean value.

For example:

SAMPLE CODE (output in red)
  >>> var num = 99 ;
  >>> output num ;
  99
  >>> var double = num * 2 ;
  >>> output double ;
  198
  >>> var expr1 = 10 * 2 + 1 ;
  >>> var expr2 = 1 + 10 * 2 ;
  >>> output expr1 == expr2 ;
  false
  >>> var str = "foo" ;
  >>> output str ;
  foo
  >>> var flag = true ;
  >>> output not flag ;
  false
  >>> output flag and true ;
  true
  >>> output "foo" != "foo" ;
  false
  >>> output 3 > 2 ;
  true
  >>> output 3 > 2 == true ;
  true
  >>> var test = "smith" < "smooth" ;
  >>> output test ;
  true
  >>> repeat 3 times
          output "Hello" ;
      end 
  Hello
Hello
Hello
>>> var count = 1 ; var sum = 0 ; while count < 6 do sum = sum + count ; count = count + 1 ; output sum ; end 1
3
6
10
15
  >>> output ; 
   
  >>> var word = "foo" ;
  >>> var longer = word ^ "bar" ^ "bizbaz" ;
  >>> output word , longer ;
  foo foobarbizbaz	
  >>> output word >= "Foo" , word == "Foo" ;
  true false	
  >>> var wordlen = # word ;
  >>> output wordlen ;
  3	  
  >>> var i = 0 ;
      while i < wordlen do
          output word @ i ;
          i = i + 1 ;
      end
  f
o
o
>>> if wordlen < 5 then output word , "is" , "short" ; end foo is short >>> var blen = # "banana" ; >>> if blen < 5 then output "short" ; else output "long" ; end long >>> var text = "foobar" ; >>> var tlen = # text ; >>> var copy = "" ; >>> var j = 0 ; >>> var ch = "?" ; >>> while j < tlen do ch = text @ j ; if ch == "o" then copy = copy ^ "*" ; else copy = copy ^ ch ; end j = j + 1 ; end >>> output copy ; f**bar

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

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

  1. Currently, most of the operators are implemented so you can test the interpreter using simple expressions. However, the following operators are not implemented: '<=', '>=', '%', 'or', '^', #' and '@'. Make the appropriate additions to the Expression class so that these operators are recognized and evaluated correctly. A runtime exception should be thrown if any operator is applied to inappropriate types.
  2. 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 can also 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 also update the toString method so that it accommodates the full format.
  3. 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 statements that are controlled by a Boolean test). However, an if statement has an optional else case that must be handled. You will also need to update the Statement class to recognize this new type of statement.

Note that in making these additions, the driver class (Interpreter) need not be modified at all. Instead, you will be modifying existing classes (Expression, Output, and Statement) and adding one new class (If). As you add new features, be sure that syntax and runtime errors result in exceptions being thrown with informative messages.

To make testing your code easier, an additional class (Batch.java) is also provided. This class will read SILLY statements from a file and execute them.