Convert numeric equal to kotlin

This commit is contained in:
Mike Cifelli 2018-05-05 16:46:53 -04:00
parent e672247738
commit 28f2351654
14 changed files with 451 additions and 360 deletions

124
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

View File

@ -14,7 +14,7 @@ import table.ExecutionContext;
import table.FunctionTable; import table.FunctionTable;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static function.builtin.special.LAMBDA.Lambda; import static function.builtin.special.Lambda.Lambda;
import static java.text.MessageFormat.format; import static java.text.MessageFormat.format;
import static sexpression.Nil.NIL; import static sexpression.Nil.NIL;
import static sexpression.Symbol.T; import static sexpression.Symbol.T;

View File

@ -1,31 +0,0 @@
package function.builtin;
import environment.RuntimeEnvironment;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
import sexpression.SExpression;
import static sexpression.Nil.NIL;
@FunctionNames({ "EXIT" })
public class EXIT extends LispFunction {
private ArgumentValidator argumentValidator;
private RuntimeEnvironment environment;
public EXIT(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setMaximumNumberOfArguments(0);
this.environment = RuntimeEnvironment.INSTANCE;
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
environment.terminateSuccessfully();
return NIL;
}
}

View File

@ -0,0 +1,26 @@
package function.builtin
import environment.RuntimeEnvironment
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import sexpression.Cons
import sexpression.Nil.NIL
import sexpression.SExpression
@FunctionNames("EXIT")
class Exit(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name)
init {
this.argumentValidator.setMaximumNumberOfArguments(0)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
RuntimeEnvironment.terminateSuccessfully()
return NIL
}
}

View File

@ -1,54 +0,0 @@
package function.builtin.predicate;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import recursion.TailCall;
import sexpression.Cons;
import sexpression.LispNumber;
import sexpression.SExpression;
import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall;
import static sexpression.Nil.NIL;
import static sexpression.Symbol.T;
@FunctionNames({ "=" })
public class NUMERIC_EQUAL extends LispFunction {
private ArgumentValidator argumentValidator;
public NUMERIC_EQUAL(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setMinimumNumberOfArguments(1);
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
}
@Override
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
return callTailRecursive(argumentList).invoke();
}
private TailCall<SExpression> callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getRest();
if (remainingArguments.isNull())
return done(T);
SExpression firstArgument = argumentList.getFirst();
LispNumber number1 = (LispNumber) firstArgument;
SExpression secondArgument = remainingArguments.getFirst();
LispNumber number2 = (LispNumber) secondArgument;
if (!isEqual(number1, number2))
return done(NIL);
return tailCall(() -> callTailRecursive(remainingArguments));
}
private boolean isEqual(LispNumber number1, LispNumber number2) {
return number1.getValue().equals(number2.getValue());
}
}

View File

@ -0,0 +1,39 @@
package function.builtin.predicate
import function.ArgumentValidator
import function.FunctionNames
import function.LispFunction
import sexpression.Cons
import sexpression.LispNumber
import sexpression.Nil.NIL
import sexpression.SExpression
import sexpression.Symbol.T
@FunctionNames("=")
class NumericEqual(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name)
init {
this.argumentValidator.setMinimumNumberOfArguments(1)
this.argumentValidator.setEveryArgumentExpectedType(LispNumber::class.java)
}
override fun call(argumentList: Cons): SExpression {
argumentValidator.validate(argumentList)
return callTailRecursive(argumentList)
}
private tailrec fun callTailRecursive(argumentList: Cons): SExpression {
val remainingArguments = argumentList.rest as Cons
if (remainingArguments.isNull)
return T
val number1 = argumentList.first as LispNumber
val number2 = remainingArguments.first as LispNumber
return if (number1.value != number2.value) NIL else callTailRecursive(remainingArguments)
}
}

View File

@ -11,7 +11,7 @@ import sexpression.SExpression
import sexpression.Symbol import sexpression.Symbol
@FunctionNames("LAMBDA", "Λ") @FunctionNames("LAMBDA", "Λ")
class LAMBDA(name: String) : LispSpecialFunction() { class Lambda(name: String) : LispSpecialFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name) private val argumentValidator: ArgumentValidator = ArgumentValidator(name)
private val lambdaListValidator: ArgumentValidator = ArgumentValidator("$name|lambda-list|") private val lambdaListValidator: ArgumentValidator = ArgumentValidator("$name|lambda-list|")
@ -59,7 +59,7 @@ class LAMBDA(name: String) : LispSpecialFunction() {
lambdaValidator.setEveryArgumentExpectedType(Cons::class.java) lambdaValidator.setEveryArgumentExpectedType(Cons::class.java)
lambdaValidator.validate(makeList(rest)) lambdaValidator.validate(makeList(rest))
val lambda = LAMBDA("LAMBDA").call(rest as Cons) val lambda = Lambda("LAMBDA").call(rest as Cons)
return lambda.function return lambda.function
} }

