Refactored code for eclipse

This commit is contained in:
Mike Cifelli 2016-12-07 16:38:26 -05:00
parent cee46a41b9
commit b875727361
111 changed files with 3967 additions and 0 deletions

10
.classpath Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="build/classes"/>
</classpath>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?><launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_AUTO_TARGETS" value="jar,"/>
<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_CLEAN_TARGETS" value="clean,"/>
<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_MANUAL_TARGETS" value="jar,"/>
<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>
<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
<booleanAttribute key="org.eclipse.ant.uiSET_INPUTHANDLER" value="false"/>
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${workspace}"/>
<booleanAttribute key="org.eclipse.debug.core.capture_output" value="false"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON" value="false"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="LispInterpreter"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/LispInterpreter/build.xml}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="incremental,auto,clean"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
</launchConfiguration>

23
.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>LispInterpreter</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<installed facet="java" version="1.8"/>
</faceted-project>

Binary file not shown.

View File

@ -0,0 +1,50 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package error;
import java.text.MessageFormat;
/**
* <code>ErrorManager</code> 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 <code>level &gt;= CRITICAL_LEVEL</code> 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);
}
}
}

3
src/error/package.html Normal file
View File

@ -0,0 +1,3 @@
<body>
Provides a class for managing errors in the Lisp Interpreter.
</body>

BIN
src/eval/APPLY.class Normal file

Binary file not shown.

74
src/eval/APPLY.java Normal file
View File

@ -0,0 +1,74 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>APPLY</code> 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 <code>argList</code>
*/
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");
}
}

BIN
src/eval/ATOM.class Normal file

Binary file not shown.

38
src/eval/ATOM.java Normal file
View File

@ -0,0 +1,38 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>ATOM</code> 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());
}
}

BIN
src/eval/CAR.class Normal file

Binary file not shown.

46
src/eval/CAR.java Normal file
View File

@ -0,0 +1,46 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>CAR</code> 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");
}
}

BIN
src/eval/CDR.class Normal file

Binary file not shown.

46
src/eval/CDR.java Normal file
View File

@ -0,0 +1,46 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>CDR</code> 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");
}
}

BIN
src/eval/COND.class Normal file

Binary file not shown.

73
src/eval/COND.java Normal file
View File

@ -0,0 +1,73 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>COND</code> 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
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

BIN
src/eval/CONS.class Normal file

Binary file not shown.

44
src/eval/CONS.java Normal file
View File

@ -0,0 +1,44 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>CONS</code> 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);
}
}

BIN
src/eval/DEFUN.class Normal file

Binary file not shown.

82
src/eval/DEFUN.java Normal file
View File

