Skip to content

Commit

Permalink
Merge branch 'master' of github.com:mwatts15/Crono
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Watts committed Nov 29, 2012
2 parents 172a3b2 + 2e85fe5 commit 7eefb1c
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 85 deletions.
10 changes: 9 additions & 1 deletion crono/build.xml
Expand Up @@ -16,13 +16,21 @@
</exec> </exec>
<javac srcdir="./src" destdir="./bin"/> <javac srcdir="./src" destdir="./bin"/>
</target> </target>

<target name="debug">
<mkdir dir="bin"/>
<exec dir="src/crono" executable="javacc" >
<arg line="Parser.jj"/>
</exec>
<javac srcdir="./src" destdir="./bin" debug="on" debuglevel="lines,source"/>
</target>


<target name="main" depends="clean,crono" /> <target name="main" depends="clean,crono" />


<target name="packages" depends="crono"> <target name="packages" depends="crono">
<javac srcdir="./packages" destdir="./packages" classpath="./bin" /> <javac srcdir="./packages" destdir="./packages" classpath="./bin" />
</target> </target>

<path id="test.classpath"> <path id="test.classpath">
<pathelement location="./bin/"/> <pathelement location="./bin/"/>
<pathelement location="/usr/share/java/junit.jar"/> <pathelement location="/usr/share/java/junit.jar"/>
Expand Down
11 changes: 11 additions & 0 deletions crono/src/crono/CronoException.java
@@ -0,0 +1,11 @@
package crono;

import crono.type.CronoType;

public class CronoException extends InterpreterException {
public final CronoType thrown;
public CronoException(CronoType thrown) {
super("CronoException: %s", thrown);
this.thrown = thrown;
}
}
44 changes: 44 additions & 0 deletions crono/src/crono/CronoFunction.java
Expand Up @@ -77,6 +77,16 @@ public CronoType run(Visitor v, CronoType[] args) {
public String toString() { public String toString() {
return "cdr"; return "cdr";
} }
}),
SUBST(new Function(new TypeId[]{CronoType.TYPEID}, CronoType.TYPEID, 1,
EvalType.PARTIAL)
{
public CronoType run(Visitor v, CronoType[] args) {
return args[0];
}
public String toString() {
return "subst";
}
}), }),
LIST(new Function(new TypeId[]{CronoType.TYPEID}, Cons.TYPEID, 1, true) LIST(new Function(new TypeId[]{CronoType.TYPEID}, Cons.TYPEID, 1, true)
{ {
Expand Down Expand Up @@ -780,6 +790,40 @@ public String toString(){
return "entail"; return "entail";
} }
}), }),
TRY(new Function(new TypeId[]{Symbol.TYPEID, CronoType.TYPEID,
CronoType.TYPEID}, CronoType.TYPEID, 3,
false, EvalType.NONE)
{
public CronoType run(Visitor v, CronoType[] args) {
Visitor.VisitorState state = v.getState();
CronoType result = Nil.NIL;
try{
result = args[1].accept(v);
}catch(CronoException ce) {
v.setState(state);
v.getEnv().put((Symbol)args[0], ce.thrown);
result = args[2].accept(v);
}catch(Exception ex) {
v.setState(state);
CronoString thrown = CronoString.fromString(ex.getMessage());
v.getEnv().put((Symbol)args[0], thrown);
result = args[2].accept(v);
}
return result;
}
public String toString() {
return "try";
}
}),
RAISE(new Function(new TypeId[]{CronoType.TYPEID}, Nil.TYPEID, 1)
{
public CronoType run(Visitor v, CronoType[] args) {
throw new CronoException(args[0]);
}
public String toString() {
return "raise";
}
}),
; ;


public final Function function; public final Function function;
Expand Down
182 changes: 103 additions & 79 deletions crono/src/crono/Interpreter.java
Expand Up @@ -49,9 +49,6 @@ public InterpreterState() {
} }
} }