View File

@ -5,7 +5,7 @@ import function.FunctionNames
import function.LispFunction import function.LispFunction
import function.builtin.APPLY import function.builtin.APPLY
import function.builtin.EVAL import function.builtin.EVAL
import function.builtin.EXIT import function.builtin.Exit
import function.builtin.FUNCALL import function.builtin.FUNCALL
import function.builtin.FUSE import function.builtin.FUSE
import function.builtin.GENSYM import function.builtin.GENSYM
@ -32,7 +32,7 @@ import function.builtin.predicate.EQUAL
import function.builtin.predicate.GENSYM_EQUAL import function.builtin.predicate.GENSYM_EQUAL
import function.builtin.predicate.LISTP import function.builtin.predicate.LISTP
import function.builtin.predicate.NULL import function.builtin.predicate.NULL
import function.builtin.predicate.NUMERIC_EQUAL import function.builtin.predicate.NumericEqual
import function.builtin.predicate.NUMERIC_GREATER import function.builtin.predicate.NUMERIC_GREATER
import function.builtin.predicate.NUMERIC_LESS import function.builtin.predicate.NUMERIC_LESS
import function.builtin.special.AND import function.builtin.special.AND
@ -42,7 +42,7 @@ import function.builtin.special.DEFINE_SPECIAL
import function.builtin.special.DEFMACRO import function.builtin.special.DEFMACRO
import function.builtin.special.DEFUN import function.builtin.special.DEFUN
import function.builtin.special.IF import function.builtin.special.IF
import function.builtin.special.LAMBDA import function.builtin.special.Lambda
import function.builtin.special.LET import function.builtin.special.LET
import function.builtin.special.LET_STAR import function.builtin.special.LET_STAR
import function.builtin.special.OR import function.builtin.special.OR
@ -72,9 +72,9 @@ object FunctionTable {
allBuiltIns.add(DIVIDE::class.java) allBuiltIns.add(DIVIDE::class.java)
allBuiltIns.add(EQ::class.java) allBuiltIns.add(EQ::class.java)
allBuiltIns.add(EQUAL::class.java) allBuiltIns.add(EQUAL::class.java)
allBuiltIns.add(NUMERIC_EQUAL::class.java) allBuiltIns.add(NumericEqual::class.java)
allBuiltIns.add(EVAL::class.java) allBuiltIns.add(EVAL::class.java)
allBuiltIns.add(EXIT::class.java) allBuiltIns.add(Exit::class.java)
allBuiltIns.add(FIRST::class.java) allBuiltIns.add(FIRST::class.java)
allBuiltIns.add(FUNCALL::class.java) allBuiltIns.add(FUNCALL::class.java)
allBuiltIns.add(FUSE::class.java) allBuiltIns.add(FUSE::class.java)
@ -82,7 +82,7 @@ object FunctionTable {
allBuiltIns.add(GENSYM_EQUAL::class.java) allBuiltIns.add(GENSYM_EQUAL::class.java)
allBuiltIns.add(NUMERIC_GREATER::class.java) allBuiltIns.add(NUMERIC_GREATER::class.java)
allBuiltIns.add(IF::class.java) allBuiltIns.add(IF::class.java)
allBuiltIns.add(LAMBDA::class.java) allBuiltIns.add(Lambda::class.java)
allBuiltIns.add(LENGTH::class.java) allBuiltIns.add(LENGTH::class.java)
allBuiltIns.add(NUMERIC_LESS::class.java) allBuiltIns.add(NUMERIC_LESS::class.java)
allBuiltIns.add(LET::class.java) allBuiltIns.add(LET::class.java)

View File

@ -1,58 +0,0 @@
package function.builtin
import environment.RuntimeEnvironment
import function.ArgumentValidator.TooManyArgumentsException
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.evaluateString
import java.util.HashSet
class EXITTest : SymbolAndFunctionCleaner() {
private val environment: RuntimeEnvironment
private var indicatorSet: MutableSet<String>? = null
init {
this.environment = RuntimeEnvironment
}
private fun assertTerminated() {
assertTrue(indicatorSet!!.contains(TERMINATED))
}
private fun assertNotTerminated() {
assertFalse(indicatorSet!!.contains(TERMINATED))
}
override fun additionalSetUp() {
indicatorSet = HashSet()
environment.reset()
environment.terminationFunction = { indicatorSet!!.add(TERMINATED) }
}
override fun additionalTearDown() {
environment.reset()
}
@Test
fun exitWorks() {
evaluateString("(exit)")
assertTerminated()
}
@Test
fun exitNotCalled_IndicatorSetIsClean() {
assertNotTerminated()
}
@Test(expected = TooManyArgumentsException::class)
fun exitWithTooManyArguments() {
evaluateString("(exit 1)")
}
companion object {
private val TERMINATED = "terminated"
}
}

View File

@ -0,0 +1,53 @@
package function.builtin
import environment.RuntimeEnvironment
import function.ArgumentValidator.TooManyArgumentsException
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.evaluateString
import java.util.HashSet
class ExitTest : SymbolAndFunctionCleaner() {
companion object {
private const val TERMINATED = "terminated"
}
private var indicatorSet = HashSet<String>()
private fun assertTerminated() {
assertThat(indicatorSet.contains(TERMINATED)).isTrue()
}
private fun assertNotTerminated() {
assertThat(indicatorSet.contains(TERMINATED)).isFalse()
}
override fun additionalSetUp() {
indicatorSet.clear()
RuntimeEnvironment.reset()
RuntimeEnvironment.terminationFunction = { indicatorSet.add(TERMINATED) }
}
override fun additionalTearDown() {
RuntimeEnvironment.reset()
}
@Test
fun `exit works`() {
evaluateString("(exit)")
assertTerminated()
}
@Test
fun `no termination when exit not called`() {
assertNotTerminated()
}
@Test
fun `too many arguments`() {
assertThrows(TooManyArgumentsException::class.java) { evaluateString("(exit 1)") }
}
}

View File

@ -1,58 +0,0 @@
package function.builtin.predicate;
import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooFewArgumentsException;
import org.junit.Test;
import testutil.SymbolAndFunctionCleaner;
import static testutil.TestUtilities.evaluateString;
import static testutil.TypeAssertions.assertNil;
import static testutil.TypeAssertions.assertT;
public class NUMERIC_EQUALTest extends SymbolAndFunctionCleaner {
@Test
public void numericEqualWithOneNumber() {
String input = "(= 1)";
assertT(evaluateString(input));
}
@Test
public void numericEqualWithEqualNumbers() {
String input = "(= 1 1)";
assertT(evaluateString(input));
}
@Test
public void numericEqualWithNonEqualNumbers() {
String input = "(= 1 2)";
assertNil(evaluateString(input));
}
@Test
public void numericEqualWithManyEqualNumbers() {
String input = "(= 4 4 4 4 4 4 4 4 4 4)";
assertT(evaluateString(input));
}
@Test
public void numericEqualWithManyNonEqualNumbers() {
String input = "(= 4 4 4 4 5 4 4 4 4 4)";
assertNil(evaluateString(input));
}
@Test(expected = BadArgumentTypeException.class)
public void numericEqualWithNonNumbers() {
evaluateString("(= 'x 'x)");
}
@Test(expected = TooFewArgumentsException.class)
public void numericEqualWithTooFewArguments() {
evaluateString("(=)");
}
}

View File

@ -0,0 +1,51 @@
package function.builtin.predicate
import function.ArgumentValidator.BadArgumentTypeException
import function.ArgumentValidator.TooFewArgumentsException
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.evaluateString
import testutil.TypeAssertions.assertNil
import testutil.TypeAssertions.assertT
@TestInstance(PER_CLASS)
class NumericEqualTest : SymbolAndFunctionCleaner() {
@Test
fun `one number`() {
assertT(evaluateString("(= 1)"))
}
@Test
fun `equal numbers`() {
assertT(evaluateString("(= 1 1)"))
}
@Test
fun `unequal numbers`() {
assertNil(evaluateString("(= 1 2)"))
}
@Test
fun `many equal numbers`() {
assertT(evaluateString("(= 4 4 4 4 4 4 4 4 4 4)"))
}
@Test
fun `many unequal numbers`() {
assertNil(evaluateString("(= 4 4 4 4 5 4 4 4 4 4)"))
}
@Test
fun `bad argument types`() {
assertThrows(BadArgumentTypeException::class.java) { evaluateString("(= 'x 'x)") }
}
@Test
fun `too few arguments`() {
assertThrows(TooFewArgumentsException::class.java) { evaluateString("(=)") }
}
}

View File

@ -1,150 +0,0 @@
package function.builtin.special;
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 sexpression.LispNumber;
import sexpression.Symbol;
import testutil.SymbolAndFunctionCleaner;
import static function.builtin.special.LAMBDA.Lambda;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static sexpression.LispNumber.ONE;
import static sexpression.Nil.NIL;
import static sexpression.Symbol.T;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;
public class LAMBDATest extends SymbolAndFunctionCleaner {
@Test
public void lambda() {
String input = "(lambda (x) x)";
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
}
@Test
public void lambdaSymbol() {
String input = "(λ (x) x)";
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input));
}
@Test
public void lambdaWithNoBody() {
String input = "(lambda ())";
assertSExpressionsMatch(parseString("(LAMBDA ())"), evaluateString(input));
}
@Test
public void lambdaExpressionIsLambdaExpression() {
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, new Cons(NIL, NIL)));
assertTrue(Lambda.isLambdaExpression(lambdaExpression));
}
@Test
public void somethingElseIsNotLambdaExpression() {
assertFalse(Lambda.isLambdaExpression(T));
}
@Test
public void createLambdaExpression() {
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, new Cons(NIL, NIL)));
assertSExpressionsMatch(parseString("(:LAMBDA () ())"),
Lambda.createFunction(lambdaExpression).getLambdaExpression());
}
@Test(expected = DottedArgumentListException.class)
public void lambdaWithDottedArgumentList() {
String input = "(apply 'lambda (cons '(x) 1))";
evaluateString(input);
}
@Test(expected = DottedArgumentListException.class)
public void lambdaWithDottedLambdaList() {
String input = "(funcall 'lambda (cons 'a 'b) ())";
evaluateString(input);
}
@Test(expected = DottedArgumentListException.class)
public void createFunctionWithDottedArgumentList() {
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, ONE));
Lambda.createFunction(lambdaExpression);
}
@Test(expected = BadArgumentTypeException.class)
public void createFunctionWithNonList() {
Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), ONE);
Lambda.createFunction(lambdaExpression);
}
@Test(expected = BadArgumentTypeException.class)
public void lambdaWithNonSymbolParameter() {
evaluateString("(lambda (1) ())");
}
@Test(expected = TooFewArgumentsException.class)
public void lambdaWithTooFewArguments() {
evaluateString("(lambda)");
}
@Test
public void anonymousLambdaCall() {
String input = "((lambda (x) x) 203)";
assertSExpressionsMatch(new LispNumber("203"), evaluateString(input));
}
@Test
public void anonymousLambdaCallWithMultipleArguments() {
String input = "((lambda (x y) (+ x y)) 203 2)";
assertSExpressionsMatch(new LispNumber("205"), evaluateString(input));
}
@Test
public void anonymousLambdaCallWithSymbol() {
String input = "((λ (x) (+ x 1)) 3)";
assertSExpressionsMatch(new LispNumber("4"), evaluateString(input));
}
@Test(expected = TooFewArgumentsException.class)
public void anonymousLambdaCallWithTooFewArguments() {
evaluateString("((lambda (x) x))");
}
@Test(expected = TooManyArgumentsException.class)
public void anonymousLambdaCallWithTooManyArguments() {
evaluateString("((lambda (x y) x) 1 2 3)");
}
@Test(expected = UndefinedFunctionException.class)
public void badAnonymousFunctionCall() {
evaluateString("((bad-lambda (x y) x) 1 2 3)");
}
@Test
public void lexicalClosure() {
evaluateString("(setq increment-count (let ((counter 0)) (lambda () (setq counter (+ 1 counter)))))");
assertSExpressionsMatch(parseString("1"), evaluateString("(funcall increment-count)"));
assertSExpressionsMatch(parseString("2"), evaluateString("(funcall increment-count)"));
assertSExpressionsMatch(parseString("3"), evaluateString("(funcall increment-count)"));
assertSExpressionsMatch(parseString("4"), evaluateString("(funcall increment-count)"));
}
}

