Convert function table to kotlin

This commit is contained in:
Mike Cifelli 2018-03-24 11:07:59 -04:00
parent fd322a385f
commit d74e472780
12 changed files with 208 additions and 255 deletions

View File

@ -5,16 +5,16 @@ import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
import table.FunctionTable;
import static function.builtin.EVAL.applyFunction;
import static function.builtin.EVAL.lookupFunctionOrLambda;
import static table.FunctionTable.lookupFunction;
@FunctionNames({ "APPLY" })
public class APPLY extends LispFunction {
public static SExpression apply(Cons argumentList) {
return lookupFunction("APPLY").call(argumentList);
return FunctionTable.INSTANCE.lookupFunction("APPLY").call(argumentList);
}
private ArgumentValidator argumentValidator;

View File

@ -4,7 +4,6 @@ import error.LispException;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import function.builtin.special.LAMBDA;
import function.builtin.special.RECUR.RecurNotInTailPositionException;
import sexpression.BackquoteExpression;
import sexpression.Cons;
@ -12,13 +11,13 @@ import sexpression.LambdaExpression;
import sexpression.SExpression;
import sexpression.Symbol;
import table.ExecutionContext;
import table.FunctionTable;
import static function.builtin.cons.LIST.makeList;
import static function.builtin.special.LAMBDA.Lambda;
import static java.text.MessageFormat.format;
import static sexpression.Nil.NIL;
import static sexpression.Symbol.T;
import static table.FunctionTable.lookupFunction;
@FunctionNames({ "EVAL" })
public class EVAL extends LispFunction {
@ -45,11 +44,11 @@ public class EVAL extends LispFunction {
}
private static EVAL lookupEval() {
return (EVAL) lookupFunction("EVAL");
return (EVAL) FunctionTable.INSTANCE.lookupFunction("EVAL");
}
public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) {
LispFunction function = lookupFunction(functionExpression.toString());
LispFunction function = FunctionTable.INSTANCE.lookupFunction(functionExpression.toString());
if (function == null)
function = createLambdaFunction(functionExpression);

View File

@ -7,15 +7,14 @@ import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
import table.ExecutionContext;
import table.FunctionTable;
import table.SymbolTable;
import static table.FunctionTable.lookupFunction;
@FunctionNames({ "SET" })
public class SET extends LispFunction {
public static SExpression set(Cons argumentList) {
return lookupFunction("SET").call(argumentList);
return FunctionTable.INSTANCE.lookupFunction("SET").call(argumentList);
}
private ArgumentValidator argumentValidator;

View File

@ -9,9 +9,9 @@ import function.UserDefinedFunction;
import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
import table.FunctionTable;
import static java.text.MessageFormat.format;
import static table.FunctionTable.lookupFunction;
@FunctionNames({ "SYMBOL-FUNCTION" })
public class SYMBOL_FUNCTION extends LispFunction {
@ -29,7 +29,7 @@ public class SYMBOL_FUNCTION extends LispFunction {
argumentValidator.validate(argumentList);
SExpression symbol = argumentList.getFirst();
LispFunction function = lookupFunction(symbol.toString());
LispFunction function = FunctionTable.INSTANCE.lookupFunction(symbol.toString());
if (function != null)
return createRepresentation(symbol, function);

View File

@ -4,9 +4,9 @@ import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import table.FunctionTable;
import static sexpression.Nil.NIL;
import static table.FunctionTable.lookupFunction;
@FunctionNames({ "APPEND" })
public class APPEND extends LispFunction {
@ -16,7 +16,7 @@ public class APPEND extends LispFunction {
}
private static APPEND lookupAppend() {
return (APPEND) lookupFunction("APPEND");
return (APPEND) FunctionTable.INSTANCE.lookupFunction("APPEND");
}
private ArgumentValidator argumentValidator;

View File

@ -6,13 +6,13 @@ import function.LispFunction;
import recursion.TailCall;
import sexpression.Cons;
import sexpression.LispNumber;
import table.FunctionTable;
import java.math.BigInteger;
import static function.builtin.cons.LIST.makeList;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import static table.FunctionTable.lookupFunction;
@FunctionNames({ "LENGTH" })
public class LENGTH extends LispFunction {
@ -24,7 +24,7 @@ public class LENGTH extends LispFunction {
}
private static LENGTH lookupLength() {
return (LENGTH) lookupFunction("LENGTH");
return (LENGTH) FunctionTable.INSTANCE.lookupFunction("LENGTH");
}
private ArgumentValidator argumentValidator;

View File

@ -8,11 +8,10 @@ import function.UserDefinedFunction;
import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
import table.FunctionTable;
import static function.builtin.cons.LIST.makeList;
import static java.text.MessageFormat.format;
import static table.FunctionTable.defineFunction;
import static table.FunctionTable.isAlreadyDefined;
public abstract class Define extends LispSpecialFunction {
@ -50,10 +49,10 @@ public abstract class Define extends LispSpecialFunction {
Cons functionBody = (Cons) remainingArguments.getRest();
UserDefinedFunction function = createFunction(functionName, lambdaList, functionBody);
if (isAlreadyDefined(functionName.toString()))
if (FunctionTable.INSTANCE.isAlreadyDefined(functionName.toString()))
environment.getErrorManager().handle(new RedefiningFunctionWarning(functionName.toString()));
defineFunction(functionName.toString(), function);
FunctionTable.INSTANCE.defineFunction(functionName.toString(), function);
return functionName;
}

View File

@ -1,200 +0,0 @@
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<Class<? extends LispFunction>> 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<Class<? extends LispFunction>> builtIns) {
getUniqueInstance().initializeFunctionTable(builtIns);
}
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() {
initializeFunctionTable(allBuiltIns);
}
private void initializeFunctionTable(Set<Class<? extends LispFunction>> builtIns) {
table = new HashMap<>();
for (Class<? extends LispFunction> function : builtIns)
addBuiltInFunctionToTable(function);
}
private void addBuiltInFunctionToTable(Class<? extends LispFunction> function) {
FunctionNames functionNames = function.getAnnotation(FunctionNames.class);
if (functionNames != null)
addAllFunctionNamesToTable(function, functionNames.value());
}
private void addAllFunctionNamesToTable(Class<? extends LispFunction> function, String[] names) {
for (String name : names)
table.put(name, createInstance(function, name));
}
private LispFunction createInstance(Class<? extends LispFunction> 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);
}
}
}

View File

@ -0,0 +1,159 @@
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.text.MessageFormat.format
import java.util.HashMap
import java.util.HashSet
object FunctionTable {
private var table: MutableMap<String, LispFunction> = HashMap()
private val allBuiltIns = HashSet<Class<out LispFunction>>()
init {
allBuiltIns.add(AND::class.java)
allBuiltIns.add(APPEND::class.java)
allBuiltIns.add(APPLY::class.java)
allBuiltIns.add(ATOM::class.java)
allBuiltIns.add(CASE::class.java)
allBuiltIns.add(COND::class.java)
allBuiltIns.add(CONS::class.java)
allBuiltIns.add(DEFINE_SPECIAL::class.java)
allBuiltIns.add(DEFMACRO::class.java)
allBuiltIns.add(DEFUN::class.java)
allBuiltIns.add(DIVIDE::class.java)
allBuiltIns.add(EQ::class.java)
allBuiltIns.add(EQUAL::class.java)
allBuiltIns.add(NUMERIC_EQUAL::class.java)
allBuiltIns.add(EVAL::class.java)
allBuiltIns.add(EXIT::class.java)
allBuiltIns.add(FIRST::class.java)
allBuiltIns.add(FUNCALL::class.java)
allBuiltIns.add(FUSE::class.java)
allBuiltIns.add(GENSYM::class.java)
allBuiltIns.add(GENSYM_EQUAL::class.java)
allBuiltIns.add(NUMERIC_GREATER::class.java)
allBuiltIns.add(IF::class.java)
allBuiltIns.add(LAMBDA::class.java)
allBuiltIns.add(LENGTH::class.java)
allBuiltIns.add(NUMERIC_LESS::class.java)
allBuiltIns.add(LET::class.java)
allBuiltIns.add(LET_STAR::class.java)
allBuiltIns.add(LIST::class.java)
allBuiltIns.add(LISTP::class.java)
allBuiltIns.add(LOAD::class.java)
allBuiltIns.add(MINUS::class.java)
allBuiltIns.add(MODULO::class.java)
allBuiltIns.add(MULTIPLY::class.java)
allBuiltIns.add(NULL::class.java)
allBuiltIns.add(OR::class.java)
allBuiltIns.add(PLUS::class.java)
allBuiltIns.add(PRINT::class.java)
allBuiltIns.add(PROGN::class.java)
allBuiltIns.add(QUOTE::class.java)
allBuiltIns.add(RECUR::class.java)
allBuiltIns.add(REMAINDER::class.java)
allBuiltIns.add(REST::class.java)
allBuiltIns.add(SET::class.java)
allBuiltIns.add(SETQ::class.java)
allBuiltIns.add(SYMBOL_FUNCTION::class.java)
allBuiltIns.add(SYMBOLS::class.java)
initializeFunctionTable(allBuiltIns)
}
private fun initializeFunctionTable(builtIns: Set<Class<out LispFunction>>) {
table.clear()
for (function in builtIns)
addBuiltInFunctionToTable(function)
}
private fun addBuiltInFunctionToTable(function: Class<out LispFunction>) {
val functionNames = function.getAnnotation(FunctionNames::class.java)
if (functionNames != null)
addAllFunctionNamesToTable(function, functionNames.value)
}
private fun addAllFunctionNamesToTable(function: Class<out LispFunction>, names: Array<String>) {
for (name in names)
table[name] = createInstance(function, name)
}
private fun createInstance(function: Class<out LispFunction>, name: String) = try {
function.getConstructor(String::class.java).newInstance(name)
} catch (e: Exception) {
throw LispFunctionInstantiationException(function.name)
}
fun lookupFunction(functionName: String) = table[functionName]
fun isAlreadyDefined(functionName: String) = table.containsKey(functionName)
fun defineFunction(functionName: String, function: LispFunction) {
table[functionName] = function
}
fun resetFunctionTable() {
initializeFunctionTable(allBuiltIns)
}
internal fun resetFunctionTable(builtIns: Set<Class<out LispFunction>>) {
initializeFunctionTable(builtIns)
}
class LispFunctionInstantiationException(private val functionName: String) : CriticalLispException() {
override val message: String
get() = format("Could not create an instance of ''{0}''", functionName)
}
}

View File

@ -5,6 +5,7 @@ import interpreter.LispInterpreter;
import interpreter.LispInterpreterBuilder;
import interpreter.LispInterpreterBuilderImpl;
import table.ExecutionContext;
import table.FunctionTable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -13,7 +14,6 @@ import java.io.FileNotFoundException;
import java.io.PrintStream;
import static application.LispMain.LANGUAGE_FILE_NAMES;
import static table.FunctionTable.resetFunctionTable;
import static util.Path.getPathPrefix;
public class LispInterpreterFixture {
@ -29,7 +29,7 @@ public class LispInterpreterFixture {
}
public static void cleanUp() {
resetFunctionTable();
FunctionTable.INSTANCE.resetFunctionTable();
executionContext.clearContext();
environment.reset();
}

View File

@ -13,88 +13,86 @@ import table.SymbolTable.NullSymbolTable
@TestInstance(PER_CLASS)
class ExecutionContextTest {
private val executionContext: ExecutionContext = ExecutionContext
@BeforeEach
fun setUp() {
executionContext.clearContext()
ExecutionContext.clearContext()
}
@AfterEach
fun tearDown() {
executionContext.clearContext()
ExecutionContext.clearContext()
}
@Test
fun `assign a new scope`() {
val scope = SymbolTable()
executionContext.scope = scope
ExecutionContext.scope = scope
assertThat(executionContext.scope).isEqualTo(scope)
assertThat(ExecutionContext.scope).isEqualTo(scope)
}
@Test
fun `clear the context`() {
val scope = SymbolTable()
executionContext.scope = scope
ExecutionContext.scope = scope
assertThat(executionContext.scope).isEqualTo(scope)
executionContext.clearContext()
assertThat(executionContext.scope).isNotEqualTo(scope)
assertThat(executionContext.scope.parent).isEqualTo(NullSymbolTable)
assertThat(ExecutionContext.scope).isEqualTo(scope)
ExecutionContext.clearContext()
assertThat(ExecutionContext.scope).isNotEqualTo(scope)
assertThat(ExecutionContext.scope.parent).isEqualTo(NullSymbolTable)
}
@Test
fun `lookup a variable`() {
executionContext.scope["test"] = T
ExecutionContext.scope["test"] = T
assertThat(executionContext.lookupSymbolValue("test")).isEqualTo(T)
assertThat(ExecutionContext.lookupSymbolValue("test")).isEqualTo(T)
}
@Test
fun `lookup a local variable`() {
val scope = SymbolTable(executionContext.scope)
val scope = SymbolTable(ExecutionContext.scope)
scope["local"] = T
executionContext.scope = scope
ExecutionContext.scope = scope
assertThat(executionContext.lookupSymbolValue("local")).isEqualTo(T)
assertThat(ExecutionContext.lookupSymbolValue("local")).isEqualTo(T)
}
@Test
fun `lookup a global variable`() {
val global = executionContext.scope
val global = ExecutionContext.scope
val scope1 = SymbolTable(global)
val scope2 = SymbolTable(scope1)
val scope3 = SymbolTable(scope2)
executionContext.scope["global"] = T
executionContext.scope = scope3
ExecutionContext.scope["global"] = T
ExecutionContext.scope = scope3
assertThat(executionContext.lookupSymbolValue("global")).isEqualTo(T)
assertThat(ExecutionContext.lookupSymbolValue("global")).isEqualTo(T)
}
@Test
fun `lookup a shadowed variable`() {
val scope = SymbolTable(executionContext.scope)
val scope = SymbolTable(ExecutionContext.scope)
scope["shadowed"] = NIL
executionContext.scope["shadowed"] = T
executionContext.scope = scope
ExecutionContext.scope["shadowed"] = T
ExecutionContext.scope = scope
assertThat(executionContext.lookupSymbolValue("shadowed")).isEqualTo(NIL)
assertThat(ExecutionContext.lookupSymbolValue("shadowed")).isEqualTo(NIL)
}
@Test
fun `restore the global context`() {
val global = executionContext.scope
val global = ExecutionContext.scope
val scope1 = SymbolTable(global)
val scope2 = SymbolTable(scope1)
val scope3 = SymbolTable(scope2)
executionContext.scope = scope3
ExecutionContext.scope = scope3
assertThat(executionContext.scope.isGlobal()).isFalse()
executionContext.restoreGlobalScope()
assertThat(executionContext.scope.isGlobal()).isTrue()
assertThat(executionContext.scope).isEqualTo(global)
assertThat(ExecutionContext.scope.isGlobal()).isFalse()
ExecutionContext.restoreGlobalScope()
assertThat(ExecutionContext.scope.isGlobal()).isTrue()
assertThat(ExecutionContext.scope).isEqualTo(global)
}
}

View File

@ -3,8 +3,7 @@ package testutil;
import org.junit.After;
import org.junit.Before;
import table.ExecutionContext;
import static table.FunctionTable.resetFunctionTable;
import table.FunctionTable;
public abstract class SymbolAndFunctionCleaner {
@ -17,14 +16,14 @@ public abstract class SymbolAndFunctionCleaner {
@Before
public final void setUp() {
executionContext.clearContext();
resetFunctionTable();
FunctionTable.INSTANCE.resetFunctionTable();
additionalSetUp();
}
@After
public final void tearDown() {
executionContext.clearContext();
resetFunctionTable();
FunctionTable.INSTANCE.resetFunctionTable();
additionalTearDown();
}