CSC 533: Programming Languages
Spring 2018

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 SILLY v. 17.0 are as follows:

<statement> --> <vardec> | <assignment> | <output> | <if> | <while> | <repeat> | <import> <vardec> --> 'var' <assignment> <assignment> --> <identifier> '=' <expression> <output> --> 'output' <expression> | 'output' '{' { <expression> } '}' <if> --> 'if' <expression> 'then' { <statement> } [ 'else' { <statement> } ] 'end' <while> --> 'while' <expression> 'do' { <statement> } 'end' <repeat> --> 'repeat' <expression> 'times' { <statement> } 'end' <import> --> 'import' <filename> <expression> --> <identifier> | <number> | <string> | <boolean> | '(' <expression> ')' | '(' <unary_op> <expression> ')' | '(' <expression> <binary_op> <expression> ')' <identifier> --> <letter> { ( <letter> | <digit> ) } <number> --> [ - ] <digit> { <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 '"' <filename> --> any legal file name (e.g., program.txt) <unary_op> --> 'not' <binary_op> --> '.' | +' | '-' | '*' | '/' | '%' | '==' | '!=' | '>' | '>=' | '<' | '<=' | 'and' | 'or'

There are no spaces between the characters in an identifier, number, 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: number, 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 assigned new values. Referencing an undeclared variable (e.g., in an expression or assignment statement) will result in a run-time error. All operators must be applied to values of the same type. The '+' operator can be applied to two numbers (where it represents addition) or two strings (where it represents concatenation). The math operators ('+', '-', '*', '/', and '%') can only be applied to numbers, whereas the Boolean operators ('and', 'or', and 'not') can only be applied to Boolean values. Comparison operators ('==', '!=', '>', '>=', '<', and '<=') can be applied to any two values of the same type. Numbers are ordered numerically (e.g., 2 < 3); strings are ordered alphabetically (e.g., "bar" < "foo"); Booleans are ordered truthfully (e.g., false < true). In addition, the expressions in if statements and while loops must evaluate to Boolean values, whereas the expression in a repeat loop must evaluate to a number (which is rounded down to an integer). Any type mismatch will result in a runtime error.

For example:

SAMPLE CODE (output in red)
  >>> var x = 5
  >>> output  x
  5
  >>> var y = ( x + 1 )
  >>> output y
  6
  >>> output ( ( x * y ) + 1 )
  31
  >>> x = ( ( y / 2 ) + ( x * 2 ) )
  >>> output x 
  13
  >>> var word = "foo"
  >>> var longer =  ( word + "bar" )
  >>> output  ( "longer=" + longer )
  longer=foobar
  >>> repeat 4 times
        output "howdy"
      end
  howdy
  howdy
  howdy
  howdy
  >>> repeat 3.9 times
        output "doodly"
      end
  doodly
  doodly
  doodly
  >>> var num = 1 
  >>> var N = 5
      repeat N times
        output num
        num = ( num * 2 )
      end
  1
  2
  4
  8
  16
  >>> var flag = true
  >>> output flag
  true
  >>> output ( flag == true )
  true
  >>> output ( "foo" != "foo" )
  false
  >>> output  ( 3 > 2 )
  true
  var test = ( "smith" <= "smooth" )
  >>> output test
  true
  >>> output ( ( 3.5 + 1 ) )
  4.5
  >>> output ( ( ( "foo" ) ) )
  foo
  >>> output  { "foo"  ( 3 - 1 ) 
                ( "a" + ( "b" + "c" ) ) 
                true }
  foo 2 abc true
  >>> var count = 5
  >>> while ( count > -1 ) do
        output count 
        count = ( count - 1 )
      end
  5
  4
  3
  2
  1
  0
  >>> var a = 1
  >>> var b = 5
  >>> while ( a < b ) do
        a = ( a + 1 )
        b = ( b - 2 )
      end
  >>> output { "a=" a "b=" b }
  a= 3 b= 1

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, the BooleanValue data type is implemented, but the only Boolean operators defined are '=='and '!='. Update the Token and Expression classes so that the other comparison operators ('>', '>=', '<', and '<=') are recognized and can be used to build Boolean expressions (evaluating to true/false). Keep in mind that if any comparison operator is applied to values of different types (e.g., a string and a number), an exception with appropriate runtime error message should be thrown.
  2. Currently, there is one case in an expression that is not implemented. In particular, the option in the syntax rule, where an expression is defined to be an expression in parentheses, is not defined. Thus, a legal expression such as ( 3 ) would not be recognized as legal by the interpreter. Modify the Expression class so that this option is implemented and correctly handled.
  3. Currently, there is one case in the output statement that is not implemented. As it is, it can only be used to print a single expression. Modify the Output class so that the other option, which allows you to print multiple expressions contained in curly-braces, is implemented and correctly handled. Note: this requires storing the expressions when an output statement is created, since the expressions cannot be evaluated until the statement is executed.
  4. Implement a While class that defines while loops. This class should be similar to the Repeat class due to their structural similarity (both contain statements that are controlled by a test). However, a while statement is controlled by a Boolean expression and repeatedly executes the block of enclosed statements as long as the expression evaluates to true. Note that you will also need to make small additions to the Token and Statement classes.

Note that in making these additions, the main program file (Interpreter.java) need not be modified at all. Instead, you will primarily be adding new derived classes, with some minor modifications to Token, Statement, and Expression. As you add new features, be sure that syntax and runtime errors result in exceptions being thrown with informative messages.

Also note that an import statement is especially useful as you debug your interpreter. You may enter SILLY statements in a text file, then use an import statement (e.g., import prog.txt) to read in and execute those statements in sequence.