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.mlinterpreter.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_sourcetrefoil2.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 TODOs 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_pstexpr_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.mlinterpreter.mltrefoil2test.ml