// File: BuiltInFunction.java
// Date: 7/4/2008
package org.bot.jscheme;
/**
*
* Original Comments:
* A primitive is a procedure that is defined as part of the Scheme report,
* and is implemented in Java code.
*
* @author Berlin Brown (refactoring modifications)
* @author Peter Norvig, peter@norvig.com http://www.norvig.com
* Copyright 1998 Peter Norvig, see http://www.norvig.com/license.html
*/
public class BuiltInFunction
extends Procedure implements FunctionConstants {
/**
* Minimum number of args for the function/procedur.
*/
private int minArgs;
private int maxArgs;
private int idNumber;
public static Double ZERO = new Double(0.0);
public static Double ONE = new Double(1.0);
private BuiltInFunction() {
}
public BuiltInFunction(int id, int minArgs, int maxArgs) {
this.idNumber = id;
this.minArgs = minArgs;
this.maxArgs = maxArgs;
}
public static Double num(double x) {
return (x == 0.0) ? ZERO : (x == 1.0) ? ONE : new Double(x);
}
public static double num(Object x) {
if (x instanceof Number) {
return ((Number)x).doubleValue();
} else {
return num(SchemeUtil.error("expected a number, got: " + x));
} // End of the if - else
}
public static Object numCompute(Object args, char op, double result) {
if (args == null) {
switch (op) {
case '-':
return num(0 - result);
case '/':
return num(1 / result);
default:
return num(result);
}
} else {
// Perform the operation against the linked list
while (args instanceof Pair) {
double x = num(SchemeUtil.first(args));
// Traverse
args = SchemeUtil.rest(args);
switch (op) {
case 'X':
if (x > result)
result = x;
break;
case 'N':
if (x < result)
result = x;
break;
case '+':
result += x;
break;
case '-':
result -= x;
break;
case '*':
result *= x;
break;
case '/':
result /= x;
break;
default:
SchemeUtil.error("Internal Error: unrecognized op: " + op);
break;
}
} // end of while
return num(result);
}
}
/**
* Apply a primitive to a list of arguments.
*/
public Object apply(Scheme interp, Object args) {
// First make sure there are the right number of arguments.
int nArgs = SchemeUtil.length(args);
if (nArgs < minArgs) {
return SchemeUtil.error("too few args, " + nArgs + ", for " + this.getName() + ": "
+ args);
} else if (nArgs > maxArgs) {
return SchemeUtil.error("too many args, " + nArgs + ", for " + this.getName()
+ ": " + args);
} // End of the If
Object x = SchemeUtil.first(args);
Object y = SchemeUtil.second(args);
switch (idNumber) {
case PLUS:
return numCompute(args, '+', 0.0);
case MINUS:
return numCompute(SchemeUtil.rest(args), '-', num(x));
case TIMES:
return numCompute(args, '*', 1.0);
case DIVIDE:
return numCompute(SchemeUtil.rest(args), '/', num(x));
case THIRD:
return SchemeUtil.third(x);
case CONS:
return SchemeUtil.cons(x, y);
case CAR:
return SchemeUtil.first(x);
case CDR:
return SchemeUtil.rest(x);
case CXR:
for (int i = this.getName().length() - 2; i >= 1; i--) {
x = (this.getName().charAt(i) == 'a') ? SchemeUtil.first(x) : SchemeUtil.rest(x);
}
return x;
default:
return SchemeUtil.error("internal error: unknown primitive: " + this
+ " applied to " + args);
}
} // End of the Apply
/**
* Define the procedures for the lisp environment.
*/
public static Environment installBuiltInFunctions(Environment env) {
int n = Integer.MAX_VALUE;
env
.defineBuiltInProc("cons", CONS, 2)
.defineBuiltInProc("*", TIMES, 0, n)
.defineBuiltInProc("+", PLUS, 0, n)
.defineBuiltInProc("-", MINUS, 1, n)
.defineBuiltInProc("/", DIVIDE, 1, n)
.defineBuiltInProc("caaaar", CXR, 1)
.defineBuiltInProc("caaadr", CXR, 1)
.defineBuiltInProc("caaar", CXR, 1)
.defineBuiltInProc("caadar", CXR, 1)
.defineBuiltInProc("caaddr", CXR, 1)
.defineBuiltInProc("caadr", CXR, 1)
.defineBuiltInProc("caar", CXR, 1)
.defineBuiltInProc("caddr", THIRD, 1)
.defineBuiltInProc("cadr", SECOND, 1)
.defineBuiltInProc("cdaaar", CXR, 1)
.defineBuiltInProc("cdadr", CXR, 1)
.defineBuiltInProc("cdar", CXR, 1)
.defineBuiltInProc("cddaar", CXR, 1)
.defineBuiltInProc("cddr", CXR, 1)
.defineBuiltInProc("car", CAR, 1)
.defineBuiltInProc("cdr", CDR, 1)
.defineBuiltInProc("*", TIMES, 0, n)
.defineBuiltInProc("+", PLUS, 0, n)
.defineBuiltInProc("-", MINUS, 1, n)
.defineBuiltInProc("/", DIVIDE, 1, n);
return env;
}
}