Convert more builtins to kotlin

This commit is contained in:
Mike Cifelli 2018-10-21 18:12:26 -04:00
parent b2867042fe
commit d95b5cc46f
24 changed files with 737 additions and 749 deletions

View File

@ -1,39 +0,0 @@
package function.builtin;
import function.ArgumentValidator;
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;
@FunctionNames({ "APPLY" })
public class APPLY extends LispFunction {
public static SExpression apply(Cons argumentList) {
return FunctionTable.INSTANCE.lookupFunction("APPLY").call(argumentList);
}
private ArgumentValidator argumentValidator;
public APPLY(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);
this.argumentValidator.setTrailingArgumentExpectedType(Cons.class);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons rest = (Cons) argumentList.getRest();
Cons functionArguments = (Cons) rest.getFirst();
SExpression functionName = argumentList.getFirst();
LispFunction function = lookupFunctionOrLambda(functionName);
return applyFunction(function, functionArguments);
}
}

View File

@ -0,0 +1,38 @@
package function.builtin
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import sexpression.Cons
import sexpression.SExpression
import table.FunctionTable
import function.builtin.Eval.Companion.applyFunction
import function.builtin.Eval.Companion.lookupFunctionOrLambda
@FunctionNames("APPLY")
class Apply(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setExactNumberOfArguments(2)
setTrailingArgumentExpectedType(Cons::class.java)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
val rest = argumentList.rest as Cons
val functionArguments = rest.first as Cons
val functionName = argumentList.first
val function = lookupFunctionOrLambda(functionName)
return applyFunction(function, functionArguments)
}
companion object {
fun apply(argumentList: Cons): SExpression {
return FunctionTable.lookupFunction("APPLY")!!.call(argumentList)
}
}
}

View File

@ -1,29 +0,0 @@
package function.builtin;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
import static function.builtin.APPLY.apply;
import static function.builtin.cons.LIST.makeList;
@FunctionNames({ "FUNCALL", "CALL" })
public class FUNCALL extends LispFunction {
private ArgumentValidator argumentValidator;
public FUNCALL(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setMinimumNumberOfArguments(1);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons applyArgs = new Cons(argumentList.getFirst(), makeList(argumentList.getRest()));
return apply(applyArgs);
}
}

View File

@ -0,0 +1,24 @@
package function.builtin
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import function.builtin.Apply.Companion.apply
import function.builtin.cons.LIST.makeList
import sexpression.Cons
import sexpression.SExpression
@FunctionNames("FUNCALL", "CALL")
class Funcall(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setMinimumNumberOfArguments(1)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
val applyArgs = Cons(argumentList.first, makeList(argumentList.rest))
return apply(applyArgs)
}
}

View File

@ -1,42 +0,0 @@
package function.builtin;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
import java.math.BigInteger;
@FunctionNames({ "GENSYM" })
public class GENSYM extends LispFunction {
public static final String GENSYM_PREFIX = "#G";
private static BigInteger counter = BigInteger.ZERO;
private static Symbol generateSymbol() {
incrementCounter();
return new Symbol(GENSYM_PREFIX + counter);
}
private static void incrementCounter() {
counter = counter.add(BigInteger.ONE);
}
private ArgumentValidator argumentValidator;
public GENSYM(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setMaximumNumberOfArguments(0);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return generateSymbol();
}
}

View File

@ -0,0 +1,41 @@
package function.builtin
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import sexpression.Cons
import sexpression.SExpression
import sexpression.Symbol
import java.math.BigInteger.ONE
import java.math.BigInteger.ZERO
@FunctionNames("GENSYM")
class Gensym(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setMaximumNumberOfArguments(0)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
return generateSymbol()
}
companion object {
const val GENSYM_PREFIX = "#G"
private var counter = ZERO
private fun generateSymbol(): Symbol {
incrementCounter()
return Symbol(GENSYM_PREFIX + counter)
}
private fun incrementCounter() {
counter = counter.add(ONE)
}
}
}

View File

@ -1,28 +0,0 @@
package function.builtin;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
import table.ExecutionContext;
@FunctionNames({ "SYMBOLS" })
public class SYMBOLS extends LispFunction {
private ArgumentValidator argumentValidator;
private ExecutionContext executionContext;
public SYMBOLS(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(0);
this.executionContext = ExecutionContext.INSTANCE;
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return executionContext.toList();
}
}

View File

