klisp

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

commit e5af907da4402212f37151c50fe61f8d13ab615f
parent 33b87dafccd559d25f27b393fde1b434701e88cd
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat,  9 Apr 2011 23:33:37 -0300

Added printing of bigints (and memory freeing on exit).

Diffstat:
Msrc/kinteger.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/kinteger.h | 20++++++++++++++++++--
Msrc/kobject.h | 23++++++++++++++++++++++-
Msrc/kstate.c | 11+++++++++++
Msrc/kwrite.c | 33+++++++++++++++++++++++++++++++++
5 files changed, 185 insertions(+), 3 deletions(-)

diff --git a/src/kinteger.c b/src/kinteger.c @@ -7,6 +7,7 @@ #include <stdbool.h> #include <stdint.h> #include <inttypes.h> +#include <math.h> #include "kinteger.h" #include "kobject.h" @@ -18,6 +19,8 @@ #define iter_next kbigint_iter_next #define iter_update_last kbigint_iter_update_last +#define LOG_BASE(base) (log(2.0) / log(base)) + Bigint_Node *make_new_node(klisp_State *K, uint32_t digit) { Bigint_Node *node = klispM_new(K, Bigint_Node); @@ -54,6 +57,25 @@ TValue kbigint_new(klisp_State *K, bool sign, uint32_t digit) return gc2bigint(new_bigint); } +/* used in write to destructively get the digits */ +TValue kbigint_copy(klisp_State *K, TValue src) +{ + Bigint *srcb = tv2bigint(src); + /* iterate in little endian mode */ + bind_iter(iter, srcb, false); + uint32_t digit = iter_next(iter); + /* GC: root copy */ + TValue copy = kbigint_new(K, kbigint_sign(srcb), digit); + Bigint *copyb = tv2bigint(copy); + + while(iter_has_next(iter)) { + uint32_t digit = iter_next(iter); + kbigint_add_node(copyb, make_new_node(K, digit)); + } + + return copy; +} + /* This algorithm is like a fused multiply add on bignums, unlike any other function here it modifies bigint. It is used in read and it assumes that bigint is positive */ @@ -82,9 +104,88 @@ void kbigint_add_digit(klisp_State *K, TValue tv_bigint, int32_t base, } } +/* This is used by the writer to get the digits of a number + tv_bigint must be positive */ +int32_t kbigint_remove_digit(klisp_State *K, TValue tv_bigint, int32_t base) +{ + assert(kbigint_has_digits(K, tv_bigint)); + + Bigint *bigint = tv2bigint(tv_bigint); + + /* iterate in big endian mode */ + bind_iter(iter, bigint, true); + + uint32_t result = 0; + uint32_t rest = 0; + uint32_t divisor = base; + + while(iter_has_next(iter)) { + uint64_t dividend = (((uint64_t) rest) << 32) | + (uint64_t) iter_next(iter); + + if (dividend >= divisor) { /* avoid division if possible */ + /* I hope the compiler calculates this only once and + is able to get that this is a 64bits by 32bits division + instruction */ + result = (uint32_t) (dividend / divisor); + rest = (uint32_t) (dividend % divisor); + } else { + result = 0; + rest = (uint32_t) dividend; + } + iter_update_last(iter, result); + } + + /* rest now contains the last digit & result the value of the top node */ + + /* adjust the node list, at most the bigint should lose a node */ + if (bigint->first->digit == 0) { + Bigint_Node *node = kbigint_remove_node(bigint); + klispM_free(K, node); + } + + return (int32_t) rest; +} + +/* This is used by write to test if there is any digit left to print */ +bool kbigint_has_digits(klisp_State *K, TValue tv_bigint) +{ + UNUSED(K); + return kbigint_size(tv2bigint(tv_bigint)) != 0; +} + +bool kbigint_negativep(TValue tv_bigint) +{ + return kbigint_negp(tv2bigint(tv_bigint)); +} + +bool kbigint_positivep(TValue tv_bigint) +{ + return kbigint_posp(tv2bigint(tv_bigint)); +} + /* Mutate the bigint to have the opposite sign, used in read */ void kbigint_invert_sign(TValue tv_bigint) { Bigint *bigint = tv2bigint(tv_bigint); bigint->sign_size = -bigint->sign_size; } + +/* this is used by write to estimate the number of chars necessary to + print the number */ +int32_t kbigint_print_size(TValue tv_bigint, int32_t base) +{ + /* count the number of bits and estimate using the log of + the base */ + Bigint *bigint = tv2bigint(tv_bigint); + + int32_t num_bits = 0; + uint32_t first_digit = bigint->first->digit; + while(first_digit != 0) { + ++num_bits; + first_digit >>= 1; + } + num_bits += 32 * (kbigint_size(bigint)) - 2 ; + /* add 1.5 for safety */ + return (int32_t)(LOG_BASE(base) * num_bits + 1.0); +} diff --git a/src/kinteger.h b/src/kinteger.h @@ -18,6 +18,8 @@ /* NOTE: is uint and has flag to allow INT32_MIN as positive argument */ TValue kbigint_new(klisp_State *K, bool sign, uint32_t digit); +/* used in write to destructively get the digits */ +TValue kbigint_copy(klisp_State *K, TValue src); /* Create a stack allocated bigints from a fixint, useful for mixed operations, relatively light weight compared @@ -34,11 +36,25 @@ TValue kbigint_new(klisp_State *K, bool sign, uint32_t digit); (KUNIQUE_NAME(bigint)).sign_size = (KUNIQUE_NAME(i)) < 0? -1 : 1; \ Bigint *name = &(KUNIQUE_NAME(bigint)); -/* This is used by the reader to destructively add digits to a number */ +/* This is used by the reader to destructively add digits to a number + tv_bigint must be positive */ void kbigint_add_digit(klisp_State *K, TValue tv_bigint, int32_t base, int32_t digit); -/* Mutate the bigint to have the opposite sign, used in read */ +/* This is used by the writer to get the digits of a number + tv_bigint must be positive */ +int32_t kbigint_remove_digit(klisp_State *K, TValue tv_bigint, int32_t base); + +/* This is used by write to test if there is any digit left to print */ +bool kbigint_has_digits(klisp_State *K, TValue tv_bigint); + +bool kbigint_negativep(TValue tv_bigint); +bool kbigint_positivep(TValue tv_bigint); +/* Mutate the bigint to have the opposite sign, used in read & write */ void kbigint_invert_sign(TValue tv_bigint); +/* this is used by write to estimate the number of chars necessary to + print the number */ +int32_t kbigint_print_size(TValue tv_bigint, int32_t base); + #endif diff --git a/src/kobject.h b/src/kobject.h @@ -285,7 +285,7 @@ typedef struct __attribute__ ((__packed__)) { /* macros to access size/sign */ #define kbigint_sign(b_) ((b_)->sign_size < 0) -#define kbigint_negp(b_) (kgigint_sign(b_)) +#define kbigint_negp(b_) (kbigint_sign(b_)) #define kbigint_posp(b_) (!kbigint_sign(b_)) #define kbigint_size(b_) ({ int32_t ss = (b_)->sign_size; \ ss < 0? -ss : ss;}) @@ -354,6 +354,27 @@ inline void kbigint_add_node(Bigint *bigint, Bigint_Node *node) bigint->sign_size += bigint->sign_size < 0? -1 : 1; } +/* Helper for removing a node from the head of the list. + The argument should have NULL as previous for this to work */ +inline Bigint_Node *kbigint_remove_node(Bigint *bigint) +{ + Bigint_Node *head = bigint->first; + assert(head != NULL); + Bigint_Node *tail = (Bigint_Node *) (head->next_xor_prev ^ + (uintptr_t) NULL); + if (tail == NULL) { /* last node removed */ + bigint->sign_size = 0; + bigint->first = bigint->last = (Bigint_Node *) NULL; + } else { + tail->next_xor_prev = (tail->next_xor_prev ^ (uintptr_t) head) ^ + (uintptr_t) NULL; + bigint->first = tail; + bigint->sign_size -= bigint->sign_size < 0? -1 : 1; + } + head->next_xor_prev = 0; /* NULL ^ NULL: 0 */ + return head; +} + typedef struct __attribute__ ((__packed__)) { CommonHeader; TValue mark; /* for cycle/sharing aware algorithms */ diff --git a/src/kstate.c b/src/kstate.c @@ -455,6 +455,17 @@ void klisp_close (klisp_State *K) int type = gch_get_type(obj); switch(type) { + case K_TBIGINT: { + Bigint *bigint = (Bigint *)obj; + int size = kbigint_size(bigint); + Bigint_Node *node; + while(size--) { + node = kbigint_remove_node(bigint); + klispM_free(K, node); + } + klispM_free(K, bigint); + break; + } case K_TPAIR: klispM_free(K, (Pair *)obj); break; diff --git a/src/kwrite.c b/src/kwrite.c @@ -11,6 +11,7 @@ #include "kwrite.h" #include "kobject.h" +#include "kinteger.h" #include "kpair.h" #include "kstring.h" #include "ksymbol.h" @@ -37,6 +38,35 @@ void kwrite_error(klisp_State *K, char *msg) /* TODO: check for return codes and throw error if necessary */ +void kw_print_bigint(klisp_State *K, TValue bigint) +{ + /* XXX: calculate appropiate size & malloc string, + leave space for sign */ + int32_t size = kbigint_print_size(bigint, 10); + TValue buf_str = kstring_new_g(K, size); + /* write backwards so we can use printf later */ + char *buf = kstring_buf(buf_str) + size - 1; + /* GC: root copy */ + TValue copy = kbigint_copy(K, bigint); + /* must work with positive bigint to get the digits */ + if (kbigint_negativep(bigint)) + kbigint_invert_sign(copy); + + while(kbigint_has_digits(K, copy)) { + int32_t digit = kbigint_remove_digit(K, copy, 10); + /* write backwards so we can use printf later */ + /* XXX: use to_digit function */ + *buf-- = '0' + digit; + } + + if (kbigint_negativep(bigint)) + *buf-- = '-'; + + kw_printf(K, "%s", buf+1); + /* MAYBE: we could free the copy & string instead of letting the + gc do it */ +} + /* ** Helper for printing strings (correcly escapes backslashes and ** double quotes & prints embedded '\0's). It includes the surrounding @@ -166,6 +196,9 @@ void kwrite_simple(klisp_State *K, TValue obj) case K_TFIXINT: kw_printf(K, "%" PRId32, ivalue(obj)); break; + case K_TBIGINT: + kw_print_bigint(K, obj); + break; case K_TNIL: kw_printf(K, "()"); break;