From c63b92bf41fcd1db36ff3391ef975f2a029afbf4 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Wed, 25 Nov 2009 13:38:12 -0600 Subject: [PATCH] Functions as first-class objects, lambdas implemented. Added several new SFs --- project.properties | 6 +- src/edu/utexas/cs345/jdblisp/FormEntry.java | 18 +- .../utexas/cs345/jdblisp/FunctionEntry.java | 15 +- src/edu/utexas/cs345/jdblisp/Lambda.java | 49 ++++ src/edu/utexas/cs345/jdblisp/List.java | 10 +- src/edu/utexas/cs345/jdblisp/Parser.jj | 5 +- .../cs345/jdblisp/SpecialFormEntry.java | 223 +++++++++++++++--- src/edu/utexas/cs345/jdblisp/SymbolTable.java | 1 + todo.txt | 9 +- 9 files changed, 291 insertions(+), 45 deletions(-) create mode 100644 src/edu/utexas/cs345/jdblisp/Lambda.java diff --git a/project.properties b/project.properties index 1b2dac9..af3e9ad 100755 --- a/project.properties +++ b/project.properties @@ -1,12 +1,12 @@ -#Tue Nov 24 11:54:14 CST 2009 +#Tue Nov 24 14:17:29 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 +build.number=19 dist.dir=dist dist.jar=${dist.dir}/JCLisp-${application.version}.jar lib.dir=lib -grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj build.classes.dir=${build.dir}/classes +grammar.file=${src.dir}/edu/utexas/cs345/jdblisp/Parser.jj application.version=0.1.0 diff --git a/src/edu/utexas/cs345/jdblisp/FormEntry.java b/src/edu/utexas/cs345/jdblisp/FormEntry.java index 00fa41f..bfaacca 100644 --- a/src/edu/utexas/cs345/jdblisp/FormEntry.java +++ b/src/edu/utexas/cs345/jdblisp/FormEntry.java @@ -4,7 +4,19 @@ package edu.utexas.cs345.jdblisp; * FormEntry * @author Jonathan Bernard(jdbernard@gmail.com) */ -public interface FormEntry { - SExp call(SymbolTable table, Seq arguments) throws LispException; - HelpTopic helpinfo(); +public abstract class FormEntry implements SExp { + + public final Symbol name; + public HelpTopic helpinfo; + + public FormEntry(Symbol name, HelpTopic helpinfo) { + this.name = name; + this.helpinfo = helpinfo; + } + + public abstract SExp call(SymbolTable table, Seq arguments) throws LispException; + public abstract String display(String offset); + + public SExp eval(SymbolTable symbolTable) { return this; } + } diff --git a/src/edu/utexas/cs345/jdblisp/FunctionEntry.java b/src/edu/utexas/cs345/jdblisp/FunctionEntry.java index e3a1f1b..816e674 100755 --- a/src/edu/utexas/cs345/jdblisp/FunctionEntry.java +++ b/src/edu/utexas/cs345/jdblisp/FunctionEntry.java @@ -4,10 +4,8 @@ package edu.utexas.cs345.jdblisp; * FunctionEntry * @author Jonathan Bernard (jdbernard@gmail.com) */ -public class FunctionEntry implements FormEntry { +public class FunctionEntry extends FormEntry { - public final HelpTopic helpinfo; - public final Symbol name; protected final Symbol[] parameters; protected final SExp body; @@ -16,6 +14,7 @@ public class FunctionEntry implements FormEntry { //private Logger traceLog = Logger.getLogger(getClass()); public FunctionEntry(Symbol name, Symbol[] parameters, SExp body) { + super(name, null); // build invocation help string String invokation = "(" + name.name; @@ -26,7 +25,6 @@ public class FunctionEntry implements FormEntry { // build help topic FormHelpTopic helpinfo = new FormHelpTopic(name.name, null, invokation, bodyInfo); - this.name = name; this.parameters = parameters; this.body = body; this.helpinfo = helpinfo; @@ -34,16 +32,21 @@ public class FunctionEntry implements FormEntry { public FunctionEntry(Symbol name, Symbol[] parameters, SExp body, HelpTopic helpinfo) { - this.name = name; + super(name, helpinfo); this.parameters = parameters; this.body = body; - this.helpinfo = helpinfo; } public boolean isTraceEnabled() { return traceEnabled ;} public void enableTrace(boolean enable) { this.traceEnabled = enable; } + public String display(String offset) { + return offset + "Function: " + name.toString(); + } + + public String toString() { return ""; } + public SExp call(SymbolTable symbolTable, Seq arguments) throws LispException { String traceString = null; diff --git a/src/edu/utexas/cs345/jdblisp/Lambda.java b/src/edu/utexas/cs345/jdblisp/Lambda.java new file mode 100644 index 0000000..3bdb4fa --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/Lambda.java @@ -0,0 +1,49 @@ +package edu.utexas.cs345.jdblisp; + +/** + * Lambda + */ +public class Lambda extends FunctionEntry { + + private SymbolTable closure; + + public Lambda(Symbol[] parameters, SExp body, SymbolTable closure) { + super(new Symbol(""), parameters, body); + + this.closure = closure; + } + + public Lambda(Symbol[] parameters, SExp body, SymbolTable closure, + HelpTopic helpinfo) { + super(new Symbol(""), parameters, body, helpinfo); + + this.closure = closure; + } + + public String display(String offset) { + StringBuilder sb = new StringBuilder(); + sb.append(offset).append("Lambda : \n"); + + for (Symbol param : parameters) + sb.append(" ").append(offset).append("Parameter: \n") + .append(param.display(offset + " ")); + + return sb.toString(); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(""); + + return sb.toString(); + } + + @Override + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + return super.call(closure, arguments); + } +} diff --git a/src/edu/utexas/cs345/jdblisp/List.java b/src/edu/utexas/cs345/jdblisp/List.java index 09015c0..d0b0648 100755 --- a/src/edu/utexas/cs345/jdblisp/List.java +++ b/src/edu/utexas/cs345/jdblisp/List.java @@ -40,7 +40,15 @@ public class List implements SExp { } - return SExp.NIL; + // if the car is not a symbol, it had better be a lambda + SExp evaluated = seq.car.eval(table); + if (evaluated instanceof Lambda) { + return ((Lambda) evaluated).call(table, seq.cdr); + } + + // not a function call and not a lambda, error + throw new TypeException(seq.car, Lambda.class); + } public String display(String offset) { diff --git a/src/edu/utexas/cs345/jdblisp/Parser.jj b/src/edu/utexas/cs345/jdblisp/Parser.jj index 2cc3e2c..353fcb4 100755 --- a/src/edu/utexas/cs345/jdblisp/Parser.jj +++ b/src/edu/utexas/cs345/jdblisp/Parser.jj @@ -12,6 +12,7 @@ import edu.utexas.cs345.jdblisp.*; public class Parser { private static Symbol QUOTE_SYMB = new Symbol("QUOTE"); + private static Symbol FUNCTION_SYMB = new Symbol("FUNCTION"); } PARSER_END(Parser) @@ -30,6 +31,7 @@ TOKEN : /* PUNCTUATION */ | < RPAREN: ")" > | < NIL: (["N","n"]["I","i"]["L","l"])> | < QUOTE: "'" > +| < FUNCTION: "#'" > | < KEYWORD: ":" > } @@ -43,7 +45,7 @@ TOKEN : /* LITERALS & SYMBOLS */ } /** - * SExp -> Symbol | Str | Num | List | "'" SExp + * SExp -> Symbol | Str | Num | List */ SExp sexp(): { SExp s = null; Token t; @@ -52,6 +54,7 @@ SExp sexp(): | t = { return new Str(t.image); } | t = { return new Num(t.image); } | t = s = sexp() { return new List(new Seq(QUOTE_SYMB, s)); } + | t = s = sexp() { return new List(new Seq(FUNCTION_SYMB, s)); } | s = list() { return s; } } diff --git a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java index 69a6502..537d9ca 100755 --- a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java +++ b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java @@ -7,22 +7,27 @@ import java.util.ArrayList; * SpecialFormEntry * @author Jonathan Bernard (jdbernard@gmail.com) */ -public abstract class SpecialFormEntry implements FormEntry { - - public final HelpTopic helpinfo; +public abstract class SpecialFormEntry extends FormEntry { protected LISPRuntime environment; - public SpecialFormEntry(LISPRuntime environment, HelpTopic helpinfo) { + public SpecialFormEntry(Symbol name, LISPRuntime environment, HelpTopic helpinfo) { + super(name, helpinfo); this.environment = environment; - this.helpinfo = helpinfo; } - public HelpTopic helpinfo() { return helpinfo; } - public abstract SExp call(SymbolTable symbolTable, Seq arguments) throws LispException; + @Override + public String display(String offset) { + return offset + "Special Form Entry: " + name.toString(); + } + + @Override + public String toString() { + return ""; + } // ------------------------ // SPECIAL FORMS DEFINITION // ------------------------ @@ -40,6 +45,7 @@ public abstract class SpecialFormEntry implements FormEntry { // --- final SpecialFormEntry DIV = new SpecialFormEntry( + new Symbol("/"), environment, new FormHelpTopic("/", "Divide several expressions.", "(- divisor) | (- dividend [... ])", @@ -99,6 +105,7 @@ public abstract class SpecialFormEntry implements FormEntry { // --- final SpecialFormEntry DIF = new SpecialFormEntry( + new Symbol("-"), environment, new FormHelpTopic("-", "Subtract several expressions.", "(- subtrahend) | (- [... ])", @@ -158,6 +165,7 @@ public abstract class SpecialFormEntry implements FormEntry { // --- final SpecialFormEntry MUL = new SpecialFormEntry( + new Symbol("*"), environment, new FormHelpTopic("*", "Multiply several expressions.", "(+ [ ... ])", @@ -193,6 +201,7 @@ public abstract class SpecialFormEntry implements FormEntry { // --- final SpecialFormEntry SUM = new SpecialFormEntry( + new Symbol("+"), environment, new FormHelpTopic("+", "Sum several expressions.", "(+ [ ... ])", @@ -222,11 +231,18 @@ public abstract class SpecialFormEntry implements FormEntry { } }; + // --- + // CAR + // --- + + // TODO + // ---- // CONS // ---- - SpecialFormEntry CONS = new SpecialFormEntry( + final SpecialFormEntry CONS = new SpecialFormEntry( + new Symbol("CONS"), environment, new FormHelpTopic("CONS", "create a cons", "(cons ) => ", @@ -262,6 +278,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ----- final SpecialFormEntry DEFUN = new SpecialFormEntry( + new Symbol("DEFUN"), environment, new FormHelpTopic("DEFUN", "Define a (global) function.", "(defun () )", @@ -330,6 +347,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ------------ final SpecialFormEntry DEFPARAMETER = new SpecialFormEntry( + new Symbol("DEFPARAMETER"), environment, new FormHelpTopic("DEFPARAMETER", "define a dynamic variable", "(defparameter []) => ", @@ -388,6 +406,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ------ final SpecialFormEntry DEFVAR = new SpecialFormEntry( + new Symbol("DEFVAR"), environment, new FormHelpTopic("DEFVAR", "define a variable", "(defvar [ []]) => ", @@ -431,6 +450,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ---------------- final SpecialFormEntry ENABLEDEBUGAST = new SpecialFormEntry( + new Symbol("ENABLE-DEBUG-AST"), environment, new FormHelpTopic("ENABLE-DEBUG-AST", "Enable debug information: abstract syntax tree.", @@ -457,11 +477,93 @@ public abstract class SpecialFormEntry implements FormEntry { } }; + // -------- + // FUNCTION + // -------- + + final SpecialFormEntry FUNCTION = new SpecialFormEntry( + new Symbol("FUNCTION"), + environment, + new FormHelpTopic("FUNCTION", "retrieve a function", + "(function ) => ", + "function returns the function specified by the symbol passed " + + " as an argument. #'funcname is equivalent to (function " + + " funcname).", + "func-name", "a symbol naming a function", + "function", "a function")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + FormEntry fe = null; + + if (arguments == null || arguments.length() != 1) + throw new InvalidArgumentQuantityException(1); + + if (!(arguments.car instanceof Symbol)) + throw new TypeException(arguments.car, Symbol.class); + + fe = symbolTable.lookupFunction((Symbol) arguments.car); + + if (fe == null) + throw new UndefinedFunctionException((Symbol) arguments.car); + + return fe; + } + }; + + // ------- + // FUNCALL + // ------- + + final SpecialFormEntry FUNCALL = new SpecialFormEntry( + new Symbol("FUNCALL"), + environment, + new FormHelpTopic("FUNCALL", "make a function call", + "(funcall *) => ", + "funcall applies function to args. If function is a symbol, " + + "it is coerced to a function as if by finding its " + + "functional value in the global environment.", + "function", "a function designator", + "arg", "an object", + "results", "the result of the function call")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + if (arguments == null) + throw new InvalidArgumentQuantityException( + "form requires at least one argument."); + + // first argument: function designator + SExp func = arguments.car.eval(symbolTable); + FormEntry fe = null; + arguments = arguments.cdr; + + // cast if already a form entry + if (func instanceof FormEntry) fe = (FormEntry) func; + + // lookup the function if it is a symbol + if (func instanceof Symbol) { + fe = environment.globalSymbolTable + .lookupFunction((Symbol) func); + if (fe == null) + throw new UndefinedFunctionException((Symbol) func); + } + + if (fe == null) + throw new TypeException(func, FormEntry.class); + + // make the function call + return fe.call(symbolTable, arguments); + } + }; + // ---- // GETF // ---- - SpecialFormEntry GETF = new SpecialFormEntry( + final SpecialFormEntry GETF = new SpecialFormEntry( + new Symbol("GETF"), environment, new FormHelpTopic("GETF", "", "(getf []) => ", @@ -525,6 +627,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ---- final SpecialFormEntry HELP = new SpecialFormEntry( + new Symbol("HELP"), environment, new FormHelpTopic("HELP", "Display online help information for a topic.", @@ -553,7 +656,7 @@ public abstract class SpecialFormEntry implements FormEntry { // lookup help for funtion FormEntry fe = symbolTable.lookupFunction( (Symbol) arguments.car); - if (fe != null) topics.add(fe.helpinfo()); + if (fe != null) topics.add(fe.helpinfo); // lookup help for variable VariableEntry ve = symbolTable.lookupVariable( @@ -576,6 +679,7 @@ public abstract class SpecialFormEntry implements FormEntry { // -- final SpecialFormEntry IF = new SpecialFormEntry( + new Symbol("IF"), environment, new FormHelpTopic("IF", "conditional code execution", @@ -612,11 +716,63 @@ public abstract class SpecialFormEntry implements FormEntry { } }; + // ------ + // LAMBDA + // ------ + + final SpecialFormEntry LAMBDA = new SpecialFormEntry( + new Symbol("LAMBDA"), + environment, + new FormHelpTopic("LAMDA", + "create an anonymous function over the current lexical closure", + "(lambda
) => ", + "", + "param-list", "a list of symbols", + "form", "a form", + "lambda", "a function")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + + ArrayList parameters = new ArrayList(); + SExp body; + Seq paramSeq; + + if (arguments.length() != 2) + throw new InvalidArgumentQuantityException( + 2, arguments.length()); + + // first parameter: parameters to the lambda + if (!(arguments.car instanceof List)) + throw new TypeException(arguments.car, List.class); + + paramSeq = ((List) arguments.car).seq; + while (paramSeq != null) { + if (!(paramSeq.car instanceof Symbol)) + throw new TypeException(paramSeq.car, Symbol.class); + + parameters.add((Symbol) paramSeq.car); + paramSeq = paramSeq.cdr; + } + + // second argument: function body + arguments = arguments.cdr; + assert (arguments != null); + + body = arguments.car; + + return new Lambda(parameters.toArray(new Symbol[]{}), + body, symbolTable); + } + }; + + // --- // LET // --- final SpecialFormEntry LET = new SpecialFormEntry( + new Symbol("LET"), environment, new FormHelpTopic("LET", "create new lexical variable bindings", "(let (([ ])*) *) => ", @@ -705,6 +861,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ---- final SpecialFormEntry LET_STAR = new SpecialFormEntry( + new Symbol("LET*"), environment, new FormHelpTopic("LET*", "create new lexical variable bindings", "(let (([ ])*) *) => ", @@ -794,6 +951,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ---- final SpecialFormEntry LIST = new SpecialFormEntry( + new Symbol("LIST"), environment, new FormHelpTopic("LIST", "create a list", "(list *) => list", @@ -829,6 +987,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ----- final SpecialFormEntry QUOTE = new SpecialFormEntry( + new Symbol("QUOTE"), environment, new FormHelpTopic("QUOTE", "Return objects unevaluated.", "(quote ) => ", @@ -851,6 +1010,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ----- final SpecialFormEntry PROGN = new SpecialFormEntry( + new Symbol("PROGN"), environment, new FormHelpTopic("PROGN", "evaluate forms in the order they are given", @@ -883,6 +1043,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ---- final SpecialFormEntry SETQ = new SpecialFormEntry( + new Symbol("SETQ"), environment, new FormHelpTopic("SETQ", "Assigns values to variables.", "(setq [ ]*)", @@ -943,6 +1104,7 @@ public abstract class SpecialFormEntry implements FormEntry { // ----- final SpecialFormEntry TRACE = new SpecialFormEntry( + new Symbol("TRACE"), environment, new FormHelpTopic("TRACE", "enable trace information for a function", @@ -975,24 +1137,27 @@ public abstract class SpecialFormEntry implements FormEntry { } }; - environment.globalSymbolTable.bind(new Symbol("-"), DIF); - 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); - 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); + environment.globalSymbolTable.bind(DIF.name, DIF); + environment.globalSymbolTable.bind(DIV.name, DIV); + environment.globalSymbolTable.bind(MUL.name, MUL); + environment.globalSymbolTable.bind(SUM.name, SUM); + environment.globalSymbolTable.bind(CONS.name, CONS); + environment.globalSymbolTable.bind(DEFUN.name, DEFUN); + environment.globalSymbolTable.bind(DEFPARAMETER.name, DEFPARAMETER); + environment.globalSymbolTable.bind(DEFVAR.name, DEFVAR); + environment.globalSymbolTable.bind(ENABLEDEBUGAST.name, ENABLEDEBUGAST); + environment.globalSymbolTable.bind(FUNCALL.name, FUNCALL); + environment.globalSymbolTable.bind(FUNCTION.name, FUNCTION); + environment.globalSymbolTable.bind(GETF.name, GETF); + environment.globalSymbolTable.bind(HELP.name, HELP); + environment.globalSymbolTable.bind(IF.name, IF); + environment.globalSymbolTable.bind(LAMBDA.name, LAMBDA); + environment.globalSymbolTable.bind(LET.name, LET); + environment.globalSymbolTable.bind(LET_STAR.name, LET_STAR); + environment.globalSymbolTable.bind(LIST.name, LIST); + environment.globalSymbolTable.bind(QUOTE.name, QUOTE); + environment.globalSymbolTable.bind(PROGN.name, PROGN); + environment.globalSymbolTable.bind(SETQ.name, SETQ); + environment.globalSymbolTable.bind(TRACE.name, TRACE); } } diff --git a/src/edu/utexas/cs345/jdblisp/SymbolTable.java b/src/edu/utexas/cs345/jdblisp/SymbolTable.java index 353b0ee..ebbebf0 100755 --- a/src/edu/utexas/cs345/jdblisp/SymbolTable.java +++ b/src/edu/utexas/cs345/jdblisp/SymbolTable.java @@ -99,4 +99,5 @@ public class SymbolTable { // search outer scope return enclosingTable.lookupVariable(s); } + } diff --git a/todo.txt b/todo.txt index 9bb204b..a3073a8 100644 --- a/todo.txt +++ b/todo.txt @@ -1,11 +1,15 @@ Outstanding ----------- + - Implement CAR + - Implement CDR - Implement FORMAT - - Implement GETF + - Implement FIRST - Implement lambdas + - Implement LAST - Implement LIST* - Implement macros + - Implement arithmetic comparisons - Implement packages - Implement READ and PRINT - Implement SETF @@ -20,11 +24,12 @@ Outstanding Partially Done -------------- - P Implement keywords Done ---- + D Implement keywords + D Implement GETF D Add ' notation for quote D Define NIL D Define T