@ -1,63 +0,0 @@
package function.builtin;
import error.LispException;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import function.LispSpecialFunction;
import function.UserDefinedFunction;
import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;
import table.FunctionTable;
import static java.text.MessageFormat.format;
@FunctionNames({ "SYMBOL-FUNCTION" })
public class SYMBOL_FUNCTION extends LispFunction {
private ArgumentValidator argumentValidator;
public SYMBOL_FUNCTION(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(1);
this.argumentValidator.setEveryArgumentExpectedType(Symbol.class);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
SExpression symbol = argumentList.getFirst();
LispFunction function = FunctionTable.INSTANCE.lookupFunction(symbol.toString());
if (function != null)
return createRepresentation(symbol, function);
throw new UndefinedSymbolFunctionException(symbol);
}
private SExpression createRepresentation(SExpression symbol, LispFunction function) {
if (function instanceof UserDefinedFunction)
return ((UserDefinedFunction) function).getLambdaExpression();
String typeIndicator = function instanceof LispSpecialFunction ? "SPECIAL-FUNCTION" : "FUNCTION";
return new Symbol(format("#<{0} {1}>", typeIndicator, symbol));
}
public static class UndefinedSymbolFunctionException extends LispException {
private static final long serialVersionUID = 1L;
private SExpression function;
public UndefinedSymbolFunctionException(SExpression function) {
this.function = function;
}
@Override
public String getMessage() {
return format("SYMBOL-FUNCTION: undefined function: {0}", function);
}
}
}

View File

@ -0,0 +1,47 @@
package function.builtin
import error.LispException
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import function.LispSpecialFunction
import function.UserDefinedFunction
import sexpression.Cons
import sexpression.SExpression
import sexpression.Symbol
import table.FunctionTable
@FunctionNames("SYMBOL-FUNCTION")
class SymbolFunction(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setExactNumberOfArguments(1)
setEveryArgumentExpectedType(Symbol::class.java)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
val symbol = argumentList.first
val function = FunctionTable.lookupFunction(symbol.toString())
if (function != null)
return createRepresentation(symbol, function)
throw UndefinedSymbolFunctionException(symbol)
}
private fun createRepresentation(symbol: SExpression, function: LispFunction): SExpression {
if (function is UserDefinedFunction)
return function.lambdaExpression
val typeIndicator = if (function is LispSpecialFunction) "SPECIAL-FUNCTION" else "FUNCTION"
return Symbol("#<$typeIndicator $symbol>")
}
class UndefinedSymbolFunctionException(function: SExpression) : LispException() {
override val message = "SYMBOL-FUNCTION: undefined function: $function"
}
}

View File

@ -0,0 +1,22 @@
package function.builtin
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import sexpression.Cons
import sexpression.SExpression
import table.ExecutionContext
@FunctionNames("SYMBOLS")
class Symbols(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setExactNumberOfArguments(0)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
return ExecutionContext.toList()
}
}

View File

@ -1,100 +0,0 @@
package function.builtin.predicate;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.Nil;
import sexpression.SExpression;
import sexpression.Symbol;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static function.builtin.GENSYM.GENSYM_PREFIX;
@FunctionNames({ "GENSYM-EQUAL", "GENSYM-EQUAL?" })
public class GENSYM_EQUAL extends LispFunction {
private ArgumentValidator argumentValidator;
public GENSYM_EQUAL(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons rest = (Cons) argumentList.getRest();
SExpression firstArgument = argumentList.getFirst();
SExpression secondArgument = rest.getFirst();
return gensymEqual(firstArgument, secondArgument);
}
private SExpression gensymEqual(SExpression firstArgument, SExpression secondArgument) {
String firstEqualized = equalizeGensyms(firstArgument);
String secondEqualized = equalizeGensyms(secondArgument);
return firstEqualized.equals(secondEqualized) ? Symbol.Companion.getT() : Nil.INSTANCE;
}
private String equalizeGensyms(SExpression expression) {
GensymEqualizer equalizer = new GensymEqualizer(expression.toString());
return equalizer.equalize();
}
private static class GensymEqualizer {
private static final String GENSYM_REGEX = Pattern.quote(GENSYM_PREFIX) + "[0-9]+";
Map<String, String> gensymAliases;
Matcher matcher;
String expression;
int counter;
public GensymEqualizer(String expression) {
this.gensymAliases = new HashMap<>();
this.matcher = Pattern.compile(GENSYM_REGEX).matcher(expression);
this.expression = expression;
this.counter = 0;
}
public String equalize() {
createGensymAliases();
return equalizeGensyms();
}
private void createGensymAliases() {
while (matcher.find())
createAliasForGensym();
}
private void createAliasForGensym() {
String gensym = matcher.group();
if (isNewGensym(gensym))
gensymAliases.put(gensym, GENSYM_PREFIX + (counter++));
}
private boolean isNewGensym(String gensym) {
return !gensymAliases.containsKey(gensym);
}
private String equalizeGensyms() {
String equalizedExpression = expression;
for (Entry<String, String> entry : gensymAliases.entrySet())
equalizedExpression = equalizedExpression.replace(entry.getKey(), entry.getValue());
return equalizedExpression;
}
}
}

