transcendental-lisp/eval/EVAL.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());
}
}