assignment01
assignment01
Assignment 1
1
correct anything after the second deadline, not even for partial credit.
You should always treat the first deadline as if it is the only deadline, and
avoid using the second.
1 Introduction
In this assignment, you will learn more about how overriding with dynamic
dispatch can be used to implement choices that need to be made at run time.
Dynamic dispatch is a form of runtime polymorphism. You already have seen
compile time polymorphism in C ++ in the form of templates. Templates are
also polymorphic, but the concrete type must be known at compile time. Java
also has a kind of templates, which are called generics.
Using overriding with dynamic dispatch is the preferred approach when
choices need to made at run time, and the possible options are not known
in advance. When it later turns out that another option is needed, one can
create a new subclass and override the required methods for this subclass. If
one would use another choice mechanism, like for example switch or if, one
would have to search for these statements in the code, and add the new option
to all of them.
In this assignment, you will implement a simple, functional language, which
we creatively call SFL. The language uses different types of expressions which
need to be printed and evaluated (computed). In order to decide how a sub-
type of expression is printed/evaluated, we will override the toString( ) and
evaluate( ) methods.
Functional languages are programming languages whose programs are simi-
lar to mathematical expressions. Computation is done by computing the result
of the expression, assuming that the variables in it have certain values. In a
functional language, values of variables can be defined once, but not overwrit-
ten. The languages that you are familiar with are called imperative. In these
languages, computation is based on a state that is changed by commands of
the programming language. Functional languages don’t use state. Computing
the value of an expression is usually called evaluating the expression. In order
to fully appreciate functional programs, one would have to introduce symbolic
computation (computations that work on trees, not only on primitive, CPU dic-
tated types) and higher-order functions (these are functions whose arguments
are functions). The most used functional languages are Scala, OCaml, and
Haskell.
2
2 SFL
SFL uses a single type, unbounded precision integers. The main building block
of SFL programs are expressions, which are recursively defined as follows:
• A variable is an Expression.
• A integer (unbounded) is an Expression.
• If E is an Expression, and op is a unary operator, then op(E) is an
Expression.
• If E1 and E2 are Expression, and op is a binary operator, then op(E1 , E2 )
is an Expression.
• If E1 , . . . , En with n ≥ 0 are Expressions, and op is an operator with
arbitrary arity, then op(E1 , . . . , En ) is an Expression.
• If C, Et , and Ef are Expressions, then If (C, Et , Ef ) is an Expression.
• If str is a String, and E1 , . . . , En with n ≥ 0 are Expressions, then
str(E1 , . . . , En ) is an Expression.
• If str is a String, then Fail( str ) is an Expression.
An SFL-program is a sequence of function definitions. A function definition con-
sists of a String (the name of the function), a parameter list (distinct Strings),
and a body, which is an Expression as defined above. Since there is only one
type, there is no need to specify the type of the parameters in a function def-
inition. Function names can be overloaded by arity, which means that the
same function name can be reused, as long as the parameter lists have different
lengths.
In order to evaluate an Expression, one needs a Program and a Context.
The context remembers the values of local function variables. It is a mapping
from String to BigInteger.
During evaluation, sometimes BigIntegers need to be interpreted as booleans.
In that case, 0 is mapped to false, and all non-zero values are mapped to true.
The rules for evaluating Expressions are as follows:
• When a variable is evaluated, it is looked up in the context. If the variable
does not occur, evaluation fails.
• The evaluation of an integer constant equals the constant itself.
• A unary Expression op(E) is evaluated as follows: First evaluate E,
using the same Program and Context. Call the result i. After that, the
result is determined by the following table:
op result
neg −i
not !i
3
In the last case, i is first converted to a boolean by the rule given above.
In the result, false will converted to 0, and true will be converted to 1,
because evaluation must return a BigInteger. If op does not occur in
the table, evaluation fails.
• A binary Expression op(E1 , E2 ) is evaluated as follows: First E1 and
E2 are evaluated, assume that the results are called i1 and i2 . After that,
the result is determined by the following table:
op result
sub i1 − i2
floordiv i1 /i2 (rounded down)
mod i1 mod i2
eq i1 = i2
ne i1 6= i2
lt i1 < i2
gt i1 > i2
le i1 ≤ i2
ge i1 ≥ i2
4
– If op equals or, then we want to return the result of the first Ej
that does not evaluate to 0. In order to do this, we evaluate the
Ej (1 ≤ j ≤ n) from left to right, until we reach the first Ej that
evaluates to a non-zero value.
If we reach the end, because all Ej evaluated to 0, then evaluation
returns 0.
• An Expression of form If (C, Et , Ef ) is evaluated as follows: First C is
evaluated, call the result c. If c is different from zero, then the result of
evaluating If (C, Et , Ef ) will be the result of evaluating Et . Otherwise, it
will be the result of evaluating Ef . If behaves the same as the ternary
operator c ? t1 : t2 in C (or Java).
• An Expression of form str(E1 , . . . , En ) is evaluated as follows: First all
E1 , . . . , En are evaluated in the current Context, call the resulting values
i1 , . . . , in .
– If the program contains a function definition for string str with a
parameter list (S1 , . . . , Sn ) whose length is n, then let Ebody be the
body of this function. Create a new context Cnew that maps each Sj
to ij . After that, the result is obtained by evaluating Ebody in the
new context Cnew .
– If no fitting function definition exists, evaluation fails.
• An Expression of form Fail(str) is ‘evaluated’ by throwing an exception
containing str.
3 Tasks
The task is to implement all subclasses of Expression. All classes must be cre-
ated in package (and corresponding directory) sfl.expression. The file name
must always be equal to the class name. The Oracle documentation for BigIn-
teger can be found here. If a specification below says ‘throws an Exception’
this is always an sfl.program.Exception.
If you want to print the arguments during evaluation, you can call method
show( Program, Context ) defined in Expression. All your implemented
classes can access it, because they are subclasses of Expression.
Class Expression contains dummy .toString( ) and evaluate( Program
prog, Context ctxt ) methods, which throw an Exception when they are
called. Normally, one would make these classes abstract, but then your pro-
gram cannot be compiled when it is unfinished. If you want, you can make the
methods abstract in the end. In order to test your evaluation methods, you
can use sfl.Tester.tester. When you are finished, you should run the tests in
sfl.Tester.main.
5
public IntConst( BigInteger value )
// Must store the value.
public IntConst( String val )
// Converts val to a BigInteger and stores it.
public String toString( )
public BigInteger evaluate( Program prog, Context ctxt )
throws Exception
// returns the stored value.
6
// with commas correctly put between the subExpressions.
// Make sure toString works correctly when there are no,
// or one subExpression.
public BigInteger evaluate(Program prog, Context ctxt)
throws Exception
Make sure that evaluate correctly handles and and or. It must stop
evaluating as soon as it knows the result, see the explanation above. You
can use method asBoolean in Expression to check if a BigInteger is
non-zero.
6. Create the class If. It must have the following methods:
First evaluate the condition. If the condition is not zero, return the eval-
uation of the second argument. If it is zero, return the evaluation of the
third argument. In order to check if a BigInteger is non-zero, use method
asBoolean( BigInteger ), defined in Expression.
7. Create class Function. It must have the following methods:
7
8. Finally, it is convenient to have a type of Expression that always fails.
Implement class Fail. It must have the following methods:
4 Examples
Some examples can be found in sfl.Tester.main. You should add more exam-
ples by yourself. This will help you understanding functional programming.
5 Submission Instructions
Locate the project folder for the assignment on your computer, and zip up
the entire src folder which contains all of your .java files, without changing
any of its contents or directory structure – this should reduce the chances of
accidentally removing a needed file or making unwanted changes. Submit this
zip file to Moodle in the proper submission box before the deadline. Note that
the auto-grader will search for, and extract the .java files in the expression
package that you implemented yourself from src, and recreate the exact original
package structure before testing. Thus, it is critical that you don’t modify the
Java classes and packages that have been provided.