diff --git a/build.xml b/build.xml
index a663a7d..ffff82c 100755
--- a/build.xml
+++ b/build.xml
@@ -47,5 +47,8 @@
-
+
+
+
+
diff --git a/project.properties b/project.properties
index 2958b63..f8fa2f6 100755
--- a/project.properties
+++ b/project.properties
@@ -4,3 +4,4 @@ dist.dir=dist
lib.dir=lib
grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj
grammar.output.dir=${src.dir}/edu/utexas/cs345/jdblisp/parser
+project.version=0.1.0
diff --git a/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java b/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java
index e823c58..c7fb241 100755
--- a/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java
+++ b/src/edu/utexas/cs345/jdblisp/InvalidArgumentQuantityException.java
@@ -11,4 +11,12 @@ public class InvalidArgumentQuantityException extends LispException {
super ("Invalid number of arguments: " + actual
+ " (expected " + expected + ").");
}
+
+ public InvalidArgumentQuantityException(int actual) {
+ super ("Invalid number of arguments: " + actual);
+ }
+
+ public InvalidArgumentQuantityException(String message) {
+ super ("Invalid number of arguments: " + message);
+ }
}
diff --git a/src/edu/utexas/cs345/jdblisp/Keyword.java b/src/edu/utexas/cs345/jdblisp/Keyword.java
new file mode 100644
index 0000000..5b8b998
--- /dev/null
+++ b/src/edu/utexas/cs345/jdblisp/Keyword.java
@@ -0,0 +1,29 @@
+package edu.utexas.cs345.jdblisp;
+
+/**
+ * Keyword
+ * @author Jonathan Bernard (jdbernard@gmail.com)
+ */
+public class Keyword extends Symbol {
+
+ public Keyword(String name) { super(name); }
+
+ /** {@inheritdoc} */
+ @Override
+ public SExp eval(SymbolTable symbolTable) { return this; }
+
+ public String display(String offset) {
+ return offset + "Keyword: " + name + "\n";
+ }
+
+ @Override
+ public String toString() { return ":" + name; }
+
+ @Override
+ public boolean equals(Object that) {
+ if (this == that) return true;
+ if (that == null) return false;
+ if (!(that instanceof Keyword)) return false;
+ return this.name.equals(((Keyword) that).name);
+ }
+}
diff --git a/src/edu/utexas/cs345/jdblisp/LISPRuntime.java b/src/edu/utexas/cs345/jdblisp/LISPRuntime.java
index 48c16af..257fc1d 100755
--- a/src/edu/utexas/cs345/jdblisp/LISPRuntime.java
+++ b/src/edu/utexas/cs345/jdblisp/LISPRuntime.java
@@ -35,9 +35,13 @@ public class LISPRuntime {
public LISPRuntime(boolean interactive) {
this.interactive = interactive;
Parser parser = new Parser(new ByteArrayInputStream(new byte[]{}));
- globalSymbolTable = new SymbolTable();
+
+ // build global constants
+ SymbolTable constantsSymbolTable = defineGlobalConstants();
+
+ // build global symbol table
+ globalSymbolTable = new SymbolTable(constantsSymbolTable);
SpecialFormEntry.defineSpecialForms(this);
- globalSymbolTable.bind(new Symbol("T"), new VariableEntry(SExp.T));
}
// TODO: is this needed?
@@ -114,4 +118,12 @@ public class LISPRuntime {
OutputStream getOutputStream() { return os; }
+ private static SymbolTable defineGlobalConstants() {
+ SymbolTable constantsTable = new SymbolTable();
+ constantsTable.bind(new Symbol("T"), new VariableEntry(SExp.T, true));
+ constantsTable.bind(new Symbol("NIL"), new VariableEntry(SExp.NIL, true));
+
+ return constantsTable;
+ }
+
}
diff --git a/src/edu/utexas/cs345/jdblisp/Lisp.java b/src/edu/utexas/cs345/jdblisp/Lisp.java
deleted file mode 100755
index 9683198..0000000
--- a/src/edu/utexas/cs345/jdblisp/Lisp.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package edu.utexas.cs345.jdblisp;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.Scanner;
-
-import edu.utexas.cs345.jdblisp.parser.Parser;
-import edu.utexas.cs345.jdblisp.parser.ParseException;
-
-/**
- * @author Jonathan Bernard (jdbernard@gmail.com)
- */
-public class Lisp {
-
- public SymbolTable globalSymbolTable;
-
- private Parser parser;
- private boolean interactive = true;
-
- public Lisp() {
- this(true);
- }
-
- public Lisp(boolean interactive) {
- this.interactive = interactive;
- Parser parser = new Parser(new ByteArrayInputStream(new byte[]{}));
- globalSymbolTable = new SymbolTable();
- SpecialFormEntry.defineSpecialForms(this);
- }
-
- public static void main(String[] args) {
- Lisp lisp = new Lisp();
- lisp.repl(System.in, System.out);
- }
-
- public void repl(InputStream is, OutputStream os) {
-
- // wrap input and output in more friendly objects
- Scanner in = new Scanner(is);
- PrintWriter out = new PrintWriter(os);
-
- // print prompt if applicable
- if (interactive) {
- out.print("> ");
- out.flush();
- }
-
- // read each line of input
- while(in.hasNextLine()) {
-
- // get line
- String line = in.nextLine();
-
- // stuff it into an input buffer for the parser
- ByteArrayInputStream bin = new ByteArrayInputStream(line.getBytes());
-
- // re-init the parser with the new stream
- parser.ReInit(bin);
-
- // parse the line
- SExp sexp;
- try { sexp = parser.sexp(); }
- catch (ParseException pe) {
- // TODO
- out.println(pe.getLocalizedMessage());
- continue;
- }
-
- // DEBUG: print abstract syntax TODO: remove
- out.println(sexp.display(" "));
- out.flush();
-
- try {
- out.println(sexp.eval(globalSymbolTable));
- } catch (LispException le) {
- out.println(le.getLocalizedMessage());
- }
-
- // print prompt if applicable
- if (interactive) {
- out.print("> ");
- out.flush();
- }
-
- }
-
- out.println("\nLeaving JDB-LISP");
- out.flush();
- }
-
-}
diff --git a/src/edu/utexas/cs345/jdblisp/List.java b/src/edu/utexas/cs345/jdblisp/List.java
index 68fa9c3..8363826 100755
--- a/src/edu/utexas/cs345/jdblisp/List.java
+++ b/src/edu/utexas/cs345/jdblisp/List.java
@@ -15,7 +15,7 @@ public class List implements SExp {
public SExp eval(SymbolTable table) throws LispException {
// null is NIL
- if (seq == null) return null;
+ if (seq == null) return SExp.NIL;
// if the car of the sequence is a symbol,
if (seq.car instanceof Symbol) {
@@ -35,7 +35,7 @@ public class List implements SExp {
}
- return null;
+ return SExp.NIL;
}
public String display(String offset) {
@@ -49,7 +49,7 @@ public class List implements SExp {
@Override
public String toString() {
- return "(" + (seq == null ? "" : seq.toString()) + ")";
+ return seq == null ? "NIL" : "(" + seq.toString() + ")";
}
}
diff --git a/src/edu/utexas/cs345/jdblisp/Parser.jj b/src/edu/utexas/cs345/jdblisp/Parser.jj
index d66bcd7..2cc3e2c 100755
--- a/src/edu/utexas/cs345/jdblisp/Parser.jj
+++ b/src/edu/utexas/cs345/jdblisp/Parser.jj
@@ -11,6 +11,7 @@ package edu.utexas.cs345.jdblisp.parser;
import edu.utexas.cs345.jdblisp.*;
public class Parser {
+ private static Symbol QUOTE_SYMB = new Symbol("QUOTE");
}
PARSER_END(Parser)
@@ -28,12 +29,14 @@ TOKEN : /* PUNCTUATION */
{ < LPAREN: "(" >
| < RPAREN: ")" >
| < NIL: (["N","n"]["I","i"]["L","l"])>
+| < QUOTE: "'" >
+| < KEYWORD: ":" >
}
TOKEN : /* LITERALS & SYMBOLS */
{ < NUMB: (["+", "-"])? (["0"-"9"])+ ("." (["0"-"9"])+ )? >
-| < STRG: "\"" (["A"-"Z", "a"-"z", "_"])* "\"" >
+| < STRG: "\"" (~["\""])* "\"" >
| < SYMB: (["A"-"Z", "a"-"z", "_", "+", "-", "*", "/", "="])+
(["A"-"Z", "a"-"z", "0"-"9",
"_", "+", "-", "*", "/", "="])? >
@@ -45,9 +48,10 @@ TOKEN : /* LITERALS & SYMBOLS */
SExp sexp():
{ SExp s = null; Token t;
}
-{ t = { return new Symbol(t.image.toUpperCase()); }
+{ s = symbol() { return s; }
| t = { return new Str(t.image); }
| t = { return new Num(t.image); }
+ | t = s = sexp() { return new List(new Seq(QUOTE_SYMB, s)); }
| s = list() { return s; }
}
@@ -64,10 +68,19 @@ List list():
/**
* Seq -> null | SExp Seq
*/
- Seq seq():
- { Seq sq; SExp se;
- }
- { [ se = sexp() sq = seq() { return new Seq(se, sq); } ]
-
- { return null; }
- }
+Seq seq():
+{ Seq sq; SExp se;
+}
+{ [ se = sexp() sq = seq() { return new Seq(se, sq); } ]
+ { return null; }
+}
+
+/**
+ * Symbol -> Symbol | Keyword Symbol
+ */
+Symbol symbol():
+{ Token t;
+}
+{ t = { return new Symbol(t.image.toUpperCase()); }
+ | t = { return new Keyword(t.image.toUpperCase()); }
+}
diff --git a/src/edu/utexas/cs345/jdblisp/SExp.java b/src/edu/utexas/cs345/jdblisp/SExp.java
index e28f1b8..0b3dda1 100755
--- a/src/edu/utexas/cs345/jdblisp/SExp.java
+++ b/src/edu/utexas/cs345/jdblisp/SExp.java
@@ -21,9 +21,9 @@ public interface SExp {
public String toString() { return "T"; }
};
- /*public static final SExp NIL = new SExp() {
- SExp eval(SymbolTable table) { return this; }
- String display(String offset) { return offset + "NIL\n"; }
- String toString() { return "NIL"; }
- };*/
+ public static final SExp NIL = new SExp() {
+ public SExp eval(SymbolTable table) { return this; }
+ 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 7a9920b..8a47710 100755
--- a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java
+++ b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java
@@ -35,57 +35,190 @@ public abstract class SpecialFormEntry implements FormEntry {
*/
public static void defineSpecialForms(LISPRuntime environment) {
- // ----
- // HELP
- // ----
+ // ---
+ // DIV
+ // ---
- SpecialFormEntry HELP = new SpecialFormEntry(
+ final SpecialFormEntry DIV = new SpecialFormEntry(
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"))
+ new FormHelpTopic("/", "Divide several expressions.",
+ "(- divisor) | (- dividend [... ])",
+ "Perform division. If there is only one argument passed "
+ + "then result = 1/ arg. If multiple arguments are passed"
+ + " then result = arg_1 / arg_2 / ... / args_n, computed "
+ + "from left to right. In general, expressions are "
+ + "evaluated before being bound to function parameters. "
+ + "The expressions passed to / must evaluate to numbers.",
+ "dividend", "In the case of multiple arguments to /, this is "
+ + "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."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
-
- HelpTopic topic = null;
- // no arguments: print help for HELP
+ Num dividend = new Num("1");
+ Num firstArg;
+
if (arguments == null)
- return this.call(symbolTable,
- new Seq(new Symbol("HELP"), null));
+ throw new InvalidArgumentQuantityException(
+ "invalid number of arguments: 0");
- // too many arguments
- if (arguments.length() > 1)
- throw new InvalidArgumentQuantityException(1,
- arguments.length());
+ // case: only one argument: 1 / arg
+ try { firstArg = (Num) arguments.car.eval(symbolTable); }
+ catch (ClassCastException cce) {
+ throw new TypeException(arguments.car, Num.class);
+ }
- // try to find the topic or function help
- if (arguments.car instanceof Str) {
- topic = HelpTopic.helpTopics.get(
- ((Str) arguments.car).value);
- } else if (arguments.car instanceof Symbol) {
+ dividend = dividend.divideBy(firstArg);
+
+ arguments = arguments.cdr;
+ if (arguments == null) return dividend;
+
+ // case: (/ x y1 ... yn)
+ dividend = firstArg;
+
+ // variable number of arguments [0..inf)
+ while (arguments != null) {
try {
- FormEntry fe = symbolTable.lookupFunction(
- (Symbol) arguments.car);
- topic = fe.helpinfo();
- } catch (LispException le) { topic = null; }
+ dividend = dividend.divideBy(
+ (Num) arguments.car.eval(symbolTable));
+ } catch (ClassCastException cce) {
+ throw new TypeException(arguments.car, Num.class);
+ }
+ arguments = arguments.cdr;
}
- // no topic found
- if (topic == null) {
- new PrintWriter(environment.getOutputStream(), true)
- .println(
- "No help information found for topic '"
- + arguments.car.toString() + "'.");
- return null;
+ return dividend;
+ }
+ };
+
+ // ---
+ // DIF
+ // ---
+
+ final SpecialFormEntry DIF = new SpecialFormEntry(
+ environment,
+ new FormHelpTopic("-", "Subtract several expressions.",
+ "(- subtrahend) | (- [... ])",
+ "Perform a subtraction. If there is only one argument passed "
+ + "then result = 0 - arg. If multiple arguments are passed"
+ + " then result = arg_1 - arg_2 - ... - args_n. In "
+ + "general, expressions are evaluated before being bound "
+ + " to function parameters. The expressions passed to - "
+ + "must evaluate to numbers.",
+ "minuend", "In the case of multiple arguments to -, this is "
+ + "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."))
+ {
+ public SExp call(SymbolTable symbolTable, Seq arguments)
+ throws LispException {
+
+ Num difference = new Num("0");
+
+ // need at least one argument
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(
+ "invalid number of arguments: 0");
+
+ // case: only one argument: 0 - arg
+ try {
+ difference = difference.subtract(
+ (Num) arguments.car.eval(symbolTable));
+ } catch (ClassCastException cce) {
+ throw new TypeException(arguments.car, Num.class);
}
- topic.print(environment.getOutputStream());
- return null;
+ arguments = arguments.cdr;
+ if (arguments == null) return difference;
+
+ // case: (- x y1 ... yn)
+ difference = difference.negate();
+
+ // variable number of arguments [0..inf)
+ while (arguments != null) {
+ try {
+ difference = difference.subtract(
+ (Num) arguments.car.eval(symbolTable));
+ } catch (ClassCastException cce) {
+ throw new TypeException(arguments.car, Num.class);
+ }
+ arguments = arguments.cdr;
+ }
+
+ return difference;
+ }
+ };
+
+ // ---
+ // MUL
+ // ---
+
+ final SpecialFormEntry MUL = new SpecialFormEntry(
+ environment,
+ new FormHelpTopic("*", "Multiply several expressions.",
+ "(+ [ ... ])",
+ "Compute the product of the zero or more expressions passed"
+ + "as arguments. In general, expressions are evaluated "
+ + "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."))
+ {
+ public SExp call(SymbolTable symbolTable, Seq arguments)
+ throws LispException {
+
+ Num product = new Num("1");
+
+ // 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);
+ }
+ arguments = arguments.cdr;
+ }
+
+ return product;
+ }
+ };
+
+ // ---
+ // SUM
+ // ---
+
+ final SpecialFormEntry SUM = new SpecialFormEntry(
+ environment,
+ new FormHelpTopic("+", "Sum several expressions.",
+ "(+ [ ... ])",
+ "Compute the summation of the zero or more expressions passed"
+ + "as arguments. In general, expressions are evaluated "
+ + "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."))
+ {
+ public SExp call(SymbolTable symbolTable, Seq arguments)
+ throws LispException {
+
+ Num sum = new Num("0");
+
+ // 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);
+ }
+ arguments = arguments.cdr;
+ }
+
+ return sum;
}
};
@@ -93,7 +226,7 @@ public abstract class SpecialFormEntry implements FormEntry {
// DEFUN
// -----
- SpecialFormEntry DEFUN = new SpecialFormEntry(
+ final SpecialFormEntry DEFUN = new SpecialFormEntry(
environment,
new FormHelpTopic("DEFUN", "Define a (global) function.",
"(defun () )",
@@ -157,256 +290,111 @@ public abstract class SpecialFormEntry implements FormEntry {
}
};
- // ----
- // SETQ
- // ----
+ // ------------
+ // DEFPARAMETER
+ // ------------
-
- SpecialFormEntry SETQ = new SpecialFormEntry(
+ final SpecialFormEntry DEFPARAMETER = new SpecialFormEntry(
environment,
- new FormHelpTopic("SETQ", "Define a global variable.",
- "(setq )",
- "Bind a value to a symbol in the global symbol table.",
- "name", "the symbol to bind to the given value.",
- "value", "an sexpression representing the value of the "
- + "variable. The value of the variable when it is "
- + "evaluated is the evaluated value of this sexpression."))
+ new FormHelpTopic("DEFPARAMETER", "define a dynamic variable",
+ "(defparameter []) => ",
+ "defparameter establishes name as a dynamic variable. "
+ + "defparameter unconditionally assigns the initial-value "
+ + "to the dynamic variable named name (as opposed to "
+ + "defvar, which assigns initial-value (if supplied) to "
+ + "the dynamic variable named name only if name is not "
+ + "already bound.)",
+ "name", "a symbol; not evaluated. ",
+ "initial-value", "a form, always evaluated",
+ "documentation", "a string; not evaluated."))
+ {
+ public SExp call(SymbolTable symbolTable, Seq arguments)
+ throws LispException {
+
+ Symbol name;
+ SExp initValue = null;
+ HelpTopic helpinfo = null;
+
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(0);
+
+ // first argument: variable name
+ if (!(arguments.car instanceof Symbol))
+ throw new TypeException(arguments.car, Symbol.class);
+
+ name = (Symbol) arguments.car;
+
+ // second argument: initial value
+ arguments = arguments.cdr;
+ if (arguments != null)
+ initValue = arguments.car.eval(symbolTable);
+
+ // thrid argument: documentation
+ arguments = arguments.cdr;
+ if (arguments != null) {
+ if (!(arguments.car instanceof Str))
+ throw new TypeException(arguments.car, Str.class);
+
+ helpinfo = new HelpTopic(name.name, "variable",
+ ((Str) arguments.car).value);
+ }
+
+ symbolTable.bind(name,
+ new VariableEntry(initValue, false, helpinfo));
+
+ return name;
+
+ }
+ };
+
+ // ------
+ // DEFVAR
+ // ------
+
+ final SpecialFormEntry DEFVAR = new SpecialFormEntry(
+ environment,
+ new FormHelpTopic("DEFVAR", "define a variable",
+ "(defvar [ []]) => ",
+ "defvar establishes name as a dynamic variable and assigns "
+ + "initial-value (if supplied) to the dynamic variable "
+ + "named name only if name is not already bound (this is"
+ + " in contrast to defparameter, which does not case if "
+ + "the name has already been bound. ",
+ "name", "a symbol; not evaluated. ",
+ "initial-value", "a form, evaluated only if name is not "
+ + "already bound.",
+ "documentation", "a string; not evaluated."))
{
public SExp call(SymbolTable symbolTable, Seq arguments)
throws LispException {
- Symbol variableName;
- SExp variableValue;
+ Symbol name;
+ SExp initValue = null;
+ HelpTopic helpinfo = null;
- // TODO: check for redifinition of variable (and warn)
-
- if (arguments.length() != 2)
- throw new InvalidArgumentQuantityException(2,
- arguments.length());
-
- // first argument: Symbol for variable name
- //if (!(arguments.car instanceof Symbol))
- // TODO: error: expected symbol
-
- variableName = (Symbol) arguments.car;
-
- // second argument: variable value
- arguments = arguments.cdr;
- assert (arguments != null);
-
- variableValue = arguments.car.eval(symbolTable);
-
- environment.globalSymbolTable.bind(variableName,
- new VariableEntry(variableValue));
-
- return variableValue;
- }
- };
-
- // -----
- // TRACE
- // -----
-
- SpecialFormEntry TRACE = new SpecialFormEntry(
- environment,
- new FormHelpTopic("TRACE",
- "enable trace information for a function",
- "(trace )",
- "Turn on trace information for a function.",
- "funcname", "the name of the function to trace"))
- {
- public SExp call(SymbolTable symbolTable, Seq arguments)
- throws LispException {
-
- if (arguments == null || arguments.car == null)
- return null;
+ if (arguments == null)
+ throw new InvalidArgumentQuantityException(0);
+ // first argument: variable name
if (!(arguments.car instanceof Symbol))
- throw new LispException(arguments.car.toString()
- + " is not a valid function name.");
+ throw new TypeException(arguments.car, Symbol.class);
- FormEntry fe = symbolTable.lookupFunction((Symbol) arguments.car);
+ name = (Symbol) arguments.car;
- if (fe instanceof FunctionEntry) ((FunctionEntry) fe).enableTrace(true);
- // TODO: else throw error
+ // if this variable is already defined, return without
+ // setting it
+ if (symbolTable.lookupVariable(name) != null)
+ return arguments.car;
- return null;
- }
- };
- // ---
- // SUM
- // ---
-
- SpecialFormEntry SUM = new SpecialFormEntry(
- environment,
- new FormHelpTopic("+", "Sum several expressions.",
- "(+ [ ... ])",
- "Compute the summation of the zero or more expressions passed"
- + "as arguments. In general, expressions are evaluated "
- + "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."))
- {
- public SExp call(SymbolTable symbolTable, Seq arguments)
- throws LispException {
-
- Num sum = new Num("0");
-
- // 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);
- }
- arguments = arguments.cdr;
- }
-
- return sum;
+ return DEFPARAMETER.call(symbolTable, arguments);
}
};
- SpecialFormEntry DIF = new SpecialFormEntry(
- environment,
- new FormHelpTopic("-", "Subtract several expressions.",
- "(- subtrahend) | (- ??? [... ])",
- "Perform a subtraction. If there is only one argument passed "
- + "then result = 0 - arg. If multiple arguments are passed"
- + " then result = arg_1 - arg_2 - ... - args_n. In "
- + "general, expressions are evaluated before being bound "
- + " to function parameters. The expressions passed to - "
- + "must evaluate to numbers.",
- "???", "In the case of multiple arguments to -, this is the "
- + "number from which the others are subtracted.",
- "subtrahend_1 ... subtrahend_n", "Subtrahends are numbers "
- + "subtracted from the ??? and may be any expression that "
- + "evaluates to a number."))
- {
- public SExp call(SymbolTable symbolTable, Seq arguments)
- throws LispException {
+ // ----------------
+ // ENABLE-DEBUG-AST
+ // ----------------
- Num difference = new Num("0");
-
- // need at least one argument
- if (arguments == null)
- throw new InvalidArgumentQuantityException(1, 0);
-
- // case: only one argument: 0 - arg
- try {
- difference = difference.subtract(
- (Num) arguments.car.eval(symbolTable));
- } catch (ClassCastException cce) {
- throw new TypeException(arguments.car, Num.class);
- }
-
- arguments = arguments.cdr;
- if (arguments == null) return difference;
-
- // case: (- x y1 ... yn)
- difference = difference.negate();
-
- // variable number of arguments [0..inf)
- while (arguments != null) {
- try {
- difference = difference.subtract(
- (Num) arguments.car.eval(symbolTable));
- } catch (ClassCastException cce) {
- throw new TypeException(arguments.car, Num.class);
- }
- arguments = arguments.cdr;
- }
-
- return difference;
- }
- };
-
- SpecialFormEntry MUL = new SpecialFormEntry(
- environment,
- new FormHelpTopic("*", "Multiply several expressions.",
- "(+ [ ... ])",
- "Compute the product of the zero or more expressions passed"
- + "as arguments. In general, expressions are evaluated "
- + "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."))
- {
- public SExp call(SymbolTable symbolTable, Seq arguments)
- throws LispException {
-
- Num product = new Num("1");
-
- // 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);
- }
- arguments = arguments.cdr;
- }
-
- return product;
- }
- };
-
- SpecialFormEntry DIV = new SpecialFormEntry(
- environment,
- new FormHelpTopic("/", "Divide several expressions.",
- "(- divisor) | (- quotient [... ])",
- "Perform division. If there is only one argument passed "
- + "then result = 1/ arg. If multiple arguments are passed"
- + " then result = arg_1 / arg_2 / ... / args_n, computed "
- + "from left to right. In general, expressions are "
- + "evaluated before being bound to function parameters. "
- + "The expressions passed to / must evaluate to numbers.",
- "quotient", "In the case of multiple arguments to /, this is "
- + "the number which is diveded.",
- "divisor_1 ... divisor_n", "Divisors are the numbers dividing "
- + "the quotient and may be any expression that evaluates "
- + "to a number."))
- {
- public SExp call(SymbolTable symbolTable, Seq arguments)
- throws LispException {
-
- Num dividend = new Num("1");
- Num firstArg;
-
- if (arguments == null)
- throw new InvalidArgumentQuantityException(1, 0);
-
- // case: only one argument: 1 / arg
- try { firstArg = (Num) arguments.car.eval(symbolTable); }
- catch (ClassCastException cce) {
- throw new TypeException(arguments.car, Num.class);
- }
-
- dividend = dividend.divideBy(firstArg);
-
- arguments = arguments.cdr;
- if (arguments == null) return dividend;
-
- // case: (/ x y1 ... yn)
- dividend = firstArg;
-
- // variable number of arguments [0..inf)
- while (arguments != null) {
- try {
- dividend = dividend.divideBy(
- (Num) arguments.car.eval(symbolTable));
- } catch (ClassCastException cce) {
- throw new TypeException(arguments.car, Num.class);
- }
- arguments = arguments.cdr;
- }
-
- return dividend;
- }
- };
-
- SpecialFormEntry ENABLEDEBUGAST = new SpecialFormEntry(
+ final SpecialFormEntry ENABLEDEBUGAST = new SpecialFormEntry(
environment,
new FormHelpTopic("ENABLE-DEBUG-AST",
"Enable debug information: abstract syntax tree.",
@@ -421,7 +409,7 @@ public abstract class SpecialFormEntry implements FormEntry {
throws LispException {
if (arguments == null) {
environment.dumpAST = true;
- return null;
+ return SExp.NIL;
}
SExp retVal = arguments.car.eval(symbolTable);
@@ -433,20 +421,473 @@ public abstract class SpecialFormEntry implements FormEntry {
}
};
- /*SpecialFormEntry LET = new SpecialFormEntry(environment) {
- public SExp call(SymbolTable table, Seq arguments) {
- // TODO
- }
- }*/
+ // ----
+ // HELP
+ // ----
+
+ final SpecialFormEntry HELP = new SpecialFormEntry(
+ 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"))
+ {
+ public SExp call(SymbolTable symbolTable, Seq arguments)
+ throws LispException {
+
+ HelpTopic topic = null;
+
+ // no arguments: print help for HELP
+ if (arguments == null)
+ return this.call(symbolTable,
+ new Seq(new Symbol("HELP"), null));
+
+ // too many arguments
+ if (arguments.length() > 1)
+ throw new InvalidArgumentQuantityException(1,
+ arguments.length());
+
+ // try to find the topic or function help
+ if (arguments.car instanceof Str) {
+ topic = HelpTopic.helpTopics.get(
+ ((Str) arguments.car).value);
+ } else if (arguments.car instanceof Symbol) {
+ try {
+ FormEntry fe = symbolTable.lookupFunction(
+ (Symbol) arguments.car);
+ topic = fe.helpinfo();
+ } catch (LispException le) { topic = null; }
+ }
+
+ // no topic found
+ if (topic == null) {
+ new PrintWriter(environment.getOutputStream(), true)
+ .println(
+ "No help information found for topic '"
+ + arguments.car.toString() + "'.");
+ return SExp.NIL;
+ }
+
+ topic.print(environment.getOutputStream());
+ return SExp.NIL;
+ }
+ };
+
+ // --
+ // IF
+ // --
+
+ final SpecialFormEntry IF = new SpecialFormEntry(
+ environment,
+ new FormHelpTopic("IF",
+ "conditional code execution",
+ "(if []) => result*",
+ "if allows the execution of a form to be dependent on a "
+ + "single test-form. First test-form is evaluated. If "
+ + "the result is true, then then-form is selected; "
+ + "otherwise else-form is selected. Whichever form is "
+ + "selected is then evaluated.",
+ "test-form", "a form.",
+ "then-form", "a form.",
+ "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."))
+ {
+ public SExp call(SymbolTable symbolTable, Seq arguments)
+ throws LispException {
+ if (arguments == null || arguments.length() < 2)
+ throw new InvalidArgumentQuantityException(
+ 2, arguments == null ? 0 : arguments.length());
+
+ // evaluate test form
+ SExp testResult = arguments.car.eval(symbolTable);
+
+ // advance to then-form
+ arguments = arguments.cdr;
+
+ // if false, advance to else-form
+ if (testResult == null) arguments = arguments.cdr;
+
+ if (arguments == null) return arguments;
+ return arguments.eval(symbolTable);
+ }
+ };
+
+ // ---
+ // LET
+ // ---
+
+ final SpecialFormEntry LET = new SpecialFormEntry(
+ environment,
+ new FormHelpTopic("LET", "create new lexical variable bindings",
+ "(let (([ ])*)