package function.builtin.cons import function.ArgumentValidator import function.FunctionNames import function.LispFunction import sexpression.Cons import sexpression.Nil import table.FunctionTable.lookupFunction @FunctionNames("APPEND") class Append(name: String) : LispFunction() { private val argumentValidator = ArgumentValidator(name).apply { setExactNumberOfArguments(2) setEveryArgumentExpectedType(Cons::class.java) } private val firstListValidator = ArgumentValidator("$name|first-list|") override fun call(argumentList: Cons): Cons { argumentValidator.validate(argumentList) val rest = argumentList.rest as Cons val firstList = argumentList.first as Cons val secondList = rest.first as Cons return appendLists(firstList, secondList) } private fun appendLists(firstList: Cons, secondList: Cons): Cons { firstListValidator.validate(firstList) if (firstList.isNull) return secondList val appendedLists = copy(firstList) appendedLists.last().rest = secondList return appendedLists } private fun copy(list: Cons): Cons { val newList = Cons(list.first, Nil) var builder = newList list.asSequence().drop(1).forEach { builder.rest = Cons(it.first, Nil) builder = builder.rest as Cons } return newList } companion object { @JvmStatic fun append(firstList: Cons, secondList: Cons): Cons { return lookupAppend().appendLists(firstList, secondList) } private fun lookupAppend() = lookupFunction("APPEND") as Append } }