View File

@ -0,0 +1,82 @@
package function.builtin.predicate
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import function.builtin.Gensym.Companion.GENSYM_PREFIX
import sexpression.Cons
import sexpression.Nil
import sexpression.SExpression
import sexpression.Symbol
import java.util.regex.Pattern
@FunctionNames("GENSYM-EQUAL", "GENSYM-EQUAL?")
class GensymEqual(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setExactNumberOfArguments(2)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
val rest = argumentList.rest as Cons
val firstArgument = argumentList.first
val secondArgument = rest.first
return gensymEqual(firstArgument, secondArgument)
}
private fun gensymEqual(firstArgument: SExpression, secondArgument: SExpression): SExpression {
val firstEqualized = equalizeGensyms(firstArgument)
val secondEqualized = equalizeGensyms(secondArgument)
return if (firstEqualized == secondEqualized) Symbol.T else Nil
}
private fun equalizeGensyms(expression: SExpression) =
GensymEqualizer(expression.toString()).equalize()
private class GensymEqualizer(internal var expression: String) {
internal val gensymAliases = mutableMapOf<String, String>()
internal var matcher = Pattern.compile(GENSYM_REGEX).matcher(expression)
internal var counter: Int = 0
fun equalize(): String {
createGensymAliases()
return equalizeGensyms()
}
private fun createGensymAliases() {
while (matcher.find())
createAliasForGensym()
}
private fun createAliasForGensym() {
val gensym = matcher.group()
if (isNewGensym(gensym))
gensymAliases[gensym] = GENSYM_PREFIX + counter++
}
private fun isNewGensym(gensym: String): Boolean {
return !gensymAliases.containsKey(gensym)
}
private fun equalizeGensyms(): String {
var equalizedExpression = expression
for ((key, value) in gensymAliases)
equalizedExpression = equalizedExpression.replace(key, value)
return equalizedExpression
}
companion object {
private val GENSYM_REGEX = Pattern.quote(GENSYM_PREFIX) + "[0-9]+"
}
}
}

View File

@ -1,78 +0,0 @@
package function.builtin;
import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.DottedArgumentListException;
import function.ArgumentValidator.TooFewArgumentsException;
import function.ArgumentValidator.TooManyArgumentsException;
import function.builtin.Eval.UndefinedFunctionException;
import org.junit.Test;
import sexpression.Cons;
import testutil.SymbolAndFunctionCleaner;
import static function.builtin.APPLY.apply;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;
public class APPLYTest extends SymbolAndFunctionCleaner {
@Test
public void applyWithSymbol() {
String input = "(apply '+ '(1 2 3))";
assertSExpressionsMatch(parseString("6"), evaluateString(input));
}
@Test
public void applyWithLambdaExpression() {
String input = "(apply (lambda (x) (+ x 1)) '(25))";
assertSExpressionsMatch(parseString("26"), evaluateString(input));
}
@Test
public void applyWithQuotedLambdaExpression() {
String input = "(apply '(lambda (x) (+ x 1)) '(25))";
assertSExpressionsMatch(parseString("26"), evaluateString(input));
}
@Test
public void staticApplyCall() {
String argumentList = "(+ (25 10))";
Cons parsedArgumentList = (Cons) parseString(argumentList);
assertSExpressionsMatch(parseString("35"), apply(parsedArgumentList));
}
@Test(expected = UndefinedFunctionException.class)
public void applyWithUndefinedFunction() {
evaluateString("(apply 'f '(1 2 3))");
}
@Test(expected = BadArgumentTypeException.class)
public void applyWithNonListSecondArgument() {
evaluateString("(apply '+ '2)");
}
@Test(expected = TooManyArgumentsException.class)
public void applyWithTooManyArguments() {
evaluateString("(apply '1 '2 '3)");
}
@Test(expected = TooFewArgumentsException.class)
public void applyWithTooFewArguments() {
evaluateString("(apply '1)");
}
@Test(expected = DottedArgumentListException.class)
public void applyWithDottedArgumentList_ThrowsException() {
evaluateString("(apply 'apply (cons 'T 'T))");
}
@Test
public void applyWithMacro() {
evaluateString("(defmacro m (x) `(+ 2 ,x))");
assertSExpressionsMatch(parseString("27"), evaluateString("(apply 'm '(25))"));
}
}

