Mini-Assignment due Feb 6, 2020 - interpreter warm-up: Calculator Language (an interpreter)
In this warm-up mini-assignment, you will write an interpreter for a simple calculator language. Our aim is to get you to start programming in JavaScript, and prepare you for the next two two homework assignments, which will involve writing interpreters that will have a very similar structure.
Assignment Instructions

Revise interp.js (and, if needed, classes.js) in order to implement an interpreter for the simple language defined below. Your interpreter should pass all the test cases below, and more generally should implement the semantics specified here for the calculator language (we reserve the right to run tests beyond those given). Turn in your files (zip them together if you are updating both) on Canvas by 11:59pm on February 6, 2020. This is an individual assignment. You may start with the OO-style or functional-style starter code developed in class.

Language Definition

In this exercise, you will write an interpreter for a simple calculator language. Let’s begin by taking a look at the syntax of expressions:
Concrete Syntax JS AST
e ::=
n
e1 + e2
e1 - e2
e1 * e2
e1 / e2
e1 ^ e2
new Num(n)
new Add(e1, e2)
new Sub(e1, e2)
new Mul(e1, e2)
new Div(e1, e2)
new Pow(e1, e2)
n ::= a JavaScript number, e.g., 42
The left-hand column of the table above shows the concrete syntax of expressions, i.e., the way a programmer writes expressions. For example, 1+2*3 is a valid expression in our language.

Parsing is covered in other classes, e.g., CS132 (Compilers). For completeness, we will also spend a lecture or two on the subject. The first step of an interpreter (and related tools like compilers) is typically to parse the program. A parser ensures that the given program is syntactically valid and, if so, produces a tree representation of the program, which is called an abstract syntax tree, or AST. This tree unambiguously represents the program, after for example taking into account the operator precedence and associativity rules of the language (which, in case you were wondering, are not shown in the table above). Parsing is a fascinating subject in its own right, but this course is not about syntax — our focus will be on techniques for rapidly prototyping the semantics of programming languages. So rather than make you write your own parsers, we will always provide them for you. (In case you’re wondering, the parser for this exercise is generated from the Ohm grammar in this page, and the set of semantic actions that produce ASTs is here.)

The right-hand column of the table above shows the format of the ASTs that are produced by our parser. The classes that we use to represent AST nodes (Num, Add, Sub, etc.) are declared in this file.

For example, to create the AST for the expression 1+2*3, we evaluate the JavaScript expression new Add(new Num(1), new Mul(new Num(2), new Num(3))). The resulting tree is shown on the right. Note that the table at the top of this page provides the names of the fields that can be used to access the components of each AST node: Add nodes have fields called e1 and e2, Num nodes have a field called n, and so on. These fields are the edges in the tree diagram.

But wait, there’s more!

To make our calculator language a little more interesting, let’s extend it with support for variables. We can do this by defining a program to be a sequence of expressions, and adding new syntax for referencing and assigning to variables:

Concrete Syntax JS AST Semantics
p ::=
e1; e2;; en
new Prog([e1, e2,, en]) Evaluates each expression in order, and produces the value of the last expression.
e ::=
n
e1 + e2
x
x = e
new Num(n)
new Add(e1, e2)
new Ref(x)
new Assign(x, e)
 
Produces the current value of x , or
0 if
x has not been assigned a value.
Evaluates e , stores the result in x, and produces the new value of x.
x ::= an identifier, e.g., sum a string, e.g., "sum"
Our newly-extended calculator language lets us write programs like this one: x = 2; y = 3 * x; y + xwhich should evaluate to 8.

The AST for this example program is shown on the right. Note that the Prog node has a single field whose value is an array of expression ASTs — Assigns, Adds, etc. That field is called es because in our description of the abstract syntax (in the table above) that array was written as [e1, e2,, en], i.e., it is an array of “e”s. Similarly, if it had been an array of identifiers, [x1, x2,, xn], it would have been called xs. We will use this convention throughout the course.

The Interpreter

Your job is to write an interpreter for our calculator language. The entry-point to your interpreter will be a function called interp that takes the AST of a program and returns the value that it produces: function interp(ast) { // do your thing! }

We have included a file called interp.js where you should do all your work. Also, at the bottom of this page, you will see some unit tests for your implementation — these will run automatically every time you refresh the page. Last but not least, we have also provided a “playground” where you can type in programs, see their corresponding ASTs, and see what value your interpreter produces.

Unit Tests

Here are a few unit tests to get you started. You can (and should!) add your own test cases, too. How? Just edit tests.js; the format is pretty self-explanatory.

Playground