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