package table; import error.CriticalLispException; import function.FunctionNames; import function.LispFunction; import function.builtin.APPLY; import function.builtin.EVAL; import function.builtin.EXIT; import function.builtin.FUNCALL; import function.builtin.FUSE; import function.builtin.GENSYM; import function.builtin.LOAD; import function.builtin.PRINT; import function.builtin.SET; import function.builtin.SYMBOLS; import function.builtin.SYMBOL_FUNCTION; import function.builtin.cons.APPEND; import function.builtin.cons.CONS; import function.builtin.cons.FIRST; import function.builtin.cons.LENGTH; import function.builtin.cons.LIST; import function.builtin.cons.REST; import function.builtin.math.DIVIDE; import function.builtin.math.MINUS; import function.builtin.math.MODULO; import function.builtin.math.MULTIPLY; import function.builtin.math.PLUS; import function.builtin.math.REMAINDER; import function.builtin.predicate.ATOM; import function.builtin.predicate.EQ; import function.builtin.predicate.EQUAL; import function.builtin.predicate.GENSYM_EQUAL; import function.builtin.predicate.LISTP; import function.builtin.predicate.NULL; import function.builtin.predicate.NUMERIC_EQUAL; import function.builtin.predicate.NUMERIC_GREATER; import function.builtin.predicate.NUMERIC_LESS; import function.builtin.special.AND; import function.builtin.special.CASE; import function.builtin.special.COND; import function.builtin.special.DEFINE_SPECIAL; import function.builtin.special.DEFMACRO; import function.builtin.special.DEFUN; import function.builtin.special.IF; import function.builtin.special.LAMBDA; import function.builtin.special.LET; import function.builtin.special.LET_STAR; import function.builtin.special.OR; import function.builtin.special.PROGN; import function.builtin.special.QUOTE; import function.builtin.special.RECUR; import function.builtin.special.SETQ; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static java.text.MessageFormat.format; public class FunctionTable { private static Set> allBuiltIns = new HashSet<>(); static { allBuiltIns.add(AND.class); allBuiltIns.add(APPEND.class); allBuiltIns.add(APPLY.class); allBuiltIns.add(ATOM.class); allBuiltIns.add(CASE.class); allBuiltIns.add(COND.class); allBuiltIns.add(CONS.class); allBuiltIns.add(DEFINE_SPECIAL.class); allBuiltIns.add(DEFMACRO.class); allBuiltIns.add(DEFUN.class); allBuiltIns.add(DIVIDE.class); allBuiltIns.add(EQ.class); allBuiltIns.add(EQUAL.class); allBuiltIns.add(NUMERIC_EQUAL.class); allBuiltIns.add(EVAL.class); allBuiltIns.add(EXIT.class); allBuiltIns.add(FIRST.class); allBuiltIns.add(FUNCALL.class); allBuiltIns.add(FUSE.class); allBuiltIns.add(GENSYM.class); allBuiltIns.add(GENSYM_EQUAL.class); allBuiltIns.add(NUMERIC_GREATER.class); allBuiltIns.add(IF.class); allBuiltIns.add(LAMBDA.class); allBuiltIns.add(LENGTH.class); allBuiltIns.add(NUMERIC_LESS.class); allBuiltIns.add(LET.class); allBuiltIns.add(LET_STAR.class); allBuiltIns.add(LIST.class); allBuiltIns.add(LISTP.class); allBuiltIns.add(LOAD.class); allBuiltIns.add(MINUS.class); allBuiltIns.add(MODULO.class); allBuiltIns.add(MULTIPLY.class); allBuiltIns.add(NULL.class); allBuiltIns.add(OR.class); allBuiltIns.add(PLUS.class); allBuiltIns.add(PRINT.class); allBuiltIns.add(PROGN.class); allBuiltIns.add(QUOTE.class); allBuiltIns.add(RECUR.class); allBuiltIns.add(REMAINDER.class); allBuiltIns.add(REST.class); allBuiltIns.add(SET.class); allBuiltIns.add(SETQ.class); allBuiltIns.add(SYMBOL_FUNCTION.class); allBuiltIns.add(SYMBOLS.class); } public static LispFunction lookupFunction(String functionName) { return getTable().get(functionName); } public static boolean isAlreadyDefined(String functionName) { return getTable().containsKey(functionName); } public static void defineFunction(String functionName, LispFunction function) { getTable().put(functionName, function); } public static void resetFunctionTable() { getUniqueInstance().initializeFunctionTable(allBuiltIns); } static void resetFunctionTable(Set> builtIns) { getUniqueInstance().initializeFunctionTable(builtIns); } private static FunctionTable uniqueInstance; private static FunctionTable getUniqueInstance() { if (uniqueInstance == null) { uniqueInstance = new FunctionTable(); } return uniqueInstance; } private static Map getTable() { return getUniqueInstance().table; } private Map table; private FunctionTable() { initializeFunctionTable(allBuiltIns); } private void initializeFunctionTable(Set> builtIns) { table = new HashMap<>(); for (Class function : builtIns) addBuiltInFunctionToTable(function); } private void addBuiltInFunctionToTable(Class function) { FunctionNames functionNames = function.getAnnotation(FunctionNames.class); if (functionNames != null) addAllFunctionNamesToTable(function, functionNames.value()); } private void addAllFunctionNamesToTable(Class function, String[] names) { for (String name : names) table.put(name, createInstance(function, name)); } private LispFunction createInstance(Class function, String name) { LispFunction instance = null; try { instance = function.getConstructor(String.class).newInstance(name); } catch (Exception e) { throw new LispFunctionInstantiationException(function.getName()); } return instance; } public static class LispFunctionInstantiationException extends CriticalLispException { private static final long serialVersionUID = 1L; private String functionName; public LispFunctionInstantiationException(String functionName) { this.functionName = functionName; } @Override public String getMessage() { return format("Could not create an instance of ''{0}''", functionName); } } }