Initial commit

This commit is contained in:
Mike Cifelli 2016-12-07 14:16:45 -05:00
commit cee46a41b9
62 changed files with 4110 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build/
doc/
jar/
*.swp

50
build.xml Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<project name="LispInterpreter" basedir="." default="compile">
<property name="src.dir" value="." />
<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="jar-file" value="${jar.dir}/LispInterpreter.jar" />
<property name="main-class" value="main.LispInterpreter" />
<target name="clean">
<delete dir="${build.dir}" />
<delete dir="${doc.dir}" />
<delete dir="${jar.dir}" />
</target>
<target name="compile">
<mkdir dir="${classes.dir}" />
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
includeantruntime="false"
excludes="main/LispInterpreter2.java" />
</target>
<target name="jar" depends="compile">
<jar destfile="${jar-file}" basedir="${classes.dir}">
<manifest>
<attribute name="Manifest-Version" value="1.0" />
<attribute name="Main-Class" value="${main-class}" />
</manifest>
</jar>
</target>
<target name="javadoc">
<mkdir dir="${doc.dir}" />
<javadoc destdir="${doc.dir}">
<fileset dir="${src.dir}"
includes="scanner/*.java,
parser/*.java,
eval/*.java,
error/*.java,
main/*.java"
excludes="main/LispInterpreter2.java" />
<tag name="postcondition" description="Postcondition:" />
</javadoc>
</target>
<target name="clean-jar" depends="clean, jar" />
</project>

47
design.txt Normal file
View File

@ -0,0 +1,47 @@
Mike Cifelli
CIS 443 - Programming Languages
Lisp Interpreter Design Document
My implementation of LispScanner takes in an InputStream in its constructor
that it will use to retrieve the Lisp tokens. It then creates a
BufferedInputStream around this input stream so I can be sure that it will
support the 'mark' and 'reset' methods (which the LispScanner requires to
operate). A LispFilterStream is then created with this BufferedInputStream so I
can retrieve all of the bytes from the original input stream without having to
worry about dealing with Lisp comments.
When the LispScanner looks for the next Lisp token to return it uses a
switch statement to determine the type of the next token (or to skip over
whitespace). In the case of an identifier or number the scanner has to keep
accumulating characters until it sees one that can not be a part of the number
or identifier. Once one is found it has obviously been read from the input
stream and this is not desirable as it is not part of the current token. This
is where I made use of the 'mark' and 'reset' methods in the scanner. I mark
the position before I read each character and when one is found that is not
part of the current token I reset the input stream to its last position before
the token is returned. This effectively unreads the last character so the input
stream is in the proper position for the scanner's next read.
In the design of the LispParser I had some difficulty in implementing the
'eof' method. This was due to the fact that in order to determine if a
LispScanner was at the end of the input stream you have to read in a token.
However, I did not want this method to read in a token since this would
normally be part of an S-expression. Unfortunately, this meant that I would not
be able to detect the end-of-file token until the 'getSExpr' method read it in.
This is too late since the 'eof' method is used to determine when to stop
calling the 'getSExpr' method.
My solution involved reading a token in the 'eof' method and then storing
it in a variable. This would only be done once, until the token was used in the
'getSExpr' method. I also stored any exceptions that were thrown during the
read so that the 'eof' method would not give the impression of having read in
any tokens. Any exception thrown in the 'eof' method during the retrieval of a
token is stored and thrown the next time the 'getSExpr' method is called.
During the evaluation phase of the Lisp interpreter, I made use of the
command pattern during function calls. By creating a LispFunction interface, I
was able to place all of the built-in functions into a hash table mapping
function names to the appropriate LispFunction. By looking up functions in this
hash table, the proper function could be easily called during the evaluation of
a list.
FINAL NOTE: The function table is located in the EVAL class and the symbol
table is located in the SETF class.

50
error/ErrorManager.java Normal file
View File

@ -0,0 +1,50 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package error;
import java.text.MessageFormat;
/**
* <code>ErrorManager</code> is an error handling class for a Lisp interpreter.
*/
public class ErrorManager {
/**
* The lowest "criticality" level of an error that will cause the currently
* running program to terminate.
*/
public static final int CRITICAL_LEVEL = 3;
public static final String ANSI_RESET = "\u001B[0m";
public static final String ANSI_RED = "\u001B[31m";
public static final String ANSI_YELLOW = "\u001B[33m";
public static final String ANSI_PURPLE = "\u001B[35m";
/**
* Prints out the specified error message to the console and decides
* whether or not to terminate the currently running program.
*
* @param message
* the error message
* @param level
* the "criticality" level of the error
* @postcondition
* If <code>level &gt;= CRITICAL_LEVEL</code> the currently running
* program has been terminated.
*/
public static void generateError(String message, int level) {
String color = (level >= CRITICAL_LEVEL) ? ANSI_PURPLE : ANSI_RED;
String formattedMessage = MessageFormat.format("{0}error: {1}{2}", color, message, ANSI_RESET);
System.out.println(formattedMessage);
if (level >= CRITICAL_LEVEL) {
System.exit(1);
}
}
}

3
error/package.html Normal file
View File

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

74
eval/APPLY.java Normal file
View File

@ -0,0 +1,74 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>APPLY</code> represents the APPLY function in Lisp.
*/
public class APPLY extends LispFunction {
/**
* Call APPLY with the specified argument list.
*
* @param argList
* the list of arguments to be sent to APPLY (MUST BE A PROPER LIST)
* @return
* the result of evaluating APPLY on <code>argList</code>
*/
public static SExpression apply(Cons argList) {
return new APPLY().call(argList);
}
// The number of arguments that APPLY takes.
private static final int NUM_ARGS = 2;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to APPLY
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("APPLY"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to APPLY: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression car = argList.getCar(); // function name
Cons cdr = (Cons) argList.getCdr();
SExpression cadr = cdr.getCar(); // argument list
// make sure the second argument is a list
if (cadr.listp()) {
LispFunction function = EVAL.lookupFunction(car.toString());
if (function == null) {
// check if the car of the list is a lambda expression
if (car.functionp()) {
function = ((LambdaExpression) car).getFunction();
} else if (LAMBDA.isLambdaExpression(car)) {
Cons lexpr = (Cons) car;
function = LAMBDA.createFunction(lexpr);
} else {
throw new RuntimeException("undefined function " + car);
}
}
// apply the given function to the given argument list
return function.call((Cons) cadr);
}
// the second argument is not a list
throw new RuntimeException("APPLY: " + cadr + " is not a list");
}
}

38
eval/ATOM.java Normal file
View File

@ -0,0 +1,38 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>ATOM</code> represents the ATOM function in Lisp.
*/
public class ATOM extends LispFunction {
// The number of arguments that ATOM takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to ATOM
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("ATOM"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to ATOM: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression arg = argList.getCar();
return (arg.atomp() ? Symbol.T : Nil.getUniqueInstance());
}
}

46
eval/CAR.java Normal file
View File

@ -0,0 +1,46 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>CAR</code> represents the CAR function in Lisp.
*/
public class CAR extends LispFunction {
// The number of arguments that CAR takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to CAR
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("CAR"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to CAR: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression argCar = argList.getCar();
// make sure that the argument is a list
if (argCar.listp()) {
Cons arg = (Cons) argCar;
return arg.getCar();
}
// the argument is not a list
throw new RuntimeException("CAR: " + argCar + " is not a list");
}
}

46
eval/CDR.java Normal file
View File

@ -0,0 +1,46 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>CDR</code> represents the CDR function in Lisp.
*/
public class CDR extends LispFunction {
// The number of arguments that CDR takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to CDR
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("CDR"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to CDR: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression argCar = argList.getCar();
// make sure that the argument is a list
if (argCar.listp()) {
Cons arg = (Cons) argCar;
return arg.getCdr();
}
// the argument is not a list
throw new RuntimeException("CDR: " + argCar + " is not a list");
}
}

73
eval/COND.java Normal file
View File

