klisp

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

commit 5cea8cb72e00d0e339b3951bc27473975a098cf6
parent 0516e1be441b8e513ceba0c03f89c4896df73bc1
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat, 26 Feb 2011 05:28:32 -0300

Used the vm state & error routine in tokenizer, reader and writer. Used the mem interface in all constructors.

Diffstat:
Msrc/Makefile | 15++++++++-------
Msrc/klisp.c | 25++++++++++++-------------
Msrc/kpair.c | 11++++-------
Msrc/kpair.h | 5+++--
Msrc/kread.c | 352+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/kread.h | 10++--------
Msrc/kstate.c | 26+++++++++++++++++++++++++-
Msrc/kstate.h | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/kstring.c | 33++++++++++++++++++++++++---------
Msrc/kstring.h | 6++++--
Msrc/ksymbol.c | 22++++++++--------------
Msrc/ksymbol.h | 8+++-----
Msrc/ktoken.c | 411+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/ktoken.h | 26+++++---------------------
Msrc/kwrite.c | 210++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/kwrite.h | 11+++--------
16 files changed, 700 insertions(+), 581 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -35,14 +35,15 @@ clean: klisp.o: klisp.c klisp.h kobject.h kread.h kwrite.h klimits.h kstate.h kmem.h \ kerror.h kauxlib.h kobject.o: kobject.c kobject.h -ktoken.o: ktoken.c ktoken.h kobject.h kpair.h kstring.h ksymbol.h -kpair.o: kpair.c kpair.h kobject.h -kstring.o: kstring.c kstring.h kobject.h +ktoken.o: ktoken.c ktoken.h kobject.h kstate.h kpair.h kstring.h ksymbol.h \ + kerror.h +kpair.o: kpair.c kpair.h kobject.h kstate.h kmem.h +kstring.o: kstring.c kstring.h kobject.h kstate.h kmem.h # XXX: kpair.h because of use of list as symbol table -ksymbol.o: ksymbol.c ksymbol.h kobject.h kpair.h kstate.h -kread.o: kread.c kread.h kobject.h ktoken.h kpair.h -kwrite.o: kwrite.c kwrite.h kobject.h kpair.h kstring.h -kstate.o: kstate.c kstate.h klisp.h kobject.h kmem.h +ksymbol.o: ksymbol.c ksymbol.h kobject.h kpair.h kstate.h kmem.h +kread.o: kread.c kread.h kobject.h ktoken.h kpair.h kstate.h kerror.h +kwrite.o: kwrite.c kwrite.h kobject.h kpair.h kstring.h kstate.h kerror.h +kstate.o: kstate.c kstate.h klisp.h kobject.h kmem.h kstring.h kmem.o: kmem.c kmem.h klisp.h kerror.h kerror.o: kerror.c kerror.h klisp.h kstate.h kauxlib.o: kauxlib.c kauxlib.h klisp.h kstate.h \ No newline at end of file diff --git a/src/klisp.c b/src/klisp.c @@ -24,14 +24,14 @@ /* ** Simple read/write loop */ -void main_body() +void main_body(klisp_State *K) { TValue obj = KNIL; while(!ttiseof(obj)) { - obj = kread(); - kwrite(obj); - knewline(); + obj = kread(K); + kwrite(K, obj); + knewline(K); } } @@ -39,13 +39,6 @@ int main(int argc, char *argv[]) { printf("Read/Write Test\n"); - /* TEMP: old initialization */ - kread_file = stdin; - kread_filename = "*STDIN*"; - kwrite_file = stdout; - kread_init(); - kwrite_init(); - klisp_State *K = klispL_newstate(); int ret_value = 0; bool done = false; @@ -53,12 +46,18 @@ int main(int argc, char *argv[]) while(!done) { if (setjmp(K->error_jb)) { /* error signaled */ - if (!K->error_can_cont) { + if (K->error_can_cont) { + /* XXX: clear stack and char buffer, clear shared dict */ + /* TODO: put these in handlers for read-token, read and write */ + ks_sclear(K); + ks_tbclear(K); + K->shared_dict = KNIL; + } else { ret_value = 1; done = true; } } else { - main_body(); + main_body(K); ret_value = 0; done = true; } diff --git a/src/kpair.c b/src/kpair.c @@ -4,18 +4,15 @@ ** See Copyright Notice in klisp.h */ -/* XXX: for malloc */ -#include <stdlib.h> -/* TODO: use a generalized alloc function */ - #include "kpair.h" #include "kobject.h" +#include "kstate.h" +#include "kmem.h" -/* TODO: Out of memory errors */ /* TEMP: for now all pairs are mutable */ -TValue kcons(TValue car, TValue cdr) +TValue kcons(klisp_State *K, TValue car, TValue cdr) { - Pair *new_pair = malloc(sizeof(Pair)); + Pair *new_pair = klispM_new(K, Pair); new_pair->next = NULL; new_pair->gct = 0; diff --git a/src/kpair.h b/src/kpair.h @@ -8,6 +8,7 @@ #define kpair_h #include "kobject.h" +#include "kstate.h" /* TODO: add type assertions */ /* TODO: add more kc[ad]*r combinations */ @@ -17,10 +18,10 @@ #define kset_car(p_, v_) (kcar(p_) = (v_)) #define kset_cdr(p_, v_) (kcdr(p_) = (v_)) -#define kdummy_cons() (kcons(KNIL, KNIL)) +#define kdummy_cons(st_) (kcons(st_, KNIL, KNIL)) /* TEMP: for now all pairs are mutable */ -TValue kcons(TValue, TValue); +TValue kcons(klisp_State *K, TValue car, TValue cdr); #define kget_source_info(p_) (tv2pair(p_)->si) #define kset_source_info(p_, si_) (kget_source_info(p_) = (si_)) diff --git a/src/kread.c b/src/kread.c @@ -10,36 +10,28 @@ ** ** - Read mutable/immutable objects (cons function should be a parameter) ** this is needed because some functions (like load) return immutable objs -** - Decent error handling mechanism ** */ #include <stdio.h> -/* XXX for malloc */ #include <stdlib.h> -/* TODO: use a generalized alloc function */ -/* TEMP: for out of mem errors */ #include <assert.h> #include "kread.h" #include "kobject.h" #include "kpair.h" #include "ktoken.h" - -/* TODO: move to the global state */ -/* TODO: replace the list with a hashtable */ -TValue shared_dict = KNIL_; -FILE *kread_file = NULL; -char *kread_filename = NULL; +#include "kstate.h" +#include "kerror.h" /* -** Stacks for the read FSM +** Stack for the read FSM ** -** The state stack is never empty while read is in process and +** There is always one state in the stack while read is in process and ** selects the action to be performed on the next read token. ** -** The data saved in the data stack changes according to state: +** The data saved in the stack is below the state and changes according to it: ** ST_FIRST_LIST: pair representing the first pair of the list ** with source info of the '(' token. ** ST_MIDDLE_LIST, ST_LAST_ILIST: two elements, first below, second on top: @@ -54,70 +46,28 @@ char *kread_filename = NULL; ** */ -/* TODO: move to the global state */ typedef enum { ST_READ, ST_SHARED_DEF, ST_LAST_ILIST, ST_PAST_LAST_ILIST, ST_FIRST_LIST, ST_MIDDLE_LIST } state_t; -state_t *sstack; -int sstack_size; -int sstack_i; - -TValue *dstack; -int dstack_size; -int dstack_i; - -/* TEMP: for now stacks are fixed size, use asserts to check */ -#define STACK_INIT_SIZE 1024 +#define push_state(kst_, st_) (ks_spush(kst_, (i2tv((int32_t)(st_))))) +#define get_state(kst_) ((state_t) ivalue(ks_sget(kst_))) +#define pop_state(kst_) (ks_sdpop(kst_)) -#define push_state(st_) ({ assert(sstack_i < sstack_size); \ - sstack[sstack_i++] = (st_); }) -#define pop_state() (--sstack_i) -#define get_state() (sstack[sstack_i-1]) -#define clear_state() (sstack_i = 0) +#define push_data(kst_, st_) (ks_spush(kst_, st_)) +#define get_data(kst_) (ks_sget(kst_)) +#define pop_data(kst_) (ks_sdpop(kst_)) -#define push_data(data_) ({ assert(dstack_i < dstack_size); \ - dstack[dstack_i++] = (data_); }) -#define pop_data() (--dstack_i) -#define get_data() (dstack[dstack_i-1]) -#define clear_data() (dstack_i = 0) /* ** Error management */ -TValue kread_error(char *str) +void kread_error(klisp_State *K, char *str) { - /* TODO: Decide on error handling mechanism for reader (& tokenizer) */ - printf("READ ERROR: %s\n", str); - return KEOF; -} - -/* -** Reader initialization -*/ -void kread_init() -{ - assert(kread_file != NULL); - assert(kread_filename != NULL); - - ktok_file = kread_file; - ktok_source_info.filename = kread_filename; - /* XXX: For now just hardcode it to 8 spaces tab-stop */ - ktok_source_info.tab_width = 8; - ktok_init(); - ktok_reset_source_info(); - - /* XXX: for now use a fixed size for stacks */ - sstack_size = STACK_INIT_SIZE; - clear_state(); - sstack = malloc(STACK_INIT_SIZE*sizeof(state_t)); - assert(sstack != NULL); - - dstack_size = STACK_INIT_SIZE; - clear_data(); - dstack = malloc(STACK_INIT_SIZE*sizeof(TValue)); - assert(dstack != NULL); + /* clear the stack */ + ks_sclear(K); + klispE_throw(K, str, true); } /* @@ -125,50 +75,56 @@ void kread_init() */ /* This is called after kread to clear the shared alist */ -void clear_shared_dict() +void clear_shared_dict(klisp_State *K) { - shared_dict = KNIL; + K->shared_dict = KNIL; } -TValue try_shared_ref(TValue ref_token) +TValue try_shared_ref(klisp_State *K, TValue ref_token) { /* TEMP: for now, only allow fixints in shared tokens */ int32_t ref_num = ivalue(kcdr(ref_token)); - TValue tail = shared_dict; + TValue tail = K->shared_dict; while (!ttisnil(tail)) { TValue head = kcar(tail); if (ref_num == ivalue(kcar(head))) return kcdr(head); tail = kcdr(tail); } - return kread_error("undefined shared ref found"); + + kread_error(K, "undefined shared ref found"); + /* avoid warning */ + return KINERT; } -TValue try_shared_def(TValue def_token, TValue value) +TValue try_shared_def(klisp_State *K, TValue def_token, TValue value) { /* TEMP: for now, only allow fixints in shared tokens */ int32_t ref_num = ivalue(kcdr(def_token)); - TValue tail = shared_dict; + TValue tail = K->shared_dict; while (!ttisnil(tail)) { TValue head = kcar(tail); - if (ref_num == ivalue(kcar(head))) - return kread_error("duplicate shared def found"); + if (ref_num == ivalue(kcar(head))) { + kread_error(K, "duplicate shared def found"); + /* avoid warning */ + return KINERT; + } tail = kcdr(tail); } - /* XXX: what happens on out of mem? & gc?(inner cons is not rooted) */ - shared_dict = kcons(kcons(kcdr(def_token), value), - shared_dict); + /* XXX: what happens on out of mem? & gc? (inner cons is not rooted) */ + K->shared_dict = kcons(K, kcons(K, kcdr(def_token), value), + K->shared_dict); return KINERT; } /* This overwrites a previouly made def, it is used in '() */ /* NOTE: the shared def is guaranteed to exist */ -void change_shared_def(TValue def_token, TValue value) +void change_shared_def(klisp_State *K, TValue def_token, TValue value) { /* TEMP: for now, only allow fixints in shared tokens */ int32_t ref_num = ivalue(kcdr(def_token)); - TValue tail = shared_dict; + TValue tail = K->shared_dict; while (!ttisnil(tail)) { TValue head = kcar(tail); if (ref_num == ivalue(kcar(head))) { @@ -185,9 +141,11 @@ void change_shared_def(TValue def_token, TValue value) ** Reader FSM */ /* TEMP: For now we'll use just one big function */ -TValue kread_fsm() +TValue kread_fsm(klisp_State *K) { - push_state(ST_READ); + assert(ks_sisempty(K)); + assert(ttisnil(K->shared_dict)); + push_state(K, ST_READ); /* read next token or process obj */ bool read_next_token = true; @@ -196,46 +154,54 @@ TValue kread_fsm() /* the source code information of that obj */ TValue obj_si; - while (!(get_state() == ST_READ && !read_next_token)) { + while (!(get_state(K) == ST_READ && !read_next_token)) { if (read_next_token) { - TValue tok = ktok_read_token(); + TValue tok = ktok_read_token(K); if (ttispair(tok)) { /* special token */ switch (chvalue(kcar(tok))) { case '(': { - if (get_state() == ST_PAST_LAST_ILIST) - return kread_error("open paren found after " - "last element of improper list"); - TValue np = kdummy_cons(); + if (get_state(K) == ST_PAST_LAST_ILIST) { + kread_error(K, "open paren found after " + "last element of improper list"); + /* avoid warning */ + return KINERT; + } + TValue np = kdummy_cons(K); /* ** NOTE: the source info of the '(' is temporarily saved ** in np (later it will be replace by the source info ** of the car of the list */ - kset_source_info(np, ktok_get_source_info()); + kset_source_info(np, ktok_get_source_info(K)); /* update the shared def to point to the new list */ - /* NOTE: this is necessary for self referrencing lists */ + /* NOTE: this is necessary for self referencing lists */ /* NOTE: the shared def was already checked for errors */ - if (get_state() == ST_SHARED_DEF) - change_shared_def(kcar(get_data()), np); + if (get_state(K) == ST_SHARED_DEF) { + /* take the state out of the way */ + pop_state(K); + change_shared_def(K, kcar(get_data(K)), np); + push_state(K, ST_SHARED_DEF); + } /* start reading elements of the new list */ - push_state(ST_FIRST_LIST); - push_data(np); + push_data(K, np); + push_state(K, ST_FIRST_LIST); read_next_token = true; break; } case ')': { - switch(get_state()) { + switch(get_state(K)) { case ST_FIRST_LIST: { /* empty list */ /* ** Discard the pair in sdata but ** retain the source info ** Return () for processing */ - TValue fp_with_old_si = get_data(); - pop_data(); - pop_state(); + pop_state(K); + TValue fp_with_old_si = get_data(K); + pop_data(K); + obj = KNIL; obj_si = kget_source_info(fp_with_old_si); read_next_token = false; @@ -243,12 +209,12 @@ TValue kread_fsm() } case ST_MIDDLE_LIST: /* end of list */ case ST_PAST_LAST_ILIST: { /* end of ilist */ + pop_state(K); /* discard info on last pair */ - pop_data(); - pop_state(); - TValue fp_old_si = get_data(); - pop_data(); - pop_state(); + pop_data(K); + pop_state(K); + TValue fp_old_si = get_data(K); + pop_data(K); /* list read ok, process it in next iteration */ obj = kcar(fp_old_si); obj_si = kcdr(fp_old_si); @@ -256,62 +222,84 @@ TValue kread_fsm() break; } case ST_LAST_ILIST: - return kread_error("missing last element in " - "improper list"); + kread_error(K, "missing last element in " + "improper list"); + /* avoid warning */ + return KINERT; case ST_SHARED_DEF: - return kread_error("unmatched closing paren found " - "in shared def"); + kread_error(K, "unmatched closing paren found " + "in shared def"); + /* avoid warning */ + return KINERT; case ST_READ: - return kread_error("unmatched closing paren found"); + kread_error(K, "unmatched closing paren found"); + /* avoid warning */ + return KINERT; default: /* shouldn't happen */ - assert(0); + kread_error(K, "Unknown read state in )"); + /* avoid warning */ + return KINERT; } break; } case '.': { - switch(get_state()) { + switch(get_state(K)) { case (ST_MIDDLE_LIST): /* tok ok, read next obj for cdr of ilist */ - pop_state(); - push_state(ST_LAST_ILIST); + pop_state(K); + push_state(K, ST_LAST_ILIST); read_next_token = true; break; case ST_FIRST_LIST: - return kread_error("missing first element of " - "improper list"); + kread_error(K, "missing first element of " + "improper list"); + /* avoid warning */ + return KINERT; case ST_LAST_ILIST: case ST_PAST_LAST_ILIST: - return kread_error("double dot in improper list"); + kread_error(K, "double dot in improper list"); + /* avoid warning */ + return KINERT; case ST_SHARED_DEF: - return kread_error("dot found in shared def"); + kread_error(K, "dot found in shared def"); + /* avoid warning */ + return KINERT; case ST_READ: - return kread_error("dot found outside list"); + kread_error(K, "dot found outside list"); + /* avoid warning */ + return KINERT; default: /* shouldn't happen */ - assert(0); + kread_error(K, "Unknown read state in ."); + /* avoid warning */ + return KINERT; } break; } case '=': { /* srfi-38 shared def */ - switch (get_state()) { + switch (get_state(K)) { case ST_SHARED_DEF: - return kread_error("shared def found in " - "shared def"); + kread_error(K, "shared def found in " + "shared def"); + /* avoid warning */ + return KINERT; case ST_PAST_LAST_ILIST: - return kread_error("shared def found after " - "last element of improper list"); + kread_error(K, "shared def found after " + "last element of improper list"); + /* avoid warning */ + return KINERT; default: { - TValue res = try_shared_def(tok, KNIL); + TValue res = try_shared_def(K, tok, KNIL); /* TEMP: while error returns EOF */ if (ttiseof(res)) { return res; } else { /* token ok, read defined object */ - push_state(ST_SHARED_DEF); /* NOTE: save the source info to return it after the defined object is read */ - push_data(kcons(tok, ktok_get_source_info())); + push_data(K, kcons(K, tok, ktok_get_source_info(K))); + push_state(K, ST_SHARED_DEF); read_next_token = true; } } @@ -319,15 +307,19 @@ TValue kread_fsm() break; } case '#': { /* srfi-38 shared ref */ - switch(get_state()) { + switch(get_state(K)) { case ST_SHARED_DEF: - return kread_error("shared ref found in " - "shared def"); + kread_error(K, "shared ref found in " + "shared def"); + /* avoid warning */ + return KINERT; case ST_PAST_LAST_ILIST: - return kread_error("shared ref found after " - "last element of improper list"); + kread_error(K, "shared ref found after " + "last element of improper list"); + /* avoid warning */ + return KINERT; default: { - TValue res = try_shared_ref(tok); + TValue res = try_shared_ref(K, tok); /* TEMP: while error returns EOF */ if (ttiseof(res)) { return res; @@ -335,7 +327,7 @@ TValue kread_fsm() /* ref ok, process it in next iteration */ obj = res; /* NOTE: use source info of ref token */ - obj_si = ktok_get_source_info(); + obj_si = ktok_get_source_info(K); read_next_token = false; } } @@ -344,45 +336,59 @@ TValue kread_fsm() } default: /* shouldn't happen */ - assert(0); + kread_error(K, "unknown special token"); + /* avoid warning */ + return KINERT; } } else if (ttiseof(tok)) { - switch (get_state()) { + switch (get_state(K)) { case ST_READ: /* will exit in next loop */ obj = tok; - obj_si = ktok_get_source_info(); + obj_si = ktok_get_source_info(K); read_next_token = false; break; case ST_FIRST_LIST: case ST_MIDDLE_LIST: - return kread_error("EOF found while reading list"); + kread_error(K, "EOF found while reading list"); + /* avoid warning */ + return KINERT; case ST_LAST_ILIST: case ST_PAST_LAST_ILIST: - return kread_error("EOF found while reading " + kread_error(K, "EOF found while reading " "improper list"); + /* avoid warning */ + return KINERT; case ST_SHARED_DEF: - return kread_error("EOF found in shared def"); + kread_error(K, "EOF found in shared def"); + /* avoid warning */ + return KINERT; default: /* shouldn't happen */ - assert(0); + kread_error(K, "unknown read state in EOF"); + /* avoid warning */ + return KINERT; } } else { /* this can only be a complete token */ - if (get_state() == ST_PAST_LAST_ILIST) { - return kread_error("Non paren found after last " - "element of improper list"); + if (get_state(K) == ST_PAST_LAST_ILIST) { + kread_error(K, "Non paren found after last " + "element of improper list"); + /* avoid warning */ + return KINERT; } else { /* token ok, process it in next iteration */ obj = tok; - obj_si = ktok_get_source_info(); + obj_si = ktok_get_source_info(K); read_next_token = false; } } } else { /* if(read_next_token) */ /* process the object just read */ - switch(get_state()) { + switch(get_state(K)) { case ST_FIRST_LIST: { - TValue fp = get_data(); + /* get the state out of the way */ + pop_state(K); + TValue fp = get_data(K); /* replace source info in fp with the saved one */ /* NOTE: the old one will be returned when list is complete */ TValue fp_old_si = kget_source_info(fp); @@ -390,39 +396,43 @@ TValue kread_fsm() kset_car(fp, obj); /* continue reading objects of list */ - push_state(ST_MIDDLE_LIST); - pop_data(); /* save first & last pair of the (still incomplete) list */ - push_data(kcons (fp, fp_old_si)); - push_data(fp); + pop_data(K); + push_data(K, kcons (K, fp, fp_old_si)); + push_state(K, ST_FIRST_LIST); + push_data(K, fp); + push_state(K, ST_MIDDLE_LIST); read_next_token = true; break; } case ST_MIDDLE_LIST: { - TValue np = kcons(obj, KNIL); + /* get the state out of the way */ + pop_state(K); + TValue np = kcons(K, obj, KNIL); kset_source_info(np, obj_si); - kset_cdr(get_data(), np); + kset_cdr(get_data(K), np); /* replace last pair of the (still incomplete) read next obj */ - pop_data(); - push_data(np); + pop_data(K); + push_data(K, np); + push_state(K, ST_MIDDLE_LIST); read_next_token = true; break; } case ST_LAST_ILIST: - kset_cdr(get_data(), obj); - /* only change the state, keep the pair in data to simplify + /* only change the state, keep the pair data to simplify the close paren code (same as for ST_MIDDLE_LIST) */ - pop_state(); - push_state(ST_PAST_LAST_ILIST); + pop_state(K); + kset_cdr(get_data(K), obj); + push_state(K, ST_PAST_LAST_ILIST); read_next_token = true; break; case ST_SHARED_DEF: { /* shared def completed, continue processing obj */ - TValue def_si = get_data(); - pop_state(); - pop_data(); + pop_state(K); + TValue def_si = get_data(K); + pop_data(K); - change_shared_def(kcar(def_si), obj); + change_shared_def(K, kcar(def_si), obj); /* obj = obj; */ /* the source info returned is the one from the shared def */ @@ -432,33 +442,37 @@ TValue kread_fsm() } case ST_READ: /* this shouldn't happen, should've exited the while */ - assert(0); + kread_error(K, "invalid read state (read in while)"); + /* avoid warning */ + return KINERT; default: /* shouldn't happen */ - assert(0); + kread_error(K, "unknown read state in process obj"); + /* avoid warning */ + return KINERT; } } } + pop_state(K); + assert(ks_sisempty(K)); return obj; } /* ** Reader Main Function */ -TValue kread() +TValue kread(klisp_State *K) { TValue obj; /* TEMP: for now assume we are in the repl: reset source info */ - ktok_reset_source_info(); + ktok_reset_source_info(K); - obj = kread_fsm(); + obj = kread_fsm(K); /* NOTE: clear after function to allow earlier gc */ - clear_shared_dict(); - clear_state(); - clear_data(); + clear_shared_dict(K); return obj; } diff --git a/src/kread.h b/src/kread.h @@ -7,19 +7,13 @@ #ifndef kread_h #define kread_h -#include <stdio.h> - #include "kobject.h" +#include "kstate.h" /* ** Reader interface */ -void kread_init(); -TValue kread(); - -/* TODO: move this to the global state */ -FILE *kread_file; -char *kread_filename; +TValue kread(klisp_State *K); #endif diff --git a/src/kstate.c b/src/kstate.c @@ -23,9 +23,16 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { if (k == NULL) return NULL; void *s = (*f)(ud, NULL, 0, KS_ISSIZE * sizeof(TValue)); if (s == NULL) { + (*f)(ud, k, state_size(), 0); + return NULL; + } + void *b = (*f)(ud, NULL, 0, KS_ITBSIZE); + if (b == NULL) { + (*f)(ud, k, state_size(), 0); (*f)(ud, s, KS_ISSIZE * sizeof(TValue), 0); return NULL; } + K = (klisp_State *) k; K->symbol_table = KNIL; @@ -39,6 +46,8 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { /* current input and output */ K->curr_in = stdin; K->curr_out = stdout; + K->filename_in = "*STDIN*"; + K->filename_out = "*STDOUT*"; /* TODO: more gc info */ K->totalbytes = KS_ISSIZE + state_size(); @@ -48,8 +57,23 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K->ssize = KS_ISSIZE; K->stop = 0; /* stack is empty */ - K->sbuf = (TValue **)s; + K->sbuf = (TValue *)s; + + /* initialize tokenizer */ + ks_tbsize(K) = KS_ITBSIZE; + ks_tbidx(K) = 0; /* buffer is empty */ + ks_tbuf(K) = (char *)b; + + /* XXX: For now just hardcode it to 8 spaces tab-stop */ + K->ktok_source_info.tab_width = 8; + K->ktok_source_info.filename = "*STDIN*"; + ktok_init(K); + ktok_reset_source_info(K); + + /* initialize reader */ + K->shared_dict = KNIL; + /* initialize writer */ return K; } diff --git a/src/kstate.h b/src/kstate.h @@ -21,6 +21,20 @@ #include "kobject.h" #include "klimits.h" #include "klisp.h" +#include "ktoken.h" + +/* XXX: for now, lines and column names are fixints */ +/* MAYBE: this should be in tokenizer */ +typedef struct { + char *filename; + int32_t tab_width; + int32_t line; + int32_t col; + + char *saved_filename; + int32_t saved_line; + int32_t saved_col; +} ksource_info_t; struct klisp_State { TValue symbol_table; @@ -40,17 +54,33 @@ struct klisp_State { /* standard input and output */ /* TODO: eventually these should be ports */ - FILE *curr_in; - FILE *curr_out; - - /* auxiliary stack */ - int32_t ssize; /* total size of array */ - int32_t stop; /* top of the stack (all elements are below this index) */ - TValue **sbuf; + FILE *curr_in; + FILE *curr_out; + char *filename_in; + char *filename_out; + + /* tokenizer */ + /* WORKAROUND for repl */ + bool ktok_seen_eof; + ksource_info_t ktok_source_info; + /* tokenizer buffer */ + int32_t ktok_buffer_size; + int32_t ktok_buffer_idx; + char *ktok_buffer; + + /* reader */ + /* TODO: replace the list with a hashtable */ + TValue shared_dict; + + /* auxiliary stack */ + int32_t ssize; /* total size of array */ + int32_t stop; /* top of the stack (all elements are below this index) */ + TValue *sbuf; }; /* some size related macros */ #define KS_ISSIZE (1024) +#define KS_ITBSIZE (1024) #define state_size() (sizeof(klisp_State)) /* @@ -59,22 +89,23 @@ struct klisp_State { ** eliminate it, change it to compiler specific or replace it ** with defines */ + +/* +** Stack functions +*/ inline void ks_spush(klisp_State *K, TValue obj); inline TValue ks_spop(klisp_State *K); /* this is for DISCARDING stack pop (value isn't used, avoid warning) */ -#define ks_dspop(st_) (UNUSED(ks_spop(st_))) +#define ks_sdpop(st_) (UNUSED(ks_spop(st_))) inline TValue ks_sget(klisp_State *K); inline void ks_sclear(klisp_State *K); +inline bool ks_sisempty(klisp_State *K); -/* -** Stack manipulation functions -*/ - -/* Aux Stack manipulation macros */ +/* some stack manipulation macros */ #define ks_ssize(st_) ((st_)->ssize) #define ks_stop(st_) ((st_)->stop) #define ks_sbuf(st_) ((st_)->sbuf) -#define ks_selem(st_, i_) ((*ks_sbuf(st_))[i_]) +#define ks_selem(st_, i_) ((ks_sbuf(st_))[i_]) inline void ks_spush(klisp_State *K, TValue obj) { @@ -114,4 +145,55 @@ inline void ks_sclear(klisp_State *K) ks_stop(K) = 0; } +inline bool ks_sisempty(klisp_State *K) +{ + return ks_stop(K) == 0; +} + +/* +** Tokenizer char buffer functions +*/ + +inline void ks_tbadd(klisp_State *K, char ch); +inline char *ks_tbget(klisp_State *K); +inline void ks_tbclear(klisp_State *K); +inline bool ks_tbisempty(klisp_State *K); + +/* some buf manipulation macros */ +#define ks_tbsize(st_) ((st_)->ktok_buffer_size) +#define ks_tbidx(st_) ((st_)->ktok_buffer_idx) +#define ks_tbuf(st_) ((st_)->ktok_buffer) +#define ks_tbelem(st_, i_) ((ks_tbuf(st_))[i_]) + +inline void ks_tbadd(klisp_State *K, char ch) +{ + if (ks_tbidx(K) == ks_tbsize(K)) { + /* TODO: try realloc */ + assert(0); + } + ks_tbelem(K, ks_tbidx(K)) = ch; + ++ks_tbidx(K); +} + +inline char *ks_tbget(klisp_State *K) +{ + assert(ks_tbelem(K, ks_tbidx(K) - 1) == '\0'); + return ks_tbuf(K); +} + +inline void ks_tbclear(klisp_State *K) +{ + if (ks_tbsize(K) != KS_ITBSIZE) { + /* NOTE: shrink can't fail */ + /* TODO do realloc */ + } + ks_tbsize(K) = KS_ITBSIZE; + ks_tbidx(K) = 0; +} + +inline bool ks_tbisempty(klisp_State *K) +{ + return ks_tbidx(K) == 0; +} + #endif diff --git a/src/kstring.c b/src/kstring.c @@ -4,29 +4,44 @@ ** See Copyright Notice in klisp.h */ -/* XXX: for malloc */ -#include <stdlib.h> -/* TODO: use a generalized alloc function */ - #include <string.h> #include "kstring.h" #include "kobject.h" +#include "kstate.h" +#include "kmem.h" -/* TEMP: for now initialized in ktoken.c */ +/* TEMP: for now initialized in kstate.c */ TValue kempty_string = KINERT_; -/* TODO: Out of memory errors */ +/* TEMP: this is for initializing the above value, for now, from ktoken.h */ +TValue kstring_new_empty(klisp_State *K) +{ + String *new_str; + + new_str = klispM_malloc(K, sizeof(String) + 1); + + new_str->next = NULL; + new_str->gct = 0; + new_str->tt = K_TSTRING; + new_str->mark = KFALSE; + new_str->size = 0; + new_str->b[0] = '\0'; + + return gc2str(new_str); +} + /* TEMP: for now all strings are mutable */ -TValue kstring_new(const char *buf, uint32_t size) +TValue kstring_new(klisp_State *K, const char *buf, uint32_t size) { String *new_str; - if (size == 0 && ttisstring(kempty_string)) { + if (size == 0) { + assert(ttisstring(kempty_string)); return kempty_string; } - new_str = malloc(sizeof(String) + size + 1); + new_str = klispM_malloc(K, sizeof(String) + size + 1); new_str->next = NULL; new_str->gct = 0; diff --git a/src/kstring.h b/src/kstring.h @@ -8,15 +8,17 @@ #define kstring_h #include "kobject.h" +#include "kstate.h" /* TEMP: for now all strings are mutable */ -TValue kstring_new(const char *, uint32_t); +TValue kstring_new_empty(klisp_State *K); +TValue kstring_new(klisp_State *K, const char *buf, uint32_t size); #define kstring_buf(tv_) (((Symbol *) ((tv_).tv.v.gc))->b) #define kstring_size(tv_) (((Symbol *) ((tv_).tv.v.gc))->size) /* The only empty string */ /* TEMP: for now initialized in ktoken.c */ TValue kempty_string; -#define kstring_is_empty(tv_) (tv_equal(tv_, kempty_string)) +#define kstring_is_empty(tv_) (kstring_size(tv_) == 0) #endif diff --git a/src/ksymbol.c b/src/ksymbol.c @@ -4,25 +4,19 @@ ** See Copyright Notice in klisp.h */ -/* XXX: for malloc */ -#include <stdlib.h> -/* TODO: use a generalized alloc function */ - #include <string.h> #include "ksymbol.h" #include "kobject.h" #include "kpair.h" +#include "kstate.h" +#include "kmem.h" -/* TODO: replace the list with a hashtable */ -/* TODO: move to global state */ -TValue ksymbol_table = KNIL_; - -/* TODO: Out of memory errors */ -TValue ksymbol_new(const char *buf) +TValue ksymbol_new(klisp_State *K, const char *buf) { + /* TODO: replace symbol list with hashtable */ /* First look for it in the symbol table */ - TValue tbl = ksymbol_table; + TValue tbl = K->symbol_table; while (!ttisnil(tbl)) { TValue first = kcar(tbl); /* NOTE: there are no embedded '\0's in symbols */ @@ -35,7 +29,7 @@ TValue ksymbol_new(const char *buf) /* Didn't find it, alloc new and save in symbol table */ /* NOTE: there are no embedded '\0's in symbols */ int32_t size = strlen(buf); - Symbol *new_sym = malloc(sizeof(Symbol) + size + 1); + Symbol *new_sym = klispM_malloc(K, sizeof(Symbol) + size + 1); new_sym->next = NULL; new_sym->gct = 0; @@ -45,7 +39,7 @@ TValue ksymbol_new(const char *buf) new_sym->b[size] = '\0'; TValue new_symv = gc2sym(new_sym); - tbl = kcons(new_symv, ksymbol_table); - ksymbol_table = tbl; + /* XXX: new_symv unrooted */ + K->symbol_table = kcons(K, new_symv, K->symbol_table); return new_symv; } diff --git a/src/ksymbol.h b/src/ksymbol.h @@ -8,13 +8,11 @@ #define ksymbol_h #include "kobject.h" - -/* TODO: replace the list with a hashtable */ -/* TODO: move to global state */ -TValue ksymbol_table; +#include "kstate.h" +#include "kmem.h" /* TEMP: for now all symbols are interned */ -TValue ksymbol_new(const char *); +TValue ksymbol_new(klisp_State *K, const char *buf); #define ksymbol_buf(tv_) (((Symbol *) ((tv_).tv.v.gc))->b) diff --git a/src/ktoken.c b/src/ktoken.c @@ -15,7 +15,6 @@ ** From the Report: ** - Support other number types besides fixints and exact infinities ** - Support for complete number syntax (exactness, radix, etc) -** - Error handling ** ** NOT from the Report: ** - Support for unicode (strings, char and symbols). @@ -26,13 +25,8 @@ ** */ #include <stdio.h> -/* XXX for malloc */ #include <stdlib.h> -/* TODO: use a generalized alloc function */ - -/* TEMP: for out of mem errors */ #include <assert.h> - #include <string.h> #include <ctype.h> #include <stdint.h> @@ -40,9 +34,11 @@ #include "ktoken.h" #include "kobject.h" +#include "kstate.h" #include "kpair.h" #include "kstring.h" #include "ksymbol.h" +#include "kerror.h" /* ** Char sets for fast ASCII char classification @@ -129,29 +125,13 @@ kcharset ktok_delimiter, ktok_extended, ktok_subsequent; */ TValue ktok_lparen, ktok_rparen, ktok_dot; -/* TODO: move this to the global state */ -char *ktok_buffer; -uint32_t ktok_buffer_size; -#define KTOK_BUFFER_INITIAL_SIZE 1024 -/* WORKAROUND: for stdin line buffering & reading of EOF */ -bool ktok_seen_eof; - -void ktok_init() +void ktok_init(klisp_State *K) { - /* TEMP: for now initialize empty string here */ - kempty_string = kstring_new("", 0); - - assert(ktok_file != NULL); - assert(ktok_source_info.filename != NULL); - + assert(K->curr_in != NULL); + assert(K->filename_in != NULL); + /* WORKAROUND: for stdin line buffering & reading of EOF */ - ktok_seen_eof = false; - /* string buffer */ - /* TEMP: for now use a fixed size */ - ktok_buffer_size = KTOK_BUFFER_INITIAL_SIZE; - ktok_buffer = malloc(KTOK_BUFFER_INITIAL_SIZE); - /* TEMP: while there is no error handling code */ - assert(ktok_buffer != NULL); + K->ktok_seen_eof = false; /* Character sets */ kcharset_fill(ktok_alphabetic, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -170,36 +150,45 @@ void ktok_init() kcharset_union(ktok_subsequent, ktok_extended); /* Special Tokens */ - ktok_lparen = kcons(ch2tv('('), KNIL); - ktok_rparen = kcons(ch2tv(')'), KNIL); - ktok_dot = kcons(ch2tv('.'), KNIL); + /* TODO: make them uncollectible */ + if (!ttispair(ktok_lparen)) { + ktok_lparen = kcons(K, ch2tv('('), KNIL); + ktok_rparen = kcons(K, ch2tv(')'), KNIL); + ktok_dot = kcons(K, ch2tv('.'), KNIL); + } + + /* Empty string */ + /* TEMP: for now initialize empty string here */ + /* TODO: make it uncollectible */ + if (!ttisstring(kempty_string)) + kempty_string = kstring_new_empty(K); } /* ** Underlying stream interface & source code location tracking */ -int ktok_getc() { +int ktok_getc(klisp_State *K) { /* WORKAROUND: for stdin line buffering & reading of EOF */ - if (ktok_seen_eof) { + if (K->ktok_seen_eof) { return EOF; } else { - int chi = getc(ktok_file); + int chi = getc(K->curr_in); if (chi == EOF) { /* NOTE: eof doesn't change source code location info */ - ktok_seen_eof = true; + K->ktok_seen_eof = true; return EOF; } /* track source code location before returning the char */ if (chi == '\t') { /* align column to next tab stop */ - ktok_source_info.col = - (ktok_source_info.col + ktok_source_info.tab_width) - - (ktok_source_info.col % ktok_source_info.tab_width); + K->ktok_source_info.col = + (K->ktok_source_info.col + K->ktok_source_info.tab_width) - + (K->ktok_source_info.col % K->ktok_source_info.tab_width); return '\t'; } else if (chi == '\n') { - ktok_source_info.line++; - ktok_source_info.col = 0; + K->ktok_source_info.line++; + K->ktok_source_info.col = 0; return '\n'; } else { return chi; @@ -207,75 +196,77 @@ int ktok_getc() { } } -int ktok_peekc() { +int ktok_peekc(klisp_State *K) { /* WORKAROUND: for stdin line buffering & reading of EOF */ - if (ktok_seen_eof) { + if (K->ktok_seen_eof) { return EOF; } else { - int chi = getc(ktok_file); + int chi = getc(K->curr_in); if (chi == EOF) - ktok_seen_eof = true; + K->ktok_seen_eof = true; else - ungetc(chi, ktok_file); + ungetc(chi, K->curr_in); return chi; } } -void ktok_reset_source_info() +void ktok_reset_source_info(klisp_State *K) { /* line is 1-base and col is 0-based */ - ktok_source_info.line = 1; - ktok_source_info.col = 0; + K->ktok_source_info.line = 1; + K->ktok_source_info.col = 0; } -void ktok_save_source_info() +void ktok_save_source_info(klisp_State *K) { - ktok_source_info.saved_filename = ktok_source_info.filename; - ktok_source_info.saved_line = ktok_source_info.line; - ktok_source_info.saved_col = ktok_source_info.col; + K->ktok_source_info.saved_filename = K->ktok_source_info.filename; + K->ktok_source_info.saved_line = K->ktok_source_info.line; + K->ktok_source_info.saved_col = K->ktok_source_info.col; } -TValue ktok_get_source_info() +TValue ktok_get_source_info(klisp_State *K) { /* XXX: what happens on gc? (unrooted objects) */ /* NOTE: the filename doesn't contains embedded '\0's */ - TValue filename_str = kstring_new(ktok_source_info.saved_filename, - strlen(ktok_source_info.saved_filename)); + TValue filename_str = + kstring_new(K, K->ktok_source_info.saved_filename, + strlen(K->ktok_source_info.saved_filename)); /* TEMP: for now, lines and column names are fixints */ - return kcons(filename_str, kcons(i2tv(ktok_source_info.saved_line), - i2tv(ktok_source_info.saved_col))); + return kcons(K, filename_str, kcons(K, i2tv(K->ktok_source_info.saved_line), + i2tv(K->ktok_source_info.saved_col))); } /* ** Error management */ -TValue ktok_error(char *str) +void ktok_error(klisp_State *K, char *str) { - /* TODO: Decide on error handling mechanism for reader (& tokenizer) */ - /* TEMP: Use eof object */ - printf("TOK ERROR: %s\n", str); - return KEOF; + /* clear the buffer before throwing an error */ + ks_tbclear(K); + klispE_throw(K, str, true); } /* ** ktok_read_token() helpers */ -void ktok_ignore_whitespace_and_comments(); -bool ktok_check_delimiter(); -TValue ktok_read_string(); -TValue ktok_read_special(); -TValue ktok_read_number(bool); -TValue ktok_read_maybe_signed_numeric(); -TValue ktok_read_identifier(); -int ktok_read_until_delimiter(); +void ktok_ignore_whitespace_and_comments(klisp_State *K); +bool ktok_check_delimiter(klisp_State *K); +TValue ktok_read_string(klisp_State *K); +TValue ktok_read_special(klisp_State *K); +TValue ktok_read_number(klisp_State *K, bool sign); +TValue ktok_read_maybe_signed_numeric(klisp_State *K); +TValue ktok_read_identifier(klisp_State *K); +int ktok_read_until_delimiter(klisp_State *K); /* ** Main tokenizer function */ -TValue ktok_read_token () +TValue ktok_read_token (klisp_State *K) { - ktok_ignore_whitespace_and_comments(); + assert(ks_tbisempty(K)); + + ktok_ignore_whitespace_and_comments(K); /* ** NOTE: We jumped over all whitespace ** so either the next token starts here or eof was reached, @@ -283,35 +274,38 @@ TValue ktok_read_token () */ /* save the source info of the start of the next token */ - ktok_save_source_info(); + ktok_save_source_info(K); - int chi = ktok_peekc(); + int chi = ktok_peekc(K); switch(chi) { case EOF: - ktok_getc(); + ktok_getc(K); return KEOF; case '(': - ktok_getc(); + ktok_getc(K); return ktok_lparen; case ')': - ktok_getc(); + ktok_getc(K); return ktok_rparen; case '.': - ktok_getc(); - if (ktok_check_delimiter()) + ktok_getc(K); + if (ktok_check_delimiter(K)) return ktok_dot; - else - return ktok_error("no delimiter found after dot"); + else { + ktok_error(K, "no delimiter found after dot"); + /* avoid warning */ + return KINERT; + } case '"': - return ktok_read_string(); + return ktok_read_string(K); case '#': - return ktok_read_special(); + return ktok_read_special(K); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - return ktok_read_number(true); /* positive number */ + return ktok_read_number(K, true); /* positive number */ case '+': case '-': - return ktok_read_maybe_signed_numeric(); + return ktok_read_maybe_signed_numeric(K); case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': @@ -328,39 +322,41 @@ TValue ktok_read_token () ** considered so identifier-subsequent is used instead of ** identifier-first-char (in the cases above) */ - return ktok_read_identifier(); + return ktok_read_identifier(K); default: - ktok_getc(); - return ktok_error("unrecognized token starting char"); + ktok_getc(K); + ktok_error(K, "unrecognized token starting char"); + /* avoid warning */ + return KINERT; } } /* ** Comments and Whitespace */ -void ktok_ignore_comment() +void ktok_ignore_comment(klisp_State *K) { int chi; do { - chi = ktok_getc(); + chi = ktok_getc(K); } while (chi != EOF && chi != '\n'); } -void ktok_ignore_whitespace_and_comments() +void ktok_ignore_whitespace_and_comments(klisp_State *K) { /* NOTE: if it's not a whitespace or comment do nothing (even on eof) */ bool end = false; while(!end) { - int chi = ktok_peekc(); + int chi = ktok_peekc(K); if (chi == EOF) { end = true; } else { char ch = (char) chi; if (ktok_is_whitespace(ch)) { - ktok_getc(); + ktok_getc(K); } else if (ch == ';') { - ktok_ignore_comment(); /* NOTE: this also reads again the ';' */ + ktok_ignore_comment(K); /* NOTE: this also reads again the ';' */ } else { end = true; } @@ -371,28 +367,26 @@ void ktok_ignore_whitespace_and_comments() /* ** Delimiter checking */ -bool ktok_check_delimiter() +bool ktok_check_delimiter(klisp_State *K) { - int chi = ktok_peekc(); + int chi = ktok_peekc(K); return (ktok_is_delimiter(chi)); } /* ** Returns the number of bytes read */ -int ktok_read_until_delimiter() +int ktok_read_until_delimiter(klisp_State *K) { int i = 0; - while (!ktok_check_delimiter()) { - /* TODO: allow buffer to grow */ - assert(i + 1 < ktok_buffer_size); - + while (!ktok_check_delimiter(K)) { /* NOTE: can't be eof, because eof is a delimiter */ - char ch = (char) ktok_getc(); - ktok_buffer[i++] = ch; + char ch = (char) ktok_getc(K); + ks_tbadd(K, ch); + i++; } - ktok_buffer[i] = '\0'; + ks_tbadd(K, '\0'); return i; } @@ -400,15 +394,18 @@ int ktok_read_until_delimiter() ** Numbers ** TEMP: for now, only fixints in base 10 */ -TValue ktok_read_number(bool is_pos) +TValue ktok_read_number(klisp_State *K, bool is_pos) { int32_t res = 0; - while(!ktok_check_delimiter()) { + while(!ktok_check_delimiter(K)) { /* NOTE: can't be eof because it's a delimiter */ - char ch = (char) ktok_getc(); - if (!ktok_is_numeric(ch)) - return ktok_error("Not a digit found in number"); + char ch = (char) ktok_getc(K); + if (!ktok_is_numeric(ch)) { + ktok_error(K, "Not a digit found in number"); + /* avoid warning */ + return KINERT; + } res = res * 10 + ktok_digit_value(ch); } @@ -417,109 +414,136 @@ TValue ktok_read_number(bool is_pos) return i2tv(res); } -TValue ktok_read_maybe_signed_numeric() +TValue ktok_read_maybe_signed_numeric(klisp_State *K) { /* NOTE: can't be eof, it's either '+' or '-' */ - char ch = (char) ktok_getc(); - if (ktok_check_delimiter()) { - ktok_buffer[0] = ch; - ktok_buffer[1] = '\0'; - return ksymbol_new(ktok_buffer); + char ch = (char) ktok_getc(K); + if (ktok_check_delimiter(K)) { + ks_tbadd(K, ch); + ks_tbadd(K, '\0'); + TValue new_sym = ksymbol_new(K, ks_tbuf(K)); + ks_tbclear(K); + return new_sym; } else { - return ktok_read_number(ch == '+'); + return ktok_read_number(K, ch == '+'); } } /* ** Strings */ -TValue ktok_read_string() +TValue ktok_read_string(klisp_State *K) { /* discard opening quote */ - ktok_getc(); + ktok_getc(K); bool done = false; int i = 0; while(!done) { - int chi = ktok_getc(); + int chi = ktok_getc(K); char ch = (char) chi; - if (chi == EOF) - return ktok_error("EOF found while reading a string"); + if (chi == EOF) { + ktok_error(K, "EOF found while reading a string"); + /* avoid warning */ + return KINERT; + } if (ch == '"') { - ktok_buffer[i] = '\0'; + ks_tbadd(K, '\0'); done = true; } else { if (ch == '\\') { - chi = ktok_getc(); + chi = ktok_getc(K); - if (chi == EOF) - return ktok_error("EOF found while reading a string"); + if (chi == EOF) { + ktok_error(K, "EOF found while reading a string"); + /* avoid warning */ + return KINERT; + } ch = (char) chi; if (ch != '\\' && ch != '"') { - return ktok_error("Invalid char after '\\' " - "while reading a string"); + ktok_error(K, "Invalid char after '\\' " + "while reading a string"); + /* avoid warning */ + return KINERT; } } - /* TODO: allow buffer to grow */ - assert(i+1 < ktok_buffer_size); - - ktok_buffer[i++] = ch; + ks_tbadd(K, ch); + i++; } } - return kstring_new(ktok_buffer, i); + TValue new_str = kstring_new(K, ks_tbuf(K), i); + ks_tbclear(K); + return new_str; } /* ** Special constants (starting with "#") ** (Special number syntax, char constants, #ignore, #inert, srfi-38 tokens) */ -TValue ktok_read_special() +TValue ktok_read_special(klisp_State *K) { /* discard the '#' */ - ktok_getc(); + ktok_getc(K); - int chi = ktok_getc(); + int chi = ktok_getc(K); char ch = (char) chi; - if (chi == EOF) - return ktok_error("EOF found while reading a '#' constant"); + if (chi == EOF) { + ktok_error(K, "EOF found while reading a '#' constant"); + /* avoid warning */ + return KINERT; + } switch(ch) { - case 'i': + case 'i': { /* ignore or inert */ /* XXX: could also be an inexact number */ - ktok_read_until_delimiter(); + ktok_read_until_delimiter(K); /* NOTE: can use strcmp even in the presence of '\0's */ - if (strcmp(ktok_buffer, "gnore") == 0) - return KIGNORE; - else if (strcmp(ktok_buffer, "nert") == 0) + TValue ret_val; + if (strcmp(ks_tbuf(K), "gnore") == 0) + ret_val = KIGNORE; + else if (strcmp(ks_tbuf(K), "nert") == 0) + ret_val = KINERT; + else { + ktok_error(K, "unexpected char in # constant"); + /* avoid warning */ return KINERT; - else - return ktok_error("unexpected char in # constant"); + } + ks_tbclear(K); + return ret_val; + } case 'e': /* an exact infinity */ /* XXX: could also be an exact number */ - if (ktok_read_until_delimiter()) { - /* NOTE: can use strcmp even in the presence of '\0's */ - if (strcmp(ktok_buffer, "+infinity") == 0) - return KEPINF; - else if (strcmp(ktok_buffer, "-infinity") == 0) - return KEMINF; - else - return ktok_error("unexpected char in # constant"); - } else - return ktok_error("unexpected error in # constant"); + ktok_read_until_delimiter(K); + TValue ret_val; + /* NOTE: can use strcmp even in the presence of '\0's */ + if (strcmp(ks_tbuf(K), "+infinity") == 0) { + ret_val = KEPINF; + } else if (strcmp(ks_tbuf(K), "-infinity") == 0) { + ret_val = KEMINF; + } else { + ktok_error(K, "unexpected char in # constant"); + /* avoid warning */ + return KINERT; + } + ks_tbclear(K); + return ret_val; case 't': case 'f': /* boolean constant */ - if (ktok_check_delimiter()) + if (ktok_check_delimiter(K)) return b2tv(ch == 't'); - else - return ktok_error("unexpected char in # constant"); + else { + ktok_error(K, "unexpected char in # constant"); + /* avoid warning */ + return KINERT; + } case '\\': /* char constant */ /* @@ -530,75 +554,88 @@ TValue ktok_read_special() ** Kernel report (R-1RK)) ** For now we follow the scheme report */ - chi = ktok_getc(); + chi = ktok_getc(K); ch = (char) chi; - if (chi == EOF) - return ktok_error("EOF found while reading a char constant"); + if (chi == EOF) { + ktok_error(K, "EOF found while reading a char constant"); + /* avoid warning */ + return KINERT; + } - if (!ktok_is_alphabetic(ch) || ktok_check_delimiter()) + if (!ktok_is_alphabetic(ch) || ktok_check_delimiter(K)) return ch2tv(ch); - ktok_read_until_delimiter(); - char *p = ktok_buffer; + ktok_read_until_delimiter(K); + char *p = ks_tbuf(K); while (*p) { *p = tolower(*p); p++; } ch = tolower(ch); /* NOTE: can use strcmp even in the presence of '\0's */ - if (ch == 's' && strcmp(ktok_buffer, "pace") == 0) - return ch2tv(' '); - else if (ch == 'n' && strcmp(ktok_buffer, "ewline") == 0) - return ch2tv('\n'); - else - return ktok_error("Unrecognized character name"); + if (ch == 's' && strcmp(ks_tbuf(K), "pace") == 0) + ch = ' '; + else if (ch == 'n' && strcmp(ks_tbuf(K), "ewline") == 0) + ch = ('\n'); + else { + ktok_error(K, "Unrecognized character name"); + /* avoid warning */ + return KINERT; + } + ks_tbclear(K); + return ch2tv(ch); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* srfi-38 type token (can be either a def or ref) */ /* TODO: allow bigints */ int32_t res = 0; while(ch != '#' && ch != '=') { - if (!ktok_is_numeric(ch)) - return ktok_error("Invalid char found in srfi-38 token"); + if (!ktok_is_numeric(ch)) { + ktok_error(K, "Invalid char found in srfi-38 token"); + /* avoid warning */ + return KINERT; + } res = res * 10 + ktok_digit_value(ch); - chi = ktok_getc(); + chi = ktok_getc(K); ch = (char) chi; - if (chi == EOF) - return ktok_error("EOF found while reading a srfi-38 token"); + if (chi == EOF) { + ktok_error(K, "EOF found while reading a srfi-38 token"); + /* avoid warning */ + return KINERT; + } } - return kcons(ch2tv(ch), i2tv(res)); + return kcons(K, ch2tv(ch), i2tv(res)); } /* TODO: add real with no primary value and undefined */ default: - return ktok_error("unexpected char in # constant"); + ktok_error(K, "unexpected char in # constant"); + /* avoid warning */ + return KINERT; } } /* ** Identifiers */ -TValue ktok_read_identifier() +TValue ktok_read_identifier(klisp_State *K) { - int i = 0; - - while (!ktok_check_delimiter()) { - /* TODO: allow buffer to grow */ - assert(i+1 < ktok_buffer_size); - + while (!ktok_check_delimiter(K)) { /* NOTE: can't be eof, because eof is a delimiter */ - char ch = (char) ktok_getc(); + char ch = (char) ktok_getc(K); /* NOTE: is_subsequent of '\0' is false, so no embedded '\0' */ if (ktok_is_subsequent(ch)) - ktok_buffer[i++] = ch; + ks_tbadd(K, ch); else - return ktok_error("Invalid char in identifier"); + ktok_error(K, "Invalid char in identifier"); } - ktok_buffer[i] = '\0'; - return ksymbol_new(ktok_buffer); + ks_tbadd(K, '\0'); + TValue new_sym = ksymbol_new(K, ks_tbuf(K)); + ks_tbclear(K); + return new_sym; } diff --git a/src/ktoken.h b/src/ktoken.h @@ -8,32 +8,16 @@ #define ktoken_h #include "kobject.h" +#include "kstate.h" #include <stdio.h> /* ** Tokenizer interface */ -void ktok_init(); -TValue ktok_read_token(); -void ktok_reset_source_info(); -TValue ktok_get_source_info(); - -/* TODO: move this to the global state */ -FILE *ktok_file; - -/* XXX: for now, lines and column names are fixints */ -typedef struct { - char *filename; - int32_t tab_width; - int32_t line; - int32_t col; - - char *saved_filename; - int32_t saved_line; - int32_t saved_col; -} ksource_info_t; - -ksource_info_t ktok_source_info; +void ktok_init(klisp_State *K); +TValue ktok_read_token(klisp_State *K); +void ktok_reset_source_info(klisp_State *K); +TValue ktok_get_source_info(klisp_State *K); #endif diff --git a/src/kwrite.c b/src/kwrite.c @@ -5,10 +5,7 @@ */ #include <stdio.h> -/* XXX for malloc */ #include <stdlib.h> -/* TODO: use a generalized alloc function */ -/* TEMP: for out of mem errors */ #include <assert.h> #include <inttypes.h> @@ -17,49 +14,41 @@ #include "kpair.h" #include "kstring.h" #include "ksymbol.h" - -/* TODO: move to the global state */ -FILE *kwrite_file = NULL; -/* TEMP: for now use fixints for shared refs */ -int32_t kw_shared_count; +#include "kstate.h" +#include "kerror.h" /* ** Stack for the write FSM ** */ -/* TODO: move to the global state */ -TValue *kw_dstack; -int kw_dstack_size; -int kw_dstack_i; - -/* TEMP: for now stacks are fixed size, use asserts to check */ -#define STACK_INIT_SIZE 1024 - -#define push_data(data_) ({ assert(kw_dstack_i < kw_dstack_size); \ - kw_dstack[kw_dstack_i++] = data_; }) -#define pop_data() (--kw_dstack_i) -#define get_data() (kw_dstack[kw_dstack_i-1]) -#define data_is_empty() (kw_dstack_i == 0) -#define clear_data() (kw_dstack_i = 0) +#define push_data(ks_, data_) (ks_spush(ks_, data_)) +#define pop_data(ks_) (ks_sdpop(ks_)) +#define get_data(ks_) (ks_sget(ks_)) +#define data_is_empty(ks_) (ks_sisempty(ks_)) /* macro for printing */ -#define kw_printf(...) fprintf(kwrite_file, __VA_ARGS__) -#define kw_flush() fflush(kwrite_file) +#define kw_printf(ks_, ...) fprintf((ks_)->curr_out, __VA_ARGS__) +#define kw_flush(ks_) fflush((ks_)->curr_out) + +void kwrite_error(klisp_State *K, char *msg) +{ + klispE_throw(K, msg, true); +} /* ** Helper for printing strings (correcly escapes backslashes and ** double quotes & prints embedded '\0's). It includes the surrounding ** double quotes. */ -void kw_print_string(TValue str) +void kw_print_string(klisp_State *K, TValue str) { int size = kstring_size(str); char *buf = kstring_buf(str); char *ptr = buf; int i = 0; - kw_printf("\""); + kw_printf(K, "\""); while (i < size) { /* find the longest printf-able substring to avoid calling printf @@ -72,58 +61,47 @@ void kw_print_string(TValue str) first or last time) */ char ch = *ptr; *ptr = '\0'; - printf("%s", buf); + kw_printf(K, "%s", buf); *ptr = ch; while(i < size && (*ptr == '\0' || *ptr == '\\' || *ptr == '"')) { if (*ptr == '\0') - printf("%c", '\0'); /* this may not show in the terminal */ + kw_printf(K, "%c", '\0'); /* this may not show in the terminal */ else - printf("\\%c", *ptr); + kw_printf(K, "\\%c", *ptr); i++; ptr++; } buf = ptr; } - kw_printf("\""); -} - -/* -** Writer initialization -*/ -void kwrite_init() -{ - assert(kwrite_file != NULL); - - /* XXX: for now use a fixed size for stack */ - kw_dstack_size = STACK_INIT_SIZE; - clear_data(); - kw_dstack = malloc(STACK_INIT_SIZE*sizeof(TValue)); - assert(kw_dstack != NULL); + kw_printf(K, "\""); } /* ** Mark initialization and clearing */ -void kw_clear_marks(TValue root) +void kw_clear_marks(klisp_State *K, TValue root) { - push_data(root); + + assert(ks_sisempty(K)); + push_data(K, root); - while(!data_is_empty()) { - TValue obj = get_data(); - pop_data(); + while(!data_is_empty(K)) { + TValue obj = get_data(K); + pop_data(K); if (ttispair(obj)) { if (kis_marked(obj)) { kunmark(obj); - push_data(kcdr(obj)); - push_data(kcar(obj)); + push_data(K, kcdr(obj)); + push_data(K, kcar(obj)); } } else if (ttisstring(obj) && (kis_marked(obj))) { kunmark(obj); } } + assert(ks_sisempty(K)); } /* @@ -137,19 +115,20 @@ void kw_clear_marks(TValue root) ** find repetitions and to allow unmarking after write */ -void kw_set_initial_marks(TValue root) +void kw_set_initial_marks(klisp_State *K, TValue root) { - push_data(root); + assert(ks_sisempty(K)); + push_data(K, root); - while(!data_is_empty()) { - TValue obj = get_data(); - pop_data(); + while(!data_is_empty(K)) { + TValue obj = get_data(K); + pop_data(K); if (ttispair(obj)) { if (kis_unmarked(obj)) { kmark(obj); /* this mark just means visited */ - push_data(kcdr(obj)); - push_data(kcar(obj)); + push_data(K, kcdr(obj)); + push_data(K, kcar(obj)); } else { /* this mark means it will need a ref number */ kset_mark(obj, i2tv(-1)); @@ -164,25 +143,28 @@ void kw_set_initial_marks(TValue root) } /* all other types of object don't matter */ } + assert(ks_sisempty(K)); } /* ** Writes all values except strings and pairs */ -void kwrite_simple(TValue obj) +void kwrite_simple(klisp_State *K, TValue obj) { switch(ttype(obj)) { case K_TSTRING: - /* this shouldn't happen */ - assert(0); + /* shouldn't happen */ + kwrite_error(K, "string type found in kwrite-simple"); + /* avoid warning */ + return; case K_TEINF: - kw_printf("#e%cinfinity", tv_equal(obj, KEPINF)? '+' : '-'); + kw_printf(K, "#e%cinfinity", tv_equal(obj, KEPINF)? '+' : '-'); break; case K_TFIXINT: - kw_printf("%" PRId32, ivalue(obj)); + kw_printf(K, "%" PRId32, ivalue(obj)); break; case K_TNIL: - kw_printf("()"); + kw_printf(K, "()"); break; case K_TCHAR: { char ch_buf[4]; @@ -198,53 +180,61 @@ void kwrite_simple(TValue obj) ch_buf[1] = '\0'; ch_ptr = ch_buf; } - kw_printf("#\\%s", ch_ptr); + kw_printf(K, "#\\%s", ch_ptr); break; } case K_TBOOLEAN: - kw_printf("#%c", bvalue(obj)? 't' : 'f'); + kw_printf(K, "#%c", bvalue(obj)? 't' : 'f'); break; case K_TSYMBOL: /* TEMP: access symbol structure directly */ /* TEMP: for now assume all symbols have external representations */ - kw_printf("%s", ksymbol_buf(obj)); + kw_printf(K, "%s", ksymbol_buf(obj)); break; case K_TINERT: - kw_printf("#inert"); + kw_printf(K, "#inert"); break; case K_TIGNORE: - kw_printf("#ignore"); + kw_printf(K, "#ignore"); break; case K_TEOF: - kw_printf("[eof]"); + kw_printf(K, "[eof]"); break; default: /* shouldn't happen */ - assert(0); + kwrite_error(K, "unknown object type"); + /* avoid warning */ + return; } } -void kwrite_fsm() +void kwrite_fsm(klisp_State *K, TValue obj) { + /* NOTE: a fixint is more than enough for output */ + int32_t kw_shared_count = 0; + + assert(ks_sisempty(K)); + push_data(K, obj); + bool middle_list = false; - while (!data_is_empty()) { - TValue obj = get_data(); - pop_data(); + while (!data_is_empty(K)) { + TValue obj = get_data(K); + pop_data(K); if (middle_list) { if (ttisnil(obj)) { /* end of list */ - kw_printf(")"); + kw_printf(K, ")"); /* middle_list = true; */ } else if (ttispair(obj) && ttisboolean(kget_mark(obj))) { - push_data(kcdr(obj)); - push_data(kcar(obj)); - kw_printf(" "); + push_data(K, kcdr(obj)); + push_data(K, kcar(obj)); + kw_printf(K, " "); middle_list = false; } else { /* improper list is the same as shared ref */ - kw_printf(" . "); - push_data(KNIL); - push_data(obj); + kw_printf(K, " . "); + push_data(K, KNIL); + push_data(K, obj); middle_list = false; } } else { /* if (middle_list) */ @@ -252,78 +242,70 @@ void kwrite_fsm() case K_TPAIR: { TValue mark = kget_mark(obj); if (ttisboolean(mark)) { /* simple pair (only once) */ - kw_printf("("); - push_data(kcdr(obj)); - push_data(kcar(obj)); + kw_printf(K, "("); + push_data(K, kcdr(obj)); + push_data(K, kcar(obj)); middle_list = false; } else if (ivalue(mark) < 0) { /* pair with no assigned # */ /* TEMP: for now only fixints in shared refs */ assert(kw_shared_count >= 0); + kset_mark(obj, i2tv(kw_shared_count)); - kw_printf("#%" PRId32 "=(", kw_shared_count); + kw_printf(K, "#%" PRId32 "=(", kw_shared_count); kw_shared_count++; - push_data(kcdr(obj)); - push_data(kcar(obj)); + push_data(K, kcdr(obj)); + push_data(K, kcar(obj)); middle_list = false; } else { /* string with an assigned number */ - kw_printf("#%" PRId32 "#", ivalue(mark)); + kw_printf(K, "#%" PRId32 "#", ivalue(mark)); middle_list = true; } break; } case K_TSTRING: { if (kstring_is_empty(obj)) { - kw_printf("\"\""); + kw_printf(K, "\"\""); } else { TValue mark = kget_mark(obj); if (ttisboolean(mark)) { /* simple string (only once) */ - kw_print_string(obj); + kw_print_string(K, obj); } else if (ivalue(mark) < 0) { /* string with no assigned # */ /* TEMP: for now only fixints in shared refs */ assert(kw_shared_count >= 0); kset_mark(obj, i2tv(kw_shared_count)); - kw_printf("#%" PRId32 "=", kw_shared_count); + kw_printf(K, "#%" PRId32 "=", kw_shared_count); kw_shared_count++; - kw_print_string(obj); + kw_print_string(K, obj); } else { /* string with an assigned number */ - kw_printf("#%" PRId32 "#", ivalue(mark)); + kw_printf(K, "#%" PRId32 "#", ivalue(mark)); } } middle_list = true; break; } default: - kwrite_simple(obj); + kwrite_simple(K, obj); middle_list = true; } } } - return; + + assert(ks_sisempty(K)); } /* ** Writer Main function */ -void kwrite(TValue obj) +void kwrite(klisp_State *K, TValue obj) { - assert(data_is_empty()); - - kw_shared_count = 0; - kw_set_initial_marks(obj); - - push_data(obj); - kwrite_fsm(); - kw_flush(); - - kw_clear_marks(obj); - - assert(data_is_empty()); - return; + kw_set_initial_marks(K, obj); + kwrite_fsm(K, obj); + kw_flush(K); + kw_clear_marks(K, obj); } -void knewline() +void knewline(klisp_State *K) { - kw_printf("\n"); - kw_flush(); - return; + kw_printf(K, "\n"); + kw_flush(K); } diff --git a/src/kwrite.h b/src/kwrite.h @@ -7,19 +7,14 @@ #ifndef kwrite_h #define kwrite_h -#include <stdio.h> - #include "kobject.h" +#include "kstate.h" /* ** Writer interface */ -void kwrite_init(); -void kwrite(TValue); -void knewline(); - -/* TODO: move this to the global state */ -FILE *kwrite_file; +void kwrite(klisp_State *K, TValue obj); +void knewline(klisp_State *K); #endif