diff --git a/build-common-versioning.xml b/build-common-versioning.xml index 2f39d52..cda1f29 100755 --- a/build-common-versioning.xml +++ b/build-common-versioning.xml @@ -1,28 +1,29 @@ - + + - + - + - + - + - + diff --git a/build.xml b/build.xml index ffff82c..728878c 100755 --- a/build.xml +++ b/build.xml @@ -12,7 +12,7 @@ - + @@ -35,20 +35,38 @@ - + - - + + + + + + + + + + + + + + diff --git a/project.properties b/project.properties index f8fa2f6..1b2dac9 100755 --- a/project.properties +++ b/project.properties @@ -1,7 +1,12 @@ -src.dir=src +#Tue Nov 24 11:54:14 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=6 dist.dir=dist +dist.jar=${dist.dir}/JCLisp-${application.version}.jar 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 +build.classes.dir=${build.dir}/classes +application.version=0.1.0 diff --git a/src/edu/utexas/cs345/jdblisp/Cons.java b/src/edu/utexas/cs345/jdblisp/Cons.java new file mode 100644 index 0000000..f950903 --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/Cons.java @@ -0,0 +1,46 @@ +package edu.utexas.cs345.jdblisp; + +/** + * Cons + * @author Jonathan Bernard (jdbernard@gmail.com) + */ +public class Cons extends Seq { + + public final SExp cdr; + + public Cons(SExp car, SExp cdr) { + super(car, (cdr == null || + cdr == SExp.NIL || + !(cdr instanceof Seq) ? + null : (Seq) cdr)); + this.cdr = (cdr == SExp.NIL ? null : cdr); + } + + public Cons(SExp car, List list) { + super(car, list.seq); + this.cdr = list.seq; + } + + @Override + public String display(String offset) { + if (this.cdr == super.cdr) + return super.display(offset); + + StringBuilder sb = new StringBuilder(); + sb.append(offset); + sb.append("Cons: \n"); + + sb.append(car.display(offset + " ")); + + if (cdr != null) sb.append(cdr.display(offset)); + + return sb.toString(); + } + + @Override + public String toString() { + if (this.cdr == super.cdr) return super.toString(); + + else return car.toString() + " . " + cdr.toString(); + } +} diff --git a/src/edu/utexas/cs345/jdblisp/HelpTopic.java b/src/edu/utexas/cs345/jdblisp/HelpTopic.java index 58d4adf..fb06566 100644 --- a/src/edu/utexas/cs345/jdblisp/HelpTopic.java +++ b/src/edu/utexas/cs345/jdblisp/HelpTopic.java @@ -94,7 +94,7 @@ public class HelpTopic { } // any left over, it will fit on one line - if (i - lineStartIdx > 1) { + if (i - lineStartIdx > 0) { String lastLine = message.substring(lineStartIdx); printer.print(lastLine); curLineLength += lastLine.length(); diff --git a/src/edu/utexas/cs345/jdblisp/List.java b/src/edu/utexas/cs345/jdblisp/List.java index 8363826..09015c0 100755 --- a/src/edu/utexas/cs345/jdblisp/List.java +++ b/src/edu/utexas/cs345/jdblisp/List.java @@ -30,7 +30,12 @@ public class List implements SExp { // look up in the symbol table FormEntry functionEntry = table.lookupFunction((Symbol) seq.car); - // call function + // throw an eror if it is not defined + if (functionEntry == null) + throw new LispException("Undefined function " + + ((Symbol) seq.car).name); + + // call function if it is return functionEntry.call(table, seq.cdr); } diff --git a/src/edu/utexas/cs345/jdblisp/Seq.java b/src/edu/utexas/cs345/jdblisp/Seq.java index 5f7fdd9..2239416 100755 --- a/src/edu/utexas/cs345/jdblisp/Seq.java +++ b/src/edu/utexas/cs345/jdblisp/Seq.java @@ -35,6 +35,7 @@ public class Seq implements SExp { return 1 + cdr.length(); } + @Override public String display(String offset) { StringBuilder sb = new StringBuilder(); sb.append(offset); diff --git a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java index 8a47710..69a6502 100755 --- a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java +++ b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java @@ -222,6 +222,41 @@ public abstract class SpecialFormEntry implements FormEntry { } }; + // ---- + // CONS + // ---- + + SpecialFormEntry CONS = new SpecialFormEntry( + 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")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + SExp object1, object2; + Cons cons; + + if (arguments.length() != 2) + throw new InvalidArgumentQuantityException(2, + arguments.length()); + + // get the two objects + object1 = arguments.car.eval(symbolTable); + object2 = arguments.cdr.car.eval(symbolTable); + + if (object2 instanceof List) cons = new Cons(object1, (List) object2); + else cons = new Cons(object1, object2); + + return new List(cons); + } + }; + // ----- // DEFUN // ----- @@ -326,17 +361,18 @@ public abstract class SpecialFormEntry implements FormEntry { // second argument: initial value arguments = arguments.cdr; - if (arguments != null) + 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); + // third 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); + helpinfo = new HelpTopic(name.toString(), "variable", + ((Str) arguments.car).value); + } } symbolTable.bind(name, @@ -421,6 +457,69 @@ public abstract class SpecialFormEntry implements FormEntry { } }; + // ---- + // GETF + // ---- + + SpecialFormEntry GETF = new SpecialFormEntry( + environment, + new FormHelpTopic("GETF", "", + "(getf []) => ", + "getf finds a property on the plist whose property indicator " + + "is identical to indicator, and returns its " + + "corresponding property value. If there are multiple " + + "properties with that property indicator, getf uses the " + + "first such property. If there is no property with that " + + "property indicator, default is returned.", + "plist", "a property list.", + "indicator", "an object", + "default", "an object. The default is NIL", + "value", "an object")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + SExp plistEval; + Seq plistSeq; + SExp indicator; + SExp retVal = SExp.NIL; + + // check number of arguments + if (arguments.length() < 2) + throw new InvalidArgumentQuantityException( + "form requires at least 2 arguments."); + + // first argument: property list + plistEval = arguments.car.eval(symbolTable); + if (!(plistEval instanceof List)) + throw new TypeException(arguments.car, List.class); + + plistSeq = ((List) plistEval).seq; + + // second argument: indicator + arguments = arguments.cdr; + indicator = arguments.car.eval(symbolTable); + + // third argument: default value + arguments = arguments.cdr; + if (arguments != null) + retVal = arguments.car.eval(symbolTable); + + while(plistSeq != null) { + + // check this value for equality + if (plistSeq.car.equals(indicator)) + if (plistSeq.cdr != null) + return plistSeq.cdr.car; + + // advance to the next pair (or terminate) + plistSeq = (plistSeq.cdr == null ? null : plistSeq.cdr.cdr); + } + + return retVal; + } + }; + // ---- // HELP // ---- @@ -429,48 +528,45 @@ public abstract class SpecialFormEntry implements FormEntry { environment, new FormHelpTopic("HELP", "Display online help information for a topic.", - "(help )", + "(help [*])", null, "topic", - "either a string representing the topic to lookup or a symbol")) + "either a string representing the topic to lookup or a symbol")) { public SExp call(SymbolTable symbolTable, Seq arguments) throws LispException { - HelpTopic topic = null; + ArrayList topics = new ArrayList(); // 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()); + while (arguments != null) { + // try to find the topic or function help + if (arguments.car instanceof Str) { + topics.add(HelpTopic.helpTopics.get( + ((Str) arguments.car).value)); + } else if (arguments.car instanceof Symbol) { - // 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 { + // lookup help for funtion FormEntry fe = symbolTable.lookupFunction( (Symbol) arguments.car); - topic = fe.helpinfo(); - } catch (LispException le) { topic = null; } + if (fe != null) topics.add(fe.helpinfo()); + + // lookup help for variable + VariableEntry ve = symbolTable.lookupVariable( + (Symbol) arguments.car); + if (ve != null) topics.add(ve.helpinfo); + } + + 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 SExp.NIL; - } + for (HelpTopic topic : topics) + topic.print(environment.getOutputStream()); - topic.print(environment.getOutputStream()); return SExp.NIL; } }; @@ -866,7 +962,13 @@ public abstract class SpecialFormEntry implements FormEntry { FormEntry fe = symbolTable.lookupFunction((Symbol) arguments.car); - if (fe instanceof FunctionEntry) ((FunctionEntry) fe).enableTrace(true); + if (fe == null) + throw new UndefinedFunctionException( + ((Symbol) arguments.car)); + + if (fe instanceof FunctionEntry) + ((FunctionEntry) fe).enableTrace(true); + // TODO: else throw error return SExp.NIL; @@ -877,9 +979,12 @@ public abstract class SpecialFormEntry implements FormEntry { environment.globalSymbolTable.bind(new Symbol("/"), DIV); environment.globalSymbolTable.bind(new Symbol("*"), MUL); environment.globalSymbolTable.bind(new Symbol("+"), SUM); + environment.globalSymbolTable.bind(new Symbol("CONS"), CONS); environment.globalSymbolTable.bind(new Symbol("DEFUN"), DEFUN); + environment.globalSymbolTable.bind(new Symbol("DEFPARAMETER"), DEFPARAMETER); environment.globalSymbolTable.bind(new Symbol("DEFVAR"), DEFVAR); environment.globalSymbolTable.bind(new Symbol("ENABLE-DEBUG-AST"), ENABLEDEBUGAST); + environment.globalSymbolTable.bind(new Symbol("GETF"), GETF); environment.globalSymbolTable.bind(new Symbol("HELP"), HELP); environment.globalSymbolTable.bind(new Symbol("IF"), IF); environment.globalSymbolTable.bind(new Symbol("LET"), LET); diff --git a/src/edu/utexas/cs345/jdblisp/Symbol.java b/src/edu/utexas/cs345/jdblisp/Symbol.java index 6049eaf..f5be9b5 100755 --- a/src/edu/utexas/cs345/jdblisp/Symbol.java +++ b/src/edu/utexas/cs345/jdblisp/Symbol.java @@ -10,7 +10,12 @@ public class Symbol implements SExp { /** {@inheritdoc}*/ public SExp eval(SymbolTable table) throws LispException { + // lookup value in the symbol table VariableEntry ve = table.lookupVariable(this); + + // err if not defined + if (ve == null) throw new UndefinedVariableException(this); + return ve.value; } diff --git a/src/edu/utexas/cs345/jdblisp/SymbolTable.java b/src/edu/utexas/cs345/jdblisp/SymbolTable.java index d5f3972..353b0ee 100755 --- a/src/edu/utexas/cs345/jdblisp/SymbolTable.java +++ b/src/edu/utexas/cs345/jdblisp/SymbolTable.java @@ -81,8 +81,7 @@ public class SymbolTable { if (fe != null) return fe; // did not find function, and there is no outer scope - if (enclosingTable == null) - throw new LispException("Undefined function " + s.toString()); + if (enclosingTable == null) return null; // search outer scope return enclosingTable.lookupFunction(s); @@ -95,8 +94,7 @@ public class SymbolTable { if (ve != null) return ve; // did not find variable entry and there is no outer scope - if (enclosingTable == null) - throw new LispException("Undefined variable " + s.toString()); + if (enclosingTable == null) return null; // search outer scope return enclosingTable.lookupVariable(s); diff --git a/src/edu/utexas/cs345/jdblisp/UndefinedFunctionException.java b/src/edu/utexas/cs345/jdblisp/UndefinedFunctionException.java new file mode 100644 index 0000000..43fb5e4 --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/UndefinedFunctionException.java @@ -0,0 +1,15 @@ +package edu.utexas.cs345.jdblisp; + +/** + * UndefinedFunctionException + * @author Jonathan Bernard (jdbernard@gmail.com) + */ +public class UndefinedFunctionException extends LispException { + + Symbol symbol; + + public UndefinedFunctionException(Symbol symbol) { + super("Undefined function: " + symbol.toString()); + this.symbol = symbol; + } +} diff --git a/src/edu/utexas/cs345/jdblisp/UndefinedVariableException.java b/src/edu/utexas/cs345/jdblisp/UndefinedVariableException.java new file mode 100644 index 0000000..f565de2 --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/UndefinedVariableException.java @@ -0,0 +1,15 @@ +package edu.utexas.cs345.jdblisp; + +/** + * UndefinedVariableException + * @author Jonathan Bernard + */ +public class UndefinedVariableException extends LispException { + + public final Symbol symbol; + + public UndefinedVariableException(Symbol symbol) { + super("Undefined variable: " + symbol.toString()); + this.symbol = symbol; + } +} diff --git a/src/lisp-samples/cd-database.lisp b/src/lisp-samples/cd-database.lisp index 7be3aa2..558bb3c 100644 --- a/src/lisp-samples/cd-database.lisp +++ b/src/lisp-samples/cd-database.lisp @@ -1,7 +1,10 @@ (defun make-cd (title artist rating ripped) - (list title artist rating ripped)) - -(make-cd "Roses" "Kathy Mattea" 7 t) + (list :title title :artist artist :rating rating :ripped ripped)) (defvar *db* nil) +(defun add-record (cd) (setq *db* (cons cd *db*))) + +(add-record (make-cd "Roses" "Kathy Mattea" 7 t)) +(add-record (make-cd "Fly" "Dixie Chicks" 8 t)) +(add-record (make-cd "Home" "Dixie Chicks" 9 t)) diff --git a/todo.txt b/todo.txt index 175e5b1..9bb204b 100644 --- a/todo.txt +++ b/todo.txt @@ -2,12 +2,20 @@ Outstanding ----------- - Implement FORMAT + - Implement GETF - Implement lambdas - Implement LIST* - Implement macros - Implement packages - Implement READ and PRINT + - Implement SETF - Redefine DEFUN as a macro + - Help for property list + - Help for keyword + - Help for symbol + - Help for sexp + - Help for number + - Help for string Partially Done --------------