@ -0,0 +1,73 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>COND</code> represents the COND form in Lisp.
*/
public class COND extends LispFunction {
public SExpression call(Cons argList) {
if (argList.nullp()) {
// return NIL if there are were no arguments passed to COND
return Nil.getUniqueInstance();
}
SExpression argCar = argList.getCar(); // first clause
Cons argCdr = (Cons) argList.getCdr(); // list of remaining clauses
// make sure the first clause is a list and is not NIL
if (argCar.consp()) {
Cons clause = (Cons) argCar;
SExpression test = EVAL.eval(clause.getCar());
if (test != Nil.getUniqueInstance()) {
// the car of this clause is true, so we evaluate its cdr
SExpression cdr = clause.getCdr();
SExpression retval = test;
// evaluate all the S-expressions in the cdr of the clause
while (cdr.consp()) {
retval = EVAL.eval(((Cons) cdr).getCar());
cdr = ((Cons) cdr).getCdr();
}
// return the value of the last S-expression evaluated
return retval;
}
// the car of this clause is false, so we test any remaining
// clauses
// check if the list of remaining clauses is a list and is not NIL
if (argCdr.consp()) {
return call(argCdr);
}
// there are no remaining clauses, so we return NIL
return Nil.getUniqueInstance();
}
throw new RuntimeException("COND: clause " + argCar +
" should be a list");
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

44
eval/CONS.java Normal file
View File

@ -0,0 +1,44 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>CONS</code> represents the CONS function in Lisp.
*/
public class CONS extends LispFunction {
// The number of arguments that CONS takes.
private static final int NUM_ARGS = 2;
public Cons call(Cons argList) {
// retrieve the number of arguments passed to CONS
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("CONS"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to CONS: " + originalSExpr;
throw new RuntimeException(errMsg);
}
// the car of the CONS cell we are going to create
SExpression argOne = argList.getCar();
Cons cdr = (Cons) argList.getCdr();
// the cdr of the CONS cell we are going to create
SExpression argTwo = cdr.getCar();
return new Cons(argOne, argTwo);
}
}

82
eval/DEFUN.java Normal file
View File

@ -0,0 +1,82 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import java.util.HashMap;
/**
* <code>DEFUN</code> represents the DEFUN form in Lisp.
*/
public class DEFUN extends LispFunction {
// The minimum number of arguments that DEFUN takes.
private static final int MIN_ARGS = 3;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to DEFUN
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength < MIN_ARGS) {
Cons originalSExpr = new Cons(new Symbol("DEFUN"), argList);
String errMsg = "too few arguments given to DEFUN: " +
originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression name = argList.getCar(); // name of the function
// make sure the function name is a symbol
if (! name.symbolp()) {
throw new RuntimeException("DEFUN: " + name + " is not a symbol");
}
Cons cdr = (Cons) argList.getCdr();
SExpression cadr = cdr.getCar();
// make sure the list of arguments (lambda list) is a proper list
if (! cadr.listp()) {
throw new RuntimeException("DEFUN: " + cadr + " is not a list");
} else if (EVAL.isDotted((Cons) cadr)) {
throw new RuntimeException("DEFUN: " + cadr +
" is not a proper list");
}
Cons lambdaList = (Cons) cadr; // lambda list of the function
// list of S-expressions making up the body of the function
Cons body = (Cons) cdr.getCdr();
HashMap<String, LispFunction> functionTable = EVAL.getFunctionTable();
// give a warning if this function has already been defined
if (functionTable.containsKey(name.toString())) {
System.out.println("WARNING: redefining function " +
name.toString());
}
// place the function in the function table
functionTable.put(name.toString(),
new UDFunction(name.toString(), lambdaList, body));
return name;
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

60
eval/DIVIDE.java Normal file
View File

@ -0,0 +1,60 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>DIVIDE</code> represents the '/' function in Lisp.
*/
public class DIVIDE extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol("/"), argList);
throw new RuntimeException("too few arguments given to /: " +
originalSExpr);
}
SExpression argFirst = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
// make sure that the first argument is a number
if (argFirst.numberp()) {
LispNumber num1 = (LispNumber) argFirst;
if (argRest.nullp()) {
// there is only one argument, so return the multiplicative
// inverse of the number
return new LispNumber(1 / num1.getValue());
}
SExpression argSecond = argRest.getCar();
// make sure that the next argument is a number as well
if (argSecond.numberp()) {
LispNumber num2 = (LispNumber) argSecond;
LispNumber quotient = new LispNumber(num1.getValue() /
num2.getValue());
SExpression argCddr = argRest.getCdr();
if (argCddr.consp()) {
return call(new Cons(quotient, argCddr));
}
return quotient;
}
throw new RuntimeException("/: " + argSecond + " is not a number");
}
throw new RuntimeException("/: " + argFirst + " is not a number");
}
}

45
eval/EQ.java Normal file
View File

@ -0,0 +1,45 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>EQ</code> represents the EQ function in Lisp.
*/
public class EQ extends LispFunction {
// The number of arguments that EQ takes.
private static final int NUM_ARGS = 2;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to EQ
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("EQ"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to EQ: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression argOne = argList.getCar(); // first argument
Cons cdr = (Cons) argList.getCdr();
SExpression argTwo = cdr.getCar(); // second argumnet
if (argOne.atomp() && argTwo.atomp()) {
return ((argOne.toString().equals(argTwo.toString()))
? Symbol.T : Nil.getUniqueInstance());
}
return ((argOne == argTwo) ? Symbol.T : Nil.getUniqueInstance());
}
}

58
eval/EQUAL.java Normal file
View File

@ -0,0 +1,58 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>EQUAL</code> represents the EQUAL function in Lisp.
*/
public class EQUAL extends LispFunction {
// The number of arguments that EQUAL takes.
private static final int NUM_ARGS = 2;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to EQUAL
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("EQUAL"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to EQUAL: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression argOne = argList.getCar(); // first argument
Cons cdr = (Cons) argList.getCdr();
SExpression argTwo = cdr.getCar(); // second argumnet
if (argOne.consp() && argTwo.consp()) {
Cons listOne = (Cons) argOne;
Cons listTwo = (Cons) argTwo;
SExpression listOneCar = listOne.getCar();
SExpression listTwoCar = listTwo.getCar();
SExpression listOneCdr = listOne.getCdr();
SExpression listTwoCdr = listTwo.getCdr();
SExpression carEqual =
call(new Cons(listOneCar, LIST.makeList(listTwoCar)));
SExpression cdrEqual =
call(new Cons(listOneCdr, LIST.makeList(listTwoCdr)));
return (((carEqual == Symbol.T) && (cdrEqual == Symbol.T))
? Symbol.T : Nil.getUniqueInstance());
}
return ((argOne.toString().equals(argTwo.toString()))
? Symbol.T : Nil.getUniqueInstance());
}
}

55
eval/EQUALSP.java Normal file
View File

@ -0,0 +1,55 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>EQUALSP</code> represents the '=' function in Lisp.
*/
public class EQUALSP extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol("="), argList);
throw new RuntimeException("too few arguments given to =: " +
originalSExpr);
}
SExpression firstArg = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
// make sure that the first argument is a number
if (firstArg.numberp()) {
LispNumber num1 = (LispNumber) firstArg;
if (argRest.nullp()) {
return Symbol.T;
}
SExpression secondArg = argRest.getCar();
// make sure that the second argument is a number as well
if (secondArg.numberp()) {
LispNumber num2 = (LispNumber) secondArg;
if (num1.getValue() == num2.getValue()) {
return call(argRest);
}
return Nil.getUniqueInstance();
}
throw new RuntimeException("=: " + secondArg + " is not a number");
}
throw new RuntimeException("=: " + firstArg + " is not a number");
}
}

251
eval/EVAL.java Normal file
View File

