Implement append built in function

This commit is contained in:
Mike Cifelli 2018-03-14 20:32:01 -04:00
parent cd7a8361fc
commit ac365918b2
10 changed files with 166 additions and 61 deletions

View File

@ -12,7 +12,7 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>

View File

@ -37,12 +37,12 @@
accumulator)) accumulator))
accumulator)) accumulator))
(defun append (list-one list-two) ; (defun append (list-one list-two)
(append-tail (reverse list-one) list-two)) ; (append-tail (reverse list-one) list-two))
(defun append-tail (reversed-list list-two) ; (defun append-tail (reversed-list list-two)
(if (null reversed-list) list-two ; (if (null reversed-list) list-two
(recur (rest reversed-list) (cons (car reversed-list) list-two)))) ; (recur (rest reversed-list) (cons (car reversed-list) list-two))))
(defun reverse (the-list) (defun reverse (the-list)
(reverse-tail () the-list)) (reverse-tail () the-list))

View File

@ -13,12 +13,12 @@ import sexpression.SExpression;
@FunctionNames({ "APPLY" }) @FunctionNames({ "APPLY" })
public class APPLY extends LispFunction { public class APPLY extends LispFunction {
private ArgumentValidator argumentValidator;
public static SExpression apply(Cons argumentList) { public static SExpression apply(Cons argumentList) {
return lookupFunction("APPLY").call(argumentList); return lookupFunction("APPLY").call(argumentList);
} }
private ArgumentValidator argumentValidator;
public APPLY(String name) { public APPLY(String name) {
this.argumentValidator = new ArgumentValidator(name); this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2); this.argumentValidator.setExactNumberOfArguments(2);

View File

@ -0,0 +1,67 @@
package function.builtin.cons;
import static sexpression.Nil.NIL;
import function.ArgumentValidator;
import function.FunctionNames;
import function.LispFunction;
import sexpression.Cons;
@FunctionNames({ "APPEND" })
public class APPEND extends LispFunction {
public static Cons append(Cons firstList, Cons secondList) {
Cons appendedLists = copy(firstList);
if (firstList.isNull())
return copy(secondList);
getLastItem(appendedLists).setRest(copy(secondList));
return appendedLists;
}
private static Cons copy(Cons list) {
if (list.isNull())
return NIL;
Cons newList = new Cons(list.getFirst(), NIL);
Cons builder = newList;
for (Cons iterator = (Cons) list.getRest(); iterator.isCons(); iterator = (Cons) iterator.getRest()) {
builder.setRest(new Cons(iterator.getFirst(), NIL));
builder = (Cons) builder.getRest();
}
return newList;
}
private static Cons getLastItem(Cons list) {
Cons tail = list;
while (tail.getRest().isCons())
tail = (Cons) tail.getRest();
return tail;
}
private ArgumentValidator argumentValidator;
public APPEND(String name) {
this.argumentValidator = new ArgumentValidator(name);
this.argumentValidator.setExactNumberOfArguments(2);
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
}
@Override
public Cons call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons rest = (Cons) argumentList.getRest();
Cons firstList = (Cons) argumentList.getFirst();
Cons secondList = (Cons) rest.getFirst();
return append(firstList, secondList);
}
}

View File

@ -3,7 +3,6 @@ package function.builtin.cons;
import static function.builtin.cons.LIST.makeList; import static function.builtin.cons.LIST.makeList;
import static recursion.TailCalls.done; import static recursion.TailCalls.done;
import static recursion.TailCalls.tailCall; import static recursion.TailCalls.tailCall;
import static table.FunctionTable.lookupFunction;
import java.math.BigInteger; import java.math.BigInteger;
@ -18,13 +17,25 @@ import sexpression.LispNumber;
public class LENGTH extends LispFunction { public class LENGTH extends LispFunction {
public static BigInteger getLength(Cons list) { public static BigInteger getLength(Cons list) {
LispNumber length = lookupLength().callWithoutArgumentValidation(makeList(list)); return callWithoutArgumentValidation(makeList(list)).getValue();
return length.getValue();
} }
private static LENGTH lookupLength() { private static LispNumber callWithoutArgumentValidation(Cons argumentList) {
return (LENGTH) lookupFunction("LENGTH"); return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
}
private static TailCall<LispNumber> callTailRecursive(BigInteger accumulatedLength, Cons argumentList) {
Cons list = (Cons) argumentList.getFirst();
Cons restOfList = makeList(list.getRest());
if (list.isNull())
return done(new LispNumber(accumulatedLength));
return tailCall(() -> callTailRecursive(increment(accumulatedLength), restOfList));
}
private static BigInteger increment(BigInteger number) {
return number.add(BigInteger.ONE);
} }
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;
@ -42,22 +53,4 @@ public class LENGTH extends LispFunction {
return callTailRecursive(BigInteger.ZERO, argumentList).invoke(); return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
} }
private LispNumber callWithoutArgumentValidation(Cons argumentList) {
return callTailRecursive(BigInteger.ZERO, argumentList).invoke();
}
private TailCall<LispNumber> callTailRecursive(BigInteger accumulatedLength, Cons argumentList) {
Cons list = (Cons) argumentList.getFirst();
Cons restOfList = makeList(list.getRest());
if (list.isNull())
return done(new LispNumber(accumulatedLength));
return tailCall(() -> callTailRecursive(increment(accumulatedLength), restOfList));
}
private BigInteger increment(BigInteger number) {
return number.add(BigInteger.ONE);
}
} }

View File

@ -21,6 +21,7 @@ import function.builtin.PRINT;
import function.builtin.SET; import function.builtin.SET;
import function.builtin.SYMBOLS; import function.builtin.SYMBOLS;
import function.builtin.SYMBOL_FUNCTION; import function.builtin.SYMBOL_FUNCTION;
import function.builtin.cons.APPEND;
import function.builtin.cons.CONS; import function.builtin.cons.CONS;
import function.builtin.cons.FIRST; import function.builtin.cons.FIRST;
import function.builtin.cons.LENGTH; import function.builtin.cons.LENGTH;
@ -63,6 +64,7 @@ public class FunctionTable {
static { static {
allBuiltIns.add(AND.class); allBuiltIns.add(AND.class);
allBuiltIns.add(APPEND.class);
allBuiltIns.add(APPLY.class); allBuiltIns.add(APPLY.class);
allBuiltIns.add(ATOM.class); allBuiltIns.add(ATOM.class);
allBuiltIns.add(CASE.class); allBuiltIns.add(CASE.class);

View File

@ -8,6 +8,7 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import function.builtin.cons.APPEND;
import sexpression.Cons; import sexpression.Cons;
import sexpression.SExpression; import sexpression.SExpression;
import sexpression.Symbol; import sexpression.Symbol;
@ -50,7 +51,7 @@ public class SymbolTable {
Cons context = NIL; Cons context = NIL;
for (Entry<String, SExpression> binding : getSortedBindings()) for (Entry<String, SExpression> binding : getSortedBindings())
context = append(context, makeList(makeSymbolValuePair(binding))); context = APPEND.append(context, makeList(makeSymbolValuePair(binding)));
return context; return context;
} }
@ -63,19 +64,4 @@ public class SymbolTable {
return new Cons(new Symbol(binding.getKey()), makeList(binding.getValue())); return new Cons(new Symbol(binding.getKey()), makeList(binding.getValue()));
} }
// TODO - extract into built in append function
private Cons append(Cons firstList, Cons secondList) {
if (firstList.isNull())
return secondList; // FIXME - copy
Cons appendedList = firstList; // FIXME - copy
Cons appender = appendedList;
for (; !((Cons) appender.getRest()).isNull(); appender = (Cons) appender.getRest()) {}
appender.setRest(secondList); // FIXME - copy
return appendedList;
}
} }

