Makevisitors: Pass 4
Generate the Java concrete class
es
For each complex Haskell data type, we need to generate a set of Java concrete classes. Each of the concrete classes should:
- Extend the corresponding
abstract
class. - Declare any fields of the appropriate names and types.
- Define a constructor that initializes the fields.
- Implement the
accept
method.
Continuing with our running example:
-- concrete Haskell declaration
data Expr = Value {value :: Integer}
| BinOp {operator :: Op, leftOperand :: Expr, rightOperand :: Expr}
-- abstract syntax (an instance of TyDefs)
[Datatype "Expr" [ Record "Value" [ ("value", Base "Integer") ]
, Record "BinOp" [ ("operator", Base "Op")
, ("leftOperand", Base "Expr")
, ("rightOperand", Base "Expr") ]]
Here is the translation to a concrete class for Value
:
// Java declaration
public class Value extends Expr {
public Long value_;
public Value(Long value) {
value_ = value;
}
public void accept(ExprVisitor visitor) {
visitor.visitValue(this);
}
}
-- abstract syntax (an instance of JDeclaration)
ClassDef [Public] "Value" (Just "Expr") []
[ FieldDecl [Public] "value_" (Typename "Long")
, ConstructorDef "Value" [(Typename "Long","value")]
(BlockBody [Assign (Var "value_") (LValueExpr (Var "value"))])
, MethodDef [Public] (Typename "void") "accept" [(Typename "ExprVisitor","visitor")]
(BlockBody [ExprStmt (MethodCall (Var "visitor") "visitValue" [LValueExpr (Var "this")])]) ]
Your task: In Translate.hs
, complete the implementation of the fourth pass.
We recommend that you write some helper functions!
After you complete this pass, you should have a working makevisitors
program.
All the generated Java code should compile.
Suggestions.
-
This task generates the most code, and it is the one with the least amount of work done for you, so it will probably take you some time to complete. Break it down into smaller steps (with corresponding helper functions) and test each one along the way. For example:
- Generate the class definition, with an empty body.
- Generate the field declarations.
- Generate the constructor with an empty body.
- Fill in the constructor body.
- Generate the
accept
method with an empty body. - Fill in the
accept
body.
-
Remember that you do not need to handle the case when a Haskell constructor declares arguments but doesn’t use records. In other words, you can assume that all of the
DtCase
s will be of the form(Record name field)
or(Plain name [])
.
Testing.
- Run
make test
, which will generate code for lots of different examples. Check that all the code is translated as you expected. You can compare your code against the code generated by the reference implementation. All the files should match, and all the code should compile.