@ -0,0 +1,251 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
import java.util.HashMap;
/**
* <code>EVAL</code> represents the EVAL function in Lisp.
*/
public class EVAL extends LispFunction {
// A table to contain all the built-in and user-defined Lisp functions.
private static HashMap<String, LispFunction> functionTable =
new HashMap<String, LispFunction>();
static {
// place all of the built-in functions into the function table
functionTable.put("*", new MULTIPLY());
functionTable.put("+", new PLUS());
functionTable.put("-", new MINUS());
functionTable.put("/", new DIVIDE());
functionTable.put("<", new LESSP());
functionTable.put("=", new EQUALSP());
functionTable.put(">", new GREATERP());
functionTable.put("APPLY", new APPLY());
functionTable.put("ATOM", new ATOM());
functionTable.put("CAR", new CAR());
functionTable.put("CDR", new CDR());
functionTable.put("COND", new COND());
functionTable.put("CONS", new CONS());
functionTable.put("DEFUN", new DEFUN());
functionTable.put("EQ", new EQ());
functionTable.put("EQUAL", new EQUAL());
functionTable.put("EVAL", new EVAL());
functionTable.put("EXIT", new EXIT());
functionTable.put("FIRST", new CAR());
functionTable.put("FUNCALL", new FUNCALL());
functionTable.put("GREATERP", new GREATERP());
functionTable.put("LAMBDA", new LAMBDA());
functionTable.put("LENGTH", new LENGTH());
functionTable.put("LET", new LET());
functionTable.put("LIST", new LIST());
functionTable.put("LISTP", new LISTP());
functionTable.put("LOAD", new LOAD());
functionTable.put("NULL", new NULL());
functionTable.put("PRINT", new PRINT());
functionTable.put("QUOTE", new QUOTE());
functionTable.put("REST", new CDR());
functionTable.put("SETF", new SETF());
functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION());
}
/**
* Retrieve the function table.
*
* @return
* the function table
*/
public static HashMap<String, LispFunction> getFunctionTable() {
return functionTable;
}
/**
* Look up a function by its name.
*
* @param functionName
* the name of the function to look up
* @return
* the function with the name <code>functionName</code> if it exists; null
* otherwise
*/
public static LispFunction lookupFunction(String functionName) {
return functionTable.get(functionName);
}
/**
* Look up a symbol's value using its name.
*
* @param symbolName
* the name of the symbol to look up (must not be null)
* @return
* the value of <code>symbolName</code> if it has one; null otherwise
*/
public static SExpression lookupSymbol(String symbolName) {
if (symbolName.equals("NIL")) {
return Nil.getUniqueInstance();
} else if (symbolName.equals("T")) {
return Symbol.T;
} else if (symbolName.startsWith(":")) {
return new Symbol(symbolName);
}
return SETF.lookup(symbolName);
}
/**
* Determine if the given list is dotted.
*
* @param list
* the list to be tested (must not be null)
* @return
* <code>true</code> if <code>list</code> is dotted; <code>false</code>
* otherwise
*/
public static boolean isDotted(Cons list) {
if (list.nullp()) {
return false;
}
SExpression cdr = list.getCdr();
if (cdr.listp()) {
return isDotted((Cons) cdr);
}
// the cdr of 'list' is not a list, therefore it is dotted
return true;
}
/**
* Evaluate the given S-expression.
*
* @param sexpr
* the S-expression to evaluate
* @return
* the value of <code>sexpr</code>
*/
public static SExpression eval(SExpression sexpr) {
Cons expList = LIST.makeList(sexpr);
EVAL evalFunction = new EVAL();
return evalFunction.call(expList);
}
// The number of arguments that EVAL takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to EVAL
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("EVAL"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to EVAL: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression arg = argList.getCar();
if (arg.listp()) {
if (arg.consp()) {
return evaluateList((Cons) arg);
}
return arg; // 'arg' is NIL
}
if (arg.symbolp()) {
SExpression symbolValue = lookupSymbol(arg.toString());
if (symbolValue != null) {
return symbolValue;
}
throw new RuntimeException("variable " + arg + " has no value");
}
return arg; // 'arg' is a NUMBER or a STRING
}
// Evaluate the specified list.
//
// Parameters: list - the list to evaluate
// Returns: the value of 'list'
// Precondition: 'list' must not be null.
private SExpression evaluateList(Cons list) {
SExpression car = list.getCar();
SExpression cdr = list.getCdr();
LispFunction function = lookupFunction(car.toString());
if (function == null) {
// check if the car of the list is a lambda expression
if (car.functionp()) {
function = ((LambdaExpression) car).getFunction();
} else if (LAMBDA.isLambdaExpression(car)) {
Cons lexpr = (Cons) car;
function = LAMBDA.createFunction(lexpr);
} else {
throw new RuntimeException("undefined function " + car);
}
}
// make sure the list of arguments for 'function' is a list
if (cdr.listp()) {
Cons args = (Cons) cdr;
// make sure the list of arguments is not dotted
if (isDotted(args)) {
throw new RuntimeException("argument list given to " + car +
" is dotted: " + list);
}
// determine if we should evaluate the arguments that will be
// passed to 'function'
if (function.evaluateArguments()) {
args = evaluateArgList(args);
}
return function.call(args);
}
// the list of arguments is not a list!
throw new RuntimeException("argument list given to " + car +
" is dotted: " + list);
}
// Evaluate a list of arguments for a function.
//
// Parameters: arguments - a list of arguments for a function
// Returns: a list consisting of the values of the S-expressions found in
// 'arguments'
// Precondition: 'arguments' must not be null.
private Cons evaluateArgList(Cons arguments) {
if (arguments.nullp()) {
return Nil.getUniqueInstance();
}
SExpression car = eval(arguments.getCar());
SExpression cdr = arguments.getCdr();
if (cdr.listp()) {
return new Cons(car, evaluateArgList((Cons) cdr));
}
// remove any parameters found after a dot (put here in case the check
// for a dotted parameter list is not done prior to this call)
return new Cons(car, Nil.getUniqueInstance());
}
}

26
eval/EXIT.java Normal file
View File

@ -0,0 +1,26 @@
package eval;
import parser.*;
public class EXIT extends LispFunction {
// The number of arguments that EXIT takes.
private static final int NUM_ARGS = 0;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to EXIT
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength > NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("EXIT"), argList);
String errMsg = "too many arguments given to EXIT: " + originalSExpr;
throw new RuntimeException(errMsg);
}
System.exit(0);
return null;
}
}

31
eval/FUNCALL.java Normal file
View File

@ -0,0 +1,31 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>FUNCALL</code> represents the FUNCALL function in Lisp.
*/
public class FUNCALL extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol("FUNCALL"), argList);
throw new RuntimeException("too few arguments given to FUNCALL: " +
originalSExpr);
}
SExpression cdr = argList.getCdr();
Cons applyArgs = new Cons(argList.getCar(), LIST.makeList(cdr));
return APPLY.apply(applyArgs);
}
}

55
eval/GREATERP.java Normal file
View File

@ -0,0 +1,55 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>GREATERP</code> represents the '&gt;' function in Lisp.
*/
public class GREATERP extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol(">"), argList);
throw new RuntimeException("too few arguments given to >: " +
originalSExpr);
}
SExpression firstArg = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
// make sure that the first argument is a number
if (firstArg.numberp()) {
LispNumber num1 = (LispNumber) firstArg;
if (argRest.nullp()) {
return Symbol.T;
}
SExpression secondArg = argRest.getCar();
// make sure that the second argument is a number as well
if (secondArg.numberp()) {
LispNumber num2 = (LispNumber) secondArg;
if (num1.getValue() > num2.getValue()) {
return call(argRest);
}
return Nil.getUniqueInstance();
}
throw new RuntimeException(">: " + secondArg + " is not a number");
}
throw new RuntimeException(">: " + firstArg + " is not a number");
}
}

108
eval/LAMBDA.java Normal file
View File

