klisp

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

commit bc4220d6b34b418e39d4470c650b7567345c6533
parent 414fbf46674713ceaf1ca79b8cd3367b1ebf41f2
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Fri,  2 Dec 2011 03:01:29 -0300

Added number->string to the ground environment.

Diffstat:
MTODO | 1-
Msrc/imath.c | 4++--
Msrc/kgnumbers.c | 119++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/kinteger.c | 2++
Msrc/kobject.h | 6++++++
Msrc/krational.c | 6++++--
Msrc/kreal.c | 2++
Msrc/tests/numbers.k | 44++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 178 insertions(+), 6 deletions(-)

diff --git a/TODO b/TODO @@ -22,7 +22,6 @@ ** update the manual with the current features ** add a section to the manual with the interpreter usage * applicatives: -** number->string (r7rs) ** string->number (r7rs) * reader/writer ** syntax support for complex numbers (Kernel report) diff --git a/src/imath.c b/src/imath.c @@ -1866,7 +1866,7 @@ mp_result mp_int_to_string(klisp_State *K, mp_int z, mp_size radix, return MP_RANGE; if(CMPZ(z) == 0) { - *str++ = s_val2ch(0, 1); + *str++ = s_val2ch(0, 0); /* changed to lowercase, Andres Navarro */ } else { mpz_t tmp; @@ -1889,7 +1889,7 @@ mp_result mp_int_to_string(klisp_State *K, mp_int z, mp_size radix, break; d = s_ddiv(&tmp, (mp_digit)radix); - *str++ = s_val2ch(d, 1); + *str++ = s_val2ch(d, 0); /* changed to lowercase, Andres Navarro */ } t = str - 1; diff --git a/src/kgnumbers.c b/src/kgnumbers.c @@ -14,6 +14,7 @@ #include <stdlib.h> #include <stdbool.h> #include <stdint.h> +#include <inttypes.h> /* for string conversion */ #include "kstate.h" #include "kobject.h" @@ -2268,6 +2269,120 @@ void kexpt(klisp_State *K) arith_kapply_cc(K, res); } +/* Number<->String conversion */ +void number_to_string(klisp_State *K) +{ + TValue *xparams = K->next_xparams; + TValue ptree = K->next_value; + TValue denv = K->next_env; + klisp_assert(ttisenvironment(K->next_env)); + UNUSED(denv); + UNUSED(xparams); + + bind_al1tp(K, ptree, "number", knumberp, obj, maybe_radix); + int radix = 10; + if (get_opt_tpar(K, maybe_radix, "radix (2, 8, 10, or 16)", ttisradix)) + radix = ivalue(maybe_radix); + + char small_buf[64]; /* for fixints */ + TValue buf_str = K->empty_string; /* for bigrats, bigints and doubles */ + krooted_vars_push(K, &buf_str); + char *buf; + + switch(ttype(obj)) { + case K_TFIXINT: { + /* can't use snprintf here... there's no support for binary, + so just do by hand */ + uint32_t value; + /* convert to unsigned to write */ + value = (uint32_t) ((ivalue(obj) < 0)? + -((int64_t) ivalue(obj)) : + ivalue(obj)); + char *digits = "0123456789abcdef"; + /* write backwards so we don't have to reverse the buffer */ + buf = small_buf + sizeof(small_buf) - 1; + *buf-- = '\0'; + do { + *buf-- = digits[value % radix]; + value /= radix; + } while(value > 0); /* with the guard down it works for zero too */ + + /* only put the sign if negative, + then correct the pointer to the first char */ + if (ivalue(obj) < 0) + *buf = '-'; + else + ++buf; + break; + } + case K_TBIGINT: { + int32_t size = kbigint_print_size(obj, radix); + /* here we are using 1 byte extra, because size already includes + 1 for the terminator, but better be safe than sorry */ + buf_str = kstring_new_s(K, size); + buf = kstring_buf(buf_str); + kbigint_print_string(K, obj, radix, buf, size); + /* the string will be copied and trimmed later, + because print_size may overestimate */ + break; + } + case K_TBIGRAT: { + int32_t size = kbigrat_print_size(obj, radix); + /* here we are using 1 byte extra, because size already includes + 1 for the terminator, but better be safe than sorry */ + buf_str = kstring_new_s(K, size); + buf = kstring_buf(buf_str); + kbigrat_print_string(K, obj, radix, buf, size); + /* the string will be copied and trimmed later, + because print_size may overestimate */ + break; + } + case K_TEINF: + buf = tv_equal(obj, KEPINF)? "#e+infinity" : "#e-infinity"; + break; + case K_TIINF: + buf = tv_equal(obj, KIPINF)? "#i+infinity" : "#i-infinity"; + break; + case K_TDOUBLE: { + if (radix != 10) { + /* only radix 10 is supported for inexact numbers + see rationale in the report (technically they could be + printed without a decimal point, like fractions, but...*/ + klispE_throw_simple_with_irritants(K, "radix != 10 with inexact " + "number", 2, obj,maybe_radix); + return; + } + /* radix is always 10 */ + int32_t size = kdouble_print_size(obj); + /* here we are using 1 byte extra, because size already includes + 1 for the terminator, but better be safe than sorry */ + buf_str = kstring_new_s(K, size); + buf = kstring_buf(buf_str); + kdouble_print_string(K, obj, buf, size); + /* the string will be copied and trimmed later, + because print_size may overestimate */ + break; + } + case K_TRWNPV: + buf = "#real"; + break; + case K_TUNDEFINED: + buf = "#undefined"; + break; + default: + /* shouldn't happen */ + klisp_assert(0); + } + + TValue str = kstring_new_b(K, buf); + krooted_vars_pop(K); + kapply_cc(K, str); +} + +/* TODO */ +void string_to_number(klisp_State *K) +{ +} /* init ground */ void kinit_numbers_ground_env(klisp_State *K) @@ -2407,5 +2522,7 @@ void kinit_numbers_ground_env(klisp_State *K) /* 12.9.6 expt */ add_applicative(K, ground_env, "expt", kexpt, 0); - /* TODO add some conversion like number->string, string->number */ + /* 12.? string->number, number->string */ + add_applicative(K, ground_env, "string->number", string_to_number, 0); + add_applicative(K, ground_env, "number->string", number_to_string, 0); } diff --git a/src/kinteger.c b/src/kinteger.c @@ -68,6 +68,7 @@ bool kinteger_read(klisp_State *K, char *buf, int32_t base, TValue *out, print the number */ int32_t kbigint_print_size(TValue tv_bigint, int32_t base) { + klisp_assert(ttisbigint(tv_bigint)); return mp_int_string_len(tv2bigint(tv_bigint), base); } @@ -75,6 +76,7 @@ int32_t kbigint_print_size(TValue tv_bigint, int32_t base) void kbigint_print_string(klisp_State *K, TValue tv_bigint, int32_t base, char *buf, int32_t limit) { + klisp_assert(ttisbigint(tv_bigint)); mp_result res = mp_int_to_string(K, tv2bigint(tv_bigint), base, buf, limit); /* only possible error is truncation */ diff --git a/src/kobject.h b/src/kobject.h @@ -248,6 +248,12 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define ttisu8(o) ({ \ TValue o__ = (o); \ (ttisfixint(o__) && ivalue(o__) >= 0 && ivalue(o__) < 256); }) +/* for radixes in string<->number */ +#define ttisradix(o) ({ \ + TValue o__ = (o); \ + (ttisfixint(o__) && \ + (ivalue(o__) == 2 || ivalue(o__) == 8 || \ + ivalue(o__) == 10 || ivalue(o__) == 16)); }) /* for bases in char->digit and related functions */ #define ttisbase(o) ({ \ TValue o__ = (o); \ diff --git a/src/krational.c b/src/krational.c @@ -190,15 +190,17 @@ bool krational_read_decimal(klisp_State *K, char *buf, int32_t base, TValue *out /* this is used by write to estimate the number of chars necessary to print the number */ -int32_t kbigrat_print_size(TValue tv_bigint, int32_t base) +int32_t kbigrat_print_size(TValue tv_bigrat, int32_t base) { - return mp_rat_string_len(tv2bigrat(tv_bigint), base); + klisp_assert(ttisbigrat(tv_bigrat)); + return mp_rat_string_len(tv2bigrat(tv_bigrat), base); } /* this is used by write */ void kbigrat_print_string(klisp_State *K, TValue tv_bigrat, int32_t base, char *buf, int32_t limit) { + klisp_assert(ttisbigrat(tv_bigrat)); mp_result res = mp_rat_to_string(K, tv2bigrat(tv_bigrat), base, buf, limit); /* only possible error is truncation */ diff --git a/src/kreal.c b/src/kreal.c @@ -562,6 +562,7 @@ bool dtoa(klisp_State *K, double d, char *buf, int32_t upoint, int32_t *out_h, number */ int32_t kdouble_print_size(TValue tv_double) { + klisp_assert(ttisdouble(tv_double)); UNUSED(tv_double); return 1024; } @@ -569,6 +570,7 @@ int32_t kdouble_print_size(TValue tv_double) void kdouble_print_string(klisp_State *K, TValue tv_double, char *buf, int32_t limit) { + klisp_assert(ttisdouble(tv_double)); /* TODO: add exponent to values too large or too small */ /* TEMP */ int32_t h = 0; diff --git a/src/tests/numbers.k b/src/tests/numbers.k @@ -423,3 +423,46 @@ ;; 12.10 Complex features ;; not implemented + +;; String conversion +($check string-ci=? (number->string 0) "0") +($check string-ci=? (number->string 1) "1") +($check string-ci=? (number->string -1) "-1") +($check string-ci=? (number->string 2 2) "10") +($check string-ci=? (number->string -2 2) "-10") +($check string-ci=? (number->string 8 8) "10") +($check string-ci=? (number->string -8 8) "-10") +($check string-ci=? (number->string 10 10) "10") +($check string-ci=? (number->string -10 10) "-10") +($check string-ci=? (number->string 16 16) "10") +($check string-ci=? (number->string -16 16) "-10") +; default base +($check string-ci=? (number->string 10) (number->string 10 10)) +;; infinities, undefined and reals with no primary value +($check string-ci=? (number->string #undefined) "#undefined") +($check string-ci=? (number->string #real) "#real") +($check string-ci=? (number->string #e+infinity) "#e+infinity") +($check string-ci=? (number->string #e-infinity) "#e-infinity") +($check string-ci=? (number->string #i+infinity) "#i+infinity") +($check string-ci=? (number->string #i-infinity) "#i-infinity") +;; rationals +($check string-ci=? (number->string 13/17) "13/17") +($check string-ci=? (number->string -17/13) "-17/13") +($check string-ci=? (number->string #o-21/15 8) "-21/15") +;; bigints +($check string-ci=? (number->string #x1234567890abcdef 16) + "1234567890abcdef") + +; only bases 2, 8, 10, 16 +($check-error (number->string 10 3)) +($check-error (number->string #inert ())) +; only numbers +($check-error (number->string #inert)) +($check-error (number->string #inert 2)) +; only numbers +($check-error (number->string "2")) +($check-error (number->string "2" 8)) +; only base 10 with inexact numbers +($check-error (number->string -1.0 2)) +($check-error (number->string 1.25 8)) +($check-error (number->string 3.0 16)) +\ No newline at end of file