tisp

tiny lisp
git clone git://edryd.org/tisp
Log | Files | Refs | LICENSE

commit bd16dd46d4df3c1cbd6fb73b38bad788c422bd43
parent adeac8c2645a06ece901d69c406de6a88dcf9a26
Author: Ed van Bruggen <edvb@uw.edu>
Date:   Thu, 24 Jan 2019 23:54:16 -0800

Internally store integers and decimals as a ratio

Makes arithmetic with different types easier and more general.

Diffstat:
tib/math.c | 164+++++++++++++++++++++++++++++--------------------------------------------------
tisp.c | 21++++++++++-----------
tisp.h | 13++++++-------
3 files changed, 75 insertions(+), 123 deletions(-)

diff --git a/tib/math.c b/tib/math.c @@ -29,14 +29,38 @@ tsp_arg_type(A, NAME, TYPE); \ } while(0) -#define RATIO_DUB(A, OP, B) return mk_dub(((double)A->v.r.num/A->v.r.den) OP B->v.n) -#define RATIO_INT(A, OP, B) return mk_rat(A->v.r.num OP (B->v.n * A->v.r.den), A->v.r.den) -#define RATIO_INT2(A, OP, B) return mk_rat(A->v.r.num OP B->v.n, A->v.r.den) -#define COMBINE_FIN(A, OP, B) do { \ - if (A->t & DOUBLE || B->t & DOUBLE) \ - return mk_dub(A->v.n OP B->v.n); \ - return mk_int(A->v.n OP B->v.n); \ -} while(0) +/* Wrapper functions to be returned by mk_num, all need same arguments */ +static Val +create_int(double num, double den) +{ + assert(den == 1); + return mk_int(num); +} + +static Val +create_dub(double num, double den) +{ + assert(den == 1); + return mk_dub(num); +} + +static Val +create_rat(double num, double den) +{ + return mk_rat(num, den); +} + +/* Return pointer to one of the preceding functions depending on what sort + * number should be created by the following arithmetic functions */ +static Val +(*mk_num(Type a, Type b, int isfrac))(double, double) +{ + if (a & DOUBLE || b & DOUBLE) + return &create_dub; + if (isfrac || a & RATIO || b & RATIO) + return &create_rat; + return &create_int; +} static Val prim_add(Env env, Val args) @@ -45,27 +69,10 @@ prim_add(Env env, Val args) tsp_arg_num(args, "+", 2); EVAL_CHECK(a, car(args), "+", NUMBER); EVAL_CHECK(b, car(cdr(args)), "+", NUMBER); - switch (a->t) { - case RATIO: - switch (b->t) { - case RATIO: - return mk_rat(a->v.r.num * b->v.r.den + a->v.r.den * b->v.r.num, - a->v.r.den * b->v.r.den); - case INTEGER: - RATIO_INT(a, +, b); - case DOUBLE: - RATIO_DUB(a, +, b); - default: break; - } - case INTEGER: - if (b->t & RATIO) - RATIO_INT(b, +, a); - case DOUBLE: - if (b->t & RATIO) - RATIO_DUB(b, +, a); - default: break; - } - COMBINE_FIN(a, +, b); + if (a->t & DOUBLE || b->t & DOUBLE) + return mk_dub((a->v.n.num/a->v.n.den) + (b->v.n.num/b->v.n.den)); + return (mk_num(a->t, b->t, 0)) + (a->v.n.num * b->v.n.den + a->v.n.den * b->v.n.num, a->v.n.den * b->v.n.den); } static Val @@ -82,27 +89,9 @@ prim_sub(Env env, Val args) } else { EVAL_CHECK(b, car(cdr(args)), "-", NUMBER); } - switch (a->t) { - case RATIO: - switch (b->t) { - case RATIO: - return mk_rat(a->v.r.num * b->v.r.den - a->v.r.den * b->v.r.num, - a->v.r.den * b->v.r.den); - case INTEGER: - RATIO_INT(a, -, b); - case DOUBLE: - RATIO_DUB(a, -, b); - default: break; - } - case INTEGER: - if (b->t & RATIO) - return mk_rat((a->v.n * b->v.r.den) - b->v.r.num, b->v.r.den); - case DOUBLE: - if (b->t & RATIO) - return mk_dub(a->v.n - ((double)b->v.r.num/b->v.r.den)); - default: break; - } - COMBINE_FIN(a, -, b); + if (a->t & DOUBLE || b->t & DOUBLE) + return mk_dub((a->v.n.num/a->v.n.den) - (b->v.n.num/b->v.n.den)); + return (mk_num(a->t, b->t, 0))(a->v.n.num * b->v.n.den - a->v.n.den * b->v.n.num, a->v.n.den * b->v.n.den); } static Val @@ -112,26 +101,10 @@ prim_mul(Env env, Val args) tsp_arg_num(args, "*", 2); EVAL_CHECK(a, car(args), "*", NUMBER); EVAL_CHECK(b, car(cdr(args)), "*", NUMBER); - switch (a->t) { - case RATIO: - switch (b->t) { - case RATIO: - return mk_rat(a->v.r.num * b->v.r.num, a->v.r.den * b->v.r.den); - case INTEGER: - RATIO_INT2(a, *, b); - case DOUBLE: - RATIO_DUB(a, *, b); - default: break; - } - case INTEGER: - if (b->t & RATIO) - RATIO_INT2(b, *, a); - case DOUBLE: - if (b->t & RATIO) - RATIO_DUB(b, *, a); - default: break; - } - COMBINE_FIN(a, *, b); + if (a->t & DOUBLE || b->t & DOUBLE) + return mk_dub((a->v.n.num/a->v.n.den) * (b->v.n.num/b->v.n.den)); + return (mk_num(a->t, b->t, 0))(a->v.n.num * b->v.n.num, a->v.n.den * b->v.n.den); + } static Val @@ -141,28 +114,9 @@ prim_div(Env env, Val args) tsp_arg_num(args, "/", 2); EVAL_CHECK(a, car(args), "/", NUMBER); EVAL_CHECK(b, car(cdr(args)), "/", NUMBER); - switch (a->t) { - case RATIO: - switch (b->t) { - case RATIO: - return mk_rat(a->v.r.num * b->v.r.den, a->v.r.den * b->v.r.num); - case INTEGER: - return mk_rat(a->v.r.num, a->v.r.den * b->v.n); - case DOUBLE: - RATIO_DUB(a, /, b); - default: break; - } - case INTEGER: - if (b->t & RATIO) - return mk_rat(b->v.r.den * a->v.n, b->v.r.num); - case DOUBLE: - if (b->t & RATIO) - return mk_dub(a->v.n / ((double)b->v.r.num/b->v.r.den)); - default: break; - } if (a->t & DOUBLE || b->t & DOUBLE) - return mk_dub(a->v.n / b->v.n); - return mk_rat(a->v.n, b->v.n); + return mk_dub((a->v.n.num/a->v.n.den) / (b->v.n.num/b->v.n.den)); + return (mk_num(a->t, b->t, 1))(a->v.n.num * b->v.n.den, a->v.n.den * b->v.n.num); } static Val @@ -172,23 +126,23 @@ prim_mod(Env env, Val args) tsp_arg_num(args, "mod", 2); EVAL_CHECK(a, car(args), "mod", INTEGER); EVAL_CHECK(b, car(cdr(args)), "mod", INTEGER); - if (b->v.n == 0) + if (b->v.n.num == 0) tsp_warn("division by zero"); - return mk_int((int)a->v.n % abs((int)b->v.n)); + return mk_int((int)a->v.n.num % abs((int)b->v.n.num)); } -#define PRIM_COMPARE(NAME, OP, FUNC) \ -static Val \ -prim_##NAME(Env env, Val args) \ -{ \ - Val v; \ - if (!(v = tisp_eval_list(env, args))) \ - return NULL; \ - if (list_len(v) != 2) \ - return env->t; \ - tsp_arg_type(car(v), FUNC, INTEGER); \ - tsp_arg_type(car(cdr(v)), FUNC, INTEGER); \ - return (car(v)->v.n OP car(cdr(v))->v.n) ? env->t : env->nil; \ +#define PRIM_COMPARE(NAME, OP, FUNC) \ +static Val \ +prim_##NAME(Env env, Val args) \ +{ \ + Val v; \ + if (!(v = tisp_eval_list(env, args))) \ + return NULL; \ + if (list_len(v) != 2) \ + return env->t; \ + tsp_arg_type(car(v), FUNC, INTEGER); \ + tsp_arg_type(car(cdr(v)), FUNC, INTEGER); \ + return (car(v)->v.n.num OP car(cdr(v))->v.n.num) ? env->t : env->nil; \ } PRIM_COMPARE(lt, <, "<") diff --git a/tisp.c b/tisp.c @@ -150,11 +150,8 @@ vals_eq(Val a, Val b) switch (a->t) { case INTEGER: case DOUBLE: - if (a->v.n != b->v.n) - return 0; - break; case RATIO: - if (a->v.r.num != b->v.r.num || a->v.r.den != b->v.r.den) + if (a->v.n.num != b->v.n.num || a->v.n.den != b->v.n.den) return 0; break; default: /* PRIMITIVE, STRING, SYMBOL */ @@ -305,7 +302,8 @@ mk_int(int i) { Val ret = emalloc(sizeof(struct Val)); ret->t = INTEGER; - ret->v.n = i; + ret->v.n.num = i; + ret->v.n.den = 1; return ret; } @@ -314,7 +312,8 @@ mk_dub(double d) { Val ret = emalloc(sizeof(struct Val)); ret->t = DOUBLE; - ret->v.n = d; + ret->v.n.num = d; + ret->v.n.den = 1; return ret; } @@ -332,7 +331,7 @@ mk_rat(int num, int den) return mk_int(num); Val ret = emalloc(sizeof(struct Val)); ret->t = RATIO; - ret->v.r = (Ratio){ num, den }; + ret->v.n = (Ratio){ num, den }; return ret; } @@ -616,15 +615,15 @@ tisp_print(FILE *f, Val v) fprintf(f, "()"); break; case INTEGER: - fprintf(f, "%d", (int)v->v.n); + fprintf(f, "%d", (int)v->v.n.num); break; case DOUBLE: - fprintf(f, "%.15g", v->v.n); - if (v->v.n == (int)v->v.n) + fprintf(f, "%.15g", v->v.n.num); + if (v->v.n.num == (int)v->v.n.num) fprintf(f, ".0"); break; case RATIO: - fprintf(f, "%d/%d", v->v.r.num, v->v.r.den); + fprintf(f, "%d/%d", (int)v->v.n.num, (int)v->v.n.den); break; case STRING: fprintf(f, "\"%s\"", v->v.s); diff --git a/tisp.h b/tisp.h @@ -59,7 +59,7 @@ typedef enum { /* fraction */ typedef struct { - int num, den; + double num, den; } Ratio; typedef struct Entry *Entry; @@ -104,12 +104,11 @@ static Type const NUMBER = INTEGER | DOUBLE | RATIO; struct Val { Type t; /* NONE, NIL */ union { - double n; /* INTEGER, DOUBLE */ - Ratio r; /* RATIO */ - char *s; /* STRING, SYMBOL */ - Prim pr; /* PRIMITIVE */ - Func f; /* FUNCTION */ - Pair p; /* PAIR */ + Ratio n; /* INTEGER, DOUBLE, RATIO */ + char *s; /* STRING, SYMBOL */ + Prim pr; /* PRIMITIVE */ + Func f; /* FUNCTION */ + Pair p; /* PAIR */ } v; };