@ -0,0 +1,108 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>LAMBDA</code> represents the LAMBDA form in Lisp.
*/
public class LAMBDA extends LispFunction {
/**
* Determine if the given S-expression is a lambda expression.
*
* @param sexpr
* the S-expression to test (must not be null)
* @return
* <code>true</code> if <code>sexpr</code> is a valid lambda expression;
* <code>false</code> otherwise
*/
public static boolean isLambdaExpression(SExpression sexpr) {
if (sexpr.consp()) {
SExpression first = ((Cons) sexpr).getCar();
return "LAMBDA".equals(first.toString());
}
return false;
}
/**
* Create an internal representation of a user-defined function from the
* specified lambda expression.
*
* @param lexpr
* the lambda expression to create the function from (must not be null)
* @return
* an internal representation of a user-defined function created from
* <code>lexpr</code>
* @throws RuntimeException
* Indicates that <code>lexpr</code> is not a valid lambda expression.
*/
public static UDFunction createFunction(Cons lexpr) {
LAMBDA lambda = new LAMBDA();
SExpression cdr = lexpr.getCdr();
// make sure lexpr is a proper list
if (! cdr.consp()) {
throw new RuntimeException("invalid lambda expression");
} else if (EVAL.isDotted((Cons) cdr)) {
throw new RuntimeException("dotted lambda expression " + lexpr);
}
Cons rest = (Cons) cdr;
return lambda.call(rest).getFunction();
}
// The minimum number of arguments that LAMBDA takes.
private static final int MIN_ARGS = 2;
public LambdaExpression call(Cons argList) {
// retrieve the number of arguments passed to LAMBDA
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength < MIN_ARGS) {
Cons originalSExpr = new Cons(new Symbol("LAMBDA"), argList);
String errMsg = "too few arguments given to LAMBDA: " +
originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression car = argList.getCar();
// make sure the list of arguments is a proper list
if (! car.listp()) {
throw new RuntimeException("LAMBDA: " + car + " is not a list");
} else if (EVAL.isDotted((Cons) car)) {
throw new RuntimeException("LAMBDA: " + car +
" must be a proper list");
}
Cons lambdaList = (Cons) car;
Cons body = (Cons) argList.getCdr();
Cons lexpr = new Cons(new Symbol("LAMBDA"), argList);
UDFunction function = new UDFunction(":LAMBDA", lambdaList, body);
return new LambdaExpression(lexpr, function);
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

69
eval/LENGTH.java Normal file
View File

@ -0,0 +1,69 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LENGTH</code> represents the LENGTH function in Lisp.
*/
public class LENGTH extends LispFunction {
/**
* Returns the length of the given list.
*
* @param list
* the list to determine the length of
* @return
* the length of <code>list</code>
*/
public static int getLength(Cons list) {
LENGTH lengthFunction = new LENGTH();
LispNumber length = lengthFunction.call(LIST.makeList(list));
return length.getValue();
}
public LispNumber call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
throw new RuntimeException("too few arguments given to LENGTH: " +
originalSExpr);
}
SExpression argCar = argList.getCar();
SExpression argCdr = argList.getCdr();
// make sure we have received only one argument
if (! argCdr.nullp()) {
Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
throw new RuntimeException("too many arguments given to LENGTH: " +
originalSExpr);
}
// make sure that the argument is a list
if (argCar.listp()) {
Cons arg = (Cons) argCar;
if (arg.nullp()) {
return new LispNumber(0);
}
Cons cdr = LIST.makeList(arg.getCdr());
LispNumber cdrLength = call(cdr);
return new LispNumber(1 + cdrLength.getValue());
}
throw new RuntimeException("LENGTH: a proper list must not end with " +
argCar);
}
}

55
eval/LESSP.java Normal file
View File

@ -0,0 +1,55 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LESSP</code> represents the '&lt;' function in Lisp.
*/
public class LESSP extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol("<"), argList);
throw new RuntimeException("too few arguments given to <: " +
originalSExpr);
}
SExpression firstArg = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
// make sure that the first argument is a number
if (firstArg.numberp()) {
LispNumber num1 = (LispNumber) firstArg;
if (argRest.nullp()) {
return Symbol.T;
}
SExpression secondArg = argRest.getCar();
// make sure that the second argument is a number as well
if (secondArg.numberp()) {
LispNumber num2 = (LispNumber) secondArg;
if (num1.getValue() < num2.getValue()) {
return call(argRest);
}
return Nil.getUniqueInstance();
}
throw new RuntimeException("<: " + secondArg + " is not a number");
}
throw new RuntimeException("<: " + firstArg + " is not a number");
}
}

116
eval/LET.java Normal file
View File

