diff --git a/fn/rational.fn b/fn/rational.fn
new file mode 100644
index 0000000..3c1511f
--- /dev/null
+++ b/fn/rational.fn
@@ -0,0 +1,16 @@
+print(#( "1 * 2", 1 * 2 ));
+print(#( "1 + 2", 1 + 2 ));
+print(#( "2 / 3 + 4", 2 / 3 + 4 ));
+print(#( "4 + 2 / 3 + 4", 4 + 2 / 3 + 4 ));
+print(#( "4 + 2 / -3 + 4", 4 + 2 / -3 + 4 ));
+print(#( "2 / 3 + 4 / 5", 2 / 3 + 4 / 5 ));
+print(#( "(2 / 3) * (4 / 5)", (2 / 3) * (4 / 5) ));
+print(#( "(6 / 5)", (6 / 5) ));
+print(#( "(2 / 3) / (6 / 5)", (2 / 3) / (6 / 5) ));
+print(#( "(2 / 3) % (4 / 5)", (2 / 3) % (4 / 5) ));
+print(#( "1914882942 ** 10", 1914882942 ** 10 ));
+print(#( "1/3 % 8", 1/3 % 8 ));
+print(#( "-9 % 8", -9 % 8 ));
+print(#( "(1/2) ** 2", (1/2) ** 2 ));
+print(#( "(1914882942 ** 5 / 5) % (2 / 3)", (1914882942 ** 5 / 5) % (2 / 3) ))
+
diff --git a/fn/test2.fn b/fn/test2.fn
new file mode 100644
index 0000000..ea945ff
--- /dev/null
+++ b/fn/test2.fn
@@ -0,0 +1,2 @@
+print((2 / 3) + 4)
+
diff --git a/src/arithmetic.c b/src/arithmetic.c
new file mode 100644
index 0000000..063b561
--- /dev/null
+++ b/src/arithmetic.c
@@ -0,0 +1,616 @@
+/*
+ * CEKF - VM supporting amb
+ * Copyright (C) 2022-2023 Bill Hails
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+#include "common.h"
+#include "bigint.h"
+#include "arithmetic.h"
+#include "cekf.h"
+#include "debug.h"
+
+#ifdef DEBUG_ARITHMETIC
+# include "debugging_on.h"
+#else
+# include "debugging_off.h"
+#endif
+
+#define NUMERATOR 0
+#define DENOMINATOR 1
+
+int rational_flag = 0;
+
+IntegerBinOp add;
+IntegerBinOp sub;
+IntegerBinOp mul;
+IntegerBinOp divide;
+IntegerBinOp power;
+IntegerBinOp modulo;
+
+static IntegerBinOp int_add;
+static IntegerBinOp int_sub;
+static IntegerBinOp int_mul;
+static IntegerBinOp int_divide;
+static IntegerBinOp int_power;
+static IntegerBinOp int_modulo;
+
+static IntegerBinOp int_gcd;
+static CmpBinOp int_cmp;
+static voidOp int_neg;
+static boolOp int_isneg;
+
+static Value One = {
+ .type = VALUE_TYPE_STDINT,
+ .val = VALUE_VAL_STDINT(1)
+};
+
+static Value Zero = {
+ .type = VALUE_TYPE_STDINT,
+ .val = VALUE_VAL_STDINT(0)
+};
+
+#ifdef DEBUG_ARITHMETIC
+static void ppNumber(Value number) {
+ switch (number.type) {
+ case VALUE_TYPE_STDINT:
+ eprintf("%d", number.val.stdint);
+ break;
+ case VALUE_TYPE_BIGINT:
+ eprintf("[");
+ fprintBigInt(errout, number.val.bigint);
+ eprintf("]");
+ break;
+ case VALUE_TYPE_RATIONAL:
+ ppNumber(number.val.vec->values[0]);
+ eprintf("/");
+ ppNumber(number.val.vec->values[1]);
+ break;
+ default:
+ eprintf("??? %d ???", number.type);
+ }
+}
+#endif
+
+////////////////////////////////
+// fixed size integer operations
+////////////////////////////////
+
+static Value intValue(int i) {
+ Value val;
+ val.type = VALUE_TYPE_STDINT;
+ val.val = VALUE_VAL_STDINT(i);
+ return val;
+}
+
+#ifdef SAFETY_CHECKS
+# define ASSERT_STDINT(x) ASSERT((x).type == VALUE_TYPE_STDINT)
+#else
+# define ASSERT_STDINT(x)
+#endif
+
+static int littleCmp(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ return left.val.stdint < right.val.stdint ? -1 : left.val.stdint == right.val.stdint ? 0 : 1;
+}
+
+static Value littleAdd(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ return intValue(left.val.stdint + right.val.stdint);
+}
+
+static Value littleMul(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ return intValue(left.val.stdint * right.val.stdint);
+}
+
+static Value littleSub(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ return intValue(left.val.stdint - right.val.stdint);
+}
+
+static Value littleDivide(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ if (littleCmp(right, Zero) == 0) {
+ cant_happen("attempted div zero");
+ }
+ return intValue(left.val.stdint / right.val.stdint);
+}
+
+static Value littlePower(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ return intValue(pow(left.val.stdint, right.val.stdint));
+}
+
+static Value littleModulo(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ if (littleCmp(right, Zero) == 0) {
+ cant_happen("attempted mod zero");
+ }
+ return intValue(left.val.stdint % right.val.stdint);
+}
+
+static int gcd (int a, int b) {
+ int i = 0, min_num = a, gcd = 1;
+ if (a > b) {
+ min_num = b;
+ }
+ for (i = 1; i <= min_num; i++) {
+ if (a % i == 0 && b % i == 0) {
+ gcd = i;
+ }
+ }
+ return gcd;
+}
+
+static Value littleGcd(Value left, Value right) {
+ ASSERT_STDINT(left);
+ ASSERT_STDINT(right);
+ return intValue(gcd(left.val.stdint, right.val.stdint));
+}
+
+static void littleNeg(Value v) {
+ ASSERT_STDINT(v);
+ v.val.stdint = -v.val.stdint;
+}
+
+static bool littleIsNeg(Value v) {
+ ASSERT_STDINT(v);
+ return v.val.stdint < 0;
+}
+
+////////////////////////////////////
+// arbitrary size integer operations
+////////////////////////////////////
+
+#ifdef SAFETY_CHECKS
+# define ASSERT_BIGINT(x) ASSERT((x).type == VALUE_TYPE_BIGINT)
+#else
+# define ASSERT_BIGINT(x)
+#endif
+
+static int bigCmp(Value left, Value right) {
+ ENTER(bigCmp);
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ LEAVE(bigCmp);
+ return cmpBigInt(left.val.bigint, right.val.bigint);
+}
+
+static Value bigIntValue(BigInt *i) {
+ Value val;
+ val.type = VALUE_TYPE_BIGINT;
+ val.val = VALUE_VAL_BIGINT(i);
+ return val;
+}
+
+static Value bigAdd(Value left, Value right) {
+ ENTER(bigAdd);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ BigInt *result = addBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(result);
+ Value res = bigIntValue(result);
+ LEAVE(bigAdd);
+ UNPROTECT(save);
+ return res;
+}
+
+static Value bigMul(Value left, Value right) {
+ ENTER(bigMul);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ BigInt *result = mulBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(result);
+ Value res = bigIntValue(result);
+ LEAVE(bigMul);
+ UNPROTECT(save);
+ return res;
+}
+
+static Value bigSub(Value left, Value right) {
+ ENTER(bigSub);
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ BigInt *result = subBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(result);
+ Value res = bigIntValue(result);
+ LEAVE(bigSub);
+ UNPROTECT(save);
+ return res;
+}
+
+static Value bigDivide(Value left, Value right) {
+ ENTER(bigDivide);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ if (bigCmp(right, Zero) == 0) {
+ cant_happen("attempted div zero");
+ }
+ BigInt *result = divBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(result);
+ Value res = bigIntValue(result);
+ protectValue(res);
+ LEAVE(bigDivide);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value bigPower(Value left, Value right) {
+ ENTER(bigPower);
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ BigInt *result = powBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(result);
+ Value res = bigIntValue(result);
+ LEAVE(bigPower);
+ UNPROTECT(save);
+ return res;
+}
+
+static Value bigModulo(Value left, Value right) {
+ ENTER(bigModulo);
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ if (bigCmp(right, Zero) == 0) {
+ cant_happen("attempted mod zero");
+ }
+ BigInt *result = modBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(result);
+ Value res = bigIntValue(result);
+ LEAVE(bigModulo);
+ UNPROTECT(save);
+ return res;
+}
+
+static Value bigGcd(Value left, Value right) {
+ ENTER(bigGcd);
+ ASSERT_BIGINT(left);
+ ASSERT_BIGINT(right);
+ BigInt *gcd = gcdBigInt(left.val.bigint, right.val.bigint);
+ int save = PROTECT(gcd);
+ Value res = bigIntValue(gcd);
+ LEAVE(bigGcd);
+ UNPROTECT(save);
+ return res;
+}
+
+static void bigNeg(Value v) {
+ ASSERT_BIGINT(v);
+ negateBigInt(v.val.bigint);
+}
+
+static bool bigIsNeg(Value v) {
+ ASSERT_BIGINT(v);
+ return isNegBigInt(v.val.bigint);
+}
+
+///////////////////////////////////////
+// unspecified size rational operations
+///////////////////////////////////////
+
+#ifdef SAFETY_CHECKS
+# define ASSERT_RATIONAL(x) ASSERT((x).type == VALUE_TYPE_RATIONAL)
+#else
+# define ASSERT_RATIONAL(x)
+#endif
+
+static Value makeRational(Value numerator, Value denominator) {
+ Vec *vec = newVec(2);
+ vec->values[NUMERATOR] = numerator;
+ vec->values[DENOMINATOR] = denominator;
+ Value res = {
+ .type = VALUE_TYPE_RATIONAL,
+ .val = VALUE_VAL_RATIONAL(vec)
+ };
+ return res;
+}
+
+static Value ratOp(Value left, Value right, ParameterizedBinOp op, IntegerBinOp intOp, bool simplify) {
+ ENTER(ratOp);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ Value res;
+ int save = protectValue(left);
+ protectValue(right);
+ if (left.type == VALUE_TYPE_RATIONAL) {
+ if (right.type == VALUE_TYPE_RATIONAL) {
+ // both rational
+ res = op(intOp, left, right);
+ protectValue(res);
+ } else {
+ // only left rational
+ right = makeRational(right, One);
+ protectValue(right);
+ res = ratOp(left, right, op, intOp, false);
+ protectValue(res);
+ }
+ } else if (right.type == VALUE_TYPE_RATIONAL) {
+ // only right rational
+ left = makeRational(left, One);
+ protectValue(left);
+ res = ratOp(left, right, op, intOp, false);
+ protectValue(res);
+ } else {
+ // neither rational
+ if (simplify) {
+ res = intOp(left, right);
+ protectValue(res);
+ } else {
+ left = makeRational(left, One);
+ protectValue(left);
+ right = makeRational(right, One);
+ protectValue(right);
+ res = ratOp(left, right, op, intOp, false);
+ protectValue(res);
+ }
+ }
+ LEAVE(ratOp);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value ratSimplify(Value numerator, Value denominator) {
+ ENTER(ratSimplify);
+ IFDEBUG(ppNumber(numerator));
+ IFDEBUG(ppNumber(denominator));
+ Value gcd = int_gcd(numerator, denominator);
+ int save = protectValue(gcd);
+ Value res;
+ if (int_cmp(gcd, One) != 0) {
+ numerator = int_divide(numerator, gcd);
+ protectValue(numerator);
+ denominator = int_divide(denominator, gcd);
+ protectValue(denominator);
+ }
+ if (int_isneg(denominator)) {
+ int_neg(numerator);
+ int_neg(denominator);
+ }
+ if (int_cmp(denominator, One) == 0) {
+ res = numerator;
+ } else {
+ res = makeRational(numerator, denominator);
+ protectValue(res);
+ }
+ LEAVE(ratSimplify);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value _rat_add_sub(IntegerBinOp base_op, Value left, Value right) {
+ ENTER(rat_add_sub);
+ ASSERT_RATIONAL(left);
+ ASSERT_RATIONAL(right);
+ Value a1b2 =
+ int_mul(left.val.vec->values[NUMERATOR],
+ right.val.vec->values[DENOMINATOR]);
+ int save = protectValue(a1b2);
+ Value a2b1 =
+ int_mul(left.val.vec->values[DENOMINATOR],
+ right.val.vec->values[NUMERATOR]);
+ protectValue(a2b1);
+ Value numerator = base_op(a1b2, a2b1);
+ protectValue(numerator);
+ Value denominator =
+ int_mul(left.val.vec->values[DENOMINATOR],
+ right.val.vec->values[DENOMINATOR]);
+ protectValue(denominator);
+ Value res = ratSimplify(numerator, denominator);
+ UNPROTECT(save);
+ LEAVE(rat_add_sub);
+ return res;
+}
+
+static Value ratAdd(Value left, Value right) {
+ ENTER(ratAdd);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ Value res = ratOp(left, right, _rat_add_sub, int_add, true);
+ LEAVE(ratAdd);
+ return res;
+}
+
+static Value ratSub(Value left, Value right) {
+ ENTER(ratSub);
+ Value res = ratOp(left, right, _rat_add_sub, int_sub, true);
+ LEAVE(ratSub);
+ return res;
+}
+
+static Value _rat_mul(IntegerBinOp base_op, Value left, Value right) {
+ ENTER(_rat_mul);
+ ASSERT_RATIONAL(left);
+ ASSERT_RATIONAL(right);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ Value numerator =
+ base_op(left.val.vec->values[NUMERATOR],
+ right.val.vec->values[NUMERATOR]);
+ int save = protectValue(numerator);
+ Value denominator =
+ base_op(left.val.vec->values[DENOMINATOR],
+ right.val.vec->values[DENOMINATOR]);
+ protectValue(denominator);
+ Value res = ratSimplify(numerator, denominator);
+ protectValue(res);
+ LEAVE(_rat_mul);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value ratMul(Value left, Value right) {
+ ENTER(ratMul);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ Value res = ratOp(left, right, _rat_mul, int_mul, true);
+ int save = protectValue(res);
+ LEAVE(ratMul);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value _rat_div(IntegerBinOp base_op, Value left, Value right) {
+ ENTER(_rat_div);
+ ASSERT_RATIONAL(left);
+ ASSERT_RATIONAL(right);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ Value newRight = makeRational(right.val.vec->values[DENOMINATOR], right.val.vec->values[NUMERATOR]);
+ int save = protectValue(newRight);
+ Value res = _rat_mul(base_op, left, newRight);
+ protectValue(res);
+ LEAVE(_rat_div);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value ratDivide(Value left, Value right) {
+ ENTER(ratDivide);
+ // N.B. int_mul not int_div
+ Value res = ratOp(left, right, _rat_div, int_mul, false);
+ int save = protectValue(res);
+ LEAVE(ratDivide);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value ratModulo(Value left, Value right) {
+ ENTER(ratModulo);
+ Value res = ratOp(left, right, _rat_add_sub, int_modulo, true);
+ LEAVE(ratModulo);
+ return res;
+}
+
+static Value _ratPower(Value left, Value right) {
+ ENTER(_ratPower);
+ ASSERT_RATIONAL(left);
+ Value numerator = left.val.vec->values[NUMERATOR];
+ Value denominator = left.val.vec->values[DENOMINATOR];
+ numerator = int_power(numerator, right);
+ int save = protectValue(numerator);
+ denominator = int_power(denominator, right);
+ protectValue(denominator);
+ Value res = ratSimplify(numerator, denominator);
+ protectValue(res);
+ LEAVE(_ratPower);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+static Value ratPower(Value left, Value right) {
+ ENTER(ratPower);
+ LEAVE(ratPower);
+ IFDEBUG(ppNumber(left));
+ IFDEBUG(ppNumber(right));
+ Value res;
+ int save = protectValue(left);
+ protectValue(right);
+ if (left.type == VALUE_TYPE_RATIONAL) {
+ if (right.type == VALUE_TYPE_RATIONAL) {
+ cant_happen("raising numbers to a rational power not supported yet");
+ } else {
+ // only left rational
+ res = _ratPower(left, right);
+ protectValue(res);
+ }
+ } else if (right.type == VALUE_TYPE_RATIONAL) {
+ cant_happen("raising numbers to a rational power not supported yet");
+ } else {
+ // neither rational
+ res = int_power(left, right);
+ protectValue(res);
+ }
+ LEAVE(ratPower);
+ IFDEBUG(ppNumber(res));
+ UNPROTECT(save);
+ return res;
+}
+
+void init_arithmetic() {
+ if (bigint_flag) {
+ int_add = bigAdd;
+ int_mul = bigMul;
+ int_sub = bigSub;
+ int_divide = bigDivide;
+ int_power = bigPower;
+ int_modulo = bigModulo;
+ int_gcd = bigGcd;
+ int_cmp = bigCmp;
+ int_neg = bigNeg;
+ int_isneg = bigIsNeg;
+ BigInt *zero = bigIntFromInt(0);
+ Zero.type = VALUE_TYPE_BIGINT;
+ Zero.val = VALUE_VAL_BIGINT(zero);
+ BigInt *one = bigIntFromInt(1);
+ One.type = VALUE_TYPE_BIGINT;
+ One.val = VALUE_VAL_BIGINT(one);
+ } else {
+ int_add = littleAdd;
+ int_mul = littleMul;
+ int_sub = littleSub;
+ int_divide = littleDivide;
+ int_power = littlePower;
+ int_modulo = littleModulo;
+ int_gcd = littleGcd;
+ int_cmp = littleCmp;
+ int_neg = littleNeg;
+ int_isneg = littleIsNeg;
+ }
+
+ if (rational_flag) {
+ add = ratAdd;
+ sub = ratSub;
+ mul = ratMul;
+ divide = ratDivide;
+ power = ratPower;
+ modulo = ratModulo;
+ } else {
+ add = int_add;
+ mul = int_mul;
+ sub = int_sub;
+ divide = int_divide;
+ power = int_power;
+ modulo = int_modulo;
+ }
+}
+
+void markArithmetic() {
+ markValue(Zero);
+ markValue(One);
+}
diff --git a/src/arithmetic.h b/src/arithmetic.h
new file mode 100644
index 0000000..21249f2
--- /dev/null
+++ b/src/arithmetic.h
@@ -0,0 +1,42 @@
+#ifndef cekf_arithmetic_h
+#define cekf_arithmetic_h
+/*
+ * CEKF - VM supporting amb
+ * Copyright (C) 2022-2023 Bill Hails
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "value.h"
+
+typedef int (*CmpBinOp)(Value, Value);
+typedef Value (*IntegerBinOp)(Value, Value);
+typedef Value (*IntegerUnOp)(Value);
+typedef Value (*ParameterizedBinOp)(IntegerBinOp, Value, Value);
+typedef void (*voidOp)(Value);
+typedef bool (*boolOp)(Value);
+
+extern int rational_flag;
+
+extern IntegerBinOp add;
+extern IntegerBinOp sub;
+extern IntegerBinOp mul;
+extern IntegerBinOp divide;
+extern IntegerBinOp power;
+extern IntegerBinOp modulo;
+
+void init_arithmetic(void);
+void markArithmetic(void);
+
+#endif
diff --git a/src/bigint.c b/src/bigint.c
index 1b64c59..d0c1db7 100644
--- a/src/bigint.c
+++ b/src/bigint.c
@@ -1288,6 +1288,13 @@ BigInt *fakeBigInt(int little) {
return x;
}
+BigInt *bigIntFromInt(int i) {
+ bigint c;
+ bigint_init(&c);
+ bigint_from_int(&c, i);
+ return newBigInt(c);
+}
+
void markBigInt(BigInt *x) {
if (x == NULL)
return;
@@ -1380,6 +1387,10 @@ BigInt *modBigInt(BigInt *a, BigInt *b) {
return _opBigInt(bigint_mod, a, b);
}
+BigInt *gcdBigInt(BigInt *a, BigInt *b) {
+ return _opBigInt(bigint_gcd, a, b);
+}
+
BigInt *powBigInt(BigInt *a, BigInt *b) {
int save = PROTECT(a);
PROTECT(b);
@@ -1409,3 +1420,19 @@ void dumpBigInt(FILE *fp, BigInt *big) {
}
fprintf(fp, "\n");
}
+
+void negateBigInt(BigInt *b) {
+ if (bigint_flag) {
+ bigint_negate(&b->bi);
+ } else {
+ b->little = -b->little;
+ }
+}
+
+bool isNegBigInt(BigInt *b) {
+ if (bigint_flag) {
+ return b->bi.neg != 0;
+ } else {
+ return b->little < 0;
+ }
+}
diff --git a/src/bigint.h b/src/bigint.h
index e36483a..429dc9b 100644
--- a/src/bigint.h
+++ b/src/bigint.h
@@ -50,6 +50,7 @@ extern "C" {
BigInt *newBigInt(bigint bi);
BigInt *fakeBigInt(int little);
+ BigInt *bigIntFromInt(int c);
void markBigInt(BigInt *bi);
void freeBigInt(BigInt *bi);
void printBigInt(BigInt *bi, int depth);
@@ -65,7 +66,10 @@ extern "C" {
BigInt *divBigInt(BigInt *a, BigInt *b);
BigInt *modBigInt(BigInt *a, BigInt *b);
BigInt *powBigInt(BigInt *a, BigInt *b);
+ BigInt *gcdBigInt(BigInt *a, BigInt *b);
void bigint_fprint(FILE *f, bigint * bi);
+ void negateBigInt(BigInt *b);
+ bool isNegBigInt(BigInt *b);
// END CEKF additions
diff --git a/src/cekf.c b/src/cekf.c
index cbb1dfb..551fe38 100644
--- a/src/cekf.c
+++ b/src/cekf.c
@@ -132,6 +132,7 @@ void markValue(Value x) {
case VALUE_TYPE_CONT:
markKont(x.val.kont);
break;
+ case VALUE_TYPE_RATIONAL:
case VALUE_TYPE_VEC:
markVec(x.val.vec);
break;
@@ -217,6 +218,9 @@ void markFail(Fail *x) {
void markCekfObj(Header *h) {
switch (h->type) {
+ case OBJTYPE_VEC:
+ markVec((Vec *) h);
+ break;
case OBJTYPE_CLO:
markClo((Clo *) h);
break;
@@ -233,7 +237,7 @@ void markCekfObj(Header *h) {
markValueList((ValueList *) h);
break;
default:
- cant_happen("unrecognised header type in markCekfObj");
+ cant_happen("unrecognised header type %d in markCekfObj", h->type);
}
}
@@ -276,3 +280,19 @@ void freeCekfObj(Header *h) {
cant_happen("unrecognised header type in freeCekfObj");
}
}
+
+int protectValue(Value v) {
+ switch (v.type) {
+ case VALUE_TYPE_CLO:
+ return PROTECT(v.val.clo);
+ case VALUE_TYPE_CONT:
+ return PROTECT(v.val.kont);
+ case VALUE_TYPE_VEC:
+ case VALUE_TYPE_RATIONAL:
+ return PROTECT(v.val.vec);
+ case VALUE_TYPE_BIGINT:
+ return PROTECT(v.val.bigint);
+ default:
+ return PROTECT(NULL);
+ }
+}
diff --git a/src/cekf.h b/src/cekf.h
index be26193..44ea116 100644
--- a/src/cekf.h
+++ b/src/cekf.h
@@ -97,6 +97,8 @@ typedef struct Vec {
struct Value values[0];
} Vec;
+int protectValue(Value v);
+
void snapshotClo(Stack *stack, struct Clo *target, int letRecOffset);
void patchClo(Stack *stack, struct Clo *target);
void snapshotKont(Stack *stack, struct Kont *target);
diff --git a/src/common.h b/src/common.h
index 9f0372b..83966c7 100644
--- a/src/common.h
+++ b/src/common.h
@@ -54,6 +54,7 @@
// # define DEBUG_ALLOC
// # define DEBUG_PRINT_GENERATOR
// # define DEBUG_PRINT_COMPILER
+// # define DEBUG_ARITHMETIC
// define this to turn on additional safety checks for things that shouldn't but just possibly might happen
# define SAFETY_CHECKS
# endif
@@ -61,7 +62,9 @@
# ifndef __GNUC__
# define __attribute__(x)
# endif
+
# define errout stdout
+
void _cant_happen(char *file, int line, const char *message, ...)
__attribute__((noreturn, format(printf, 3, 4)));
void can_happen(const char *message, ...)
@@ -73,4 +76,10 @@ bool hadErrors(void);
# define PAD_WIDTH 2
+#define ASSERT(assertion) do {\
+ if (!(assertion)) { \
+ cant_happen("assertion failed " #assertion); \
+ } \
+} while (0);
+
#endif
diff --git a/src/debug.c b/src/debug.c
index 713ef83..cbc8141 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -74,6 +74,7 @@ void printContainedValue(Value x, int depth) {
case VALUE_TYPE_CONT:
printKont(x.val.kont, depth);
break;
+ case VALUE_TYPE_RATIONAL:
case VALUE_TYPE_VEC:
printPad(depth);
printVec(x.val.vec);
diff --git a/src/main.c b/src/main.c
index f1a4024..bce70ef 100644
--- a/src/main.c
+++ b/src/main.c
@@ -43,6 +43,7 @@
#include "tc_analyze.h"
#include "tc_debug.h"
#include "tpmc_mermaid.h"
+#include "arithmetic.h"
int report_flag = 0;
static int help_flag = 0;
@@ -54,6 +55,7 @@ static void processArgs(int argc, char *argv[]) {
while (1) {
static struct option long_options[] = {
{ "bigint", no_argument, &bigint_flag, 1 },
+ { "rational", no_argument, &rational_flag, 1 },
{ "report", no_argument, &report_flag, 1 },
{ "anf", no_argument, &anf_flag, 1 },
{ "dump-bytecode", no_argument, &dump_bytecode_flag, 1 },
@@ -81,6 +83,7 @@ static void processArgs(int argc, char *argv[]) {
if (help_flag) {
printf("%s",
"--bigint use arbitrary precision integers\n"
+ "--rational use precision-preserving rational numbers\n"
"--report report statistics\n"
"--anf display the generated ANF\n"
"--lambda=function display the intermediate code\n"
diff --git a/src/memory.c b/src/memory.c
index af4292d..7608f2c 100644
--- a/src/memory.c
+++ b/src/memory.c
@@ -31,6 +31,7 @@
#include "cekf.h"
#include "module.h"
#include "symbol.h"
+#include "arithmetic.h"
static int bytesAllocated = 0;
static int nextGC = 0;
@@ -371,6 +372,7 @@ static void markProtected() {
static void mark() {
markCEKF();
markProtected();
+ markArithmetic();
#ifdef DEBUG_LOG_GC
eprintf("starting markVarTable\n");
#endif
diff --git a/src/step.c b/src/step.c
index 782ef53..c56788a 100644
--- a/src/step.c
+++ b/src/step.c
@@ -23,13 +23,13 @@
#include
#include
#include
-#include
#include "common.h"
#include "debug.h"
#include "cekf.h"
#include "step.h"
#include "hash.h"
+#include "arithmetic.h"
int dump_bytecode_flag = 0;
@@ -45,7 +45,6 @@ int dump_bytecode_flag = 0;
static void step();
static Value lookup(int frame, int offset);
-static int protectValue(Value v);
void putValue(Value x);
static CEKF state;
@@ -131,141 +130,11 @@ static inline int readCurrentOffsetAt(int i) {
return readOffsetAt(&state.B, state.C, i);
}
-static Value intValue(int i) {
- Value value;
- value.type = VALUE_TYPE_STDINT;
- value.val = VALUE_VAL_STDINT(i);
- return value;
-}
-
-static Value bigIntValue(BigInt *i) {
- Value value;
- value.type = VALUE_TYPE_BIGINT;
- value.val = VALUE_VAL_BIGINT(i);
- return value;
-}
-
static bool truthy(Value v) {
return !((v.type == VALUE_TYPE_STDINT && v.val.stdint == 0)
|| v.type == VALUE_TYPE_VOID);
}
-typedef Value (*IntegerBinOp)(Value, Value);
-
-static IntegerBinOp add;
-
-static Value bigAdd(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_BIGINT);
- assert(right.type == VALUE_TYPE_BIGINT);
-#endif
- BigInt *result = addBigInt(left.val.bigint, right.val.bigint);
- return bigIntValue(result);
-}
-
-static Value littleAdd(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_STDINT);
- assert(right.type == VALUE_TYPE_STDINT);
-#endif
- return intValue(left.val.stdint + right.val.stdint);
-}
-
-static IntegerBinOp mul;
-
-static Value bigMul(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_BIGINT);
- assert(right.type == VALUE_TYPE_BIGINT);
-#endif
- BigInt *result = mulBigInt(left.val.bigint, right.val.bigint);
- return bigIntValue(result);
-}
-
-static Value littleMul(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_STDINT);
- assert(right.type == VALUE_TYPE_STDINT);
-#endif
- return intValue(left.val.stdint * right.val.stdint);
-}
-
-static IntegerBinOp sub;
-
-static Value bigSub(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_BIGINT);
- assert(right.type == VALUE_TYPE_BIGINT);
-#endif
- BigInt *result = subBigInt(left.val.bigint, right.val.bigint);
- return bigIntValue(result);
-}
-
-static Value littleSub(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_STDINT);
- assert(right.type == VALUE_TYPE_STDINT);
-#endif
- return intValue(left.val.stdint - right.val.stdint);
-}
-
-static IntegerBinOp divide;
-
-static Value bigDivide(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_BIGINT);
- assert(right.type == VALUE_TYPE_BIGINT);
-#endif
- BigInt *result = divBigInt(left.val.bigint, right.val.bigint);
- return bigIntValue(result);
-}
-
-static Value littleDivide(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_STDINT);
- assert(right.type == VALUE_TYPE_STDINT);
-#endif
- return intValue(left.val.stdint / right.val.stdint);
-}
-
-static IntegerBinOp power;
-
-static Value bigPower(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_BIGINT);
- assert(right.type == VALUE_TYPE_BIGINT);
-#endif
- BigInt *result = powBigInt(left.val.bigint, right.val.bigint);
- return bigIntValue(result);
-}
-
-static Value littlePower(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_STDINT);
- assert(right.type == VALUE_TYPE_STDINT);
-#endif
- return intValue(pow(left.val.stdint, right.val.stdint));
-}
-
-static IntegerBinOp modulo;
-
-static Value bigModulo(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_BIGINT);
- assert(right.type == VALUE_TYPE_BIGINT);
-#endif
- BigInt *result = modBigInt(left.val.bigint, right.val.bigint);
- return bigIntValue(result);
-}
-
-static Value littleModulo(Value left, Value right) {
-#ifdef SAFETY_CHECKS
- assert(left.type == VALUE_TYPE_STDINT);
- assert(right.type == VALUE_TYPE_STDINT);
-#endif
- return intValue(left.val.stdint % right.val.stdint);
-}
-
static int _cmp(Value left, Value right);
static int _vecCmp(Vec *left, Vec *right) {
@@ -414,21 +283,6 @@ static Value lookup(int frame, int offset) {
return env->values[offset];
}
-static int protectValue(Value v) {
- switch (v.type) {
- case VALUE_TYPE_CLO:
- return PROTECT(v.val.clo);
- case VALUE_TYPE_CONT:
- return PROTECT(v.val.kont);
- case VALUE_TYPE_VEC:
- return PROTECT(v.val.vec);
- case VALUE_TYPE_BIGINT:
- return PROTECT(v.val.bigint);
- default:
- return PROTECT(NULL);
- }
-}
-
/**
* on reaching this point, the stack will contain a number
* of arguments, and the callable on top.
@@ -532,21 +386,7 @@ void reportSteps(void) {
static void step() {
if (dump_bytecode_flag)
dumpByteCode(&state.B);
- if (bigint_flag) {
- add = bigAdd;
- mul = bigMul;
- sub = bigSub;
- divide = bigDivide;
- power = bigPower;
- modulo = bigModulo;
- } else {
- add = littleAdd;
- mul = littleMul;
- sub = littleSub;
- divide = littleDivide;
- power = littlePower;
- modulo = littleModulo;
- }
+ init_arithmetic();
state.C = 0;
while (state.C != UINT64_MAX) {
++count;
@@ -616,10 +456,20 @@ static void step() {
// peek value, print it
DEBUGPRINTF("PUTN\n");
Value b = tos();
- if (b.type == VALUE_TYPE_BIGINT) {
- fprintBigInt(stdout, b.val.bigint);
- } else {
- printf("%d", b.val.stdint);
+ switch (b.type) {
+ case VALUE_TYPE_BIGINT:
+ fprintBigInt(stdout, b.val.bigint);
+ break;
+ case VALUE_TYPE_STDINT:
+ printf("%d", b.val.stdint);
+ break;
+ case VALUE_TYPE_RATIONAL:
+ putValue(b.val.vec->values[0]);
+ printf("/");
+ putValue(b.val.vec->values[1]);
+ break;
+ default:
+ cant_happen("unrecognised type %d", b.type);
}
}
break;
@@ -627,119 +477,163 @@ static void step() {
// pop two values, perform the binop and push the result
DEBUGPRINTF("CMP\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(cmp(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_ADD:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("ADD\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(add(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_SUB:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("SUB\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(sub(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_MUL:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("MUL\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(mul(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_DIV:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("DIV\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(divide(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_POW:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("POW\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(power(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_MOD:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("MOD\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(modulo(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_EQ:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("EQ\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(eq(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_NE:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("NE\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(ne(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_GT:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("GT\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(gt(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_LT:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("LT\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(lt(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_GE:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("GE\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(ge(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_LE:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("LE\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(le(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_XOR:{
// pop two values, perform the binop and push the result
DEBUGPRINTF("XOR\n");
Value right = pop();
+ int save = protectValue(right);
Value left = pop();
+ protectValue(left);
push(xor(left, right));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_NOT:{
// pop value, perform the op and push the result
DEBUGPRINTF("NOT\n");
Value a = pop();
+ int save = protectValue(a);
push(not(a));
+ UNPROTECT(save);
}
break;
case BYTECODE_PRIM_VEC:{
diff --git a/src/value.h b/src/value.h
index 731eb2e..83adb4c 100644
--- a/src/value.h
+++ b/src/value.h
@@ -24,6 +24,7 @@ typedef enum {
VALUE_TYPE_VOID,
VALUE_TYPE_STDINT,
VALUE_TYPE_BIGINT,
+ VALUE_TYPE_RATIONAL,
VALUE_TYPE_CHARACTER,
VALUE_TYPE_CLO,
VALUE_TYPE_PCLO,
@@ -53,7 +54,9 @@ typedef struct Value {
# define VALUE_VAL_CLO(x) ((ValueVal){.clo = (x)})
# define VALUE_VAL_PCLO(x) ((ValueVal){.clo = (x)})
# define VALUE_VAL_CONT(x) ((ValueVal){.kont = (x)})
+// RATIONAL and VEC share the same Vec struct
# define VALUE_VAL_VEC(x) ((ValueVal){.vec = (x)})
+# define VALUE_VAL_RATIONAL(x) ((ValueVal){.vec = (x)})
# define VALUE_VAL_NONE() ((ValueVal){.none = NULL})
// constants