package jscheme;
/** Environments allow you to look up the value of a variable, given
* its name. Keep a list of variables and values, and a pointer to
* the parent environment. If a variable list ends in a symbol rather
* than null, it means that symbol is bound to the remainder of the
* values list.
* @author Peter Norvig, peter@norvig.com http://www.norvig.com
* Copyright 1998 Peter Norvig, see http://www.norvig.com/license.html */
public class Environment extends SchemeUtils {
public Object vars;
public Object vals;
public Environment parent;
/** A constructor to extend an environment with var/val pairs. */
public Environment(Object vars, Object vals, Environment parent) {
this.vars = vars;
this.vals = vals;
this.parent = parent;
if (!numberArgsOK(vars, vals))
warn("wrong number of arguments: expected " + vars + " got " + vals);
}
/** Construct an empty environment: no bindings. **/
public Environment() {
}
/** Find the value of a symbol, in this environment or a parent. */
public Object lookup(String symbol) {
Object varList = vars, valList = vals;
// See if the symbol is bound locally
while (varList != null) {
if (first(varList) == symbol) {
return first(valList);
} else if (varList == symbol) {
return valList;
} else {
varList = rest(varList);
valList = rest(valList);
}
}
// If not, try to look for the parent
if (parent != null)
return parent.lookup(symbol);
else
return error("Unbound variable: " + symbol);
}
/** Add a new variable,value pair to this environment. */
public Object define(Object var, Object val) {
vars = cons(var, vars);
vals = cons(val, vals);
if (val instanceof Procedure
&& ((Procedure) val).name.equals("anonymous procedure"))
((Procedure) val).name = var.toString();
return var;
}
/** Set the value of an existing variable **/
public Object set(Object var, Object val) {
if (!(var instanceof String))
return error("Attempt to set a non-symbol: " + stringify(var));
;
String symbol = (String) var;
Object varList = vars, valList = vals;
// See if the symbol is bound locally
while (varList != null) {
if (first(varList) == symbol) {
return setFirst(valList, val);
} else if (rest(varList) == symbol) {
return setRest(valList, val);
} else {
varList = rest(varList);
valList = rest(valList);
}
}
// If not, try to look for the parent
if (parent != null)
return parent.set(symbol, val);
else
return error("Unbound variable: " + symbol);
}
public Environment defPrim(String name, int id, int minArgs) {
define(name, new Primitive(id, minArgs, minArgs));
return this;
}
public Environment defPrim(String name, int id, int minArgs, int maxArgs) {
define(name, new Primitive(id, minArgs, maxArgs));
return this;
}
/** See if there is an appropriate number of vals for these vars. **/
boolean numberArgsOK(Object vars, Object vals) {
return ((vars == null && vals == null) || (vars instanceof String) || (vars instanceof Pair
&& vals instanceof Pair && numberArgsOK(((Pair) vars).rest,
((Pair) vals).rest)));
}
}