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 (([ ])*)
*) => ", + "let creates new variable bindings and executes a series of " + + "forms that use these bindings. let performs the " + + "bindings in parallel (as opposed to let* which does " + + "them sequentially). The form (let ((var1 init-form-1) " + + "(var2 init-form-2) ... (varm init-form-m)) form1 form2 " + + "... formn) first evaluates the expressions init-form-1, " + + "init-form-2, and so on, in that order, saving the " + + "resulting values. Then all of the variables varj are " + + "bound to the corresponding values; each binding is " + + "lexical. The expressions formk are then evaluated in " + + "order; the values of all but the last are discarded " + + "(that is, the body of a let is an implicit progn).", + "var", "a symbol", + "init-form", "a form", + "form", "a form", + "result", "the value returned by the last form")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + SymbolTable newScope; + Seq letBinding; + ArrayList symbols = new ArrayList(); + ArrayList values = new ArrayList(); + SExp retVal = SExp.NIL; + + if (arguments == null) + throw new InvalidArgumentQuantityException(0); + + if (!(arguments.car instanceof List)) + throw new LispException("Malformed LET bindings: " + + arguments.car.toString()); + + letBinding = ((List) arguments.car).seq; + + while (letBinding != null) { + + // each binding should be a list of a symbol and form + if (!(letBinding.car instanceof List)) + throw new LispException("Malformed LET bindings: " + + letBinding.car.toString()); + + // get the symbol + Seq binding = ((List) letBinding.car).seq; + if (binding == null) + throw new LispException(""); // TODO + + if (!(binding.car instanceof Symbol)) + throw new TypeException(binding.car, Symbol.class); + + symbols.add((Symbol) binding.car); + + // get and evaluate the value + binding = binding.cdr; + if (binding == null) values.add(SExp.NIL); + else values.add(binding.car.eval(symbolTable)); + + // next let binding + letBinding = letBinding.cdr; + } + + // perform actual binding in a new scope + newScope = new SymbolTable(symbolTable); + + for (int i = 0; i < symbols.size(); ++i) + newScope.bind(symbols.get(i), + new VariableEntry(values.get(i))); + + // evaluate all forms with the new scope + arguments = arguments.cdr; + + while (arguments != null) { + retVal = arguments.car.eval(newScope); + arguments = arguments.cdr; + } + + return retVal; + } + }; + + // ---- + // LET* + // ---- + + final SpecialFormEntry LET_STAR = new SpecialFormEntry( + environment, + new FormHelpTopic("LET*", "create new lexical variable bindings", + "(let (([ ])*) *) => ", + "let* creates new variable bindings and executes a series of " + + "forms that use these bindings. let* performs the " + + "bindings sequentially (as opposed to let which does " + + "them in parallel). The expression for the init-form of " + + "a var can refer to vars previously bound in the let*." + + "The form (let* ((var1 init-form-1) (var2 init-form-2)" + + " ... (varm init-form-m)) form1 form2 ... formn) first " + + "evaluates the expression init-form-1, then binds the " + + "variable var1 to that value; then it evaluates " + + "init-form-2 and binds var2, and so on. The expressions " + + "formj are then evaluated in order; the values of all " + + "but the last are discarded (that is, the body of let* " + + "is an implicit progn).", + "var", "a symbol", + "init-form", "a form", + "form", "a form", + "result", "the value returned by the last form")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + SymbolTable newScope; + Seq letBinding; + Symbol var; + SExp initvalue; + SExp retVal = SExp.NIL; + + if (arguments == null) + throw new InvalidArgumentQuantityException(0); + + if (!(arguments.car instanceof List)) + throw new LispException("Malformed LET bindings: " + + arguments.car.toString()); + + letBinding = ((List) arguments.car).seq; + + // perform actual binding in a new scope + newScope = new SymbolTable(symbolTable); + + while (letBinding != null) { + + // each binding should be a list of a symbol and form + if (!(letBinding.car instanceof List)) + throw new LispException("Malformed LET bindings: " + + letBinding.car.toString()); + + // get the symbol + Seq binding = ((List) letBinding.car).seq; + if (binding == null) + throw new LispException(""); // TODO + + if (!(binding.car instanceof Symbol)) + throw new TypeException(binding.car, Symbol.class); + + var = (Symbol) binding.car; + + // get and evaluate the value + // include already bound variables from the let* in the + // scope for this evaluation + binding = binding.cdr; + if (binding == null) initvalue = SExp.NIL; + else initvalue = binding.car.eval(newScope); + + newScope.bind(var, new VariableEntry(initvalue)); + + // next let binding + letBinding = letBinding.cdr; + } + + // evaluate all forms with the new scope + arguments = arguments.cdr; + + while (arguments != null) { + retVal = arguments.car.eval(newScope); + arguments = arguments.cdr; + } + + return retVal; + } + }; + + // ---- + // LIST + // ---- + + final SpecialFormEntry LIST = new SpecialFormEntry( + environment, + new FormHelpTopic("LIST", "create a list", + "(list *) => list", + "list returns a list containing the supplied objects.", + "object", "an object.", + "list", "a list.")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + ArrayList values = new ArrayList(); + Seq retVal = null; + + if (arguments == null) return SExp.NIL; + + while(arguments != null) { + // eval and push the current object + values.add(arguments.car.eval(symbolTable)); + + // next object in argument list + arguments = arguments.cdr; + } + + for (int i = values.size(); i != 0;) + retVal = new Seq(values.get(--i), retVal); + + return new List(retVal); + } + }; + + // ----- + // QUOTE + // ----- + + final SpecialFormEntry QUOTE = new SpecialFormEntry( + environment, + new FormHelpTopic("QUOTE", "Return objects unevaluated.", + "(quote ) => ", + "The quote special operator just returns object. The " + + "consequences are undefined if literal objects (including " + + "quoted objects) are destructively modified. ", + "object", "an object; not evaluated.")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + if (arguments == null) + throw new InvalidArgumentQuantityException(1, 0); + + return arguments.car; + } + }; + + // ----- + // PROGN + // ----- + + final SpecialFormEntry PROGN = new SpecialFormEntry( + environment, + new FormHelpTopic("PROGN", + "evaluate forms in the order they are given", + "(progn *) => *", + "progn evaluates forms, in the order in which they are given. " + + "The values of each form but the last are discarded. If " + + "progn appears as a top level form, then all forms " + + "within that progn are considered by the compiler to be " + + "top level forms. ", + "form", "a list of forms", + "result", "the value of the last form")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + SExp result = SExp.NIL; + + // evaluate all forms, left to right + while (arguments != null) { + result = arguments.car.eval(symbolTable); + arguments = arguments.cdr; + } + + return result; + } + }; + + // ---- + // SETQ + // ---- + + final SpecialFormEntry SETQ = new SpecialFormEntry( + environment, + new FormHelpTopic("SETQ", "Assigns values to variables.", + "(setq [ ]*)", + "Assigns values to variables. (setq var1 form1 var2 " + + "form2 ...) is the simple variable assignment statement " + + "of Lisp. First form1 is evaluated and the result is " + + "stored in the variable var1, then form2 is evaluated " + + "and the result stored in var2, and so forth. setq may " + + "be used for assignment of both lexical and dynamic " + + "variables. If any var refers to a binding made by " + + "symbol-macrolet, then that var is treated as if setf " + + "(not setq) had been used. ", + "name", + "a symbol naming a variable other than a constant variable", + "form", "a form")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + Symbol variableName; + SExp variableValue = SExp.NIL; + + if (arguments.length() % 2 != 0) + throw new InvalidArgumentQuantityException( + "there must be an even number of arguments " + + "(name, value pairs)"); + + // treat each pair + while (arguments != null) { + + // first argument of pair: Symbol for variable name + if (!(arguments.car instanceof Symbol)) + throw new TypeException(arguments.car, Symbol.class); + + variableName = (Symbol) arguments.car; + + // TODO: check for redifinition of variable and warn or err + // if the variable is a constant + + // second argument: variable value + arguments = arguments.cdr; + assert (arguments != null); + + variableValue = arguments.car.eval(symbolTable); + + symbolTable.rebind(variableName, + new VariableEntry(variableValue)); + + arguments = arguments.cdr; + } + + return variableValue; + } + }; + + // ----- + // TRACE + // ----- + + final 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 SExp.NIL; + + if (!(arguments.car instanceof Symbol)) + throw new LispException(arguments.car.toString() + + " is not a valid function name."); + + FormEntry fe = symbolTable.lookupFunction((Symbol) arguments.car); + + if (fe instanceof FunctionEntry) ((FunctionEntry) fe).enableTrace(true); + // TODO: else throw error + + return SExp.NIL; + } + }; - environment.globalSymbolTable.bind(new Symbol("HELP"), HELP); - environment.globalSymbolTable.bind(new Symbol("DEFUN"), DEFUN); - environment.globalSymbolTable.bind(new Symbol("SETQ"), SETQ); - environment.globalSymbolTable.bind(new Symbol("TRACE"), TRACE); - environment.globalSymbolTable.bind(new Symbol("+"), SUM); environment.globalSymbolTable.bind(new Symbol("-"), DIF); - environment.globalSymbolTable.bind(new Symbol("*"), MUL); environment.globalSymbolTable.bind(new Symbol("/"), DIV); + environment.globalSymbolTable.bind(new Symbol("*"), MUL); + environment.globalSymbolTable.bind(new Symbol("+"), SUM); + environment.globalSymbolTable.bind(new Symbol("DEFUN"), DEFUN); + environment.globalSymbolTable.bind(new Symbol("DEFVAR"), DEFVAR); environment.globalSymbolTable.bind(new Symbol("ENABLE-DEBUG-AST"), ENABLEDEBUGAST); + environment.globalSymbolTable.bind(new Symbol("HELP"), HELP); + environment.globalSymbolTable.bind(new Symbol("IF"), IF); + environment.globalSymbolTable.bind(new Symbol("LET"), LET); + environment.globalSymbolTable.bind(new Symbol("LET*"), LET_STAR); + environment.globalSymbolTable.bind(new Symbol("LIST"), LIST); + environment.globalSymbolTable.bind(new Symbol("QUOTE"), QUOTE); + environment.globalSymbolTable.bind(new Symbol("PROGN"), PROGN); + environment.globalSymbolTable.bind(new Symbol("SETQ"), SETQ); + environment.globalSymbolTable.bind(new Symbol("TRACE"), TRACE); } } diff --git a/src/edu/utexas/cs345/jdblisp/Symbol.java b/src/edu/utexas/cs345/jdblisp/Symbol.java index 0f94931..6049eaf 100755 --- a/src/edu/utexas/cs345/jdblisp/Symbol.java +++ b/src/edu/utexas/cs345/jdblisp/Symbol.java @@ -6,9 +6,7 @@ package edu.utexas.cs345.jdblisp; public class Symbol implements SExp { public final String name; - public Symbol(String name) { - this.name = name; - } + public Symbol(String name) { this.name = name; } /** {@inheritdoc}*/ public SExp eval(SymbolTable table) throws LispException { diff --git a/src/edu/utexas/cs345/jdblisp/SymbolTable.java b/src/edu/utexas/cs345/jdblisp/SymbolTable.java index b837dbf..d5f3972 100755 --- a/src/edu/utexas/cs345/jdblisp/SymbolTable.java +++ b/src/edu/utexas/cs345/jdblisp/SymbolTable.java @@ -42,6 +42,38 @@ public class SymbolTable { return s; } + public Symbol rebind(Symbol s, VariableEntry v) throws LispException { + + // look up the variable in the current table + VariableEntry curVal = variables.get(s); + + // not present + if (curVal == null) { + + // no parent table, variable is undefine + if (enclosingTable == null) + throw new LispException("No such variable defined: " + s.name); + + // check parent table + return enclosingTable.rebind(s, v); + + } + + // it is present + else { + + // if this variable is constant, err + if (curVal.isConstant) + throw new LispException("Cannot set variable " + s.name + + ": the variable is constant."); + + // if not, set the new value + variables.put(s, v); + } + + return s; + } + public FormEntry lookupFunction(Symbol s) throws LispException { FormEntry fe = functions.get(s); diff --git a/src/edu/utexas/cs345/jdblisp/VariableEntry.java b/src/edu/utexas/cs345/jdblisp/VariableEntry.java index b70ea2b..1224b79 100755 --- a/src/edu/utexas/cs345/jdblisp/VariableEntry.java +++ b/src/edu/utexas/cs345/jdblisp/VariableEntry.java @@ -7,6 +7,19 @@ package edu.utexas.cs345.jdblisp; public class VariableEntry { public final SExp value; + public final boolean isConstant; + public final HelpTopic helpinfo; - public VariableEntry(SExp value) { this.value = value; } + public VariableEntry(SExp value) { this(value, false); } + + public VariableEntry(SExp value, boolean constant) { + this(value, constant, null); + } + + public VariableEntry(SExp value, boolean constant, HelpTopic helpinfo) { + this.value = value; + this.isConstant = constant; + this.helpinfo = helpinfo; + } + } diff --git a/src/edu/utexas/cs345/jdblisp/differences-from-cl.txt b/src/edu/utexas/cs345/jdblisp/differences-from-cl.txt new file mode 100644 index 0000000..f0e1c56 --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/differences-from-cl.txt @@ -0,0 +1,4 @@ +Special Forms: +-------------- + + LET diff --git a/src/edu/utexas/cs345/jdblisp/parser/Parser.java b/src/edu/utexas/cs345/jdblisp/parser/Parser.java index 1b6f994..116f7c6 100644 --- a/src/edu/utexas/cs345/jdblisp/parser/Parser.java +++ b/src/edu/utexas/cs345/jdblisp/parser/Parser.java @@ -4,15 +4,18 @@ package edu.utexas.cs345.jdblisp.parser; import edu.utexas.cs345.jdblisp.*; public class Parser implements ParserConstants { + private static Symbol QUOTE_SYMB = new Symbol("QUOTE"); + /** * SExp -> Symbol | Str | Num | List | "'" SExp */ static final public SExp sexp() throws ParseException { SExp s = null; Token t; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case KEYWORD: case SYMB: - t = jj_consume_token(SYMB); - {if (true) return new Symbol(t.image.toUpperCase());} + s = symbol(); + {if (true) return s;} break; case STRG: t = jj_consume_token(STRG); @@ -22,6 +25,11 @@ public class Parser implements ParserConstants { t = jj_consume_token(NUMB); {if (true) return new Num(t.image);} break; + case QUOTE: + t = jj_consume_token(QUOTE); + s = sexp(); + {if (true) return new List(new Seq(QUOTE_SYMB, s));} + break; case LPAREN: case NIL: s = list(); @@ -67,18 +75,43 @@ public class Parser implements ParserConstants { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case LPAREN: case NIL: + case QUOTE: + case KEYWORD: case NUMB: case STRG: case SYMB: se = sexp(); sq = seq(); - {if (true) return new Seq(se, sq);} + {if (true) return new Seq(se, sq);} break; default: jj_la1[2] = jj_gen; ; } - {if (true) return null;} + {if (true) return null;} + throw new Error("Missing return statement in function"); + } + +/** + * Symbol -> Symbol | Keyword Symbol + */ + static final public Symbol symbol() throws ParseException { + Token t; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SYMB: + t = jj_consume_token(SYMB); + {if (true) return new Symbol(t.image.toUpperCase());} + break; + case KEYWORD: + jj_consume_token(KEYWORD); + t = jj_consume_token(SYMB); + {if (true) return new Keyword(t.image.toUpperCase());} + break; + default: + jj_la1[3] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } throw new Error("Missing return statement in function"); } @@ -92,13 +125,13 @@ public class Parser implements ParserConstants { static public Token jj_nt; static private int jj_ntk; static private int jj_gen; - static final private int[] jj_la1 = new int[3]; + static final private int[] jj_la1 = new int[4]; static private int[] jj_la1_0; static { jj_la1_init_0(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x1e80,0x280,0x1e80,}; + jj_la1_0 = new int[] {0x7e80,0x280,0x7e80,0x4800,}; } /** Constructor with InputStream. */ @@ -119,7 +152,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 3; i++) jj_la1[i] = -1; + for (int i = 0; i < 4; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -133,7 +166,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 3; i++) jj_la1[i] = -1; + for (int i = 0; i < 4; i++) jj_la1[i] = -1; } /** Constructor. */ @@ -150,7 +183,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 3; i++) jj_la1[i] = -1; + for (int i = 0; i < 4; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -160,7 +193,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 3; i++) jj_la1[i] = -1; + for (int i = 0; i < 4; i++) jj_la1[i] = -1; } /** Constructor with generated Token Manager. */ @@ -176,7 +209,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 3; i++) jj_la1[i] = -1; + for (int i = 0; i < 4; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -185,7 +218,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 3; i++) jj_la1[i] = -1; + for (int i = 0; i < 4; i++) jj_la1[i] = -1; } static private Token jj_consume_token(int kind) throws ParseException { @@ -236,12 +269,12 @@ public class Parser implements ParserConstants { /** Generate ParseException. */ static public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[13]; + boolean[] la1tokens = new boolean[15]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1<", + "\"\\\'\"", + "\":\"", "", "", "", diff --git a/src/edu/utexas/cs345/jdblisp/parser/ParserTokenManager.java b/src/edu/utexas/cs345/jdblisp/parser/ParserTokenManager.java index 8141680..a58d29c 100644 --- a/src/edu/utexas/cs345/jdblisp/parser/ParserTokenManager.java +++ b/src/edu/utexas/cs345/jdblisp/parser/ParserTokenManager.java @@ -35,10 +35,14 @@ static private int jjMoveStringLiteralDfa0_0() case 10: jjmatchedKind = 4; return jjMoveStringLiteralDfa1_0(0x20L); + case 39: + return jjStopAtPos(0, 10); case 40: return jjStopAtPos(0, 7); case 41: return jjStopAtPos(0, 8); + case 58: + return jjStopAtPos(0, 11); case 59: return jjMoveStringLiteralDfa1_0(0x40L); default : @@ -103,6 +107,9 @@ static private int jjMoveStringLiteralDfa3_0(long old0, long active0) } return jjStartNfa_0(2, active0); } +static final long[] jjbitVec0 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; static private int jjMoveNfa_0(int startState, int curPos) { int startsAt = 0; @@ -124,18 +131,18 @@ static private int jjMoveNfa_0(int startState, int curPos) case 0: if ((0x3ff000000000000L & l) != 0L) { - if (kind > 10) - kind = 10; + if (kind > 12) + kind = 12; jjCheckNAddTwoStates(4, 5); } else if ((0x2000ac0000000000L & l) != 0L) { - if (kind > 12) - kind = 12; + if (kind > 14) + kind = 14; jjCheckNAddTwoStates(10, 11); } else if (curChar == 34) - jjAddStates(0, 1); + jjCheckNAddTwoStates(8, 9); if ((0x280000000000L & l) != 0L) jjCheckNAdd(4); break; @@ -146,8 +153,8 @@ static private int jjMoveNfa_0(int startState, int curPos) case 4: if ((0x3ff000000000000L & l) == 0L) break; - if (kind > 10) - kind = 10; + if (kind > 12) + kind = 12; jjCheckNAddTwoStates(4, 5); break; case 5: @@ -157,28 +164,32 @@ static private int jjMoveNfa_0(int startState, int curPos) case 6: if ((0x3ff000000000000L & l) == 0L) break; - if (kind > 10) - kind = 10; + if (kind > 12) + kind = 12; jjCheckNAdd(6); break; case 7: if (curChar == 34) - jjAddStates(0, 1); + jjCheckNAddTwoStates(8, 9); + break; + case 8: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddTwoStates(8, 9); break; case 9: - if (curChar == 34 && kind > 11) - kind = 11; + if (curChar == 34 && kind > 13) + kind = 13; break; case 10: if ((0x2000ac0000000000L & l) == 0L) break; - if (kind > 12) - kind = 12; + if (kind > 14) + kind = 14; jjCheckNAddTwoStates(10, 11); break; case 11: - if ((0x23ffac0000000000L & l) != 0L && kind > 12) - kind = 12; + if ((0x23ffac0000000000L & l) != 0L && kind > 14) + kind = 14; break; default : break; } @@ -194,8 +205,8 @@ static private int jjMoveNfa_0(int startState, int curPos) case 0: if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 12) - kind = 12; + if (kind > 14) + kind = 14; jjCheckNAddTwoStates(10, 11); } if ((0x400000004000L & l) != 0L) @@ -210,19 +221,18 @@ static private int jjMoveNfa_0(int startState, int curPos) kind = 9; break; case 8: - if ((0x7fffffe87fffffeL & l) != 0L) - jjAddStates(0, 1); + jjAddStates(0, 1); break; case 10: if ((0x7fffffe87fffffeL & l) == 0L) break; - if (kind > 12) - kind = 12; + if (kind > 14) + kind = 14; jjCheckNAddTwoStates(10, 11); break; case 11: - if ((0x7fffffe87fffffeL & l) != 0L && kind > 12) - kind = 12; + if ((0x7fffffe87fffffeL & l) != 0L && kind > 14) + kind = 14; break; default : break; } @@ -236,6 +246,10 @@ static private int jjMoveNfa_0(int startState, int curPos) { switch(jjstateSet[--i]) { + case 8: + if ((jjbitVec0[i2] & l2) != 0L) + jjAddStates(0, 1); + break; default : break; } } while(i != startsAt); @@ -259,14 +273,15 @@ static final int[] jjnextStates = { /** Token literal values. */ public static final String[] jjstrLiteralImages = { -"", null, null, null, null, null, null, "\50", "\51", null, null, null, null, }; +"", null, null, null, null, null, null, "\50", "\51", null, "\47", "\72", null, +null, null, }; /** Lexer state names. */ public static final String[] lexStateNames = { "DEFAULT", }; static final long[] jjtoToken = { - 0x1f81L, + 0x7f81L, }; static final long[] jjtoSkip = { 0x7eL, diff --git a/src/lisp-samples/cd-database.lisp b/src/lisp-samples/cd-database.lisp new file mode 100644 index 0000000..7be3aa2 --- /dev/null +++ b/src/lisp-samples/cd-database.lisp @@ -0,0 +1,7 @@ +(defun make-cd (title artist rating ripped) + (list title artist rating ripped)) + +(make-cd "Roses" "Kathy Mattea" 7 t) + +(defvar *db* nil) + diff --git a/todo.txt b/todo.txt index 6d081fc..175e5b1 100644 --- a/todo.txt +++ b/todo.txt @@ -1,3 +1,30 @@ -Implement FORMAT -Implement READ and PRINT -Implement IF +Outstanding +----------- + + - Implement FORMAT + - Implement lambdas + - Implement LIST* + - Implement macros + - Implement packages + - Implement READ and PRINT + - Redefine DEFUN as a macro + +Partially Done +-------------- + + P Implement keywords + +Done +---- + + D Add ' notation for quote + D Define NIL + D Define T + D Implement DEFPARAMETER + D Implement DEFVAR + D Implement IF + D Implement LET + D Implement LET* + D Implement LIST + D Implement PROGN + D Implement QUOTE