klisp

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

commit 2fef85618d6017a883269993b109f4840f42b34f
parent ba3e6bebfcb7f348fb380665a25ee68ed38a44d3
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat, 23 Apr 2011 18:40:08 -0300

Added support for bigrats to +, - & *. Added / & rational?. Bugfixes: changed the types of some applicatives from number to real.

Diffstat:
Msrc/kgnumbers.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/kgnumbers.h | 15+++++++++++++++
Msrc/kground.c | 29++++++++++++++++++++---------
Msrc/krational.c | 39+++++++++++++++++++++++++++++++++++++++
Msrc/krational.h | 2+-
5 files changed, 244 insertions(+), 28 deletions(-)

diff --git a/src/kgnumbers.c b/src/kgnumbers.c @@ -18,24 +18,26 @@ #include "kerror.h" #include "ksymbol.h" #include "kinteger.h" - #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 */ +#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 & rational, should also include inexact + integers */ bool kintegerp(TValue obj) { return ttisinteger(obj); } bool krationalp(TValue obj) { return ttisrational(obj); } +/* all real are rationals in klisp */ +bool krealp(TValue obj) { return ttisrational(obj); } /* 12.5.2 =? */ /* uses typed_bpredp */ @@ -157,6 +159,11 @@ bool krationalp(TValue obj) { return ttisrational(obj); } kensure_bigint(n2); return kbigint_plus(K, n1, n2); } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_plus(K, n1, n2); + } case K_TEINF: if (!ttiseinf(n1)) return n2; @@ -191,6 +198,11 @@ bool krationalp(TValue obj) { return ttisrational(obj); } kensure_bigint(n2); return kbigint_times(K, n1, n2); } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_times(K, n1, n2); + } case K_TEINF: if (!ttiseinf(n1) || !ttiseinf(n2)) { if (kfast_zerop(n1) || kfast_zerop(n2)) { @@ -224,6 +236,11 @@ bool krationalp(TValue obj) { return ttisrational(obj); } kensure_bigint(n2); return kbigint_minus(K, n1, n2); } + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_minus(K, n1, n2); + } case K_TEINF: if (!ttiseinf(n1)) return kneg_inf(n2); @@ -240,6 +257,48 @@ bool krationalp(TValue obj) { return ttisrational(obj); } } } + /* May throw an error */ + /* GC: assumes n1 & n2 rooted */ + TValue knum_divided(klisp_State *K, TValue n1, TValue n2) + { + /* first check the most common error, division by zero */ + if (kfast_zerop(n2)) { + klispE_throw(K, "/: division by zero (no primary value)"); + return KINERT; + } + + switch(max_ttype(n1, n2)) { + case K_TFIXINT: { + int64_t res = (int64_t) ivalue(n1) / (int64_t) ivalue(n2); + int64_t rem = (int64_t) ivalue(n1) % (int64_t) ivalue(n2); + if (rem == 0 && res >= (int64_t) INT32_MIN && + res <= (int64_t) INT32_MAX) { + return i2tv((int32_t) res); + } /* else fall through */ + } + case K_TBIGINT: /* just handle it as a rational */ + case K_TBIGRAT: { + kensure_bigrat(n1); + kensure_bigrat(n2); + return kbigrat_divided(K, n1, n2); + } + case K_TEINF: { + if (ttiseinf(n1) && ttiseinf(n2)) { + klispE_throw(K, "/: (infinity divided by infinity) " + "no primary value"); + return KINERT; + } else if (ttiseinf(n1)) { + return knum_same_signp(n1, n2)? KEPINF : KEMINF; + } else { /* ttiseinf(n2) */ + return i2tv(0); + } + } + default: + klispE_throw(K, "/: unsopported type"); + return KINERT; + } +} + /* GC: assumes n rooted */ TValue knum_abs(klisp_State *K, TValue n) { @@ -623,8 +682,8 @@ void kdiv_mod(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(denv); - bind_2tp(K, name, ptree, "number", knumberp, tv_n, - "number", knumberp, tv_d); + bind_2tp(K, name, ptree, "number", krealp, tv_n, + "number", krealp, tv_d); TValue tv_div, tv_mod; @@ -895,3 +954,95 @@ void klcm(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) kapply_cc(K, res); } + +/* TODO: remaining of rational module */ + +/* 12.8.1 rational? */ +/* uses ftypep */ + +/* 12.8.2 / */ +void kdivided(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(denv); + UNUSED(xparams); + /* cycles are allowed, loop counting pairs */ + int32_t cpairs; + + /* / in kernel (and unlike in scheme) requires at least 2 arguments */ + if (!ttispair(ptree) || !ttispair(kcdr(ptree))) { + klispE_throw(K, "/: at least two values are required"); + return; + } else if (!knumberp(kcar(ptree))) { + klispE_throw(K, "/: bad type on first argument (expected number)"); + return; + } + TValue first_val = kcar(ptree); + int32_t pairs = check_typed_list(K, "/", "number", knumberp, true, + kcdr(ptree), &cpairs); + int32_t apairs = pairs - cpairs; + + TValue res; + + /* first the acyclic part */ + TValue ares = i2tv(1); + TValue tail = kcdr(ptree); + + krooted_vars_push(K, &ares); + + while(apairs--) { + TValue first = kcar(tail); + tail = kcdr(tail); + ares = knum_times(K, ares, first); + } + + /* next the cyclic part */ + TValue cres = i2tv(1); + + if (cpairs == 0) { + /* speed things up if there is no cycle */ + res = ares; + krooted_vars_pop(K); + } else { + bool all_one = true; + + krooted_vars_push(K, &cres); + while(cpairs--) { + TValue first = kcar(tail); + tail = kcdr(tail); + all_one = all_one && kfast_onep(first); + cres = knum_times(K, cres, first); + } + + /* think of cres as the product of an infinite series */ + if (kfast_zerop(cres)) + ; /* do nothing */ + else if (kpositivep(cres) && knum_ltp(K, cres, i2tv(1))) + cres = i2tv(0); + else if (kfast_onep(cres)) { + if (all_one) + cres = i2tv(1); + else { + klispE_throw(K, "/: result has no primary value"); + return; + } + } 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; + } else { + /* cycle result less than zero */ + klispE_throw(K, "/: result has no primary value"); + return; + } + + res = knum_times(K, ares, cres); + krooted_vars_pop(K); + krooted_vars_pop(K); + } + /* now divide first value by the product of all the elements in the list */ + krooted_tvs_push(K, res); + res = knum_divided(K, first_val, res); + krooted_tvs_pop(K); + + kapply_cc(K, res); +} diff --git a/src/kgnumbers.h b/src/kgnumbers.h @@ -28,6 +28,7 @@ bool knumberp(TValue obj); bool kfinitep(TValue obj); bool kintegerp(TValue obj); bool krationalp(TValue obj); +bool krealp(TValue obj); /* 12.5.2 =? */ @@ -110,6 +111,20 @@ void kmin_max(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); void kgcd(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); void klcm(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); +/* 12.8.1 rational? */ +/* uses ftypep */ + +/* 12.8.2 / */ +void kdivided(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); + +/* 12.8.3 numerator, denominator */ +/* TODO */ + +/* 12.8.4 floor, ceiling, truncate, round */ +/* TODO */ + +/* 12.8.5 rationalize, simplest-rational */ +/* TODO */ /* REFACTOR: These should be in a knumber.h header */ diff --git a/src/kground.c b/src/kground.c @@ -671,24 +671,21 @@ void kinit_ground_env(klisp_State *K) /* 12.5.3 <?, <=?, >?, >=? */ add_applicative(K, ground_env, "<?", ftyped_kbpredp, 3, - symbol, p2tv(knumberp), p2tv(knum_ltp)); + symbol, p2tv(krealp), p2tv(knum_ltp)); add_applicative(K, ground_env, "<=?", ftyped_kbpredp, 3, - symbol, p2tv(knumberp), p2tv(knum_lep)); + symbol, p2tv(krealp), p2tv(knum_lep)); add_applicative(K, ground_env, ">?", ftyped_kbpredp, 3, - symbol, p2tv(knumberp), p2tv(knum_gtp)); + symbol, p2tv(krealp), p2tv(knum_gtp)); add_applicative(K, ground_env, ">=?", ftyped_kbpredp, 3, - symbol, p2tv(knumberp), p2tv(knum_gep)); + symbol, p2tv(krealp), p2tv(knum_gep)); /* 12.5.4 + */ - /* TEMP: for now only accept two arguments */ add_applicative(K, ground_env, "+", kplus, 0); /* 12.5.5 * */ - /* TEMP: for now only accept two arguments */ add_applicative(K, ground_env, "*", ktimes, 0); /* 12.5.6 - */ - /* TEMP: for now only accept two arguments */ add_applicative(K, ground_env, "-", kminus, 0); /* 12.5.7 zero? */ @@ -713,9 +710,9 @@ void kinit_ground_env(klisp_State *K) /* 12.5.10 positive?, negative? */ add_applicative(K, ground_env, "positive?", ftyped_predp, 3, symbol, - p2tv(knumberp), p2tv(kpositivep)); + p2tv(krealp), p2tv(kpositivep)); add_applicative(K, ground_env, "negative?", ftyped_predp, 3, symbol, - p2tv(knumberp), p2tv(knegativep)); + p2tv(krealp), p2tv(knegativep)); /* 12.5.11 odd?, even? */ add_applicative(K, ground_env, "odd?", ftyped_predp, 3, symbol, @@ -734,6 +731,20 @@ void kinit_ground_env(klisp_State *K) add_applicative(K, ground_env, "gcd", kgcd, 0); add_applicative(K, ground_env, "lcm", klcm, 0); + /* + ** 12.8 Rational features + */ + + /* 12.8.1 rational */ + add_applicative(K, ground_env, "rational?", ftypep, 2, symbol, + p2tv(krationalp)); + + /* 12.8.2 / */ + add_applicative(K, ground_env, "/", kdivided, 0); + + /* TODO */ + /* complete module rational */ + /* ** ** 13 Strings diff --git a/src/krational.c b/src/krational.c @@ -187,6 +187,45 @@ bool kbigrat_gep(klisp_State *K, TValue tv_bigrat1, TValue tv_bigrat2) tv2bigrat(tv_bigrat2)) >= 0); } +/* +** GC: All of these assume the parameters are rooted +*/ +TValue kbigrat_plus(klisp_State *K, TValue n1, TValue n2) +{ + TValue res = kbigrat_make_simple(K); + krooted_tvs_push(K, res); + UNUSED(mp_rat_add(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res))); + krooted_tvs_pop(K); + return kbigrat_try_integer(K, res); +} + +TValue kbigrat_times(klisp_State *K, TValue n1, TValue n2) +{ + TValue res = kbigrat_make_simple(K); + krooted_tvs_push(K, res); + UNUSED(mp_rat_mul(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res))); + krooted_tvs_pop(K); + return kbigrat_try_integer(K, res); +} + +TValue kbigrat_minus(klisp_State *K, TValue n1, TValue n2) +{ + TValue res = kbigrat_make_simple(K); + krooted_tvs_push(K, res); + UNUSED(mp_rat_sub(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res))); + krooted_tvs_pop(K); + return kbigrat_try_integer(K, res); +} + +TValue kbigrat_divided(klisp_State *K, TValue n1, TValue n2) +{ + TValue res = kbigrat_make_simple(K); + krooted_tvs_push(K, res); + UNUSED(mp_rat_div(K, tv2bigrat(n1), tv2bigrat(n2), tv2bigrat(res))); + krooted_tvs_pop(K); + return kbigrat_try_integer(K, res); +} + bool kbigrat_negativep(TValue tv_bigrat) { return (mp_rat_compare_zero(tv2bigrat(tv_bigrat)) < 0); diff --git a/src/krational.h b/src/krational.h @@ -123,7 +123,7 @@ bool kbigrat_gep(klisp_State *K, TValue bigrat1, TValue bigrat2); TValue kbigrat_plus(klisp_State *K, TValue n1, TValue n2); TValue kbigrat_times(klisp_State *K, TValue n1, TValue n2); TValue kbigrat_minus(klisp_State *K, TValue n1, TValue n2); -TValue kbigrat_divide(klisp_State *K, TValue n1, TValue n2); +TValue kbigrat_divided(klisp_State *K, TValue n1, TValue n2); /* TODO: Kernel allows arbitrary reals for these... will have to define */ #if 0