klisp

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

commit 99f6d8eadaaefaba9c56f02a4b15f22675431a0e
parent bc4220d6b34b418e39d4470c650b7567345c6533
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Fri,  2 Dec 2011 03:45:56 -0300

Added string->number to the ground environment.

Diffstat:
MTODO | 2--
Msrc/kgnumbers.c | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/tests/numbers.k | 53++++++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 161 insertions(+), 6 deletions(-)

diff --git a/TODO b/TODO @@ -21,8 +21,6 @@ * documentation ** update the manual with the current features ** add a section to the manual with the interpreter usage -* applicatives: -** string->number (r7rs) * reader/writer ** syntax support for complex numbers (Kernel report) * library diff --git a/src/kgnumbers.c b/src/kgnumbers.c @@ -11,6 +11,7 @@ #include <assert.h> #include <stdio.h> +#include <string.h> #include <stdlib.h> #include <stdbool.h> #include <stdint.h> @@ -2272,6 +2273,8 @@ void kexpt(klisp_State *K) /* Number<->String conversion */ void number_to_string(klisp_State *K) { + /* MAYBE this code could be factored out and used in kwrite too, + but maybe it's too much allocation for kwrite in the simpler cases */ TValue *xparams = K->next_xparams; TValue ptree = K->next_value; TValue denv = K->next_env; @@ -2379,9 +2382,116 @@ void number_to_string(klisp_State *K) kapply_cc(K, str); } -/* TODO */ +struct kspecial_number { + const char *ext_rep; /* downcase external representation */ + TValue obj; +} kspecial_numbers[] = { { "#e+infinity", KEPINF_ }, + { "#e-infinity", KEMINF_ }, + { "#i+infinity", KIPINF_ }, + { "#i-infinity", KIMINF_ }, + { "#real", KRWNPV_ }, + { "#undefined", KUNDEF_ } +}; + void string_to_number(klisp_State *K) { + /* MAYBE try to unify with ktoken */ + + 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, "string", ttisstring, str, maybe_radix); + int radix = 10; + if (get_opt_tpar(K, maybe_radix, "radix (2, 8, 10, or 16)", ttisradix)) + radix = ivalue(maybe_radix); + + /* track length to throw better error msgs */ + char *buf = kstring_buf(str); + int32_t len = kstring_size(str); + + /* if at some point we reach the end of the string + the char will be '\0' and will fail all tests, + so there is no need to test the length explicitly */ + bool has_exactp = false; + bool exactp = false; /* the default exactness will depend on the format */ + bool has_radixp = false; + + TValue res = KINERT; + size_t snum_size = sizeof(kspecial_numbers) / + sizeof(struct kspecial_number); + for (int i = 0; i < snum_size; i++) { + struct kspecial_number number = kspecial_numbers[i]; + /* NOTE: must check type because buf may contain embedded '\0's */ + if (len == strlen(number.ext_rep) && + strcmp(number.ext_rep, buf) == 0) { + res = number.obj; + break; + } + } + if (ttisinert(res)) { + /* number wasn't a special number */ + while (*buf == '#') { + switch(*++buf) { + case 'e': case 'E': case 'i': case 'I': + if (has_exactp) { + klispE_throw_simple_with_irritants( + K, "two exactness prefixes", 1, str); + return; + } + has_exactp = true; + exactp = (*buf == 'e'); + ++buf; + break; + case 'b': case 'B': radix = 2; goto RADIX; + case 'o': case 'O': radix = 8; goto RADIX; + case 'd': case 'D': radix = 10; goto RADIX; + case 'x': case 'X': radix = 16; goto RADIX; + RADIX: + if (has_radixp) { + klispE_throw_simple_with_irritants( + K, "two radix prefixes", 1, str); + return; + } + has_radixp = true; + ++buf; + break; + default: + klispE_throw_simple_with_irritants(K, "unexpected char " + "after #", 1, str); + return; + } + } + + if (radix == 10) { + /* only allow decimals with radix 10 */ + bool decimalp = false; + if (!krational_read_decimal(K, buf, radix, &res, NULL, &decimalp)) { + klispE_throw_simple_with_irritants(K, "Bad format", 1, str); + return; + } + if (decimalp && !has_exactp) { + /* handle decimal format as an explicit #i */ + has_exactp = true; + exactp = false; + } + } else { + if (!krational_read(K, buf, radix, &res, NULL)) { + klispE_throw_simple_with_irritants(K, "Bad format", 1, str); + return; + } + } + + if (has_exactp && !exactp) { + krooted_tvs_push(K, res); + res = kexact_to_inexact(K, res); + krooted_tvs_pop(K); + } + } + kapply_cc(K, res); } /* init ground */ diff --git a/src/tests/numbers.k b/src/tests/numbers.k @@ -425,6 +425,8 @@ ;; not implemented ;; String conversion + +;; 12.? number->string ($check string-ci=? (number->string 0) "0") ($check string-ci=? (number->string 1) "1") ($check string-ci=? (number->string -1) "-1") @@ -455,7 +457,6 @@ ; 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)) @@ -465,4 +466,51 @@ ; 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 +($check-error (number->string 3.0 16)) + +;; 12.? string->number +($check =? (string->number "0") 0) +($check =? (string->number "1") 1) +($check =? (string->number "-1") -1) +($check =? (string->number "10" 2) 2) +($check =? (string->number "-10" 2) -2) +($check =? (string->number "10" 8) 8) +($check =? (string->number "-10" 8) -8) +($check =? (string->number "10" 10) 10) +($check =? (string->number "-10" 10) -10) +($check =? (string->number "10" 16) 16) +($check =? (string->number "-10" 16) -16) +; default base +($check =? (string->number "10") (string->number "10" 10)) +;; infinities, undefined and reals with no primary value +;; #undefined and #real can't be compared with =? +($check equal? (string->number "#undefined") #undefined) +($check equal? (string->number "#real") #real) +($check =? (string->number "#e+infinity") #e+infinity) +($check =? (string->number "#e-infinity") #e-infinity) +($check =? (string->number "#i+infinity") #i+infinity) +($check =? (string->number "#i-infinity") #i-infinity) +;; rationals +($check =? (string->number "13/17") 13/17) +($check =? (string->number "-17/13") -17/13) +($check =? (string->number "-21/15" 8) #o-21/15) +;; bigints +($check =? (string->number "1234567890abcdef" 16) + #x1234567890abcdef) +($check =? (string->number "1234567890ABCDEF" 16) + #x1234567890abcdef) +;; doubles +($check =? (string->number "1.25e10") 1.25e10) +($check =? (string->number "-1.25e10" 10) -1.25e10) + +; only bases 2, 8, 10, 16 +($check-error (string->number "10" 3)) +; only strings +($check-error (string->number #inert)) +($check-error (string->number #inert 2)) +($check-error (string->number 2)) +($check-error (string->number 2 8)) +; only base 10 with inexact numbers +($check-error (string->number "-1.0" 2)) +($check-error (string->number "1.25" 8)) +($check-error (string->number "3.0" 16))