diff --git a/lisp/random/array.lisp b/lisp/random/array.lisp new file mode 100644 index 0000000..b94a19e --- /dev/null +++ b/lisp/random/array.lisp @@ -0,0 +1,13 @@ +(defmacro array (size) + `(let ,(map (lambda (x) (list (fuse 'index x))) (create-indices size)) + (dlambda + (:get (i) (eval (fuse 'index i))) + (:set (i value) (set (fuse 'index i) value))))) + +(defun create-indices (size) + (create-indices-tail (- size 1) ())) + +(defun create-indices-tail (index accumulator) + (if (< index 0) + accumulator + (recur (- index 1) (cons index accumulator)))) diff --git a/src/function/builtin/FUSE.java b/src/function/builtin/FUSE.java new file mode 100644 index 0000000..30731c2 --- /dev/null +++ b/src/function/builtin/FUSE.java @@ -0,0 +1,36 @@ +package function.builtin; + +import function.ArgumentValidator; +import function.FunctionNames; +import function.LispFunction; +import sexpression.Atom; +import sexpression.Cons; +import sexpression.LispString; +import sexpression.SExpression; +import sexpression.Symbol; + +@FunctionNames({ "FUSE" }) +public class FUSE extends LispFunction { + + private static final String SEPARATOR = "-"; + + private ArgumentValidator argumentValidator; + + public FUSE(String name) { + this.argumentValidator = new ArgumentValidator(name); + this.argumentValidator.setExactNumberOfArguments(2); + this.argumentValidator.setFirstArgumentExpectedType(Symbol.class); + this.argumentValidator.setTrailingArgumentExpectedType(Atom.class); + this.argumentValidator.setTrailingArgumentExcludedType(LispString.class); + } + + @Override + public SExpression call(Cons argumentList) { + argumentValidator.validate(argumentList); + Symbol left = (Symbol) argumentList.getFirst(); + Atom right = (Atom) ((Cons) argumentList.getRest()).getFirst(); + + return new Symbol(left.toString() + SEPARATOR + right.toString()); + } + +} diff --git a/src/table/FunctionTable.java b/src/table/FunctionTable.java index 383ce24..7f2d3c0 100644 --- a/src/table/FunctionTable.java +++ b/src/table/FunctionTable.java @@ -14,6 +14,7 @@ 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; @@ -77,6 +78,7 @@ public class FunctionTable { 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); diff --git a/test/function/builtin/special/FUSETest.java b/test/function/builtin/special/FUSETest.java new file mode 100644 index 0000000..77803a3 --- /dev/null +++ b/test/function/builtin/special/FUSETest.java @@ -0,0 +1,58 @@ +package function.builtin.special; + +import static testutil.TestUtilities.assertSExpressionsMatch; +import static testutil.TestUtilities.evaluateString; + +import org.junit.Test; + +import function.ArgumentValidator.BadArgumentTypeException; +import function.ArgumentValidator.TooFewArgumentsException; +import function.ArgumentValidator.TooManyArgumentsException; +import sexpression.Symbol; +import testutil.SymbolAndFunctionCleaner; + +public class FUSETest extends SymbolAndFunctionCleaner { + + @Test + public void fuseSymbolAndNumber() { + assertSExpressionsMatch(new Symbol("A-1"), evaluateString("(fuse 'a 1)")); + } + + @Test + public void fuseTwoSymbols_NeitherQuoted() { + evaluateString("(setq a 'aaa)"); + evaluateString("(setq b 'bbb)"); + assertSExpressionsMatch(new Symbol("AAA-BBB"), evaluateString("(fuse a b)")); + } + + @Test + public void fuseTwoSymbols_OneQuoted() { + evaluateString("(setq b 'bbb)"); + assertSExpressionsMatch(new Symbol("A-BBB"), evaluateString("(fuse 'a b)")); + } + + @Test + public void fuseTwoSymbols_BothQuoted() { + assertSExpressionsMatch(new Symbol("A-B"), evaluateString("(fuse 'a 'b)")); + } + + @Test(expected = TooFewArgumentsException.class) + public void fuseWithTooFewArguments() { + evaluateString("(fuse 'a)"); + } + + @Test(expected = TooManyArgumentsException.class) + public void fuseWithTooManyArguments() { + evaluateString("(fuse 'a 'b 'c)"); + } + + @Test(expected = BadArgumentTypeException.class) + public void fuseWithBadFirstArgumentType() { + evaluateString("(fuse 1 'b)"); + } + + @Test(expected = BadArgumentTypeException.class) + public void fuseWithBadSecondArgumentType() { + evaluateString("(fuse 'a \"b\")"); + } +}