Get the starter code.
Due: Friday, November 17, 11:59pm Pacific Time
This homework is the next step in our Trefoil journey. You will implement an interpreter for version 2 of Trefoil, which supports nested syntax based on parenthesized symbol trees (PSTs). (Everyone in the world except for us calls PSTs S-expressions, in case you want to Google.)
This tour is careful to discuss every file, so you might want to only skim this tour on first reading.
ast.ml
: represents the abstract syntax tree (AST) of expressions and bindingsexpr
,
similar to the examples like IntLit
, Plus
, etc. in the starter code.expr
variants as well as add several new ones.expr_of_pst
converts (or tries to convert) a PST into an expression AST.expr
there should be a case in expr_of_pst
that produces it from the corresponding PST.binding
.dune
: the build fileerrors.ml
interpreter.ml
: the interpreter!!interpret_expression
evaluates an expression to a value in a given dynamic environmentinterpret_binding
transforms and returns a dynamic environment as the result of executing
the given bindingdynamic_environment
type, which implements a map from strings
to values.pst.ml
: represents a PSTpst
type defined in this file.pstparser.ml
implements a parser for PSTs that you do not need to understand or edit.
Of course, you are free to read the file if you are curious how it works. It is not
too complicated.parse_pst
and pstparser_of_source
trefoil2.ml
: main entry point of the interpretermain
function supports either keyboard or file input. If you pass a
file name on the command line, then it will read input from that file. Otherwise,
it will read input from standard input.pstparser
and then repeatedly reads PSTs off the inputAst.binding_of_pst
, and then interprets the binding.trefoil2test.ml
: defines several starter tests The starter code uses the [@@deriving show]
annotation, which automatically
generates "toString" like functions for various types.
You need to install this feature with
opam install ppx_deriving
Please ask questions on Ed if you get stuck!
A Trefoil v2 program is a sequence of bindings which are evaluated in order in a (initially empty) dynamic environment. The environment can be transformed by each binding as that binding executes.
There are three kinds of bindings: variable bindings, top-level expressions, and test bindings.
We have discussed all but test bindings in lecture. See the slides and demo code for an informal description of those. See the Trefoil v2 language spec for a formal description.
Test bindings are written like this example:
(test (= 3 (+ 1 2)))
A test binding evaluates its expression. If expression evaluates to true, the test passes and nothing happens to the environment. If the expression evaluates to anything other than true (false or any other value, e.g., 17), the test fails and a (nice) error is reported to the Trefoil programmer.
There are 15 kinds of expressions. See the lecture material for an informal introduction and the spec for a formal list and description.
The only expressions we did not discuss in lecture are:
(= (+ 1 2) 3)
. =
is a binary operator that
takes two integers, just like +
does, except that =
returns a boolean
indicating whether its arguments are equal. Note that =
only works on
integers. It is a runtime error to pass anything besides an integer to =
.(cons? (+ 1 2))
. cons?
is a unary
operator similar to nil?
that returns true if its argument is a pair. Unlike
nil?
, which returns true only on nil
, there are many different values on
which cons?
returns true: for example, (cons 0 1)
, (cons true (cons false
nil))
, etc.Complete the starter code to implement the Trefoil v2 language as specified. Write tests that cover the normal and error case behavior for all language features.
We have left several TODO
s for you throughout the code.
You can do anything in whatever order you want, but here is one suggested path.
Familiarize yourself with the provided files:
opam install ppx_deriving
if you haven't already.dune build
ensure there are no errors.dune test
to run the tests in trefoil2test.ml
and ensure that 26 tests fail out of 34. (!)
The passing tests are due to features we have implemented for you in the starter code.Implement new expressions:
true
. The AST node is already defined (BoolLit
), and
expr_of_pst
is done for you in the starter code in ast.ml
. All that's left is to
implement the case in interpret_expression
in interpreter.ml
.IntLit
"interpret_true"
passes.false
.true
, just with a different value for its argument.expr_of_pst
.true
."parsing_false"
passes.false
as well. Depending on how you
handled true
, you may not need any code changes."interpret_false"
passes.-
and *
. They are similar to +
.Plus
and renaming it.expr_of_pst
with cases for -
and *
."parsing_false"
but for -
and *
.Minus(IntLit 3, IntLit 17)
or whatever.interpret_expression
with cases for -
and *
."interpret_minus"
and "interpret_times"
pass.+
.
You must throw RuntimeError
and not anything else."test_plus_wrong_types"
for a similar example+
in expr_of_pst
.
You must throw AbstractSyntaxError
and not anything else."test_plus_abstract_syntax_error"
=
following the workflow below.RuntimeError
if =
is given
anything other than integers.expr_of_pst
expr_of_pst
by comparing it to a manually
constructed AST, and ensure the test passes.interpret_expression
.RuntimeError
)AbstractSyntaxError
)if
following the workflow.Implement the new test
binding:
test
binding using this modified workflow.test
is a binding not an expression:binding
.binding_of_pst
instead of the similar function for expressionsinterpret_binding
instead of the similar function for expressions"test binding"
in their name pass.binding_of_pst
test with a manually constructed ASTFinish implementing the other expressions:
let
following the workflow."test let..."
tests pass.nil
, cons
, nil?
, cons?
, car
, and cdr
following the workflow for each.nil?
and cons?
. You should write both normal and
error case tests for these.Implement your own feature (optional):
ast.ml
interpreter.ml
trefoil2test.ml