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. 23 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 dynamically typed, meaning that a variable does not have a set type but is defined by the value assigned to it. Variables are not explicitly declared - the first time an assignment is made to a variable, that variable is implicitly declared and initialized. If a variable is used in an expression before having a value assigned to it, a run-time error should occur. Compound statements represent new, nested scopes. If a variable is initialized inside a compound statement, it is local and will not exist outside of that scope.
There are a number of operators in SILLY for building expressions, as listed below. Note that '~' is used for subtraction, as the '-' symbol is used for representing negative integer values (e.g., -3).
DESCRIPTION OPERATOR(S) OPERAND(S) EXAMPLE ------------------------ ----------- ------------------------------- ---------------------------- standard math operations + ~ * / % ^ applied to 2 integers (4 ~ 1) → 3 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) → true4>
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 assignment, compound, print, 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) >>> print(-11) -11 >>> print("foobar") foobar >>> x = 9 >>> print(x) 9 >>> y = (2 * x) >>> print(y) 18 >>> word = "foo" >>> print(word) foo >>> print(((word + "bar") + (word + "biz"))) foobarfoobiz >>> flag = true >>> print(flag) true >>> print((not flag)) false >>> if (x < y) { print("ok") print("still-ok") } noelse ok still-ok >>> if (x > y) { print("not-ok") print("still-not-ok") } noelse >>> count = 5 >>> while (count > 0) { print(count) count = (count ~ 1) } 5 4 3 2 1 >>> stop = false >>> num = 2 >>> while (not stop) { print(num) num = (2 * num) if (num > 1000) { stop = true } noelse } 2 4 8 16 32 64 128 256 512 >>> print(1, (15 / 3), (true or false), ("ab" + "cd")) 1 5 true abcd >>> text = "banana" >>> print((# text), (text @ 0)) 6 b >>> reverse = "" >>> i = 0 >>> while (i < (# text)) { reverse = ((text @ i) + reverse) i = (i + 1) } >>> print(text, reverse) banana ananab >>> grade = 88 >>> if (grade >= 90) { print("excellent") } else { if (grade >= 75) { print("good") } else { print("needs_work") } } good >>> current = 5 >>> while (current != 1) { if ((current % 2) == 0) { current = (current / 2) } else { current = ((3 * current) + 1) } print(current) } 16 8 4 2 1 >>> for rep from 1 to 5 { print(rep, (rep^2)) } 1 1 2 4 3 9 4 16 5 25 >>> for j from 1 to 3 { for k from j to 3 { print(j, k) } } 1 1 1 2 1 3 2 2 2 3 3 3
An incomplete version of the SILLY interpreter is provided for you
via the following classes/files (which are also downloadable as SILLY.zip
):
Interpreter.java
: This is the main interpreter class for the language, which runs an infinite loop to read and execute statements. If the user enters a file name at the initial prompt, then statements are read in and executed from that file. If the user simply hits return, then they can enter and execute statements interactively.Token.java
,TokenStream.java
: These classes define the different types of tokens that make up the language and define an input stream for reading in program tokens.MemorySpace.java
,Scope.java
: These classes define how memory is organized and accessed. TheMemorySpace
class implements the stack and heap segments and utilizesScope
s to represent the distinct scopes defined by compound statements.Statement.java
: This abstract class provides the framework for all types of statements and includes a general-purpose method for reading a statement.Assignment.java
,Print.java
,If.java
,While.java
,Compound.java
: These classes, derived fromStatement
, define specific statements of the SILLY language.DataValue.java
: This interface provides the framework for the different data types in SILLY.IntegerValue.java
,StringValue.java
,BooleanValue.java
: These classes implement theDataValue
interface to define integer, string and Boolean values.Expression.java
: This class defines an expression, which is either a simple term (integer, string, Boolean, or identifier) or a parenthesized expression involving operators.
As provided, the interpreter runs a subset of the SILLY language. Assignments, while loops and compound statements are fully implemented. If and print statements are partially implemented (see details below). 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.
For this assignment, you will need to make the following modifications/additions:
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. If
class so that it can handle this additional case. 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). Print
class so that it can print multiple expressions, separated by commas. When printed, the values should all appear on the same line, separated by a single space. 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 expression values on a single line), and the toString
method (to display the general format). For
. A for statement specifies a loop variable and a range for values. The loop variable is assigned a value from that range for each pass through the loop. For example, the statement:
For
class, including toString
. 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.