@ -0,0 +1,82 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import java.util.HashMap;
/**
* <code>DEFUN</code> 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();
HashMap<String, LispFunction> functionTable = EVAL.getFunctionTable();
// give a warning if this function has already been defined
if (functionTable.containsKey(name.toString())) {
System.out.println("WARNING: redefining function " +
name.toString());
}
// place the function in the function table
functionTable.put(name.toString(),
new UDFunction(name.toString(), lambdaList, body));
return name;
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

BIN
src/eval/DIVIDE.class Normal file

Binary file not shown.

60
src/eval/DIVIDE.java Normal file
View File

@ -0,0 +1,60 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>DIVIDE</code> 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");
}
}

BIN
src/eval/EQ.class Normal file

Binary file not shown.

45
src/eval/EQ.java Normal file
View File

@ -0,0 +1,45 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>EQ</code> 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());
}
}

BIN
src/eval/EQUAL.class Normal file

Binary file not shown.

58
src/eval/EQUAL.java Normal file
View File

@ -0,0 +1,58 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>EQUAL</code> 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());
}
}

BIN
src/eval/EQUALSP.class Normal file

Binary file not shown.

55
src/eval/EQUALSP.java Normal file
View File

@ -0,0 +1,55 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>EQUALSP</code> 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");
}
}

BIN
src/eval/EVAL.class Normal file

Binary file not shown.

251
src/eval/EVAL.java Normal file
View File

@ -0,0 +1,251 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
import java.util.HashMap;
/**
* <code>EVAL</code> 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 HashMap<String, LispFunction> functionTable =
new HashMap<String, LispFunction>();
static {
// place all of the built-in functions into the function table
functionTable.put("*", new MULTIPLY());
functionTable.put("+", new PLUS());
functionTable.put("-", new MINUS());
functionTable.put("/", new DIVIDE());
functionTable.put("<", new LESSP());
functionTable.put("=", new EQUALSP());
functionTable.put(">", new GREATERP());
functionTable.put("APPLY", new APPLY());
functionTable.put("ATOM", new ATOM());
functionTable.put("CAR", new CAR());
functionTable.put("CDR", new CDR());
functionTable.put("COND", new COND());
functionTable.put("CONS", new CONS());
functionTable.put("DEFUN", new DEFUN());
functionTable.put("EQ", new EQ());
functionTable.put("EQUAL", new EQUAL());
functionTable.put("EVAL", new EVAL());
functionTable.put("EXIT", new EXIT());
functionTable.put("FIRST", new CAR());
functionTable.put("FUNCALL", new FUNCALL());
functionTable.put("GREATERP", new GREATERP());
functionTable.put("LAMBDA", new LAMBDA());
functionTable.put("LENGTH", new LENGTH());
functionTable.put("LET", new LET());
functionTable.put("LIST", new LIST());
functionTable.put("LISTP", new LISTP());
functionTable.put("LOAD", new LOAD());
functionTable.put("NULL", new NULL());
functionTable.put("PRINT", new PRINT());
functionTable.put("QUOTE", new QUOTE());
functionTable.put("REST", new CDR());
functionTable.put("SETF", new SETF());
functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION());
}
/**
* Retrieve the function table.
*
* @return
* the function table
*/
public static HashMap<String, LispFunction> getFunctionTable() {
return functionTable;
}
/**
* Look up a function by its name.
*
* @param functionName
* the name of the function to look up
* @return
* the function with the name <code>functionName</code> 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 <code>symbolName</code> 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
* <code>true</code> if <code>list</code> is dotted; <code>false</code>
* 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 <code>sexpr</code>
*/
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());
}
}

BIN
src/eval/EXIT.class Normal file

Binary file not shown.

26
src/eval/EXIT.java Normal file
View File

@ -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;
}
}

BIN
src/eval/FUNCALL.class Normal file

Binary file not shown.

31
src/eval/FUNCALL.java Normal file
View File

@ -0,0 +1,31 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>FUNCALL</code> 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);
}
}

BIN
src/eval/GREATERP.class Normal file

Binary file not shown.

55
src/eval/GREATERP.java Normal file
View File

@ -0,0 +1,55 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>GREATERP</code> represents the '&gt;' 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");
}
}

BIN
src/eval/LAMBDA.class Normal file

Binary file not shown.

108
src/eval/LAMBDA.java Normal file
View File

@ -0,0 +1,108 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>LAMBDA</code> 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
* <code>true</code> if <code>sexpr</code> is a valid lambda expression;
* <code>false</code> 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
* <code>lexpr</code>
* @throws RuntimeException
* Indicates that <code>lexpr</code> 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
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

BIN
src/eval/LENGTH.class Normal file

Binary file not shown.

69
src/eval/LENGTH.java Normal file
View File

@ -0,0 +1,69 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LENGTH</code> 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 <code>list</code>
*/
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);
}
}

BIN
src/eval/LESSP.class Normal file

Binary file not shown.

55
src/eval/LESSP.java Normal file
View File

@ -0,0 +1,55 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LESSP</code> represents the '&lt;' 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");
}
}

BIN
src/eval/LET.class Normal file

Binary file not shown.

116
src/eval/LET.java Normal file
View File

@ -0,0 +1,116 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>LET</code> 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
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

BIN
src/eval/LIST.class Normal file

Binary file not shown.

40
src/eval/LIST.java Normal file
View File

@ -0,0 +1,40 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LIST</code> 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 <code>sexpr</code> 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));
}
}

BIN
src/eval/LISTP.class Normal file

Binary file not shown.

38
src/eval/LISTP.java Normal file
View File

@ -0,0 +1,38 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LISTP</code> 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());
}
}