View File

@ -0,0 +1,90 @@
package function.builtin
import function.ArgumentValidator.BadArgumentTypeException
import function.ArgumentValidator.DottedArgumentListException
import function.ArgumentValidator.TooFewArgumentsException
import function.ArgumentValidator.TooManyArgumentsException
import function.builtin.Apply.Companion.apply
import function.builtin.Eval.UndefinedFunctionException
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import sexpression.Cons
import testutil.LispTestInstance
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.assertSExpressionsMatch
import testutil.TestUtilities.evaluateString
import testutil.TestUtilities.parseString
@LispTestInstance
class ApplyTest : SymbolAndFunctionCleaner() {
@Test
fun applyWithSymbol() {
val input = "(apply '+ '(1 2 3))"
assertSExpressionsMatch(parseString("6"), evaluateString(input))
}
@Test
fun applyWithLambdaExpression() {
val input = "(apply (lambda (x) (+ x 1)) '(25))"
assertSExpressionsMatch(parseString("26"), evaluateString(input))
}
@Test
fun applyWithQuotedLambdaExpression() {
val input = "(apply '(lambda (x) (+ x 1)) '(25))"
assertSExpressionsMatch(parseString("26"), evaluateString(input))
}
@Test
fun staticApplyCall() {
val argumentList = "(+ (25 10))"
val parsedArgumentList = parseString(argumentList) as Cons
assertSExpressionsMatch(parseString("35"), apply(parsedArgumentList))
}
@Test
fun applyWithUndefinedFunction() {
assertThrows(UndefinedFunctionException::class.java) {
evaluateString("(apply 'f '(1 2 3))")
}
}
@Test
fun applyWithNonListSecondArgument() {
assertThrows(BadArgumentTypeException::class.java) {
evaluateString("(apply '+ '2)")
}
}
@Test
fun applyWithTooManyArguments() {
assertThrows(TooManyArgumentsException::class.java) {
evaluateString("(apply '1 '2 '3)")
}
}
@Test
fun applyWithTooFewArguments() {
assertThrows(TooFewArgumentsException::class.java) {
evaluateString("(apply '1)")
}
}
@Test
fun applyWithDottedArgumentList_ThrowsException() {
assertThrows(DottedArgumentListException::class.java) {
evaluateString("(apply 'apply (cons 'T 'T))")
}
}
@Test
fun applyWithMacro() {
evaluateString("(defmacro m (x) `(+ 2 ,x))")
assertSExpressionsMatch(parseString("27"), evaluateString("(apply 'm '(25))"))
}
}

View File

@ -1,40 +0,0 @@
package function.builtin;
import function.ArgumentValidator.TooFewArgumentsException;
import org.junit.Test;
import testutil.SymbolAndFunctionCleaner;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;
public class FUNCALLTest extends SymbolAndFunctionCleaner {
@Test
public void funcallWithNumbers() {
String input = "(funcall '+ 1 2 3)";
assertSExpressionsMatch(parseString("6"), evaluateString(input));
}
@Test
public void callWithNumbers() {
String input = "(call '+ 1 2 3)";
assertSExpressionsMatch(parseString("6"), evaluateString(input));
}
@Test
public void funcallWithUserDefinedFunction() {
String defineUserFunction = "(defun x (n m) (+ n m))";
String input = "(funcall 'x 2 30)";
evaluateString(defineUserFunction);
assertSExpressionsMatch(parseString("32"), evaluateString(input));
}
@Test(expected = TooFewArgumentsException.class)
public void funcallWithTooFewArguments() {
evaluateString("(funcall)");
}
}

View File

@ -0,0 +1,44 @@
package function.builtin
import function.ArgumentValidator.TooFewArgumentsException
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import testutil.LispTestInstance
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.assertSExpressionsMatch
import testutil.TestUtilities.evaluateString
import testutil.TestUtilities.parseString
@LispTestInstance
class FuncallTest : SymbolAndFunctionCleaner() {
@Test
fun funcallWithNumbers() {
val input = "(funcall '+ 1 2 3)"
assertSExpressionsMatch(parseString("6"), evaluateString(input))
}
@Test
fun callWithNumbers() {
val input = "(call '+ 1 2 3)"
assertSExpressionsMatch(parseString("6"), evaluateString(input))
}
@Test
fun funcallWithUserDefinedFunction() {
val defineUserFunction = "(defun x (n m) (+ n m))"
val input = "(funcall 'x 2 30)"
evaluateString(defineUserFunction)
assertSExpressionsMatch(parseString("32"), evaluateString(input))
}
@Test
fun funcallWithTooFewArguments() {
assertThrows(TooFewArgumentsException::class.java) {
evaluateString("(funcall)")
}
}
}

