package function.builtin.cons; import static sexpression.Nil.NIL; import static table.FunctionTable.lookupFunction; 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) { return lookupAppend().appendLists(firstList, secondList); } private static APPEND lookupAppend() { return (APPEND) lookupFunction("APPEND"); } private ArgumentValidator argumentValidator; private ArgumentValidator firstListValidator; public APPEND(String name) { this.argumentValidator = new ArgumentValidator(name); this.argumentValidator.setExactNumberOfArguments(2); this.argumentValidator.setEveryArgumentExpectedType(Cons.class); this.firstListValidator = new ArgumentValidator(name + "|first-list|"); } @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 appendLists(firstList, secondList); } private Cons appendLists(Cons firstList, Cons secondList) { firstListValidator.validate(firstList); if (firstList.isNull()) return secondList; Cons appendedLists = copy(firstList); getLastItem(appendedLists).setRest(secondList); return appendedLists; } private Cons copy(Cons list) { 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 Cons getLastItem(Cons list) { Cons tail = list; while (tail.getRest().isCons()) tail = (Cons) tail.getRest(); return tail; } }