View File

@ -0,0 +1,68 @@
package function.builtin.cons;
import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;
import org.junit.Test;
import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.TooFewArgumentsException;
import function.ArgumentValidator.TooManyArgumentsException;
import testutil.SymbolAndFunctionCleaner;
public class APPENDTest extends SymbolAndFunctionCleaner {
@Test
public void appendNil() {
String input = "(append () ())";
assertSExpressionsMatch(parseString("nil"), evaluateString(input));
}
@Test
public void appendNilToList() {
String input = "(append () '(1 2 3))";
assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString(input));
}
@Test
public void appendListToNil() {
String input = "(append '(1 2 3) ())";
assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString(input));
}
@Test
public void appendTwoLists() {
String input = "(append '(1 2 3) '(4 5 6))";
assertSExpressionsMatch(parseString("(1 2 3 4 5 6)"), evaluateString(input));
}
@Test
public void appendMakesCopies() {
evaluateString("(setq x '(1 2 3))");
evaluateString("(setq y '(4 5 6))");
evaluateString("(append x y)");
assertSExpressionsMatch(parseString("(1 2 3)"), evaluateString("x"));
assertSExpressionsMatch(parseString("(4 5 6)"), evaluateString("y"));
}
@Test(expected = TooManyArgumentsException.class)
public void appendWithTooManyArguments() {
evaluateString("(append () () ())");
}
@Test(expected = TooFewArgumentsException.class)
public void appendWithTooFewArguments() {
evaluateString("(append ())");
}
@Test(expected = BadArgumentTypeException.class)
public void appendWithBadArgumentType() {
evaluateString("(append 1 '(2))");
}
}

View File

@ -51,16 +51,12 @@ public class CONSTest extends SymbolAndFunctionCleaner {
@Test(expected = TooManyArgumentsException.class) @Test(expected = TooManyArgumentsException.class)
public void consWithTooManyArguments() { public void consWithTooManyArguments() {
String input = "(cons 1 2 3)"; evaluateString("(cons 1 2 3)");
evaluateString(input);
} }
@Test(expected = TooFewArgumentsException.class) @Test(expected = TooFewArgumentsException.class)
public void consWithTooFewArguments() { public void consWithTooFewArguments() {
String input = "(cons 1)"; evaluateString("(cons 1)");
evaluateString(input);
} }
} }

View File

@ -24,13 +24,6 @@
(defun extend-apply (function-name param-list) (defun extend-apply (function-name param-list)
(eval (cons function-name param-list))) (eval (cons function-name param-list)))
(defun append (listA listB)
(cond
((null listA) listB)
(t (cons (first listA) (append (rest listA) listB)))
)
)
(defun second (listA) (first (rest listA))) (defun second (listA) (first (rest listA)))
(defun third (listA) (first (rest (rest listA)))) (defun third (listA) (first (rest (rest listA))))
(defun fourth (listA) (first (rest (rest (rest listA))))) (defun fourth (listA) (first (rest (rest (rest listA)))))