View File

@ -1,42 +0,0 @@
package function.builtin;
import function.ArgumentValidator.TooManyArgumentsException;
import org.junit.Test;
import testutil.SymbolAndFunctionCleaner;
import token.TokenFactory.BadCharacterException;
import static function.builtin.GENSYM.GENSYM_PREFIX;
import static testutil.TestUtilities.assertSExpressionsDoNotMatch;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TypeAssertions.assertSymbol;
public class GENSYMTest extends SymbolAndFunctionCleaner {
@Test
public void gensymCreatesSymbol() {
assertSymbol(evaluateString("(gensym)"));
}
@Test
public void gensymCreatesUniqueSymbol() {
assertSExpressionsDoNotMatch(evaluateString("(gensym)"), evaluateString("(gensym)"));
}
@Test
public void simpleGensymUsage() {
String input = "(let ((x (gensym))) (set x 23) (eval x))";
assertSExpressionsMatch(evaluateString("23"), evaluateString(input));
}
@Test(expected = BadCharacterException.class)
public void cannotUseGensymValueManually() {
String variableName = GENSYM_PREFIX + 1;
evaluateString("(setq " + variableName + " 100)");
}
@Test(expected = TooManyArgumentsException.class)
public void gensymWithTooManyArguments() {
evaluateString("(gensym 1)");
}
}

View File

@ -0,0 +1,47 @@
package function.builtin
import function.ArgumentValidator.TooManyArgumentsException
import function.builtin.Gensym.Companion.GENSYM_PREFIX
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.assertSExpressionsDoNotMatch
import testutil.TestUtilities.assertSExpressionsMatch
import testutil.TestUtilities.evaluateString
import testutil.TypeAssertions.assertSymbol
import token.TokenFactory.BadCharacterException
class GensymTest : SymbolAndFunctionCleaner() {
@Test
fun gensymCreatesSymbol() {
assertSymbol(evaluateString("(gensym)"))
}
@Test
fun gensymCreatesUniqueSymbol() {
assertSExpressionsDoNotMatch(evaluateString("(gensym)"), evaluateString("(gensym)"))
}
@Test
fun simpleGensymUsage() {
val input = "(let ((x (gensym))) (set x 23) (eval x))"
assertSExpressionsMatch(evaluateString("23"), evaluateString(input))
}
@Test
fun cannotUseGensymValueManually() {
val variableName = GENSYM_PREFIX + 1
assertThrows(BadCharacterException::class.java) {
evaluateString("(setq $variableName 100)")
}
}
@Test
fun gensymWithTooManyArguments() {
assertThrows(TooManyArgumentsException::class.java) {
evaluateString("(gensym 1)")
}
}
}

View File

@ -1,56 +0,0 @@
package function.builtin;
import function.ArgumentValidator.TooManyArgumentsException;
import org.junit.Test;
import testutil.SymbolAndFunctionCleaner;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
public class SYMBOLSTest extends SymbolAndFunctionCleaner {
@Test
public void noSymbols() {
assertSExpressionsMatch(evaluateString("'(nil)"), evaluateString("(symbols)"));
}
@Test
public void globalSymbol() {
evaluateString("(setq x 10)");
assertSExpressionsMatch(evaluateString("'(((x 10)))"), evaluateString("(symbols)"));
}
@Test
public void multipleSymbolsSorted() {
evaluateString("(setq x 10)");
evaluateString("(setq a 20)");
evaluateString("(setq y 30)");
evaluateString("(setq w 40)");
evaluateString("(setq e 50)");
assertSExpressionsMatch(evaluateString("'(((a 20) (e 50) (w 40) (x 10) (y 30)))"), evaluateString("(symbols)"));
}
@Test
public void fullExecutionContext() {
evaluateString("(setq x 10)");
evaluateString("(setq y 30)");
assertSExpressionsMatch(evaluateString("'(((x 10) (y 30)) ((a 100) (q 99) (z nil)))"),
evaluateString("(let ((q 99) (a 100) (z)) (symbols))"));
}
@Test
public void updateSymbolInLet() {
evaluateString("(setq x 10)");
evaluateString("(setq y 30)");
assertSExpressionsMatch(evaluateString("'(((x 10) (y 30)) ((q 99) (x 1) (z 2)))"),
evaluateString("(let ((q 99) (x 100) (z 2)) (setq x 1) (symbols))"));
}
@Test(expected = TooManyArgumentsException.class)
public void symbolsWithTooManyArguments() {
evaluateString("(symbols 1)");
}
}