BIN
src/eval/LOAD.class Normal file

Binary file not shown.

87
src/eval/LOAD.java Normal file
View File

@ -0,0 +1,87 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import java.io.*;
/**
* <code>LOAD</code> 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;
}
}

Binary file not shown.

View File

@ -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
* <code>true</code>
*/
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();
}
}

BIN
src/eval/LispFunction.class Normal file

Binary file not shown.

View File

@ -0,0 +1,44 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* A <code>LispFunction</code> 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
* <code>false</code> if it does not want its arguments to be evaluated
* prior to being passed.
*
* @return
* <code>true</code>
*/
public boolean evaluateArguments() {
return true;
}
}

BIN
src/eval/MINUS.class Normal file

Binary file not shown.

60
src/eval/MINUS.java Normal file
View File

@ -0,0 +1,60 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>MINUS</code> 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");
}
}

BIN
src/eval/MULTIPLY.class Normal file

Binary file not shown.

34
src/eval/MULTIPLY.java Normal file
View File

@ -0,0 +1,34 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>MULTIPLY</code> 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");
}
}

BIN
src/eval/NULL.class Normal file

Binary file not shown.

38
src/eval/NULL.java Normal file
View File

@ -0,0 +1,38 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>NULL</code> 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());
}
}

BIN
src/eval/PLUS.class Normal file

Binary file not shown.

34
src/eval/PLUS.java Normal file
View File

@ -0,0 +1,34 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>PLUS</code> 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");
}
}

BIN
src/eval/PRINT.class Normal file

Binary file not shown.

40
src/eval/PRINT.java Normal file
View File

@ -0,0 +1,40 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>PRINT</code> 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;
}
}

BIN
src/eval/QUOTE.class Normal file

Binary file not shown.

47
src/eval/QUOTE.java Normal file
View File

@ -0,0 +1,47 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>QUOTE</code> 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
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

BIN
src/eval/SETF.class Normal file

Binary file not shown.

112
src/eval/SETF.java Normal file
View File

@ -0,0 +1,112 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>SETF</code> 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 <code>symbolName</code> 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
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

Binary file not shown.

View File

@ -0,0 +1,65 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>SYMBOL_FUNCTION</code> 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");
}
}

BIN
src/eval/SymbolTable.class Normal file

Binary file not shown.

90
src/eval/SymbolTable.java Normal file
View File

@ -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 <code>SymbolTable</code> maps symbol names to values.
*/
public class SymbolTable {
private HashMap<String, SExpression> table;
private SymbolTable parent;
/**
* Create a new symbol table with no parent.
*/
public SymbolTable() {
this(null);
}
/**
* Create a new symbol table with the specified parent.
*
* @param parent
* the parent of this symbol table
*/
public SymbolTable(SymbolTable parent) {
this.table = new HashMap<String, SExpression>();
this.parent = parent;
}
/**
* Determine if the specified symbol name is in this symbol table.
*
* @param symbolName
* the name of the symbol to look up
* @return
* <code>true</code> if the symbol is in this symbol table;
* <code>false</code> 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 <code>symbolName</code>, 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;
}
}

BIN
src/eval/UDFunction.class Normal file

Binary file not shown.

122
src/eval/UDFunction.java Normal file
View File

@ -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 <code>UDFunction</code> 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 ArrayList<String> parameters;
/**
* Create a new user-defined function with the specified name, lambda list
* and body.
*
* @param name
* the name of this user-defined function
* @param lambdaList
* a list of the formal parameters of this user-defined function (MUST BE
* A PROPER LIST)
* @param body
* the body of this user-defined function (MUST BE A PROPER LIST)
*/
public UDFunction(String name, Cons lambdaList, Cons body) {
this.name = name;
this.body = body;
this.lexpr = new Cons(new Symbol(name), new Cons(lambdaList, body));
this.environment = SETF.getEnvironment();
this.parameters = new ArrayList<String>();
// retrieve the names of all the formal parameters of this function
while (lambdaList.consp()) {
this.parameters.add(lambdaList.getCar().toString());
lambdaList = (Cons) lambdaList.getCdr();
}
this.NUM_ARGS = this.parameters.size();
}
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to this 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(name), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to " + name + ": " +
originalSExpr;
throw new RuntimeException(errMsg);
}
// push a new symbol table onto this function's environment (for its
// parameters)
environment = new SymbolTable(environment);
// bind the values of the arguments to the formal parameter names
for (String param : parameters) {
SExpression currentArg = argList.getCar();
environment.put(param, currentArg);
argList = (Cons) argList.getCdr();
}
// store the environment of the S-expression that called this function
// (the current environment)
SymbolTable currentEnvironment = SETF.getEnvironment();
// replace the current environment with the environment of this
// function
SETF.setEnvironment(environment);
Cons currentSExpression = body;
SExpression retval = null;
// evaluate all the S-expressions making up this function's body
while (currentSExpression.consp()) {
retval = EVAL.eval(currentSExpression.getCar());
currentSExpression = (Cons) currentSExpression.getCdr();
}
// replace the environment of the S-expression that called this
// function
SETF.setEnvironment(currentEnvironment);
// remove the bindings of the arguments to the formal parameter names
// in the environment of this function
environment = new SymbolTable(environment.getParent());
return retval;
}
/**
* Return the lambda expression that represents this user-defined function.
*
* @return
* the lambda expression that represents this user-defined function
*/
public Cons getLexpr() {
return lexpr;
}
}

