Additional refactoring for eclipse

This commit is contained in:
Mike Cifelli 2016-12-07 16:40:08 -05:00
parent b875727361
commit 4ad31c0570
61 changed files with 39 additions and 4067 deletions

View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="test"/>
<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="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="build/classes"/>
</classpath>

View File

@ -15,6 +15,19 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/Ant_Builder.launch</value>
</dictionary>
<dictionary>
<key>incclean</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?>
<project name="LispInterpreter" basedir="." default="compile">
<property name="src.dir" value="." />
<property name="src.dir" value="src" />
<property name="build.dir" value="build" />
<property name="classes.dir" value="${build.dir}/classes" />
<property name="jar.dir" value="jar" />
<property name="doc.dir" value="doc" />
<property name="test.dir" value="test" />
<property name="jar-file" value="${jar.dir}/LispInterpreter.jar" />
<property name="main-class" value="main.LispInterpreter" />
@ -16,11 +17,8 @@
</target>
<target name="compile">
<mkdir dir="${classes.dir}" />
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
includeantruntime="false"
excludes="main/LispInterpreter2.java" />
includeantruntime="false" />
</target>
<target name="jar" depends="compile">

View File

@ -1,50 +0,0 @@
/*
* 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);
}
}
}

View File

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

View File

@ -1,74 +0,0 @@
/*
* 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");
}
}

View File

@ -1,38 +0,0 @@
/*
* 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());
}
}

View File

@ -1,46 +0,0 @@
/*
* 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");
}
}

View File

@ -1,46 +0,0 @@
/*
* 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");
}
}

View File

@ -1,73 +0,0 @@
/*
* 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;
}
}

View File

@ -1,44 +0,0 @@
/*
* 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);
}
}

View File

@ -1,82 +0,0 @@
/*
* 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;
}
}

View File

@ -1,60 +0,0 @@
/*
* 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");
}
}

View File

@ -1,45 +0,0 @@
/*
* 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());
}
}

View File

@ -1,58 +0,0 @@
/*
* 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());
}
}

View File

@ -1,55 +0,0 @@
/*
* 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");
}
}

View File

@ -1,251 +0,0 @@
/*
* 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());
}
}

View File

@ -1,26 +0,0 @@
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;
}
}

View File

@ -1,31 +0,0 @@
/*
* 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);
}
}

View File

@ -1,55 +0,0 @@
/*
* 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");
}
}

View File

@ -1,108 +0,0 @@
/*
* 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;
}
}

View File

@ -1,69 +0,0 @@
/*
* 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);
}
}

View File

@ -1,55 +0,0 @@
/*
* 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");
}
}

View File

@ -1,116 +0,0 @@
/*
* 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;
}
}

View File

@ -1,40 +0,0 @@
/*
* 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));
}
}

View File

@ -1,38 +0,0 @@
/*
* 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());
}
}

View File

@ -1,87 +0,0 @@
/*
* 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;
}
}

View File

@ -1,73 +0,0 @@
/*
* 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();
}
}

View File

@ -1,44 +0,0 @@
/*
* 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;
}
}

View File

@ -1,60 +0,0 @@
/*
* 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");
}
}

View File

@ -1,34 +0,0 @@
/*
* 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");
}
}

View File

@ -1,38 +0,0 @@
/*
* 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());
}
}

View File

@ -1,34 +0,0 @@
/*
* 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");
}
}

View File

@ -1,40 +0,0 @@
/*
* 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;
}
}

View File

@ -1,47 +0,0 @@
/*
* 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;
}
}

View File

@ -1,112 +0,0 @@
/*
* 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;
}
}

View File

@ -1,65 +0,0 @@
/*
* 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");
}
}

View File

@ -1,90 +0,0 @@
/*
* 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;
}
}

View File

@ -1,122 +0,0 @@
/*
* 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;
}
}

View File

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

View File

@ -1,91 +0,0 @@
/*
* 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");
}
}
}
}

View File

@ -1,50 +0,0 @@
package main;
import parser.*;
import eval.*;
import error.ErrorManager;
import java.io.*;
import java.net.*;
import remotefs.RemoteFileInputStream;
public class LispInterpreter2 {
public static final String HOST_NAME = "localhost";
public static final int PORT_NUM = 5150;
public static void main(String[] args) {
LispParser parser = null;
if (args.length > 0) {
try {
InetAddress host = InetAddress.getByName(HOST_NAME);
RemoteFileInputStream remoteIn =
new RemoteFileInputStream(host, PORT_NUM, args[0]);
parser = new LispParser(remoteIn, args[0]);
} catch (UnknownHostException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} else {
parser = new LispParser(System.in, "System.in");
}
while (! parser.eof()) {
try {
SExpression sexpr = parser.getSExpr();
System.out.println(EVAL.eval(sexpr));
} catch (RuntimeException e) {
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
}
}
}

View File

@ -1,65 +0,0 @@
/*
* 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);
}
}
}
}

View File

@ -1,69 +0,0 @@
/*
* 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));
}
}

View File

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

View File

@ -1,46 +0,0 @@
/*
* 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;
}
}

View File

@ -1,111 +0,0 @@
/*
* 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() + ")");
}
}

View File

@ -1,67 +0,0 @@
/*
* 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;
}
}

View File

@ -1,192 +0,0 @@
/*
* 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);
}
}
}

View File

@ -1,34 +0,0 @@
/*
* 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;
}
}

View File

@ -1,104 +0,0 @@
/*
* 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";
}
}

View File

@ -1,122 +0,0 @@
/*
* 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;
}
}

View File

@ -1,37 +0,0 @@
/*
* 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
* <code>true</code>
*/
public boolean symbolp() {
return true;
}
}