View File

@ -1,72 +0,0 @@
package function.builtin;
import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooFewArgumentsException;
import function.ArgumentValidator.TooManyArgumentsException;
import function.builtin.SYMBOL_FUNCTION.UndefinedSymbolFunctionException;
import org.junit.Test;
import sexpression.Nil;
import testutil.SymbolAndFunctionCleaner;
import static error.Severity.ERROR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static testutil.TestUtilities.evaluateString;
public class SYMBOL_FUNCTIONTest extends SymbolAndFunctionCleaner {
@Test
public void symbolFunction_BuiltInFunction() {
String input = "(symbol-function '+)";
assertEquals("#<FUNCTION +>", evaluateString(input).toString());
}
@Test
public void symbolFunction_BuiltInSpecialFunction() {
String input = "(symbol-function 'if)";
assertEquals("#<SPECIAL-FUNCTION IF>", evaluateString(input).toString());
}
@Test
public void symbolFunction_UserDefinedFunction() {
String defineUserFunction = "(defun y (n m) (+ n m))";
String input = "(symbol-function 'y)";
evaluateString(defineUserFunction);
assertEquals("(Y (N M) (+ N M))", evaluateString(input).toString());
}
@Test(expected = UndefinedSymbolFunctionException.class)
public void symbolFunction_NonFunction() {
String input = "(symbol-function 'a)";
evaluateString(input);
}
@Test(expected = BadArgumentTypeException.class)
public void symbolFunctionWithBadArgumentType() {
evaluateString("(symbol-function 2)");
}
@Test(expected = TooManyArgumentsException.class)
public void symbolFunctionWithTooManyArguments() {
evaluateString("(symbol-function 'a 'b)");
}
@Test(expected = TooFewArgumentsException.class)
public void symbolFunctionWithTooFewArguments() {
evaluateString("(symbol-function)");
}
@Test
public void undefinedSymbolFunctionException_HasCorrectAttributes() {
UndefinedSymbolFunctionException e = new UndefinedSymbolFunctionException(Nil.INSTANCE);
assertEquals(ERROR, e.getSeverity());
assertNotNull(e.getMessage());
assertTrue(e.getMessage().length() > 0);
}
}

View File

@ -0,0 +1,76 @@
package function.builtin
import function.ArgumentValidator.BadArgumentTypeException
import function.ArgumentValidator.TooFewArgumentsException
import function.ArgumentValidator.TooManyArgumentsException
import function.builtin.SymbolFunction.UndefinedSymbolFunctionException
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import sexpression.Nil
import testutil.LispTestInstance
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.assertIsErrorWithMessage
import testutil.TestUtilities.evaluateString
@LispTestInstance
class SymbolFunctionTest : SymbolAndFunctionCleaner() {
@Test
fun symbolFunction_BuiltInFunction() {
val input = "(symbol-function '+)"
assertThat(evaluateString(input).toString()).isEqualTo("#<FUNCTION +>")
}
@Test
fun symbolFunction_BuiltInSpecialFunction() {
val input = "(symbol-function 'if)"
assertThat(evaluateString(input).toString()).isEqualTo("#<SPECIAL-FUNCTION IF>")
}
@Test
fun symbolFunction_UserDefinedFunction() {
val defineUserFunction = "(defun y (n m) (+ n m))"
val input = "(symbol-function 'y)"
evaluateString(defineUserFunction)
assertThat(evaluateString(input).toString()).isEqualTo("(Y (N M) (+ N M))")
}
@Test
fun symbolFunction_NonFunction() {
val input = "(symbol-function 'a)"
assertThrows(UndefinedSymbolFunctionException::class.java) {
evaluateString(input)
}
}
@Test
fun symbolFunctionWithBadArgumentType() {
assertThrows(BadArgumentTypeException::class.java) {
evaluateString("(symbol-function 2)")
}
}
@Test
fun symbolFunctionWithTooManyArguments() {
assertThrows(TooManyArgumentsException::class.java) {
evaluateString("(symbol-function 'a 'b)")
}
}
@Test
fun symbolFunctionWithTooFewArguments() {
assertThrows(TooFewArgumentsException::class.java) {
evaluateString("(symbol-function)")
}
}
@Test
fun undefinedSymbolFunctionException_HasCorrectAttributes() {
assertIsErrorWithMessage(UndefinedSymbolFunctionException(Nil))
}
}