4
src/eval/package.html Normal file
View File

@ -0,0 +1,4 @@
<body>
Provides functions and forms to be used during the evaluation of an
S-expression.
</body>

Binary file not shown.

View File

@ -0,0 +1,91 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package main;
import parser.*;
import eval.*;
import error.ErrorManager;
import java.io.*;
import java.text.MessageFormat;
/**
* <code>LispInterpreter</code> 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:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
*/
public static void main(String[] args) {
LispParser parser = null;
boolean interactive = false;
if (args.length > 0) {
// a file name was given at the command-line, attempt to create a
// 'LispParser' on it
try {
parser = new LispParser(new FileInputStream(args[0]), args[0]);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(), ErrorManager.CRITICAL_LEVEL);
}
} else {
// no file name was given, create a 'LispParser' on standard input
parser = new LispParser(System.in, "System.in");
interactive = true;
System.out.println(GREETING);
System.out.println();
System.out.print(PROMPT);
}
while (! parser.eof()) {
try {
SExpression sexpr = parser.getSExpr();
String result = MessageFormat.format("{0}{1}{2}", ANSI_GREEN, EVAL.eval(sexpr), ANSI_RESET);
LispInterpreter.erasePrompt(interactive);
System.out.println(result);
} catch (RuntimeException e) {
LispInterpreter.erasePrompt(interactive);
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(), ErrorManager.CRITICAL_LEVEL);
}
if (interactive) {
System.out.print(PROMPT);
}
}
}
private static void erasePrompt(boolean interactive) {
if (interactive) {
for (int i = 0; i < PROMPT.length(); i++) {
System.out.print("\b");
}
}
}
}

Binary file not shown.

View File

