diff --git a/src/edu/utexas/cs345/jdblisp/FormEntry.java b/src/edu/utexas/cs345/jdblisp/FormEntry.java index 5006d3b..00fa41f 100644 --- a/src/edu/utexas/cs345/jdblisp/FormEntry.java +++ b/src/edu/utexas/cs345/jdblisp/FormEntry.java @@ -6,4 +6,5 @@ package edu.utexas.cs345.jdblisp; */ public interface FormEntry { SExp call(SymbolTable table, Seq arguments) throws LispException; + HelpTopic helpinfo(); } diff --git a/src/edu/utexas/cs345/jdblisp/FormHelpTopic.java b/src/edu/utexas/cs345/jdblisp/FormHelpTopic.java new file mode 100644 index 0000000..480fb78 --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/FormHelpTopic.java @@ -0,0 +1,56 @@ +package edu.utexas.cs345.jdblisp; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * FormHelpTopic + * @author Jonathan Bernard (jdbernard@gmail.com) + */ +public class FormHelpTopic extends HelpTopic { + + protected final String invokation; + protected final String[][] parameters; + + /** + * TODO + */ + public FormHelpTopic(String operatorName, String shortDescription, + String invokation, String longDescription, String... parameterDefinitions) { + super(operatorName, shortDescription, longDescription); + this.invokation = invokation; + + assert (parameterDefinitions.length % 2 == 0); + + ArrayList paramDefs = new ArrayList(); + for (int i = 0; i < parameterDefinitions.length; i+=2) { + paramDefs.add(new String[] { + parameterDefinitions[i], + parameterDefinitions[i+1] }); + } + + parameters = paramDefs.toArray(new String[][] {}); + } + + @Override + public void print(OutputStream os, int wrapWidth) { + WrappedPrinter out = new WrappedPrinter(os, wrapWidth, true); + + out.print(name + ": "); + if (shortDescription != null) + out.println(shortDescription, " "); + out.println(); + out.println(invokation, " "); + out.println(); + if (body != null) { + out.println(body, " "); + out.println(); + } + + for (String[] param : parameters) { + out.print(param[0] + "\t", " "); + out.println(param[1], " "); + } + } +} diff --git a/src/edu/utexas/cs345/jdblisp/FunctionEntry.java b/src/edu/utexas/cs345/jdblisp/FunctionEntry.java index a94227b..e3a1f1b 100755 --- a/src/edu/utexas/cs345/jdblisp/FunctionEntry.java +++ b/src/edu/utexas/cs345/jdblisp/FunctionEntry.java @@ -6,16 +6,52 @@ package edu.utexas.cs345.jdblisp; */ public class FunctionEntry implements FormEntry { + public final HelpTopic helpinfo; + public final Symbol name; protected final Symbol[] parameters; protected final SExp body; - public FunctionEntry(Symbol[] parameters, SExp body) { + protected boolean traceEnabled; + + //private Logger traceLog = Logger.getLogger(getClass()); + + public FunctionEntry(Symbol name, Symbol[] parameters, SExp body) { + + // build invocation help string + String invokation = "(" + name.name; + for (Symbol param : parameters) invokation += " <" + param.name + ">"; + invokation += ")"; + String bodyInfo = "Function body: " + body.toString(); + + // build help topic + FormHelpTopic helpinfo = new FormHelpTopic(name.name, null, invokation, bodyInfo); + + this.name = name; this.parameters = parameters; this.body = body; + this.helpinfo = helpinfo; } + public FunctionEntry(Symbol name, Symbol[] parameters, SExp body, + HelpTopic helpinfo) { + this.name = name; + this.parameters = parameters; + this.body = body; + this.helpinfo = helpinfo; + } + + public boolean isTraceEnabled() { return traceEnabled ;} + + public void enableTrace(boolean enable) { this.traceEnabled = enable; } + public SExp call(SymbolTable symbolTable, Seq arguments) throws LispException { + String traceString = null; + SExp evaluatedArg, retVal; + + if (traceEnabled) + traceString = "(" + name.name; + // bind arguments to parameters SymbolTable localScope = new SymbolTable(symbolTable); int i = 0; @@ -27,7 +63,10 @@ public class FunctionEntry implements FormEntry { parameters.length, i); // bind one arg to param - localScope.bind(parameters[i], new VariableEntry(arguments.car.eval(symbolTable))); + evaluatedArg = arguments.car.eval(symbolTable); + localScope.bind(parameters[i], new VariableEntry(evaluatedArg)); + + if (traceEnabled) traceString += " " + evaluatedArg.toString(); arguments = arguments.cdr; ++i; @@ -38,6 +77,18 @@ public class FunctionEntry implements FormEntry { throw new InvalidArgumentQuantityException(parameters.length, (i + arguments.length())); - return body.eval(localScope); + if (traceEnabled) traceString += ")"; + if (traceEnabled) System.out.println(traceString); + + + retVal = body.eval(localScope); + + if (traceEnabled) + traceString = name.name + " returned " + retVal.toString(); + if (traceEnabled) System.out.println(traceString); + + return retVal; } + + public HelpTopic helpinfo() { return helpinfo; } } diff --git a/src/edu/utexas/cs345/jdblisp/HelpTopic.java b/src/edu/utexas/cs345/jdblisp/HelpTopic.java new file mode 100644 index 0000000..58d4adf --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/HelpTopic.java @@ -0,0 +1,120 @@ +package edu.utexas.cs345.jdblisp; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * HelpTopic + * @author Jonathan Bernard (jdbernard@gmail.com) + */ +public class HelpTopic { + + public static Map helpTopics; + + protected final String name; + protected final String shortDescription; + protected final String body; + + public HelpTopic(String name, String shortDescription, String body) { + this.name = name; + this.shortDescription = shortDescription; + this.body = body; + } + + public void print(OutputStream os) { print(os, 79); } + + public void print(OutputStream os, int wrapWidth) { + WrappedPrinter out = new WrappedPrinter(os, wrapWidth, true); + out.println(name); + out.println(shortDescription, " "); + out.println(); + out.println(body, " "); + } + + class WrappedPrinter { + + private PrintWriter printer; + private int wrapWidth; + private int lastPrintedLineLength = 0; + + public WrappedPrinter(OutputStream os, int wrapWidth, + boolean autoflush) { + this(new PrintWriter(os, autoflush), wrapWidth); + } + + public WrappedPrinter(PrintWriter pw, int wrapWidth) { + this.printer = pw; + this.wrapWidth = wrapWidth; + } + + public void print(String message) { print(message, ""); } + + public void print(String message, String offset) { + int lastSpaceIdx = 0; + int curLineLength = 0; + int lineStartIdx = 0; + int i = 0; + int actualWidth = wrapWidth - offset.length(); + + //message = message.replaceAll("[\n\r]", " "); + + // print initial offset if this is the beginning of the line + if (lastPrintedLineLength == 0) { + printer.print(offset); + curLineLength = offset.length(); + } + + + for (i = 0; i < message.length(); ++i) { + + curLineLength++; + if (message.charAt(i) == '\t') curLineLength += 7; + + // line has overflowed the prescribed width + if (curLineLength > actualWidth) { + // print up to the last space before the overflow + printer.println(message.substring(lineStartIdx, lastSpaceIdx)); + + // pick up the next line after said space + lineStartIdx = lastSpaceIdx + 1; + i = lastSpaceIdx; + curLineLength = 0; + + // print initial offset if there is still more to print + if (lineStartIdx < message.length()) { + printer.print(offset); + curLineLength = offset.length(); + } + } + + // see whitespace, update last space index + if (Character.isWhitespace(message.charAt(i))) lastSpaceIdx = i; + } + + // any left over, it will fit on one line + if (i - lineStartIdx > 1) { + String lastLine = message.substring(lineStartIdx); + printer.print(lastLine); + curLineLength += lastLine.length(); + } + + // save back the new position + lastPrintedLineLength = curLineLength; + + } + + public void println() { println("", ""); } + + public void println(String message) { println(message, ""); } + + public void println(String message, String offset) { + + print(message, offset); + printer.println(); + lastPrintedLineLength = 0; + } + + } +} diff --git a/src/edu/utexas/cs345/jdblisp/LISPRuntime.java b/src/edu/utexas/cs345/jdblisp/LISPRuntime.java new file mode 100755 index 0000000..48c16af --- /dev/null +++ b/src/edu/utexas/cs345/jdblisp/LISPRuntime.java @@ -0,0 +1,117 @@ +package edu.utexas.cs345.jdblisp; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +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 LISPRuntime { + + public static final String VERSION = "0.1.0"; + + public SymbolTable globalSymbolTable; + + private Parser parser; + + private boolean interactive = true; + + boolean dumpAST = false; + + private OutputStream os; + + public LISPRuntime() { + this(true); + } + + public LISPRuntime(boolean interactive) { + this.interactive = interactive; + Parser parser = new Parser(new ByteArrayInputStream(new byte[]{})); + globalSymbolTable = new SymbolTable(); + SpecialFormEntry.defineSpecialForms(this); + globalSymbolTable.bind(new Symbol("T"), new VariableEntry(SExp.T)); + } + + // TODO: is this needed? + // public void setInteractive(boolean interactive) { this.interactive = interactive; } + + public static void main(String[] args) throws IOException { + + // create the LISP environment + LISPRuntime lisp = new LISPRuntime(); + + // print welcome message + System.out.println("Welcome to JDB-Lisp v" + VERSION + ", a subset of Common Lisp.");; + System.out.println("On-line help is available via the (help funcname) command."); + // parse command-line arguments (treat non-options as input files) + // TODO: replace with Apache Commons CLI + for (String arg : args) { + System.out.println("Loading file '" + arg + "..."); + lisp.interactive = false; + lisp.repl(new FileInputStream(arg), System.out); + } + + // drop into interactive mode + lisp.interactive = true; + lisp.repl(System.in, System.out); + } + + public void repl(InputStream is, OutputStream os) throws IOException { + + // wrap output in a more friendly object + this.os = os; + PrintWriter out = new PrintWriter(os,true); + + parser.ReInit(is); + SExp sexp; + while (true) { + + // print prompt if applicable + if (interactive) { + out.print("> "); + out.flush(); + } + + try { sexp = parser.sexp(); } + catch (ParseException pe) { + + // using the available call to check if the stream is open. + try { is.available(); } // if an exception is + catch (IOException ioe) { break; } // then it is closed + + out.println(pe.getLocalizedMessage()); + continue; + } + + if (dumpAST) { + out.println("ABSTRACT SYNTAX:"); + out.println(sexp.display(" ")); + out.println("----------------"); + } + + if (interactive) { + try { + SExp result = sexp.eval(globalSymbolTable); + out.println(result == null ? "NIL" : result.toString()); + } catch (LispException le) { + out.println(le.getLocalizedMessage()); + continue; + } + } + + } + + out.println("\nLeaving JDB-LISP"); + } + + OutputStream getOutputStream() { return os; } + +} diff --git a/src/edu/utexas/cs345/jdblisp/List.java b/src/edu/utexas/cs345/jdblisp/List.java index 11aa688..68fa9c3 100755 --- a/src/edu/utexas/cs345/jdblisp/List.java +++ b/src/edu/utexas/cs345/jdblisp/List.java @@ -13,6 +13,10 @@ public class List implements SExp { /** {@inheritdoc}*/ public SExp eval(SymbolTable table) throws LispException { + + // null is NIL + if (seq == null) return null; + // if the car of the sequence is a symbol, if (seq.car instanceof Symbol) { // then that symbol is the name of an operator diff --git a/src/edu/utexas/cs345/jdblisp/Parser.jj b/src/edu/utexas/cs345/jdblisp/Parser.jj index 4d22e47..d66bcd7 100755 --- a/src/edu/utexas/cs345/jdblisp/Parser.jj +++ b/src/edu/utexas/cs345/jdblisp/Parser.jj @@ -21,11 +21,13 @@ SKIP : /* WHITE SPACE */ | "\t" | "\n" | "\n\r" +| ";.*$" } TOKEN : /* PUNCTUATION */ { < LPAREN: "(" > | < RPAREN: ")" > +| < NIL: (["N","n"]["I","i"]["L","l"])> } @@ -55,7 +57,8 @@ SExp sexp(): List list(): { Seq s; } -{ s = seq() { return new List(s); } +{ s = seq() { return new List(s); } + | { return new List(null); } // allow NIL to be shorthand for () } /** diff --git a/src/edu/utexas/cs345/jdblisp/SExp.java b/src/edu/utexas/cs345/jdblisp/SExp.java index f93db52..e28f1b8 100755 --- a/src/edu/utexas/cs345/jdblisp/SExp.java +++ b/src/edu/utexas/cs345/jdblisp/SExp.java @@ -15,4 +15,15 @@ public interface SExp { String display(String offset); + public static final SExp T = new SExp() { + public SExp eval(SymbolTable table) { return this; } + public String display(String offset) { return offset + "T\n"; } + 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"; } + };*/ } diff --git a/src/edu/utexas/cs345/jdblisp/Seq.java b/src/edu/utexas/cs345/jdblisp/Seq.java index b46af21..5f7fdd9 100755 --- a/src/edu/utexas/cs345/jdblisp/Seq.java +++ b/src/edu/utexas/cs345/jdblisp/Seq.java @@ -49,6 +49,6 @@ public class Seq implements SExp { @Override public String toString() { - return car.toString() + (cdr == null ? "" : cdr.toString()); + return car.toString() + (cdr == null ? "" : " " + cdr.toString()); } } diff --git a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java index ae2818a..7a9920b 100755 --- a/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java +++ b/src/edu/utexas/cs345/jdblisp/SpecialFormEntry.java @@ -1,5 +1,6 @@ package edu.utexas.cs345.jdblisp; +import java.io.PrintWriter; import java.util.ArrayList; /** @@ -8,17 +9,103 @@ import java.util.ArrayList; */ public abstract class SpecialFormEntry implements FormEntry { - protected Lisp environment; + public final HelpTopic helpinfo; - public SpecialFormEntry(Lisp environment) { this.environment = environment; } + protected LISPRuntime environment; - public abstract SExp call(SymbolTable symbolTable, Seq arguments) throws LispException; + public SpecialFormEntry(LISPRuntime environment, HelpTopic helpinfo) { + this.environment = environment; + this.helpinfo = helpinfo; + } + public HelpTopic helpinfo() { return helpinfo; } - public static void defineSpecialForms(Lisp environment) { + public abstract SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException; + + // ------------------------ + // SPECIAL FORMS DEFINITION + // ------------------------ + + // JDB-Lisp includes on-line help for all of its special forms. See the + // help strings for documentation of the individual special forms. - SpecialFormEntry DEFUN = new SpecialFormEntry(environment) { - public SExp call(SymbolTable symbolTable, Seq arguments) throws LispException { + /** + * TODO + */ + public static void defineSpecialForms(LISPRuntime environment) { + + // ---- + // HELP + // ---- + + 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 null; + } + + topic.print(environment.getOutputStream()); + return null; + } + }; + + // ----- + // DEFUN + // ----- + + SpecialFormEntry DEFUN = new SpecialFormEntry( + environment, + new FormHelpTopic("DEFUN", "Define a (global) function.", + "(defun () )", + "Create a function binding. This will replace any existing binding.", + "name", "the symbol to bind to the function definition.", + "param-list", "a list of symbols that will be bound in the " + + "function scope to the arguments passed to the function.", + "func-body", "an sexpression evaluated when the function is " + + "called.")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { Symbol functionName; ArrayList parameters = new ArrayList(); @@ -27,7 +114,7 @@ public abstract class SpecialFormEntry implements FormEntry { // TODO: check to see if a function for this symbol exists // and warn if so - if (arguments.length() != 3) + if (arguments == null || arguments.length() != 3) new InvalidArgumentQuantityException(3, arguments.length()); // first argument: Symbol for function name @@ -62,14 +149,31 @@ public abstract class SpecialFormEntry implements FormEntry { body = arguments.car; environment.globalSymbolTable.bind(functionName, - new FunctionEntry(parameters.toArray(new Symbol[]{}), body)); + new FunctionEntry(functionName, + parameters.toArray(new Symbol[]{}), + body)); return functionName; } }; - SpecialFormEntry SETQ = new SpecialFormEntry(environment) { - public SExp call(SymbolTable symbolTable, Seq arguments) throws LispException { + // ---- + // SETQ + // ---- + + + SpecialFormEntry SETQ = 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.")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { Symbol variableName; SExp variableValue; @@ -90,7 +194,7 @@ public abstract class SpecialFormEntry implements FormEntry { arguments = arguments.cdr; assert (arguments != null); - variableValue = arguments.car; + variableValue = arguments.car.eval(symbolTable); environment.globalSymbolTable.bind(variableName, new VariableEntry(variableValue)); @@ -99,7 +203,51 @@ public abstract class SpecialFormEntry implements FormEntry { } }; - SpecialFormEntry SUM = new SpecialFormEntry(environment) { + // ----- + // 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.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 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 { @@ -119,7 +267,22 @@ public abstract class SpecialFormEntry implements FormEntry { } }; - SpecialFormEntry DIF = new SpecialFormEntry(environment) { + 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 { @@ -158,7 +321,17 @@ public abstract class SpecialFormEntry implements FormEntry { } }; - SpecialFormEntry MUL = new SpecialFormEntry(environment) { + 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 { @@ -179,7 +352,22 @@ public abstract class SpecialFormEntry implements FormEntry { } }; - SpecialFormEntry DIV = new SpecialFormEntry(environment) { + 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 { @@ -218,11 +406,47 @@ public abstract class SpecialFormEntry implements FormEntry { } }; + SpecialFormEntry ENABLEDEBUGAST = new SpecialFormEntry( + environment, + new FormHelpTopic("ENABLE-DEBUG-AST", + "Enable debug information: abstract syntax tree.", + "(enable-debug-ast [])", + "When DEBUG-AST is enabled, the runtime environment prints a " + + "representation of the abstract syntax tree generated " + + "by the parser for each sexpression it parses.", + "enable", "NIL = disabled, anything else = enabled. No " + + "argument = enabled.")) + { + public SExp call(SymbolTable symbolTable, Seq arguments) + throws LispException { + if (arguments == null) { + environment.dumpAST = true; + return null; + } + + SExp retVal = arguments.car.eval(symbolTable); + + if (retVal != null) environment.dumpAST = true; + else environment.dumpAST = false; + + return retVal; + } + }; + + /*SpecialFormEntry LET = new SpecialFormEntry(environment) { + public SExp call(SymbolTable table, Seq arguments) { + // TODO + } + }*/ + + 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("ENABLE-DEBUG-AST"), ENABLEDEBUGAST); } } diff --git a/src/edu/utexas/cs345/jdblisp/Symbol.java b/src/edu/utexas/cs345/jdblisp/Symbol.java index cdeaec9..0f94931 100755 --- a/src/edu/utexas/cs345/jdblisp/Symbol.java +++ b/src/edu/utexas/cs345/jdblisp/Symbol.java @@ -13,7 +13,7 @@ public class Symbol implements SExp { /** {@inheritdoc}*/ public SExp eval(SymbolTable table) throws LispException { VariableEntry ve = table.lookupVariable(this); - return ve.eval(table); + return ve.value; } public String display(String offset) { diff --git a/src/edu/utexas/cs345/jdblisp/VariableEntry.java b/src/edu/utexas/cs345/jdblisp/VariableEntry.java index 19366ae..b70ea2b 100755 --- a/src/edu/utexas/cs345/jdblisp/VariableEntry.java +++ b/src/edu/utexas/cs345/jdblisp/VariableEntry.java @@ -6,11 +6,7 @@ package edu.utexas.cs345.jdblisp; */ public class VariableEntry { - protected final SExp expression; + public final SExp value; - public VariableEntry(SExp expression) { this.expression = expression; } - - public SExp eval(SymbolTable symbolTable) throws LispException { - return expression.eval(symbolTable); - } + public VariableEntry(SExp value) { this.value = value; } } diff --git a/src/edu/utexas/cs345/jdblisp/parser/Parser.java b/src/edu/utexas/cs345/jdblisp/parser/Parser.java index 26f7a6c..1b6f994 100644 --- a/src/edu/utexas/cs345/jdblisp/parser/Parser.java +++ b/src/edu/utexas/cs345/jdblisp/parser/Parser.java @@ -23,6 +23,7 @@ public class Parser implements ParserConstants { {if (true) return new Num(t.image);} break; case LPAREN: + case NIL: s = list(); {if (true) return s;} break; @@ -39,10 +40,22 @@ public class Parser implements ParserConstants { */ static final public List list() throws ParseException { Seq s; - jj_consume_token(LPAREN); - s = seq(); - jj_consume_token(RPAREN); - {if (true) return new List(s);} + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LPAREN: + jj_consume_token(LPAREN); + s = seq(); + jj_consume_token(RPAREN); + {if (true) return new List(s);} + break; + case NIL: + jj_consume_token(NIL); + {if (true) return new List(null);} + break; + default: + jj_la1[1] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } throw new Error("Missing return statement in function"); } @@ -53,6 +66,7 @@ public class Parser implements ParserConstants { Seq sq; SExp se; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case LPAREN: + case NIL: case NUMB: case STRG: case SYMB: @@ -61,7 +75,7 @@ public class Parser implements ParserConstants { {if (true) return new Seq(se, sq);} break; default: - jj_la1[1] = jj_gen; + jj_la1[2] = jj_gen; ; } {if (true) return null;} @@ -78,13 +92,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[2]; + static final private int[] jj_la1 = new int[3]; static private int[] jj_la1_0; static { jj_la1_init_0(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x740,0x740,}; + jj_la1_0 = new int[] {0x1e80,0x280,0x1e80,}; } /** Constructor with InputStream. */ @@ -105,7 +119,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 2; i++) jj_la1[i] = -1; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -119,7 +133,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 2; i++) jj_la1[i] = -1; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; } /** Constructor. */ @@ -136,7 +150,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 2; i++) jj_la1[i] = -1; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -146,7 +160,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 2; i++) jj_la1[i] = -1; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; } /** Constructor with generated Token Manager. */ @@ -162,7 +176,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 2; i++) jj_la1[i] = -1; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -171,7 +185,7 @@ public class Parser implements ParserConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 2; i++) jj_la1[i] = -1; + for (int i = 0; i < 3; i++) jj_la1[i] = -1; } static private Token jj_consume_token(int kind) throws ParseException { @@ -222,12 +236,12 @@ public class Parser implements ParserConstants { /** Generate ParseException. */ static public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[11]; + boolean[] la1tokens = new boolean[13]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 3; 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 eea8e7e..8141680 100644 --- a/src/edu/utexas/cs345/jdblisp/parser/ParserTokenManager.java +++ b/src/edu/utexas/cs345/jdblisp/parser/ParserTokenManager.java @@ -36,9 +36,11 @@ static private int jjMoveStringLiteralDfa0_0() jjmatchedKind = 4; return jjMoveStringLiteralDfa1_0(0x20L); case 40: - return jjStopAtPos(0, 6); - case 41: return jjStopAtPos(0, 7); + case 41: + return jjStopAtPos(0, 8); + case 59: + return jjMoveStringLiteralDfa1_0(0x40L); default : return jjMoveNfa_0(0, 0); } @@ -56,15 +58,55 @@ static private int jjMoveStringLiteralDfa1_0(long active0) if ((active0 & 0x20L) != 0L) return jjStopAtPos(1, 5); break; + case 46: + return jjMoveStringLiteralDfa2_0(active0, 0x40L); default : break; } return jjStartNfa_0(0, active0); } +static private int jjMoveStringLiteralDfa2_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(1, active0); + return 2; + } + switch(curChar) + { + case 42: + return jjMoveStringLiteralDfa3_0(active0, 0x40L); + default : + break; + } + return jjStartNfa_0(1, active0); +} +static private int jjMoveStringLiteralDfa3_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(1, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(2, active0); + return 3; + } + switch(curChar) + { + case 36: + if ((active0 & 0x40L) != 0L) + return jjStopAtPos(3, 6); + break; + default : + break; + } + return jjStartNfa_0(2, active0); +} static private int jjMoveNfa_0(int startState, int curPos) { int startsAt = 0; - jjnewStateCnt = 9; + jjnewStateCnt = 12; int i = 1; jjstateSet[0] = startState; int kind = 0x7fffffff; @@ -82,57 +124,61 @@ static private int jjMoveNfa_0(int startState, int curPos) case 0: if ((0x3ff000000000000L & l) != 0L) { - if (kind > 8) - kind = 8; - jjCheckNAddTwoStates(1, 2); + if (kind > 10) + kind = 10; + jjCheckNAddTwoStates(4, 5); } else if ((0x2000ac0000000000L & l) != 0L) { - if (kind > 10) - kind = 10; - jjCheckNAddTwoStates(7, 8); + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(10, 11); } else if (curChar == 34) jjAddStates(0, 1); if ((0x280000000000L & l) != 0L) - jjCheckNAdd(1); - break; - case 1: - if ((0x3ff000000000000L & l) == 0L) - break; - if (kind > 8) - kind = 8; - jjCheckNAddTwoStates(1, 2); - break; - case 2: - if (curChar == 46) - jjCheckNAdd(3); + jjCheckNAdd(4); break; case 3: - if ((0x3ff000000000000L & l) == 0L) - break; - if (kind > 8) - kind = 8; - jjCheckNAdd(3); + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(4); break; case 4: - if (curChar == 34) - jjAddStates(0, 1); - break; - case 6: - if (curChar == 34 && kind > 9) - kind = 9; - break; - case 7: - if ((0x2000ac0000000000L & l) == 0L) + if ((0x3ff000000000000L & l) == 0L) break; if (kind > 10) kind = 10; - jjCheckNAddTwoStates(7, 8); + jjCheckNAddTwoStates(4, 5); break; - case 8: - if ((0x23ffac0000000000L & l) != 0L && kind > 10) + case 5: + if (curChar == 46) + jjCheckNAdd(6); + break; + case 6: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 10) kind = 10; + jjCheckNAdd(6); + break; + case 7: + if (curChar == 34) + jjAddStates(0, 1); + break; + case 9: + if (curChar == 34 && kind > 11) + kind = 11; + break; + case 10: + if ((0x2000ac0000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(10, 11); + break; + case 11: + if ((0x23ffac0000000000L & l) != 0L && kind > 12) + kind = 12; break; default : break; } @@ -146,20 +192,37 @@ static private int jjMoveNfa_0(int startState, int curPos) switch(jjstateSet[--i]) { case 0: - case 7: - if ((0x7fffffe87fffffeL & l) == 0L) - break; - if (kind > 10) - kind = 10; - jjCheckNAddTwoStates(7, 8); + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(10, 11); + } + if ((0x400000004000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 1; break; - case 5: + case 1: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 2: + if ((0x100000001000L & l) != 0L && kind > 9) + kind = 9; + break; + case 8: if ((0x7fffffe87fffffeL & l) != 0L) jjAddStates(0, 1); break; - case 8: - if ((0x7fffffe87fffffeL & l) != 0L && kind > 10) - kind = 10; + case 10: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(10, 11); + break; + case 11: + if ((0x7fffffe87fffffeL & l) != 0L && kind > 12) + kind = 12; break; default : break; } @@ -184,33 +247,33 @@ static private int jjMoveNfa_0(int startState, int curPos) kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 9 - (jjnewStateCnt = startsAt))) + if ((i = jjnewStateCnt) == (startsAt = 12 - (jjnewStateCnt = startsAt))) return curPos; try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { return curPos; } } } static final int[] jjnextStates = { - 5, 6, + 8, 9, }; /** Token literal values. */ public static final String[] jjstrLiteralImages = { -"", null, null, null, null, null, "\50", "\51", null, null, null, }; +"", null, null, null, null, null, null, "\50", "\51", null, null, null, null, }; /** Lexer state names. */ public static final String[] lexStateNames = { "DEFAULT", }; static final long[] jjtoToken = { - 0x7c1L, + 0x1f81L, }; static final long[] jjtoSkip = { - 0x3eL, + 0x7eL, }; static protected SimpleCharStream input_stream; -static private final int[] jjrounds = new int[9]; -static private final int[] jjstateSet = new int[18]; +static private final int[] jjrounds = new int[12]; +static private final int[] jjstateSet = new int[24]; static protected char curChar; /** Constructor. */ public ParserTokenManager(SimpleCharStream stream){ @@ -237,7 +300,7 @@ static private void ReInitRounds() { int i; jjround = 0x80000001; - for (i = 9; i-- > 0;) + for (i = 12; i-- > 0;) jjrounds[i] = 0x80000000; } diff --git a/src/org/jdbernard/util/NullOutputStream.java b/src/org/jdbernard/util/NullOutputStream.java new file mode 100644 index 0000000..1ff9560 --- /dev/null +++ b/src/org/jdbernard/util/NullOutputStream.java @@ -0,0 +1,14 @@ +package org.jdbernard.util; + +import java.io.OutputStream; + +/** + * NullOutputStream + * @author Jonathan Bernard (jdbernard@gmail.com) + * All output directed to this OutputStream is ignored and lost. Writing to + * stream is like writing to /dev/null. + */ +public class NullOutputStream extends OutputStream { + + public void write(int b) {}; +} diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..6d081fc --- /dev/null +++ b/todo.txt @@ -0,0 +1,3 @@ +Implement FORMAT +Implement READ and PRINT +Implement IF