klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit a6ee8eadad848d8fab83fdf5df652859ebeffdae
parent 8ccb5720da8ed45968ca4eb6206497a645ad43b4
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat, 23 Apr 2011 14:06:14 -0300

Added support for bigints in number comparison predicates.

Diffstat:
Msrc/kghelpers.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/kghelpers.h | 4++++
Msrc/kgnumbers.c | 411++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/kgnumbers.h | 11++++++-----
Msrc/kground.c | 10+++++-----
5 files changed, 289 insertions(+), 204 deletions(-)

diff --git a/src/kghelpers.c b/src/kghelpers.c @@ -176,6 +176,63 @@ void ftyped_bpredp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) kapply_cc(K, b2tv(res)); } +/* This is the same, but the comparison predicate takes a klisp_State */ +/* TODO unify them */ +void ftyped_kbpredp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + (void) denv; + /* + ** xparams[0]: name symbol + ** xparams[1]: type fn pointer (as a void * in a user TValue) + ** xparams[2]: fn pointer (as a void * in a user TValue) + */ + char *name = ksymbol_buf(xparams[0]); + bool (*typep)(TValue obj) = pvalue(xparams[1]); + bool (*predp)(klisp_State *K, TValue obj1, TValue obj2) = + pvalue(xparams[2]); + + /* check the ptree is a list first to allow the structure + errors to take precedence over the type errors. */ + int32_t cpairs; + int32_t pairs = check_list(K, name, true, ptree, &cpairs); + + /* cyclical list require an extra comparison of the last + & first element of the cycle */ + int32_t comps = cpairs? pairs : pairs - 1; + + TValue tail = ptree; + bool res = true; + + /* check the type while checking the predicate. + Keep going even if the result is false to catch errors in + type */ + + if (comps == 0) { + /* this case has to be here because otherwise there is no check + for the type of the lone operand */ + TValue first = kcar(tail); + if (!(*typep)(first)) { + /* TODO show expected type */ + klispE_throw_extra(K, name, ": bad argument type"); + return; + } + } + + while(comps-- > 0) { /* comps could be -1 if ptree is () */ + TValue first = kcar(tail); + tail = kcdr(tail); /* tail only advances one place per iteration */ + TValue second = kcar(tail); + + if (!(*typep)(first) || !(*typep)(second)) { + /* TODO show expected type */ + klispE_throw_extra(K, name, ": bad argument type"); + return; + } + res &= (*predp)(K, first, second); + } + kapply_cc(K, b2tv(res)); +} + /* typed finite list. Structure error should be throw before type errors */ int32_t check_typed_list(klisp_State *K, char *name, char *typename, bool (*typep)(TValue), bool allow_infp, TValue obj, diff --git a/src/kghelpers.h b/src/kghelpers.h @@ -385,6 +385,10 @@ void ftyped_predp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); */ void ftyped_bpredp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); +/* This is the same, but the comparison predicate takes a klisp_State */ +/* TODO unify them */ +void ftyped_kbpredp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + /* ** Continuation that ignores the value received and instead returns diff --git a/src/kgnumbers.c b/src/kgnumbers.c @@ -18,196 +18,219 @@ #include "kerror.h" #include "ksymbol.h" #include "kinteger.h" - -#include "kghelpers.h" -#include "kgnumbers.h" - -/* 15.5.1? number?, finite?, integer? */ -/* use ftypep & ftypep_predp */ - -/* Helpers for typed predicates */ -bool knumberp(TValue obj) { return ttype(obj) <= K_LAST_NUMBER_TYPE; } -/* This is used in gcd & lcm */ -bool kimp_intp(TValue obj) { return ttisinteger(obj) || ttiseinf(obj); } -/* obj is known to be a number */ -bool kfinitep(TValue obj) { return (!ttiseinf(obj) && !ttisiinf(obj)); } -/* TEMP: for now only fixint & bigints, should also include inexact - integers */ -bool kintegerp(TValue obj) { return ttisinteger(obj); } - -/* 12.5.2 =? */ -/* uses typed_bpredp */ - -/* 12.5.3 <?, <=?, >?, >=? */ -/* use typed_bpredp */ - -/* Helpers for typed binary predicates */ -/* XXX: this should probably be in a file knumber.h but there is no real need for - that file yet */ - -/* this will come handy when there are more numeric types, - it is intended to be used in switch */ -/* MAYBE: change to return -1, 0, 1 to indicate which type is bigger, and - return min & max in two extra pointers passed in. Change name to - classify_types */ -inline int32_t max_ttype(TValue obj1, TValue obj2) -{ - int32_t t1 = ttype(obj1); - int32_t t2 = ttype(obj2); - - return (t1 > t2? t1 : t2); -} - -inline int32_t min_ttype(TValue obj1, TValue obj2) -{ - int32_t t1 = ttype(obj1); - int32_t t2 = ttype(obj2); - - return (t1 < t2? t1 : t2); -} - -/* TEMP: for now only fixints, bigints and exact infinities */ -bool knum_eqp(TValue n1, TValue n2) -{ - switch(max_ttype(n1, n2)) { - case K_TFIXINT: - return ivalue(n1) == ivalue(n2); - case K_TBIGINT: - if (min_ttype(n1, n2) != K_TBIGINT) { - /* NOTE: no fixint is =? to a bigint */ - return false; - } else { - /* both are bigints */ - return kbigint_eqp(n1, n2); - } - case K_TEINF: - return (tv_equal(n1, n2)); - default: - /* shouldn't happen */ - assert(0); - return false; - } -} - -bool knum_ltp(TValue n1, TValue n2) -{ - switch(max_ttype(n1, n2)) { - case K_TFIXINT: - return ivalue(n1) < ivalue(n2); - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_ltp(n1, n2); - } - case K_TEINF: - return !tv_equal(n1, n2) && (tv_equal(n1, KEMINF) || - tv_equal(n2, KEPINF)); - default: - /* shouldn't happen */ - assert(0); - return false; - } -} - -bool knum_lep(TValue n1, TValue n2) { return !knum_ltp(n2, n1); } -bool knum_gtp(TValue n1, TValue n2) { return knum_ltp(n2, n1); } -bool knum_gep(TValue n1, TValue n2) { return !knum_ltp(n1, n2); } - -/* REFACTOR/MAYBE: add small inlineable plus that - first tries fixint addition and if that fails calls knum_plus */ - -/* May throw an error */ -/* GC: assumes n1 & n2 rooted */ -TValue knum_plus(klisp_State *K, TValue n1, TValue n2) -{ - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) + (int64_t) ivalue(n2); - if (res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_plus(K, n1, n2); - } - case K_TEINF: - if (!ttiseinf(n1)) - return n2; - else if (!ttiseinf(n2)) - return n1; - if (tv_equal(n1, n2)) - return n1; - else { - klispE_throw(K, "+: no primary value"); - return KINERT; - } - default: - klispE_throw(K, "+: unsopported type"); - return KINERT; - } -} - -/* May throw an error */ -/* GC: assumes n1 & n2 rooted */ -TValue knum_times(klisp_State *K, TValue n1, TValue n2) -{ - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) * (int64_t) ivalue(n2); - if (res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_times(K, n1, n2); - } - case K_TEINF: - if (!ttiseinf(n1) || !ttiseinf(n2)) { - if (kfast_zerop(n1) || kfast_zerop(n2)) { - /* report: #e+infinity * 0 has no primary value */ - klispE_throw(K, "*: result has no primary value"); - return KINERT; - } else - return knum_same_signp(n1, n2)? KEPINF : KEMINF; - } else - return (tv_equal(n1, n2))? KEPINF : KEMINF; - default: - klispE_throw(K, "*: unsopported type"); - return KINERT; - } -} - -/* May throw an error */ -/* GC: assumes n1 & n2 rooted */ -TValue knum_minus(klisp_State *K, TValue n1, TValue n2) -{ - switch(max_ttype(n1, n2)) { - case K_TFIXINT: { - int64_t res = (int64_t) ivalue(n1) - (int64_t) ivalue(n2); - if (res >= (int64_t) INT32_MIN && - res <= (int64_t) INT32_MAX) { - return i2tv((int32_t) res); - } /* else fall through */ - } - case K_TBIGINT: { - kensure_bigint(n1); - kensure_bigint(n2); - return kbigint_minus(K, n1, n2); - } - case K_TEINF: - if (!ttiseinf(n1)) - return kneg_inf(n2); - else if (!ttiseinf(n2)) - return n1; - if (tv_equal(n1, n2)) { - klispE_throw(K, "-: no primary value"); - return KINERT; + #include "krational.h" + + #include "kghelpers.h" + #include "kgnumbers.h" + + /* 15.5.1? number?, finite?, integer? */ + /* use ftypep & ftypep_predp */ + + /* Helpers for typed predicates */ + bool knumberp(TValue obj) { return ttype(obj) <= K_LAST_NUMBER_TYPE; } + /* This is used in gcd & lcm */ + bool kimp_intp(TValue obj) { return ttisinteger(obj) || ttiseinf(obj); } + /* obj is known to be a number */ + bool kfinitep(TValue obj) { return (!ttiseinf(obj) && !ttisiinf(obj)); } + /* TEMP: for now only fixint & bigints, should also include inexact + integers */ + bool kintegerp(TValue obj) { return ttisinteger(obj); } + + /* 12.5.2 =? */ + /* uses typed_bpredp */ + + /* 12.5.3 <?, <=?, >?, >=? */ + /* use typed_bpredp */ + + /* Helpers for typed binary predicates */ + /* XXX: this should probably be in a file knumber.h but there is no real need for + that file yet */ + + /* this will come handy when there are more numeric types, + it is intended to be used in switch */ + /* MAYBE: change to return -1, 0, 1 to indicate which type is bigger, and + return min & max in two extra pointers passed in. Change name to + classify_types */ + inline int32_t max_ttype(TValue obj1, TValue obj2) + { + int32_t t1 = ttype(obj1); + int32_t t2 = ttype(obj2); + + return (t1 > t2? t1 : t2); + } + + inline int32_t min_ttype(TValue obj1, TValue obj2) + { + int32_t t1 = ttype(obj1); + int32_t t2 = ttype(obj2); + + return (t1 < t2? t1 : t2); + } + + /* TEMP: for now only fixints, bigints, bigrats and exact infinities */ + bool knum_eqp(klisp_State *K, TValue n1, TValue n2) + { + switch(max_ttype(n1, n2)) { + case K_TFIXINT: + return ivalue(n1) == ivalue(n2); + case K_TBIGINT: + if (min_ttype(n1, n2) != K_TBIGINT) { + /* NOTE: no fixint is =? to a bigint */ + return false; + } else { + /* both are bigints */ + return kbigint_eqp(n1, n2); + } + case K_TBIGRAT: + if (min_ttype(n1, n2) != K_TBIGRAT) { + /* NOTE: no fixint or bigint is =? to a bigrat */ + return false; + } else { + /* both are bigints */ + return kbigrat_eqp(K, n1, n2); + } + case K_TEINF: + return (tv_equal(n1, n2)); + default: + /* shouldn't happen */ + assert(0); + return false; + } + } + + bool knum_ltp(klisp_State *K, TValue n1, TValue n2) + { + switch(max_ttype(n1, n2)) { + case K_TFIXINT: + return ivalue(n1) < ivalue(n2); + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_ltp(n1, n2); + } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_ltp(K, n1, n2); + } + case K_TEINF: + return !tv_equal(n1, n2) && (tv_equal(n1, KEMINF) || + tv_equal(n2, KEPINF)); + default: + /* shouldn't happen */ + assert(0); + return false; + } + } + + bool knum_lep(klisp_State *K, TValue n1, TValue n2) + { + return !knum_ltp(K, n2, n1); + } + bool knum_gtp(klisp_State *K, TValue n1, TValue n2) + { + return knum_ltp(K, n2, n1); + } + bool knum_gep(klisp_State *K, TValue n1, TValue n2) + { + return !knum_ltp(K, n1, n2); + } + + /* REFACTOR/MAYBE: add small inlineable plus that + first tries fixint addition and if that fails calls knum_plus */ + + /* May throw an error */ + /* GC: assumes n1 & n2 rooted */ + TValue knum_plus(klisp_State *K, TValue n1, TValue n2) + { + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) + (int64_t) ivalue(n2); + if (res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_plus(K, n1, n2); + } + case K_TEINF: + if (!ttiseinf(n1)) + return n2; + else if (!ttiseinf(n2)) + return n1; + if (tv_equal(n1, n2)) + return n1; + else { + klispE_throw(K, "+: no primary value"); + return KINERT; + } + default: + klispE_throw(K, "+: unsopported type"); + return KINERT; + } + } + + /* May throw an error */ + /* GC: assumes n1 & n2 rooted */ + TValue knum_times(klisp_State *K, TValue n1, TValue n2) + { + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) * (int64_t) ivalue(n2); + if (res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_times(K, n1, n2); + } + case K_TEINF: + if (!ttiseinf(n1) || !ttiseinf(n2)) { + if (kfast_zerop(n1) || kfast_zerop(n2)) { + /* report: #e+infinity * 0 has no primary value */ + klispE_throw(K, "*: result has no primary value"); + return KINERT; + } else + return knum_same_signp(n1, n2)? KEPINF : KEMINF; + } else + return (tv_equal(n1, n2))? KEPINF : KEMINF; + default: + klispE_throw(K, "*: unsopported type"); + return KINERT; + } + } + + /* May throw an error */ + /* GC: assumes n1 & n2 rooted */ + TValue knum_minus(klisp_State *K, TValue n1, TValue n2) + { + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) - (int64_t) ivalue(n2); + if (res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: { + kensure_bigint(n1); + kensure_bigint(n2); + return kbigint_minus(K, n1, n2); + } + case K_TEINF: + if (!ttiseinf(n1)) + return kneg_inf(n2); + else if (!ttiseinf(n2)) + return n1; + if (tv_equal(n1, n2)) { + klispE_throw(K, "-: no primary value"); + return KINERT; } else return n1; default: @@ -413,7 +436,7 @@ void ktimes(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* think of cres as the product of an infinite series */ if (kfast_zerop(cres)) ; /* do nothing */ - else if (kpositivep(cres) && knum_ltp(cres, i2tv(1))) + else if (kpositivep(cres) && knum_ltp(K, cres, i2tv(1))) cres = i2tv(0); else if (kfast_onep(cres)) { if (all_one) @@ -422,7 +445,7 @@ void ktimes(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) klispE_throw(K, "*: result has no primary value"); return; } - } else if (knum_gtp(cres, i2tv(1))) { + } else if (knum_gtp(K, cres, i2tv(1))) { /* ASK JOHN: this is as per the report, but maybe we should check that all elements are positive... */ cres = KEPINF; @@ -787,13 +810,13 @@ void kmin_max(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) } TValue tail = ptree; - bool (*cmp)(TValue, TValue) = minp? knum_ltp : knum_gtp; + bool (*cmp)(klisp_State *K, TValue, TValue) = minp? knum_ltp : knum_gtp; while(pairs--) { TValue first = kcar(tail); tail = kcdr(tail); - if ((*cmp)(first, res)) + if ((*cmp)(K, first, res)) res = first; } kapply_cc(K, res); diff --git a/src/kgnumbers.h b/src/kgnumbers.h @@ -27,6 +27,7 @@ bool knumberp(TValue obj); bool kfinitep(TValue obj); bool kintegerp(TValue obj); +bool krationalp(TValue obj); /* 12.5.2 =? */ @@ -38,11 +39,11 @@ bool kintegerp(TValue obj); /* Helpers for typed binary predicates */ /* XXX: this should probably be in a file knumber.h but there is no real need for that file yet */ -bool knum_eqp(TValue n1, TValue n2); -bool knum_ltp(TValue n1, TValue n2); -bool knum_lep(TValue n1, TValue n2); -bool knum_gtp(TValue n1, TValue n2); -bool knum_gep(TValue n1, TValue n2); +bool knum_eqp(klisp_State *K, TValue n1, TValue n2); +bool knum_ltp(klisp_State *K, TValue n1, TValue n2); +bool knum_lep(klisp_State *K, TValue n1, TValue n2); +bool knum_gtp(klisp_State *K, TValue n1, TValue n2); +bool knum_gep(klisp_State *K, TValue n1, TValue n2); /* 12.5.4 + */ /* TEMP: for now only accept two arguments */ diff --git a/src/kground.c b/src/kground.c @@ -666,17 +666,17 @@ void kinit_ground_env(klisp_State *K) p2tv(kintegerp)); /* 12.5.2 =? */ - add_applicative(K, ground_env, "=?", ftyped_bpredp, 3, + add_applicative(K, ground_env, "=?", ftyped_kbpredp, 3, symbol, p2tv(knumberp), p2tv(knum_eqp)); /* 12.5.3 <?, <=?, >?, >=? */ - add_applicative(K, ground_env, "<?", ftyped_bpredp, 3, + add_applicative(K, ground_env, "<?", ftyped_kbpredp, 3, symbol, p2tv(knumberp), p2tv(knum_ltp)); - add_applicative(K, ground_env, "<=?", ftyped_bpredp, 3, + add_applicative(K, ground_env, "<=?", ftyped_kbpredp, 3, symbol, p2tv(knumberp), p2tv(knum_lep)); - add_applicative(K, ground_env, ">?", ftyped_bpredp, 3, + add_applicative(K, ground_env, ">?", ftyped_kbpredp, 3, symbol, p2tv(knumberp), p2tv(knum_gtp)); - add_applicative(K, ground_env, ">=?", ftyped_bpredp, 3, + add_applicative(K, ground_env, ">=?", ftyped_kbpredp, 3, symbol, p2tv(knumberp), p2tv(knum_gep)); /* 12.5.4 + */