klisp

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

commit 4334d29f126a24d21444006f0de2739708b5e63d
parent a1ca4a6a0cbdbcfb8a78abf45226e2553cdd0615
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Tue, 19 Apr 2011 18:30:12 -0300

Added immutable strings. Added string->immutable-string to the ground environment. symbol->string now returns the immutable string that is inside the symbol obj with no copying overhead. PENDING: interning of strings to make eq? work correctly on immutable strings. USE hashtable with weak keys.

Diffstat:
Msrc/kerror.c | 4++--
Msrc/kgequalp.c | 2+-
Msrc/kground.c | 8+++++++-
Msrc/kgstrings.c | 59+++++++++++++++++++++++++++++++++++++++++------------------
Msrc/kgstrings.h | 8+++++---
Msrc/kobject.h | 3+++
Msrc/kread.c | 9---------
Msrc/kstate.c | 4++--
Msrc/kstring.c | 34++++++++++++++++++++--------------
Msrc/kstring.h | 46++++++++++++++++++++++++++++++++++++----------
Msrc/ksymbol.c | 6++----
Msrc/ksymbol.h | 3++-
Msrc/ktoken.c | 8+++++---
Msrc/kwrite.c | 4++--
14 files changed, 128 insertions(+), 70 deletions(-)

diff --git a/src/kerror.c b/src/kerror.c @@ -33,7 +33,7 @@ void clear_buffers(klisp_State *K) void klispE_throw(klisp_State *K, char *msg) { - TValue error_msg = kstring_new(K, msg, strlen(msg)); + TValue error_msg = kstring_new_b_imm(K, msg); /* TEMP */ clear_buffers(K); @@ -53,7 +53,7 @@ void klispE_throw_extra(klisp_State *K, char *msg, char *extra_msg) { strcpy(msg_buf+l1, extra_msg); /* if the mem allocator could throw errors, this could potentially leak msg_buf */ - TValue error_msg = kstring_new(K, msg_buf, tl); + TValue error_msg = kstring_new_bs_imm(K, msg_buf, tl); klispM_freemem(K, msg_buf, tl); clear_buffers(K); diff --git a/src/kgequalp.c b/src/kgequalp.c @@ -181,7 +181,7 @@ bool equal2p(klisp_State *K, TValue obj1, TValue obj2) while(!ks_sisempty(K)) { obj2 = ks_spop(K); obj1 = ks_spop(K); - +/* REFACTOR these ifs: compare both types first, then switch on type */ if (!eq2p(K, obj1, obj2)) { if (ttispair(obj1) && ttispair(obj2)) { /* if they were already compaired, consider equal for now diff --git a/src/kground.c b/src/kground.c @@ -807,7 +807,13 @@ void kinit_ground_env(klisp_State *K) /* 13.2.8? string-copy */ add_applicative(K, ground_env, "string-copy", string_copy, 0); - /* 13.2.9? string-fill! */ + /* 13.2.9? string->immutable-string */ + add_applicative(K, ground_env, "string->immutable-string", + string_to_immutable_string, 0); + + /* TODO: add string-immutable? or general immutable? */ + + /* 13.2.10? string-fill! */ add_applicative(K, ground_env, "string-fill!", string_fillS, 0); /* diff --git a/src/kgstrings.c b/src/kgstrings.c @@ -49,7 +49,7 @@ void make_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) return; } - TValue new_str = kstring_new_sc(K, ivalue(tv_s), fill); + TValue new_str = kstring_new_sf(K, ivalue(tv_s), fill); kapply_cc(K, new_str); } @@ -102,6 +102,9 @@ void string_setS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* TODO show index */ klispE_throw(K, "string-set!: index out of bounds"); return; + } else if (kstring_immutablep(str)) { + klispE_throw(K, "string-set!: immutable string"); + return; } int32_t i = ivalue(tv_i); @@ -130,7 +133,7 @@ inline TValue list_to_string_h(klisp_State *K, char *name, TValue ls) if (pairs == 0) { return K->empty_string; } else { - new_str = kstring_new_g(K, pairs); + new_str = kstring_new_s(K, pairs); char *buf = kstring_buf(new_str); TValue tail = ls; while(pairs--) { @@ -162,14 +165,9 @@ void string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* Helpers for binary predicates */ /* XXX: this should probably be in file kstring.h */ -bool kstring_eqp(TValue str1, TValue str2) -{ - int32_t size = kstring_size(str1); - if (kstring_size(str2) != size) - return false; - else - return ((size == 0) || - memcmp(kstring_buf(str1), kstring_buf(str2), size) == 0); + +bool kstring_eqp(TValue str1, TValue str2) { + return tv_equal(str1, str2) || kstring_equalp(str1, str2); } bool kstring_ci_eqp(TValue str1, TValue str2) @@ -241,6 +239,7 @@ bool kstring_ci_gep(TValue str1, TValue str2) } /* 13.2.5? substring */ +/* TEMP: at least for now this always returns mutable strings */ void substring(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); @@ -278,12 +277,13 @@ void substring(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (size == 0) { new_str = K->empty_string; } else { - new_str = kstring_new(K, kstring_buf(str)+start, size); + new_str = kstring_new_bs(K, kstring_buf(str)+start, size); } kapply_cc(K, new_str); } /* 13.2.6? string-append */ +/* TEMP: at least for now this always returns mutable strings */ /* TEMP: this does 3 passes over the list */ void string_append(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) @@ -314,7 +314,7 @@ void string_append(klisp_State *K, TValue *xparams, TValue ptree, if (size == 0) { new_str = K->empty_string; } else { - new_str = kstring_new_g(K, size); + new_str = kstring_new_s(K, size); char *buf = kstring_buf(new_str); /* loop again to copy the chars of each string */ tail = ptree; @@ -369,6 +369,7 @@ void list_to_string(klisp_State *K, TValue *xparams, TValue ptree, } /* 13.2.8? string-copy */ +/* TEMP: at least for now this always returns mutable strings */ void string_copy(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); @@ -380,12 +381,29 @@ void string_copy(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (tv_equal(str, K->empty_string)) { new_str = str; } else { - new_str = kstring_new(K, kstring_buf(str), kstring_size(str)); + new_str = kstring_new_bs(K, kstring_buf(str), kstring_size(str)); } kapply_cc(K, new_str); } -/* 13.2.9? string-fill! */ +/* 13.2.9? string->immutable-string */ +void string_to_immutable_string(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv) +{ + UNUSED(xparams); + UNUSED(denv); + bind_1tp(K, "string->immutable-string", ptree, "string", ttisstring, str); + + TValue res_str; + if (kstring_immutablep(str)) {/* this includes the empty list */ + res_str = str; + } else { + res_str = kstring_new_bs_imm(K, kstring_buf(str), kstring_size(str)); + } + kapply_cc(K, res_str); +} + +/* 13.2.10? string-fill! */ void string_fillS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); @@ -393,22 +411,26 @@ void string_fillS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) bind_2tp(K, "string-fill!", ptree, "string", ttisstring, str, "char", ttischar, tv_ch); + if (kstring_immutablep(str)) { + klispE_throw(K, "string-fill!: immutable string"); + return; + } + memset(kstring_buf(str), chvalue(tv_ch), kstring_size(str)); kapply_cc(K, KINERT); } /* 13.3.1? symbol->string */ -/* TEMP: for now all strings are mutable, this returns a new object - each time */ +/* The strings in symbols are immutable so we can just return that */ void symbol_to_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); UNUSED(denv); bind_1tp(K, "symbol->string", ptree, "symbol", ttissymbol, sym); - TValue new_str = kstring_new(K, ksymbol_buf(sym), ksymbol_size(sym)); - kapply_cc(K, new_str); + TValue str = ksymbol_str(sym); + kapply_cc(K, str); } /* 13.3.2? string->symbol */ @@ -421,6 +443,7 @@ void symbol_to_string(klisp_State *K, TValue *xparams, TValue ptree, because the report only says that read objects when written and read again must be equal? which happens here */ +/* If the string is mutable it is copied */ void string_to_symbol(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { diff --git a/src/kgstrings.h b/src/kgstrings.h @@ -78,12 +78,14 @@ void string_to_list(klisp_State *K, TValue *xparams, TValue ptree, /* 13.2.8? string-copy */ void string_copy(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); -/* 13.2.9? string-fill! */ +/* 13.2.9? string->immutable-string */ +void string_to_immutable_string(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv); + +/* 13.2.10? string-fill! */ void string_fillS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); /* 13.3.1? symbol->string */ -/* TEMP: for now all strings are mutable, this returns a new object - each time */ void symbol_to_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); diff --git a/src/kobject.h b/src/kobject.h @@ -627,6 +627,7 @@ int32_t kmark_count; #define kis_unmarked(p_) (tv_equal(kget_mark(p_), KFALSE)) /* Macros to access kflags & type in GCHeader */ +/* TODO: 1 should always be reserved for mutability flag */ #define gch_get_type(o_) (obj2gch(o_)->tt) #define gch_get_kflags(o_) (obj2gch(o_)->kflags) #define tv_get_kflags(o_) (gch_get_kflags(tv2gch(o_))) @@ -653,6 +654,7 @@ int32_t kmark_count; #define kis_dyn_cont(c_) ((tv_get_kflags(c_) & K_FLAG_DYNAMIC) != 0) #define kis_bool_check_cont(c_) ((tv_get_kflags(c_) & K_FLAG_BOOL_CHECK) != 0) +/* for now only used in pairs and strings */ #define K_FLAG_IMMUTABLE 0x01 #define kis_mutable(o_) ((tv_get_kflags(o_) & K_FLAG_IMMUTABLE) == 0) #define kis_immutable(o_) (!kis_mutable(o_)) @@ -671,6 +673,7 @@ int32_t kmark_count; #define K_FLAG_WEAK_KEYS 0x01 #define K_FLAG_WEAK_VALUES 0x02 +#define K_FLAG_WEAK_NOTHING 0x00 #define ktable_has_weak_keys(o_) \ ((tv_get_kflags(o_) & K_FLAG_WEAK_KEYS) != 0) diff --git a/src/kread.c b/src/kread.c @@ -4,15 +4,6 @@ ** See Copyright Notice in klisp.h */ - -/* -** TODO: -** -** - Read mutable/immutable objects (cons function should be a parameter) -** this is needed because some functions (like load) return immutable objs -** -*/ - #include <stdio.h> #include <stdlib.h> #include <assert.h> diff --git a/src/kstate.c b/src/kstate.c @@ -161,9 +161,9 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K->sbuf = (TValue *)s; /* the dynamic ports and the keys for the dynamic ports */ - TValue in_port = kmake_std_port(K, kstring_new_ns(K, "*STDIN*"), + TValue in_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDIN*"), false, KNIL, KNIL, stdin); - TValue out_port = kmake_std_port(K, kstring_new_ns(K, "*STDOUT*"), + TValue out_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDOUT*"), true, KNIL, KNIL, stdout); K->kd_in_port_key = kcons(K, KTRUE, in_port); K->kd_out_port_key = kcons(K, KTRUE, out_port); diff --git a/src/kstring.c b/src/kstring.c @@ -13,7 +13,7 @@ #include "kmem.h" #include "kgc.h" -/* TEMP: this is for initializing the above value, for now, from ktoken.h */ +/* TEMP: this is for initializing the above value */ TValue kstring_new_empty(klisp_State *K) { String *new_str; @@ -21,7 +21,7 @@ TValue kstring_new_empty(klisp_State *K) new_str = klispM_malloc(K, sizeof(String) + 1); /* header + gc_fields */ - klispC_link(K, (GCObject *) new_str, K_TSTRING, 0); + klispC_link(K, (GCObject *) new_str, K_TSTRING, K_FLAG_IMMUTABLE); /* string specific fields */ new_str->mark = KFALSE; @@ -31,8 +31,8 @@ TValue kstring_new_empty(klisp_State *K) return gc2str(new_str); } -/* TEMP: for now all strings are mutable */ -TValue kstring_new_g(klisp_State *K, uint32_t size) +/* with just size */ +TValue kstring_new_s_g(klisp_State *K, bool m, uint32_t size) { String *new_str; @@ -43,7 +43,8 @@ TValue kstring_new_g(klisp_State *K, uint32_t size) new_str = klispM_malloc(K, sizeof(String) + size + 1); /* header + gc_fields */ - klispC_link(K, (GCObject *) new_str, K_TSTRING, 0); + klispC_link(K, (GCObject *) new_str, K_TSTRING, + m? 0 : K_FLAG_IMMUTABLE); /* string specific fields */ new_str->mark = KFALSE; @@ -58,34 +59,39 @@ TValue kstring_new_g(klisp_State *K, uint32_t size) return gc2str(new_str); } -TValue kstring_new(klisp_State *K, const char *buf, uint32_t size) +/* with buffer & size */ +TValue kstring_new_bs_g(klisp_State *K, bool m, const char *buf, uint32_t size) { - TValue new_str = kstring_new_g(K, size); + TValue new_str = kstring_new_s_g(K, m, size); memcpy(kstring_buf(new_str), buf, size); return new_str; } -/* with no size, no embedded '\0's */ -TValue kstring_new_ns(klisp_State *K, const char *buf) +/* with buffer but no size, no embedded '\0's */ +TValue kstring_new_b_g(klisp_State *K, bool m, const char *buf) { - return (kstring_new(K, buf, strlen(buf))); + return (kstring_new_bs_g(K, m, buf, strlen(buf))); } -TValue kstring_new_sc(klisp_State *K, uint32_t size, char fill) +/* with size and fill char */ +TValue kstring_new_sf_g(klisp_State *K, bool m, uint32_t size, char fill) { - TValue new_str = kstring_new_g(K, size); + TValue new_str = kstring_new_s_g(K, m, size); memset(kstring_buf(new_str), fill, size); return new_str; } -/* both obj1 and obj2 should be strings! */ +/* both obj1 and obj2 should be strings */ bool kstring_equalp(TValue obj1, TValue obj2) { + klisp_assert(ttisstring(obj1) && ttisstring(obj2)); + String *str1 = tv2str(obj1); String *str2 = tv2str(obj2); if (str1->size == str2->size) { - return (memcmp(str1->b, str2->b, str1->size) == 0); + return (str1->size == 0) || + (memcmp(str1->b, str2->b, str1->size) == 0); } else { return false; } diff --git a/src/kstring.h b/src/kstring.h @@ -12,21 +12,47 @@ #include "kobject.h" #include "kstate.h" -/* TEMP: for now all strings are mutable */ - +/* used for initialization */ TValue kstring_new_empty(klisp_State *K); -TValue kstring_new(klisp_State *K, const char *buf, uint32_t size); -/* with no size, no embedded '\0's */ -TValue kstring_new_ns(klisp_State *K, const char *buf); -TValue kstring_new_g(klisp_State *K, uint32_t size); -TValue kstring_new_sc(klisp_State *K, uint32_t size, char fill); - +/* general string constructor, buf remains uninit + (except for an extra trailing zero used for printing */ +TValue kstring_new_s_g(klisp_State *K, bool m, uint32_t size); +/* with buffer & size */ +TValue kstring_new_bs_g(klisp_State *K, bool m, const char *buf, uint32_t size); +/* with buffer but no size, no embedded '\0's */ +TValue kstring_new_b_g(klisp_State *K, bool m, const char *buf); +/* with size & fill char */ +TValue kstring_new_sf_g(klisp_State *K, bool m, uint32_t size, char fill); + +/* macros for mutable & immutable versions of the above */ +#define kstring_new_s(K_, size_) \ + kstring_new_s_g(K_, true, size_) +#define kstring_new_bs(K_, buf_, size_) \ + kstring_new_bs_g(K_, true, buf_, size_) +#define kstring_new_b(K_, buf_) \ + kstring_new_b_g(K_, true, buf_) +#define kstring_new_sf(K_, size_, fill_) \ + kstring_new_sf_g(K_, true, size_, fill_) + +#define kstring_new_s_imm(K_, size_) \ + kstring_new_s_g(K_, false, size_) +#define kstring_new_bs_imm(K_, buf_, size_) \ + kstring_new_bs_g(K_, false, buf_, size_) +#define kstring_new_b_imm(K_, buf_) \ + kstring_new_b_g(K_, false, buf_) +#define kstring_new_sf_imm(K_, size_, fill_) \ + kstring_new_sf_g(K_, false, size_, fill_) + +/* some macros to access the parts of the string */ #define kstring_buf(tv_) (tv2str(tv_)->b) #define kstring_size(tv_) (tv2str(tv_)->size) -#define kstring_is_empty(tv_) (kstring_size(tv_) == 0) +#define kstring_emptyp(tv_) (kstring_size(tv_) == 0) +#define kstring_mutablep(tv_) (kis_mutable(tv_)) +#define kstring_immutablep(tv_) (kis_immutable(tv_)) -/* both obj1 and obj2 should be strings! */ +/* both obj1 and obj2 should be strings, this compares char by char + but differentiates immutable from mutable strings */ bool kstring_equalp(TValue obj1, TValue obj2); #endif diff --git a/src/ksymbol.c b/src/ksymbol.c @@ -33,10 +33,8 @@ TValue ksymbol_new_g(klisp_State *K, const char *buf, int32_t size, tbl = kcdr(tbl); } - /* Didn't find it, alloc new string and save in symbol table */ - /* NOTE: there are no embedded '\0's in symbols */ - /* GC: root new_str */ - TValue new_str = kstring_new(K, buf, size); /* this copies the buf */ + /* Didn't find it, alloc new immutable string and save in symbol table */ + TValue new_str = kstring_new_bs_imm(K, buf, size); krooted_tvs_push(K, new_str); Symbol *new_sym = klispM_new(K, Symbol); krooted_tvs_pop(K); diff --git a/src/ksymbol.h b/src/ksymbol.h @@ -17,9 +17,10 @@ TValue ksymbol_new_i(klisp_State *K, const char *buf, int32_t size); /* For identifiers, simplified for unknown size */ TValue ksymbol_new(klisp_State *K, const char *buf); -/* For general strings */ +/* For general strings, copies str if not immutable */ TValue ksymbol_new_check_i(klisp_State *K, TValue str); +#define ksymbol_str(tv_) (tv2sym(tv_)->str) #define ksymbol_buf(tv_) (kstring_buf(tv2sym(tv_)->str)) #define ksymbol_size(tv_) (kstring_size(tv2sym(tv_)->str)) diff --git a/src/ktoken.c b/src/ktoken.c @@ -188,8 +188,7 @@ TValue ktok_get_source_info(klisp_State *K) { /* NOTE: the filename doesn't contains embedded '\0's */ TValue filename_str = - kstring_new(K, K->ktok_source_info.saved_filename, - strlen(K->ktok_source_info.saved_filename)); + kstring_new_b_imm(K, K->ktok_source_info.saved_filename); krooted_tvs_push(K, filename_str); /* TEMP: for now, lines and column names are fixints */ TValue res = kcons(K, i2tv(K->ktok_source_info.saved_line), @@ -521,7 +520,10 @@ TValue ktok_read_string(klisp_State *K) i++; } } - TValue new_str = kstring_new(K, ks_tbget_buffer(K), i); + /* TEMP: for now strings "read" are mutable but strings "loaded" are + not */ + TValue new_str = kstring_new_bs_g(K, K->read_mconsp, + ks_tbget_buffer(K), i); krooted_tvs_push(K, new_str); ks_tbclear(K); /* shouldn't cause gc, but still */ krooted_tvs_pop(K); diff --git a/src/kwrite.c b/src/kwrite.c @@ -44,7 +44,7 @@ void kw_print_bigint(klisp_State *K, TValue bigint) ((kbigint_negativep(bigint))? 1 : 0); krooted_tvs_push(K, bigint); - TValue buf_str = kstring_new_g(K, size); + TValue buf_str = kstring_new_s(K, size); krooted_tvs_push(K, buf_str); /* write backwards so we can use printf later */ @@ -338,7 +338,7 @@ void kwrite_fsm(klisp_State *K, TValue obj) break; } case K_TSTRING: { - if (kstring_is_empty(obj)) { + if (kstring_emptyp(obj)) { kw_printf(K, "\"\""); } else { TValue mark = kget_mark(obj);