|
|
|
Purpose: |
|
|
|
In this phase we complete the construction of the calculator language interpreter by adding a handful of key features and upgrading its data types. What has the language been missing? What keeps us from writing useful programs in it? The answer is simple: Without conditional evaluation, our language can't really be said to be a programming language. It is just a simple macro-expression language. With it, though, it becomes the real thing. (An argument has been made that what really distinguished ENIAC from its predecessors, what made it a computer, rather than a calculator, was the fact that it had a conditional branch instruction.) In this phase we will add conditional evaluation, as well as sequencing, local variables, a print statement, and, to round things out, strings as a data type. |
Special Note for Spring 2000: |
|
|
|
In various places below, the project will refer to changes you must make to the SyntaxConverter functor. Because of the delay in completing the sample solution and the project description, you are not required to implement the new syntax converter for this project. It has been provided for you compiled into the version of SML used for this project. |
Strings as a Datatype |
|
|
|
The lowest level change to the language involves the
incorporation of strings as a datatype. To this end, the
datatype In order to incorporate this change into the inerpreter,
you will need to make various changes in
The change from number to literal for datavalues is the only change made in the UserInterface functor. Therefore, that change has been made for you and a useable functor loaded into the sml version for this project. |
Write and Newline |
|
|
|
You must add a new unary built-in When printing strings, do not include quotes around the string. To allow for full generality, the While we could require
|
Conditonal Evaluation |
|
|
|
In eager languages, conditional evaluation is what is known as a special form. That is, while it will look just like the other built-in operators from a syntactic standpoint, it will require a different operational behavior. Unlike the ordinary built-in operators, this one does not necessarilly evaluate all of its arguments. Instead a prescribed procedure is used to determine which arguments are actually evaluated. The form of conditional evaluation we will include in the
language is the
The operational semantics (i.e. what the system does when it encounters this expression) are as follows: Evaluate the first element of the first argument pair. If that expression evaluates to a true value (as outlined below) then evaluate the second expression in the pair and return its value as the value of the conditional expression. If, on the other hand, the first expression evaluates to false (as outlined below) then the second expression in the pair is discarded unevaluated, and the process repeats with the next pair. If the last pair is reached and none of the pairs has a test expression that evaluates to true then the value of the whole conditional expression is false. Of course, our definition of literals does not include a
boolean type, so we must pick a suitable notion of true
and false. At this point we will take the C/Rex
way out and say that any non-zero value is true and
any zero value is false. Since the test expressions
can evaluate to any literal type, we must, in turn, have a
suitable notion of "zero" at each object type. Therefore we
will assume that the following are all "zero" for the
purposes of deciding whether to evaluate the other
expression in the pair: In the case that a conditional expression returns
false because none of the test expressions evaluated
to non-zero values, you should use (In later projects we will change our notion of
true and false as our data types get richer.
Therefore, you may find it useful to define
In order to distinguish the conditional expression from
an ordinary application structure, a new case has been added
to the definition of the datatype
You must modify |
Relational Operators |
|
|
|
In order to make the conditional statement useful, you
need some test expressions. To that end, you should
implement the following built-in relational operators:
Note, these are just ordinary built-in operators. They
are not new special forms. Also, we are not saying these are
the only operators that can be used to construct test
expressions. Any expression can be used as the first
expression in a |
Sequencing |
|
|
|
As shown above, with the addition of side effects to the language, it is natural to add a notion of sequencing. Strictly speaking it is not necessary to add a new
special form for this to the language, since function
application can be used to accomplish the desired efects.
For example, if we knew that the arguments to a function
were always evaluated left to right, we could define the
Even if we did not know the order of evaluation of function arguments, eager evaluation imparts an ordering to nested applications. So, we could define it as:
Nevertheless, to avoid such machinations, it is useful to add a syntax for sequencing to the language, and to implement it as a special form. The syntax you are to support is:
This syntax should be mapped (in
and evaluated in the way sequences are evaluated in ML. That is, a sequence should evaluate each of the individual expressions in order, and return the value of the last expression as the value of the sequence. |
Local Declarations with Let |
|
|
|
As you well know from your experience with ML, because the body of a function is often composed of several sub-computations, it is often desireable to evaluate these sub-expressions individually and assign names to the results, in order to make the computation clearer, and, in the case of a sub-expression used several times, more efficient. The basic syntax for declaring local variables is the let expression. The syntax of this special form is:
The value of the let expression is the result of evaluating each of the n expressions and binding the n variables to the results and then evaluating the body expression in the resulting environment. In this basic form of the let expression each of the n expressions is evaluated in the same environment, the one that is active at the point that the let expression is evaluated. Often it is desirable to define one of the local variables in terms of one of the others already defined. While this can be done by using nested let expressions, this is annoying in practice. Therefore, a second form of let expression, introduced with the keyword let* is used for this need. In order to support these two special forms, two new cases have been added to AbstractSyntax.cexp:
Clet' is used for the let* form (since * is not allowed in ML identifiers). |
What I have Provided: |
|
|
|
I have created a version of the SML-NJ interpreter with
the parser structure (I have also loaded a sample solution to the evaluator functor and built a structure called testEvaluator as well. You can use the functions in this structure from the SML command line to see what the results of evaluation should be.) I have also provided an executable sample solution at
Finally, the file |
What You Should Turn In |
|
|
|
Only the evaluator and the syntax converter get
appreciable changes in this phase. As mentioned above, the
changes to the syntax converter and user interface have
already been made for you. Therefore, you should submit just
the new implementation of We will test your solution (which should be a single file
containing just the |
|
|
This page copyright ©2000 by Joshua S. Hodas. It was built on a Macintosh. Last rebuilt on Tuesday March 21, 2000. |
http://www.cs.hmc.edu/~hodas/courses/cs131/projects/project06.html |
|