Implement append built in function
This commit is contained in:
parent
cd7a8361fc
commit
ac365918b2
|
@ -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>
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))))
|
||||||
|
|
Loading…
Reference in New Issue