Built-in function names are now defined through annotations

This commit is contained in:
Mike Cifelli 2017-02-26 16:47:06 -05:00
parent 8fdaaeb23d
commit 462e5ea15e
38 changed files with 237 additions and 55 deletions

View File

@ -0,0 +1,11 @@
package function;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionNames {
String[] value();
}

View File

@ -3,6 +3,7 @@ package function.builtin;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "APPLY" })
public class APPLY extends LispFunction { public class APPLY extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -9,6 +9,7 @@ import function.builtin.special.LAMBDA;
import sexpression.*; import sexpression.*;
import table.*; import table.*;
@FunctionNames({ "EVAL" })
public class EVAL extends LispFunction { public class EVAL extends LispFunction {
public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) { public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) {

View File

@ -4,6 +4,7 @@ import environment.RuntimeEnvironment;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "EXIT" })
public class EXIT extends LispFunction { public class EXIT extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -6,6 +6,7 @@ import static function.builtin.cons.LIST.makeList;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "FUNCALL" })
public class FUNCALL extends LispFunction { public class FUNCALL extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -11,6 +11,7 @@ import function.*;
import parser.LispParser; import parser.LispParser;
import sexpression.*; import sexpression.*;
@FunctionNames({ "LOAD" })
public class LOAD extends LispFunction { public class LOAD extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -4,6 +4,7 @@ import environment.RuntimeEnvironment;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "PRINT" })
public class PRINT extends LispFunction { public class PRINT extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -4,6 +4,7 @@ import function.*;
import sexpression.*; import sexpression.*;
import table.*; import table.*;
@FunctionNames({ "SET" })
public class SET extends LispFunction { public class SET extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -7,6 +7,7 @@ import function.*;
import sexpression.*; import sexpression.*;
import table.FunctionTable; import table.FunctionTable;
@FunctionNames({ "SYMBOL-FUNCTION" })
public class SYMBOL_FUNCTION extends LispFunction { public class SYMBOL_FUNCTION extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.cons;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "CONS" })
public class CONS extends LispFunction { public class CONS extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.cons;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "FIRST", "CAR" })
public class FIRST extends LispFunction { public class FIRST extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import java.math.BigInteger;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "LENGTH" })
public class LENGTH extends LispFunction { public class LENGTH extends LispFunction {
public static BigInteger getLength(Cons list) { public static BigInteger getLength(Cons list) {

View File

@ -3,6 +3,7 @@ package function.builtin.cons;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "LIST" })
public class LIST extends LispFunction { public class LIST extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.cons;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "REST", "CDR" })
public class REST extends LispFunction { public class REST extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import java.math.BigInteger;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "/" })
public class DIVIDE extends LispFunction { public class DIVIDE extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import java.math.BigInteger;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "-" })
public class MINUS extends LispFunction { public class MINUS extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.math;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "*" })
public class MULTIPLY extends LispFunction { public class MULTIPLY extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.math;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "+" })
public class PLUS extends LispFunction { public class PLUS extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "ATOM" })
public class ATOM extends LispFunction { public class ATOM extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "EQ" })
public class EQ extends LispFunction { public class EQ extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -4,6 +4,7 @@ import function.*;
import function.builtin.cons.LIST; import function.builtin.cons.LIST;
import sexpression.*; import sexpression.*;
@FunctionNames({ "EQUAL" })
public class EQUAL extends LispFunction { public class EQUAL extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "=" })
public class EQUALSP extends LispFunction { public class EQUALSP extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ ">" })
public class GREATERP extends LispFunction { public class GREATERP extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "<" })
public class LESSP extends LispFunction { public class LESSP extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "LISTP" })
public class LISTP extends LispFunction { public class LISTP extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.predicate;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "NULL" })
public class NULL extends LispFunction { public class NULL extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "AND" })
public class AND extends LispSpecialFunction { public class AND extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "COND" })
public class COND extends LispSpecialFunction { public class COND extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.special;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "DEFINE-MACRO" })
public class DEFINE_MACRO extends Define { public class DEFINE_MACRO extends Define {
public DEFINE_MACRO() { public DEFINE_MACRO() {

View File

@ -1,8 +1,9 @@
package function.builtin.special; package function.builtin.special;
import function.UserDefinedFunction; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "DEFUN" })
public class DEFUN extends Define { public class DEFUN extends Define {
public DEFUN() { public DEFUN() {

View File

@ -5,6 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "IF" })
public class IF extends LispSpecialFunction { public class IF extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import static function.builtin.cons.LIST.makeList;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "LAMBDA" })
public class LAMBDA extends LispSpecialFunction { public class LAMBDA extends LispSpecialFunction {
public static boolean isLambdaExpression(SExpression sexpr) { public static boolean isLambdaExpression(SExpression sexpr) {

View File

@ -6,6 +6,7 @@ import function.*;
import sexpression.*; import sexpression.*;
import table.*; import table.*;
@FunctionNames({ "LET" })
public class LET extends LispSpecialFunction { public class LET extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -5,6 +5,7 @@ import static function.builtin.EVAL.eval;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "OR" })
public class OR extends LispSpecialFunction { public class OR extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -3,6 +3,7 @@ package function.builtin.special;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "QUOTE" })
public class QUOTE extends LispSpecialFunction { public class QUOTE extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -7,6 +7,7 @@ import static function.builtin.cons.LIST.makeList;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
@FunctionNames({ "SETF", "SETQ" })
public class SETF extends LispSpecialFunction { public class SETF extends LispSpecialFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;

View File

@ -1,8 +1,10 @@
package table; package table;
import java.util.HashMap; import java.text.MessageFormat;
import java.util.*;
import function.LispFunction; import error.CriticalLispException;
import function.*;
import function.builtin.*; import function.builtin.*;
import function.builtin.cons.*; import function.builtin.cons.*;
import function.builtin.math.*; import function.builtin.math.*;
@ -11,72 +13,132 @@ import function.builtin.special.*;
public class FunctionTable { public class FunctionTable {
private static FunctionTable uniqueInstance = new FunctionTable();
public static LispFunction lookupFunction(String functionName) { public static LispFunction lookupFunction(String functionName) {
return uniqueInstance.functionTable.get(functionName); return getTable().get(functionName);
} }
public static boolean isAlreadyDefined(String functionName) { public static boolean isAlreadyDefined(String functionName) {
return uniqueInstance.functionTable.containsKey(functionName); return getTable().containsKey(functionName);
} }
public static void defineFunction(String functionName, LispFunction function) { public static void defineFunction(String functionName, LispFunction function) {
uniqueInstance.functionTable.put(functionName, function); getTable().put(functionName, function);
} }
public static void reset() { public static void reset() {
uniqueInstance.initializeFunctionTable(); getUniqueInstance().initializeFunctionTable(allBuiltIns);
} }
private HashMap<String, LispFunction> functionTable; static void reset(Set<Class<? extends LispFunction>> builtIns) {
getUniqueInstance().initializeFunctionTable(builtIns);
}
private static Set<Class<? extends LispFunction>> allBuiltIns = new HashSet<>();
static {
allBuiltIns.add(AND.class);
allBuiltIns.add(APPLY.class);
allBuiltIns.add(ATOM.class);
allBuiltIns.add(COND.class);
allBuiltIns.add(CONS.class);
allBuiltIns.add(DEFINE_MACRO.class);
allBuiltIns.add(DEFUN.class);
allBuiltIns.add(DIVIDE.class);
allBuiltIns.add(EQ.class);
allBuiltIns.add(EQUAL.class);
allBuiltIns.add(EQUALSP.class);
allBuiltIns.add(EVAL.class);
allBuiltIns.add(EXIT.class);
allBuiltIns.add(FIRST.class);
allBuiltIns.add(FUNCALL.class);
allBuiltIns.add(GREATERP.class);
allBuiltIns.add(IF.class);
allBuiltIns.add(LAMBDA.class);
allBuiltIns.add(LENGTH.class);
allBuiltIns.add(LESSP.class);
allBuiltIns.add(LET.class);
allBuiltIns.add(LIST.class);
allBuiltIns.add(LISTP.class);
allBuiltIns.add(LOAD.class);
allBuiltIns.add(MINUS.class);
allBuiltIns.add(MULTIPLY.class);
allBuiltIns.add(NULL.class);
allBuiltIns.add(OR.class);
allBuiltIns.add(PLUS.class);
allBuiltIns.add(PRINT.class);
allBuiltIns.add(QUOTE.class);
allBuiltIns.add(REST.class);
allBuiltIns.add(SET.class);
allBuiltIns.add(SETF.class);
allBuiltIns.add(SYMBOL_FUNCTION.class);
}
private static FunctionTable uniqueInstance;
private static FunctionTable getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new FunctionTable();
}
return uniqueInstance;
}
private static Map<String, LispFunction> getTable() {
return getUniqueInstance().table;
}
private Map<String, LispFunction> table;
private FunctionTable() { private FunctionTable() {
initializeFunctionTable(); initializeFunctionTable(allBuiltIns);
} }
private void initializeFunctionTable() { private void initializeFunctionTable(Set<Class<? extends LispFunction>> builtIns) {
functionTable = new HashMap<>(); table = new HashMap<>();
functionTable.put("*", new MULTIPLY()); for (Class<? extends LispFunction> function : builtIns)
functionTable.put("+", new PLUS()); addBuiltInFunctionToTable(function);
functionTable.put("-", new MINUS()); }
functionTable.put("/", new DIVIDE());
functionTable.put("<", new LESSP()); private void addBuiltInFunctionToTable(Class<? extends LispFunction> function) {
functionTable.put("=", new EQUALSP()); FunctionNames functionNames = function.getAnnotation(FunctionNames.class);
functionTable.put(">", new GREATERP());
functionTable.put("AND", new AND()); if (functionNames != null)
functionTable.put("APPLY", new APPLY()); addAllFunctionNamesToTable(function, functionNames.value());
functionTable.put("ATOM", new ATOM()); }
functionTable.put("CAR", new FIRST());
functionTable.put("CDR", new REST()); private void addAllFunctionNamesToTable(Class<? extends LispFunction> function, String[] names) {
functionTable.put("COND", new COND()); LispFunction instance = createInstance(function);
functionTable.put("CONS", new CONS());
functionTable.put("DEFINE-MACRO", new DEFINE_MACRO()); for (String name : names)
functionTable.put("DEFUN", new DEFUN()); table.put(name, instance);
functionTable.put("EQ", new EQ()); }
functionTable.put("EQUAL", new EQUAL());
functionTable.put("EVAL", new EVAL()); private LispFunction createInstance(Class<? extends LispFunction> function) {
functionTable.put("EXIT", new EXIT()); LispFunction instance = null;
functionTable.put("FIRST", new FIRST());
functionTable.put("FUNCALL", new FUNCALL()); try {
functionTable.put("GREATERP", new GREATERP()); instance = function.getConstructor().newInstance();
functionTable.put("IF", new IF()); } catch (Exception e) {
functionTable.put("LAMBDA", new LAMBDA()); throw new LispFunctionInstantiationException(function.getName());
functionTable.put("LENGTH", new LENGTH()); }
functionTable.put("LET", new LET());
functionTable.put("LIST", new LIST()); return instance;
functionTable.put("LISTP", new LISTP()); }
functionTable.put("LOAD", new LOAD());
functionTable.put("NULL", new NULL()); public static class LispFunctionInstantiationException extends CriticalLispException {
functionTable.put("OR", new OR());
functionTable.put("PRINT", new PRINT()); private static final long serialVersionUID = 1L;
functionTable.put("QUOTE", new QUOTE()); private String functionName;
functionTable.put("REST", new REST());
functionTable.put("SET", new SET()); public LispFunctionInstantiationException(String functionName) {
functionTable.put("SETF", new SETF()); this.functionName = functionName;
functionTable.put("SETQ", new SETF()); }
functionTable.put("SYMBOL-FUNCTION", new SYMBOL_FUNCTION());
@Override
public String getMessage() {
return MessageFormat.format("Could not create an instance of ''{0}''", functionName);
}
} }
} }

View File

@ -2,13 +2,47 @@ package table;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.*;
import org.junit.*; import org.junit.*;
import function.LispFunction; import error.ErrorManager;
import function.*;
import sexpression.*; import sexpression.*;
import table.FunctionTable.LispFunctionInstantiationException;
public class FunctionTableTester { public class FunctionTableTester {
@FunctionNames({ "GOOD" })
public static class GoodFunction extends LispFunction {
@Override
public SExpression call(Cons argList) {
return Nil.getInstance();
}
}
@FunctionNames({ "BAD" })
public static class BadFunction extends LispFunction {
public BadFunction() {
throw new IllegalArgumentException("bad function");
}
@Override
public SExpression call(Cons argList) {
return Nil.getInstance();
}
}
public static class NamelessFunction extends LispFunction {
@Override
public SExpression call(Cons argList) {
return Nil.getInstance();
}
}
private LispFunction createLispFunction() { private LispFunction createLispFunction() {
return new LispFunction() { return new LispFunction() {
@ -30,7 +64,7 @@ public class FunctionTableTester {
} }
@Test @Test
public void builtinFunctionIsDefined() { public void builtInFunctionIsDefined() {
assertTrue(FunctionTable.isAlreadyDefined("CONS")); assertTrue(FunctionTable.isAlreadyDefined("CONS"));
} }
@ -40,7 +74,7 @@ public class FunctionTableTester {
} }
@Test @Test
public void lookupBuiltinFunction_ReturnsFunction() { public void lookupBuiltInFunction_ReturnsFunction() {
assertNotNull(FunctionTable.lookupFunction("CONS")); assertNotNull(FunctionTable.lookupFunction("CONS"));
} }
@ -68,10 +102,50 @@ public class FunctionTableTester {
String functionName = "testFunction"; String functionName = "testFunction";
LispFunction testFunction = createLispFunction(); LispFunction testFunction = createLispFunction();
FunctionTable.defineFunction(functionName, testFunction); FunctionTable.defineFunction(functionName, testFunction);
FunctionTable.reset(); FunctionTable.reset();
assertFalse(FunctionTable.isAlreadyDefined(functionName)); assertFalse(FunctionTable.isAlreadyDefined(functionName));
assertNull(FunctionTable.lookupFunction(functionName)); assertNull(FunctionTable.lookupFunction(functionName));
} }
@Test
public void resetWithCustomBuitIns() {
Set<Class<? extends LispFunction>> goodBuiltIns = new HashSet<>();
goodBuiltIns.add(GoodFunction.class);
FunctionTable.reset(goodBuiltIns);
assertTrue(FunctionTable.isAlreadyDefined("GOOD"));
assertNotNull(FunctionTable.lookupFunction("GOOD"));
}
@Test(expected = LispFunctionInstantiationException.class)
public void unableToInitializeBuiltIn() {
Set<Class<? extends LispFunction>> badBuiltIns = new HashSet<>();
badBuiltIns.add(BadFunction.class);
FunctionTable.reset(badBuiltIns);
}
@Test
public void lispFunctionInstantiationException_HasCorrectAttributes() {
LispFunctionInstantiationException e = new LispFunctionInstantiationException("Bad");
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
assertEquals(ErrorManager.Severity.CRITICAL, e.getSeverity());
}
@Test
public void namelessBuiltIn_DoesNotCauseNPE() {
Set<Class<? extends LispFunction>> namelessBuiltins = new HashSet<>();
namelessBuiltins.add(NamelessFunction.class);
FunctionTable.reset(namelessBuiltins);
assertFalse(FunctionTable.isAlreadyDefined("NAMELESS"));
assertNull(FunctionTable.lookupFunction("NAMELESS"));
}
} }