diff --git a/project.properties b/project.properties
index ff41943..5b7271b 100755
--- a/project.properties
+++ b/project.properties
@@ -1,13 +1,13 @@
-#Wed Nov 25 16:01:45 CST 2009
-build.dir=build
-src.dir=src
-grammar.output.dir=${src.dir}/edu/utexas/cs345/jdblisp/parser
-build.jar=${build.dir}/JCLisp-${application.version}.${build.number}.jar
-build.number=27
-dist.dir=dist
-javacc.home=${lib.dir}/javacc
-lib.dir=lib
-dist.jar=${dist.dir}/JCLisp-${application.version}.jar
-build.classes.dir=${build.dir}/classes
-grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj
-application.version=0.1.0
+#Mon Nov 30 20:37:52 CST 2009
+build.dir=build
+src.dir=src
+grammar.output.dir=${src.dir}/edu/utexas/cs345/jdblisp/parser
+build.jar=${build.dir}/JCLisp-${application.version}.${build.number}.jar
+build.number=46
+dist.dir=dist
+javacc.home=${lib.dir}/javacc
+dist.jar=${dist.dir}/JCLisp-${application.version}.jar
+lib.dir=lib
+grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj
+build.classes.dir=${build.dir}/classes
+application.version=0.1.0
diff --git a/src/edu/utexas/cs345/jdblisp/ClosureSymbolTable.java b/src/edu/utexas/cs345/jdblisp/ClosureSymbolTable.java
new file mode 100644
index 0000000..ab4f100
--- /dev/null
+++ b/src/edu/utexas/cs345/jdblisp/ClosureSymbolTable.java
@@ -0,0 +1,58 @@
+package edu.utexas.cs345.jdblisp;
+
+import java.util.Map;
+
+/**
+ * ClosureSymbolTable
+ * @author Jonathan Bernard (jdbernard@gmail.com)
+ */
+public class ClosureSymbolTable extends SymbolTable {
+
+ private SymbolTable lexicalScope;
+
+ public ClosureSymbolTable(SymbolTable lexicalScope,
+ SymbolTable closedScope) {
+ super(closedScope);
+ this.lexicalScope = lexicalScope;
+ }
+
+ /**
+ *
+ */
+ @Override
+ public FormEntry lookupFunction(Symbol s) throws LispException {
+
+ // lookup function in the closed binding first
+ FormEntry fe = super.lookupFunction(s);
+
+ // lookup in the lexical scope if it is not found in the closure
+ if (fe == null && lexicalScope != null)
+ fe = lexicalScope.lookupFunction(s);
+
+ // return the result (may be null if not found anywhere)
+ return fe;
+ }
+
+ /**
+ * Lookup a variable bound to the given
+ * {@link edu.utexas.cs345.jdblisp.Symbol}. The lookup is performed first
+ * in the closed scope and then the lexical scope if the variable is not
+ * found in the closed scope.
+ * @param s The symbol to lookup.
+ * @return The variable bound to the symbol, or null if the symbol
+ * is unbound.
+ */
+ @Override
+ public VariableEntry lookupVariable(Symbol s) throws LispException {
+
+ // lookup variable in the closed binding first
+ VariableEntry ve = super.lookupVariable(s);
+
+ // lookup in the lexical scope if it is not found in the closure
+ if (ve == null && lexicalScope != null)
+ ve = lexicalScope.lookupVariable(s);
+
+ // return the result (may be null if not found anywhere)
+ return ve;
+ }
+}
diff --git a/src/edu/utexas/cs345/jdblisp/FormEntry.java b/src/edu/utexas/cs345/jdblisp/FormEntry.java
index bfaacca..7457abd 100644
--- a/src/edu/utexas/cs345/jdblisp/FormEntry.java
+++ b/src/edu/utexas/cs345/jdblisp/FormEntry.java
@@ -4,13 +4,13 @@ package edu.utexas.cs345.jdblisp;
* FormEntry
* @author Jonathan Bernard(jdbernard@gmail.com)
*/
-public abstract class FormEntry implements SExp {
+public abstract class FormEntry implements SExp, SymbolTableEntry {
- public final Symbol name;
+ public final Symbol symbol;
public HelpTopic helpinfo;
- public FormEntry(Symbol name, HelpTopic helpinfo) {
- this.name = name;
+ public FormEntry(Symbol symbol, HelpTopic helpinfo) {
+ this.symbol = symbol;
this.helpinfo = helpinfo;
}
@@ -19,4 +19,5 @@ public abstract class FormEntry implements SExp {
public SExp eval(SymbolTable symbolTable) { return this; }
+ public Symbol symbol() { return symbol; }
}
diff --git a/src/edu/utexas/cs345/jdblisp/FunctionEntry.java b/src/edu/utexas/cs345/jdblisp/FunctionEntry.java
index 816e674..6412518 100755
--- a/src/edu/utexas/cs345/jdblisp/FunctionEntry.java
+++ b/src/edu/utexas/cs345/jdblisp/FunctionEntry.java
@@ -13,26 +13,26 @@ public class FunctionEntry extends FormEntry {
//private Logger traceLog = Logger.getLogger(getClass());
- public FunctionEntry(Symbol name, Symbol[] parameters, SExp body) {
- super(name, null);
+ public FunctionEntry(Symbol symbol, Symbol[] parameters, SExp body) {
+ super(symbol, null);
// build invocation help string
- String invokation = "(" + name.name;
+ String invokation = "(" + symbol.name;
for (Symbol param : parameters) invokation += " <" + param.name + ">";
invokation += ")";
String bodyInfo = "Function body: " + body.toString();
// build help topic
- FormHelpTopic helpinfo = new FormHelpTopic(name.name, null, invokation, bodyInfo);
+ FormHelpTopic helpinfo = new FormHelpTopic(symbol.name, null, invokation, bodyInfo);
this.parameters = parameters;
this.body = body;
this.helpinfo = helpinfo;
}
- public FunctionEntry(Symbol name, Symbol[] parameters, SExp body,
+ public FunctionEntry(Symbol symbol, Symbol[] parameters, SExp body,
HelpTopic helpinfo) {
- super(name, helpinfo);
+ super(symbol, helpinfo);
this.parameters = parameters;
this.body = body;
}
@@ -42,10 +42,10 @@ public class FunctionEntry extends FormEntry {
public void enableTrace(boolean enable) { this.traceEnabled = enable; }
public String display(String offset) {
- return offset + "Function: " + name.toString();
+ return offset + "Function: " + symbol.toString();
}
- public String toString() { return ""; }
+ public String toString() { return ""; }
public SExp call(SymbolTable symbolTable, Seq arguments) throws LispException {
@@ -53,7 +53,7 @@ public class FunctionEntry extends FormEntry {
SExp evaluatedArg, retVal;
if (traceEnabled)
- traceString = "(" + name.name;
+ traceString = "(" + symbol.name;
// bind arguments to parameters
SymbolTable localScope = new SymbolTable(symbolTable);
@@ -63,11 +63,12 @@ public class FunctionEntry extends FormEntry {
// too few arguments
if (arguments == null)
throw new InvalidArgumentQuantityException(
- parameters.length, i);
+ toString(), parameters.length, i);
// bind one arg to param
evaluatedArg = arguments.car.eval(symbolTable);
- localScope.bind(parameters[i], new VariableEntry(evaluatedArg));
+ localScope.bind(parameters[i], new VariableEntry(parameters[i],
+ evaluatedArg));
if (traceEnabled) traceString += " " + evaluatedArg.toString();
@@ -77,8 +78,8 @@ public class FunctionEntry extends FormEntry {
// too many arguments
if (arguments != null)
- throw new InvalidArgumentQuantityException(parameters.length,
- (i + arguments.length()));
+ throw new InvalidArgumentQuantityException(
+ toString(),parameters.length, (i + arguments.length()));
if (traceEnabled) traceString += ")";
if (traceEnabled) System.out.println(traceString);
@@ -87,7 +88,7 @@ public class FunctionEntry extends FormEntry {
retVal = body.eval(localScope);
if (traceEnabled)
- traceString = name.name + " returned " + retVal.toString();
+ traceString = symbol.name + " returned " + retVal.toString();
if (traceEnabled) System.out.println(traceString);
return retVal;
diff --git a/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java b/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java
index c7fb241..22c6c01 100755
--- a/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java
+++ b/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java
@@ -7,16 +7,16 @@ package edu.utexas.cs345.jdblisp;
*/
public class InvalidArgumentQuantityException extends LispException {
- public InvalidArgumentQuantityException(int expected, int actual) {
- super ("Invalid number of arguments: " + actual
+ public InvalidArgumentQuantityException(String invokeName, int expected, int actual) {
+ super ("Invalid number of arguments to " + invokeName + ": " + actual
+ " (expected " + expected + ").");
}
- public InvalidArgumentQuantityException(int actual) {
- super ("Invalid number of arguments: " + actual);
+ public InvalidArgumentQuantityException(String invokeName, int expected) {
+ super ("Invalid number of arguments to " + invokeName + ": expected " + expected);
}
- public InvalidArgumentQuantityException(String message) {
- super ("Invalid number of arguments: " + message);
+ public InvalidArgumentQuantityException(String invokeName, String message) {
+ super ("Invalid number of arguments to " + invokeName + ": " + message);
}
}
diff --git a/src/edu/utexas/cs345/jdblisp/LISPRuntime.java b/src/edu/utexas/cs345/jdblisp/LISPRuntime.java
index bc2a9f8..cb8c0a5 100755
--- a/src/edu/utexas/cs345/jdblisp/LISPRuntime.java
+++ b/src/edu/utexas/cs345/jdblisp/LISPRuntime.java
@@ -122,9 +122,11 @@ public class LISPRuntime {
void signalStop() { stop = true; }
private static SymbolTable defineGlobalConstants() {
+ Symbol T = new Symbol("T");
+ Symbol NIL = new Symbol("NIL");
SymbolTable constantsTable = new SymbolTable();
- constantsTable.bind(new Symbol("T"), new VariableEntry(SExp.T, true));
- constantsTable.bind(new Symbol("NIL"), new VariableEntry(SExp.NIL, true));
+ constantsTable.bind(T, new VariableEntry(T, SExp.T, true));
+ constantsTable.bind(NIL, new VariableEntry(NIL, SExp.NIL, true));
return constantsTable;
}
diff --git a/src/edu/utexas/cs345/jdblisp/Lambda.java b/src/edu/utexas/cs345/jdblisp/Lambda.java
index 3bdb4fa..a552d86 100644
--- a/src/edu/utexas/cs345/jdblisp/Lambda.java
+++ b/src/edu/utexas/cs345/jdblisp/Lambda.java
@@ -8,14 +8,14 @@ public class Lambda extends FunctionEntry {
private SymbolTable closure;
public Lambda(Symbol[] parameters, SExp body, SymbolTable closure) {
- super(new Symbol(""), parameters, body);
+ super(new Symbol("L()"), parameters, body);
this.closure = closure;
}
public Lambda(Symbol[] parameters, SExp body, SymbolTable closure,
HelpTopic helpinfo) {
- super(new Symbol(""), parameters, body, helpinfo);
+ super(new Symbol("L()"), parameters, body, helpinfo);
this.closure = closure;
}
@@ -31,6 +31,7 @@ public class Lambda extends FunctionEntry {
return sb.toString();
}
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(" {
return new Num(n.abs());
}
+ public Num remainder(Num divisor) {
+ return new Num(n.remainder(divisor.n));
+ }
+
@Override
public int compareTo(Num that) {
return this.n.compareTo(that.n);
diff --git a/src/edu/utexas/cs345/jdblisp/Parser.jj b/src/edu/utexas/cs345/jdblisp/Parser.jj
index 75aa6e8..3dc193f 100755
--- a/src/edu/utexas/cs345/jdblisp/Parser.jj
+++ b/src/edu/utexas/cs345/jdblisp/Parser.jj
@@ -41,7 +41,7 @@ TOKEN : /* LITERALS & SYMBOLS */
| < STRG: "\"" (~["\""])* "\"" >
| < SYMB: (["A"-"Z", "a"-"z", "_", "+", "-", "*", "/", "=", ">", "<"])+
(["A"-"Z", "a"-"z", "0"-"9",
- "_", "+", "-", "*", "/", "=", ">", "<"])? >
+ "_", "+", "-", "*", "/", "=", ">", "<"])* >
}
/**
diff --git a/src/edu/utexas/cs345/jdblisp/SExp.java b/src/edu/utexas/cs345/jdblisp/SExp.java
index 0b3dda1..cb6e2f3 100755
--- a/src/edu/utexas/cs345/jdblisp/SExp.java
+++ b/src/edu/utexas/cs345/jdblisp/SExp.java
@@ -26,4 +26,5 @@ public interface SExp {
public String display(String offset) { return offset + "NIL\n"; }
public String toString() { return "NIL"; }
};
+
}
diff --git a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java
index 0203867..f5362b7 100755
--- a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java
+++ b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java
@@ -10,22 +10,12 @@ import java.util.ArrayList;
public abstract class SpecialFormEntry extends FormEntry {
protected LISPRuntime environment;
- protected int expectedArgumentCount;
- protected Class extends SExp>[] expectedArgumentTypes;
public SpecialFormEntry(Symbol name, LISPRuntime environment,
- HelpTopic helpinfo, int expectedArgCount, Class extends SExp>... expectedArgTypes) {
+ HelpTopic helpinfo) {
super(name, helpinfo);
- // three cases: no arguments, so EAT == null || 0; arg types homogenous
- // so EAT == 1; arg types heterogenous, EAT == numArgs
- assert (expectedArgumentTypes == null ||
- expectedArgumentTypes.length <= 1 ||
- expectedArgumentTypes.length == expectedArgumentCount);
-
this.environment = environment;
- this.expectedArgumentCount = expectedArgCount;
- this.expectedArgumentTypes = expectedArgTypes;
}
public abstract SExp call(SymbolTable symbolTable, Seq arguments)
@@ -33,63 +23,48 @@ public abstract class SpecialFormEntry extends FormEntry {
@Override
public String display(String offset) {
- return offset + "Special Form Entry: " + name.toString();
+ return offset + "Special Form Entry: " + symbol.toString();
}
@Override
public String toString() {
- return "";
+ return "";
}
- protected void checkArguments(Seq arguments) throws LispException {
+ static final Symbol LTE = new Symbol("<=");
+ static final Symbol LT = new Symbol("<");
+ static final Symbol NUMEQ = new Symbol("=");
+ static final Symbol NUMNOTEQ = new Symbol("/=");
+ static final Symbol GT = new Symbol(">");
+ static final Symbol GTE = new Symbol(">=");
+ static final Symbol DIV = new Symbol("/");
+ static final Symbol DIF = new Symbol("-");
+ static final Symbol MUL = new Symbol("*");
+ static final Symbol SUM = new Symbol("+");
+ static final Symbol CAR = new Symbol("CAR");
+ static final Symbol CDR = new Symbol("CDR");
+ static final Symbol CONS = new Symbol("CONS");
+ static final Symbol DEFUN = new Symbol("DEFUN");
+ static final Symbol DEFPARAMETER = new Symbol("DEFPARAMETER");
+ static final Symbol DEFVAR = new Symbol("DEFVAR");
+ static final Symbol ENABLEDEBUGAST = new Symbol("ENABLE-DEBUG-AST");
+ static final Symbol FUNCTION = new Symbol("FUNCTION");
+ static final Symbol FUNCALL = new Symbol("FUNCALL");
+ static final Symbol GETF = new Symbol("GETF");
+ static final Symbol HELP = new Symbol("HELP");
+ static final Symbol IF = new Symbol("IF");
+ static final Symbol LAMBDA = new Symbol("LAMBDA");
+ static final Symbol LET = new Symbol("LET");
+ static final Symbol LET_STAR = new Symbol("LET*");
+ static final Symbol LIST = new Symbol("LIST");
+ static final Symbol MOD = new Symbol("MOD");
+ static final Symbol QUOTE = new Symbol("QUOTE");
+ static final Symbol PROGN = new Symbol("PROGN");
+ static final Symbol REM = new Symbol("REM");
+ static final Symbol SETQ = new Symbol("SETQ");
+ static final Symbol TRACE = new Symbol("TRACE");
+ static final Symbol QUIT = new Symbol("QUIT");
- boolean argsUnbounded = expectedArgumentCount < 0;
- int expectedArgs = Math.abs(expectedArgumentCount);
- int actualArgs;
-
- // first case: expect 0 arguments
- if (expectedArgs == 0) {
- if (arguments != null)
- throw new InvalidArgumentQuantityException(0, arguments.length());
- return;
- }
-
- // expect at least one arg, err if none given
- if (arguments == null)
- throw new InvalidArgumentQuantityException(expectedArgs, 0);
-
- actualArgs = arguments.length();
-
- // there should be at least as many actual argument as expected
- if ( actualArgs < expectedArgs ||
- (actualArgs > expectedArgs && !argsUnbounded)) {
- if (argsUnbounded)
- throw new InvalidArgumentQuantityException("expected at least "
- + expectedArgs + " arguments");
- else
- throw new InvalidArgumentQuantityException(expectedArgs, actualArgs);
- }
-
- assert (expectedArgumentTypes != null &&
- expectedArgumentTypes.length > 0);
-
- // at this point, we know that the amount of arguments is valid
- for (int i = 0; arguments != null; ++i) {
- Class extends SExp> expectedType =
- (i >= expectedArgumentTypes.length ?
- expectedArgumentTypes[expectedArgumentTypes.length - 1] :
- expectedArgumentTypes[i]);
-
- // check the type of the argument
- if (!expectedType.isAssignableFrom(arguments.car.getClass()))
- throw new TypeException(arguments.car, expectedType);
-
- // next argument
- arguments = arguments.cdr;
- }
-
- return; // no error, good times
- }
// ------------------------
// SPECIAL FORMS DEFINITION
// ------------------------
@@ -107,15 +82,13 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---
final SpecialFormEntry LTE = new SpecialFormEntry(
- new Symbol("<="),
- environment,
+ SpecialFormEntry.LTE, environment,
new FormHelpTopic("<=", "Less than or equal to",
"(<= *) => ",
"The value of <= is true if the numbers are in monotonically "
+ "nondecreasing order; otherwise it is false.",
"number", "a real",
- "result", "a boolean"),
- -2, Num.class)
+ "result", "a boolean"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -123,17 +96,22 @@ public abstract class SpecialFormEntry extends FormEntry {
Num current;
Num next;
- checkArguments(arguments);
+ // check that there is at least one argument
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument required.");
// get first number
- current = (Num) arguments.car;
+ current = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// advance to next argument
arguments = arguments.cdr;
while(arguments != null) {
// get next number
- next = (Num) arguments.car;
+ next = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// current > next, return false
if (current.compareTo(next) > 0) return SExp.NIL;
@@ -155,15 +133,13 @@ public abstract class SpecialFormEntry extends FormEntry {
// --
final SpecialFormEntry LT = new SpecialFormEntry(
- new Symbol("<"),
- environment,
+ SpecialFormEntry.LT, environment,
new FormHelpTopic("<", "Less than",
"(< *) => ",
"The value of < is true if the numbers are in monotonically "
+ "increasing order; otherwise it is false.",
"number", "a real",
- "result", "a boolean"),
- -2, Num.class)
+ "result", "a boolean"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -171,17 +147,21 @@ public abstract class SpecialFormEntry extends FormEntry {
Num current;
Num next;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// get first number
- current = (Num) arguments.car;
+ current = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// advance to next argument
arguments = arguments.cdr;
while(arguments != null) {
// get next number
- next = (Num) arguments.car;
+ next = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// current >= next, return false
if (current.compareTo(next) >= 0) return SExp.NIL;
@@ -203,14 +183,12 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---------
final SpecialFormEntry NUMEQ = new SpecialFormEntry(
- new Symbol("="),
- environment,
+ SpecialFormEntry.NUMEQ, environment,
new FormHelpTopic("=", "Equal to",
"(= *) => ",
"The value of = is true if all numbers are the same in value.",
"number", "a number",
- "result", "a boolean"),
- -2, Num.class)
+ "result", "a boolean"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -218,17 +196,21 @@ public abstract class SpecialFormEntry extends FormEntry {
Num current;
Num next;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// get first number
- current = (Num) arguments.car;
+ current = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// advance to next argument
arguments = arguments.cdr;
while(arguments != null) {
// get next number
- next = (Num) arguments.car;
+ next = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// current != next, return false
if (current.compareTo(next) != 0) return SExp.NIL;
@@ -250,14 +232,12 @@ public abstract class SpecialFormEntry extends FormEntry {
// -------------
final SpecialFormEntry NUMNOTEQ = new SpecialFormEntry(
- new Symbol("/="),
- environment,
+ SpecialFormEntry.NUMNOTEQ, environment,
new FormHelpTopic("/=", "Not equal to",
"(/= *) => ",
"The value of /= is true if no two numbers are the same in value.",
"number", "a number",
- "result", "a boolean"),
- -2, Num.class)
+ "result", "a boolean"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -265,17 +245,21 @@ public abstract class SpecialFormEntry extends FormEntry {
Num current;
Num next;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// get first number
- current = (Num) arguments.car;
+ current = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// advance to next argument
arguments = arguments.cdr;
while(arguments != null) {
// get next number
- next = (Num) arguments.car;
+ next = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// current == next, return false
if (current.compareTo(next) == 0) return SExp.NIL;
@@ -297,15 +281,13 @@ public abstract class SpecialFormEntry extends FormEntry {
// --
final SpecialFormEntry GT = new SpecialFormEntry(
- new Symbol(">"),
- environment,
+ SpecialFormEntry.GT, environment,
new FormHelpTopic(">", "Greater than",
"(> *) => ",
"The value of > is true if the numbers are in monotonically "
+ "decreasing order; otherwise it is false.",
"number", "a number",
- "result", "a boolean"),
- -2, Num.class)
+ "result", "a boolean"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -313,17 +295,21 @@ public abstract class SpecialFormEntry extends FormEntry {
Num current;
Num next;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// get first number
- current = (Num) arguments.car;
+ current = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// advance to next argument
arguments = arguments.cdr;
while(arguments != null) {
// get next number
- next = (Num) arguments.car;
+ next = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// current <= next, return false
if (current.compareTo(next) <= 0) return SExp.NIL;
@@ -345,15 +331,13 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---
final SpecialFormEntry GTE = new SpecialFormEntry(
- new Symbol(">="),
- environment,
+ SpecialFormEntry.GTE, environment,
new FormHelpTopic(">=", "Greater than or equal to",
"(>= *) => ",
"The value of > is true if the numbers are in monotonically "
+ "non-increasing order; otherwise it is false.",
"number", "a number",
- "result", "a boolean"),
- -2, Num.class)
+ "result", "a boolean"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -361,17 +345,21 @@ public abstract class SpecialFormEntry extends FormEntry {
Num current;
Num next;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// get first number
- current = (Num) arguments.car;
+ current = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// advance to next argument
arguments = arguments.cdr;
while(arguments != null) {
// get next number
- next = (Num) arguments.car;
+ next = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
// current < next, return false
if (current.compareTo(next) < 0) return SExp.NIL;
@@ -393,8 +381,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---
final SpecialFormEntry DIV = new SpecialFormEntry(
- new Symbol("/"),
- environment,
+ SpecialFormEntry.DIV, environment,
new FormHelpTopic("/", "Divide several expressions.",
"(- divisor) | (- dividend [... ])",
"Perform division. If there is only one argument passed "
@@ -407,8 +394,7 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "the number which is diveded.",
"divisor_1 ... divisor_n", "Divisors are the numbers dividing "
+ "the dividend and may be any expression that evaluates "
- + "to a number."),
- -1, Num.class)
+ + "to a number."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -416,10 +402,13 @@ public abstract class SpecialFormEntry extends FormEntry {
Num dividend = new Num("1");
Num firstArg;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// case: only one argument: 1 / arg
- firstArg = (Num) arguments.car.eval(symbolTable);
+ firstArg = TypeUtil.attemptCast(Num.class,
+ arguments.car.eval(symbolTable));
dividend = dividend.divideBy(firstArg);
@@ -431,8 +420,9 @@ public abstract class SpecialFormEntry extends FormEntry {
// variable number of arguments [0..inf)
while (arguments != null) {
- dividend = dividend.divideBy(
- (Num) arguments.car.eval(symbolTable));
+ dividend = dividend.divideBy(TypeUtil.attemptCast(
+ Num.class,
+ arguments.car.eval(symbolTable)));
arguments = arguments.cdr;
}
@@ -445,8 +435,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---
final SpecialFormEntry DIF = new SpecialFormEntry(
- new Symbol("-"),
- environment,
+ SpecialFormEntry.DIF, environment,
new FormHelpTopic("-", "Subtract several expressions.",
"(- subtrahend) | (- [... ])",
"Perform a subtraction. If there is only one argument passed "
@@ -459,19 +448,21 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "the number from which the others are subtracted.",
"subtrahend_1 ... subtrahend_n", "Subtrahends are numbers "
+ "subtracted from the minuend and may be any expression "
- + "that evaluates to a number."),
- -1, Num.class)
+ + "that evaluates to a number."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
Num difference = new Num("0");
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// case: only one argument: 0 - arg
- difference = difference.subtract(
- (Num) arguments.car.eval(symbolTable));
+ difference = difference.subtract(TypeUtil.attemptCast(
+ Num.class,
+ arguments.car.eval(symbolTable)));
arguments = arguments.cdr;
if (arguments == null) return difference;
@@ -481,8 +472,9 @@ public abstract class SpecialFormEntry extends FormEntry {
// variable number of arguments [0..inf)
while (arguments != null) {
- difference = difference.subtract(
- (Num) arguments.car.eval(symbolTable));
+ difference = difference.subtract(TypeUtil.attemptCast(
+ Num.class,
+ arguments.car.eval(symbolTable)));
arguments = arguments.cdr;
}
@@ -495,8 +487,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---
final SpecialFormEntry MUL = new SpecialFormEntry(
- new Symbol("*"),
- environment,
+ SpecialFormEntry.MUL, environment,
new FormHelpTopic("*", "Multiply several expressions.",
"(+ [ ... ])",
"Compute the product of the zero or more expressions passed"
@@ -504,8 +495,7 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "before being bound to function parameters. The"
+ " expressions passed to multiply must evaluate to numbers.",
"multiplicand_1 ... multiplicand_n", "Multiplicands may be "
- + "any expression that evaluates to a number."),
- -1, Num.class) // not technically correct, * accepts 0 arguments
+ + "any expression that evaluates to a number."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -514,12 +504,8 @@ public abstract class SpecialFormEntry extends FormEntry {
// variable number of arguments [0..inf)
while (arguments != null) {
- try {
- product = product.multiply(
- (Num) arguments.car.eval(symbolTable));
- } catch (ClassCastException cce) {
- throw new TypeException(arguments.car, Num.class);
- }
+ product = product.multiply(TypeUtil.attemptCast(
+ Num.class, arguments.car.eval(symbolTable)));
arguments = arguments.cdr;
}
@@ -532,8 +518,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ---
final SpecialFormEntry SUM = new SpecialFormEntry(
- new Symbol("+"),
- environment,
+ SpecialFormEntry.SUM, environment,
new FormHelpTopic("+", "Sum several expressions.",
"(+ [ ... ])",
"Compute the summation of the zero or more expressions passed"
@@ -541,8 +526,7 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "before being bound to function parameters. The"
+ " expressions passed to sum must evaluate to numbers.",
"addend_1 ... addend_n", "Addends may be any expression that "
- + "evaluates to a number."),
- -1, Num.class) // not technically correct, + accepts 0 arguments
+ + "evaluates to a number."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -551,11 +535,8 @@ public abstract class SpecialFormEntry extends FormEntry {
// variable number of arguments [0..inf)
while (arguments != null) {
- try {
- sum = sum.add((Num) arguments.car.eval(symbolTable));
- } catch (ClassCastException cce) {
- throw new TypeException(arguments.car, Num.class);
- }
+ sum = sum.add(TypeUtil.attemptCast(
+ Num.class,arguments.car.eval(symbolTable)));
arguments = arguments.cdr;
}
@@ -569,21 +550,25 @@ public abstract class SpecialFormEntry extends FormEntry {
// TODO
+ // ---
+ // CDR
+ // ---
+
+ // TODO
+
// ----
// CONS
// ----
final SpecialFormEntry CONS = new SpecialFormEntry(
- new Symbol("CONS"),
- environment,
+ SpecialFormEntry.CONS, environment,
new FormHelpTopic("CONS", "create a cons",
"(cons ) => ",
"Creates a fresh cons, the car of which is object-1 and the "
+ "cdr of which is object-2.",
"object-1", "an object",
"object-2", "an object",
- "cons", "a cons"),
- 2, SExp.class)
+ "cons", "a cons"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -591,7 +576,8 @@ public abstract class SpecialFormEntry extends FormEntry {
SExp object1, object2;
Cons cons;
- checkArguments(arguments);
+ if (arguments == null || arguments.cdr == null)
+ throw new InvalidArgumentQuantityException(toString(), 2);
// get the two objects
object1 = arguments.car.eval(symbolTable);
@@ -609,17 +595,16 @@ public abstract class SpecialFormEntry extends FormEntry {
// -----
final SpecialFormEntry DEFUN = new SpecialFormEntry(
- new Symbol("DEFUN"),
- environment,
+ SpecialFormEntry.DEFUN, environment,
new FormHelpTopic("DEFUN", "Define a (global) function.",
"(defun () )",
"Create a function binding. This will replace any existing binding.",
- "name", "the symbol to bind to the function definition.",
+ "name", "the symbol to bind to the function definition, "
+ + "not evaluated.",
"param-list", "a list of symbols that will be bound in the "
+ "function scope to the arguments passed to the function.",
"func-body", "an sexpression evaluated when the function is "
- + "called."),
- 3, Symbol.class, List.class, SExp.class)
+ + "called."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -628,25 +613,32 @@ public abstract class SpecialFormEntry extends FormEntry {
ArrayList parameters = new ArrayList();
SExp body;
- checkArguments(arguments);
+ // check for the correct number of arguments
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(
+ toString(), 3, 0);
+ if (arguments.length() != 3)
+ throw new InvalidArgumentQuantityException(
+ toString(), 3, arguments.length());
// TODO: check to see if a function for this symbol exists
// and warn if so
// first argument: Symbol for function name
- functionName = (Symbol) arguments.car;
+ functionName = TypeUtil.attemptCast(
+ Symbol.class, arguments.car);
// second argument, parameter list
arguments = arguments.cdr;
assert (arguments != null);
// read parameters
- Seq paramSeq = ((List) arguments.car).seq;
- while (paramSeq != null) {
- if (!(paramSeq.car instanceof Symbol))
- throw new TypeException(paramSeq.car, Symbol.class);
+ Seq paramSeq = TypeUtil.attemptCast(
+ List.class, arguments.car).seq;
- parameters.add((Symbol) paramSeq.car);
+ while (paramSeq != null) {
+ parameters.add(TypeUtil.attemptCast(
+ Symbol.class, paramSeq.car));
paramSeq = paramSeq.cdr;
}
@@ -671,9 +663,8 @@ public abstract class SpecialFormEntry extends FormEntry {
// DEFPARAMETER
// ------------
- final SpecialFormEntry DEFPARAMETER = new SpecialFormEntry(
- new Symbol("DEFPARAMETER"),
- environment,
+ final SpecialFormEntry DEFPARAM = new SpecialFormEntry(
+ SpecialFormEntry.DEFPARAMETER, environment,
new FormHelpTopic("DEFPARAMETER", "define a dynamic variable",
"(defparameter [ []]) => ",
"defparameter establishes name as a dynamic variable. "
@@ -684,8 +675,7 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "already bound.)",
"name", "a symbol; not evaluated. ",
"initial-value", "a form, always evaluated",
- "documentation", "a string; not evaluated."),
- -1, Symbol.class, SExp.class, Str.class)
+ "documentation", "a string; not evaluated."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -694,10 +684,12 @@ public abstract class SpecialFormEntry extends FormEntry {
SExp initValue = null;
HelpTopic helpinfo = null;
- checkArguments(arguments);
+ if (arguments == null || arguments.length() < 1)
+ throw new InvalidArgumentQuantityException(
+ toString(), 1, 0);
// first argument: variable name
- name = (Symbol) arguments.car;
+ name = TypeUtil.attemptCast(Symbol.class, arguments.car);
// second argument: initial value
arguments = arguments.cdr;
@@ -708,11 +700,12 @@ public abstract class SpecialFormEntry extends FormEntry {
arguments = arguments.cdr;
if (arguments != null)
helpinfo = new HelpTopic(name.toString(), "variable",
- ((Str) arguments.car).value);
+ TypeUtil.attemptCast(
+ Str.class, arguments.car).value);
}
- symbolTable.bind(name,
- new VariableEntry(initValue, false, helpinfo));
+ environment.globalSymbolTable.bind(name,
+ new VariableEntry(name, initValue, false, helpinfo));
return name;
@@ -724,8 +717,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ------
final SpecialFormEntry DEFVAR = new SpecialFormEntry(
- new Symbol("DEFVAR"),
- environment,
+ SpecialFormEntry.DEFVAR, environment,
new FormHelpTopic("DEFVAR", "define a variable",
"(defvar [ []]) => ",
"defvar establishes name as a dynamic variable and assigns "
@@ -736,8 +728,7 @@ public abstract class SpecialFormEntry extends FormEntry {
"name", "a symbol; not evaluated. ",
"initial-value", "a form, evaluated only if name is not "
+ "already bound.",
- "documentation", "a string; not evaluated."),
- -1, Symbol.class, SExp.class, Str.class)
+ "documentation", "a string; not evaluated."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -746,17 +737,19 @@ public abstract class SpecialFormEntry extends FormEntry {
SExp initValue = null;
HelpTopic helpinfo = null;
- checkArguments(arguments);
+ if (arguments == null || arguments.length() < 1)
+ throw new InvalidArgumentQuantityException(
+ toString(), 1, 0);
// first argument: variable name
- name = (Symbol) arguments.car;
+ name = TypeUtil.attemptCast(Symbol.class, arguments.car);
// if this variable is already defined, return without
// setting it
if (symbolTable.lookupVariable(name) != null)
return arguments.car;
- return DEFPARAMETER.call(symbolTable, arguments);
+ return DEFPARAM.call(symbolTable, arguments);
}
};
@@ -765,8 +758,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ----------------
final SpecialFormEntry ENABLEDEBUGAST = new SpecialFormEntry(
- new Symbol("ENABLE-DEBUG-AST"),
- environment,
+ SpecialFormEntry.ENABLEDEBUGAST, environment,
new FormHelpTopic("ENABLE-DEBUG-AST",
"Enable debug information: abstract syntax tree.",
"(enable-debug-ast [])",
@@ -774,7 +766,7 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "representation of the abstract syntax tree generated "
+ "by the parser for each sexpression it parses.",
"enable", "NIL = disabled, anything else = enabled. No "
- + "argument = enabled."), -1)
+ + "argument = enabled."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -797,24 +789,25 @@ public abstract class SpecialFormEntry extends FormEntry {
// --------
final SpecialFormEntry FUNCTION = new SpecialFormEntry(
- new Symbol("FUNCTION"),
- environment,
+ SpecialFormEntry.FUNCTION, environment,
new FormHelpTopic("FUNCTION", "retrieve a function",
"(function ) => ",
"function returns the function specified by the symbol passed "
+ " as an argument. #'funcname is equivalent to (function "
+ " funcname).",
"func-name", "a symbol naming a function",
- "function", "a function"),
- 1, Symbol.class)
+ "function", "a function"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
FormEntry fe = null;
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(
+ toString(), 1, 0);
- fe = symbolTable.lookupFunction((Symbol) arguments.car);
+ fe = symbolTable.lookupFunction(TypeUtil.attemptCast(
+ Symbol.class, arguments.car));
if (fe == null)
throw new UndefinedFunctionException((Symbol) arguments.car);
@@ -828,8 +821,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// -------
final SpecialFormEntry FUNCALL = new SpecialFormEntry(
- new Symbol("FUNCALL"),
- environment,
+ SpecialFormEntry.FUNCALL, environment,
new FormHelpTopic("FUNCALL", "make a function call",
"(funcall *) => ",
"funcall applies function to args. If function is a symbol, "
@@ -837,13 +829,14 @@ public abstract class SpecialFormEntry extends FormEntry {
+ "functional value in the global environment.",
"function", "a function designator",
"arg", "an object",
- "results", "the result of the function call"),
- -1, SExp.class)
+ "results", "the result of the function call"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
- checkArguments(arguments);
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(toString(),
+ "at least one argument is required.");
// first argument: function designator
SExp func = arguments.car.eval(symbolTable);
@@ -874,8 +867,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// ----
final SpecialFormEntry GETF = new SpecialFormEntry(
- new Symbol("GETF"),
- environment,
+ SpecialFormEntry.GETF, environment,
new FormHelpTopic("GETF", "",
"(getf []) => ",
"getf finds a property on the plist whose property indicator "
@@ -887,8 +879,7 @@ public abstract class SpecialFormEntry extends FormEntry {
"plist", "a property list.",
"indicator", "an object",
"default", "an object. The default is NIL",
- "value", "an object"),
- -2, List.class, SExp.class, SExp.class)
+ "value", "an object"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -898,11 +889,13 @@ public abstract class SpecialFormEntry extends FormEntry {
SExp indicator;
SExp retVal = SExp.NIL;
- checkArguments(arguments);
+ if (arguments == null || arguments.length() < 2)
+ throw new InvalidArgumentQuantityException(
+ toString(), 2, 0);
// first argument: property list
plistEval = arguments.car.eval(symbolTable);
- plistSeq = ((List) plistEval).seq;
+ plistSeq = TypeUtil.attemptCast(List.class, plistEval).seq;
// second argument: indicator
arguments = arguments.cdr;
@@ -933,15 +926,13 @@ public abstract class SpecialFormEntry extends FormEntry {
// ----
final SpecialFormEntry HELP = new SpecialFormEntry(
- new Symbol("HELP"),
- environment,
+ SpecialFormEntry.HELP, environment,
new FormHelpTopic("HELP",
"Display online help information for a topic.",
"(help [*])",
null,
"topic",
- "either a string representing the topic to lookup or a symbol"),
- -1, SExp.class) // technically can accept 0 arguments
+ "either a string representing the topic to lookup or a symbol"))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
@@ -951,7 +942,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// no arguments: print help for HELP
if (arguments == null)
return this.call(symbolTable,
- new Seq(new Symbol("HELP"), null));
+ new Seq(SpecialFormEntry.HELP, null));
while (arguments != null) {
// try to find the topic or function help
@@ -986,8 +977,7 @@ public abstract class SpecialFormEntry extends FormEntry {
// --
final SpecialFormEntry IF = new SpecialFormEntry(
- new Symbol("IF"),
- environment,
+ SpecialFormEntry.IF, environment,
new FormHelpTopic("IF",
"conditional code execution",
"(if []) => result*",
@@ -1001,13 +991,13 @@ public abstract class SpecialFormEntry extends FormEntry {
"else-form", "a form. The default is nil. ",
"results", "if the test-form yielded true, the values "
+ "returned by the then-form; otherwise, the values "
- + "returned by the else-form."),
- -2, SExp.class)
+ + "returned by the else-form."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
- checkArguments(arguments);
+ if (arguments == null || arguments.length() < 2)
+ throw new InvalidArgumentQuantityException(toString(), 2);
// evaluate test form
SExp testResult = arguments.car.eval(symbolTable);
@@ -1016,11 +1006,12 @@ public abstract class SpecialFormEntry extends FormEntry {
arguments = arguments.cdr;
// if false, advance to else-form
- if (testResult == null || testResult == SExp.NIL) arguments = arguments.cdr;
+ if (testResult == null || testResult == SExp.NIL)
+ arguments = arguments.cdr;
if (arguments == null) return SExp.NIL;
- return arguments.eval(symbolTable);
+ return arguments.car.eval(symbolTable);
}
};
@@ -1029,16 +1020,14 @@ public abstract class SpecialFormEntry extends FormEntry {
// ------
final SpecialFormEntry LAMBDA = new SpecialFormEntry(
- new Symbol("LAMBDA"),
- environment,
+ SpecialFormEntry.LAMBDA, environment,
new FormHelpTopic("LAMDA",
"create an anonymous function over the current lexical closure",
"(lambda