View File

@ -1,4 +0,0 @@
<body>
Provides the classes necessary for creating an internal representation of
the Lisp programming language.
</body>

View File

@ -1,160 +0,0 @@
/*
* 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 <code>LispFilterStream</code> 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 <code>LispFilterStream</code> with the specified underlying
* <code>InputStream</code>.
*
* @param in
* the underlying input stream (must not be <code>null</code>)
*/
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 <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned.
*
* @return
* the next byte of data, or <code>-1</code> 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
* <code>-1</code> 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
* <code>null</code>)
* @param off
* the start offset in <code>b</code> at which the data is written (must
* be <code>&gt;= 0</code> and <code>&lt; b.length</code>)
* @param len
* the maximum number of bytes to read into <code>b</code> (<code>len +
* off</code> must be <code>&lt; b.length</code>)
* @return
* the total number of bytes read into the buffer, or <code>-1</code> 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 <code>b</code>
* for some reason other than reaching the end of the stream.
* @throws IndexOutOfBoundsException
* Indicates that this method attempted to access an index in
* <code>b</code> that was either negative or greater than or equal to
* <code>b.length</code> as a result of the given parameters.
* @throws NullPointerException
* Indicates that <code>b</code> is <code>null</code>.
*/
@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 <code>0</code>. 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;
}
}

View File

@ -1,320 +0,0 @@
/*
* 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 <code>LispScanner</code> 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
* <code>Token.Type.EOF</code> is returned from the <code>nextToken</code>
* 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 <code>LispScanner</code> that produces Lisp tokens from the
* specified input stream.
*
* @param in
* the input stream to obtain Lisp tokens from (must not be
* <code>null</code>)
* @param fileName
* the name of the file that <code>in</code> 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
* <code>nextToken</code> method of this scanner. In the case that no calls
* to <code>nextToken</code> have been made yet, this method returns
* <code>null</code>.
*
* @return
* the last Lisp token returned from this scanner or <code>null</code> (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 != ';'));
}
}

View File

@ -1,126 +0,0 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package scanner;
/**
* A <code>Token</code> 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;
}
}

View File

@ -1,4 +0,0 @@
<body>
Provides the classes necessary to perform a lexical analysis of the Lisp
programming language.
</body>

Binary file not shown.

View File

@ -1,9 +1,3 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package scanner;
import java.io.InputStream;
@ -11,150 +5,48 @@ import java.io.FilterInputStream;
import java.io.IOException;
/**
* A <code>LispFilterStream</code> is an input stream that returns the bytes
* from an underlying input stream with all Lisp comments removed and replaced
* with newlines.
* Replaces Lisp comments with newlines in an input stream.
*/
public class LispFilterStream extends FilterInputStream {
private boolean inQuote;
private int nextCharacter;
/**
* Creates a <code>LispFilterStream</code> with the specified underlying
* <code>InputStream</code>.
*
* @param in
* the underlying input stream (must not be <code>null</code>)
*/
public LispFilterStream(InputStream in) {
super(in);
inQuote = false;
nextCharacter = 0;
}
/**
* Reads the next byte of data from this input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned.
*
* @return
* the next byte of data, or <code>-1</code> 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();
nextCharacter = 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);
if (haveEnteredComment()) {
consumeAllBytesInComment();
} else if (haveEncounteredStringBoundary()) {
inQuote = (!inQuote);
}
return next;
return nextCharacter;
}
/**
* 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
* <code>-1</code> 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
* <code>null</code>)
* @param off
* the start offset in <code>b</code> at which the data is written (must
* be <code>&gt;= 0</code> and <code>&lt; b.length</code>)
* @param len
* the maximum number of bytes to read into <code>b</code> (<code>len +
* off</code> must be <code>&lt; b.length</code>)
* @return
* the total number of bytes read into the buffer, or <code>-1</code> 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 <code>b</code>
* for some reason other than reaching the end of the stream.
* @throws IndexOutOfBoundsException
* Indicates that this method attempted to access an index in
* <code>b</code> that was either negative or greater than or equal to
* <code>b.length</code> as a result of the given parameters.
* @throws NullPointerException
* Indicates that <code>b</code> is <code>null</code>.
*/
@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;
}
}
private void consumeAllBytesInComment() throws IOException {
while (stillInComment()) {
nextCharacter = super.read();
}
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 <code>0</code>. 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;
private boolean stillInComment() {
return (nextCharacter != '\n') && (nextCharacter != -1);
}
while ((n > 0) && (read() != -1)) {
++bytesSkipped;
--n;
}
private boolean haveEnteredComment() {
return (nextCharacter == ';') && (!inQuote);
}
return bytesSkipped;
private boolean haveEncounteredStringBoundary() {
return nextCharacter == '\"';
}
}

View File

@ -19,19 +19,4 @@ public class LispFilterStreamTester {
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");
}
}