@ -0,0 +1,116 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>LET</code> represents the LET form in Lisp.
*/
public class LET extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
throw new RuntimeException("too few arguments given to LET");
}
// create a new symbol table on top of the current environment to add
// all the local variables to
SymbolTable environment = new SymbolTable(SETF.getEnvironment());
SExpression car = argList.getCar();
Cons cdr = (Cons) argList.getCdr();
addVariablesToTable(environment, car);
SETF.setEnvironment(environment);
SExpression retval = Nil.getUniqueInstance();
// evaluate all S-expression in the body
while (cdr.consp()) {
retval = EVAL.eval(cdr.getCar());
cdr = (Cons) cdr.getCdr();
}
// restore the environment to its original value
SETF.setEnvironment(environment.getParent());
return retval;
}
// Add a list of variables and their values to the specified symbol table.
//
// Parameters: environment - the symbol table to add the variables and
// their values to (must not be null)
// vars - a list of variable/value pairs (must be either a
// proper list of pairs or NIL)
// Throws: RuntimeException - Indicates that 'vars' is not a proper list or
// that it contains an member that is not a
// variable/value pair.
// Precondition: 'environment' and 'vars' must not be null.
// Postcondition: All of the variables in 'vars' have been placed into
// 'environment' with their values.
private void addVariablesToTable(SymbolTable environment,
SExpression vars) {
// makes sure the list of variable/value pairs is a list
if (! vars.listp()) {
throw new RuntimeException("LET: " + vars +
" is not a properly formatted" +
" variable/value pair list");
}
// add all variables in 'vars' to 'environment'
while (vars.consp()) {
Cons varList = (Cons) vars;
SExpression varListCar = varList.getCar();
// make sure this variable/value pair is a list
if (! varListCar.consp()) {
throw new RuntimeException("LET: " + varListCar +
" is not a properly formatted" +
" variable/value pair");
}
Cons varSpec = (Cons) varListCar;
SExpression symbol = varSpec.getCar();
SExpression varSpecCdr = varSpec.getCdr();
// make sure this variable pair has a value associated with it
if (! varSpecCdr.consp()) {
throw new RuntimeException("LET: illegal variable " +
"specification " + varSpec);
}
Cons varValue = (Cons) varSpecCdr;
SExpression value = varValue.getCar();
// make sure there are no more members of this variable/value pair
// and that 'symbol' is actually a symbol
if ((! varValue.getCdr().nullp()) || (! symbol.symbolp())) {
throw new RuntimeException("LET: illegal variable " +
"specification " + varSpec);
}
environment.put(symbol.toString(), value);
vars = varList.getCdr();
}
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

40
eval/LIST.java Normal file
View File

@ -0,0 +1,40 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LIST</code> represents the LIST function in Lisp.
*/
public class LIST extends LispFunction {
/**
* Places the given S-expression into a list.
*
* @param sexpr
* the S-expression to be placed into a list
* @return
* a list with <code>sexpr</code> as the car and NIL as the cdr.
*/
public static Cons makeList(SExpression sexpr) {
return new Cons(sexpr, Nil.getUniqueInstance());
}
public Cons call(Cons argList) {
if (argList.nullp()) {
// return NIL if there were no arguments passed to LIST
return Nil.getUniqueInstance();
}
SExpression argCar = argList.getCar();
Cons argCdr = (Cons) argList.getCdr();
return new Cons(argCar, call(argCdr));
}
}

38
eval/LISTP.java Normal file
View File

@ -0,0 +1,38 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>LISTP</code> represents the LISTP function in Lisp.
*/
public class LISTP extends LispFunction {
// The number of arguments that LISTP takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to LISTP
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("LISTP"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to LISTP: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression arg = argList.getCar();
return (arg.listp() ? Symbol.T : Nil.getUniqueInstance());
}
}

87
eval/LOAD.java Normal file
View File

@ -0,0 +1,87 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import java.io.*;
/**
* <code>LOAD</code> represents the LOAD function in Lisp.
*/
public class LOAD extends LispFunction {
// The number of arguments that LOAD takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to LOAD
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("LOAD"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to LOAD: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression argCar = argList.getCar();
// make sure the argument is a string
if (! argCar.stringp()) {
throw new RuntimeException("LOAD: " + argCar + " is not a string");
}
LispString quotedName = (LispString) argCar;
String fileName = quotedName.toString();
// remove the surrounding quotes from the file name
fileName = fileName.substring(1, (fileName.length() - 1));
return processFile(fileName);
}
// Evaluate all the S-expressions found in the file with the specified
// name.
//
// Parameters: fileName - the name of the file to be evaluated
// Returns: 'T' if the file was processed successfully; 'NIL' otherwise
private SExpression processFile(String fileName) {
LispParser parser = null;
// attempt to create a new 'LispParser' on the specified file
try {
parser = new LispParser(new FileInputStream(fileName), fileName);
} catch (FileNotFoundException e) {
System.out.println("LOAD: could not open " + fileName);
return Nil.getUniqueInstance();
}
// attempt to evaluate all the S-expressions contained in the file
while (! parser.eof()) {
try {
SExpression sexpr = parser.getSExpr();
EVAL.eval(sexpr);
} catch (RuntimeException e) {
System.out.println("LOAD: " + e.getMessage());
return Nil.getUniqueInstance();
} catch (IOException e) {
System.out.println("LOAD: " + e.getMessage());
return Nil.getUniqueInstance();
}
}
// success!
return Symbol.T;
}
}

View File

@ -0,0 +1,73 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* This class represents a Lisp FUNCTION in the PL-Lisp implementation.
*/
public class LambdaExpression extends SExpression {
private Cons lexpr;
private UDFunction function;
/**
* Create a new FUNCTION with the specified lambda expression and
* internal representation.
*
* @param lexpr
* the lambda expression of this FUNCTION
* @param function
* the internal representation of this FUNCTION
*/
public LambdaExpression(Cons lexpr, UDFunction function) {
this.lexpr = lexpr;
this.function = function;
}
/**
* Test if this S-expression is a FUNCTION.
*
* @return
* <code>true</code>
*/
public boolean functionp() {
return true;
}
/**
* Retrieve the lambda expression of this FUNCTION.
*
* @return
* the lambda expression of this FUNCTION
*/
public Cons getLExpression() {
return lexpr;
}
/**
* Retrieve the internal representation of this FUNCTION.
*
* @return
* the user-defined function of this FUNCTION
*/
public UDFunction getFunction() {
return function;
}
/**
* Returns a string representation of this FUNCTION.
*
* @return
* a string representation of this FUNCTION
*/
public String toString() {
return lexpr.toString();
}
}

44
eval/LispFunction.java Normal file
View File

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

60
eval/MINUS.java Normal file
View File

@ -0,0 +1,60 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>MINUS</code> represents the '-' function in Lisp.
*/
public class MINUS extends LispFunction {
public SExpression call(Cons argList) {
// make sure we have received at least one argument
if (argList.nullp()) {
Cons originalSExpr = new Cons(new Symbol("-"), argList);
throw new RuntimeException("too few arguments given to -: " +
originalSExpr);
}
SExpression argFirst = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
// make sure that the first argument is a number
if (argFirst.numberp()) {
LispNumber num1 = (LispNumber) argFirst;
if (argRest.nullp()) {
// there is only one argument, so return the additive
// inverse of the number
return new LispNumber(- num1.getValue());
}
SExpression argSecond = argRest.getCar();
// make sure that the next argument is a number as well
if (argSecond.numberp()) {
LispNumber num2 = (LispNumber) argSecond;
LispNumber difference = new LispNumber(num1.getValue() -
num2.getValue());
SExpression argCddr = argRest.getCdr();
if (argCddr.consp()) {
return call(new Cons(difference, argCddr));
}
return difference;
}
throw new RuntimeException("-: " + argSecond + " is not a number");
}
throw new RuntimeException("-: " + argFirst + " is not a number");
}
}

34
eval/MULTIPLY.java Normal file
View File

@ -0,0 +1,34 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>MULTIPLY</code> represents the '*' function in Lisp.
*/
public class MULTIPLY extends LispFunction {
public LispNumber call(Cons argList) {
if (argList.nullp()) {
return new LispNumber(1);
}
SExpression argFirst = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
if (argFirst.numberp()) {
LispNumber num1 = (LispNumber) argFirst;
LispNumber num2 = call(argRest);
return new LispNumber(num1.getValue() * num2.getValue());
}
throw new RuntimeException("*: " + argFirst + " is not a number");
}
}

38
eval/NULL.java Normal file
View File

@ -0,0 +1,38 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>NULL</code> represents the NULL function in Lisp.
*/
public class NULL extends LispFunction {
// The number of arguments that NULL takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to NULL
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("NULL"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to NULL: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression arg = argList.getCar();
return (arg.nullp() ? Symbol.T : Nil.getUniqueInstance());
}
}

34
eval/PLUS.java Normal file
View File

@ -0,0 +1,34 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>PLUS</code> represents the '+' function in Lisp.
*/
public class PLUS extends LispFunction {
public LispNumber call(Cons argList) {
if (argList.nullp()) {
return new LispNumber(0);
}
SExpression argFirst = argList.getCar();
Cons argRest = (Cons) argList.getCdr();
if (argFirst.numberp()) {
LispNumber num1 = (LispNumber) argFirst;
LispNumber num2 = call(argRest);
return new LispNumber(num1.getValue() + num2.getValue());
}
throw new RuntimeException("+: " + argFirst + " is not a number");
}
}

40
eval/PRINT.java Normal file
View File

@ -0,0 +1,40 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>PRINT</code> represents the PRINT function in Lisp.
*/
public class PRINT extends LispFunction {
// The number of arguments that PRINT takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to PRINT
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("PRINT"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to PRINT: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression arg = argList.getCar();
System.out.println(arg);
return arg;
}
}

47
eval/QUOTE.java Normal file
View File

@ -0,0 +1,47 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package eval;
import parser.*;
/**
* <code>QUOTE</code> represents the QUOTE form in Lisp.
*/
public class QUOTE extends LispFunction {
// The number of arguments that QUOTE takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to QUOTE
int argListLength = LENGTH.getLength(argList);
// make sure we have received exactly one argument
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("QUOTE"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to QUOTE: " + originalSExpr;
throw new RuntimeException(errMsg);
}
return argList.getCar();
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

112
eval/SETF.java Normal file
View File

@ -0,0 +1,112 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>SETF</code> represents the SETF form in Lisp.
*/
public class SETF extends LispFunction {
private static SymbolTable environment = new SymbolTable();
/**
* Look up the value of a symbol using its name.
*
* @param symbolName
* the name of the symbol to look up
* @return
* the value of <code>symbolName</code> if it has one; null otherwise
*/
public static SExpression lookup(String symbolName) {
SymbolTable current = environment;
while (current != null) {
if (current.contains(symbolName)) {
return current.get(symbolName);
}
current = current.getParent();
}
return null;
}
/**
* Set the current environment to the specified value.
*
* @param newEnvironment
* the value to set the environment to
*/
public static void setEnvironment(SymbolTable newEnvironment) {
environment = newEnvironment;
}
/**
* Retrieve the current environment.
*
* @return
* the current environment
*/
public static SymbolTable getEnvironment() {
return environment;
}
// The number of arguments that SETF takes.
private static final int NUM_ARGS = 2;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to SETF
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("SETF"), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to SETF: " + originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression symbol = argList.getCar();
// make sure the first argument is a symbol
if (! symbol.symbolp()) {
throw new RuntimeException("SETF: " + symbol + " is not a symbol");
}
Cons cdr = (Cons) argList.getCdr();
SExpression value = EVAL.eval(cdr.getCar());
SymbolTable current = environment;
// set 'current' to the symbol table that contains 'symbol' or the
// global symbol table if 'symbol' is not in the environment
while ((! current.contains(symbol.toString())) &&
(current.getParent() != null)) {
current = current.getParent();
}
current.put(symbol.toString(), value);
return value;
}
/**
* Determine if the arguments passed to this Lisp function should be
* evaluated.
*
* @return
* <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}
}

65
eval/SYMBOL_FUNCTION.java Normal file
View File

@ -0,0 +1,65 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
/**
* <code>SYMBOL_FUNCTION</code> represents the SYMBOL-FUNCTION function in
* Lisp.
*/
public class SYMBOL_FUNCTION extends LispFunction {
// The number of arguments that SYMBOL-FUNCTION takes.
private static final int NUM_ARGS = 1;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to SYMBOL-FUNCTION
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol("SYMBOL-FUNCTION"),
argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to SYMBOL-FUNCTION: " +
originalSExpr;
throw new RuntimeException(errMsg);
}
SExpression arg = argList.getCar();
// make sure the argument is a symbol
if (arg.symbolp()) {
LispFunction function = EVAL.lookupFunction(arg.toString());
// make sure the function actually exists
if (function != null) {
if (function instanceof UDFunction) {
// this is a user-defined function
UDFunction udFunction = (UDFunction) function;
return udFunction.getLexpr();
}
// this is a built-in function
return new Symbol("SUBR-" + arg.toString());
}
throw new RuntimeException("SYMBOL-FUNCTION: undefined function " +
arg);
}
throw new RuntimeException("SYMBOL-FUNCTION: " + arg +
" is not a symbol");
}
}

90
eval/SymbolTable.java Normal file
View File

