diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..1938c4d
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,10 @@
+
+ErrorManager
is an error handling class for a Lisp interpreter.
+ */
+public class ErrorManager {
+
+ /**
+ * The lowest "criticality" level of an error that will cause the currently
+ * running program to terminate.
+ */
+ public static final int CRITICAL_LEVEL = 3;
+
+ public static final String ANSI_RESET = "\u001B[0m";
+ public static final String ANSI_RED = "\u001B[31m";
+ public static final String ANSI_YELLOW = "\u001B[33m";
+ public static final String ANSI_PURPLE = "\u001B[35m";
+
+ /**
+ * Prints out the specified error message to the console and decides
+ * whether or not to terminate the currently running program.
+ *
+ * @param message
+ * the error message
+ * @param level
+ * the "criticality" level of the error
+ * @postcondition
+ * If level >= CRITICAL_LEVEL
the currently running
+ * program has been terminated.
+ */
+ public static void generateError(String message, int level) {
+ String color = (level >= CRITICAL_LEVEL) ? ANSI_PURPLE : ANSI_RED;
+ String formattedMessage = MessageFormat.format("{0}error: {1}{2}", color, message, ANSI_RESET);
+
+ System.out.println(formattedMessage);
+
+ if (level >= CRITICAL_LEVEL) {
+ System.exit(1);
+ }
+ }
+
+}
diff --git a/src/error/package.html b/src/error/package.html
new file mode 100644
index 0000000..b8e7489
--- /dev/null
+++ b/src/error/package.html
@@ -0,0 +1,3 @@
+
APPLY
represents the APPLY function in Lisp.
+ */
+public class APPLY extends LispFunction {
+
+ /**
+ * Call APPLY with the specified argument list.
+ *
+ * @param argList
+ * the list of arguments to be sent to APPLY (MUST BE A PROPER LIST)
+ * @return
+ * the result of evaluating APPLY on argList
+ */
+ public static SExpression apply(Cons argList) {
+ return new APPLY().call(argList);
+ }
+
+ // The number of arguments that APPLY takes.
+ private static final int NUM_ARGS = 2;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to APPLY
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("APPLY"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to APPLY: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression car = argList.getCar(); // function name
+ Cons cdr = (Cons) argList.getCdr();
+ SExpression cadr = cdr.getCar(); // argument list
+
+ // make sure the second argument is a list
+ if (cadr.listp()) {
+ LispFunction function = EVAL.lookupFunction(car.toString());
+
+ if (function == null) {
+ // check if the car of the list is a lambda expression
+ if (car.functionp()) {
+ function = ((LambdaExpression) car).getFunction();
+ } else if (LAMBDA.isLambdaExpression(car)) {
+ Cons lexpr = (Cons) car;
+
+ function = LAMBDA.createFunction(lexpr);
+ } else {
+ throw new RuntimeException("undefined function " + car);
+ }
+ }
+
+ // apply the given function to the given argument list
+ return function.call((Cons) cadr);
+ }
+
+ // the second argument is not a list
+ throw new RuntimeException("APPLY: " + cadr + " is not a list");
+ }
+
+}
diff --git a/src/eval/ATOM.class b/src/eval/ATOM.class
new file mode 100644
index 0000000..b5cf96e
Binary files /dev/null and b/src/eval/ATOM.class differ
diff --git a/src/eval/ATOM.java b/src/eval/ATOM.java
new file mode 100644
index 0000000..fbee068
--- /dev/null
+++ b/src/eval/ATOM.java
@@ -0,0 +1,38 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * ATOM
represents the ATOM function in Lisp.
+ */
+public class ATOM extends LispFunction {
+
+ // The number of arguments that ATOM takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to ATOM
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("ATOM"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to ATOM: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression arg = argList.getCar();
+
+ return (arg.atomp() ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+}
diff --git a/src/eval/CAR.class b/src/eval/CAR.class
new file mode 100644
index 0000000..86a7ae3
Binary files /dev/null and b/src/eval/CAR.class differ
diff --git a/src/eval/CAR.java b/src/eval/CAR.java
new file mode 100644
index 0000000..3b75078
--- /dev/null
+++ b/src/eval/CAR.java
@@ -0,0 +1,46 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * CAR
represents the CAR function in Lisp.
+ */
+public class CAR extends LispFunction {
+
+ // The number of arguments that CAR takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to CAR
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("CAR"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to CAR: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression argCar = argList.getCar();
+
+ // make sure that the argument is a list
+ if (argCar.listp()) {
+ Cons arg = (Cons) argCar;
+
+ return arg.getCar();
+ }
+
+ // the argument is not a list
+ throw new RuntimeException("CAR: " + argCar + " is not a list");
+ }
+
+}
diff --git a/src/eval/CDR.class b/src/eval/CDR.class
new file mode 100644
index 0000000..128383f
Binary files /dev/null and b/src/eval/CDR.class differ
diff --git a/src/eval/CDR.java b/src/eval/CDR.java
new file mode 100644
index 0000000..0cc281e
--- /dev/null
+++ b/src/eval/CDR.java
@@ -0,0 +1,46 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * CDR
represents the CDR function in Lisp.
+ */
+public class CDR extends LispFunction {
+
+ // The number of arguments that CDR takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to CDR
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("CDR"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to CDR: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression argCar = argList.getCar();
+
+ // make sure that the argument is a list
+ if (argCar.listp()) {
+ Cons arg = (Cons) argCar;
+
+ return arg.getCdr();
+ }
+
+ // the argument is not a list
+ throw new RuntimeException("CDR: " + argCar + " is not a list");
+ }
+
+}
diff --git a/src/eval/COND.class b/src/eval/COND.class
new file mode 100644
index 0000000..29bb23a
Binary files /dev/null and b/src/eval/COND.class differ
diff --git a/src/eval/COND.java b/src/eval/COND.java
new file mode 100644
index 0000000..9bd4e79
--- /dev/null
+++ b/src/eval/COND.java
@@ -0,0 +1,73 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * COND
represents the COND form in Lisp.
+ */
+public class COND extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ if (argList.nullp()) {
+ // return NIL if there are were no arguments passed to COND
+ return Nil.getUniqueInstance();
+ }
+
+ SExpression argCar = argList.getCar(); // first clause
+ Cons argCdr = (Cons) argList.getCdr(); // list of remaining clauses
+
+ // make sure the first clause is a list and is not NIL
+ if (argCar.consp()) {
+ Cons clause = (Cons) argCar;
+ SExpression test = EVAL.eval(clause.getCar());
+
+ if (test != Nil.getUniqueInstance()) {
+ // the car of this clause is true, so we evaluate its cdr
+
+ SExpression cdr = clause.getCdr();
+ SExpression retval = test;
+
+ // evaluate all the S-expressions in the cdr of the clause
+ while (cdr.consp()) {
+ retval = EVAL.eval(((Cons) cdr).getCar());
+ cdr = ((Cons) cdr).getCdr();
+ }
+
+ // return the value of the last S-expression evaluated
+ return retval;
+ }
+
+ // the car of this clause is false, so we test any remaining
+ // clauses
+
+ // check if the list of remaining clauses is a list and is not NIL
+ if (argCdr.consp()) {
+ return call(argCdr);
+ }
+
+ // there are no remaining clauses, so we return NIL
+ return Nil.getUniqueInstance();
+ }
+
+ throw new RuntimeException("COND: clause " + argCar +
+ " should be a list");
+ }
+
+ /**
+ * Determine if the arguments passed to this Lisp function should be
+ * evaluated.
+ *
+ * @return
+ * false
+ */
+ public boolean evaluateArguments() {
+ return false;
+ }
+
+}
diff --git a/src/eval/CONS.class b/src/eval/CONS.class
new file mode 100644
index 0000000..c95ed56
Binary files /dev/null and b/src/eval/CONS.class differ
diff --git a/src/eval/CONS.java b/src/eval/CONS.java
new file mode 100644
index 0000000..db967a7
--- /dev/null
+++ b/src/eval/CONS.java
@@ -0,0 +1,44 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * CONS
represents the CONS function in Lisp.
+ */
+public class CONS extends LispFunction {
+
+ // The number of arguments that CONS takes.
+ private static final int NUM_ARGS = 2;
+
+ public Cons call(Cons argList) {
+ // retrieve the number of arguments passed to CONS
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("CONS"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to CONS: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ // the car of the CONS cell we are going to create
+ SExpression argOne = argList.getCar();
+
+ Cons cdr = (Cons) argList.getCdr();
+
+ // the cdr of the CONS cell we are going to create
+ SExpression argTwo = cdr.getCar();
+
+ return new Cons(argOne, argTwo);
+ }
+
+}
diff --git a/src/eval/DEFUN.class b/src/eval/DEFUN.class
new file mode 100644
index 0000000..fd9c707
Binary files /dev/null and b/src/eval/DEFUN.class differ
diff --git a/src/eval/DEFUN.java b/src/eval/DEFUN.java
new file mode 100644
index 0000000..c435737
--- /dev/null
+++ b/src/eval/DEFUN.java
@@ -0,0 +1,82 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+import java.util.HashMap;
+
+/**
+ * DEFUN
represents the DEFUN form in Lisp.
+ */
+public class DEFUN extends LispFunction {
+
+ // The minimum number of arguments that DEFUN takes.
+ private static final int MIN_ARGS = 3;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to DEFUN
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength < MIN_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("DEFUN"), argList);
+ String errMsg = "too few arguments given to DEFUN: " +
+ originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression name = argList.getCar(); // name of the function
+
+ // make sure the function name is a symbol
+ if (! name.symbolp()) {
+ throw new RuntimeException("DEFUN: " + name + " is not a symbol");
+ }
+
+ Cons cdr = (Cons) argList.getCdr();
+ SExpression cadr = cdr.getCar();
+
+ // make sure the list of arguments (lambda list) is a proper list
+ if (! cadr.listp()) {
+ throw new RuntimeException("DEFUN: " + cadr + " is not a list");
+ } else if (EVAL.isDotted((Cons) cadr)) {
+ throw new RuntimeException("DEFUN: " + cadr +
+ " is not a proper list");
+ }
+
+ Cons lambdaList = (Cons) cadr; // lambda list of the function
+
+ // list of S-expressions making up the body of the function
+ Cons body = (Cons) cdr.getCdr();
+
+ HashMapfalse
+ */
+ public boolean evaluateArguments() {
+ return false;
+ }
+
+}
diff --git a/src/eval/DIVIDE.class b/src/eval/DIVIDE.class
new file mode 100644
index 0000000..edb1745
Binary files /dev/null and b/src/eval/DIVIDE.class differ
diff --git a/src/eval/DIVIDE.java b/src/eval/DIVIDE.java
new file mode 100644
index 0000000..0794f57
--- /dev/null
+++ b/src/eval/DIVIDE.java
@@ -0,0 +1,60 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * DIVIDE
represents the '/' function in Lisp.
+ */
+public class DIVIDE extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("/"), argList);
+
+ throw new RuntimeException("too few arguments given to /: " +
+ originalSExpr);
+ }
+
+ SExpression argFirst = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ // make sure that the first argument is a number
+ if (argFirst.numberp()) {
+ LispNumber num1 = (LispNumber) argFirst;
+
+ if (argRest.nullp()) {
+ // there is only one argument, so return the multiplicative
+ // inverse of the number
+ return new LispNumber(1 / num1.getValue());
+ }
+
+ SExpression argSecond = argRest.getCar();
+
+ // make sure that the next argument is a number as well
+ if (argSecond.numberp()) {
+ LispNumber num2 = (LispNumber) argSecond;
+ LispNumber quotient = new LispNumber(num1.getValue() /
+ num2.getValue());
+ SExpression argCddr = argRest.getCdr();
+
+ if (argCddr.consp()) {
+ return call(new Cons(quotient, argCddr));
+ }
+
+ return quotient;
+ }
+
+ throw new RuntimeException("/: " + argSecond + " is not a number");
+ }
+
+ throw new RuntimeException("/: " + argFirst + " is not a number");
+ }
+
+}
diff --git a/src/eval/EQ.class b/src/eval/EQ.class
new file mode 100644
index 0000000..83a270c
Binary files /dev/null and b/src/eval/EQ.class differ
diff --git a/src/eval/EQ.java b/src/eval/EQ.java
new file mode 100644
index 0000000..0227525
--- /dev/null
+++ b/src/eval/EQ.java
@@ -0,0 +1,45 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * EQ
represents the EQ function in Lisp.
+ */
+public class EQ extends LispFunction {
+
+ // The number of arguments that EQ takes.
+ private static final int NUM_ARGS = 2;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to EQ
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("EQ"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to EQ: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression argOne = argList.getCar(); // first argument
+ Cons cdr = (Cons) argList.getCdr();
+ SExpression argTwo = cdr.getCar(); // second argumnet
+
+ if (argOne.atomp() && argTwo.atomp()) {
+ return ((argOne.toString().equals(argTwo.toString()))
+ ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+ return ((argOne == argTwo) ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+}
diff --git a/src/eval/EQUAL.class b/src/eval/EQUAL.class
new file mode 100644
index 0000000..3fc65a8
Binary files /dev/null and b/src/eval/EQUAL.class differ
diff --git a/src/eval/EQUAL.java b/src/eval/EQUAL.java
new file mode 100644
index 0000000..0e4e02e
--- /dev/null
+++ b/src/eval/EQUAL.java
@@ -0,0 +1,58 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * EQUAL
represents the EQUAL function in Lisp.
+ */
+public class EQUAL extends LispFunction {
+
+ // The number of arguments that EQUAL takes.
+ private static final int NUM_ARGS = 2;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to EQUAL
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("EQUAL"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to EQUAL: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression argOne = argList.getCar(); // first argument
+ Cons cdr = (Cons) argList.getCdr();
+ SExpression argTwo = cdr.getCar(); // second argumnet
+
+ if (argOne.consp() && argTwo.consp()) {
+ Cons listOne = (Cons) argOne;
+ Cons listTwo = (Cons) argTwo;
+ SExpression listOneCar = listOne.getCar();
+ SExpression listTwoCar = listTwo.getCar();
+ SExpression listOneCdr = listOne.getCdr();
+ SExpression listTwoCdr = listTwo.getCdr();
+
+ SExpression carEqual =
+ call(new Cons(listOneCar, LIST.makeList(listTwoCar)));
+ SExpression cdrEqual =
+ call(new Cons(listOneCdr, LIST.makeList(listTwoCdr)));
+
+ return (((carEqual == Symbol.T) && (cdrEqual == Symbol.T))
+ ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+ return ((argOne.toString().equals(argTwo.toString()))
+ ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+}
diff --git a/src/eval/EQUALSP.class b/src/eval/EQUALSP.class
new file mode 100644
index 0000000..4cd829e
Binary files /dev/null and b/src/eval/EQUALSP.class differ
diff --git a/src/eval/EQUALSP.java b/src/eval/EQUALSP.java
new file mode 100644
index 0000000..1c6fb65
--- /dev/null
+++ b/src/eval/EQUALSP.java
@@ -0,0 +1,55 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * EQUALSP
represents the '=' function in Lisp.
+ */
+public class EQUALSP extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("="), argList);
+
+ throw new RuntimeException("too few arguments given to =: " +
+ originalSExpr);
+ }
+
+ SExpression firstArg = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ // make sure that the first argument is a number
+ if (firstArg.numberp()) {
+ LispNumber num1 = (LispNumber) firstArg;
+
+ if (argRest.nullp()) {
+ return Symbol.T;
+ }
+
+ SExpression secondArg = argRest.getCar();
+
+ // make sure that the second argument is a number as well
+ if (secondArg.numberp()) {
+ LispNumber num2 = (LispNumber) secondArg;
+
+ if (num1.getValue() == num2.getValue()) {
+ return call(argRest);
+ }
+
+ return Nil.getUniqueInstance();
+ }
+
+ throw new RuntimeException("=: " + secondArg + " is not a number");
+ }
+
+ throw new RuntimeException("=: " + firstArg + " is not a number");
+ }
+
+}
diff --git a/src/eval/EVAL.class b/src/eval/EVAL.class
new file mode 100644
index 0000000..a661ef5
Binary files /dev/null and b/src/eval/EVAL.class differ
diff --git a/src/eval/EVAL.java b/src/eval/EVAL.java
new file mode 100644
index 0000000..2eacfc2
--- /dev/null
+++ b/src/eval/EVAL.java
@@ -0,0 +1,251 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+import java.util.HashMap;
+
+/**
+ * EVAL
represents the EVAL function in Lisp.
+ */
+public class EVAL extends LispFunction {
+
+ // A table to contain all the built-in and user-defined Lisp functions.
+ private static HashMapfunctionName
if it exists; null
+ * otherwise
+ */
+ public static LispFunction lookupFunction(String functionName) {
+ return functionTable.get(functionName);
+ }
+
+ /**
+ * Look up a symbol's value using its name.
+ *
+ * @param symbolName
+ * the name of the symbol to look up (must not be null)
+ * @return
+ * the value of symbolName
if it has one; null otherwise
+ */
+ public static SExpression lookupSymbol(String symbolName) {
+ if (symbolName.equals("NIL")) {
+ return Nil.getUniqueInstance();
+ } else if (symbolName.equals("T")) {
+ return Symbol.T;
+ } else if (symbolName.startsWith(":")) {
+ return new Symbol(symbolName);
+ }
+
+ return SETF.lookup(symbolName);
+ }
+
+ /**
+ * Determine if the given list is dotted.
+ *
+ * @param list
+ * the list to be tested (must not be null)
+ * @return
+ * true
if list
is dotted; false
+ * otherwise
+ */
+ public static boolean isDotted(Cons list) {
+ if (list.nullp()) {
+ return false;
+ }
+
+ SExpression cdr = list.getCdr();
+
+ if (cdr.listp()) {
+ return isDotted((Cons) cdr);
+ }
+
+ // the cdr of 'list' is not a list, therefore it is dotted
+ return true;
+ }
+
+ /**
+ * Evaluate the given S-expression.
+ *
+ * @param sexpr
+ * the S-expression to evaluate
+ * @return
+ * the value of sexpr
+ */
+ public static SExpression eval(SExpression sexpr) {
+ Cons expList = LIST.makeList(sexpr);
+ EVAL evalFunction = new EVAL();
+
+ return evalFunction.call(expList);
+ }
+
+ // The number of arguments that EVAL takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to EVAL
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("EVAL"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to EVAL: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression arg = argList.getCar();
+
+ if (arg.listp()) {
+ if (arg.consp()) {
+ return evaluateList((Cons) arg);
+ }
+
+ return arg; // 'arg' is NIL
+ }
+
+ if (arg.symbolp()) {
+ SExpression symbolValue = lookupSymbol(arg.toString());
+
+ if (symbolValue != null) {
+ return symbolValue;
+ }
+
+ throw new RuntimeException("variable " + arg + " has no value");
+ }
+
+ return arg; // 'arg' is a NUMBER or a STRING
+ }
+
+ // Evaluate the specified list.
+ //
+ // Parameters: list - the list to evaluate
+ // Returns: the value of 'list'
+ // Precondition: 'list' must not be null.
+ private SExpression evaluateList(Cons list) {
+ SExpression car = list.getCar();
+ SExpression cdr = list.getCdr();
+
+ LispFunction function = lookupFunction(car.toString());
+
+ if (function == null) {
+ // check if the car of the list is a lambda expression
+ if (car.functionp()) {
+ function = ((LambdaExpression) car).getFunction();
+ } else if (LAMBDA.isLambdaExpression(car)) {
+ Cons lexpr = (Cons) car;
+
+ function = LAMBDA.createFunction(lexpr);
+ } else {
+ throw new RuntimeException("undefined function " + car);
+ }
+ }
+
+ // make sure the list of arguments for 'function' is a list
+ if (cdr.listp()) {
+ Cons args = (Cons) cdr;
+
+ // make sure the list of arguments is not dotted
+ if (isDotted(args)) {
+ throw new RuntimeException("argument list given to " + car +
+ " is dotted: " + list);
+ }
+
+ // determine if we should evaluate the arguments that will be
+ // passed to 'function'
+ if (function.evaluateArguments()) {
+ args = evaluateArgList(args);
+ }
+
+ return function.call(args);
+ }
+
+ // the list of arguments is not a list!
+ throw new RuntimeException("argument list given to " + car +
+ " is dotted: " + list);
+ }
+
+ // Evaluate a list of arguments for a function.
+ //
+ // Parameters: arguments - a list of arguments for a function
+ // Returns: a list consisting of the values of the S-expressions found in
+ // 'arguments'
+ // Precondition: 'arguments' must not be null.
+ private Cons evaluateArgList(Cons arguments) {
+ if (arguments.nullp()) {
+ return Nil.getUniqueInstance();
+ }
+
+ SExpression car = eval(arguments.getCar());
+ SExpression cdr = arguments.getCdr();
+
+ if (cdr.listp()) {
+ return new Cons(car, evaluateArgList((Cons) cdr));
+ }
+
+ // remove any parameters found after a dot (put here in case the check
+ // for a dotted parameter list is not done prior to this call)
+ return new Cons(car, Nil.getUniqueInstance());
+ }
+
+}
diff --git a/src/eval/EXIT.class b/src/eval/EXIT.class
new file mode 100644
index 0000000..a5733c5
Binary files /dev/null and b/src/eval/EXIT.class differ
diff --git a/src/eval/EXIT.java b/src/eval/EXIT.java
new file mode 100644
index 0000000..1def13f
--- /dev/null
+++ b/src/eval/EXIT.java
@@ -0,0 +1,26 @@
+package eval;
+
+import parser.*;
+
+public class EXIT extends LispFunction {
+
+ // The number of arguments that EXIT takes.
+ private static final int NUM_ARGS = 0;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to EXIT
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength > NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("EXIT"), argList);
+ String errMsg = "too many arguments given to EXIT: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ System.exit(0);
+ return null;
+ }
+
+}
diff --git a/src/eval/FUNCALL.class b/src/eval/FUNCALL.class
new file mode 100644
index 0000000..60b34d3
Binary files /dev/null and b/src/eval/FUNCALL.class differ
diff --git a/src/eval/FUNCALL.java b/src/eval/FUNCALL.java
new file mode 100644
index 0000000..395dd88
--- /dev/null
+++ b/src/eval/FUNCALL.java
@@ -0,0 +1,31 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * FUNCALL
represents the FUNCALL function in Lisp.
+ */
+public class FUNCALL extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("FUNCALL"), argList);
+
+ throw new RuntimeException("too few arguments given to FUNCALL: " +
+ originalSExpr);
+ }
+
+ SExpression cdr = argList.getCdr();
+ Cons applyArgs = new Cons(argList.getCar(), LIST.makeList(cdr));
+
+ return APPLY.apply(applyArgs);
+ }
+
+}
diff --git a/src/eval/GREATERP.class b/src/eval/GREATERP.class
new file mode 100644
index 0000000..38cddbf
Binary files /dev/null and b/src/eval/GREATERP.class differ
diff --git a/src/eval/GREATERP.java b/src/eval/GREATERP.java
new file mode 100644
index 0000000..2dfc7aa
--- /dev/null
+++ b/src/eval/GREATERP.java
@@ -0,0 +1,55 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * GREATERP
represents the '>' function in Lisp.
+ */
+public class GREATERP extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol(">"), argList);
+
+ throw new RuntimeException("too few arguments given to >: " +
+ originalSExpr);
+ }
+
+ SExpression firstArg = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ // make sure that the first argument is a number
+ if (firstArg.numberp()) {
+ LispNumber num1 = (LispNumber) firstArg;
+
+ if (argRest.nullp()) {
+ return Symbol.T;
+ }
+
+ SExpression secondArg = argRest.getCar();
+
+ // make sure that the second argument is a number as well
+ if (secondArg.numberp()) {
+ LispNumber num2 = (LispNumber) secondArg;
+
+ if (num1.getValue() > num2.getValue()) {
+ return call(argRest);
+ }
+
+ return Nil.getUniqueInstance();
+ }
+
+ throw new RuntimeException(">: " + secondArg + " is not a number");
+ }
+
+ throw new RuntimeException(">: " + firstArg + " is not a number");
+ }
+
+}
diff --git a/src/eval/LAMBDA.class b/src/eval/LAMBDA.class
new file mode 100644
index 0000000..10468c9
Binary files /dev/null and b/src/eval/LAMBDA.class differ
diff --git a/src/eval/LAMBDA.java b/src/eval/LAMBDA.java
new file mode 100644
index 0000000..eaec557
--- /dev/null
+++ b/src/eval/LAMBDA.java
@@ -0,0 +1,108 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * LAMBDA
represents the LAMBDA form in Lisp.
+ */
+public class LAMBDA extends LispFunction {
+
+ /**
+ * Determine if the given S-expression is a lambda expression.
+ *
+ * @param sexpr
+ * the S-expression to test (must not be null)
+ * @return
+ * true
if sexpr
is a valid lambda expression;
+ * false
otherwise
+ */
+ public static boolean isLambdaExpression(SExpression sexpr) {
+ if (sexpr.consp()) {
+ SExpression first = ((Cons) sexpr).getCar();
+
+ return "LAMBDA".equals(first.toString());
+ }
+
+ return false;
+ }
+
+ /**
+ * Create an internal representation of a user-defined function from the
+ * specified lambda expression.
+ *
+ * @param lexpr
+ * the lambda expression to create the function from (must not be null)
+ * @return
+ * an internal representation of a user-defined function created from
+ * lexpr
+ * @throws RuntimeException
+ * Indicates that lexpr
is not a valid lambda expression.
+ */
+ public static UDFunction createFunction(Cons lexpr) {
+ LAMBDA lambda = new LAMBDA();
+ SExpression cdr = lexpr.getCdr();
+
+ // make sure lexpr is a proper list
+ if (! cdr.consp()) {
+ throw new RuntimeException("invalid lambda expression");
+ } else if (EVAL.isDotted((Cons) cdr)) {
+ throw new RuntimeException("dotted lambda expression " + lexpr);
+ }
+
+ Cons rest = (Cons) cdr;
+
+ return lambda.call(rest).getFunction();
+ }
+
+ // The minimum number of arguments that LAMBDA takes.
+ private static final int MIN_ARGS = 2;
+
+ public LambdaExpression call(Cons argList) {
+ // retrieve the number of arguments passed to LAMBDA
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength < MIN_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("LAMBDA"), argList);
+ String errMsg = "too few arguments given to LAMBDA: " +
+ originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression car = argList.getCar();
+
+ // make sure the list of arguments is a proper list
+ if (! car.listp()) {
+ throw new RuntimeException("LAMBDA: " + car + " is not a list");
+ } else if (EVAL.isDotted((Cons) car)) {
+ throw new RuntimeException("LAMBDA: " + car +
+ " must be a proper list");
+ }
+
+ Cons lambdaList = (Cons) car;
+ Cons body = (Cons) argList.getCdr();
+ Cons lexpr = new Cons(new Symbol("LAMBDA"), argList);
+ UDFunction function = new UDFunction(":LAMBDA", lambdaList, body);
+
+ return new LambdaExpression(lexpr, function);
+ }
+
+ /**
+ * Determine if the arguments passed to this Lisp function should be
+ * evaluated.
+ *
+ * @return
+ * false
+ */
+ public boolean evaluateArguments() {
+ return false;
+ }
+
+}
diff --git a/src/eval/LENGTH.class b/src/eval/LENGTH.class
new file mode 100644
index 0000000..e5619b9
Binary files /dev/null and b/src/eval/LENGTH.class differ
diff --git a/src/eval/LENGTH.java b/src/eval/LENGTH.java
new file mode 100644
index 0000000..dcab21b
--- /dev/null
+++ b/src/eval/LENGTH.java
@@ -0,0 +1,69 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * LENGTH
represents the LENGTH function in Lisp.
+ */
+public class LENGTH extends LispFunction {
+
+ /**
+ * Returns the length of the given list.
+ *
+ * @param list
+ * the list to determine the length of
+ * @return
+ * the length of list
+ */
+ public static int getLength(Cons list) {
+ LENGTH lengthFunction = new LENGTH();
+ LispNumber length = lengthFunction.call(LIST.makeList(list));
+
+ return length.getValue();
+ }
+
+ public LispNumber call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
+
+ throw new RuntimeException("too few arguments given to LENGTH: " +
+ originalSExpr);
+ }
+
+ SExpression argCar = argList.getCar();
+ SExpression argCdr = argList.getCdr();
+
+ // make sure we have received only one argument
+ if (! argCdr.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
+
+ throw new RuntimeException("too many arguments given to LENGTH: " +
+ originalSExpr);
+ }
+
+ // make sure that the argument is a list
+ if (argCar.listp()) {
+ Cons arg = (Cons) argCar;
+
+ if (arg.nullp()) {
+ return new LispNumber(0);
+ }
+
+ Cons cdr = LIST.makeList(arg.getCdr());
+ LispNumber cdrLength = call(cdr);
+
+ return new LispNumber(1 + cdrLength.getValue());
+ }
+
+ throw new RuntimeException("LENGTH: a proper list must not end with " +
+ argCar);
+ }
+
+}
diff --git a/src/eval/LESSP.class b/src/eval/LESSP.class
new file mode 100644
index 0000000..bdb80e4
Binary files /dev/null and b/src/eval/LESSP.class differ
diff --git a/src/eval/LESSP.java b/src/eval/LESSP.java
new file mode 100644
index 0000000..162d8c6
--- /dev/null
+++ b/src/eval/LESSP.java
@@ -0,0 +1,55 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * LESSP
represents the '<' function in Lisp.
+ */
+public class LESSP extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("<"), argList);
+
+ throw new RuntimeException("too few arguments given to <: " +
+ originalSExpr);
+ }
+
+ SExpression firstArg = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ // make sure that the first argument is a number
+ if (firstArg.numberp()) {
+ LispNumber num1 = (LispNumber) firstArg;
+
+ if (argRest.nullp()) {
+ return Symbol.T;
+ }
+
+ SExpression secondArg = argRest.getCar();
+
+ // make sure that the second argument is a number as well
+ if (secondArg.numberp()) {
+ LispNumber num2 = (LispNumber) secondArg;
+
+ if (num1.getValue() < num2.getValue()) {
+ return call(argRest);
+ }
+
+ return Nil.getUniqueInstance();
+ }
+
+ throw new RuntimeException("<: " + secondArg + " is not a number");
+ }
+
+ throw new RuntimeException("<: " + firstArg + " is not a number");
+ }
+
+}
diff --git a/src/eval/LET.class b/src/eval/LET.class
new file mode 100644
index 0000000..fa99e32
Binary files /dev/null and b/src/eval/LET.class differ
diff --git a/src/eval/LET.java b/src/eval/LET.java
new file mode 100644
index 0000000..becdbdd
--- /dev/null
+++ b/src/eval/LET.java
@@ -0,0 +1,116 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * LET
represents the LET form in Lisp.
+ */
+public class LET extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ throw new RuntimeException("too few arguments given to LET");
+ }
+
+ // create a new symbol table on top of the current environment to add
+ // all the local variables to
+ SymbolTable environment = new SymbolTable(SETF.getEnvironment());
+
+ SExpression car = argList.getCar();
+ Cons cdr = (Cons) argList.getCdr();
+
+ addVariablesToTable(environment, car);
+ SETF.setEnvironment(environment);
+
+ SExpression retval = Nil.getUniqueInstance();
+
+ // evaluate all S-expression in the body
+ while (cdr.consp()) {
+ retval = EVAL.eval(cdr.getCar());
+ cdr = (Cons) cdr.getCdr();
+ }
+
+ // restore the environment to its original value
+ SETF.setEnvironment(environment.getParent());
+
+ return retval;
+ }
+
+ // Add a list of variables and their values to the specified symbol table.
+ //
+ // Parameters: environment - the symbol table to add the variables and
+ // their values to (must not be null)
+ // vars - a list of variable/value pairs (must be either a
+ // proper list of pairs or NIL)
+ // Throws: RuntimeException - Indicates that 'vars' is not a proper list or
+ // that it contains an member that is not a
+ // variable/value pair.
+ // Precondition: 'environment' and 'vars' must not be null.
+ // Postcondition: All of the variables in 'vars' have been placed into
+ // 'environment' with their values.
+ private void addVariablesToTable(SymbolTable environment,
+ SExpression vars) {
+ // makes sure the list of variable/value pairs is a list
+ if (! vars.listp()) {
+ throw new RuntimeException("LET: " + vars +
+ " is not a properly formatted" +
+ " variable/value pair list");
+ }
+
+ // add all variables in 'vars' to 'environment'
+ while (vars.consp()) {
+ Cons varList = (Cons) vars;
+ SExpression varListCar = varList.getCar();
+
+ // make sure this variable/value pair is a list
+ if (! varListCar.consp()) {
+ throw new RuntimeException("LET: " + varListCar +
+ " is not a properly formatted" +
+ " variable/value pair");
+ }
+
+ Cons varSpec = (Cons) varListCar;
+ SExpression symbol = varSpec.getCar();
+ SExpression varSpecCdr = varSpec.getCdr();
+
+ // make sure this variable pair has a value associated with it
+ if (! varSpecCdr.consp()) {
+ throw new RuntimeException("LET: illegal variable " +
+ "specification " + varSpec);
+ }
+
+ Cons varValue = (Cons) varSpecCdr;
+ SExpression value = varValue.getCar();
+
+ // make sure there are no more members of this variable/value pair
+ // and that 'symbol' is actually a symbol
+ if ((! varValue.getCdr().nullp()) || (! symbol.symbolp())) {
+ throw new RuntimeException("LET: illegal variable " +
+ "specification " + varSpec);
+ }
+
+ environment.put(symbol.toString(), value);
+
+ vars = varList.getCdr();
+ }
+ }
+
+ /**
+ * Determine if the arguments passed to this Lisp function should be
+ * evaluated.
+ *
+ * @return
+ * false
+ */
+ public boolean evaluateArguments() {
+ return false;
+ }
+
+}
diff --git a/src/eval/LIST.class b/src/eval/LIST.class
new file mode 100644
index 0000000..f33586b
Binary files /dev/null and b/src/eval/LIST.class differ
diff --git a/src/eval/LIST.java b/src/eval/LIST.java
new file mode 100644
index 0000000..d801e6e
--- /dev/null
+++ b/src/eval/LIST.java
@@ -0,0 +1,40 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * LIST
represents the LIST function in Lisp.
+ */
+public class LIST extends LispFunction {
+
+ /**
+ * Places the given S-expression into a list.
+ *
+ * @param sexpr
+ * the S-expression to be placed into a list
+ * @return
+ * a list with sexpr
as the car and NIL as the cdr.
+ */
+ public static Cons makeList(SExpression sexpr) {
+ return new Cons(sexpr, Nil.getUniqueInstance());
+ }
+
+ public Cons call(Cons argList) {
+ if (argList.nullp()) {
+ // return NIL if there were no arguments passed to LIST
+ return Nil.getUniqueInstance();
+ }
+
+ SExpression argCar = argList.getCar();
+ Cons argCdr = (Cons) argList.getCdr();
+
+ return new Cons(argCar, call(argCdr));
+ }
+
+}
diff --git a/src/eval/LISTP.class b/src/eval/LISTP.class
new file mode 100644
index 0000000..2bb22ae
Binary files /dev/null and b/src/eval/LISTP.class differ
diff --git a/src/eval/LISTP.java b/src/eval/LISTP.java
new file mode 100644
index 0000000..b2ad0ce
--- /dev/null
+++ b/src/eval/LISTP.java
@@ -0,0 +1,38 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * LISTP
represents the LISTP function in Lisp.
+ */
+public class LISTP extends LispFunction {
+
+ // The number of arguments that LISTP takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to LISTP
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("LISTP"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to LISTP: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression arg = argList.getCar();
+
+ return (arg.listp() ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+}
diff --git a/src/eval/LOAD.class b/src/eval/LOAD.class
new file mode 100644
index 0000000..0529e47
Binary files /dev/null and b/src/eval/LOAD.class differ
diff --git a/src/eval/LOAD.java b/src/eval/LOAD.java
new file mode 100644
index 0000000..4a94235
--- /dev/null
+++ b/src/eval/LOAD.java
@@ -0,0 +1,87 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+import java.io.*;
+
+/**
+ * LOAD
represents the LOAD function in Lisp.
+ */
+public class LOAD extends LispFunction {
+ // The number of arguments that LOAD takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to LOAD
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("LOAD"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to LOAD: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression argCar = argList.getCar();
+
+ // make sure the argument is a string
+ if (! argCar.stringp()) {
+ throw new RuntimeException("LOAD: " + argCar + " is not a string");
+ }
+
+ LispString quotedName = (LispString) argCar;
+ String fileName = quotedName.toString();
+
+ // remove the surrounding quotes from the file name
+ fileName = fileName.substring(1, (fileName.length() - 1));
+
+ return processFile(fileName);
+ }
+
+ // Evaluate all the S-expressions found in the file with the specified
+ // name.
+ //
+ // Parameters: fileName - the name of the file to be evaluated
+ // Returns: 'T' if the file was processed successfully; 'NIL' otherwise
+ private SExpression processFile(String fileName) {
+ LispParser parser = null;
+
+ // attempt to create a new 'LispParser' on the specified file
+ try {
+ parser = new LispParser(new FileInputStream(fileName), fileName);
+ } catch (FileNotFoundException e) {
+ System.out.println("LOAD: could not open " + fileName);
+
+ return Nil.getUniqueInstance();
+ }
+
+ // attempt to evaluate all the S-expressions contained in the file
+ while (! parser.eof()) {
+ try {
+ SExpression sexpr = parser.getSExpr();
+
+ EVAL.eval(sexpr);
+ } catch (RuntimeException e) {
+ System.out.println("LOAD: " + e.getMessage());
+
+ return Nil.getUniqueInstance();
+ } catch (IOException e) {
+ System.out.println("LOAD: " + e.getMessage());
+
+ return Nil.getUniqueInstance();
+ }
+ }
+
+ // success!
+ return Symbol.T;
+ }
+
+}
diff --git a/src/eval/LambdaExpression.class b/src/eval/LambdaExpression.class
new file mode 100644
index 0000000..bc54d92
Binary files /dev/null and b/src/eval/LambdaExpression.class differ
diff --git a/src/eval/LambdaExpression.java b/src/eval/LambdaExpression.java
new file mode 100644
index 0000000..570d162
--- /dev/null
+++ b/src/eval/LambdaExpression.java
@@ -0,0 +1,73 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * This class represents a Lisp FUNCTION in the PL-Lisp implementation.
+ */
+public class LambdaExpression extends SExpression {
+
+ private Cons lexpr;
+ private UDFunction function;
+
+ /**
+ * Create a new FUNCTION with the specified lambda expression and
+ * internal representation.
+ *
+ * @param lexpr
+ * the lambda expression of this FUNCTION
+ * @param function
+ * the internal representation of this FUNCTION
+ */
+ public LambdaExpression(Cons lexpr, UDFunction function) {
+ this.lexpr = lexpr;
+ this.function = function;
+ }
+
+ /**
+ * Test if this S-expression is a FUNCTION.
+ *
+ * @return
+ * true
+ */
+ public boolean functionp() {
+ return true;
+ }
+
+ /**
+ * Retrieve the lambda expression of this FUNCTION.
+ *
+ * @return
+ * the lambda expression of this FUNCTION
+ */
+ public Cons getLExpression() {
+ return lexpr;
+ }
+
+ /**
+ * Retrieve the internal representation of this FUNCTION.
+ *
+ * @return
+ * the user-defined function of this FUNCTION
+ */
+ public UDFunction getFunction() {
+ return function;
+ }
+
+ /**
+ * Returns a string representation of this FUNCTION.
+ *
+ * @return
+ * a string representation of this FUNCTION
+ */
+ public String toString() {
+ return lexpr.toString();
+ }
+
+}
diff --git a/src/eval/LispFunction.class b/src/eval/LispFunction.class
new file mode 100644
index 0000000..a6f8297
Binary files /dev/null and b/src/eval/LispFunction.class differ
diff --git a/src/eval/LispFunction.java b/src/eval/LispFunction.java
new file mode 100644
index 0000000..c861fa2
--- /dev/null
+++ b/src/eval/LispFunction.java
@@ -0,0 +1,44 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * A LispFunction
is an internal representation of a built-in
+ * function in the Lisp programming language.
+ */
+public abstract class LispFunction {
+
+ /**
+ * Call this Lisp function with the given list of arguments.
+ *
+ * @param argList
+ * the list of arguments to pass to this function (MUST BE A PROPER LIST)
+ * @return
+ * the resulting S-expression of calling this function with the specified
+ * arguments
+ * @throws RuntimeException
+ * Indicates that an incorrect number of arguments has been passed to this
+ * function or that one of the arguments is not of the expected type.
+ */
+ public abstract SExpression call(Cons argList);
+
+ /**
+ * Determine if the arguments passed to this Lisp function should be
+ * evaluated. A subclass should override this method to return
+ * false
if it does not want its arguments to be evaluated
+ * prior to being passed.
+ *
+ * @return
+ * true
+ */
+ public boolean evaluateArguments() {
+ return true;
+ }
+
+}
diff --git a/src/eval/MINUS.class b/src/eval/MINUS.class
new file mode 100644
index 0000000..cdd4ee9
Binary files /dev/null and b/src/eval/MINUS.class differ
diff --git a/src/eval/MINUS.java b/src/eval/MINUS.java
new file mode 100644
index 0000000..a913624
--- /dev/null
+++ b/src/eval/MINUS.java
@@ -0,0 +1,60 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * MINUS
represents the '-' function in Lisp.
+ */
+public class MINUS extends LispFunction {
+
+ public SExpression call(Cons argList) {
+ // make sure we have received at least one argument
+ if (argList.nullp()) {
+ Cons originalSExpr = new Cons(new Symbol("-"), argList);
+
+ throw new RuntimeException("too few arguments given to -: " +
+ originalSExpr);
+ }
+
+ SExpression argFirst = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ // make sure that the first argument is a number
+ if (argFirst.numberp()) {
+ LispNumber num1 = (LispNumber) argFirst;
+
+ if (argRest.nullp()) {
+ // there is only one argument, so return the additive
+ // inverse of the number
+ return new LispNumber(- num1.getValue());
+ }
+
+ SExpression argSecond = argRest.getCar();
+
+ // make sure that the next argument is a number as well
+ if (argSecond.numberp()) {
+ LispNumber num2 = (LispNumber) argSecond;
+ LispNumber difference = new LispNumber(num1.getValue() -
+ num2.getValue());
+ SExpression argCddr = argRest.getCdr();
+
+ if (argCddr.consp()) {
+ return call(new Cons(difference, argCddr));
+ }
+
+ return difference;
+ }
+
+ throw new RuntimeException("-: " + argSecond + " is not a number");
+ }
+
+ throw new RuntimeException("-: " + argFirst + " is not a number");
+ }
+
+}
diff --git a/src/eval/MULTIPLY.class b/src/eval/MULTIPLY.class
new file mode 100644
index 0000000..53eab28
Binary files /dev/null and b/src/eval/MULTIPLY.class differ
diff --git a/src/eval/MULTIPLY.java b/src/eval/MULTIPLY.java
new file mode 100644
index 0000000..ac15950
--- /dev/null
+++ b/src/eval/MULTIPLY.java
@@ -0,0 +1,34 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * MULTIPLY
represents the '*' function in Lisp.
+ */
+public class MULTIPLY extends LispFunction {
+
+ public LispNumber call(Cons argList) {
+ if (argList.nullp()) {
+ return new LispNumber(1);
+ }
+
+ SExpression argFirst = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ if (argFirst.numberp()) {
+ LispNumber num1 = (LispNumber) argFirst;
+ LispNumber num2 = call(argRest);
+
+ return new LispNumber(num1.getValue() * num2.getValue());
+ }
+
+ throw new RuntimeException("*: " + argFirst + " is not a number");
+ }
+
+}
diff --git a/src/eval/NULL.class b/src/eval/NULL.class
new file mode 100644
index 0000000..c74646b
Binary files /dev/null and b/src/eval/NULL.class differ
diff --git a/src/eval/NULL.java b/src/eval/NULL.java
new file mode 100644
index 0000000..fd30367
--- /dev/null
+++ b/src/eval/NULL.java
@@ -0,0 +1,38 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * NULL
represents the NULL function in Lisp.
+ */
+public class NULL extends LispFunction {
+
+ // The number of arguments that NULL takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to NULL
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("NULL"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to NULL: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression arg = argList.getCar();
+
+ return (arg.nullp() ? Symbol.T : Nil.getUniqueInstance());
+ }
+
+}
diff --git a/src/eval/PLUS.class b/src/eval/PLUS.class
new file mode 100644
index 0000000..334668c
Binary files /dev/null and b/src/eval/PLUS.class differ
diff --git a/src/eval/PLUS.java b/src/eval/PLUS.java
new file mode 100644
index 0000000..144f46c
--- /dev/null
+++ b/src/eval/PLUS.java
@@ -0,0 +1,34 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * PLUS
represents the '+' function in Lisp.
+ */
+public class PLUS extends LispFunction {
+
+ public LispNumber call(Cons argList) {
+ if (argList.nullp()) {
+ return new LispNumber(0);
+ }
+
+ SExpression argFirst = argList.getCar();
+ Cons argRest = (Cons) argList.getCdr();
+
+ if (argFirst.numberp()) {
+ LispNumber num1 = (LispNumber) argFirst;
+ LispNumber num2 = call(argRest);
+
+ return new LispNumber(num1.getValue() + num2.getValue());
+ }
+
+ throw new RuntimeException("+: " + argFirst + " is not a number");
+ }
+
+}
diff --git a/src/eval/PRINT.class b/src/eval/PRINT.class
new file mode 100644
index 0000000..f02a693
Binary files /dev/null and b/src/eval/PRINT.class differ
diff --git a/src/eval/PRINT.java b/src/eval/PRINT.java
new file mode 100644
index 0000000..f9fedb5
--- /dev/null
+++ b/src/eval/PRINT.java
@@ -0,0 +1,40 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * PRINT
represents the PRINT function in Lisp.
+ */
+public class PRINT extends LispFunction {
+
+ // The number of arguments that PRINT takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to PRINT
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("PRINT"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to PRINT: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression arg = argList.getCar();
+
+ System.out.println(arg);
+
+ return arg;
+ }
+
+}
diff --git a/src/eval/QUOTE.class b/src/eval/QUOTE.class
new file mode 100644
index 0000000..9154e6e
Binary files /dev/null and b/src/eval/QUOTE.class differ
diff --git a/src/eval/QUOTE.java b/src/eval/QUOTE.java
new file mode 100644
index 0000000..960ab3e
--- /dev/null
+++ b/src/eval/QUOTE.java
@@ -0,0 +1,47 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 1
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * QUOTE
represents the QUOTE form in Lisp.
+ */
+public class QUOTE extends LispFunction {
+
+ // The number of arguments that QUOTE takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to QUOTE
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received exactly one argument
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("QUOTE"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to QUOTE: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ return argList.getCar();
+ }
+
+ /**
+ * Determine if the arguments passed to this Lisp function should be
+ * evaluated.
+ *
+ * @return
+ * false
+ */
+ public boolean evaluateArguments() {
+ return false;
+ }
+
+}
diff --git a/src/eval/SETF.class b/src/eval/SETF.class
new file mode 100644
index 0000000..4010526
Binary files /dev/null and b/src/eval/SETF.class differ
diff --git a/src/eval/SETF.java b/src/eval/SETF.java
new file mode 100644
index 0000000..90c25d4
--- /dev/null
+++ b/src/eval/SETF.java
@@ -0,0 +1,112 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * SETF
represents the SETF form in Lisp.
+ */
+public class SETF extends LispFunction {
+
+ private static SymbolTable environment = new SymbolTable();
+
+ /**
+ * Look up the value of a symbol using its name.
+ *
+ * @param symbolName
+ * the name of the symbol to look up
+ * @return
+ * the value of symbolName
if it has one; null otherwise
+ */
+ public static SExpression lookup(String symbolName) {
+ SymbolTable current = environment;
+
+ while (current != null) {
+ if (current.contains(symbolName)) {
+ return current.get(symbolName);
+ }
+
+ current = current.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the current environment to the specified value.
+ *
+ * @param newEnvironment
+ * the value to set the environment to
+ */
+ public static void setEnvironment(SymbolTable newEnvironment) {
+ environment = newEnvironment;
+ }
+
+ /**
+ * Retrieve the current environment.
+ *
+ * @return
+ * the current environment
+ */
+ public static SymbolTable getEnvironment() {
+ return environment;
+ }
+
+ // The number of arguments that SETF takes.
+ private static final int NUM_ARGS = 2;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to SETF
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("SETF"), argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to SETF: " + originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression symbol = argList.getCar();
+
+ // make sure the first argument is a symbol
+ if (! symbol.symbolp()) {
+ throw new RuntimeException("SETF: " + symbol + " is not a symbol");
+ }
+
+ Cons cdr = (Cons) argList.getCdr();
+ SExpression value = EVAL.eval(cdr.getCar());
+
+ SymbolTable current = environment;
+
+ // set 'current' to the symbol table that contains 'symbol' or the
+ // global symbol table if 'symbol' is not in the environment
+ while ((! current.contains(symbol.toString())) &&
+ (current.getParent() != null)) {
+ current = current.getParent();
+ }
+
+ current.put(symbol.toString(), value);
+
+ return value;
+ }
+
+ /**
+ * Determine if the arguments passed to this Lisp function should be
+ * evaluated.
+ *
+ * @return
+ * false
+ */
+ public boolean evaluateArguments() {
+ return false;
+ }
+
+}
diff --git a/src/eval/SYMBOL_FUNCTION.class b/src/eval/SYMBOL_FUNCTION.class
new file mode 100644
index 0000000..4638fb6
Binary files /dev/null and b/src/eval/SYMBOL_FUNCTION.class differ
diff --git a/src/eval/SYMBOL_FUNCTION.java b/src/eval/SYMBOL_FUNCTION.java
new file mode 100644
index 0000000..4a591ee
--- /dev/null
+++ b/src/eval/SYMBOL_FUNCTION.java
@@ -0,0 +1,65 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+
+/**
+ * SYMBOL_FUNCTION
represents the SYMBOL-FUNCTION function in
+ * Lisp.
+ */
+public class SYMBOL_FUNCTION extends LispFunction {
+
+ // The number of arguments that SYMBOL-FUNCTION takes.
+ private static final int NUM_ARGS = 1;
+
+ public SExpression call(Cons argList) {
+ // retrieve the number of arguments passed to SYMBOL-FUNCTION
+ int argListLength = LENGTH.getLength(argList);
+
+ // make sure we have received the proper number of arguments
+ if (argListLength != NUM_ARGS) {
+ Cons originalSExpr = new Cons(new Symbol("SYMBOL-FUNCTION"),
+ argList);
+ String errMsg = "too " +
+ ((argListLength > NUM_ARGS) ? "many" : "few") +
+ " arguments given to SYMBOL-FUNCTION: " +
+ originalSExpr;
+
+ throw new RuntimeException(errMsg);
+ }
+
+ SExpression arg = argList.getCar();
+
+ // make sure the argument is a symbol
+ if (arg.symbolp()) {
+ LispFunction function = EVAL.lookupFunction(arg.toString());
+
+ // make sure the function actually exists
+ if (function != null) {
+ if (function instanceof UDFunction) {
+ // this is a user-defined function
+
+ UDFunction udFunction = (UDFunction) function;
+
+ return udFunction.getLexpr();
+ }
+
+ // this is a built-in function
+
+ return new Symbol("SUBR-" + arg.toString());
+ }
+
+ throw new RuntimeException("SYMBOL-FUNCTION: undefined function " +
+ arg);
+ }
+
+ throw new RuntimeException("SYMBOL-FUNCTION: " + arg +
+ " is not a symbol");
+ }
+
+}
diff --git a/src/eval/SymbolTable.class b/src/eval/SymbolTable.class
new file mode 100644
index 0000000..6e6dbb7
Binary files /dev/null and b/src/eval/SymbolTable.class differ
diff --git a/src/eval/SymbolTable.java b/src/eval/SymbolTable.java
new file mode 100644
index 0000000..aa72b28
--- /dev/null
+++ b/src/eval/SymbolTable.java
@@ -0,0 +1,90 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+import java.util.HashMap;
+
+/**
+ * A SymbolTable
maps symbol names to values.
+ */
+public class SymbolTable {
+
+ private HashMaptrue
if the symbol is in this symbol table;
+ * false
otherwise
+ */
+ public boolean contains(String symbolName) {
+ return table.containsKey(symbolName);
+ }
+
+ /**
+ * Returns the value to which the specified symbol name is mapped in this
+ * symbol table.
+ *
+ * @param symbolName
+ * the name of the symbol whose associated value is to be returned
+ * @return
+ * the value to which this symbol table maps symbolName
, or
+ * null if no mapping exists
+ */
+ public SExpression get(String symbolName) {
+ return table.get(symbolName);
+ }
+
+ /**
+ * Associates the specified symbol name with the specified value in this
+ * symbol table. If the symbol table previously contained a mapping for
+ * this symbol name, the old value has been replaced.
+ *
+ * @param symbolName
+ * the name of the symbol with which the specified value is to be
+ * associated
+ * @param value
+ * the value to be associated with the specified symbol name
+ */
+ public void put(String symbolName, SExpression value) {
+ table.put(symbolName, value);
+ }
+
+ /**
+ * Returns the parent of this symbol table.
+ *
+ * @return
+ * the parent of this symbol table
+ */
+ public SymbolTable getParent() {
+ return parent;
+ }
+
+}
diff --git a/src/eval/UDFunction.class b/src/eval/UDFunction.class
new file mode 100644
index 0000000..afbca9e
Binary files /dev/null and b/src/eval/UDFunction.class differ
diff --git a/src/eval/UDFunction.java b/src/eval/UDFunction.java
new file mode 100644
index 0000000..44e02c8
--- /dev/null
+++ b/src/eval/UDFunction.java
@@ -0,0 +1,122 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter 2
+ */
+
+package eval;
+
+import parser.*;
+import java.util.ArrayList;
+
+/**
+ * A UDFunction
is an internal representation of a user-defined
+ * function in the Lisp programming language.
+ */
+public class UDFunction extends LispFunction {
+
+ // the number of arguments that this user-defined function takes.
+ private final int NUM_ARGS;
+
+ private String name;
+ private Cons body;
+ private Cons lexpr;
+ private SymbolTable environment;
+ private ArrayListLispInterpreter
is an interpreter for the Lisp programming
+ * language. It takes the name of a file as a command-line argument, evaluates
+ * the S-expressions found in the file and then prints the results to the
+ * console. If no file name is provided at the command-line, this program will
+ * read from standard input.
+ */
+public class LispInterpreter {
+
+ private static final String GREETING = "SUNY Potsdam Lisp Interpreter - Version 1.0.1";
+ private static final String PROMPT = "~ ";
+
+ public static final String ANSI_RESET = "\u001B[0m";
+ public static final String ANSI_GREEN = "\u001B[32m";
+
+ /**
+ * Evaluate the S-expressions found in the file given as a command-line
+ * argument and print the results to the console. If no file name was
+ * given, retrieve the S-expressions from standard input.
+ *
+ * @param args
+ * the command-line arguments:
+ * args[0]
- file name (optional)LispParserDriver
is a program that takes the name of a file
+ * as a command-line argument, creates an internal representation of the
+ * S-expressions found in the file and prints them to the console. If no file
+ * name is provided at the command-line, this program will read from standard
+ * input.
+ */
+public class LispParserDriver {
+
+ /**
+ * Create internal representations of the S-expressions found in the file
+ * whose name was given as a command-line argument and print them to the
+ * console. If no file name was given, retrieve the S-expressions from
+ * standard input.
+ *
+ * @param args
+ * the command-line arguments:
+ * args[0]
- file name (optional)LispScannerDriver
is a program that takes the name of a file
+ * as a command-line argument, retrieves all of the Lisp tokens from the file
+ * and prints them to the console. If no file name is provided at the
+ * command-line, this program will read from standard input.
+ */
+public class LispScannerDriver {
+
+ /**
+ * Obtain the Lisp tokens from the file whose name was given as a
+ * command-line argument and print them to the console. If no file name was
+ * given, retrieve the tokens from standard input.
+ *
+ * @param args
+ * the command-line arguments:
+ * args[0]
- file name (optional)true
+ */
+ public boolean atomp() {
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this ATOM.
+ *
+ * @return
+ * a string representation of this ATOM
+ */
+ public String toString() {
+ return text;
+ }
+
+}
diff --git a/src/parser/Cons.class b/src/parser/Cons.class
new file mode 100644
index 0000000..7d5d2c6
Binary files /dev/null and b/src/parser/Cons.class differ
diff --git a/src/parser/Cons.java b/src/parser/Cons.java
new file mode 100644
index 0000000..2402a99
--- /dev/null
+++ b/src/parser/Cons.java
@@ -0,0 +1,111 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+/**
+ * This class represents a Lisp CONS cell in the PL-Lisp implementation.
+ */
+public class Cons extends SExpression {
+
+ private SExpression car;
+ private SExpression cdr;
+
+ /**
+ * Create a new CONS cell with the specified car and cdr.
+ *
+ * @param car
+ * the car of this CONS cell
+ * @param cdr
+ * the cdr of this CONS cell
+ */
+ public Cons(SExpression car, SExpression cdr) {
+ this.car = car;
+ this.cdr = cdr;
+ }
+
+ /**
+ * Retrieve the car of this CONS cell.
+ *
+ * @return
+ * the car of this CONS cell
+ */
+ public SExpression getCar() {
+ return car;
+ }
+
+ /**
+ * Retrieve the cdr of this CONS cell.
+ *
+ * @return
+ * the cdr of this CONS cell
+ */
+ public SExpression getCdr() {
+ return cdr;
+ }
+
+ /**
+ * Set the car of this CONS cell to the specified value.
+ *
+ * @param newCar
+ * the value to assign to the car of this CONS cell
+ */
+ public void setCar(SExpression newCar) {
+ car = newCar;
+ }
+
+ /**
+ * Set the cdr of this CONS cell to the specified value.
+ *
+ * @param newCdr
+ * the value to assign to the cdr of this CONS cell
+ */
+ public void setCdr(SExpression newCdr) {
+ cdr = newCdr;
+ }
+
+ /**
+ * Test if this S-expression is a CONS cell.
+ *
+ * @return
+ * true
+ */
+ public boolean consp() {
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this CONS cell.
+ *
+ * @return
+ * a string representation of this CONS cell
+ */
+ public String toString() {
+ return ("(" + toStringAux());
+ }
+
+ // Returns a string representation of the car of a CONS cell followed by
+ // its cdr. If the cdr of this CONS cell is not a CONS cell itself, this
+ // method places a ')' at the end of its return value. Also, if the cdr of
+ // this CONS cell is NIL, it will not be included in the return value of
+ // this method. When used in conjunction with the 'toString' method of this
+ // class, this method provides a means for creating the correct string
+ // representation of a list.
+ //
+ // Returns: a string representation of the car of a CONS cell followed by
+ // its cdr
+ private String toStringAux() {
+ if (cdr.nullp()) {
+ return (car.toString() + ")");
+ } else if (cdr.consp()) {
+ return (car.toString() + " " + ((Cons) cdr).toStringAux());
+ }
+
+ // the cdr of this CONS cell is not a list
+ return (car.toString() + " . " + cdr.toString() + ")");
+ }
+
+}
diff --git a/src/parser/LispNumber.class b/src/parser/LispNumber.class
new file mode 100644
index 0000000..6f45292
Binary files /dev/null and b/src/parser/LispNumber.class differ
diff --git a/src/parser/LispNumber.java b/src/parser/LispNumber.java
new file mode 100644
index 0000000..34c3bde
--- /dev/null
+++ b/src/parser/LispNumber.java
@@ -0,0 +1,67 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+/**
+ * This class represents a NUMBER in the PL-Lisp implementation.
+ */
+public class LispNumber extends Atom {
+
+ private int value;
+
+ /**
+ * Create a new NUMBER with the specified text.
+ *
+ * @param text
+ * the text representing this NUMBER
+ * @throws IllegalArgumentException
+ * Indicates that text
does not represent a valid integer.
+ */
+ public LispNumber(String text) {
+ super(text.replaceFirst("^0+(?!$)", ""));
+
+ try {
+ this.value = Integer.parseInt(text);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(text +
+ " is not a valid integer");
+ }
+ }
+
+ /**
+ * Create a new NUMBER with the specified value.
+ *
+ * @param value
+ * the integer value of this NUMBER
+ */
+ public LispNumber(int value) {
+ super(Integer.toString(value));
+
+ this.value = value;
+ }
+
+ /**
+ * Test if this S-expression is a NUMBER.
+ *
+ * @return
+ * true
+ */
+ public boolean numberp() {
+ return true;
+ }
+
+ /**
+ * Retrieve the integer value of this NUMBER.
+ *
+ * @return
+ * the integer value of this NUMBER
+ */
+ public int getValue() {
+ return value;
+ }
+
+}
diff --git a/src/parser/LispParser$1.class b/src/parser/LispParser$1.class
new file mode 100644
index 0000000..349ebea
Binary files /dev/null and b/src/parser/LispParser$1.class differ
diff --git a/src/parser/LispParser.class b/src/parser/LispParser.class
new file mode 100644
index 0000000..b428895
Binary files /dev/null and b/src/parser/LispParser.class differ
diff --git a/src/parser/LispParser.java b/src/parser/LispParser.java
new file mode 100644
index 0000000..9a7733f
--- /dev/null
+++ b/src/parser/LispParser.java
@@ -0,0 +1,192 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+import scanner.*;
+import java.io.*;
+
+/**
+ * A LispParser
converts a stream of bytes into internal
+ * representations of Lisp S-expressions. When the end of stream has been
+ * reached the eof
method of this parser will return true.
+ */
+public class LispParser {
+
+ private LispScanner scanner;
+ private Token nextToken;
+
+ // A field to store an exception that has been thrown in the 'eof' method
+ // as a result of reading in the next token from 'scanner'.
+ private Exception delayedException;
+
+ // A field to notify us if the next token has already been stored in
+ // 'nextToken' by the 'eof' method.
+ private boolean nextTokenStored;
+
+ /**
+ * Create a new LispParser
that produces S-expressions from
+ * the specified input stream.
+ *
+ * @param in
+ * the input stream to obtain S-expressions from (must not be
+ * null
)
+ * @param fileName
+ * the name of the file that in
is reading from
+ */
+ public LispParser(InputStream in, String fileName) {
+ scanner = new LispScanner(in, fileName);
+ nextToken = null;
+ delayedException = null;
+ nextTokenStored = false;
+ }
+
+ /**
+ * Determing if this parser has reached the end of its underlying input
+ * stream.
+ *
+ * @return
+ * true
if this parser has reached the end of its underlying
+ * input stream; false
otherwise
+ */
+ public boolean eof() {
+ if (! nextTokenStored) {
+ // attempt to read the next token from 'scanner' and store it in
+ // 'nextToken'
+ try {
+ nextToken = scanner.nextToken();
+ nextTokenStored = true;
+ } catch (Exception e) {
+ // this method should give the illusion of not actually reading
+ // a token, so we store any exceptions thrown as a result of
+ // reading in the next token from 'scanner' (it will be thrown
+ // the next time the 'getSExpr' method is called)
+ delayedException = e;
+
+ if (nextToken == null) {
+ // we have not successfully read in any tokens yet, so we
+ // could not have read in an end-of-file token
+
+ return false;
+ }
+ }
+ }
+
+ return (nextToken.getType() == Token.Type.EOF);
+ }
+
+ /**
+ * Returns the next S-expression from this parser.
+ *
+ * @return
+ * the next S-expression from this parser
+ * @throws RuntimeException
+ * Indicates that an illegal S-expression was encountered in the
+ * underlying input stream.
+ * @throws IOException
+ * Indicates that an I/O error has occurred.
+ */
+ public SExpression getSExpr() throws IOException {
+ if (delayedException != null) {
+ // the 'eof' method has stored an exception for us to throw
+
+ // determine the type of the stored exception and throw it!
+ if (delayedException instanceof IOException) {
+ IOException e = (IOException) delayedException;
+
+ // remove the exception from 'delayedException'
+ delayedException = null;
+
+ throw e;
+ } else if (delayedException instanceof RuntimeException) {
+ RuntimeException e = (RuntimeException) delayedException;
+
+ // remove the exception from 'delayedException'
+ delayedException = null;
+
+ throw e;
+ }
+ }
+
+ if (! nextTokenStored) {
+ // the next token has not been stored in 'nextToken' by the 'eof'
+ // method
+
+ nextToken = scanner.nextToken();
+ } else {
+ // the 'eof' method has been called and has read in the next token
+ // already to determine if we have reached the end-of-file
+
+ nextTokenStored = false;
+ }
+
+ return sExpr();
+ }
+
+ // sExpr ::= NUMBER | IDENTIFIER | RESERVED | STRING | QUOTE_MARK sExpr |
+ // LEFT_PAREN sExprTail
+ //
+ // Returns: an S-expression that matches the rules given above
+ // Throws: RuntimeException - Indicates that an illegal S-expression was
+ // encountered in the underlying input stream.
+ // Throws: IOException - Indicates that an I/O error has occurred.
+ // Precondition: 'nextToken' is not null.
+ private SExpression sExpr() throws IOException {
+ // determine the type of 'nextToken' and create the appropriate
+ // S-expression
+ switch (nextToken.getType()) {
+ case NUMBER:
+ return new LispNumber(nextToken.getText());
+ case IDENTIFIER:
+ case RESERVED:
+ return new Symbol(nextToken.getText());
+ case STRING:
+ return new LispString(nextToken.getText());
+ case QUOTE_MARK:
+ nextToken = scanner.nextToken();
+ SExpression arg = sExpr();
+
+ return new Cons(new Symbol("QUOTE"),
+ new Cons(arg, Nil.getUniqueInstance()));
+ case LEFT_PAREN:
+ return sExprTail();
+ case RIGHT_PAREN:
+ throw new RuntimeException("expression begins with \')\'" +
+ " - line " + nextToken.getLine() +
+ " column " + nextToken.getColumn());
+ case EOF:
+ throw new RuntimeException("end-of-file encountered" +
+ " - line " + nextToken.getLine() +
+ " column " + nextToken.getColumn());
+ default: // unrecognized token type
+ throw new RuntimeException("unrecognizable token" +
+ " - line " + nextToken.getLine() +
+ " column " + nextToken.getColumn());
+ }
+ }
+
+ // sExprTail ::= RIGHT_PAREN | sExpr sExprTail
+ //
+ // Returns: an S-expression that matches the rules given above
+ // Throws: IOException - Indicates that an I/O error has occurred.
+ // Precondition: 'scanner' is not null.
+ private SExpression sExprTail() throws IOException {
+ nextToken = scanner.nextToken();
+
+ // determine the type of 'nextToken' and create the appropriate
+ // S-expression
+ switch (nextToken.getType()) {
+ case RIGHT_PAREN:
+ return Nil.getUniqueInstance();
+ default:
+ SExpression car = sExpr();
+ SExpression cdr = sExprTail();
+
+ return new Cons(car, cdr);
+ }
+ }
+
+}
diff --git a/src/parser/LispString.class b/src/parser/LispString.class
new file mode 100644
index 0000000..49a710f
Binary files /dev/null and b/src/parser/LispString.class differ
diff --git a/src/parser/LispString.java b/src/parser/LispString.java
new file mode 100644
index 0000000..66b56a3
--- /dev/null
+++ b/src/parser/LispString.java
@@ -0,0 +1,34 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+/**
+ * This class represents a STRING in the PL-Lisp implementation.
+ */
+public class LispString extends Atom {
+
+ /**
+ * Create a new STRING with the specified text.
+ *
+ * @param text
+ * the text representing this STRING
+ */
+ public LispString(String text) {
+ super(text);
+ }
+
+ /**
+ * Test if this S-expression is a STRING.
+ *
+ * @return
+ * true
+ */
+ public boolean stringp() {
+ return true;
+ }
+
+}
diff --git a/src/parser/Nil.class b/src/parser/Nil.class
new file mode 100644
index 0000000..73def30
Binary files /dev/null and b/src/parser/Nil.class differ
diff --git a/src/parser/Nil.java b/src/parser/Nil.java
new file mode 100644
index 0000000..5344053
--- /dev/null
+++ b/src/parser/Nil.java
@@ -0,0 +1,104 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+/**
+ * This class represents NIL in the PL-Lisp implementation.
+ */
+public class Nil extends Cons {
+
+ private static Nil uniqueInstance = new Nil();
+
+ /**
+ * Retrieve the single unique instance of NIL.
+ *
+ * @return
+ * the single unique instance of NIL
+ */
+ public static Nil getUniqueInstance() {
+ return uniqueInstance;
+ }
+
+ // We are using the Singleton pattern, so the constructor for 'Nil' is
+ // private.
+ private Nil() {
+ super(null, null);
+
+ // the car and cdr of NIL both refer to NIL
+ super.setCar(this);
+ super.setCdr(this);
+ }
+
+ /**
+ * Test if this S-expression is NULL.
+ *
+ * @return
+ * true
+ */
+ public boolean nullp() {
+ return true;
+ }
+
+ /**
+ * Test if this S-expression is an ATOM.
+ *
+ * @return
+ * true
+ */
+ public boolean atomp() {
+ return true;
+ }
+
+ /**
+ * Test if this S-expression is a CONS cell.
+ *
+ * @return
+ * false
+ */
+ public boolean consp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is a SYMBOL.
+ *
+ * @return
+ * true
+ */
+ public boolean symbolp() {
+ return true;
+ }
+
+ /**
+ * Set the car of this CONS cell to the specified value. This method does
+ * nothing (the car of NIL can not be changed).
+ *
+ * @param newCar
+ * the value to assign to the car of this CONS cell
+ */
+ public void setCar(SExpression newCar) {}
+
+ /**
+ * Set the cdr of this CONS cell to the specified value. This method does
+ * nothing (the cdr of NIL can not be changed).
+ *
+ * @param newCdr
+ * the value to assign to the cdr of this CONS cell
+ */
+ public void setCdr(SExpression newCdr) {}
+
+ /**
+ * Returns a string representation of NIL.
+ *
+ * @return
+ * a string representation of NIL
+ */
+ public String toString() {
+ return "NIL";
+ }
+
+}
diff --git a/src/parser/SExpression.class b/src/parser/SExpression.class
new file mode 100644
index 0000000..2c11139
Binary files /dev/null and b/src/parser/SExpression.class differ
diff --git a/src/parser/SExpression.java b/src/parser/SExpression.java
new file mode 100644
index 0000000..bb8aaa4
--- /dev/null
+++ b/src/parser/SExpression.java
@@ -0,0 +1,122 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+/**
+ * This is the base class for memory in the PL-Lisp implementation.
+ */
+public class SExpression {
+
+ // for mark and sweep garbage collection
+ private boolean marked = false;
+
+ /**
+ * Determine if this SExpression
is marked.
+ *
+ * @return
+ * true
if this SExpression
is marked;
+ * false
otherwise
+ */
+ public final boolean isMarked() {
+ return marked;
+ }
+
+ /**
+ * Set the marked status of this S-expression to the specified value.
+ *
+ * @param value
+ * the value to assign to this S-expression's marked status
+ */
+ public final void setMarked(final boolean value) {
+ marked = value;
+ }
+
+ // Lisp type predicates;
+ // by default, all return false (an SExpression effectively has NO type);
+ // overridden in subclasses to describe their Lisp type
+
+ /**
+ * Test if this S-expression is NULL.
+ *
+ * @return
+ * false
+ */
+ public boolean nullp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is an ATOM.
+ *
+ * @return
+ * false
+ */
+ public boolean atomp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is a CONS cell.
+ *
+ * @return
+ * false
+ */
+ public boolean consp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is a LIST.
+ *
+ * @return
+ * the value of (consp() || nullp())
+ */
+ public boolean listp() {
+ return (consp() || nullp());
+ }
+
+ /**
+ * Test if this S-expression is a NUMBER.
+ *
+ * @return
+ * false
+ */
+ public boolean numberp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is a SYMBOL.
+ *
+ * @return
+ * false
+ */
+ public boolean symbolp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is a FUNCTION.
+ *
+ * @return
+ * false
+ */
+ public boolean functionp() {
+ return false;
+ }
+
+ /**
+ * Test if this S-expression is a STRING.
+ *
+ * @return
+ * false
+ */
+ public boolean stringp() {
+ return false;
+ }
+
+}
diff --git a/src/parser/Symbol.class b/src/parser/Symbol.class
new file mode 100644
index 0000000..0b28c98
Binary files /dev/null and b/src/parser/Symbol.class differ
diff --git a/src/parser/Symbol.java b/src/parser/Symbol.java
new file mode 100644
index 0000000..96f1a96
--- /dev/null
+++ b/src/parser/Symbol.java
@@ -0,0 +1,37 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Parser
+ */
+
+package parser;
+
+/**
+ * This class represents a SYMBOL in the PL-Lisp implementation.
+ */
+public class Symbol extends Atom {
+
+ /** This SYMBOL represents TRUE in the PL-Lisp implementation. */
+ public static final Symbol T = new Symbol("T");
+
+ /**
+ * Create a new SYMBOL with the specified text.
+ *
+ * @param text
+ * the text representing this SYMBOL
+ */
+ public Symbol(String text) {
+ super(text.toUpperCase());
+ }
+
+ /**
+ * Test if this S-expression is a SYMBOL.
+ *
+ * @return
+ * true
+ */
+ public boolean symbolp() {
+ return true;
+ }
+
+}
diff --git a/src/parser/package.html b/src/parser/package.html
new file mode 100644
index 0000000..ed4dbdd
--- /dev/null
+++ b/src/parser/package.html
@@ -0,0 +1,4 @@
+
+ Provides the classes necessary for creating an internal representation of
+ the Lisp programming language.
+
diff --git a/src/scanner/LispFilterStream.class b/src/scanner/LispFilterStream.class
new file mode 100644
index 0000000..4abccba
Binary files /dev/null and b/src/scanner/LispFilterStream.class differ
diff --git a/src/scanner/LispFilterStream.java b/src/scanner/LispFilterStream.java
new file mode 100644
index 0000000..d9838dc
--- /dev/null
+++ b/src/scanner/LispFilterStream.java
@@ -0,0 +1,160 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
+ */
+
+package scanner;
+
+import java.io.InputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+
+/**
+ * A LispFilterStream
is an input stream that returns the bytes
+ * from an underlying input stream with all Lisp comments removed and replaced
+ * with newlines.
+ */
+public class LispFilterStream extends FilterInputStream {
+
+ private boolean inQuote;
+
+ /**
+ * Creates a LispFilterStream
with the specified underlying
+ * InputStream
.
+ *
+ * @param in
+ * the underlying input stream (must not be null
)
+ */
+ public LispFilterStream(InputStream in) {
+ super(in);
+
+ inQuote = false;
+ }
+
+ /**
+ * Reads the next byte of data from this input stream. The value byte is
+ * returned as an int
in the range 0
to
+ * 255
. If no byte is available because the end of the stream
+ * has been reached, the value -1
is returned.
+ *
+ * @return
+ * the next byte of data, or -1
if the end of the stream has
+ * been reached.
+ * @throws IOException
+ * Indicates that an I/O error has occurred.
+ */
+ @Override
+ public int read() throws IOException {
+ int next = super.read();
+
+ if ((next == ';') && (! inQuote)) {
+ // we have entered a comment, consume all the bytes from the
+ // underlying input stream until we reach a newline character or
+ // the end of the stream
+ while ((next != '\n') && (next != -1)) {
+ next = super.read();
+ }
+ } else if (next == '\n') {
+ inQuote = false;
+ } else if (next == '\"') { // we have entered or left a quoted string
+ inQuote = (! inQuote);
+ }
+
+ return next;
+ }
+
+ /**
+ * Reads up to the specified number of data bytes from this input stream
+ * into an array of bytes starting at the specified offset. If no bytes are
+ * available because the end of this stream has been reached then
+ * -1
is returned. Also, If the specified number of bytes is
+ * more than the number of remaining data bytes in this input stream, then
+ * only the number of remaining data bytes are copied into the byte array.
+ *
+ * @param b
+ * the buffer into which the data bytes are read (must not be
+ * null
)
+ * @param off
+ * the start offset in b
at which the data is written (must
+ * be >= 0
and < b.length
)
+ * @param len
+ * the maximum number of bytes to read into b
(len +
+ * off
must be < b.length
)
+ * @return
+ * the total number of bytes read into the buffer, or -1
if
+ * there is no more data because the end of the stream has been reached
+ * @throws IOException
+ * Indicates that the first byte could not be read into b
+ * for some reason other than reaching the end of the stream.
+ * @throws IndexOutOfBoundsException
+ * Indicates that this method attempted to access an index in
+ * b
that was either negative or greater than or equal to
+ * b.length
as a result of the given parameters.
+ * @throws NullPointerException
+ * Indicates that b
is null
.
+ */
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int bytesRead = 0;
+
+ // make sure we are supposed to read at least one byte into 'b'
+ if (len > 0) {
+ int next = read();
+
+ if (next == -1) {
+ // there are no more bytes to read from this input stream
+
+ return -1;
+ }
+
+ int i = off;
+
+ while (next != -1) {
+ ++bytesRead;
+
+ b[i++] = (byte) next;
+
+ if (i >= (off + len)) { // we have read 'len' bytes into 'b'
+ break;
+ }
+
+ try {
+ next = read();
+ } catch (IOException e) {
+ // treat this exception like an end of stream
+
+ break;
+ }
+ }
+ }
+
+ return bytesRead;
+ }
+
+ /**
+ * Skip over and discard the specified number of bytes from this input
+ * stream. This method may, for a variety of reasons, end up skipping some
+ * smaller number of bytes, possibly 0
. The actual number of
+ * bytes skipped is returned.
+ *
+ * @param n
+ * the number of bytes to be skipped
+ * @return
+ * the actual number of bytes skipped
+ * @throws IOException
+ * Indicates that an I/O error has occurred.
+ */
+ @Override
+ public long skip (long n) throws IOException {
+ long bytesSkipped = 0;
+
+ while ((n > 0) && (read() != -1)) {
+ ++bytesSkipped;
+ --n;
+ }
+
+ return bytesSkipped;
+ }
+
+}
diff --git a/src/scanner/LispScanner.class b/src/scanner/LispScanner.class
new file mode 100644
index 0000000..fc19894
Binary files /dev/null and b/src/scanner/LispScanner.class differ
diff --git a/src/scanner/LispScanner.java b/src/scanner/LispScanner.java
new file mode 100644
index 0000000..df6df9b
--- /dev/null
+++ b/src/scanner/LispScanner.java
@@ -0,0 +1,320 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
+ */
+
+package scanner;
+
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+
+/**
+ * A LispScanner
converts a stream of bytes into a stream of Lisp
+ * tokens. When the end of stream has been reached a token with a type of
+ * Token.Type.EOF
is returned from the nextToken
+ * method of this scanner.
+ */
+public class LispScanner {
+
+ private LispFilterStream inStream;
+ private Token currToken;
+ private String fileName;
+ private int line;
+ private int column;
+
+ /**
+ * Create a new LispScanner
that produces Lisp tokens from the
+ * specified input stream.
+ *
+ * @param in
+ * the input stream to obtain Lisp tokens from (must not be
+ * null
)
+ * @param fileName
+ * the name of the file that in
is reading from
+ */
+ public LispScanner(InputStream in, String fileName) {
+ this.inStream = new LispFilterStream(new BufferedInputStream(in));
+ this.currToken = null;
+ this.fileName = fileName;
+ this.line = 1;
+ this.column = 0;
+ }
+
+ /**
+ * Returns the same Lisp token returned from the last call to the
+ * nextToken
method of this scanner. In the case that no calls
+ * to nextToken
have been made yet, this method returns
+ * null
.
+ *
+ * @return
+ * the last Lisp token returned from this scanner or null
(if
+ * no tokens have been returned from this scanner yet)
+ */
+ public Token getCurrToken() {
+ return currToken;
+ }
+
+ /**
+ * Returns the next Lisp token from this scanner.
+ *
+ * @return
+ * the next Lisp token from this scanner.
+ * @throws RuntimeException
+ * Indicates that an illegal character or an unterminated quoted string
+ * was encountered in the input stream (not counting comments).
+ * @throws IOException
+ * Indicates that an I/O error has occurred.
+ */
+ public Token nextToken() throws IOException {
+ currToken = retrieveNextToken();
+
+ return currToken;
+ }
+
+ // Retrieve the next Lisp token from 'inStream'.
+ //
+ // Returns: the next Lisp token found in 'inStream'
+ // Precondition: 'inStream' must not be null.
+ // Throws: RuntimeException - Indicates that an illegal character or an
+ // unterminated quoted string was encountered in
+ // 'inStream'.
+ // Throws: IOException - Indicates that an I/O error has occurred.
+ private Token retrieveNextToken() throws IOException {
+ int c;
+
+ while ((c = inStream.read()) != -1) {
+ char nextChar = (char) c;
+
+ ++column;
+
+ // determine the type of the Lisp token from the character obtained
+ // from 'inStream'
+ switch (nextChar) {
+ case '\n':
+ // we have hit a new line so increment 'line' and reset
+ // 'column'
+ ++line;
+ column = 0;
+
+ break;
+ case '(':
+ return new Token(Token.Type.LEFT_PAREN,
+ "(",
+ fileName,
+ line,
+ column);
+ case ')':
+ return new Token(Token.Type.RIGHT_PAREN,
+ ")",
+ fileName,
+ line,
+ column);
+ case '\'':
+ return new Token(Token.Type.QUOTE_MARK,
+ "\'",
+ fileName,
+ line,
+ column);
+ case '\"':
+ return retrieveString(nextChar);
+ default:
+ if (Character.isWhitespace(nextChar)) { // skip whitespace
+ continue;
+ } else if (Character.isDigit(nextChar)) { // number
+ return retrieveNumber(nextChar);
+ } else if (isLegalIdChar(nextChar)) { // identifier
+ return retrieveIdentifier(nextChar);
+ } else {
+ // 'nextChar' can not start any Lisp token
+
+ throw new RuntimeException("illegal character " +
+ "\'" + nextChar + "\'" +
+ " - line " + line +
+ " column " + column);
+ }
+ }
+ }
+
+ // we have reached the end of 'inStream' so we return an end-of-file
+ // token
+ return new Token(Token.Type.EOF, "EOF", fileName, line, column);
+ }
+
+ // Retrieve a quoted string token from 'inStream'.
+ //
+ // Parameters: firstDoubleQuote - the opening double quote of this quoted
+ // string
+ // Returns: a quoted string token obtained from 'instream'
+ // Throws: RuntimeException - Indicates that this quoted string was
+ // missing its terminating double quote.
+ // Throws: IOException - Indicates that an I/O error has occurred.
+ // Precondition: 'firstDoubleQuote' must be the leading double quote
+ // character of this quoted string and 'inStream' must not
+ // be null.
+ private Token retrieveString(char firstDoubleQuote) throws IOException {
+ StringBuffer text = new StringBuffer();
+ int startLine = line;
+ int startColumn = column;
+ char prevChar = firstDoubleQuote;
+
+ text.append(firstDoubleQuote);
+
+ int c;
+
+ while ((c = inStream.read()) != -1) {
+ char nextChar = (char) c;
+
+ ++column;
+ text.append(nextChar);
+
+ switch(nextChar) {
+ case '\n':
+ ++line;
+ column = 0;
+
+ break;
+ case '\"':
+ if (prevChar != '\\') {
+ // we have found the terminating double quote
+
+ return new Token(Token.Type.STRING,
+ text.toString(),
+ fileName,
+ startLine,
+ startColumn);
+ }
+
+ // this is an escaped double quote
+ }
+
+ prevChar = nextChar;
+ }
+
+ // the end of 'inStream' was reached before the terminating double
+ // quote
+
+ throw new RuntimeException("unterminated quoted string" +
+ " - line " + startLine +
+ " column " + startColumn);
+ }
+
+ // Retrieve a number token from 'inStream'.
+ //
+ // Parameters: firstDigit - the first digit of this number
+ // Returns: a number token obtained from 'inStream'
+ // Throws: IOException - Indicates that an I/O error has occurred.
+ // Precondition: 'firstDigit' must be the first digit of this number and
+ // 'inStream' must not be null.
+ private Token retrieveNumber(char firstDigit) throws IOException {
+ StringBuffer text = new StringBuffer();
+ int startColumn = column;
+
+ text.append(firstDigit);
+ inStream.mark(1);
+
+ int c;
+
+ while ((c = inStream.read()) != -1) {
+ char nextChar = (char) c;
+
+ if (Character.isDigit(nextChar)) {
+ // 'nextChar' is a digit in this number
+
+ text.append(nextChar);
+ ++column;
+ } else {
+ // we have reached the end of the number
+
+ inStream.reset(); // unread the last character
+
+ return new Token(Token.Type.NUMBER,
+ text.toString(),
+ fileName,
+ line,
+ startColumn);
+ }
+
+ inStream.mark(1);
+ }
+
+ // there are no more bytes to be read from 'inStream' after this number
+ // token
+
+ return new Token(Token.Type.NUMBER,
+ text.toString(),
+ fileName,
+ line,
+ startColumn);
+ }
+
+ // Retrieve an identifier token from 'inStream'.
+ //
+ // Parameters: firstChar - the first character of this identifier
+ // Returns: an identifier token obtained from 'inStream'
+ // Throws: IOException - Indicates that an I/O error has occurred.
+ // Precondition: 'firsChar' must be the first character of this identifier
+ // and 'inStream' must not be null.
+ private Token retrieveIdentifier(char firstChar) throws IOException {
+ StringBuffer text = new StringBuffer();
+ int startColumn = column;
+
+ text.append(firstChar);
+ inStream.mark(1);
+
+ int c;
+
+ while ((c = inStream.read()) != -1) {
+ char nextChar = (char) c;
+
+ if (isLegalIdChar(nextChar)) {
+ // 'nextChar' is part of the identifier
+
+ text.append(nextChar);
+ ++column;
+ } else {
+ // we have reached the end of this identifier
+
+ inStream.reset(); // unread the last character
+
+ return new Token(Token.Type.IDENTIFIER,
+ text.toString(),
+ fileName,
+ line,
+ startColumn);
+ }
+
+ inStream.mark(1);
+ }
+
+ // there are no more bytes to be read from 'inStream' after this
+ // identifier token
+
+ return new Token(Token.Type.IDENTIFIER,
+ text.toString(),
+ fileName,
+ line,
+ startColumn);
+ }
+
+ // Test if a character is legal to be contained within an identifier in
+ // Lisp.
+ //
+ // Returns: 'true' if the character can be found within an identifier in
+ // Lisp; 'false' otherwise
+ private boolean isLegalIdChar(char c) {
+ return ((! Character.isWhitespace(c)) && (c != '\"')
+ && (c != '\'')
+ && (c != '\\')
+ && (c != '`')
+ && (c != '(')
+ && (c != ')')
+ && (c != '[')
+ && (c != ']')
+ && (c != '#')
+ && (c != '.')
+ && (c != ';'));
+ }
+
+}
diff --git a/src/scanner/Token$Type.class b/src/scanner/Token$Type.class
new file mode 100644
index 0000000..6f4aff2
Binary files /dev/null and b/src/scanner/Token$Type.class differ
diff --git a/src/scanner/Token.class b/src/scanner/Token.class
new file mode 100644
index 0000000..ec0cbe6
Binary files /dev/null and b/src/scanner/Token.class differ
diff --git a/src/scanner/Token.java b/src/scanner/Token.java
new file mode 100644
index 0000000..c410102
--- /dev/null
+++ b/src/scanner/Token.java
@@ -0,0 +1,126 @@
+/*
+ * Name: Mike Cifelli
+ * Course: CIS 443 - Programming Languages
+ * Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
+ */
+
+package scanner;
+
+/**
+ * A Token
represents a token in Common Lisp.
+ */
+public class Token {
+
+ /**
+ * An enumeration representing all of the types of tokens found in Common
+ * Lisp.
+ */
+ public enum Type {
+ /** A left parenthesis token */
+ LEFT_PAREN,
+
+ /** A right parenthesis token */
+ RIGHT_PAREN,
+
+ /** A quoted string token */
+ STRING,
+
+ /** A quote mark */
+ QUOTE_MARK,
+
+ /** A number token */
+ NUMBER,
+
+ /** A reserved word token */
+ RESERVED,
+
+ /** An identifier token */
+ IDENTIFIER,
+
+ /** An end-of-file token */
+ EOF
+ }
+
+ private Type type;
+ private String text;
+ private String fName;
+ private int line;
+ private int column;
+
+ /**
+ * Create a new token with the specified type, text, file name, line number
+ * and column number.
+ *
+ * @param type
+ * the type of this token
+ * @param text
+ * the text associated with this token
+ * @param fName
+ * the name of the file that this token is located in
+ * @param line
+ * the line number that this token is found on
+ * @param column
+ * the column number that this token is found on
+ */
+ public Token(Type type, String text, String fName, int line, int column) {
+ this.type = type;
+ this.text = text;
+ this.fName = fName;
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * Accessor method to determine the type of this token.
+ *
+ * @return
+ * the type of this token
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Accessor method to determine the text associated with this token.
+ *
+ * @return
+ * the text associated with this token
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Accessor method to determine the name of the file that this token was
+ * located in.
+ *
+ * @return
+ * the name of the file that this token was located in
+ */
+ public String getFName() {
+ return fName;
+ }
+
+ /**
+ * Accessor method to determine the line number that this token was found
+ * on.
+ *
+ * @return
+ * the line number this token was found on
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * Accessor method to determine the column number that this token was found
+ * on.
+ *
+ * @return
+ * the column number this token was found on
+ */
+ public int getColumn() {
+ return column;
+ }
+
+}
diff --git a/src/scanner/package.html b/src/scanner/package.html
new file mode 100644
index 0000000..6be4f13
--- /dev/null
+++ b/src/scanner/package.html
@@ -0,0 +1,4 @@
+
+ Provides the classes necessary to perform a lexical analysis of the Lisp
+ programming language.
+
diff --git a/test/scanner/LispFilterStreamTester.java b/test/scanner/LispFilterStreamTester.java
new file mode 100644
index 0000000..3de7dc6
--- /dev/null
+++ b/test/scanner/LispFilterStreamTester.java
@@ -0,0 +1,37 @@
+package scanner;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class LispFilterStreamTester {
+
+ private LispFilterStream lispFilterStream;
+
+ @Before
+ public void setUp() throws Exception {
+ lispFilterStream = new LispFilterStream(null);
+ }
+
+ @Test
+ public void testRead() {
+ fail("Not yet implemented");
+ }
+
+ @Test
+ public void testReadByteArrayIntInt() {
+ fail("Not yet implemented");
+ }
+
+ @Test
+ public void testSkip() {
+ fail("Not yet implemented");
+ }
+
+ @Test
+ public void testLispFilterStream() {
+ fail("Not yet implemented");
+ }
+
+}