Convert remaining built in functions to kotlin
This commit is contained in:
parent
add24979d5
commit
35406b9f03
|
@ -1,7 +1,7 @@
|
|||
package function
|
||||
|
||||
import error.LispException
|
||||
import function.builtin.cons.LENGTH.getLength
|
||||
import function.builtin.cons.Length.Companion.getLength
|
||||
import sexpression.Cons
|
||||
import sexpression.DisplayName
|
||||
import sexpression.SExpression
|
||||
|
|
|
@ -4,7 +4,7 @@ import error.LispException
|
|||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.cons.List.Companion.makeList
|
||||
import function.builtin.special.Lambda.Lambda.createFunction
|
||||
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
||||
import function.builtin.special.Recur.RecurNotInTailPositionException
|
||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator
|
|||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import function.builtin.Apply.Companion.apply
|
||||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.cons.List.Companion.makeList
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.Nil;
|
||||
import table.FunctionTable;
|
||||
|
||||
@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) FunctionTable.INSTANCE.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.INSTANCE);
|
||||
Cons builder = newList;
|
||||
|
||||
for (Cons iterator = (Cons) list.getRest(); iterator.isCons(); iterator = (Cons) iterator.getRest()) {
|
||||
builder.setRest(new Cons(iterator.getFirst(), Nil.INSTANCE));
|
||||
builder = (Cons) builder.getRest();
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
||||
private Cons getLastItem(Cons list) {
|
||||
Cons tail = list;
|
||||
|
||||
while (tail.getRest().isCons())
|
||||
tail = (Cons) tail.getRest();
|
||||
|
||||
return tail;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package function.builtin.cons
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.Nil
|
||||
import table.FunctionTable
|
||||
|
||||
@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() = FunctionTable.lookupFunction("APPEND") as Append
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
|
||||
@FunctionNames({ "CONS" })
|
||||
public class CONS extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public CONS(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setExactNumberOfArguments(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cons call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
Cons rest = (Cons) argumentList.getRest();
|
||||
SExpression firstArgument = argumentList.getFirst();
|
||||
SExpression secondArgument = rest.getFirst();
|
||||
|
||||
return new Cons(firstArgument, secondArgument);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package function.builtin.cons
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
|
||||
@FunctionNames("CONS")
|
||||
class Construct(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setExactNumberOfArguments(2)
|
||||
}
|
||||
|
||||
override fun call(argumentList: Cons): Cons {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
val rest = argumentList.rest as Cons
|
||||
val firstArgument = argumentList.first
|
||||
val secondArgument = rest.first
|
||||
|
||||
return Cons(firstArgument, secondArgument)
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
|
||||
@FunctionNames({ "FIRST", "CAR" })
|
||||
public class FIRST extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public FIRST(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setExactNumberOfArguments(1);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
Cons argument = (Cons) argumentList.getFirst();
|
||||
|
||||
return argument.getFirst();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package function.builtin.cons
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
|
||||
@FunctionNames("FIRST", "CAR")
|
||||
class First(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setExactNumberOfArguments(1)
|
||||
setEveryArgumentExpectedType(Cons::class.java)
|
||||
}
|
||||
|
||||
override fun call(argumentList: Cons): SExpression {
|
||||
argumentValidator.validate(argumentList)
|
||||
val argument = argumentList.first as Cons
|
||||
|
||||
return argument.first
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import recursion.TailCall;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import table.FunctionTable;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static function.builtin.cons.LIST.makeList;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
@FunctionNames({ "LENGTH" })
|
||||
public class LENGTH extends LispFunction {
|
||||
|
||||
public static BigInteger getLength(Cons list) {
|
||||
LispNumber length = lookupLength().callWithoutArgumentValidation(makeList(list));
|
||||
|
||||
return length.getValue();
|
||||
}
|
||||
|
||||
private static LENGTH lookupLength() {
|
||||
return (LENGTH) FunctionTable.INSTANCE.lookupFunction("LENGTH");
|
||||
}
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
private ArgumentValidator properListValidator;
|
||||
|
||||
public LENGTH(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setExactNumberOfArguments(1);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
||||
this.properListValidator = new ArgumentValidator(name + "|list|");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
properListValidator.validate((Cons) argumentList.getFirst());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.Nil;
|
||||
import sexpression.SExpression;
|
||||
|
||||
@FunctionNames({ "LIST" })
|
||||
public class LIST extends LispFunction {
|
||||
|
||||
public static Cons makeList(SExpression sexpr) {
|
||||
return new Cons(sexpr, Nil.INSTANCE);
|
||||
}
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public LIST(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cons call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return argumentList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package function.builtin.cons
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
import table.FunctionTable
|
||||
import table.FunctionTable.lookupFunction
|
||||
import java.math.BigInteger.ZERO
|
||||
|
||||
@FunctionNames("LENGTH")
|
||||
class Length(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setExactNumberOfArguments(1)
|
||||
setEveryArgumentExpectedType(Cons::class.java)
|
||||
}
|
||||
|
||||
private val properListValidator = ArgumentValidator("$name|list|")
|
||||
|
||||
override fun call(argumentList: Cons): LispNumber {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
val arguments = argumentList.first as Cons
|
||||
properListValidator.validate(arguments)
|
||||
|
||||
return getLength(arguments)
|
||||
}
|
||||
|
||||
private fun getLength(arguments: Cons) =
|
||||
LispNumber(arguments.fold(ZERO) { count, _ -> count.inc() })
|
||||
|
||||
companion object {
|
||||
|
||||
fun getLength(list: Cons) = lookupLength().getLength(list).value
|
||||
private fun lookupLength() = lookupFunction("LENGTH") as Length
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package function.builtin.cons
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.Nil
|
||||
import sexpression.SExpression
|
||||
|
||||
@FunctionNames("LIST")
|
||||
class List(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name)
|
||||
|
||||
override fun call(argumentList: Cons): Cons {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
return argumentList
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun makeList(sexpr: SExpression): Cons {
|
||||
return Cons(sexpr, Nil)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package function.builtin.cons;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
|
||||
@FunctionNames({ "REST", "CDR" })
|
||||
public class REST extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public REST(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setExactNumberOfArguments(1);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
Cons argument = (Cons) argumentList.getFirst();
|
||||
|
||||
return argument.getRest();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package function.builtin.cons
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
|
||||
@FunctionNames("REST", "CDR")
|
||||
class Rest(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setExactNumberOfArguments(1)
|
||||
setEveryArgumentExpectedType(Cons::class.java)
|
||||
}
|
||||
|
||||
override fun call(argumentList: Cons): SExpression {
|
||||
argumentValidator.validate(argumentList)
|
||||
val argument = argumentList.first as Cons
|
||||
|
||||
return argument.rest
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import error.LispException;
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
@FunctionNames({ "/" })
|
||||
public class DIVIDE extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
private MathFunction mathFunction;
|
||||
|
||||
public DIVIDE(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setMinimumNumberOfArguments(1);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
this.mathFunction = new MathFunction(this::getReciprocal, this::divide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
try {
|
||||
return mathFunction.callTailRecursive(argumentList).invoke();
|
||||
} catch (ArithmeticException e) {
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
}
|
||||
|
||||
private LispNumber getReciprocal(LispNumber number) {
|
||||
return new LispNumber(BigInteger.ONE.divide(number.getValue()));
|
||||
}
|
||||
|
||||
private LispNumber divide(LispNumber number1, LispNumber number2) {
|
||||
return new LispNumber(number1.getValue().divide(number2.getValue()));
|
||||
}
|
||||
|
||||
public static class DivideByZeroException extends LispException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "divide by zero";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package function.builtin.math
|
||||
|
||||
import error.LispException
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
import java.math.BigInteger.ONE
|
||||
|
||||
@FunctionNames("/")
|
||||
class Divide(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setMinimumNumberOfArguments(1)
|
||||
setEveryArgumentExpectedType(LispNumber::class.java)
|
||||
}
|
||||
|
||||
private val mathFunction = MathFunction(this::getReciprocal, this::divide)
|
||||
|
||||
override fun call(argumentList: Cons): LispNumber {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
try {
|
||||
return mathFunction.callTailRecursive(argumentList)
|
||||
} catch (e: ArithmeticException) {
|
||||
throw DivideByZeroException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getReciprocal(number: LispNumber) =
|
||||
LispNumber(ONE / number.value)
|
||||
|
||||
private fun divide(number1: LispNumber, number2: LispNumber) =
|
||||
LispNumber(number1.value / number2.value)
|
||||
|
||||
class DivideByZeroException : LispException() {
|
||||
|
||||
override val message = "divide by zero"
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
@FunctionNames({ "-" })
|
||||
public class MINUS extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
private MathFunction mathFunction;
|
||||
|
||||
public MINUS(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setMinimumNumberOfArguments(1);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
this.mathFunction = new MathFunction(this::additiveInverse, this::subtract);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return mathFunction.callTailRecursive(argumentList).invoke();
|
||||
}
|
||||
|
||||
private LispNumber additiveInverse(LispNumber number) {
|
||||
return new LispNumber(BigInteger.ZERO.subtract(number.getValue()));
|
||||
}
|
||||
|
||||
private LispNumber subtract(LispNumber number1, LispNumber number2) {
|
||||
return new LispNumber(number1.getValue().subtract(number2.getValue()));
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import error.LispException;
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
||||
@FunctionNames({ "MOD", "MODULO", "%" })
|
||||
public class MODULO extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public MODULO(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setExactNumberOfArguments(2);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
LispNumber dividend = (LispNumber) argumentList.getFirst();
|
||||
LispNumber divisor = (LispNumber) ((Cons) argumentList.getRest()).getFirst();
|
||||
|
||||
try {
|
||||
return new LispNumber(dividend.getValue().mod(divisor.getValue()));
|
||||
} catch (ArithmeticException e) {
|
||||
throw new ModulusNotPositiveException();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModulusNotPositiveException extends LispException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "modulus not positive";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
@FunctionNames({ "*" })
|
||||
public class MULTIPLY extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
private MathFunction mathFunction;
|
||||
|
||||
public MULTIPLY(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
this.mathFunction = new MathFunction(number -> number, this::multiply);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return mathFunction.callTailRecursive(new Cons(LispNumber.Companion.getONE(), argumentList)).invoke();
|
||||
}
|
||||
|
||||
private LispNumber multiply(LispNumber number1, LispNumber number2) {
|
||||
return new LispNumber(number1.getValue().multiply(number2.getValue()));
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import recursion.TailCall;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
class MathFunction {
|
||||
|
||||
Function<LispNumber, LispNumber> singleValueOperation;
|
||||
BiFunction<LispNumber, LispNumber, LispNumber> multipleValueOperation;
|
||||
|
||||
public MathFunction(Function<LispNumber, LispNumber> singleValueOperation,
|
||||
BiFunction<LispNumber, LispNumber, LispNumber> multipleValueOperation) {
|
||||
this.singleValueOperation = singleValueOperation;
|
||||
this.multipleValueOperation = multipleValueOperation;
|
||||
}
|
||||
|
||||
public TailCall<LispNumber> callTailRecursive(Cons argumentList) {
|
||||
Cons remainingArguments = (Cons) argumentList.getRest();
|
||||
SExpression firstArgument = argumentList.getFirst();
|
||||
LispNumber number1 = (LispNumber) firstArgument;
|
||||
|
||||
if (remainingArguments.isNull())
|
||||
return done(singleValueOperation.apply(number1));
|
||||
|
||||
SExpression secondArgument = remainingArguments.getFirst();
|
||||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
LispNumber operationResult = multipleValueOperation.apply(number1, number2);
|
||||
SExpression remainingNumbers = remainingArguments.getRest();
|
||||
|
||||
if (remainingNumbers.isNull())
|
||||
return done(operationResult);
|
||||
|
||||
return tailCall(() -> callTailRecursive(new Cons(operationResult, remainingNumbers)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package function.builtin.math
|
||||
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
|
||||
internal class MathFunction(private val singleValueOperation: (LispNumber) -> LispNumber,
|
||||
private val multipleValueOperation: (LispNumber, LispNumber) -> LispNumber) {
|
||||
|
||||
tailrec fun callTailRecursive(argumentList: Cons): LispNumber {
|
||||
val remainingArguments = argumentList.rest as Cons
|
||||
val firstArgument = argumentList.first
|
||||
val number1 = firstArgument as LispNumber
|
||||
|
||||
if (remainingArguments.isNull)
|
||||
return singleValueOperation(number1)
|
||||
|
||||
val secondArgument = remainingArguments.first
|
||||
val number2 = secondArgument as LispNumber
|
||||
val operationResult = multipleValueOperation(number1, number2)
|
||||
val remainingNumbers = remainingArguments.rest
|
||||
|
||||
return if (remainingNumbers.isNull)
|
||||
operationResult
|
||||
else
|
||||
callTailRecursive(Cons(operationResult, remainingNumbers))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package function.builtin.math
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
|
||||
import java.math.BigInteger.ZERO
|
||||
|
||||
@FunctionNames("-")
|
||||
class Minus(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setMinimumNumberOfArguments(1)
|
||||
setEveryArgumentExpectedType(LispNumber::class.java)
|
||||
}
|
||||
|
||||
private val mathFunction: MathFunction = MathFunction(this::additiveInverse, this::subtract)
|
||||
|
||||
override fun call(argumentList: Cons): LispNumber {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
return mathFunction.callTailRecursive(argumentList)
|
||||
}
|
||||
|
||||
private fun additiveInverse(number: LispNumber) =
|
||||
LispNumber(ZERO - number.value)
|
||||
|
||||
private fun subtract(number1: LispNumber, number2: LispNumber) =
|
||||
LispNumber(number1.value - number2.value)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package function.builtin.math
|
||||
|
||||
import error.LispException
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
import sexpression.SExpression
|
||||
|
||||
@FunctionNames("MOD", "MODULO", "%")
|
||||
class Module(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setExactNumberOfArguments(2)
|
||||
setEveryArgumentExpectedType(LispNumber::class.java)
|
||||
}
|
||||
|
||||
override fun call(argumentList: Cons): SExpression {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
val dividend = argumentList.first as LispNumber
|
||||
val divisor = (argumentList.rest as Cons).first as LispNumber
|
||||
|
||||
try {
|
||||
return LispNumber(dividend.value.mod(divisor.value))
|
||||
} catch (e: ArithmeticException) {
|
||||
throw ModulusNotPositiveException()
|
||||
}
|
||||
}
|
||||
|
||||
class ModulusNotPositiveException : LispException() {
|
||||
|
||||
override val message = "modulus not positive"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package function.builtin.math
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
import sexpression.LispNumber.Companion.ONE
|
||||
|
||||
@FunctionNames("*")
|
||||
class Multiply(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setEveryArgumentExpectedType(LispNumber::class.java)
|
||||
}
|
||||
|
||||
private val mathFunction: MathFunction = MathFunction({ it }, this::multiply)
|
||||
|
||||
override fun call(argumentList: Cons): LispNumber {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
return mathFunction.callTailRecursive(Cons(ONE, argumentList))
|
||||
}
|
||||
|
||||
private fun multiply(number1: LispNumber, number2: LispNumber) =
|
||||
LispNumber(number1.value * number2.value)
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
@FunctionNames({ "+" })
|
||||
public class PLUS extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
private MathFunction mathFunction;
|
||||
|
||||
public PLUS(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
this.mathFunction = new MathFunction(number -> number, this::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return mathFunction.callTailRecursive(new Cons(LispNumber.Companion.getZERO(), argumentList)).invoke();
|
||||
}
|
||||
|
||||
private LispNumber add(LispNumber number1, LispNumber number2) {
|
||||
return new LispNumber(number1.getValue().add(number2.getValue()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package function.builtin.math
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
|
||||
@FunctionNames("+")
|
||||
class Plus(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setEveryArgumentExpectedType(LispNumber::class.java)
|
||||
}
|
||||
|
||||
private val mathFunction: MathFunction = MathFunction({ it }, this::add)
|
||||
|
||||
override fun call(argumentList: Cons): LispNumber {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
return mathFunction.callTailRecursive(Cons(LispNumber.ZERO, argumentList))
|
||||
}
|
||||
|
||||
private fun add(number1: LispNumber, number2: LispNumber) =
|
||||
LispNumber(number1.value + number2.value)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package function.builtin.math;
|
||||
|
||||
import function.ArgumentValidator;
|
||||
import function.FunctionNames;
|
||||
import function.LispFunction;
|
||||
import function.builtin.math.DIVIDE.DivideByZeroException;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
import sexpression.SExpression;
|
||||
|
||||
@FunctionNames({ "REM", "REMAINDER" })
|
||||
public class REMAINDER extends LispFunction {
|
||||
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public REMAINDER(String name) {
|
||||
this.argumentValidator = new ArgumentValidator(name);
|
||||
this.argumentValidator.setExactNumberOfArguments(2);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
LispNumber dividend = (LispNumber) argumentList.getFirst();
|
||||
LispNumber divisor = (LispNumber) ((Cons) argumentList.getRest()).getFirst();
|
||||
|
||||
try {
|
||||
return new LispNumber(dividend.getValue().remainder(divisor.getValue()));
|
||||
} catch (ArithmeticException e) {
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package function.builtin.math
|
||||
|
||||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import function.builtin.math.Divide.DivideByZeroException
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
import sexpression.SExpression
|
||||
|
||||
@FunctionNames("REM", "REMAINDER")
|
||||
class Remainder(name: String) : LispFunction() {
|
||||
|
||||
private val argumentValidator = ArgumentValidator(name).apply {
|
||||
setExactNumberOfArguments(2)
|
||||
setEveryArgumentExpectedType(LispNumber::class.java)
|
||||
}
|
||||
|
||||
override fun call(argumentList: Cons): SExpression {
|
||||
argumentValidator.validate(argumentList)
|
||||
|
||||
val dividend = argumentList.first as LispNumber
|
||||
val divisor = (argumentList.rest as Cons).first as LispNumber
|
||||
|
||||
try {
|
||||
return LispNumber(dividend.value % divisor.value)
|
||||
} catch (e: ArithmeticException) {
|
||||
throw DivideByZeroException()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,14 +3,10 @@ package function.builtin.predicate
|
|||
import function.ArgumentValidator
|
||||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import recursion.TailCall
|
||||
import sexpression.Cons
|
||||
import sexpression.LispNumber
|
||||
import sexpression.Nil
|
||||
import sexpression.SExpression
|
||||
|
||||
import recursion.TailCalls.done
|
||||
import recursion.TailCalls.tailCall
|
||||
import sexpression.Symbol.Companion.T
|
||||
|
||||
@FunctionNames("<")
|
||||
|
|
|
@ -5,7 +5,7 @@ import error.LispWarning
|
|||
import function.ArgumentValidator
|
||||
import function.LispSpecialFunction
|
||||
import function.UserDefinedFunction
|
||||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.cons.List.Companion.makeList
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
import sexpression.Symbol
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package function.builtin.special
|
||||
|
||||
import function.FunctionNames
|
||||
import function.UserDefinedFunction
|
||||
import function.UserDefinedSpecialFunction
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package function.builtin.special
|
||||
|
||||
import function.FunctionNames
|
||||
import function.UserDefinedFunction
|
||||
import function.UserDefinedMacro
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator
|
|||
import function.FunctionNames
|
||||
import function.LispSpecialFunction
|
||||
import function.UserDefinedFunction
|
||||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.cons.List.Companion.makeList
|
||||
import sexpression.Cons
|
||||
import sexpression.LambdaExpression
|
||||
import sexpression.SExpression
|
||||
|
|
|
@ -5,7 +5,7 @@ import function.FunctionNames
|
|||
import function.LispSpecialFunction
|
||||
import function.builtin.Eval.Companion.eval
|
||||
import function.builtin.Set.Companion.set
|
||||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.cons.List.Companion.makeList
|
||||
import sexpression.Cons
|
||||
import sexpression.SExpression
|
||||
import sexpression.Symbol
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package table
|
||||
|
||||
import function.builtin.cons.APPEND.append
|
||||
import function.builtin.cons.LIST.makeList
|
||||
import function.builtin.cons.Append.Companion.append
|
||||
import function.builtin.cons.List.Companion.makeList
|
||||
import sexpression.Cons
|
||||
import sexpression.Nil
|
||||
import sexpression.SExpression
|
||||
|
|
|
@ -11,7 +11,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
|||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class APPENDTest extends SymbolAndFunctionCleaner {
|
||||
public class AppendTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void appendNil() {
|
|
@ -11,7 +11,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
|||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class CONSTest extends SymbolAndFunctionCleaner {
|
||||
public class ConstructTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void consWithNilValues() {
|
|
@ -10,7 +10,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
|||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class FIRSTTest extends SymbolAndFunctionCleaner {
|
||||
public class FirstTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void firstOfNil() {
|
|
@ -11,7 +11,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
|||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class LENGTHTest extends SymbolAndFunctionCleaner {
|
||||
public class LengthTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void lengthOfNil() {
|
|
@ -3,12 +3,12 @@ package function.builtin.cons;
|
|||
import org.junit.Test;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
||||
import static function.builtin.cons.LIST.makeList;
|
||||
import static function.builtin.cons.List.makeList;
|
||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class LISTTest extends SymbolAndFunctionCleaner {
|
||||
public class ListTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void listWithNoArguments() {
|
|
@ -10,7 +10,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
|||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class RESTTest extends SymbolAndFunctionCleaner {
|
||||
public class RestTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void restOfNil() {
|
|
@ -2,7 +2,7 @@ package function.builtin.math;
|
|||
|
||||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.builtin.math.DIVIDE.DivideByZeroException;
|
||||
import function.builtin.math.Divide.DivideByZeroException;
|
||||
import org.junit.Test;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
||||
|
@ -11,7 +11,7 @@ import static testutil.TestUtilities.assertSExpressionsMatch;
|
|||
import static testutil.TestUtilities.evaluateString;
|
||||
import static testutil.TestUtilities.parseString;
|
||||
|
||||
public class DIVIDETest extends SymbolAndFunctionCleaner {
|
||||
public class DivideTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void divideWithOne() {
|
|
@ -9,7 +9,7 @@ import testutil.SymbolAndFunctionCleaner;
|
|||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
|
||||
public class MINUSTest extends SymbolAndFunctionCleaner {
|
||||
public class MinusTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void minusWithOneNumber() {
|
|
@ -3,7 +3,7 @@ package function.builtin.math;
|
|||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.math.MODULO.ModulusNotPositiveException;
|
||||
import function.builtin.math.Module.ModulusNotPositiveException;
|
||||
import org.junit.Test;
|
||||
import sexpression.LispNumber;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
@ -12,7 +12,7 @@ import static testutil.TestUtilities.assertIsErrorWithMessage;
|
|||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
|
||||
public class MODULOTest extends SymbolAndFunctionCleaner {
|
||||
public class ModuleTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void mod() {
|
|
@ -8,7 +8,7 @@ import testutil.SymbolAndFunctionCleaner;
|
|||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
|
||||
public class MULTIPLYTest extends SymbolAndFunctionCleaner {
|
||||
public class MultiplyTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void multiplyWithNoArguments() {
|
|
@ -8,7 +8,7 @@ import testutil.SymbolAndFunctionCleaner;
|
|||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
|
||||
public class PLUSTest extends SymbolAndFunctionCleaner {
|
||||
public class PlusTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void plusWithNoArguments() {
|
|
@ -3,7 +3,7 @@ package function.builtin.math;
|
|||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.math.DIVIDE.DivideByZeroException;
|
||||
import function.builtin.math.Divide.DivideByZeroException;
|
||||
import org.junit.Test;
|
||||
import sexpression.LispNumber;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
@ -11,7 +11,7 @@ import testutil.SymbolAndFunctionCleaner;
|
|||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
||||
import static testutil.TestUtilities.evaluateString;
|
||||
|
||||
public class REMAINDERTest extends SymbolAndFunctionCleaner {
|
||||
public class RemainderTest extends SymbolAndFunctionCleaner {
|
||||
|
||||
@Test
|
||||
public void rem() {
|
Loading…
Reference in New Issue