View File

@ -0,0 +1,60 @@
package function.builtin
import function.ArgumentValidator.TooManyArgumentsException
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import testutil.LispTestInstance
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.assertSExpressionsMatch
import testutil.TestUtilities.evaluateString
@LispTestInstance
class SymbolsTest : SymbolAndFunctionCleaner() {
@Test
fun noSymbols() {
assertSExpressionsMatch(evaluateString("'(nil)"), evaluateString("(symbols)"))
}
@Test
fun globalSymbol() {
evaluateString("(setq x 10)")
assertSExpressionsMatch(evaluateString("'(((x 10)))"), evaluateString("(symbols)"))
}
@Test
fun multipleSymbolsSorted() {
evaluateString("(setq x 10)")
evaluateString("(setq a 20)")
evaluateString("(setq y 30)")
evaluateString("(setq w 40)")
evaluateString("(setq e 50)")
assertSExpressionsMatch(evaluateString("'(((a 20) (e 50) (w 40) (x 10) (y 30)))"), evaluateString("(symbols)"))
}
@Test
fun fullExecutionContext() {
evaluateString("(setq x 10)")
evaluateString("(setq y 30)")
assertSExpressionsMatch(evaluateString("'(((x 10) (y 30)) ((a 100) (q 99) (z nil)))"),
evaluateString("(let ((q 99) (a 100) (z)) (symbols))"))
}
@Test
fun updateSymbolInLet() {
evaluateString("(setq x 10)")
evaluateString("(setq y 30)")
assertSExpressionsMatch(evaluateString("'(((x 10) (y 30)) ((q 99) (x 1) (z 2)))"),
evaluateString("(let ((q 99) (x 100) (z 2)) (setq x 1) (symbols))"))
}
@Test
fun symbolsWithTooManyArguments() {
assertThrows(TooManyArgumentsException::class.java) {
evaluateString("(symbols 1)")
}
}
}

View File

