436 lines
10 KiB
Java
436 lines
10 KiB
Java
// -*- java -*-
|
|
|
|
/*
|
|
* General Notes:
|
|
*
|
|
* 1) we need some sort of error reporting mechanism. Full exceptions
|
|
* would be nice, but even a MzScheme-like error primitive would be
|
|
* sufficient for now.
|
|
* 2) We desperately need some sort of polymorphism -- even Java 1.4
|
|
* style would be an improvement!
|
|
* 3) What did we decide w.r.t typing _this_ again?
|
|
*/
|
|
|
|
/*
|
|
* Assumed built-in types, operations
|
|
* OCaml-style extensional equality
|
|
* null? (or, more generally, intensional equality)
|
|
* String (changed to str)
|
|
* int
|
|
* boolean (changed to bool)
|
|
*/
|
|
|
|
/*
|
|
**********************************************************************
|
|
* Infrastructure: sets, lists, etc.
|
|
**********************************************************************/
|
|
|
|
type StringList
|
|
{
|
|
bool contains(str s);
|
|
|
|
// Returns copy of this list with first occurrence of s removed.
|
|
// If no s, returns copy of this list.
|
|
StringList remove(str s);
|
|
|
|
// fold that produces a set.
|
|
StringSet setFold(StringSet initial, StringSetOp op);
|
|
|
|
bool hasDuplicates();
|
|
}
|
|
|
|
type StringSetOp
|
|
{
|
|
// invoke this closure.
|
|
StringSet run(StringSet accum, str s);
|
|
}
|
|
|
|
class EmptyStringList() : StringList impl StringList
|
|
{
|
|
bool contains(str s) { false; }
|
|
StringList remove(str s) { this : StringList; }
|
|
|
|
StringSet setFold(StringSet initial, StringSetOp op) { initial; }
|
|
|
|
bool hasDuplicates() { false; }
|
|
|
|
export StringList : contains, setFold, remove, hasDuplicates;
|
|
}
|
|
|
|
class ConsStringList(StringList rest) : StringList impl StringList
|
|
{
|
|
init str first;
|
|
|
|
StringList rest =
|
|
if rest == null {
|
|
error("ConsStringList.rest null");
|
|
} else { rest; };
|
|
|
|
bool contains(str s)
|
|
{
|
|
(s == first) || rest.contains(s);
|
|
}
|
|
|
|
StringList remove(str s)
|
|
{
|
|
if s == first { rest; }
|
|
else { new ConsStringList : StringList
|
|
(first = first,
|
|
rest = rest.remove(s)); };
|
|
}
|
|
|
|
StringSet setFold(StringSet initial, StringSetOp op)
|
|
{
|
|
rest.setFold(op.run(initial, first), op);
|
|
}
|
|
|
|
bool hasDuplicates()
|
|
{
|
|
rest.contains(first) || rest.hasDuplicates();
|
|
}
|
|
|
|
export StringList : contains, setFold, remove, hasDuplicates;
|
|
}
|
|
|
|
class UnionOp() : StringSetOp impl StringSetOp
|
|
{
|
|
StringSet run(StringSet accum, str s)
|
|
{
|
|
accum.singleUnion(s);
|
|
}
|
|
|
|
export StringSetOp : run;
|
|
}
|
|
|
|
type StringSet
|
|
{
|
|
StringSet singleUnion(str elt);
|
|
StringSet union(StringSet rhs);
|
|
StringSet subElt(str elt);
|
|
}
|
|
|
|
class StringSetCls() : StringSet impl StringSet
|
|
{
|
|
init StringList contents = new EmptyStringList : StringList();
|
|
// this showed up a bug in the parser where I wasn't adding
|
|
// the default expressions to things. This caused problems
|
|
// when relying on them (i.e. not giving a contents init arg).
|
|
// Fixed.
|
|
|
|
// How should this get translated? You have a default value
|
|
// for contents so it can't go into the init args in the header,
|
|
// but you want an additional check later (hasDuplicates()).
|
|
StringList contents =
|
|
if contents.hasDuplicates() {
|
|
error("StringList.contents must be set");
|
|
} else { contents; };
|
|
|
|
StringSet singleUnion(str elt)
|
|
{
|
|
if contents.contains(elt) { this : StringSet; }
|
|
else {
|
|
new StringSetCls : StringSet
|
|
(contents = new ConsStringList : StringList(first = elt,
|
|
rest = contents));
|
|
};
|
|
}
|
|
|
|
StringSet union(StringSet rhs)
|
|
{
|
|
contents.setFold(rhs, new UnionOp : StringSetOp());
|
|
}
|
|
|
|
StringSet subElt(str elt)
|
|
{
|
|
new StringSetCls : StringSet(contents = contents.remove(elt));
|
|
}
|
|
|
|
export StringSet : singleUnion, union, subElt;
|
|
// was export StringSetCls, typechecker caught this.
|
|
}
|
|
|
|
/*
|
|
**********************************************************************
|
|
* Values
|
|
**********************************************************************/
|
|
|
|
type Value
|
|
{
|
|
Value apply(Value arg);
|
|
}
|
|
|
|
class IntValue() : Value impl Value // was class Constant, typechecker caught this
|
|
// line 359 was the use of IntValue
|
|
{
|
|
init int n;
|
|
|
|
Value apply(Value arg) { error("cannot apply a constant"); }
|
|
|
|
export Value : apply;
|
|
}
|
|
|
|
class Closure(Ast body, Env rho) : Value impl Value
|
|
{
|
|
init str argName;
|
|
Ast body =
|
|
if body == null {
|
|
error("Closure.body null");
|
|
} else { body; };
|
|
Env rho =
|
|
if rho == null {
|
|
error("Closure.rho null");
|
|
} else { rho; }; // was else body, Typechecker caught this
|
|
|
|
Value apply(Value arg)
|
|
{
|
|
body.getValue(rho.extend(argName, arg));
|
|
// originally thought this was a bug in the interpreter,
|
|
// but it was a bug in the typechecker (did supertype
|
|
// checking on method arguments, not subtype checking). Fixed.
|
|
}
|
|
|
|
export Value : apply; // no export statement, typechecker caught this.
|
|
}
|
|
|
|
/*
|
|
**********************************************************************
|
|
* Types
|
|
**********************************************************************/
|
|
|
|
type Type
|
|
{
|
|
Type getRange(Type domain);
|
|
}
|
|
|
|
class IntType() : Type impl Type
|
|
{
|
|
Type getRange(Type domain) { error("IntType.getRange"); }
|
|
|
|
export Type : getRange;
|
|
}
|
|
|
|
class ArrowType(Type dom, Type rng) : Type impl Type
|
|
{
|
|
Type dom =
|
|
if dom == null {
|
|
error("ArrowType.dom null");
|
|
} else { dom; };
|
|
Type rng =
|
|
if rng == null {
|
|
error("ArrowType.rng null");
|
|
} else { rng; };
|
|
|
|
Type getRange(Type domain)
|
|
{
|
|
// extensional equality
|
|
if domain ==== dom { rng; }
|
|
else { error("arrowType.getRange: domain mismatch"); };
|
|
}
|
|
|
|
export Type : getRange;
|
|
}
|
|
|
|
/*
|
|
**********************************************************************
|
|
* Environments
|
|
**********************************************************************/
|
|
|
|
type Env
|
|
{
|
|
Env extend(str var, Any t);
|
|
Any lookup(str var);
|
|
}
|
|
|
|
class EmptyEnv() : Env impl Env
|
|
{
|
|
Env extend(str var, Any t)
|
|
{
|
|
new RibEnv : Env(id = var, bdg = t, rest = this : Env);
|
|
}
|
|
|
|
Any lookup(str var)
|
|
{
|
|
error("unbound id");
|
|
}
|
|
|
|
export Env : extend, lookup;
|
|
}
|
|
|
|
class RibEnv(Any bdg, Env rest) : Env impl Env
|
|
{
|
|
init str id;
|
|
Any bdg =
|
|
if bdg == null {
|
|
error("RibEnv.bdg null");
|
|
} else { bdg; };
|
|
Env rest =
|
|
if rest == null {
|
|
error("RibEnv.rest null");
|
|
} else { rest; };
|
|
|
|
Env extend(str var, Any t)
|
|
{
|
|
new RibEnv : Env(id = var, bdg = t, rest = this : Env);
|
|
}
|
|
|
|
Any lookup(str var)
|
|
{
|
|
if var == id { bdg; } else { rest.lookup(var); };
|
|
}
|
|
|
|
export Env : extend, lookup;
|
|
// was export TypeEnv, should be Env, typechecker caught this.
|
|
}
|
|
/*
|
|
**********************************************************************
|
|
* ASTS
|
|
**********************************************************************/
|
|
|
|
type Ast
|
|
{
|
|
StringSet vars();
|
|
StringSet freeVars();
|
|
|
|
Type getType(Env gamma);
|
|
Value getValue(Env rho);
|
|
}
|
|
|
|
class Constant() : Ast impl Ast
|
|
{
|
|
init int n;
|
|
|
|
StringSet vars()
|
|
{
|
|
new StringSetCls : StringSet();
|
|
}
|
|
|
|
StringSet fv()
|
|
{
|
|
vars();
|
|
}
|
|
|
|
Type getType(Env gamma)
|
|
{
|
|
new IntType : Type();
|
|
}
|
|
|
|
Value getValue(Env rho)
|
|
{
|
|
new IntValue : Value(n = n);
|
|
}
|
|
|
|
export Ast : vars, fv as freeVars, getType, getValue;
|
|
}
|
|
|
|
class Variable() : Ast impl Ast
|
|
{
|
|
init str name;
|
|
|
|
StringSet vars()
|
|
{
|
|
StringSet result = new StringSetCls : StringSet();
|
|
result.singleUnion(name);
|
|
}
|
|
|
|
StringSet fv() { vars(); }
|
|
|
|
Type getType(Env gamma)
|
|
{
|
|
gamma.lookup(name) : Type;
|
|
}
|
|
|
|
Value getValue(Env rho)
|
|
{
|
|
rho.lookup(name) : Value;
|
|
}
|
|
|
|
export Ast : vars, fv as freeVars, getType, getValue;
|
|
// last part was erroneously typed "getName" instead of "getValue",
|
|
// caught by typechecker.
|
|
}
|
|
|
|
class Lambda(Ast body, Type argType) : Ast impl Ast
|
|
{
|
|
init str argName;
|
|
|
|
Ast body =
|
|
if body == null {
|
|
error("Lambda.body null");
|
|
} else { body; };
|
|
Type argType =
|
|
if argType == null {
|
|
error("Lambda.argType null");
|
|
} else { argType; };
|
|
|
|
StringSet vars()
|
|
{
|
|
body.vars();
|
|
}
|
|
|
|
StringSet freeVars()
|
|
{
|
|
body.freeVars().subElt(argName);
|
|
// was arg (init field) instead of argName (field).
|
|
// typechecker caught this.
|
|
}
|
|
|
|
Type getType(Env gamma)
|
|
{
|
|
body.getType(gamma.extend(argName, argType));
|
|
// originally thought this was a bug in the interpreter,
|
|
// but it was a bug in the typechecker (did supertype
|
|
// checking on method arguments, not subtype checking). Fixed.
|
|
}
|
|
|
|
Value getValue(Env rho)
|
|
{
|
|
return new Closure : Value(argName = argName, body = body, rho = rho);
|
|
}
|
|
|
|
export Ast : vars, freeVars, getType, getValue;
|
|
}
|
|
|
|
class Application(Ast rator, Ast rand) : Ast impl Ast
|
|
{
|
|
Ast rator =
|
|
if rator == null {
|
|
error("Application.rator null");
|
|
} else { rator; };
|
|
Ast rand =
|
|
if rand == null {
|
|
error("Application.rand null");
|
|
} else { rand; };
|
|
|
|
StringSet vars()
|
|
{
|
|
StringSet ratorVars = rator.vars();
|
|
ratorVars.union(rand.vars());
|
|
}
|
|
|
|
StringSet fv()
|
|
{
|
|
StringSet ratorFv = rator.freeVars();
|
|
// rator.freeVars (public name) was rator.fv (private name).
|
|
// typechecker caught this (happened below also with rand)
|
|
ratorFv.union(rand.freeVars());
|
|
}
|
|
|
|
Type getType(Env gamma)
|
|
{
|
|
Type ratorType = rator.getType(gamma);
|
|
ratorType.getRange(rand.getType(gamma));
|
|
}
|
|
|
|
Value getValue(Env rho)
|
|
{
|
|
Value ratorVal = rator.getValue(rho);
|
|
ratorVal.apply(rand.getValue(rho));
|
|
// ratorVal.apply was rator.apply, which gave method not found error
|
|
// typechecker caught this.
|
|
// also, rand.getValue was rand.value. Same error.
|
|
// typechecker caught this.
|
|
}
|
|
|
|
export Ast : vars, fv as freeVars, getType, getValue;
|
|
}
|