View File

@ -0,0 +1,149 @@
package function.builtin.special
import function.ArgumentValidator.BadArgumentTypeException
import function.ArgumentValidator.DottedArgumentListException
import function.ArgumentValidator.TooFewArgumentsException
import function.ArgumentValidator.TooManyArgumentsException
import function.builtin.EVAL.UndefinedFunctionException
import function.builtin.special.Lambda.Lambda.createFunction
import function.builtin.special.Lambda.Lambda.isLambdaExpression
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import sexpression.Cons
import sexpression.LispNumber
import sexpression.LispNumber.ONE
import sexpression.Nil.NIL
import sexpression.Symbol
import sexpression.Symbol.T
import testutil.SymbolAndFunctionCleaner
import testutil.TestUtilities.assertSExpressionsMatch
import testutil.TestUtilities.evaluateString
import testutil.TestUtilities.parseString
class LambdaTest : SymbolAndFunctionCleaner() {
@Test
fun `lambda is evaluated`() {
val input = "(lambda (x) x)"
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input))
}
@Test
fun `lambda symbol is evaluated`() {
val input = "(λ (x) x)"
assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input))
}
@Test
fun `lambda with no body`() {
val input = "(lambda ())"
assertSExpressionsMatch(parseString("(LAMBDA ())"), evaluateString(input))
}
@Test
fun `lambda expression is a lambda expression`() {
val lambdaExpression = Cons(Symbol("LAMBDA"), Cons(NIL, Cons(NIL, NIL)))
assertThat(isLambdaExpression(lambdaExpression)).isTrue()
}
@Test
fun `something else is not a lambda expression`() {
assertThat(isLambdaExpression(T)).isFalse()
}
@Test
fun `create lambda expression`() {
val lambdaExpression = Cons(Symbol("LAMBDA"), Cons(NIL, Cons(NIL, NIL)))
assertSExpressionsMatch(parseString("(:LAMBDA () ())"), createFunction(lambdaExpression).lambdaExpression)
}
@Test
fun `dotted argument list`() {
val input = "(apply 'lambda (cons '(x) 1))"
assertThrows(DottedArgumentListException::class.java) { evaluateString(input) }
}
@Test
fun `dotted lambda list`() {
val input = "(funcall 'lambda (cons 'a 'b) ())"
assertThrows(DottedArgumentListException::class.java) { evaluateString(input) }
}
@Test
fun `create function with dotted argument list`() {
val lambdaExpression = Cons(Symbol("LAMBDA"), Cons(NIL, ONE))
assertThrows(DottedArgumentListException::class.java) { createFunction(lambdaExpression) }
}
@Test
fun `create function with non list`() {
val lambdaExpression = Cons(Symbol("LAMBDA"), ONE)
assertThrows(BadArgumentTypeException::class.java) { createFunction(lambdaExpression) }
}
@Test
fun `bad argument type`() {
assertThrows(BadArgumentTypeException::class.java) { evaluateString("(lambda (1) ())") }
}
@Test
fun `too few arguments`() {
assertThrows(TooFewArgumentsException::class.java) { evaluateString("(lambda)") }
}
@Test
fun `anonymous lambda call`() {
val input = "((lambda (x) x) 203)"
assertSExpressionsMatch(LispNumber("203"), evaluateString(input))
}
@Test
fun `anonymous lambda call with multiple arguments`() {
val input = "((lambda (x y) (+ x y)) 203 2)"
assertSExpressionsMatch(LispNumber("205"), evaluateString(input))
}
@Test
fun `anonymous lambda call with symbol`() {
val input = "((λ (x) (+ x 1)) 3)"
assertSExpressionsMatch(LispNumber("4"), evaluateString(input))
}
@Test
fun `anonymous lambda call with too few arguments`() {
assertThrows(TooFewArgumentsException::class.java) { evaluateString("((lambda (x) x))") }
}
@Test
fun `anonymous lambda call with too many arguments`() {
assertThrows(TooManyArgumentsException::class.java) { evaluateString("((lambda (x y) x) 1 2 3)") }
}
@Test
fun `bad anonymous function call`() {
assertThrows(UndefinedFunctionException::class.java) { evaluateString("((bad-lambda (x y) x) 1 2 3)") }
}
@Test
fun `lexical closure`() {
evaluateString("(setq increment-count (let ((counter 0)) (lambda () (setq counter (+ 1 counter)))))")
assertSExpressionsMatch(parseString("1"), evaluateString("(funcall increment-count)"))
assertSExpressionsMatch(parseString("2"), evaluateString("(funcall increment-count)"))
assertSExpressionsMatch(parseString("3"), evaluateString("(funcall increment-count)"))
assertSExpressionsMatch(parseString("4"), evaluateString("(funcall increment-count)"))
}
}