@ -0,0 +1,65 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package main;
import parser.*;
import error.ErrorManager;
import java.io.*;
/**
* <code>LispParserDriver</code> 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:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
*/
public static void main(String[] args) {
LispParser parser = null;
if (args.length > 0) {
// a file name was given at the command-line, attempt to create a
// 'LispParser' on it
try {
parser = new LispParser(new FileInputStream(args[0]), args[0]);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} else {
// no file name was given, create a 'LispParser' on standard input
parser = new LispParser(System.in, "System.in");
}
while (! parser.eof()) {
try {
SExpression sexpr = parser.getSExpr();
System.out.println(sexpr.toString());
} catch (RuntimeException e) {
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
}
}
}

Binary file not shown.

View File

@ -0,0 +1,69 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package main;
import scanner.*;
import error.ErrorManager;
import java.io.*;
/**
* <code>LispScannerDriver</code> 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:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
*/
public static void main(String[] args) {
LispScanner in = null;
if (args.length > 0) {
// a file name was given at the command-line, attempt to create a
// 'LispScanner' on it
try {
in = new LispScanner(new FileInputStream(args[0]), args[0]);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} else {
// no file name was given, create a 'LispScanner' on standard input
in = new LispScanner(System.in, "System.in");
}
Token t = null;
do {
try {
t = in.nextToken();
System.out.printf("%-15s%-25s%5d%5d%25s\n", t.getType(),
t.getText(),
t.getLine(),
t.getColumn(),
t.getFName());
} catch (RuntimeException e) {
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} while ((t == null) || (t.getType() != Token.Type.EOF));
}
}

3
src/main/package.html Normal file
View File

@ -0,0 +1,3 @@
<body>
Provides test drivers for the various stages of the Lisp Interpreter.
</body>

BIN
src/parser/Atom.class Normal file

Binary file not shown.

46
src/parser/Atom.java Normal file
View File

@ -0,0 +1,46 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This class represents an ATOM in the PL-Lisp implementation.
*/
public class Atom extends SExpression {
private String text;
/**
* Create a new ATOM with the specified text.
*
* @param text
* the text representing this ATOM
*/
public Atom(String text) {
this.text = text;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>true</code>
*/
public boolean atomp() {
return true;
}
/**
* Returns a string representation of this ATOM.
*
* @return
* a string representation of this ATOM
*/
public String toString() {
return text;
}
}

BIN
src/parser/Cons.class Normal file

Binary file not shown.

111
src/parser/Cons.java Normal file
View File

@ -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
* <code>true</code>
*/
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() + ")");
}
}

BIN
src/parser/LispNumber.class Normal file

Binary file not shown.

View File

@ -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 <code>text</code> 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
* <code>true</code>
*/
public boolean numberp() {
return true;
}
/**
* Retrieve the integer value of this NUMBER.
*
* @return
* the integer value of this NUMBER
*/
public int getValue() {
return value;
}
}

Binary file not shown.

BIN
src/parser/LispParser.class Normal file

Binary file not shown.

192
src/parser/LispParser.java Normal file
View File

@ -0,0 +1,192 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
import scanner.*;
import java.io.*;
/**
* A <code>LispParser</code> converts a stream of bytes into internal
* representations of Lisp S-expressions. When the end of stream has been
* reached the <code>eof</code> 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 <code>LispParser</code> that produces S-expressions from
* the specified input stream.
*
* @param in
* the input stream to obtain S-expressions from (must not be
* <code>null</code>)
* @param fileName
* the name of the file that <code>in</code> 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
* <code>true</code> if this parser has reached the end of its underlying
* input stream; <code>false</code> 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);
}
}
}

BIN
src/parser/LispString.class Normal file

Binary file not shown.

View File

@ -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
* <code>true</code>
*/
public boolean stringp() {
return true;
}
}

BIN
src/parser/Nil.class Normal file

Binary file not shown.

104
src/parser/Nil.java Normal file
View File

@ -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
* <code>true</code>
*/
public boolean nullp() {
return true;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>true</code>
*/
public boolean atomp() {
return true;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>false</code>
*/
public boolean consp() {
return false;
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>true</code>
*/
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";
}
}

Binary file not shown.

122
src/parser/SExpression.java Normal file
View File

@ -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 <code>SExpression</code> is marked.
*
* @return
* <code>true</code> if this <code>SExpression</code> is marked;
* <code>false</code> 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
* <code>false</code>
*/
public boolean nullp() {
return false;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>false</code>
*/
public boolean atomp() {
return false;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>false</code>
*/
public boolean consp() {
return false;
}
/**
* Test if this S-expression is a LIST.
*
* @return
* the value of <code>(consp() || nullp())</code>
*/
public boolean listp() {
return (consp() || nullp());
}
/**
* Test if this S-expression is a NUMBER.
*
* @return
* <code>false</code>
*/
public boolean numberp() {
return false;
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>false</code>
*/
public boolean symbolp() {
return false;
}
/**
* Test if this S-expression is a FUNCTION.
*
* @return
* <code>false</code>
*/
public boolean functionp() {
return false;
}
/**
* Test if this S-expression is a STRING.
*
* @return
* <code>false</code>
*/
public boolean stringp() {
return false;
}
}

BIN
src/parser/Symbol.class Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More