@ -1,160 +0,0 @@
package function.builtin.predicate;
import function.ArgumentValidator.TooFewArgumentsException;
import function.ArgumentValidator.TooManyArgumentsException;
import org.junit.Test;
import testutil.SymbolAndFunctionCleaner;
import static testutil.TestUtilities.evaluateString;
import static testutil.TypeAssertions.assertNil;
import static testutil.TypeAssertions.assertT;
public class GENSYM_EQUALTest extends SymbolAndFunctionCleaner {
@Test
public void gensymEqualWithTwoEqualAtoms() {
String input = "(gensym-equal 'a 'a)";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithTwoEqualAtomsAndAlias() {
String input = "(gensym-equal? 'a 'a)";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithTwoUnequalAtoms() {
String input = "(gensym-equal 'a 'b)";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithTwoEqualNumbers() {
String input = "(gensym-equal -4 -4)";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithTwoUnequalNumbers() {
String input = "(gensym-equal +5 +7)";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithTwoEqualStrings() {
String input = "(gensym-equal \"potato\" \"potato\")";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithTwoUnequalStrings() {
String input = "(gensym-equal \"tomato\" \"potato\")";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithTwoDifferentCasedStrings() {
String input = "(gensym-equal \"Potato\" \"potato\")";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithAtomAndList() {
String input = "(gensym-equal \"string\" '(m i k e))";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithListAndAtom() {
String input = "(gensym-equal '(m i k e) \"string\")";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithTwoEqualLists() {
String input = "(gensym-equal '(1 2 3) '(1 2 3))";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithTwoUnequalLists() {
String input = "(gensym-equal '(1 2 3) '(1 3 3))";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithTwoEqualNestedLists() {
String input = "(gensym-equal '(1 ((2) 3)) '(1 ((2) 3)))";
assertT(evaluateString(input));
}
@Test(expected = TooManyArgumentsException.class)
public void gensymEqualWithTooManyArguments() {
evaluateString("(gensym-equal 1 2 3)");
}
@Test(expected = TooFewArgumentsException.class)
public void gensymEqualWithTooFewArguments() {
evaluateString("(gensym-equal 1)");
}
@Test
public void gensymEqualWithGensyms() {
String input = "(gensym-equal (gensym) (gensym))";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithNestedGensyms() {
String input = "(gensym-equal `(1 ,(gensym) (2 ,(gensym))) `(1 ,(gensym) (2 ,(gensym))))";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithUnmatchedGensymPositions() {
String input = "(gensym-equal (let ((one (gensym))) `(,one ,one))"
+ " (let ((one (gensym)) (two (gensym))) `(,one ,two)))";
assertNil(evaluateString(input));
}
@Test
public void gensymEqualWithMatchedGensymPositions() {
String input = "(gensym-equal (let ((one (gensym))) `(,one ,one))"
+ " (let ((one (gensym)) (two (gensym))) `(,one ,one)))";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithComplexMatchedGensymPositions() {
String input = "(gensym-equal (let ((x (gensym)) (y (gensym)) (z (gensym))) `(,x (,y ,z)))"
+ " (let ((a (gensym)) (b (gensym)) (c (gensym))) `(,c (,a ,b))))";
assertT(evaluateString(input));
}
@Test
public void gensymEqualWithComplexUnmatchedGensymPositions() {
String input = "(gensym-equal (let ((x (gensym)) (y (gensym)) (z (gensym))) `(,x , y (,z ,z)))"
+ " (let ((a (gensym)) (b (gensym)) (c (gensym))) `(,a ,c (,b ,c))))";
assertNil(evaluateString(input));
}
}

View File

@ -0,0 +1,166 @@
package function.builtin.predicate
import function.ArgumentValidator.TooFewArgumentsException
import function.ArgumentValidator.TooManyArgumentsException
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import testutil.LispTestInstance
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.evaluateString
import testutil.TypeAssertions.assertNil
import testutil.TypeAssertions.assertT
@LispTestInstance
class GensymEqualTest : SymbolAndFunctionCleaner() {
@Test
fun gensymEqualWithTwoEqualAtoms() {
val input = "(gensym-equal 'a 'a)"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithTwoEqualAtomsAndAlias() {
val input = "(gensym-equal? 'a 'a)"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithTwoUnequalAtoms() {
val input = "(gensym-equal 'a 'b)"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithTwoEqualNumbers() {
val input = "(gensym-equal -4 -4)"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithTwoUnequalNumbers() {
val input = "(gensym-equal +5 +7)"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithTwoEqualStrings() {
val input = "(gensym-equal \"potato\" \"potato\")"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithTwoUnequalStrings() {
val input = "(gensym-equal \"tomato\" \"potato\")"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithTwoDifferentCasedStrings() {
val input = "(gensym-equal \"Potato\" \"potato\")"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithAtomAndList() {
val input = "(gensym-equal \"string\" '(m i k e))"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithListAndAtom() {
val input = "(gensym-equal '(m i k e) \"string\")"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithTwoEqualLists() {
val input = "(gensym-equal '(1 2 3) '(1 2 3))"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithTwoUnequalLists() {
val input = "(gensym-equal '(1 2 3) '(1 3 3))"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithTwoEqualNestedLists() {
val input = "(gensym-equal '(1 ((2) 3)) '(1 ((2) 3)))"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithTooManyArguments() {
assertThrows(TooManyArgumentsException::class.java) {
evaluateString("(gensym-equal 1 2 3)")
}
}
@Test
fun gensymEqualWithTooFewArguments() {
assertThrows(TooFewArgumentsException::class.java) {
evaluateString("(gensym-equal 1)")
}
}
@Test
fun gensymEqualWithGensyms() {
val input = "(gensym-equal (gensym) (gensym))"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithNestedGensyms() {
val input = "(gensym-equal `(1 ,(gensym) (2 ,(gensym))) `(1 ,(gensym) (2 ,(gensym))))"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithUnmatchedGensymPositions() {
val input = "(gensym-equal (let ((one (gensym))) `(,one ,one))" +
" (let ((one (gensym)) (two (gensym))) `(,one ,two)))"
assertNil(evaluateString(input))
}
@Test
fun gensymEqualWithMatchedGensymPositions() {
val input = "(gensym-equal (let ((one (gensym))) `(,one ,one))" +
" (let ((one (gensym)) (two (gensym))) `(,one ,one)))"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithComplexMatchedGensymPositions() {
val input = "(gensym-equal (let ((x (gensym)) (y (gensym)) (z (gensym))) `(,x (,y ,z)))" +
" (let ((a (gensym)) (b (gensym)) (c (gensym))) `(,c (,a ,b))))"
assertT(evaluateString(input))
}
@Test
fun gensymEqualWithComplexUnmatchedGensymPositions() {
val input = "(gensym-equal (let ((x (gensym)) (y (gensym)) (z (gensym))) `(,x , y (,z ,z)))" +
" (let ((a (gensym)) (b (gensym)) (c (gensym))) `(,a ,c (,b ,c))))"
assertNil(evaluateString(input))
}
}