diff --git a/src/function/builtin/GENSYM.java b/src/function/builtin/GENSYM.java new file mode 100644 index 0000000..6bb7587 --- /dev/null +++ b/src/function/builtin/GENSYM.java @@ -0,0 +1,36 @@ +package function.builtin; + +import java.math.BigInteger; + +import function.*; +import sexpression.*; + +@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); + } + + public SExpression call(Cons argumentList) { + argumentValidator.validate(argumentList); + + return generateSymbol(); + } + +} diff --git a/src/table/FunctionTable.java b/src/table/FunctionTable.java index 6768295..b5ee66f 100644 --- a/src/table/FunctionTable.java +++ b/src/table/FunctionTable.java @@ -32,6 +32,7 @@ public class FunctionTable { allBuiltIns.add(EXIT.class); allBuiltIns.add(FIRST.class); allBuiltIns.add(FUNCALL.class); + allBuiltIns.add(GENSYM.class); allBuiltIns.add(NUMERIC_GREATER.class); allBuiltIns.add(IF.class); allBuiltIns.add(LAMBDA.class); diff --git a/test/function/builtin/GENSYMTester.java b/test/function/builtin/GENSYMTester.java new file mode 100644 index 0000000..815594f --- /dev/null +++ b/test/function/builtin/GENSYMTester.java @@ -0,0 +1,41 @@ +package function.builtin; + +import static function.builtin.GENSYM.GENSYM_PREFIX; +import static testutil.TestUtilities.*; +import static testutil.TypeAssertions.assertSymbol; + +import org.junit.Test; + +import function.ArgumentValidator.TooManyArgumentsException; +import token.TokenFactory.BadCharacterException; + +public class GENSYMTester { + + @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)"); + } + +} diff --git a/test/testutil/TestUtilities.java b/test/testutil/TestUtilities.java index af14fac..d598880 100644 --- a/test/testutil/TestUtilities.java +++ b/test/testutil/TestUtilities.java @@ -1,7 +1,7 @@ package testutil; import static function.builtin.EVAL.eval; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.*; @@ -37,4 +37,8 @@ public final class TestUtilities { assertEquals(one.toString(), two.toString()); } + public static void assertSExpressionsDoNotMatch(SExpression one, SExpression two) { + assertNotEquals(one.toString(), two.toString()); + } + }