@ -0,0 +1,90 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import java.util.HashMap;
/**
* A <code>SymbolTable</code> maps symbol names to values.
*/
public class SymbolTable {
private HashMap<String, SExpression> table;
private SymbolTable parent;
/**
* Create a new symbol table with no parent.
*/
public SymbolTable() {
this(null);
}
/**
* Create a new symbol table with the specified parent.
*
* @param parent
* the parent of this symbol table
*/
public SymbolTable(SymbolTable parent) {
this.table = new HashMap<String, SExpression>();
this.parent = parent;
}
/**
* Determine if the specified symbol name is in this symbol table.
*
* @param symbolName
* the name of the symbol to look up
* @return
* <code>true</code> if the symbol is in this symbol table;
* <code>false</code> otherwise
*/
public boolean contains(String symbolName) {
return table.containsKey(symbolName);
}
/**
* Returns the value to which the specified symbol name is mapped in this
* symbol table.
*
* @param symbolName
* the name of the symbol whose associated value is to be returned
* @return
* the value to which this symbol table maps <code>symbolName</code>, or
* null if no mapping exists
*/
public SExpression get(String symbolName) {
return table.get(symbolName);
}
/**
* Associates the specified symbol name with the specified value in this
* symbol table. If the symbol table previously contained a mapping for
* this symbol name, the old value has been replaced.
*
* @param symbolName
* the name of the symbol with which the specified value is to be
* associated
* @param value
* the value to be associated with the specified symbol name
*/
public void put(String symbolName, SExpression value) {
table.put(symbolName, value);
}
/**
* Returns the parent of this symbol table.
*
* @return
* the parent of this symbol table
*/
public SymbolTable getParent() {
return parent;
}
}

122
eval/UDFunction.java Normal file
View File

@ -0,0 +1,122 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 2
*/
package eval;
import parser.*;
import java.util.ArrayList;
/**
* A <code>UDFunction</code> is an internal representation of a user-defined
* function in the Lisp programming language.
*/
public class UDFunction extends LispFunction {
// the number of arguments that this user-defined function takes.
private final int NUM_ARGS;
private String name;
private Cons body;
private Cons lexpr;
private SymbolTable environment;
private ArrayList<String> parameters;
/**
* Create a new user-defined function with the specified name, lambda list
* and body.
*
* @param name
* the name of this user-defined function
* @param lambdaList
* a list of the formal parameters of this user-defined function (MUST BE
* A PROPER LIST)
* @param body
* the body of this user-defined function (MUST BE A PROPER LIST)
*/
public UDFunction(String name, Cons lambdaList, Cons body) {
this.name = name;
this.body = body;
this.lexpr = new Cons(new Symbol(name), new Cons(lambdaList, body));
this.environment = SETF.getEnvironment();
this.parameters = new ArrayList<String>();
// retrieve the names of all the formal parameters of this function
while (lambdaList.consp()) {
this.parameters.add(lambdaList.getCar().toString());
lambdaList = (Cons) lambdaList.getCdr();
}
this.NUM_ARGS = this.parameters.size();
}
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to this function
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength != NUM_ARGS) {
Cons originalSExpr = new Cons(new Symbol(name), argList);
String errMsg = "too " +
((argListLength > NUM_ARGS) ? "many" : "few") +
" arguments given to " + name + ": " +
originalSExpr;
throw new RuntimeException(errMsg);
}
// push a new symbol table onto this function's environment (for its
// parameters)
environment = new SymbolTable(environment);
// bind the values of the arguments to the formal parameter names
for (String param : parameters) {
SExpression currentArg = argList.getCar();
environment.put(param, currentArg);
argList = (Cons) argList.getCdr();
}
// store the environment of the S-expression that called this function
// (the current environment)
SymbolTable currentEnvironment = SETF.getEnvironment();
// replace the current environment with the environment of this
// function
SETF.setEnvironment(environment);
Cons currentSExpression = body;
SExpression retval = null;
// evaluate all the S-expressions making up this function's body
while (currentSExpression.consp()) {
retval = EVAL.eval(currentSExpression.getCar());
currentSExpression = (Cons) currentSExpression.getCdr();
}
// replace the environment of the S-expression that called this
// function
SETF.setEnvironment(currentEnvironment);
// remove the bindings of the arguments to the formal parameter names
// in the environment of this function
environment = new SymbolTable(environment.getParent());
return retval;
}
/**
* Return the lambda expression that represents this user-defined function.
*
* @return
* the lambda expression that represents this user-defined function
*/
public Cons getLexpr() {
return lexpr;
}
}

4
eval/package.html Normal file
View File

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

50
extend.lisp Normal file
View File

@ -0,0 +1,50 @@
(defun extend-null (the-list)
(cond
((equal (length the-list) 0) t)
(t nil)
)
)
(defun mapcar (function-name the-list)
(cond
((null the-list) nil)
(t (cons (funcall function-name (first the-list))
(mapcar function-name (rest the-list))))
)
)
(defun maplist (function-name the-list)
(cond
((null the-list) nil)
(t (cons (funcall function-name the-list)
(maplist function-name (rest the-list))))
)
)
(defun extend-apply (function-name param-list)
(eval (cons function-name param-list)))
(defun append (listA listB)
(cond
((null listA) listB)
(t (cons (first listA) (append (rest listA) listB)))
)
)
(defun second (listA) (first (rest listA)))
(defun third (listA) (first (rest (rest listA))))
(defun fourth (listA) (first (rest (rest (rest listA)))))
(defun fifth (listA) (first (rest (rest (rest (rest listA))))))
(defun sixth (listA) (first (rest (rest (rest (rest (rest listA)))))))
(defun seventh (listA) (first (rest (rest (rest (rest (rest (rest listA))))))))
(defun eighth (listA) (first (rest (rest (rest (rest (rest (rest (rest listA)))))))))
(defun ninth (listA) (first (rest (rest (rest (rest (rest (rest (rest (rest listA))))))))))
(defun tenth (listA) (first (rest (rest (rest (rest (rest (rest (rest (rest (rest listA)))))))))))
(defun nth (n listA)
(cond
((equal 0 n) (first listA))
(t (nth (- n 1) (rest listA)))
)
)

3
fact.lisp Normal file
View File

@ -0,0 +1,3 @@
(defun fact (x)
(cond ((> 2 x) 1)
(t (* x (fact (- x 1))))))

91
main/LispInterpreter.java Normal file
View File

@ -0,0 +1,91 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter 1
*/
package main;
import parser.*;
import eval.*;
import error.ErrorManager;
import java.io.*;
import java.text.MessageFormat;
/**
* <code>LispInterpreter</code> is an interpreter for the Lisp programming
* language. It takes the name of a file as a command-line argument, evaluates
* the S-expressions found in the file and then prints the results to the
* console. If no file name is provided at the command-line, this program will
* read from standard input.
*/
public class LispInterpreter {
private static final String GREETING = "SUNY Potsdam Lisp Interpreter - Version 1.0.1";
private static final String PROMPT = "~ ";
public static final String ANSI_RESET = "\u001B[0m";
public static final String ANSI_GREEN = "\u001B[32m";
/**
* Evaluate the S-expressions found in the file given as a command-line
* argument and print the results to the console. If no file name was
* given, retrieve the S-expressions from standard input.
*
* @param args
* the command-line arguments:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
*/
public static void main(String[] args) {
LispParser parser = null;
boolean interactive = false;
if (args.length > 0) {
// a file name was given at the command-line, attempt to create a
// 'LispParser' on it
try {
parser = new LispParser(new FileInputStream(args[0]), args[0]);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(), ErrorManager.CRITICAL_LEVEL);
}
} else {
// no file name was given, create a 'LispParser' on standard input
parser = new LispParser(System.in, "System.in");
interactive = true;
System.out.println(GREETING);
System.out.println();
System.out.print(PROMPT);
}
while (! parser.eof()) {
try {
SExpression sexpr = parser.getSExpr();
String result = MessageFormat.format("{0}{1}{2}", ANSI_GREEN, EVAL.eval(sexpr), ANSI_RESET);
LispInterpreter.erasePrompt(interactive);
System.out.println(result);
} catch (RuntimeException e) {
LispInterpreter.erasePrompt(interactive);
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(), ErrorManager.CRITICAL_LEVEL);
}
if (interactive) {
System.out.print(PROMPT);
}
}
}
private static void erasePrompt(boolean interactive) {
if (interactive) {
for (int i = 0; i < PROMPT.length(); i++) {
System.out.print("\b");
}
}
}
}

View File

@ -0,0 +1,50 @@
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

