klisp

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

commit 9d9a849c43826ed22b529229eaf960ab866e79a8
parent ce0f5b63ec8157281107b48191decbfedbd02bf1
Author: Oto Havle <havleoto@gmail.com>
Date:   Sun, 22 Apr 2012 16:18:40 +0200

eq-hashtables: Lisp interface to internal hash tables.

Diffstat:
Msrc/Makefile | 8++++++--
Msrc/kground.c | 2++
Asrc/kgtables.c | 337+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/kgtables.h | 15+++++++++++++++
Msrc/ktable.c | 21+++++++++++++++++++++
Msrc/ktable.h | 3+++
Msrc/kwrite.c | 9+++++++++
Asrc/tests/tables.k | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/tests/test-all.k | 1+
9 files changed, 688 insertions(+), 2 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -56,7 +56,7 @@ CORE_O= kobject.o ktoken.o kpair.o kstring.o ksymbol.o kread.o \ kgenvironments.o kgenv_mut.o kgcombiners.o kgcontinuations.o \ kgencapsulations.o kgpromises.o kgkd_vars.o kgks_vars.o kgports.o \ kgchars.o kgnumbers.o kgstrings.o kgbytevectors.o kgvectors.o \ - kgsystem.o kgerrors.o kgkeywords.o \ + kgtables.o kgsystem.o kgerrors.o kgkeywords.o \ $(if $(USE_LIBFFI),kgffi.o) # TEMP: in klisp there is no distinction between core & lib @@ -258,7 +258,7 @@ kground.o: kground.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ kgcontrol.h kgpairs_lists.h kgpair_mut.h kgenvironments.h kgenv_mut.h \ kgcombiners.h kgcontinuations.h kgencapsulations.h kgpromises.h \ kgkd_vars.h kgks_vars.h kgnumbers.h kgstrings.h kgchars.h kgports.h \ - kgbytevectors.h kgvectors.h kgsystem.h kgerrors.h kglibraries.h \ + kgbytevectors.h kgvectors.h kgtables.h kgsystem.h kgerrors.h kglibraries.h \ kgffi.h keval.h krepl.h kgstrings.o: kgstrings.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ ktoken.h kmem.h kapplicative.h koperative.h kcontinuation.h kerror.h \ @@ -276,6 +276,10 @@ kgsystem.o: kgsystem.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ ktoken.h kmem.h kpair.h kgc.h kerror.h ksystem.h kghelpers.h \ kapplicative.h koperative.h kcontinuation.h kenvironment.h ksymbol.h \ kstring.h ktable.h kgsystem.h kinteger.h kmem.h imath.h kgc.h +kgtables.o: kgtables.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ + ktoken.h kmem.h kapplicative.h koperative.h kcontinuation.h kerror.h \ + kpair.h kgc.h kvector.h kbytevector.h kghelpers.h kenvironment.h \ + ksymbol.h kstring.h ktable.h kgtables.h kgvectors.o: kgvectors.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ ktoken.h kmem.h kapplicative.h koperative.h kcontinuation.h kerror.h \ kpair.h kgc.h kvector.h kbytevector.h kghelpers.h kenvironment.h \ diff --git a/src/kground.c b/src/kground.c @@ -37,6 +37,7 @@ #include "kgports.h" #include "kgbytevectors.h" #include "kgvectors.h" +#include "kgtables.h" #include "kgsystem.h" #include "kgerrors.h" #include "kgkeywords.h" @@ -117,6 +118,7 @@ void kinit_ground_env(klisp_State *K) kinit_ports_ground_env(K); kinit_bytevectors_ground_env(K); kinit_vectors_ground_env(K); + kinit_tables_ground_env(K); kinit_system_ground_env(K); kinit_error_ground_env(K); kinit_keywords_ground_env(K); diff --git a/src/kgtables.c b/src/kgtables.c @@ -0,0 +1,337 @@ +/* +** kgtables.c +** Hash table interface for the ground environment +** See Copyright Notice in klisp.h +*/ + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> + +#include "kstate.h" +#include "kobject.h" +#include "kapplicative.h" +#include "koperative.h" +#include "kcontinuation.h" +#include "kerror.h" +#include "kpair.h" + +#include "kghelpers.h" +#include "kgtables.h" + +/* Provide lisp interface to internal hash tables. The interface + * is modeled after SRFI-69. + * + * MISSING FUNCTIONALITY + * - no user definable equivalence predicates + * - no user definable hash functions + * - hash function itself is not available + * - hash-table-ref/default not implemented + * - hash-table-update! not implemented + * - hash-table-for-each (hash-table-walk in SRFI-69) not implemented + * + * DEVIATIONS FROM SRFI-69 + * - hash-table-size renamed to hash-table-length to match klisp's vector-length + * - hash-table-exists? and hash-table-delete! accept more than one key + * - hash-table-merge! accepts more than two arguments + * + * KNOWN BUGS + * - removing elements do not cause hash tables shrink + * - hash_table_merge() may compute too low initial table size + * - "array" optimization never used + * + * Hash tables are equal? if and only if they are eq?. Hash + * tables do not have external representation. + * + * BASIC OPERATIONS + * + * (hash-table? OBJECT...) + * Type predicate. Evaluates to #t iff all arguments are hash + * tables, and #f otherwise. + * + * (make-hash-table) + * Create new, empty hash table. Currently accepts no optional + * parameters (SRFI-69 allows user-defined hash function, etc.) + * + * (hash-table-set! TABLE KEY VALUE) + * Set KEY => VALUE in TABLE, silently replacing + * any existing binding. The result is #inert. + * + * (hash-table-ref TABLE KEY [THUNK]) + * Returns value corresponding to KEY in TABLE, if present. + * If KEY is not bound and THUNK is given, returns result of + * evaluation of (THUNK) in the dynamic environment. Otherwise, + * an error is signalled. + * + * (hash-table-exists? TABLE KEY1 KEY2 ...) + * Returns #t if all keys KEY1, KEY2, ... are bound in TABLE. + * Returns #f otherwise. + * + * (hash-table-delete! TABLE KEY1 KEY2 ...) + * Removes binding of KEY1, KEY2, ... from TABLE. If keys are not + * present, nothing happens. The result is #inert. + * + * (hash-table-length TABLE) + * Returns number of KEY => VALUE bindings in TABLE. + * + * (hash-table-copy TABLE) + * Returns a copy of TABLE. + * + * (hash-table-merge T1 T2 ... Tn) + * Creates new hash table with all bindings from T1, T2, ... Tn. + * If more than one of the tables bind the same key, only the + * value from the table which is comes last in the argument + * list is preserved. + * + * (hash-table-merge! DEST T1 T2 ... Tn) + * Copy all bindings from T1, T2, ... Tn to DEST. If more than + * one of the tables bind the same key, only the value from the + * table which is comes last in the argument list is preserved. + * The result is #inert. + * + * HIGH-LEVEL CONSTRUCTORS + * + * (hash-table K1 V1 K2 V2 ...) + * Creates new hash table, binding Kn => Vn. If Ki = Kj for i < j, + * then Vj overrides Vi. + * + * (alist->hash-table ALIST) + * Creates new hash table from association list. + * + * WHOLE CONTENTS MANIPULATION + * + * (hash-table->alist TABLE) + * Returns list (KEY . VALUE) pairs from TABLE. + * + * (hash-table-keys TABLE) + * Returns list of all keys from TABLE. + * + * (hash-table-values TABLE) + * Returns list of all values from TABLE. + * + */ + +static void make_hash_table(klisp_State *K) +{ + check_0p(K, K->next_value); + TValue tab = klispH_new(K, + 0, /* narray - not used in klisp */ + 32, /* nhash - size of the hash table */ + 0 /* wflags - no weak pointers */ ); + kapply_cc(K, tab); +} + +static void hash_table_setB(klisp_State *K) +{ + bind_3tp(K, K->next_value, + "hash table", ttistable, tab, + "any", anytype, key, + "any", anytype, val); + *klispH_set(K, tv2table(tab), key) = val; + kapply_cc(K, KINERT); +} + +static void hash_table_ref(klisp_State *K) +{ + bind_al2tp(K, K->next_value, + "hash table", ttistable, tab, + "any", anytype, key, + dfl); + (void) get_opt_tpar(K, dfl, "combiner", ttiscombiner); + + const TValue *node = klispH_get(tv2table(tab), key); + if (!ttisfree(*node)) { + kapply_cc(K, *node); + } else if (ttiscombiner(dfl)) { + while(ttisapplicative(dfl)) + dfl = tv2app(dfl)->underlying; + ktail_call(K, dfl, KNIL, K->next_env); + } else { + klispE_throw_simple_with_irritants(K, "key not found", + 1, key); + } +} + +static void hash_table_existsP(klisp_State *K) +{ + int32_t i, pairs; + TValue res = KTRUE; + bind_al1tp(K, K->next_value, + "hash table", ttistable, tab, + keys); + check_list(K, 1, keys, &pairs, NULL); + + for (i = 0; i < pairs; i++, keys = kcdr(keys)) { + const TValue *node = klispH_get(tv2table(tab), kcar(keys)); + if (ttisfree(*node)) { + res = KFALSE; + break; + } + } + kapply_cc(K, res); +} + +static void hash_table_deleteB(klisp_State *K) +{ + int32_t i, pairs; + bind_al1tp(K, K->next_value, + "hash table", ttistable, tab, + keys); + check_list(K, 1, keys, &pairs, NULL); + + for (i = 0; i < pairs; i++, keys = kcdr(keys)) { + TValue *node = klispH_set(K, tv2table(tab), kcar(keys)); + if (!ttisfree(*node)) { + *node = KFREE; /* TODO: shrink ? */ + } + } + kapply_cc(K, KINERT); +} + +static void hash_table_length(klisp_State *K) +{ + bind_1tp(K, K->next_value, "hash table", ttistable, tab); + kapply_cc(K, i2tv(klispH_numuse(tv2table(tab)))); +} + +static void hash_table_constructor(klisp_State *K) +{ + int32_t pairs, cpairs, i; + TValue rest = K->next_value; + check_list(K, 1, rest, &pairs, &cpairs); + if ((pairs % 2 != 0) || (cpairs % 2 != 0)) + klispE_throw_simple(K, "expected even number of arguments"); + + TValue tab = klispH_new(K, 0, 32 + 2 * pairs, 0); + krooted_tvs_push(K, tab); + for (i = 0; i < pairs; i += 2, rest = kcddr(rest)) + *klispH_set(K, tv2table(tab), kcar(rest)) = kcadr(rest); + krooted_tvs_pop(K); + kapply_cc(K, tab); +} + +static void alist_to_hash_table(klisp_State *K) +{ + int32_t pairs, i; + bind_1p(K, K->next_value, rest); + check_typed_list(K, kpairp, true, rest, &pairs, NULL); + + TValue tab = klispH_new(K, 0, 32 + 2 * pairs, 0); + krooted_tvs_push(K, tab); + for (i = 0; i < pairs; i++, rest = kcdr(rest)) + *klispH_set(K, tv2table(tab), kcaar(rest)) = kcdar(rest); + krooted_tvs_pop(K); + kapply_cc(K, tab); +} + +static void hash_table_merge(klisp_State *K) +{ + int32_t pairs; + bool destructive = bvalue(K->next_xparams[0]); + bool only_one_arg = ivalue(K->next_xparams[1]); + TValue dest, rest = K->next_value; + + check_typed_list(K, ktablep, true, rest, &pairs, NULL); + if (only_one_arg && pairs != 1) { + klispE_throw_simple(K, "expected one argument"); + } + if (destructive) { + if (pairs == 0) + klispE_throw_simple(K, "expected at least one argument"); + dest = kcar(rest); + rest = kcdr(rest); + pairs--; + } else { + dest = klispH_new(K, 0, 32 + 2 * pairs, 0); + } + + krooted_tvs_push(K, dest); + while (pairs--) { + TValue key = KFREE, data; + Table *t = tv2table(kcar(rest)); + while (klispH_next(K, t, &key, &data)) + *klispH_set(K, tv2table(dest), key) = data; + rest = kcdr(rest); + } + krooted_tvs_pop(K); + + kapply_cc(K, (destructive ? KINERT : dest)); +} + +/* table_elements(K, TAB, MKELT) calls MKELT(key, value) + * on each key=>value binding in TAB and returns a list + * of objects returned by MKELT. TAB must be rooted. + */ +static TValue table_elements + (klisp_State *K, Table *t, + TValue (*mkelt)(klisp_State *K, TValue k, TValue v)) +{ + TValue key = KFREE, data, res = KNIL, elt = KINERT; + + krooted_vars_push(K, &res); + krooted_vars_push(K, &elt); + while (klispH_next(K, t, &key, &data)) { + elt = mkelt(K, key, data); + res = kcons(K, elt, res); + } + krooted_vars_pop(K); + krooted_vars_pop(K); + return res; +} + +static TValue mkelt_proj1(klisp_State *K, TValue k, TValue v) +{ + UNUSED(K); + UNUSED(v); + return k; +} + +static TValue mkelt_proj2(klisp_State *K, TValue k, TValue v) +{ + UNUSED(K); + UNUSED(k); + return v; +} + +static TValue mkelt_cons(klisp_State *K, TValue k, TValue v) +{ + return kcons(K, k, v); +} + +static void hash_table_to_list(klisp_State *K) +{ + bind_1tp(K, K->next_value, "hash table", ttistable, tab); + TValue res = table_elements(K, tv2table(tab), pvalue(K->next_xparams[0])); + kapply_cc(K, res); +} + +/* init ground */ +void kinit_tables_ground_env(klisp_State *K) +{ + TValue ground_env = K->ground_env; + TValue symbol, value; + + add_applicative(K, ground_env, "hash-table?", typep, 2, symbol, + i2tv(K_TTABLE)); + add_applicative(K, ground_env, "make-hash-table", make_hash_table, 0); + + add_applicative(K, ground_env, "hash-table-set!", hash_table_setB, 0); + add_applicative(K, ground_env, "hash-table-ref", hash_table_ref, 0); + add_applicative(K, ground_env, "hash-table-exists?", hash_table_existsP, 0); + add_applicative(K, ground_env, "hash-table-delete!", hash_table_deleteB, 0); + add_applicative(K, ground_env, "hash-table-length", hash_table_length, 0); + + add_applicative(K, ground_env, "hash-table", hash_table_constructor, 0); + add_applicative(K, ground_env, "alist->hash-table", alist_to_hash_table, 0); + + add_applicative(K, ground_env, "hash-table-merge", hash_table_merge, 2, KFALSE, KFALSE); + add_applicative(K, ground_env, "hash-table-copy", hash_table_merge, 2, KFALSE, KTRUE); + add_applicative(K, ground_env, "hash-table-merge!", hash_table_merge, 2, KTRUE, KFALSE); + + add_applicative(K, ground_env, "hash-table-keys", hash_table_to_list, 1, p2tv(mkelt_proj1)); + add_applicative(K, ground_env, "hash-table-values", hash_table_to_list, 1, p2tv(mkelt_proj2)); + add_applicative(K, ground_env, "hash-table->alist", hash_table_to_list, 1, p2tv(mkelt_cons)); +} diff --git a/src/kgtables.h b/src/kgtables.h @@ -0,0 +1,15 @@ +/* +** kgtables.h +** Hash table interface for the ground environment +** See Copyright Notice in klisp.h +*/ + +#ifndef kgtables_h +#define kgtables_h + +#include "kstate.h" + +/* init ground */ +void kinit_tables_ground_env(klisp_State *K); + +#endif diff --git a/src/ktable.c b/src/ktable.c @@ -641,3 +641,24 @@ int32_t klispH_getn (Table *t) { return j; /* that is easy... */ else return unbound_search(t, j); } + +/* Return number of used elements in the hashtable. Code copied + * from rehash(). */ + +int32_t klispH_numuse(Table *t) +{ + int32_t nasize; + int32_t nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ + int32_t i; + int32_t totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + return totaluse; +} + +bool ktablep(TValue obj) +{ + return ttistable(obj); +} diff --git a/src/ktable.h b/src/ktable.h @@ -37,4 +37,7 @@ void klispH_free (klisp_State *K, Table *t); int32_t klispH_next (klisp_State *K, Table *t, TValue *key, TValue *data); int32_t klispH_getn (Table *t); +int32_t klispH_numuse(Table *t); +bool ktablep(TValue obj); + #endif diff --git a/src/kwrite.c b/src/kwrite.c @@ -702,6 +702,15 @@ void kwrite_scalar(klisp_State *K, TValue obj) #endif kw_printf(K, "]"); break; + case K_TTABLE: + kw_printf(K, "#[hash-table"); +#if KTRACK_NAMES + if (khas_name(obj)) { + kw_print_name(K, obj); + } +#endif + kw_printf(K, "]"); + break; case K_TLIBRARY: kw_printf(K, "#[library"); #if KTRACK_NAMES diff --git a/src/tests/tables.k b/src/tests/tables.k @@ -0,0 +1,294 @@ +;; check.k & test-helpers.k should be loaded +;; +;; Tests of hash table features. +;; + +;; XXX make-hash-table hash-table? + +($check-predicate (applicative? hash-table? make-hash-table)) +($check-predicate (hash-table?)) +($check-predicate (hash-table? (make-hash-table))) + +($check-not-predicate (hash-table? 0)) +($check-not-predicate (hash-table? "")) +($check-not-predicate (hash-table? ())) +($check-not-predicate (hash-table? (make-bytevector 0))) +($check-not-predicate (hash-table? (make-vector 1))) +($check-not-predicate (hash-table? (make-environment))) + +($check-error (make-hash-table eq?)) +($check-error (make-hash-table 32)) +($check-error (make-hash-table ($lambda (x) 1))) + +;; XXX hash-table-set! hash-table-ref hash-table-exists? hash-table-delete! + +($check-predicate + (applicative? hash-table-set! hash-table-ref + hash-table-exists? hash-table-delete!)) + +($check equal? + ($let ((t (make-hash-table))) + (hash-table-set! t 0 "a") + (hash-table-set! t 1 "b") + (hash-table-set! t -30 "c") + (hash-table-set! t "x" "y") + (hash-table-set! t #\u 15) + (hash-table-set! t #:kwd 42) + (hash-table-set! t #t #f) + (hash-table-set! t #inert #\i) + (list + (map ($lambda (k) (hash-table-ref t k)) + (list -30 #:kwd 0 1 #t #\u #inert)) + (list + (hash-table-exists? t 0 1 #t) + (hash-table-exists? t) + (hash-table-exists? t #inert #ignore)))) + (list + (list "c" 42 "a" "b" #f 15 #\i) + (list #t #t #f))) + +($check equal? + ($let ((t (make-hash-table))) + (hash-table-set! t 42 "a") + (hash-table-set! t 13 "b") + (hash-table-set! t -5 "c") + (hash-table-set! t 42 "d") + (hash-table-set! t 13 "e") + (list + (hash-table-ref t 13) + (hash-table-ref t 42) + (hash-table-exists? t 0) + (hash-table-exists? t -5))) + (list "e" "d" #f #t)) + +($check equal? + ($let ((t (make-hash-table))) + (hash-table-set! t 42 "a") + (hash-table-delete! t 13) + (hash-table-delete! t 42 13) + (hash-table-set! t 13 "c") + (list (hash-table-ref t 13) (hash-table-exists? t 42))) + (list "c" #f)) + +($check equal? + ($let + ((vx ($vau () denv ($binds? denv x))) + (vy ($vau () denv ($binds? denv y)))) + ($let ((t (make-hash-table)) (x -5)) + (list + (hash-table-ref t "a" ($lambda () "d")) + (hash-table-ref t "b" vx) + (hash-table-ref t "c" vy)))) + (list "d" #t #f)) + +($check-error (hash-table-ref (make-hash-table) 0)) +($check-error + ($let ((t (make-hash-table))) + (hash-table-set! t "a" "b") + (hash-table-ref t "b"))) + +($let ((t (make-hash-table))) + (hash-table-set! t 3 "x") + ($check-error (hash-table-ref)) + ($check-error (hash-table-ref () 2)) + ($check-error (hash-table-ref t)) + ($check-error (hash-table-ref t 2)) + ($check-no-error (hash-table-ref t 3)) + ($check-error (hash-table-ref t 3 5)) + ($check-error (apply hash-table-ref (list* t 3 ($lambda () ())))) + ($check-error (hash-table-ref t 3 ($lambda () ()) 5)) + ($check-error (hash-table-set!)) + ($check-error (hash-table-set! t)) + ($check-error (hash-table-set! t 1)) + ($check-no-error (hash-table-set! t 1 2)) + ($check-error (hash-table-set! () 1 2)) + ($check-error (hash-table-set! t 1 3 4)) + ($check-error (hash-table-exists?)) + ($check-no-error (hash-table-exists? t)) + ($check-error (hash-table-exists? ())) + ($check-error (hash-table-delete!)) + ($check-error (hash-table-delete! () 1 2 3)) + ($check-no-error (hash-table-delete! t))) + +;; XXX hash-table-length + +($check-predicate (applicative? hash-table-length)) +($check equal? (hash-table-length (make-hash-table)) 0) +($check equal? + ($let ((t (make-hash-table))) + (hash-table-set! t "a" "b") + (hash-table-set! t "c" "d") + (hash-table-set! t "e" "f") + (hash-table-delete! t "c") + (hash-table-length t)) + 2) +($check-error (hash-table-length)) +($check-error (hash-table-length ())) +($check-error (apply hash-table-length 1)) +($check-error (hash-table-length (make-hash-table) (make-hash-table))) + +;; XXX hash-table + +($check-predicate (applicative? hash-table)) +($check-predicate (hash-table? (hash-table))) +($check equal? + ($let ((t (hash-table 0 #f 1 #t))) + (list + (hash-table? t) + (hash-table-length t) + (hash-table-exists? t 0) + (hash-table-exists? t 1) + (hash-table-exists? t 2) + (hash-table-ref t 0) + (hash-table-ref t 1))) + (list #t 2 #t #t #f #f #t)) + +($check-error (hash-table 1)) +($check-error (hash-table 1 2 3)) +($check-error (hash-table 1 2 3 4 5)) +($check-error + ($let ((ls (list 1 2 3 4 5 6))) + (encycle! ls 3 3) + (apply hash-table ls))) + +;; XXX alist->hash-table + +($check-predicate (applicative? alist->hash-table)) +($check-predicate (hash-table? (alist->hash-table ()))) +($check equal? + ($let + ((t + (alist->hash-table + ($quote (("x" . "y") (1 . 2) (3 . 4)))))) + (list + (hash-table? t) + (hash-table-length t) + (hash-table-exists? t "x") + (hash-table-exists? t "y") + (hash-table-exists? t 1) + (hash-table-exists? t 2) + (hash-table-ref t 1) + (hash-table-ref t 3))) + (list #t 3 #t #f #t #f 2 4)) + +($check-no-error (alist->hash-table ())) +($check-error (alist->hash-table)) +($check-error (alist->hash-table () ())) +($check-error ((unwrap alist->hash-table) 0)) +($check-error (alist->hash-table 1)) +($check-error (alist->hash-table (list 1 2))) +($check-error (alist->hash-table (list (cons 1 2) 3))) + +;; XXX hash-table-keys hash-table-values hash-table->alist + +($provide! (list-set-equal?) + ($define! list-subset? + ($lambda (a b) + ($if (null? a) + #t + ($and? (member? (car a) b) (list-subset? (cdr a) b))))) + ($define! list-set-equal? + ($lambda (a b) + ($and? + (=? (length a) (length b)) + (list-subset? a b) + (list-subset? b a))))) + +($check-predicate + (applicative? hash-table-keys hash-table-values hash-table->alist)) + +($check list-set-equal? + (hash-table-keys (hash-table "a" 1 "b" 2 "c" 3)) + (list "a" "b" "c")) + +($check list-set-equal? + (hash-table-values (hash-table "a" 1 "b" 2 "c" 3)) + (list 1 2 3)) + +($check list-set-equal? + (hash-table->alist (hash-table "a" 1 "b" 2 "c" 3)) + (list (cons "a" 1) (cons "b" 2) (cons "c" 3))) + +($let ((t (hash-table 1 2 3 4))) + ($check-error (hash-table-keys)) + ($check-error (hash-table-keys ())) + ($check-error (hash-table-keys t t)) + ($check-error (hash-table-values)) + ($check-error (hash-table-values ())) + ($check-error (hash-table-values t t)) + ($check-error (hash-table->alist)) + ($check-error (hash-table->alist ())) + ($check-error (hash-table->alist t t))) + +;; XXX hash-table-merge hash-table-copy hash-table-merge! + +($check-predicate + (applicative? hash-table-merge hash-table-copy hash-table-merge!)) + +($check equal? + (hash-table->alist (hash-table-merge)) + ()) + +($check list-set-equal? + (hash-table->alist + (hash-table-merge + (hash-table 1 "a" 2 "b") + (hash-table 1 "c" 3 "d") + (hash-table 1 "f" 5 "z"))) + (list (cons 1 "f") (cons 2 "b") (cons 3 "d") (cons 5 "z"))) + +($check list-set-equal? + (hash-table->alist + (hash-table-copy + (hash-table 1 "a" 2 "b"))) + (list (cons 1 "a") (cons 2 "b"))) + +($check list-set-equal? + ($let ((t (hash-table 1 "a" 2 "b"))) + (hash-table-merge! t (hash-table "x" "y" 2 "w")) + (hash-table->alist t)) + (list + (cons 1 "a") (cons 2 "w") (cons "x" "y"))) + +($check equal? + ($let* + ((t1 (hash-table 1 "a" 2 "b")) + (t2 (hash-table 2 "c" 3 "d")) + (t3 (hash-table-merge t1 t2)) + (t4 (hash-table-copy t2))) + (list + (eq? t1 t2) (eq? t1 t3) (eq? t1 t4) + (eq? t2 t3) (eq? t2 t4) + (eq? t3 t4))) + (list #f #f #f #f #f #f)) + +($let* + ((t (hash-table 1 2 3 4)) + (ls1 (list t)) + (ls2 (list t t))) + (encycle! ls1 0 1) + (encycle! ls2 1 1) + ($check-no-error (hash-table-merge)) + ($check-no-error (hash-table-merge t)) + ($check-error (hash-table-merge t ())) + ($check-error (hash-table-merge () t)) + ($check-no-error (hash-table-merge t t t t)) + ($check-no-error (apply hash-table-merge ls1)) + ($check-no-error (apply hash-table-merge ls2)) + ($check-error ((unwrap hash-table-merge) 1)) + ($check-error (hash-table-merge!)) + ($check-no-error (hash-table-merge! t)) + ($check-error (hash-table-merge! t ())) + ($check-error (hash-table-merge! () t)) + ($check-no-error (hash-table-merge! t t t t)) + ($check-no-error (apply hash-table-merge! ls1)) + ($check-no-error (apply hash-table-merge! ls2)) + ($check-error ((unwrap hash-table-merge!) 1)) + ($check-error (hash-table-copy)) + ($check-no-error (hash-table-copy t)) + ($check-error (hash-table-copy t ())) + ($check-error (hash-table-copy () t)) + ($check-no-error (apply hash-table-copy ls1)) + ($check-error (apply hash-table-copy ls2)) + ($check-error (hash-table-copy t t t t)) + ($check-error ((unwrap hash-table-copy) 1))) diff --git a/src/tests/test-all.k b/src/tests/test-all.k @@ -26,6 +26,7 @@ (load "tests/error.k") (load "tests/bytevectors.k") (load "tests/vectors.k") +(load "tests/tables.k") (load "tests/system.k") (load "tests/keywords.k") (load "tests/libraries.k")