private static final String _scope_err = "No object %s in scope";
private static final String _too_many_args =
"Too many arguments to %s: %d/%d recieved";
private static final String _type_scope_err = "No type %s in scope"; private static final String _type_scope_err = "No type %s in scope";
private static final String _type_mismatch = private static final String _type_mismatch =
"Function '%s' expected arguments %s; got %s"; "Function '%s' expected arguments %s; got %s";
Expand All @@ -67,6 +64,10 @@ public InterpreterState() {
protected StringBuilder indent; protected StringBuilder indent;
protected Stack<Environment> envStack; protected Stack<Environment> envStack;


/* Used for keeping track of how deep we are down the nested-lambda rabbit
* hole. */
private int lambdaDepth;

/** /**
* Creates a new Interpreter with default option values. * Creates a new Interpreter with default option values.
*/ */
Expand All @@ -77,6 +78,8 @@ public Interpreter() {
trace(false); trace(false);
printAST(false); printAST(false);


lambdaDepth = 0;

indent = new StringBuilder(); indent = new StringBuilder();
eval = Function.EvalType.FULL; eval = Function.EvalType.FULL;


Expand Down Expand Up @@ -138,8 +141,12 @@ protected void indent() {
*/ */
protected void deindent() { protected void deindent() {
int size = indent.length(); int size = indent.length();
indent.deleteCharAt(size - 1); if(size < 2) {
indent.deleteCharAt(size - 2); indent = new StringBuilder();
}else {
indent.deleteCharAt(size - 1);
indent.deleteCharAt(size - 2);
}
} }


/** /**
Expand Down Expand Up @@ -271,7 +278,6 @@ public CronoType visit(Cons c) {
CronoType value = iter.next().accept(this); CronoType value = iter.next().accept(this);
if(value instanceof Function) { if(value instanceof Function) {
Function fun = ((Function)value); Function fun = ((Function)value);

EvalType reserve = eval; EvalType reserve = eval;


eval = fun.eval; eval = fun.eval;
Expand All @@ -288,8 +294,11 @@ public CronoType visit(Cons c) {
int nargs = fun.arity; int nargs = fun.arity;
if(arglen < nargs) { if(arglen < nargs) {
if(arglen == 0) { if(arglen == 0) {
/* Special case -- we don't have to do anything to the if(fun instanceof LambdaFunction) {
* function to return it properly. */ /* We technically have to substitute these */
fun = substitute((LambdaFunction)fun,new CronoType[0]);
}

deindent(); deindent();
traceResult(fun); traceResult(fun);
printEnvironment(); printEnvironment();
Expand All @@ -298,49 +307,10 @@ public CronoType visit(Cons c) {


/* Curry it */ /* Curry it */
if(fun instanceof LambdaFunction) { if(fun instanceof LambdaFunction) {
/* Lambdas are easy - just use the substitute method */
LambdaFunction lfun = ((LambdaFunction)fun); LambdaFunction lfun = ((LambdaFunction)fun);
Environment env = getEnv(); CronoType[] larr = new CronoType[arglen];
if(!dynamic) { LambdaFunction clfun = substitute(lfun,args.toArray(larr));
/* Use the lambda's stored environment */
env = lfun.environment;
}
/* We want to preserve the current environment */
env = new Environment(false);

/* Put known values into the new environment */
for(int i = 0; i < arglen; ++i) {
env.put(lfun.arglist[i], args.get(i));
}
/* Create new argument list and remove remaining args from
* the new environment */
List<Symbol> largs = new ArrayList<Symbol>();
for(int i = arglen; i < lfun.arglist.length; ++i) {
largs.add(lfun.arglist[i]);
env.remove(lfun.arglist[i]);
}

/* Evaluate the body as much as possible */
reserve = eval;

CronoType[] lbody = new CronoType[lfun.body.length];
optionsOff(); /*< Extra indent for clarity */
{
eval = EvalType.PARTIAL;
pushEnv(env);
for(int i = 0; i < lfun.body.length; ++i) {
lbody[i] = lfun.body[i].accept(this);
}
eval = reserve;
popEnv();
}
resetOptions(); /*< Set options back to what they were */

/* Return the new, partially evaluated lambda */
Symbol[] arglist = new Symbol[largs.size()];

LambdaFunction clfun;
clfun = new LambdaFunction(largs.toArray(arglist), lbody,
lfun.environment);
deindent(); deindent();
traceResult(clfun); traceResult(clfun);
printEnvironment(); printEnvironment();
Expand Down Expand Up @@ -375,41 +345,31 @@ public CronoType visit(Cons c) {
} }
if(arglen > nargs && !fun.variadic) { if(arglen > nargs && !fun.variadic) {
eval = reserve; eval = reserve;
except(new InterpreterException(_too_many_args, fun, arglen, except(new TooManyArgsException(fun, arglen, nargs));
nargs));
} }


/* Full evaluation */ /* Full evaluation */
if(eval == EvalType.FULL) { if(eval == EvalType.FULL) {
if(fun instanceof LambdaFunction && dynamic) { CronoType[] argarray =
/* We have to trick the lambda function if we want dynamic args.toArray(new CronoType[args.size()]);
* scoping. I hate making so many objects left and right,
* but this is the easiest way to do what I want here. */ if(fun instanceof LambdaFunction) {
LambdaFunction lfun = ((LambdaFunction)fun); LambdaFunction lfun = (LambdaFunction)fun;
lfun = new LambdaFunction(lfun.arglist, lfun.body, if(dynamic) {
getEnv()); lfun = new LambdaFunction(lfun.arglist, lfun.body,
CronoType[] argarray = new CronoType[args.size()]; getEnv());

}
optionsOff(); fun = substitute(lfun, argarray);
CronoType lresult = lfun.run(this, args.toArray(argarray));
resetOptions();

deindent();
traceResult(lresult);
printEnvironment();

return lresult;
} }


CronoType[] argarray = new CronoType[args.size()];
argarray = args.toArray(argarray);
TypeId[] types = new TypeId[args.size()]; TypeId[] types = new TypeId[args.size()];
for(int i = 0; i < types.length; ++i) { for(int i = 0; i < types.length; ++i) {
types[i] = argarray[i].typeId(); types[i] = argarray[i].typeId();
} }
int check = Math.min(argarray.length,fun.args.length); int check = 0;
for(int i = 0; i < check; ++i) { for(int i = 0; i < fun.args.length; ++i) {
if(!(fun.args[i].isType(argarray[i]))) { check = Math.min(i, argarray.length);
if(!(fun.args[i].isType(argarray[check]))) {
String argstr = Arrays.toString(types); String argstr = Arrays.toString(types);
String expected = Arrays.toString(fun.args); String expected = Arrays.toString(fun.args);
except(new InterpreterException(_type_mismatch, fun, except(new InterpreterException(_type_mismatch, fun,
Expand All @@ -418,8 +378,12 @@ public CronoType visit(Cons c) {
} }


optionsOff(); optionsOff();
CronoType fresult; CronoType fresult = null;
fresult = ((Function)value).run(this, args.toArray(argarray)); try {
fresult = fun.run(this, args.toArray(argarray));
}catch(RuntimeException re) {
except(re);
}
resetOptions(); resetOptions();


deindent(); deindent();
Expand Down Expand Up @@ -479,7 +443,7 @@ public CronoType visit(Atom a) {
t = getEnv().get((Symbol)a); t = getEnv().get((Symbol)a);
if(t == null) { if(t == null) {
if(eval == EvalType.FULL) { if(eval == EvalType.FULL) {
except(new InterpreterException(_scope_err, a.repr())); except(new SymbolScopeException((Symbol)a));
} }
t = a; t = a;
} }
Expand All @@ -491,7 +455,7 @@ public CronoType visit(Atom a) {
t = getEnv().getType((CronoTypeId)t); t = getEnv().getType((CronoTypeId)t);
if(t == null) { if(t == null) {
if(eval == EvalType.FULL) { if(eval == EvalType.FULL) {
except(new InterpreterException(_type_scope_err, a)); except(new TypeScopeException(((CronoTypeId)res).type));
} }
t = res; /*< Revert to symbol resolution */ t = res; /*< Revert to symbol resolution */
} }
Expand Down Expand Up @@ -544,6 +508,66 @@ public void setState(Visitor.VisitorState state) {
} }
} }


/* Cut down on code duplication. */
private LambdaFunction substitute(LambdaFunction lfun, CronoType[] args) {
EvalType reserve = eval;
LambdaFunction ret = null;

/* Everything we do here should be undone after substutition. */
lambdaDepth++;
if(lambdaDepth <= 1) {
optionsOff();
eval = EvalType.PARTIAL;
pushEnv(new Environment(false));
ret = doSubst(lfun, args);
popEnv();
eval = reserve;
resetOptions();
}else {
/* If this is not the first time, we need to create a copy of the
* environment. This could be optimized by using some form of
* transactional database instead of a map for the Environment.
*/
pushEnv(new Environment(getEnv()));
ret = doSubst(lfun, args);
popEnv();
}
lambdaDepth--;
return ret;
}
private LambdaFunction doSubst(LambdaFunction lfun, CronoType[] args) {
/* Initialization for substitution */
Environment env = getEnv();
CronoType[] body = new CronoType[lfun.body.length];
int remain = lfun.arglist.length - args.length;
if(remain < 0) {
throw new TooManyArgsException(lfun, lfun.arglist.length,
args.length);
}
Symbol[] largs = new Symbol[remain];

/* Put variables we have in the substitution env */
for(int i = 0; i < args.length; ++i) {
env.put(lfun.arglist[i], args[i]);
}

/* Remove symbols that this lambda defines but doesn't have. */
Symbol missing = null;
for(int i = 0; i < largs.length; ++i) {
missing = lfun.arglist[args.length + i];
largs[i] = missing;
env.remove(missing);
}

/* Loop over the body and have the interpreter visit them */
for(int i = 0; i < body.length; ++i) {
/* This may call substitute */
body[i] = lfun.body[i].accept(this);
}

return new LambdaFunction(largs, body, lfun.environment);
}

public Environment getEnv() { public Environment getEnv() {
return envStack.peek(); return envStack.peek();
} }
Expand Down
9 changes: 9 additions & 0 deletions crono/src/crono/SymbolScopeException.java
@@ -0,0 +1,9 @@
package crono;

import crono.type.Symbol;

public class SymbolScopeException extends InterpreterException {
public SymbolScopeException(Symbol sym) {
super("No object %s in scope", sym);
}
}

0 comments on commit 7eefb1c

Please sign in to comment.