@ -0,0 +1,65 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package main;
import parser.*;
import error.ErrorManager;
import java.io.*;
/**
* <code>LispParserDriver</code> is a program that takes the name of a file
* as a command-line argument, creates an internal representation of the
* S-expressions found in the file and prints them to the console. If no file
* name is provided at the command-line, this program will read from standard
* input.
*/
public class LispParserDriver {
/**
* Create internal representations of the S-expressions found in the file
* whose name was given as a command-line argument and print them to the
* console. If no file name was given, retrieve the S-expressions from
* standard input.
*
* @param args
* the command-line arguments:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
*/
public static void main(String[] args) {
LispParser parser = null;
if (args.length > 0) {
// a file name was given at the command-line, attempt to create a
// 'LispParser' on it
try {
parser = new LispParser(new FileInputStream(args[0]), args[0]);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} else {
// no file name was given, create a 'LispParser' on standard input
parser = new LispParser(System.in, "System.in");
}
while (! parser.eof()) {
try {
SExpression sexpr = parser.getSExpr();
System.out.println(sexpr.toString());
} catch (RuntimeException e) {
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package main;
import scanner.*;
import error.ErrorManager;
import java.io.*;
/**
* <code>LispScannerDriver</code> is a program that takes the name of a file
* as a command-line argument, retrieves all of the Lisp tokens from the file
* and prints them to the console. If no file name is provided at the
* command-line, this program will read from standard input.
*/
public class LispScannerDriver {
/**
* Obtain the Lisp tokens from the file whose name was given as a
* command-line argument and print them to the console. If no file name was
* given, retrieve the tokens from standard input.
*
* @param args
* the command-line arguments:
* <ul>
* <li><code>args[0]</code> - file name (optional)</li>
* </ul>
*/
public static void main(String[] args) {
LispScanner in = null;
if (args.length > 0) {
// a file name was given at the command-line, attempt to create a
// 'LispScanner' on it
try {
in = new LispScanner(new FileInputStream(args[0]), args[0]);
} catch (FileNotFoundException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} else {
// no file name was given, create a 'LispScanner' on standard input
in = new LispScanner(System.in, "System.in");
}
Token t = null;
do {
try {
t = in.nextToken();
System.out.printf("%-15s%-25s%5d%5d%25s\n", t.getType(),
t.getText(),
t.getLine(),
t.getColumn(),
t.getFName());
} catch (RuntimeException e) {
ErrorManager.generateError(e.getMessage(), 2);
} catch (IOException e) {
ErrorManager.generateError(e.getMessage(),
ErrorManager.CRITICAL_LEVEL);
}
} while ((t == null) || (t.getType() != Token.Type.EOF));
}
}

3
main/package.html Normal file
View File

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

46
parser/Atom.java Normal file
View File

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

111
parser/Cons.java Normal file
View File

@ -0,0 +1,111 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This class represents a Lisp CONS cell in the PL-Lisp implementation.
*/
public class Cons extends SExpression {
private SExpression car;
private SExpression cdr;
/**
* Create a new CONS cell with the specified car and cdr.
*
* @param car
* the car of this CONS cell
* @param cdr
* the cdr of this CONS cell
*/
public Cons(SExpression car, SExpression cdr) {
this.car = car;
this.cdr = cdr;
}
/**
* Retrieve the car of this CONS cell.
*
* @return
* the car of this CONS cell
*/
public SExpression getCar() {
return car;
}
/**
* Retrieve the cdr of this CONS cell.
*
* @return
* the cdr of this CONS cell
*/
public SExpression getCdr() {
return cdr;
}
/**
* Set the car of this CONS cell to the specified value.
*
* @param newCar
* the value to assign to the car of this CONS cell
*/
public void setCar(SExpression newCar) {
car = newCar;
}
/**
* Set the cdr of this CONS cell to the specified value.
*
* @param newCdr
* the value to assign to the cdr of this CONS cell
*/
public void setCdr(SExpression newCdr) {
cdr = newCdr;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>true</code>
*/
public boolean consp() {
return true;
}
/**
* Returns a string representation of this CONS cell.
*
* @return
* a string representation of this CONS cell
*/
public String toString() {
return ("(" + toStringAux());
}
// Returns a string representation of the car of a CONS cell followed by
// its cdr. If the cdr of this CONS cell is not a CONS cell itself, this
// method places a ')' at the end of its return value. Also, if the cdr of
// this CONS cell is NIL, it will not be included in the return value of
// this method. When used in conjunction with the 'toString' method of this
// class, this method provides a means for creating the correct string
// representation of a list.
//
// Returns: a string representation of the car of a CONS cell followed by
// its cdr
private String toStringAux() {
if (cdr.nullp()) {
return (car.toString() + ")");
} else if (cdr.consp()) {
return (car.toString() + " " + ((Cons) cdr).toStringAux());
}
// the cdr of this CONS cell is not a list
return (car.toString() + " . " + cdr.toString() + ")");
}
}

67
parser/LispNumber.java Normal file
View File

@ -0,0 +1,67 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This class represents a NUMBER in the PL-Lisp implementation.
*/
public class LispNumber extends Atom {
private int value;
/**
* Create a new NUMBER with the specified text.
*
* @param text
* the text representing this NUMBER
* @throws IllegalArgumentException
* Indicates that <code>text</code> does not represent a valid integer.
*/
public LispNumber(String text) {
super(text.replaceFirst("^0+(?!$)", ""));
try {
this.value = Integer.parseInt(text);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(text +
" is not a valid integer");
}
}
/**
* Create a new NUMBER with the specified value.
*
* @param value
* the integer value of this NUMBER
*/
public LispNumber(int value) {
super(Integer.toString(value));
this.value = value;
}
/**
* Test if this S-expression is a NUMBER.
*
* @return
* <code>true</code>
*/
public boolean numberp() {
return true;
}
/**
* Retrieve the integer value of this NUMBER.
*
* @return
* the integer value of this NUMBER
*/
public int getValue() {
return value;
}
}

192
parser/LispParser.java Normal file
View File

@ -0,0 +1,192 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
import scanner.*;
import java.io.*;
/**
* A <code>LispParser</code> converts a stream of bytes into internal
* representations of Lisp S-expressions. When the end of stream has been
* reached the <code>eof</code> method of this parser will return true.
*/
public class LispParser {
private LispScanner scanner;
private Token nextToken;
// A field to store an exception that has been thrown in the 'eof' method
// as a result of reading in the next token from 'scanner'.
private Exception delayedException;
// A field to notify us if the next token has already been stored in
// 'nextToken' by the 'eof' method.
private boolean nextTokenStored;
/**
* Create a new <code>LispParser</code> that produces S-expressions from
* the specified input stream.
*
* @param in
* the input stream to obtain S-expressions from (must not be
* <code>null</code>)
* @param fileName
* the name of the file that <code>in</code> is reading from
*/
public LispParser(InputStream in, String fileName) {
scanner = new LispScanner(in, fileName);
nextToken = null;
delayedException = null;
nextTokenStored = false;
}
/**
* Determing if this parser has reached the end of its underlying input
* stream.
*
* @return
* <code>true</code> if this parser has reached the end of its underlying
* input stream; <code>false</code> otherwise
*/
public boolean eof() {
if (! nextTokenStored) {
// attempt to read the next token from 'scanner' and store it in
// 'nextToken'
try {
nextToken = scanner.nextToken();
nextTokenStored = true;
} catch (Exception e) {
// this method should give the illusion of not actually reading
// a token, so we store any exceptions thrown as a result of
// reading in the next token from 'scanner' (it will be thrown
// the next time the 'getSExpr' method is called)
delayedException = e;
if (nextToken == null) {
// we have not successfully read in any tokens yet, so we
// could not have read in an end-of-file token
return false;
}
}
}
return (nextToken.getType() == Token.Type.EOF);
}
/**
* Returns the next S-expression from this parser.
*
* @return
* the next S-expression from this parser
* @throws RuntimeException
* Indicates that an illegal S-expression was encountered in the
* underlying input stream.
* @throws IOException
* Indicates that an I/O error has occurred.
*/
public SExpression getSExpr() throws IOException {
if (delayedException != null) {
// the 'eof' method has stored an exception for us to throw
// determine the type of the stored exception and throw it!
if (delayedException instanceof IOException) {
IOException e = (IOException) delayedException;
// remove the exception from 'delayedException'
delayedException = null;
throw e;
} else if (delayedException instanceof RuntimeException) {
RuntimeException e = (RuntimeException) delayedException;
// remove the exception from 'delayedException'
delayedException = null;
throw e;
}
}
if (! nextTokenStored) {
// the next token has not been stored in 'nextToken' by the 'eof'
// method
nextToken = scanner.nextToken();
} else {
// the 'eof' method has been called and has read in the next token
// already to determine if we have reached the end-of-file
nextTokenStored = false;
}
return sExpr();
}
// sExpr ::= NUMBER | IDENTIFIER | RESERVED | STRING | QUOTE_MARK sExpr |
// LEFT_PAREN sExprTail
//
// Returns: an S-expression that matches the rules given above
// Throws: RuntimeException - Indicates that an illegal S-expression was
// encountered in the underlying input stream.
// Throws: IOException - Indicates that an I/O error has occurred.
// Precondition: 'nextToken' is not null.
private SExpression sExpr() throws IOException {
// determine the type of 'nextToken' and create the appropriate
// S-expression
switch (nextToken.getType()) {
case NUMBER:
return new LispNumber(nextToken.getText());
case IDENTIFIER:
case RESERVED:
return new Symbol(nextToken.getText());
case STRING:
return new LispString(nextToken.getText());
case QUOTE_MARK:
nextToken = scanner.nextToken();
SExpression arg = sExpr();
return new Cons(new Symbol("QUOTE"),
new Cons(arg, Nil.getUniqueInstance()));
case LEFT_PAREN:
return sExprTail();
case RIGHT_PAREN:
throw new RuntimeException("expression begins with \')\'" +
" - line " + nextToken.getLine() +
" column " + nextToken.getColumn());
case EOF:
throw new RuntimeException("end-of-file encountered" +
" - line " + nextToken.getLine() +
" column " + nextToken.getColumn());
default: // unrecognized token type
throw new RuntimeException("unrecognizable token" +
" - line " + nextToken.getLine() +
" column " + nextToken.getColumn());
}
}
// sExprTail ::= RIGHT_PAREN | sExpr sExprTail
//
// Returns: an S-expression that matches the rules given above
// Throws: IOException - Indicates that an I/O error has occurred.
// Precondition: 'scanner' is not null.
private SExpression sExprTail() throws IOException {
nextToken = scanner.nextToken();
// determine the type of 'nextToken' and create the appropriate
// S-expression
switch (nextToken.getType()) {
case RIGHT_PAREN:
return Nil.getUniqueInstance();
default:
SExpression car = sExpr();
SExpression cdr = sExprTail();
return new Cons(car, cdr);
}
}
}

34
parser/LispString.java Normal file
View File

@ -0,0 +1,34 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This class represents a STRING in the PL-Lisp implementation.
*/
public class LispString extends Atom {
/**
* Create a new STRING with the specified text.
*
* @param text
* the text representing this STRING
*/
public LispString(String text) {
super(text);
}
/**
* Test if this S-expression is a STRING.
*
* @return
* <code>true</code>
*/
public boolean stringp() {
return true;
}
}

104
parser/Nil.java Normal file
View File

@ -0,0 +1,104 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This class represents NIL in the PL-Lisp implementation.
*/
public class Nil extends Cons {
private static Nil uniqueInstance = new Nil();
/**
* Retrieve the single unique instance of NIL.
*
* @return
* the single unique instance of NIL
*/
public static Nil getUniqueInstance() {
return uniqueInstance;
}
// We are using the Singleton pattern, so the constructor for 'Nil' is
// private.
private Nil() {
super(null, null);
// the car and cdr of NIL both refer to NIL
super.setCar(this);
super.setCdr(this);
}
/**
* Test if this S-expression is NULL.
*
* @return
* <code>true</code>
*/
public boolean nullp() {
return true;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>true</code>
*/
public boolean atomp() {
return true;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>false</code>
*/
public boolean consp() {
return false;
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>true</code>
*/
public boolean symbolp() {
return true;
}
/**
* Set the car of this CONS cell to the specified value. This method does
* nothing (the car of NIL can not be changed).
*
* @param newCar
* the value to assign to the car of this CONS cell
*/
public void setCar(SExpression newCar) {}
/**
* Set the cdr of this CONS cell to the specified value. This method does
* nothing (the cdr of NIL can not be changed).
*
* @param newCdr
* the value to assign to the cdr of this CONS cell
*/
public void setCdr(SExpression newCdr) {}
/**
* Returns a string representation of NIL.
*
* @return
* a string representation of NIL
*/
public String toString() {
return "NIL";
}
}

122
parser/SExpression.java Normal file
View File

@ -0,0 +1,122 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This is the base class for memory in the PL-Lisp implementation.
*/
public class SExpression {
// for mark and sweep garbage collection
private boolean marked = false;
/**
* Determine if this <code>SExpression</code> is marked.
*
* @return
* <code>true</code> if this <code>SExpression</code> is marked;
* <code>false</code> otherwise
*/
public final boolean isMarked() {
return marked;
}
/**
* Set the marked status of this S-expression to the specified value.
*
* @param value
* the value to assign to this S-expression's marked status
*/
public final void setMarked(final boolean value) {
marked = value;
}
// Lisp type predicates;
// by default, all return false (an SExpression effectively has NO type);
// overridden in subclasses to describe their Lisp type
/**
* Test if this S-expression is NULL.
*
* @return
* <code>false</code>
*/
public boolean nullp() {
return false;
}
/**
* Test if this S-expression is an ATOM.
*
* @return
* <code>false</code>
*/
public boolean atomp() {
return false;
}
/**
* Test if this S-expression is a CONS cell.
*
* @return
* <code>false</code>
*/
public boolean consp() {
return false;
}
/**
* Test if this S-expression is a LIST.
*
* @return
* the value of <code>(consp() || nullp())</code>
*/
public boolean listp() {
return (consp() || nullp());
}
/**
* Test if this S-expression is a NUMBER.
*
* @return
* <code>false</code>
*/
public boolean numberp() {
return false;
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>false</code>
*/
public boolean symbolp() {
return false;
}
/**
* Test if this S-expression is a FUNCTION.
*
* @return
* <code>false</code>
*/
public boolean functionp() {
return false;
}
/**
* Test if this S-expression is a STRING.
*
* @return
* <code>false</code>
*/
public boolean stringp() {
return false;
}
}

37
parser/Symbol.java Normal file
View File

@ -0,0 +1,37 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Parser
*/
package parser;
/**
* This class represents a SYMBOL in the PL-Lisp implementation.
*/
public class Symbol extends Atom {
/** This SYMBOL represents TRUE in the PL-Lisp implementation. */
public static final Symbol T = new Symbol("T");
/**
* Create a new SYMBOL with the specified text.
*
* @param text
* the text representing this SYMBOL
*/
public Symbol(String text) {
super(text.toUpperCase());
}
/**
* Test if this S-expression is a SYMBOL.
*
* @return
* <code>true</code>
*/
public boolean symbolp() {
return true;
}
}

4
parser/package.html Normal file
View File

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

23
reverse.lisp Normal file
View File

@ -0,0 +1,23 @@
(defun reverse (the-list)
(cond
(the-list (append (reverse (rest the-list))
(list (first the-list))
)
)
)
)
(defun deep-reverse (the-list)
(cond
(the-list (append (deep-reverse (rest the-list))
(list (cond
((listp (first the-list))
(deep-reverse (first the-list))
)
(t (first the-list))
)
)
)
)
)
)

View File

@ -0,0 +1,160 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package scanner;
import java.io.InputStream;
import java.io.FilterInputStream;
import java.io.IOException;
/**
* A <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;
}
}

320
scanner/LispScanner.java Normal file
View File

@ -0,0 +1,320 @@
/*
* Name: Mike Cifelli
* Course: CIS 443 - Programming Languages
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
*/
package scanner;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
/**
* A <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 != ';'));
}
}

126
scanner/Token.java Normal file
View File

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

4
scanner/package.html Normal file
View File

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

15
test.lisp Normal file
View File

@ -0,0 +1,15 @@
;; A list containing the values of single-letter Roman numerals.
(setf *roman-number-list*
'((I 1) (V 5) (X 10) (L 50) (C 100) (D 500) (M 1000)))
;; Converts a single Roman numeral letter into its equivalent decimal value.
(defun letter-to-decimal (letter)
(car (cdr ((lambda (lst f)
(cond ((null lst) ())
((eq (car (car lst)) letter) (car lst))
(t (funcall f (cdr lst) f))))
*roman-number-list*
(lambda (lst f)
(cond ((null lst) ())
((eq (car (car lst)) letter) (car lst))
(t (funcall f (cdr lst) f))))))))