Additional refactoring for eclipse
This commit is contained in:
parent
b875727361
commit
4ad31c0570
|
@ -1,10 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="test"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="owner.project.facets" value="java"/>
|
<attribute name="owner.project.facets" value="java"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
<classpathentry kind="output" path="build/classes"/>
|
<classpathentry kind="output" path="build/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
13
.project
13
.project
|
@ -15,6 +15,19 @@
|
||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
<dictionary>
|
||||||
|
<key>LaunchConfigHandle</key>
|
||||||
|
<value><project>/.externalToolBuilders/Ant_Builder.launch</value>
|
||||||
|
</dictionary>
|
||||||
|
<dictionary>
|
||||||
|
<key>incclean</key>
|
||||||
|
<value>true</value>
|
||||||
|
</dictionary>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
</buildSpec>
|
</buildSpec>
|
||||||
<natures>
|
<natures>
|
||||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
<project name="LispInterpreter" basedir="." default="compile">
|
<project name="LispInterpreter" basedir="." default="compile">
|
||||||
<property name="src.dir" value="." />
|
<property name="src.dir" value="src" />
|
||||||
<property name="build.dir" value="build" />
|
<property name="build.dir" value="build" />
|
||||||
<property name="classes.dir" value="${build.dir}/classes" />
|
<property name="classes.dir" value="${build.dir}/classes" />
|
||||||
<property name="jar.dir" value="jar" />
|
<property name="jar.dir" value="jar" />
|
||||||
<property name="doc.dir" value="doc" />
|
<property name="doc.dir" value="doc" />
|
||||||
|
<property name="test.dir" value="test" />
|
||||||
<property name="jar-file" value="${jar.dir}/LispInterpreter.jar" />
|
<property name="jar-file" value="${jar.dir}/LispInterpreter.jar" />
|
||||||
<property name="main-class" value="main.LispInterpreter" />
|
<property name="main-class" value="main.LispInterpreter" />
|
||||||
|
|
||||||
|
@ -16,11 +17,8 @@
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="compile">
|
<target name="compile">
|
||||||
<mkdir dir="${classes.dir}" />
|
|
||||||
<javac srcdir="${src.dir}"
|
<javac srcdir="${src.dir}"
|
||||||
destdir="${classes.dir}"
|
includeantruntime="false" />
|
||||||
includeantruntime="false"
|
|
||||||
excludes="main/LispInterpreter2.java" />
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="jar" depends="compile">
|
<target name="jar" depends="compile">
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
|
|
||||||
*/
|
|
||||||
|
|
||||||
package error;
|
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>ErrorManager</code> is an error handling class for a Lisp interpreter.
|
|
||||||
*/
|
|
||||||
public class ErrorManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The lowest "criticality" level of an error that will cause the currently
|
|
||||||
* running program to terminate.
|
|
||||||
*/
|
|
||||||
public static final int CRITICAL_LEVEL = 3;
|
|
||||||
|
|
||||||
public static final String ANSI_RESET = "\u001B[0m";
|
|
||||||
public static final String ANSI_RED = "\u001B[31m";
|
|
||||||
public static final String ANSI_YELLOW = "\u001B[33m";
|
|
||||||
public static final String ANSI_PURPLE = "\u001B[35m";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints out the specified error message to the console and decides
|
|
||||||
* whether or not to terminate the currently running program.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
* the error message
|
|
||||||
* @param level
|
|
||||||
* the "criticality" level of the error
|
|
||||||
* @postcondition
|
|
||||||
* If <code>level >= 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
<body>
|
|
||||||
Provides a class for managing errors in the Lisp Interpreter.
|
|
||||||
</body>
|
|
|
@ -1,74 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>APPLY</code> represents the APPLY function in Lisp.
|
|
||||||
*/
|
|
||||||
public class APPLY extends LispFunction {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call APPLY with the specified argument list.
|
|
||||||
*
|
|
||||||
* @param argList
|
|
||||||
* the list of arguments to be sent to APPLY (MUST BE A PROPER LIST)
|
|
||||||
* @return
|
|
||||||
* the result of evaluating APPLY on <code>argList</code>
|
|
||||||
*/
|
|
||||||
public static SExpression apply(Cons argList) {
|
|
||||||
return new APPLY().call(argList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of arguments that APPLY takes.
|
|
||||||
private static final int NUM_ARGS = 2;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to APPLY
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("APPLY"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to APPLY: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression car = argList.getCar(); // function name
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
SExpression cadr = cdr.getCar(); // argument list
|
|
||||||
|
|
||||||
// make sure the second argument is a list
|
|
||||||
if (cadr.listp()) {
|
|
||||||
LispFunction function = EVAL.lookupFunction(car.toString());
|
|
||||||
|
|
||||||
if (function == null) {
|
|
||||||
// check if the car of the list is a lambda expression
|
|
||||||
if (car.functionp()) {
|
|
||||||
function = ((LambdaExpression) car).getFunction();
|
|
||||||
} else if (LAMBDA.isLambdaExpression(car)) {
|
|
||||||
Cons lexpr = (Cons) car;
|
|
||||||
|
|
||||||
function = LAMBDA.createFunction(lexpr);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("undefined function " + car);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply the given function to the given argument list
|
|
||||||
return function.call((Cons) cadr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the second argument is not a list
|
|
||||||
throw new RuntimeException("APPLY: " + cadr + " is not a list");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>ATOM</code> represents the ATOM function in Lisp.
|
|
||||||
*/
|
|
||||||
public class ATOM extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that ATOM takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to ATOM
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("ATOM"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to ATOM: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
return (arg.atomp() ? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>CAR</code> represents the CAR function in Lisp.
|
|
||||||
*/
|
|
||||||
public class CAR extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that CAR takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to CAR
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("CAR"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to CAR: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar();
|
|
||||||
|
|
||||||
// make sure that the argument is a list
|
|
||||||
if (argCar.listp()) {
|
|
||||||
Cons arg = (Cons) argCar;
|
|
||||||
|
|
||||||
return arg.getCar();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the argument is not a list
|
|
||||||
throw new RuntimeException("CAR: " + argCar + " is not a list");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>CDR</code> represents the CDR function in Lisp.
|
|
||||||
*/
|
|
||||||
public class CDR extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that CDR takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to CDR
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("CDR"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to CDR: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar();
|
|
||||||
|
|
||||||
// make sure that the argument is a list
|
|
||||||
if (argCar.listp()) {
|
|
||||||
Cons arg = (Cons) argCar;
|
|
||||||
|
|
||||||
return arg.getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the argument is not a list
|
|
||||||
throw new RuntimeException("CDR: " + argCar + " is not a list");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>COND</code> represents the COND form in Lisp.
|
|
||||||
*/
|
|
||||||
public class COND extends LispFunction {
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
if (argList.nullp()) {
|
|
||||||
// return NIL if there are were no arguments passed to COND
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar(); // first clause
|
|
||||||
Cons argCdr = (Cons) argList.getCdr(); // list of remaining clauses
|
|
||||||
|
|
||||||
// make sure the first clause is a list and is not NIL
|
|
||||||
if (argCar.consp()) {
|
|
||||||
Cons clause = (Cons) argCar;
|
|
||||||
SExpression test = EVAL.eval(clause.getCar());
|
|
||||||
|
|
||||||
if (test != Nil.getUniqueInstance()) {
|
|
||||||
// the car of this clause is true, so we evaluate its cdr
|
|
||||||
|
|
||||||
SExpression cdr = clause.getCdr();
|
|
||||||
SExpression retval = test;
|
|
||||||
|
|
||||||
// evaluate all the S-expressions in the cdr of the clause
|
|
||||||
while (cdr.consp()) {
|
|
||||||
retval = EVAL.eval(((Cons) cdr).getCar());
|
|
||||||
cdr = ((Cons) cdr).getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the value of the last S-expression evaluated
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the car of this clause is false, so we test any remaining
|
|
||||||
// clauses
|
|
||||||
|
|
||||||
// check if the list of remaining clauses is a list and is not NIL
|
|
||||||
if (argCdr.consp()) {
|
|
||||||
return call(argCdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are no remaining clauses, so we return NIL
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("COND: clause " + argCar +
|
|
||||||
" should be a list");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>CONS</code> represents the CONS function in Lisp.
|
|
||||||
*/
|
|
||||||
public class CONS extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that CONS takes.
|
|
||||||
private static final int NUM_ARGS = 2;
|
|
||||||
|
|
||||||
public Cons call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to CONS
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("CONS"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to CONS: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the car of the CONS cell we are going to create
|
|
||||||
SExpression argOne = argList.getCar();
|
|
||||||
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
// the cdr of the CONS cell we are going to create
|
|
||||||
SExpression argTwo = cdr.getCar();
|
|
||||||
|
|
||||||
return new Cons(argOne, argTwo);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>DEFUN</code> represents the DEFUN form in Lisp.
|
|
||||||
*/
|
|
||||||
public class DEFUN extends LispFunction {
|
|
||||||
|
|
||||||
// The minimum number of arguments that DEFUN takes.
|
|
||||||
private static final int MIN_ARGS = 3;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to DEFUN
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength < MIN_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("DEFUN"), argList);
|
|
||||||
String errMsg = "too few arguments given to DEFUN: " +
|
|
||||||
originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression name = argList.getCar(); // name of the function
|
|
||||||
|
|
||||||
// make sure the function name is a symbol
|
|
||||||
if (! name.symbolp()) {
|
|
||||||
throw new RuntimeException("DEFUN: " + name + " is not a symbol");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
SExpression cadr = cdr.getCar();
|
|
||||||
|
|
||||||
// make sure the list of arguments (lambda list) is a proper list
|
|
||||||
if (! cadr.listp()) {
|
|
||||||
throw new RuntimeException("DEFUN: " + cadr + " is not a list");
|
|
||||||
} else if (EVAL.isDotted((Cons) cadr)) {
|
|
||||||
throw new RuntimeException("DEFUN: " + cadr +
|
|
||||||
" is not a proper list");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons lambdaList = (Cons) cadr; // lambda list of the function
|
|
||||||
|
|
||||||
// list of S-expressions making up the body of the function
|
|
||||||
Cons body = (Cons) cdr.getCdr();
|
|
||||||
|
|
||||||
HashMap<String, LispFunction> functionTable = EVAL.getFunctionTable();
|
|
||||||
|
|
||||||
// give a warning if this function has already been defined
|
|
||||||
if (functionTable.containsKey(name.toString())) {
|
|
||||||
System.out.println("WARNING: redefining function " +
|
|
||||||
name.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// place the function in the function table
|
|
||||||
functionTable.put(name.toString(),
|
|
||||||
new UDFunction(name.toString(), lambdaList, body));
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>DIVIDE</code> represents the '/' function in Lisp.
|
|
||||||
*/
|
|
||||||
public class DIVIDE extends LispFunction {
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// make sure we have received at least one argument
|
|
||||||
if (argList.nullp()) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("/"), argList);
|
|
||||||
|
|
||||||
throw new RuntimeException("too few arguments given to /: " +
|
|
||||||
originalSExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argFirst = argList.getCar();
|
|
||||||
Cons argRest = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
// make sure that the first argument is a number
|
|
||||||
if (argFirst.numberp()) {
|
|
||||||
LispNumber num1 = (LispNumber) argFirst;
|
|
||||||
|
|
||||||
if (argRest.nullp()) {
|
|
||||||
// there is only one argument, so return the multiplicative
|
|
||||||
// inverse of the number
|
|
||||||
return new LispNumber(1 / num1.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argSecond = argRest.getCar();
|
|
||||||
|
|
||||||
// make sure that the next argument is a number as well
|
|
||||||
if (argSecond.numberp()) {
|
|
||||||
LispNumber num2 = (LispNumber) argSecond;
|
|
||||||
LispNumber quotient = new LispNumber(num1.getValue() /
|
|
||||||
num2.getValue());
|
|
||||||
SExpression argCddr = argRest.getCdr();
|
|
||||||
|
|
||||||
if (argCddr.consp()) {
|
|
||||||
return call(new Cons(quotient, argCddr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return quotient;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("/: " + argSecond + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("/: " + argFirst + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
45
eval/EQ.java
45
eval/EQ.java
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>EQ</code> represents the EQ function in Lisp.
|
|
||||||
*/
|
|
||||||
public class EQ extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that EQ takes.
|
|
||||||
private static final int NUM_ARGS = 2;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to EQ
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("EQ"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to EQ: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argOne = argList.getCar(); // first argument
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
SExpression argTwo = cdr.getCar(); // second argumnet
|
|
||||||
|
|
||||||
if (argOne.atomp() && argTwo.atomp()) {
|
|
||||||
return ((argOne.toString().equals(argTwo.toString()))
|
|
||||||
? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((argOne == argTwo) ? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>EQUAL</code> represents the EQUAL function in Lisp.
|
|
||||||
*/
|
|
||||||
public class EQUAL extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that EQUAL takes.
|
|
||||||
private static final int NUM_ARGS = 2;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to EQUAL
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("EQUAL"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to EQUAL: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argOne = argList.getCar(); // first argument
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
SExpression argTwo = cdr.getCar(); // second argumnet
|
|
||||||
|
|
||||||
if (argOne.consp() && argTwo.consp()) {
|
|
||||||
Cons listOne = (Cons) argOne;
|
|
||||||
Cons listTwo = (Cons) argTwo;
|
|
||||||
SExpression listOneCar = listOne.getCar();
|
|
||||||
SExpression listTwoCar = listTwo.getCar();
|
|
||||||
SExpression listOneCdr = listOne.getCdr();
|
|
||||||
SExpression listTwoCdr = listTwo.getCdr();
|
|
||||||
|
|
||||||
SExpression carEqual =
|
|
||||||
call(new Cons(listOneCar, LIST.makeList(listTwoCar)));
|
|
||||||
SExpression cdrEqual =
|
|
||||||
call(new Cons(listOneCdr, LIST.makeList(listTwoCdr)));
|
|
||||||
|
|
||||||
return (((carEqual == Symbol.T) && (cdrEqual == Symbol.T))
|
|
||||||
? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((argOne.toString().equals(argTwo.toString()))
|
|
||||||
? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>EQUALSP</code> represents the '=' function in Lisp.
|
|
||||||
*/
|
|
||||||
public class EQUALSP extends LispFunction {
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// make sure we have received at least one argument
|
|
||||||
if (argList.nullp()) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("="), argList);
|
|
||||||
|
|
||||||
throw new RuntimeException("too few arguments given to =: " +
|
|
||||||
originalSExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression firstArg = argList.getCar();
|
|
||||||
Cons argRest = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
// make sure that the first argument is a number
|
|
||||||
if (firstArg.numberp()) {
|
|
||||||
LispNumber num1 = (LispNumber) firstArg;
|
|
||||||
|
|
||||||
if (argRest.nullp()) {
|
|
||||||
return Symbol.T;
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression secondArg = argRest.getCar();
|
|
||||||
|
|
||||||
// make sure that the second argument is a number as well
|
|
||||||
if (secondArg.numberp()) {
|
|
||||||
LispNumber num2 = (LispNumber) secondArg;
|
|
||||||
|
|
||||||
if (num1.getValue() == num2.getValue()) {
|
|
||||||
return call(argRest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("=: " + secondArg + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("=: " + firstArg + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
251
eval/EVAL.java
251
eval/EVAL.java
|
@ -1,251 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>EVAL</code> represents the EVAL function in Lisp.
|
|
||||||
*/
|
|
||||||
public class EVAL extends LispFunction {
|
|
||||||
|
|
||||||
// A table to contain all the built-in and user-defined Lisp functions.
|
|
||||||
private static HashMap<String, LispFunction> functionTable =
|
|
||||||
new HashMap<String, LispFunction>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
// place all of the built-in functions into the function table
|
|
||||||
functionTable.put("*", new MULTIPLY());
|
|
||||||
functionTable.put("+", new PLUS());
|
|
||||||
functionTable.put("-", new MINUS());
|
|
||||||
functionTable.put("/", new DIVIDE());
|
|
||||||
functionTable.put("<", new LESSP());
|
|
||||||
functionTable.put("=", new EQUALSP());
|
|
||||||
functionTable.put(">", new GREATERP());
|
|
||||||
functionTable.put("APPLY", new APPLY());
|
|
||||||
functionTable.put("ATOM", new ATOM());
|
|
||||||
functionTable.put("CAR", new CAR());
|
|
||||||
functionTable.put("CDR", new CDR());
|
|
||||||
functionTable.put("COND", new COND());
|
|
||||||
functionTable.put("CONS", new CONS());
|
|
||||||
functionTable.put("DEFUN", new DEFUN());
|
|
||||||
functionTable.put("EQ", new EQ());
|
|
||||||
functionTable.put("EQUAL", new EQUAL());
|
|
||||||
functionTable.put("EVAL", new EVAL());
|
|
||||||
functionTable.put("EXIT", new EXIT());
|
|
||||||
functionTable.put("FIRST", new CAR());
|
|
||||||
functionTable.put("FUNCALL", new FUNCALL());
|
|
||||||
functionTable.put("GREATERP", new GREATERP());
|
|
||||||
functionTable.put("LAMBDA", new LAMBDA());
|
|
||||||
functionTable.put("LENGTH", new LENGTH());
|
|
||||||
functionTable.put("LET", new LET());
|
|
||||||
functionTable.put("LIST", new LIST());
|
|
||||||
functionTable.put("LISTP", new LISTP());
|
|
||||||
functionTable.put("LOAD", new LOAD());
|
|
||||||
functionTable.put("NULL", new NULL());
|
|
||||||
functionTable.put("PRINT", new PRINT());
|
|
||||||
functionTable.put("QUOTE", new QUOTE());
|
|
||||||
functionTable.put("REST", new CDR());
|
|
||||||
functionTable.put("SETF", new SETF());
|
|
||||||
functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the function table.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the function table
|
|
||||||
*/
|
|
||||||
public static HashMap<String, LispFunction> getFunctionTable() {
|
|
||||||
return functionTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Look up a function by its name.
|
|
||||||
*
|
|
||||||
* @param functionName
|
|
||||||
* the name of the function to look up
|
|
||||||
* @return
|
|
||||||
* the function with the name <code>functionName</code> if it exists; null
|
|
||||||
* otherwise
|
|
||||||
*/
|
|
||||||
public static LispFunction lookupFunction(String functionName) {
|
|
||||||
return functionTable.get(functionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Look up a symbol's value using its name.
|
|
||||||
*
|
|
||||||
* @param symbolName
|
|
||||||
* the name of the symbol to look up (must not be null)
|
|
||||||
* @return
|
|
||||||
* the value of <code>symbolName</code> if it has one; null otherwise
|
|
||||||
*/
|
|
||||||
public static SExpression lookupSymbol(String symbolName) {
|
|
||||||
if (symbolName.equals("NIL")) {
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
} else if (symbolName.equals("T")) {
|
|
||||||
return Symbol.T;
|
|
||||||
} else if (symbolName.startsWith(":")) {
|
|
||||||
return new Symbol(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SETF.lookup(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the given list is dotted.
|
|
||||||
*
|
|
||||||
* @param list
|
|
||||||
* the list to be tested (must not be null)
|
|
||||||
* @return
|
|
||||||
* <code>true</code> if <code>list</code> is dotted; <code>false</code>
|
|
||||||
* otherwise
|
|
||||||
*/
|
|
||||||
public static boolean isDotted(Cons list) {
|
|
||||||
if (list.nullp()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression cdr = list.getCdr();
|
|
||||||
|
|
||||||
if (cdr.listp()) {
|
|
||||||
return isDotted((Cons) cdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the cdr of 'list' is not a list, therefore it is dotted
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluate the given S-expression.
|
|
||||||
*
|
|
||||||
* @param sexpr
|
|
||||||
* the S-expression to evaluate
|
|
||||||
* @return
|
|
||||||
* the value of <code>sexpr</code>
|
|
||||||
*/
|
|
||||||
public static SExpression eval(SExpression sexpr) {
|
|
||||||
Cons expList = LIST.makeList(sexpr);
|
|
||||||
EVAL evalFunction = new EVAL();
|
|
||||||
|
|
||||||
return evalFunction.call(expList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of arguments that EVAL takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to EVAL
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("EVAL"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to EVAL: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
if (arg.listp()) {
|
|
||||||
if (arg.consp()) {
|
|
||||||
return evaluateList((Cons) arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return arg; // 'arg' is NIL
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.symbolp()) {
|
|
||||||
SExpression symbolValue = lookupSymbol(arg.toString());
|
|
||||||
|
|
||||||
if (symbolValue != null) {
|
|
||||||
return symbolValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("variable " + arg + " has no value");
|
|
||||||
}
|
|
||||||
|
|
||||||
return arg; // 'arg' is a NUMBER or a STRING
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the specified list.
|
|
||||||
//
|
|
||||||
// Parameters: list - the list to evaluate
|
|
||||||
// Returns: the value of 'list'
|
|
||||||
// Precondition: 'list' must not be null.
|
|
||||||
private SExpression evaluateList(Cons list) {
|
|
||||||
SExpression car = list.getCar();
|
|
||||||
SExpression cdr = list.getCdr();
|
|
||||||
|
|
||||||
LispFunction function = lookupFunction(car.toString());
|
|
||||||
|
|
||||||
if (function == null) {
|
|
||||||
// check if the car of the list is a lambda expression
|
|
||||||
if (car.functionp()) {
|
|
||||||
function = ((LambdaExpression) car).getFunction();
|
|
||||||
} else if (LAMBDA.isLambdaExpression(car)) {
|
|
||||||
Cons lexpr = (Cons) car;
|
|
||||||
|
|
||||||
function = LAMBDA.createFunction(lexpr);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("undefined function " + car);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure the list of arguments for 'function' is a list
|
|
||||||
if (cdr.listp()) {
|
|
||||||
Cons args = (Cons) cdr;
|
|
||||||
|
|
||||||
// make sure the list of arguments is not dotted
|
|
||||||
if (isDotted(args)) {
|
|
||||||
throw new RuntimeException("argument list given to " + car +
|
|
||||||
" is dotted: " + list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine if we should evaluate the arguments that will be
|
|
||||||
// passed to 'function'
|
|
||||||
if (function.evaluateArguments()) {
|
|
||||||
args = evaluateArgList(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function.call(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the list of arguments is not a list!
|
|
||||||
throw new RuntimeException("argument list given to " + car +
|
|
||||||
" is dotted: " + list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate a list of arguments for a function.
|
|
||||||
//
|
|
||||||
// Parameters: arguments - a list of arguments for a function
|
|
||||||
// Returns: a list consisting of the values of the S-expressions found in
|
|
||||||
// 'arguments'
|
|
||||||
// Precondition: 'arguments' must not be null.
|
|
||||||
private Cons evaluateArgList(Cons arguments) {
|
|
||||||
if (arguments.nullp()) {
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression car = eval(arguments.getCar());
|
|
||||||
SExpression cdr = arguments.getCdr();
|
|
||||||
|
|
||||||
if (cdr.listp()) {
|
|
||||||
return new Cons(car, evaluateArgList((Cons) cdr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove any parameters found after a dot (put here in case the check
|
|
||||||
// for a dotted parameter list is not done prior to this call)
|
|
||||||
return new Cons(car, Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
public class EXIT extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that EXIT takes.
|
|
||||||
private static final int NUM_ARGS = 0;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to EXIT
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength > NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("EXIT"), argList);
|
|
||||||
String errMsg = "too many arguments given to EXIT: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.exit(0);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>FUNCALL</code> represents the FUNCALL function in Lisp.
|
|
||||||
*/
|
|
||||||
public class FUNCALL extends LispFunction {
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// make sure we have received at least one argument
|
|
||||||
if (argList.nullp()) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("FUNCALL"), argList);
|
|
||||||
|
|
||||||
throw new RuntimeException("too few arguments given to FUNCALL: " +
|
|
||||||
originalSExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression cdr = argList.getCdr();
|
|
||||||
Cons applyArgs = new Cons(argList.getCar(), LIST.makeList(cdr));
|
|
||||||
|
|
||||||
return APPLY.apply(applyArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>GREATERP</code> represents the '>' 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
108
eval/LAMBDA.java
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LAMBDA</code> represents the LAMBDA form in Lisp.
|
|
||||||
*/
|
|
||||||
public class LAMBDA extends LispFunction {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the given S-expression is a lambda expression.
|
|
||||||
*
|
|
||||||
* @param sexpr
|
|
||||||
* the S-expression to test (must not be null)
|
|
||||||
* @return
|
|
||||||
* <code>true</code> if <code>sexpr</code> is a valid lambda expression;
|
|
||||||
* <code>false</code> otherwise
|
|
||||||
*/
|
|
||||||
public static boolean isLambdaExpression(SExpression sexpr) {
|
|
||||||
if (sexpr.consp()) {
|
|
||||||
SExpression first = ((Cons) sexpr).getCar();
|
|
||||||
|
|
||||||
return "LAMBDA".equals(first.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an internal representation of a user-defined function from the
|
|
||||||
* specified lambda expression.
|
|
||||||
*
|
|
||||||
* @param lexpr
|
|
||||||
* the lambda expression to create the function from (must not be null)
|
|
||||||
* @return
|
|
||||||
* an internal representation of a user-defined function created from
|
|
||||||
* <code>lexpr</code>
|
|
||||||
* @throws RuntimeException
|
|
||||||
* Indicates that <code>lexpr</code> is not a valid lambda expression.
|
|
||||||
*/
|
|
||||||
public static UDFunction createFunction(Cons lexpr) {
|
|
||||||
LAMBDA lambda = new LAMBDA();
|
|
||||||
SExpression cdr = lexpr.getCdr();
|
|
||||||
|
|
||||||
// make sure lexpr is a proper list
|
|
||||||
if (! cdr.consp()) {
|
|
||||||
throw new RuntimeException("invalid lambda expression");
|
|
||||||
} else if (EVAL.isDotted((Cons) cdr)) {
|
|
||||||
throw new RuntimeException("dotted lambda expression " + lexpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons rest = (Cons) cdr;
|
|
||||||
|
|
||||||
return lambda.call(rest).getFunction();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The minimum number of arguments that LAMBDA takes.
|
|
||||||
private static final int MIN_ARGS = 2;
|
|
||||||
|
|
||||||
public LambdaExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to LAMBDA
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength < MIN_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("LAMBDA"), argList);
|
|
||||||
String errMsg = "too few arguments given to LAMBDA: " +
|
|
||||||
originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression car = argList.getCar();
|
|
||||||
|
|
||||||
// make sure the list of arguments is a proper list
|
|
||||||
if (! car.listp()) {
|
|
||||||
throw new RuntimeException("LAMBDA: " + car + " is not a list");
|
|
||||||
} else if (EVAL.isDotted((Cons) car)) {
|
|
||||||
throw new RuntimeException("LAMBDA: " + car +
|
|
||||||
" must be a proper list");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons lambdaList = (Cons) car;
|
|
||||||
Cons body = (Cons) argList.getCdr();
|
|
||||||
Cons lexpr = new Cons(new Symbol("LAMBDA"), argList);
|
|
||||||
UDFunction function = new UDFunction(":LAMBDA", lambdaList, body);
|
|
||||||
|
|
||||||
return new LambdaExpression(lexpr, function);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LENGTH</code> represents the LENGTH function in Lisp.
|
|
||||||
*/
|
|
||||||
public class LENGTH extends LispFunction {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length of the given list.
|
|
||||||
*
|
|
||||||
* @param list
|
|
||||||
* the list to determine the length of
|
|
||||||
* @return
|
|
||||||
* the length of <code>list</code>
|
|
||||||
*/
|
|
||||||
public static int getLength(Cons list) {
|
|
||||||
LENGTH lengthFunction = new LENGTH();
|
|
||||||
LispNumber length = lengthFunction.call(LIST.makeList(list));
|
|
||||||
|
|
||||||
return length.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LispNumber call(Cons argList) {
|
|
||||||
// make sure we have received at least one argument
|
|
||||||
if (argList.nullp()) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
|
|
||||||
|
|
||||||
throw new RuntimeException("too few arguments given to LENGTH: " +
|
|
||||||
originalSExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar();
|
|
||||||
SExpression argCdr = argList.getCdr();
|
|
||||||
|
|
||||||
// make sure we have received only one argument
|
|
||||||
if (! argCdr.nullp()) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
|
|
||||||
|
|
||||||
throw new RuntimeException("too many arguments given to LENGTH: " +
|
|
||||||
originalSExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that the argument is a list
|
|
||||||
if (argCar.listp()) {
|
|
||||||
Cons arg = (Cons) argCar;
|
|
||||||
|
|
||||||
if (arg.nullp()) {
|
|
||||||
return new LispNumber(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons cdr = LIST.makeList(arg.getCdr());
|
|
||||||
LispNumber cdrLength = call(cdr);
|
|
||||||
|
|
||||||
return new LispNumber(1 + cdrLength.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("LENGTH: a proper list must not end with " +
|
|
||||||
argCar);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LESSP</code> represents the '<' 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
116
eval/LET.java
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LET</code> represents the LET form in Lisp.
|
|
||||||
*/
|
|
||||||
public class LET extends LispFunction {
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// make sure we have received at least one argument
|
|
||||||
if (argList.nullp()) {
|
|
||||||
throw new RuntimeException("too few arguments given to LET");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new symbol table on top of the current environment to add
|
|
||||||
// all the local variables to
|
|
||||||
SymbolTable environment = new SymbolTable(SETF.getEnvironment());
|
|
||||||
|
|
||||||
SExpression car = argList.getCar();
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
addVariablesToTable(environment, car);
|
|
||||||
SETF.setEnvironment(environment);
|
|
||||||
|
|
||||||
SExpression retval = Nil.getUniqueInstance();
|
|
||||||
|
|
||||||
// evaluate all S-expression in the body
|
|
||||||
while (cdr.consp()) {
|
|
||||||
retval = EVAL.eval(cdr.getCar());
|
|
||||||
cdr = (Cons) cdr.getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore the environment to its original value
|
|
||||||
SETF.setEnvironment(environment.getParent());
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a list of variables and their values to the specified symbol table.
|
|
||||||
//
|
|
||||||
// Parameters: environment - the symbol table to add the variables and
|
|
||||||
// their values to (must not be null)
|
|
||||||
// vars - a list of variable/value pairs (must be either a
|
|
||||||
// proper list of pairs or NIL)
|
|
||||||
// Throws: RuntimeException - Indicates that 'vars' is not a proper list or
|
|
||||||
// that it contains an member that is not a
|
|
||||||
// variable/value pair.
|
|
||||||
// Precondition: 'environment' and 'vars' must not be null.
|
|
||||||
// Postcondition: All of the variables in 'vars' have been placed into
|
|
||||||
// 'environment' with their values.
|
|
||||||
private void addVariablesToTable(SymbolTable environment,
|
|
||||||
SExpression vars) {
|
|
||||||
// makes sure the list of variable/value pairs is a list
|
|
||||||
if (! vars.listp()) {
|
|
||||||
throw new RuntimeException("LET: " + vars +
|
|
||||||
" is not a properly formatted" +
|
|
||||||
" variable/value pair list");
|
|
||||||
}
|
|
||||||
|
|
||||||
// add all variables in 'vars' to 'environment'
|
|
||||||
while (vars.consp()) {
|
|
||||||
Cons varList = (Cons) vars;
|
|
||||||
SExpression varListCar = varList.getCar();
|
|
||||||
|
|
||||||
// make sure this variable/value pair is a list
|
|
||||||
if (! varListCar.consp()) {
|
|
||||||
throw new RuntimeException("LET: " + varListCar +
|
|
||||||
" is not a properly formatted" +
|
|
||||||
" variable/value pair");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons varSpec = (Cons) varListCar;
|
|
||||||
SExpression symbol = varSpec.getCar();
|
|
||||||
SExpression varSpecCdr = varSpec.getCdr();
|
|
||||||
|
|
||||||
// make sure this variable pair has a value associated with it
|
|
||||||
if (! varSpecCdr.consp()) {
|
|
||||||
throw new RuntimeException("LET: illegal variable " +
|
|
||||||
"specification " + varSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons varValue = (Cons) varSpecCdr;
|
|
||||||
SExpression value = varValue.getCar();
|
|
||||||
|
|
||||||
// make sure there are no more members of this variable/value pair
|
|
||||||
// and that 'symbol' is actually a symbol
|
|
||||||
if ((! varValue.getCdr().nullp()) || (! symbol.symbolp())) {
|
|
||||||
throw new RuntimeException("LET: illegal variable " +
|
|
||||||
"specification " + varSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
environment.put(symbol.toString(), value);
|
|
||||||
|
|
||||||
vars = varList.getCdr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LIST</code> represents the LIST function in Lisp.
|
|
||||||
*/
|
|
||||||
public class LIST extends LispFunction {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Places the given S-expression into a list.
|
|
||||||
*
|
|
||||||
* @param sexpr
|
|
||||||
* the S-expression to be placed into a list
|
|
||||||
* @return
|
|
||||||
* a list with <code>sexpr</code> as the car and NIL as the cdr.
|
|
||||||
*/
|
|
||||||
public static Cons makeList(SExpression sexpr) {
|
|
||||||
return new Cons(sexpr, Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cons call(Cons argList) {
|
|
||||||
if (argList.nullp()) {
|
|
||||||
// return NIL if there were no arguments passed to LIST
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar();
|
|
||||||
Cons argCdr = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
return new Cons(argCar, call(argCdr));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LISTP</code> represents the LISTP function in Lisp.
|
|
||||||
*/
|
|
||||||
public class LISTP extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that LISTP takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to LISTP
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("LISTP"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to LISTP: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
return (arg.listp() ? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LOAD</code> represents the LOAD function in Lisp.
|
|
||||||
*/
|
|
||||||
public class LOAD extends LispFunction {
|
|
||||||
// The number of arguments that LOAD takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to LOAD
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("LOAD"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to LOAD: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argCar = argList.getCar();
|
|
||||||
|
|
||||||
// make sure the argument is a string
|
|
||||||
if (! argCar.stringp()) {
|
|
||||||
throw new RuntimeException("LOAD: " + argCar + " is not a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
LispString quotedName = (LispString) argCar;
|
|
||||||
String fileName = quotedName.toString();
|
|
||||||
|
|
||||||
// remove the surrounding quotes from the file name
|
|
||||||
fileName = fileName.substring(1, (fileName.length() - 1));
|
|
||||||
|
|
||||||
return processFile(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate all the S-expressions found in the file with the specified
|
|
||||||
// name.
|
|
||||||
//
|
|
||||||
// Parameters: fileName - the name of the file to be evaluated
|
|
||||||
// Returns: 'T' if the file was processed successfully; 'NIL' otherwise
|
|
||||||
private SExpression processFile(String fileName) {
|
|
||||||
LispParser parser = null;
|
|
||||||
|
|
||||||
// attempt to create a new 'LispParser' on the specified file
|
|
||||||
try {
|
|
||||||
parser = new LispParser(new FileInputStream(fileName), fileName);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
System.out.println("LOAD: could not open " + fileName);
|
|
||||||
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to evaluate all the S-expressions contained in the file
|
|
||||||
while (! parser.eof()) {
|
|
||||||
try {
|
|
||||||
SExpression sexpr = parser.getSExpr();
|
|
||||||
|
|
||||||
EVAL.eval(sexpr);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
System.out.println("LOAD: " + e.getMessage());
|
|
||||||
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println("LOAD: " + e.getMessage());
|
|
||||||
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// success!
|
|
||||||
return Symbol.T;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents a Lisp FUNCTION in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class LambdaExpression extends SExpression {
|
|
||||||
|
|
||||||
private Cons lexpr;
|
|
||||||
private UDFunction function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new FUNCTION with the specified lambda expression and
|
|
||||||
* internal representation.
|
|
||||||
*
|
|
||||||
* @param lexpr
|
|
||||||
* the lambda expression of this FUNCTION
|
|
||||||
* @param function
|
|
||||||
* the internal representation of this FUNCTION
|
|
||||||
*/
|
|
||||||
public LambdaExpression(Cons lexpr, UDFunction function) {
|
|
||||||
this.lexpr = lexpr;
|
|
||||||
this.function = function;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a FUNCTION.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean functionp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the lambda expression of this FUNCTION.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the lambda expression of this FUNCTION
|
|
||||||
*/
|
|
||||||
public Cons getLExpression() {
|
|
||||||
return lexpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the internal representation of this FUNCTION.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the user-defined function of this FUNCTION
|
|
||||||
*/
|
|
||||||
public UDFunction getFunction() {
|
|
||||||
return function;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this FUNCTION.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* a string representation of this FUNCTION
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
return lexpr.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>LispFunction</code> is an internal representation of a built-in
|
|
||||||
* function in the Lisp programming language.
|
|
||||||
*/
|
|
||||||
public abstract class LispFunction {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this Lisp function with the given list of arguments.
|
|
||||||
*
|
|
||||||
* @param argList
|
|
||||||
* the list of arguments to pass to this function (MUST BE A PROPER LIST)
|
|
||||||
* @return
|
|
||||||
* the resulting S-expression of calling this function with the specified
|
|
||||||
* arguments
|
|
||||||
* @throws RuntimeException
|
|
||||||
* Indicates that an incorrect number of arguments has been passed to this
|
|
||||||
* function or that one of the arguments is not of the expected type.
|
|
||||||
*/
|
|
||||||
public abstract SExpression call(Cons argList);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated. A subclass should override this method to return
|
|
||||||
* <code>false</code> if it does not want its arguments to be evaluated
|
|
||||||
* prior to being passed.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>MINUS</code> represents the '-' function in Lisp.
|
|
||||||
*/
|
|
||||||
public class MINUS extends LispFunction {
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// make sure we have received at least one argument
|
|
||||||
if (argList.nullp()) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("-"), argList);
|
|
||||||
|
|
||||||
throw new RuntimeException("too few arguments given to -: " +
|
|
||||||
originalSExpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argFirst = argList.getCar();
|
|
||||||
Cons argRest = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
// make sure that the first argument is a number
|
|
||||||
if (argFirst.numberp()) {
|
|
||||||
LispNumber num1 = (LispNumber) argFirst;
|
|
||||||
|
|
||||||
if (argRest.nullp()) {
|
|
||||||
// there is only one argument, so return the additive
|
|
||||||
// inverse of the number
|
|
||||||
return new LispNumber(- num1.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argSecond = argRest.getCar();
|
|
||||||
|
|
||||||
// make sure that the next argument is a number as well
|
|
||||||
if (argSecond.numberp()) {
|
|
||||||
LispNumber num2 = (LispNumber) argSecond;
|
|
||||||
LispNumber difference = new LispNumber(num1.getValue() -
|
|
||||||
num2.getValue());
|
|
||||||
SExpression argCddr = argRest.getCdr();
|
|
||||||
|
|
||||||
if (argCddr.consp()) {
|
|
||||||
return call(new Cons(difference, argCddr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("-: " + argSecond + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("-: " + argFirst + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>MULTIPLY</code> represents the '*' function in Lisp.
|
|
||||||
*/
|
|
||||||
public class MULTIPLY extends LispFunction {
|
|
||||||
|
|
||||||
public LispNumber call(Cons argList) {
|
|
||||||
if (argList.nullp()) {
|
|
||||||
return new LispNumber(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argFirst = argList.getCar();
|
|
||||||
Cons argRest = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
if (argFirst.numberp()) {
|
|
||||||
LispNumber num1 = (LispNumber) argFirst;
|
|
||||||
LispNumber num2 = call(argRest);
|
|
||||||
|
|
||||||
return new LispNumber(num1.getValue() * num2.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("*: " + argFirst + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>NULL</code> represents the NULL function in Lisp.
|
|
||||||
*/
|
|
||||||
public class NULL extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that NULL takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to NULL
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("NULL"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to NULL: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
return (arg.nullp() ? Symbol.T : Nil.getUniqueInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>PLUS</code> represents the '+' function in Lisp.
|
|
||||||
*/
|
|
||||||
public class PLUS extends LispFunction {
|
|
||||||
|
|
||||||
public LispNumber call(Cons argList) {
|
|
||||||
if (argList.nullp()) {
|
|
||||||
return new LispNumber(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression argFirst = argList.getCar();
|
|
||||||
Cons argRest = (Cons) argList.getCdr();
|
|
||||||
|
|
||||||
if (argFirst.numberp()) {
|
|
||||||
LispNumber num1 = (LispNumber) argFirst;
|
|
||||||
LispNumber num2 = call(argRest);
|
|
||||||
|
|
||||||
return new LispNumber(num1.getValue() + num2.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("+: " + argFirst + " is not a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>PRINT</code> represents the PRINT function in Lisp.
|
|
||||||
*/
|
|
||||||
public class PRINT extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that PRINT takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to PRINT
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("PRINT"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to PRINT: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
System.out.println(arg);
|
|
||||||
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>QUOTE</code> represents the QUOTE form in Lisp.
|
|
||||||
*/
|
|
||||||
public class QUOTE extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that QUOTE takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to QUOTE
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received exactly one argument
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("QUOTE"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to QUOTE: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return argList.getCar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
112
eval/SETF.java
112
eval/SETF.java
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>SETF</code> represents the SETF form in Lisp.
|
|
||||||
*/
|
|
||||||
public class SETF extends LispFunction {
|
|
||||||
|
|
||||||
private static SymbolTable environment = new SymbolTable();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Look up the value of a symbol using its name.
|
|
||||||
*
|
|
||||||
* @param symbolName
|
|
||||||
* the name of the symbol to look up
|
|
||||||
* @return
|
|
||||||
* the value of <code>symbolName</code> if it has one; null otherwise
|
|
||||||
*/
|
|
||||||
public static SExpression lookup(String symbolName) {
|
|
||||||
SymbolTable current = environment;
|
|
||||||
|
|
||||||
while (current != null) {
|
|
||||||
if (current.contains(symbolName)) {
|
|
||||||
return current.get(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
current = current.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current environment to the specified value.
|
|
||||||
*
|
|
||||||
* @param newEnvironment
|
|
||||||
* the value to set the environment to
|
|
||||||
*/
|
|
||||||
public static void setEnvironment(SymbolTable newEnvironment) {
|
|
||||||
environment = newEnvironment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the current environment.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the current environment
|
|
||||||
*/
|
|
||||||
public static SymbolTable getEnvironment() {
|
|
||||||
return environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of arguments that SETF takes.
|
|
||||||
private static final int NUM_ARGS = 2;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to SETF
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("SETF"), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to SETF: " + originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression symbol = argList.getCar();
|
|
||||||
|
|
||||||
// make sure the first argument is a symbol
|
|
||||||
if (! symbol.symbolp()) {
|
|
||||||
throw new RuntimeException("SETF: " + symbol + " is not a symbol");
|
|
||||||
}
|
|
||||||
|
|
||||||
Cons cdr = (Cons) argList.getCdr();
|
|
||||||
SExpression value = EVAL.eval(cdr.getCar());
|
|
||||||
|
|
||||||
SymbolTable current = environment;
|
|
||||||
|
|
||||||
// set 'current' to the symbol table that contains 'symbol' or the
|
|
||||||
// global symbol table if 'symbol' is not in the environment
|
|
||||||
while ((! current.contains(symbol.toString())) &&
|
|
||||||
(current.getParent() != null)) {
|
|
||||||
current = current.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
current.put(symbol.toString(), value);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the arguments passed to this Lisp function should be
|
|
||||||
* evaluated.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean evaluateArguments() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>SYMBOL_FUNCTION</code> represents the SYMBOL-FUNCTION function in
|
|
||||||
* Lisp.
|
|
||||||
*/
|
|
||||||
public class SYMBOL_FUNCTION extends LispFunction {
|
|
||||||
|
|
||||||
// The number of arguments that SYMBOL-FUNCTION takes.
|
|
||||||
private static final int NUM_ARGS = 1;
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to SYMBOL-FUNCTION
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol("SYMBOL-FUNCTION"),
|
|
||||||
argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to SYMBOL-FUNCTION: " +
|
|
||||||
originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
SExpression arg = argList.getCar();
|
|
||||||
|
|
||||||
// make sure the argument is a symbol
|
|
||||||
if (arg.symbolp()) {
|
|
||||||
LispFunction function = EVAL.lookupFunction(arg.toString());
|
|
||||||
|
|
||||||
// make sure the function actually exists
|
|
||||||
if (function != null) {
|
|
||||||
if (function instanceof UDFunction) {
|
|
||||||
// this is a user-defined function
|
|
||||||
|
|
||||||
UDFunction udFunction = (UDFunction) function;
|
|
||||||
|
|
||||||
return udFunction.getLexpr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a built-in function
|
|
||||||
|
|
||||||
return new Symbol("SUBR-" + arg.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("SYMBOL-FUNCTION: undefined function " +
|
|
||||||
arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("SYMBOL-FUNCTION: " + arg +
|
|
||||||
" is not a symbol");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>SymbolTable</code> maps symbol names to values.
|
|
||||||
*/
|
|
||||||
public class SymbolTable {
|
|
||||||
|
|
||||||
private HashMap<String, SExpression> table;
|
|
||||||
private SymbolTable parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new symbol table with no parent.
|
|
||||||
*/
|
|
||||||
public SymbolTable() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new symbol table with the specified parent.
|
|
||||||
*
|
|
||||||
* @param parent
|
|
||||||
* the parent of this symbol table
|
|
||||||
*/
|
|
||||||
public SymbolTable(SymbolTable parent) {
|
|
||||||
this.table = new HashMap<String, SExpression>();
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the specified symbol name is in this symbol table.
|
|
||||||
*
|
|
||||||
* @param symbolName
|
|
||||||
* the name of the symbol to look up
|
|
||||||
* @return
|
|
||||||
* <code>true</code> if the symbol is in this symbol table;
|
|
||||||
* <code>false</code> otherwise
|
|
||||||
*/
|
|
||||||
public boolean contains(String symbolName) {
|
|
||||||
return table.containsKey(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value to which the specified symbol name is mapped in this
|
|
||||||
* symbol table.
|
|
||||||
*
|
|
||||||
* @param symbolName
|
|
||||||
* the name of the symbol whose associated value is to be returned
|
|
||||||
* @return
|
|
||||||
* the value to which this symbol table maps <code>symbolName</code>, or
|
|
||||||
* null if no mapping exists
|
|
||||||
*/
|
|
||||||
public SExpression get(String symbolName) {
|
|
||||||
return table.get(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associates the specified symbol name with the specified value in this
|
|
||||||
* symbol table. If the symbol table previously contained a mapping for
|
|
||||||
* this symbol name, the old value has been replaced.
|
|
||||||
*
|
|
||||||
* @param symbolName
|
|
||||||
* the name of the symbol with which the specified value is to be
|
|
||||||
* associated
|
|
||||||
* @param value
|
|
||||||
* the value to be associated with the specified symbol name
|
|
||||||
*/
|
|
||||||
public void put(String symbolName, SExpression value) {
|
|
||||||
table.put(symbolName, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the parent of this symbol table.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the parent of this symbol table
|
|
||||||
*/
|
|
||||||
public SymbolTable getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
package eval;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>UDFunction</code> is an internal representation of a user-defined
|
|
||||||
* function in the Lisp programming language.
|
|
||||||
*/
|
|
||||||
public class UDFunction extends LispFunction {
|
|
||||||
|
|
||||||
// the number of arguments that this user-defined function takes.
|
|
||||||
private final int NUM_ARGS;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private Cons body;
|
|
||||||
private Cons lexpr;
|
|
||||||
private SymbolTable environment;
|
|
||||||
private ArrayList<String> parameters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new user-defined function with the specified name, lambda list
|
|
||||||
* and body.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* the name of this user-defined function
|
|
||||||
* @param lambdaList
|
|
||||||
* a list of the formal parameters of this user-defined function (MUST BE
|
|
||||||
* A PROPER LIST)
|
|
||||||
* @param body
|
|
||||||
* the body of this user-defined function (MUST BE A PROPER LIST)
|
|
||||||
*/
|
|
||||||
public UDFunction(String name, Cons lambdaList, Cons body) {
|
|
||||||
this.name = name;
|
|
||||||
this.body = body;
|
|
||||||
this.lexpr = new Cons(new Symbol(name), new Cons(lambdaList, body));
|
|
||||||
this.environment = SETF.getEnvironment();
|
|
||||||
this.parameters = new ArrayList<String>();
|
|
||||||
|
|
||||||
// retrieve the names of all the formal parameters of this function
|
|
||||||
while (lambdaList.consp()) {
|
|
||||||
this.parameters.add(lambdaList.getCar().toString());
|
|
||||||
|
|
||||||
lambdaList = (Cons) lambdaList.getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.NUM_ARGS = this.parameters.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SExpression call(Cons argList) {
|
|
||||||
// retrieve the number of arguments passed to this function
|
|
||||||
int argListLength = LENGTH.getLength(argList);
|
|
||||||
|
|
||||||
// make sure we have received the proper number of arguments
|
|
||||||
if (argListLength != NUM_ARGS) {
|
|
||||||
Cons originalSExpr = new Cons(new Symbol(name), argList);
|
|
||||||
String errMsg = "too " +
|
|
||||||
((argListLength > NUM_ARGS) ? "many" : "few") +
|
|
||||||
" arguments given to " + name + ": " +
|
|
||||||
originalSExpr;
|
|
||||||
|
|
||||||
throw new RuntimeException(errMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// push a new symbol table onto this function's environment (for its
|
|
||||||
// parameters)
|
|
||||||
environment = new SymbolTable(environment);
|
|
||||||
|
|
||||||
// bind the values of the arguments to the formal parameter names
|
|
||||||
for (String param : parameters) {
|
|
||||||
SExpression currentArg = argList.getCar();
|
|
||||||
|
|
||||||
environment.put(param, currentArg);
|
|
||||||
|
|
||||||
argList = (Cons) argList.getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the environment of the S-expression that called this function
|
|
||||||
// (the current environment)
|
|
||||||
SymbolTable currentEnvironment = SETF.getEnvironment();
|
|
||||||
|
|
||||||
// replace the current environment with the environment of this
|
|
||||||
// function
|
|
||||||
SETF.setEnvironment(environment);
|
|
||||||
|
|
||||||
Cons currentSExpression = body;
|
|
||||||
SExpression retval = null;
|
|
||||||
|
|
||||||
// evaluate all the S-expressions making up this function's body
|
|
||||||
while (currentSExpression.consp()) {
|
|
||||||
retval = EVAL.eval(currentSExpression.getCar());
|
|
||||||
currentSExpression = (Cons) currentSExpression.getCdr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace the environment of the S-expression that called this
|
|
||||||
// function
|
|
||||||
SETF.setEnvironment(currentEnvironment);
|
|
||||||
|
|
||||||
// remove the bindings of the arguments to the formal parameter names
|
|
||||||
// in the environment of this function
|
|
||||||
environment = new SymbolTable(environment.getParent());
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the lambda expression that represents this user-defined function.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the lambda expression that represents this user-defined function
|
|
||||||
*/
|
|
||||||
public Cons getLexpr() {
|
|
||||||
return lexpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
<body>
|
|
||||||
Provides functions and forms to be used during the evaluation of an
|
|
||||||
S-expression.
|
|
||||||
</body>
|
|
|
@ -1,91 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import eval.*;
|
|
||||||
import error.ErrorManager;
|
|
||||||
import java.io.*;
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LispInterpreter</code> is an interpreter for the Lisp programming
|
|
||||||
* language. It takes the name of a file as a command-line argument, evaluates
|
|
||||||
* the S-expressions found in the file and then prints the results to the
|
|
||||||
* console. If no file name is provided at the command-line, this program will
|
|
||||||
* read from standard input.
|
|
||||||
*/
|
|
||||||
public class LispInterpreter {
|
|
||||||
|
|
||||||
private static final String GREETING = "SUNY Potsdam Lisp Interpreter - Version 1.0.1";
|
|
||||||
private static final String PROMPT = "~ ";
|
|
||||||
|
|
||||||
public static final String ANSI_RESET = "\u001B[0m";
|
|
||||||
public static final String ANSI_GREEN = "\u001B[32m";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluate the S-expressions found in the file given as a command-line
|
|
||||||
* argument and print the results to the console. If no file name was
|
|
||||||
* given, retrieve the S-expressions from standard input.
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
* the command-line arguments:
|
|
||||||
* <ul>
|
|
||||||
* <li><code>args[0]</code> - file name (optional)</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
LispParser parser = null;
|
|
||||||
boolean interactive = false;
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
// a file name was given at the command-line, attempt to create a
|
|
||||||
// 'LispParser' on it
|
|
||||||
try {
|
|
||||||
parser = new LispParser(new FileInputStream(args[0]), args[0]);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(), ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no file name was given, create a 'LispParser' on standard input
|
|
||||||
parser = new LispParser(System.in, "System.in");
|
|
||||||
interactive = true;
|
|
||||||
|
|
||||||
System.out.println(GREETING);
|
|
||||||
System.out.println();
|
|
||||||
System.out.print(PROMPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (! parser.eof()) {
|
|
||||||
try {
|
|
||||||
SExpression sexpr = parser.getSExpr();
|
|
||||||
String result = MessageFormat.format("{0}{1}{2}", ANSI_GREEN, EVAL.eval(sexpr), ANSI_RESET);
|
|
||||||
|
|
||||||
LispInterpreter.erasePrompt(interactive);
|
|
||||||
System.out.println(result);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
LispInterpreter.erasePrompt(interactive);
|
|
||||||
ErrorManager.generateError(e.getMessage(), 2);
|
|
||||||
} catch (IOException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(), ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interactive) {
|
|
||||||
System.out.print(PROMPT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void erasePrompt(boolean interactive) {
|
|
||||||
if (interactive) {
|
|
||||||
for (int i = 0; i < PROMPT.length(); i++) {
|
|
||||||
System.out.print("\b");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package main;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import eval.*;
|
|
||||||
import error.ErrorManager;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.*;
|
|
||||||
import remotefs.RemoteFileInputStream;
|
|
||||||
|
|
||||||
public class LispInterpreter2 {
|
|
||||||
|
|
||||||
public static final String HOST_NAME = "localhost";
|
|
||||||
public static final int PORT_NUM = 5150;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
LispParser parser = null;
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
try {
|
|
||||||
InetAddress host = InetAddress.getByName(HOST_NAME);
|
|
||||||
RemoteFileInputStream remoteIn =
|
|
||||||
new RemoteFileInputStream(host, PORT_NUM, args[0]);
|
|
||||||
|
|
||||||
parser = new LispParser(remoteIn, args[0]);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parser = new LispParser(System.in, "System.in");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (! parser.eof()) {
|
|
||||||
try {
|
|
||||||
SExpression sexpr = parser.getSExpr();
|
|
||||||
|
|
||||||
System.out.println(EVAL.eval(sexpr));
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(), 2);
|
|
||||||
} catch (IOException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main;
|
|
||||||
|
|
||||||
import parser.*;
|
|
||||||
import error.ErrorManager;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LispParserDriver</code> is a program that takes the name of a file
|
|
||||||
* as a command-line argument, creates an internal representation of the
|
|
||||||
* S-expressions found in the file and prints them to the console. If no file
|
|
||||||
* name is provided at the command-line, this program will read from standard
|
|
||||||
* input.
|
|
||||||
*/
|
|
||||||
public class LispParserDriver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create internal representations of the S-expressions found in the file
|
|
||||||
* whose name was given as a command-line argument and print them to the
|
|
||||||
* console. If no file name was given, retrieve the S-expressions from
|
|
||||||
* standard input.
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
* the command-line arguments:
|
|
||||||
* <ul>
|
|
||||||
* <li><code>args[0]</code> - file name (optional)</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
LispParser parser = null;
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
// a file name was given at the command-line, attempt to create a
|
|
||||||
// 'LispParser' on it
|
|
||||||
try {
|
|
||||||
parser = new LispParser(new FileInputStream(args[0]), args[0]);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no file name was given, create a 'LispParser' on standard input
|
|
||||||
parser = new LispParser(System.in, "System.in");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (! parser.eof()) {
|
|
||||||
try {
|
|
||||||
SExpression sexpr = parser.getSExpr();
|
|
||||||
|
|
||||||
System.out.println(sexpr.toString());
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(), 2);
|
|
||||||
} catch (IOException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main;
|
|
||||||
|
|
||||||
import scanner.*;
|
|
||||||
import error.ErrorManager;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>LispScannerDriver</code> is a program that takes the name of a file
|
|
||||||
* as a command-line argument, retrieves all of the Lisp tokens from the file
|
|
||||||
* and prints them to the console. If no file name is provided at the
|
|
||||||
* command-line, this program will read from standard input.
|
|
||||||
*/
|
|
||||||
public class LispScannerDriver {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the Lisp tokens from the file whose name was given as a
|
|
||||||
* command-line argument and print them to the console. If no file name was
|
|
||||||
* given, retrieve the tokens from standard input.
|
|
||||||
*
|
|
||||||
* @param args
|
|
||||||
* the command-line arguments:
|
|
||||||
* <ul>
|
|
||||||
* <li><code>args[0]</code> - file name (optional)</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
LispScanner in = null;
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
// a file name was given at the command-line, attempt to create a
|
|
||||||
// 'LispScanner' on it
|
|
||||||
try {
|
|
||||||
in = new LispScanner(new FileInputStream(args[0]), args[0]);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no file name was given, create a 'LispScanner' on standard input
|
|
||||||
in = new LispScanner(System.in, "System.in");
|
|
||||||
}
|
|
||||||
|
|
||||||
Token t = null;
|
|
||||||
|
|
||||||
do {
|
|
||||||
try {
|
|
||||||
t = in.nextToken();
|
|
||||||
|
|
||||||
System.out.printf("%-15s%-25s%5d%5d%25s\n", t.getType(),
|
|
||||||
t.getText(),
|
|
||||||
t.getLine(),
|
|
||||||
t.getColumn(),
|
|
||||||
t.getFName());
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(), 2);
|
|
||||||
} catch (IOException e) {
|
|
||||||
ErrorManager.generateError(e.getMessage(),
|
|
||||||
ErrorManager.CRITICAL_LEVEL);
|
|
||||||
}
|
|
||||||
} while ((t == null) || (t.getType() != Token.Type.EOF));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
<body>
|
|
||||||
Provides test drivers for the various stages of the Lisp Interpreter.
|
|
||||||
</body>
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents an ATOM in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class Atom extends SExpression {
|
|
||||||
|
|
||||||
private String text;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ATOM with the specified text.
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
* the text representing this ATOM
|
|
||||||
*/
|
|
||||||
public Atom(String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is an ATOM.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean atomp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this ATOM.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* a string representation of this ATOM
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
111
parser/Cons.java
111
parser/Cons.java
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents a Lisp CONS cell in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class Cons extends SExpression {
|
|
||||||
|
|
||||||
private SExpression car;
|
|
||||||
private SExpression cdr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new CONS cell with the specified car and cdr.
|
|
||||||
*
|
|
||||||
* @param car
|
|
||||||
* the car of this CONS cell
|
|
||||||
* @param cdr
|
|
||||||
* the cdr of this CONS cell
|
|
||||||
*/
|
|
||||||
public Cons(SExpression car, SExpression cdr) {
|
|
||||||
this.car = car;
|
|
||||||
this.cdr = cdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the car of this CONS cell.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the car of this CONS cell
|
|
||||||
*/
|
|
||||||
public SExpression getCar() {
|
|
||||||
return car;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the cdr of this CONS cell.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the cdr of this CONS cell
|
|
||||||
*/
|
|
||||||
public SExpression getCdr() {
|
|
||||||
return cdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the car of this CONS cell to the specified value.
|
|
||||||
*
|
|
||||||
* @param newCar
|
|
||||||
* the value to assign to the car of this CONS cell
|
|
||||||
*/
|
|
||||||
public void setCar(SExpression newCar) {
|
|
||||||
car = newCar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the cdr of this CONS cell to the specified value.
|
|
||||||
*
|
|
||||||
* @param newCdr
|
|
||||||
* the value to assign to the cdr of this CONS cell
|
|
||||||
*/
|
|
||||||
public void setCdr(SExpression newCdr) {
|
|
||||||
cdr = newCdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a CONS cell.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean consp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this CONS cell.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* a string representation of this CONS cell
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
return ("(" + toStringAux());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a string representation of the car of a CONS cell followed by
|
|
||||||
// its cdr. If the cdr of this CONS cell is not a CONS cell itself, this
|
|
||||||
// method places a ')' at the end of its return value. Also, if the cdr of
|
|
||||||
// this CONS cell is NIL, it will not be included in the return value of
|
|
||||||
// this method. When used in conjunction with the 'toString' method of this
|
|
||||||
// class, this method provides a means for creating the correct string
|
|
||||||
// representation of a list.
|
|
||||||
//
|
|
||||||
// Returns: a string representation of the car of a CONS cell followed by
|
|
||||||
// its cdr
|
|
||||||
private String toStringAux() {
|
|
||||||
if (cdr.nullp()) {
|
|
||||||
return (car.toString() + ")");
|
|
||||||
} else if (cdr.consp()) {
|
|
||||||
return (car.toString() + " " + ((Cons) cdr).toStringAux());
|
|
||||||
}
|
|
||||||
|
|
||||||
// the cdr of this CONS cell is not a list
|
|
||||||
return (car.toString() + " . " + cdr.toString() + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents a NUMBER in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class LispNumber extends Atom {
|
|
||||||
|
|
||||||
private int value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new NUMBER with the specified text.
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
* the text representing this NUMBER
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* Indicates that <code>text</code> does not represent a valid integer.
|
|
||||||
*/
|
|
||||||
public LispNumber(String text) {
|
|
||||||
super(text.replaceFirst("^0+(?!$)", ""));
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.value = Integer.parseInt(text);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
throw new IllegalArgumentException(text +
|
|
||||||
" is not a valid integer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new NUMBER with the specified value.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* the integer value of this NUMBER
|
|
||||||
*/
|
|
||||||
public LispNumber(int value) {
|
|
||||||
super(Integer.toString(value));
|
|
||||||
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a NUMBER.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean numberp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the integer value of this NUMBER.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the integer value of this NUMBER
|
|
||||||
*/
|
|
||||||
public int getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,192 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
import scanner.*;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>LispParser</code> converts a stream of bytes into internal
|
|
||||||
* representations of Lisp S-expressions. When the end of stream has been
|
|
||||||
* reached the <code>eof</code> method of this parser will return true.
|
|
||||||
*/
|
|
||||||
public class LispParser {
|
|
||||||
|
|
||||||
private LispScanner scanner;
|
|
||||||
private Token nextToken;
|
|
||||||
|
|
||||||
// A field to store an exception that has been thrown in the 'eof' method
|
|
||||||
// as a result of reading in the next token from 'scanner'.
|
|
||||||
private Exception delayedException;
|
|
||||||
|
|
||||||
// A field to notify us if the next token has already been stored in
|
|
||||||
// 'nextToken' by the 'eof' method.
|
|
||||||
private boolean nextTokenStored;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new <code>LispParser</code> that produces S-expressions from
|
|
||||||
* the specified input stream.
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* the input stream to obtain S-expressions from (must not be
|
|
||||||
* <code>null</code>)
|
|
||||||
* @param fileName
|
|
||||||
* the name of the file that <code>in</code> is reading from
|
|
||||||
*/
|
|
||||||
public LispParser(InputStream in, String fileName) {
|
|
||||||
scanner = new LispScanner(in, fileName);
|
|
||||||
nextToken = null;
|
|
||||||
delayedException = null;
|
|
||||||
nextTokenStored = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determing if this parser has reached the end of its underlying input
|
|
||||||
* stream.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code> if this parser has reached the end of its underlying
|
|
||||||
* input stream; <code>false</code> otherwise
|
|
||||||
*/
|
|
||||||
public boolean eof() {
|
|
||||||
if (! nextTokenStored) {
|
|
||||||
// attempt to read the next token from 'scanner' and store it in
|
|
||||||
// 'nextToken'
|
|
||||||
try {
|
|
||||||
nextToken = scanner.nextToken();
|
|
||||||
nextTokenStored = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// this method should give the illusion of not actually reading
|
|
||||||
// a token, so we store any exceptions thrown as a result of
|
|
||||||
// reading in the next token from 'scanner' (it will be thrown
|
|
||||||
// the next time the 'getSExpr' method is called)
|
|
||||||
delayedException = e;
|
|
||||||
|
|
||||||
if (nextToken == null) {
|
|
||||||
// we have not successfully read in any tokens yet, so we
|
|
||||||
// could not have read in an end-of-file token
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (nextToken.getType() == Token.Type.EOF);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next S-expression from this parser.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the next S-expression from this parser
|
|
||||||
* @throws RuntimeException
|
|
||||||
* Indicates that an illegal S-expression was encountered in the
|
|
||||||
* underlying input stream.
|
|
||||||
* @throws IOException
|
|
||||||
* Indicates that an I/O error has occurred.
|
|
||||||
*/
|
|
||||||
public SExpression getSExpr() throws IOException {
|
|
||||||
if (delayedException != null) {
|
|
||||||
// the 'eof' method has stored an exception for us to throw
|
|
||||||
|
|
||||||
// determine the type of the stored exception and throw it!
|
|
||||||
if (delayedException instanceof IOException) {
|
|
||||||
IOException e = (IOException) delayedException;
|
|
||||||
|
|
||||||
// remove the exception from 'delayedException'
|
|
||||||
delayedException = null;
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
} else if (delayedException instanceof RuntimeException) {
|
|
||||||
RuntimeException e = (RuntimeException) delayedException;
|
|
||||||
|
|
||||||
// remove the exception from 'delayedException'
|
|
||||||
delayedException = null;
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! nextTokenStored) {
|
|
||||||
// the next token has not been stored in 'nextToken' by the 'eof'
|
|
||||||
// method
|
|
||||||
|
|
||||||
nextToken = scanner.nextToken();
|
|
||||||
} else {
|
|
||||||
// the 'eof' method has been called and has read in the next token
|
|
||||||
// already to determine if we have reached the end-of-file
|
|
||||||
|
|
||||||
nextTokenStored = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sExpr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// sExpr ::= NUMBER | IDENTIFIER | RESERVED | STRING | QUOTE_MARK sExpr |
|
|
||||||
// LEFT_PAREN sExprTail
|
|
||||||
//
|
|
||||||
// Returns: an S-expression that matches the rules given above
|
|
||||||
// Throws: RuntimeException - Indicates that an illegal S-expression was
|
|
||||||
// encountered in the underlying input stream.
|
|
||||||
// Throws: IOException - Indicates that an I/O error has occurred.
|
|
||||||
// Precondition: 'nextToken' is not null.
|
|
||||||
private SExpression sExpr() throws IOException {
|
|
||||||
// determine the type of 'nextToken' and create the appropriate
|
|
||||||
// S-expression
|
|
||||||
switch (nextToken.getType()) {
|
|
||||||
case NUMBER:
|
|
||||||
return new LispNumber(nextToken.getText());
|
|
||||||
case IDENTIFIER:
|
|
||||||
case RESERVED:
|
|
||||||
return new Symbol(nextToken.getText());
|
|
||||||
case STRING:
|
|
||||||
return new LispString(nextToken.getText());
|
|
||||||
case QUOTE_MARK:
|
|
||||||
nextToken = scanner.nextToken();
|
|
||||||
SExpression arg = sExpr();
|
|
||||||
|
|
||||||
return new Cons(new Symbol("QUOTE"),
|
|
||||||
new Cons(arg, Nil.getUniqueInstance()));
|
|
||||||
case LEFT_PAREN:
|
|
||||||
return sExprTail();
|
|
||||||
case RIGHT_PAREN:
|
|
||||||
throw new RuntimeException("expression begins with \')\'" +
|
|
||||||
" - line " + nextToken.getLine() +
|
|
||||||
" column " + nextToken.getColumn());
|
|
||||||
case EOF:
|
|
||||||
throw new RuntimeException("end-of-file encountered" +
|
|
||||||
" - line " + nextToken.getLine() +
|
|
||||||
" column " + nextToken.getColumn());
|
|
||||||
default: // unrecognized token type
|
|
||||||
throw new RuntimeException("unrecognizable token" +
|
|
||||||
" - line " + nextToken.getLine() +
|
|
||||||
" column " + nextToken.getColumn());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sExprTail ::= RIGHT_PAREN | sExpr sExprTail
|
|
||||||
//
|
|
||||||
// Returns: an S-expression that matches the rules given above
|
|
||||||
// Throws: IOException - Indicates that an I/O error has occurred.
|
|
||||||
// Precondition: 'scanner' is not null.
|
|
||||||
private SExpression sExprTail() throws IOException {
|
|
||||||
nextToken = scanner.nextToken();
|
|
||||||
|
|
||||||
// determine the type of 'nextToken' and create the appropriate
|
|
||||||
// S-expression
|
|
||||||
switch (nextToken.getType()) {
|
|
||||||
case RIGHT_PAREN:
|
|
||||||
return Nil.getUniqueInstance();
|
|
||||||
default:
|
|
||||||
SExpression car = sExpr();
|
|
||||||
SExpression cdr = sExprTail();
|
|
||||||
|
|
||||||
return new Cons(car, cdr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents a STRING in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class LispString extends Atom {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new STRING with the specified text.
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
* the text representing this STRING
|
|
||||||
*/
|
|
||||||
public LispString(String text) {
|
|
||||||
super(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a STRING.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean stringp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
104
parser/Nil.java
104
parser/Nil.java
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents NIL in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class Nil extends Cons {
|
|
||||||
|
|
||||||
private static Nil uniqueInstance = new Nil();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the single unique instance of NIL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the single unique instance of NIL
|
|
||||||
*/
|
|
||||||
public static Nil getUniqueInstance() {
|
|
||||||
return uniqueInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are using the Singleton pattern, so the constructor for 'Nil' is
|
|
||||||
// private.
|
|
||||||
private Nil() {
|
|
||||||
super(null, null);
|
|
||||||
|
|
||||||
// the car and cdr of NIL both refer to NIL
|
|
||||||
super.setCar(this);
|
|
||||||
super.setCdr(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is NULL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean nullp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is an ATOM.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean atomp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a CONS cell.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean consp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a SYMBOL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean symbolp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the car of this CONS cell to the specified value. This method does
|
|
||||||
* nothing (the car of NIL can not be changed).
|
|
||||||
*
|
|
||||||
* @param newCar
|
|
||||||
* the value to assign to the car of this CONS cell
|
|
||||||
*/
|
|
||||||
public void setCar(SExpression newCar) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the cdr of this CONS cell to the specified value. This method does
|
|
||||||
* nothing (the cdr of NIL can not be changed).
|
|
||||||
*
|
|
||||||
* @param newCdr
|
|
||||||
* the value to assign to the cdr of this CONS cell
|
|
||||||
*/
|
|
||||||
public void setCdr(SExpression newCdr) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of NIL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* a string representation of NIL
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
return "NIL";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the base class for memory in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class SExpression {
|
|
||||||
|
|
||||||
// for mark and sweep garbage collection
|
|
||||||
private boolean marked = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if this <code>SExpression</code> is marked.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code> if this <code>SExpression</code> is marked;
|
|
||||||
* <code>false</code> otherwise
|
|
||||||
*/
|
|
||||||
public final boolean isMarked() {
|
|
||||||
return marked;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the marked status of this S-expression to the specified value.
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* the value to assign to this S-expression's marked status
|
|
||||||
*/
|
|
||||||
public final void setMarked(final boolean value) {
|
|
||||||
marked = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lisp type predicates;
|
|
||||||
// by default, all return false (an SExpression effectively has NO type);
|
|
||||||
// overridden in subclasses to describe their Lisp type
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is NULL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean nullp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is an ATOM.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean atomp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a CONS cell.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean consp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a LIST.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the value of <code>(consp() || nullp())</code>
|
|
||||||
*/
|
|
||||||
public boolean listp() {
|
|
||||||
return (consp() || nullp());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a NUMBER.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean numberp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a SYMBOL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean symbolp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a FUNCTION.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean functionp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a STRING.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>false</code>
|
|
||||||
*/
|
|
||||||
public boolean stringp() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Parser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents a SYMBOL in the PL-Lisp implementation.
|
|
||||||
*/
|
|
||||||
public class Symbol extends Atom {
|
|
||||||
|
|
||||||
/** This SYMBOL represents TRUE in the PL-Lisp implementation. */
|
|
||||||
public static final Symbol T = new Symbol("T");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new SYMBOL with the specified text.
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
* the text representing this SYMBOL
|
|
||||||
*/
|
|
||||||
public Symbol(String text) {
|
|
||||||
super(text.toUpperCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if this S-expression is a SYMBOL.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* <code>true</code>
|
|
||||||
*/
|
|
||||||
public boolean symbolp() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
<body>
|
|
||||||
Provides the classes necessary for creating an internal representation of
|
|
||||||
the Lisp programming language.
|
|
||||||
</body>
|
|
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
|
|
||||||
*/
|
|
||||||
|
|
||||||
package scanner;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>LispFilterStream</code> is an input stream that returns the bytes
|
|
||||||
* from an underlying input stream with all Lisp comments removed and replaced
|
|
||||||
* with newlines.
|
|
||||||
*/
|
|
||||||
public class LispFilterStream extends FilterInputStream {
|
|
||||||
|
|
||||||
private boolean inQuote;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a <code>LispFilterStream</code> with the specified underlying
|
|
||||||
* <code>InputStream</code>.
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* the underlying input stream (must not be <code>null</code>)
|
|
||||||
*/
|
|
||||||
public LispFilterStream(InputStream in) {
|
|
||||||
super(in);
|
|
||||||
|
|
||||||
inQuote = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the next byte of data from this input stream. The value byte is
|
|
||||||
* returned as an <code>int</code> in the range <code>0</code> to
|
|
||||||
* <code>255</code>. If no byte is available because the end of the stream
|
|
||||||
* has been reached, the value <code>-1</code> is returned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the next byte of data, or <code>-1</code> if the end of the stream has
|
|
||||||
* been reached.
|
|
||||||
* @throws IOException
|
|
||||||
* Indicates that an I/O error has occurred.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
int next = super.read();
|
|
||||||
|
|
||||||
if ((next == ';') && (! inQuote)) {
|
|
||||||
// we have entered a comment, consume all the bytes from the
|
|
||||||
// underlying input stream until we reach a newline character or
|
|
||||||
// the end of the stream
|
|
||||||
while ((next != '\n') && (next != -1)) {
|
|
||||||
next = super.read();
|
|
||||||
}
|
|
||||||
} else if (next == '\n') {
|
|
||||||
inQuote = false;
|
|
||||||
} else if (next == '\"') { // we have entered or left a quoted string
|
|
||||||
inQuote = (! inQuote);
|
|
||||||
}
|
|
||||||
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads up to the specified number of data bytes from this input stream
|
|
||||||
* into an array of bytes starting at the specified offset. If no bytes are
|
|
||||||
* available because the end of this stream has been reached then
|
|
||||||
* <code>-1</code> is returned. Also, If the specified number of bytes is
|
|
||||||
* more than the number of remaining data bytes in this input stream, then
|
|
||||||
* only the number of remaining data bytes are copied into the byte array.
|
|
||||||
*
|
|
||||||
* @param b
|
|
||||||
* the buffer into which the data bytes are read (must not be
|
|
||||||
* <code>null</code>)
|
|
||||||
* @param off
|
|
||||||
* the start offset in <code>b</code> at which the data is written (must
|
|
||||||
* be <code>>= 0</code> and <code>< b.length</code>)
|
|
||||||
* @param len
|
|
||||||
* the maximum number of bytes to read into <code>b</code> (<code>len +
|
|
||||||
* off</code> must be <code>< 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,320 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
|
|
||||||
*/
|
|
||||||
|
|
||||||
package scanner;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>LispScanner</code> converts a stream of bytes into a stream of Lisp
|
|
||||||
* tokens. When the end of stream has been reached a token with a type of
|
|
||||||
* <code>Token.Type.EOF</code> is returned from the <code>nextToken</code>
|
|
||||||
* method of this scanner.
|
|
||||||
*/
|
|
||||||
public class LispScanner {
|
|
||||||
|
|
||||||
private LispFilterStream inStream;
|
|
||||||
private Token currToken;
|
|
||||||
private String fileName;
|
|
||||||
private int line;
|
|
||||||
private int column;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new <code>LispScanner</code> that produces Lisp tokens from the
|
|
||||||
* specified input stream.
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* the input stream to obtain Lisp tokens from (must not be
|
|
||||||
* <code>null</code>)
|
|
||||||
* @param fileName
|
|
||||||
* the name of the file that <code>in</code> is reading from
|
|
||||||
*/
|
|
||||||
public LispScanner(InputStream in, String fileName) {
|
|
||||||
this.inStream = new LispFilterStream(new BufferedInputStream(in));
|
|
||||||
this.currToken = null;
|
|
||||||
this.fileName = fileName;
|
|
||||||
this.line = 1;
|
|
||||||
this.column = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the same Lisp token returned from the last call to the
|
|
||||||
* <code>nextToken</code> method of this scanner. In the case that no calls
|
|
||||||
* to <code>nextToken</code> have been made yet, this method returns
|
|
||||||
* <code>null</code>.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the last Lisp token returned from this scanner or <code>null</code> (if
|
|
||||||
* no tokens have been returned from this scanner yet)
|
|
||||||
*/
|
|
||||||
public Token getCurrToken() {
|
|
||||||
return currToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next Lisp token from this scanner.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the next Lisp token from this scanner.
|
|
||||||
* @throws RuntimeException
|
|
||||||
* Indicates that an illegal character or an unterminated quoted string
|
|
||||||
* was encountered in the input stream (not counting comments).
|
|
||||||
* @throws IOException
|
|
||||||
* Indicates that an I/O error has occurred.
|
|
||||||
*/
|
|
||||||
public Token nextToken() throws IOException {
|
|
||||||
currToken = retrieveNextToken();
|
|
||||||
|
|
||||||
return currToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the next Lisp token from 'inStream'.
|
|
||||||
//
|
|
||||||
// Returns: the next Lisp token found in 'inStream'
|
|
||||||
// Precondition: 'inStream' must not be null.
|
|
||||||
// Throws: RuntimeException - Indicates that an illegal character or an
|
|
||||||
// unterminated quoted string was encountered in
|
|
||||||
// 'inStream'.
|
|
||||||
// Throws: IOException - Indicates that an I/O error has occurred.
|
|
||||||
private Token retrieveNextToken() throws IOException {
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = inStream.read()) != -1) {
|
|
||||||
char nextChar = (char) c;
|
|
||||||
|
|
||||||
++column;
|
|
||||||
|
|
||||||
// determine the type of the Lisp token from the character obtained
|
|
||||||
// from 'inStream'
|
|
||||||
switch (nextChar) {
|
|
||||||
case '\n':
|
|
||||||
// we have hit a new line so increment 'line' and reset
|
|
||||||
// 'column'
|
|
||||||
++line;
|
|
||||||
column = 0;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case '(':
|
|
||||||
return new Token(Token.Type.LEFT_PAREN,
|
|
||||||
"(",
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
column);
|
|
||||||
case ')':
|
|
||||||
return new Token(Token.Type.RIGHT_PAREN,
|
|
||||||
")",
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
column);
|
|
||||||
case '\'':
|
|
||||||
return new Token(Token.Type.QUOTE_MARK,
|
|
||||||
"\'",
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
column);
|
|
||||||
case '\"':
|
|
||||||
return retrieveString(nextChar);
|
|
||||||
default:
|
|
||||||
if (Character.isWhitespace(nextChar)) { // skip whitespace
|
|
||||||
continue;
|
|
||||||
} else if (Character.isDigit(nextChar)) { // number
|
|
||||||
return retrieveNumber(nextChar);
|
|
||||||
} else if (isLegalIdChar(nextChar)) { // identifier
|
|
||||||
return retrieveIdentifier(nextChar);
|
|
||||||
} else {
|
|
||||||
// 'nextChar' can not start any Lisp token
|
|
||||||
|
|
||||||
throw new RuntimeException("illegal character " +
|
|
||||||
"\'" + nextChar + "\'" +
|
|
||||||
" - line " + line +
|
|
||||||
" column " + column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have reached the end of 'inStream' so we return an end-of-file
|
|
||||||
// token
|
|
||||||
return new Token(Token.Type.EOF, "EOF", fileName, line, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve a quoted string token from 'inStream'.
|
|
||||||
//
|
|
||||||
// Parameters: firstDoubleQuote - the opening double quote of this quoted
|
|
||||||
// string
|
|
||||||
// Returns: a quoted string token obtained from 'instream'
|
|
||||||
// Throws: RuntimeException - Indicates that this quoted string was
|
|
||||||
// missing its terminating double quote.
|
|
||||||
// Throws: IOException - Indicates that an I/O error has occurred.
|
|
||||||
// Precondition: 'firstDoubleQuote' must be the leading double quote
|
|
||||||
// character of this quoted string and 'inStream' must not
|
|
||||||
// be null.
|
|
||||||
private Token retrieveString(char firstDoubleQuote) throws IOException {
|
|
||||||
StringBuffer text = new StringBuffer();
|
|
||||||
int startLine = line;
|
|
||||||
int startColumn = column;
|
|
||||||
char prevChar = firstDoubleQuote;
|
|
||||||
|
|
||||||
text.append(firstDoubleQuote);
|
|
||||||
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = inStream.read()) != -1) {
|
|
||||||
char nextChar = (char) c;
|
|
||||||
|
|
||||||
++column;
|
|
||||||
text.append(nextChar);
|
|
||||||
|
|
||||||
switch(nextChar) {
|
|
||||||
case '\n':
|
|
||||||
++line;
|
|
||||||
column = 0;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case '\"':
|
|
||||||
if (prevChar != '\\') {
|
|
||||||
// we have found the terminating double quote
|
|
||||||
|
|
||||||
return new Token(Token.Type.STRING,
|
|
||||||
text.toString(),
|
|
||||||
fileName,
|
|
||||||
startLine,
|
|
||||||
startColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is an escaped double quote
|
|
||||||
}
|
|
||||||
|
|
||||||
prevChar = nextChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the end of 'inStream' was reached before the terminating double
|
|
||||||
// quote
|
|
||||||
|
|
||||||
throw new RuntimeException("unterminated quoted string" +
|
|
||||||
" - line " + startLine +
|
|
||||||
" column " + startColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve a number token from 'inStream'.
|
|
||||||
//
|
|
||||||
// Parameters: firstDigit - the first digit of this number
|
|
||||||
// Returns: a number token obtained from 'inStream'
|
|
||||||
// Throws: IOException - Indicates that an I/O error has occurred.
|
|
||||||
// Precondition: 'firstDigit' must be the first digit of this number and
|
|
||||||
// 'inStream' must not be null.
|
|
||||||
private Token retrieveNumber(char firstDigit) throws IOException {
|
|
||||||
StringBuffer text = new StringBuffer();
|
|
||||||
int startColumn = column;
|
|
||||||
|
|
||||||
text.append(firstDigit);
|
|
||||||
inStream.mark(1);
|
|
||||||
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = inStream.read()) != -1) {
|
|
||||||
char nextChar = (char) c;
|
|
||||||
|
|
||||||
if (Character.isDigit(nextChar)) {
|
|
||||||
// 'nextChar' is a digit in this number
|
|
||||||
|
|
||||||
text.append(nextChar);
|
|
||||||
++column;
|
|
||||||
} else {
|
|
||||||
// we have reached the end of the number
|
|
||||||
|
|
||||||
inStream.reset(); // unread the last character
|
|
||||||
|
|
||||||
return new Token(Token.Type.NUMBER,
|
|
||||||
text.toString(),
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
startColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
inStream.mark(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are no more bytes to be read from 'inStream' after this number
|
|
||||||
// token
|
|
||||||
|
|
||||||
return new Token(Token.Type.NUMBER,
|
|
||||||
text.toString(),
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
startColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve an identifier token from 'inStream'.
|
|
||||||
//
|
|
||||||
// Parameters: firstChar - the first character of this identifier
|
|
||||||
// Returns: an identifier token obtained from 'inStream'
|
|
||||||
// Throws: IOException - Indicates that an I/O error has occurred.
|
|
||||||
// Precondition: 'firsChar' must be the first character of this identifier
|
|
||||||
// and 'inStream' must not be null.
|
|
||||||
private Token retrieveIdentifier(char firstChar) throws IOException {
|
|
||||||
StringBuffer text = new StringBuffer();
|
|
||||||
int startColumn = column;
|
|
||||||
|
|
||||||
text.append(firstChar);
|
|
||||||
inStream.mark(1);
|
|
||||||
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = inStream.read()) != -1) {
|
|
||||||
char nextChar = (char) c;
|
|
||||||
|
|
||||||
if (isLegalIdChar(nextChar)) {
|
|
||||||
// 'nextChar' is part of the identifier
|
|
||||||
|
|
||||||
text.append(nextChar);
|
|
||||||
++column;
|
|
||||||
} else {
|
|
||||||
// we have reached the end of this identifier
|
|
||||||
|
|
||||||
inStream.reset(); // unread the last character
|
|
||||||
|
|
||||||
return new Token(Token.Type.IDENTIFIER,
|
|
||||||
text.toString(),
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
startColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
inStream.mark(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are no more bytes to be read from 'inStream' after this
|
|
||||||
// identifier token
|
|
||||||
|
|
||||||
return new Token(Token.Type.IDENTIFIER,
|
|
||||||
text.toString(),
|
|
||||||
fileName,
|
|
||||||
line,
|
|
||||||
startColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test if a character is legal to be contained within an identifier in
|
|
||||||
// Lisp.
|
|
||||||
//
|
|
||||||
// Returns: 'true' if the character can be found within an identifier in
|
|
||||||
// Lisp; 'false' otherwise
|
|
||||||
private boolean isLegalIdChar(char c) {
|
|
||||||
return ((! Character.isWhitespace(c)) && (c != '\"')
|
|
||||||
&& (c != '\'')
|
|
||||||
&& (c != '\\')
|
|
||||||
&& (c != '`')
|
|
||||||
&& (c != '(')
|
|
||||||
&& (c != ')')
|
|
||||||
&& (c != '[')
|
|
||||||
&& (c != ']')
|
|
||||||
&& (c != '#')
|
|
||||||
&& (c != '.')
|
|
||||||
&& (c != ';'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
|
|
||||||
*/
|
|
||||||
|
|
||||||
package scanner;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A <code>Token</code> represents a token in Common Lisp.
|
|
||||||
*/
|
|
||||||
public class Token {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An enumeration representing all of the types of tokens found in Common
|
|
||||||
* Lisp.
|
|
||||||
*/
|
|
||||||
public enum Type {
|
|
||||||
/** A left parenthesis token */
|
|
||||||
LEFT_PAREN,
|
|
||||||
|
|
||||||
/** A right parenthesis token */
|
|
||||||
RIGHT_PAREN,
|
|
||||||
|
|
||||||
/** A quoted string token */
|
|
||||||
STRING,
|
|
||||||
|
|
||||||
/** A quote mark */
|
|
||||||
QUOTE_MARK,
|
|
||||||
|
|
||||||
/** A number token */
|
|
||||||
NUMBER,
|
|
||||||
|
|
||||||
/** A reserved word token */
|
|
||||||
RESERVED,
|
|
||||||
|
|
||||||
/** An identifier token */
|
|
||||||
IDENTIFIER,
|
|
||||||
|
|
||||||
/** An end-of-file token */
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type type;
|
|
||||||
private String text;
|
|
||||||
private String fName;
|
|
||||||
private int line;
|
|
||||||
private int column;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new token with the specified type, text, file name, line number
|
|
||||||
* and column number.
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* the type of this token
|
|
||||||
* @param text
|
|
||||||
* the text associated with this token
|
|
||||||
* @param fName
|
|
||||||
* the name of the file that this token is located in
|
|
||||||
* @param line
|
|
||||||
* the line number that this token is found on
|
|
||||||
* @param column
|
|
||||||
* the column number that this token is found on
|
|
||||||
*/
|
|
||||||
public Token(Type type, String text, String fName, int line, int column) {
|
|
||||||
this.type = type;
|
|
||||||
this.text = text;
|
|
||||||
this.fName = fName;
|
|
||||||
this.line = line;
|
|
||||||
this.column = column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessor method to determine the type of this token.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the type of this token
|
|
||||||
*/
|
|
||||||
public Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessor method to determine the text associated with this token.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the text associated with this token
|
|
||||||
*/
|
|
||||||
public String getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessor method to determine the name of the file that this token was
|
|
||||||
* located in.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the name of the file that this token was located in
|
|
||||||
*/
|
|
||||||
public String getFName() {
|
|
||||||
return fName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessor method to determine the line number that this token was found
|
|
||||||
* on.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the line number this token was found on
|
|
||||||
*/
|
|
||||||
public int getLine() {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessor method to determine the column number that this token was found
|
|
||||||
* on.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the column number this token was found on
|
|
||||||
*/
|
|
||||||
public int getColumn() {
|
|
||||||
return column;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
<body>
|
|
||||||
Provides the classes necessary to perform a lexical analysis of the Lisp
|
|
||||||
programming language.
|
|
||||||
</body>
|
|
Binary file not shown.
|
@ -1,9 +1,3 @@
|
||||||
/*
|
|
||||||
* Name: Mike Cifelli
|
|
||||||
* Course: CIS 443 - Programming Languages
|
|
||||||
* Assignment: Lisp Interpreter Phase 1 - Lexical Analysis
|
|
||||||
*/
|
|
||||||
|
|
||||||
package scanner;
|
package scanner;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -11,150 +5,48 @@ import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A <code>LispFilterStream</code> is an input stream that returns the bytes
|
* Replaces Lisp comments with newlines in an input stream.
|
||||||
* from an underlying input stream with all Lisp comments removed and replaced
|
|
||||||
* with newlines.
|
|
||||||
*/
|
*/
|
||||||
public class LispFilterStream extends FilterInputStream {
|
public class LispFilterStream extends FilterInputStream {
|
||||||
|
|
||||||
private boolean inQuote;
|
private boolean inQuote;
|
||||||
|
private int nextCharacter;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a <code>LispFilterStream</code> with the specified underlying
|
|
||||||
* <code>InputStream</code>.
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* the underlying input stream (must not be <code>null</code>)
|
|
||||||
*/
|
|
||||||
public LispFilterStream(InputStream in) {
|
public LispFilterStream(InputStream in) {
|
||||||
super(in);
|
super(in);
|
||||||
|
|
||||||
inQuote = false;
|
inQuote = false;
|
||||||
|
nextCharacter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the next byte of data from this input stream. The value byte is
|
|
||||||
* returned as an <code>int</code> in the range <code>0</code> to
|
|
||||||
* <code>255</code>. If no byte is available because the end of the stream
|
|
||||||
* has been reached, the value <code>-1</code> is returned.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* the next byte of data, or <code>-1</code> if the end of the stream has
|
|
||||||
* been reached.
|
|
||||||
* @throws IOException
|
|
||||||
* Indicates that an I/O error has occurred.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
int next = super.read();
|
nextCharacter = super.read();
|
||||||
|
|
||||||
if ((next == ';') && (! inQuote)) {
|
if (haveEnteredComment()) {
|
||||||
// we have entered a comment, consume all the bytes from the
|
consumeAllBytesInComment();
|
||||||
// underlying input stream until we reach a newline character or
|
} else if (haveEncounteredStringBoundary()) {
|
||||||
// the end of the stream
|
inQuote = (!inQuote);
|
||||||
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;
|
return nextCharacter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void consumeAllBytesInComment() throws IOException {
|
||||||
* Reads up to the specified number of data bytes from this input stream
|
while (stillInComment()) {
|
||||||
* into an array of bytes starting at the specified offset. If no bytes are
|
nextCharacter = super.read();
|
||||||
* 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>>= 0</code> and <code>< b.length</code>)
|
|
||||||
* @param len
|
|
||||||
* the maximum number of bytes to read into <code>b</code> (<code>len +
|
|
||||||
* off</code> must be <code>< b.length</code>)
|
|
||||||
* @return
|
|
||||||
* the total number of bytes read into the buffer, or <code>-1</code> if
|
|
||||||
* there is no more data because the end of the stream has been reached
|
|
||||||
* @throws IOException
|
|
||||||
* Indicates that the first byte could not be read into <code>b</code>
|
|
||||||
* for some reason other than reaching the end of the stream.
|
|
||||||
* @throws IndexOutOfBoundsException
|
|
||||||
* Indicates that this method attempted to access an index in
|
|
||||||
* <code>b</code> that was either negative or greater than or equal to
|
|
||||||
* <code>b.length</code> as a result of the given parameters.
|
|
||||||
* @throws NullPointerException
|
|
||||||
* Indicates that <code>b</code> is <code>null</code>.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
int bytesRead = 0;
|
|
||||||
|
|
||||||
// make sure we are supposed to read at least one byte into 'b'
|
|
||||||
if (len > 0) {
|
|
||||||
int next = read();
|
|
||||||
|
|
||||||
if (next == -1) {
|
|
||||||
// there are no more bytes to read from this input stream
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = off;
|
|
||||||
|
|
||||||
while (next != -1) {
|
|
||||||
++bytesRead;
|
|
||||||
|
|
||||||
b[i++] = (byte) next;
|
|
||||||
|
|
||||||
if (i >= (off + len)) { // we have read 'len' bytes into 'b'
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
next = read();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// treat this exception like an end of stream
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean stillInComment() {
|
||||||
|
return (nextCharacter != '\n') && (nextCharacter != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesRead;
|
private boolean haveEnteredComment() {
|
||||||
|
return (nextCharacter == ';') && (!inQuote);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private boolean haveEncounteredStringBoundary() {
|
||||||
* Skip over and discard the specified number of bytes from this input
|
return nextCharacter == '\"';
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,4 @@ public class LispFilterStreamTester {
|
||||||
fail("Not yet implemented");
|
fail("Not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadByteArrayIntInt() {
|
|
||||||
fail("Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSkip() {
|
|
||||||
fail("Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLispFilterStream() {
|
|
||||||
fail("Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue