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))); } }