252 lines
7.9 KiB
Java
252 lines
7.9 KiB
Java
|
/*
|
||
|
* 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());
|
||
|
}
|
||
|
|
||
|
}
|