klisp

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

commit 1a83162c930cf69884edf3b9003f5f16f81265ab
parent c35909811fc2b67b65d639d36ca348881dfd178a
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Thu, 10 Mar 2011 22:52:09 -0300

Added equal? to the ground environment.

Diffstat:
Msrc/kground.c | 196++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/kobject.h | 3+++
Msrc/kstring.c | 14++++++++++++++
Msrc/kstring.h | 9+++++++--
4 files changed, 214 insertions(+), 8 deletions(-)

diff --git a/src/kground.c b/src/kground.c @@ -12,6 +12,7 @@ #include "kobject.h" #include "kground.h" #include "kpair.h" +#include "kstring.h" #include "kenvironment.h" #include "kcontinuation.h" #include "ksymbol.h" @@ -115,6 +116,7 @@ */ inline void unmark_list(klisp_State *K, TValue obj) { + (void) K; /* not needed, it's here for consistency */ while(ttispair(obj) && kis_marked(obj)) { kunmark(obj); obj = kcdr(obj); @@ -168,15 +170,27 @@ void booleanp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) */ /* 4.2.1 eq? */ + +/* Helper (also used in equal?) */ +inline bool eq2p(klisp_State *K, TValue obj1, TValue obj2); + /* TEMP: for now it takes only two argument */ void eqp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { (void) denv; (void) xparams; - bind_2p(K, "eq?", ptree, o1, o2); - /* TEMP: for now this is the same as - later it will change with numbers and immutable objects */ - kapply_cc(K, b2tv(tv_equal(o1, o2))); + + bind_2p(K, "eq?", ptree, obj1, obj2); + + bool res = eq2p(K, obj1, obj2); + kapply_cc(K, b2tv(res)); +} + +/* TEMP: for now this is the same as tv_equal, + later it will change with numbers and immutable objects */ +inline bool eq2p(klisp_State *K, TValue obj1, TValue obj2) +{ + return (tv_equal(obj1, obj2)); } /* @@ -184,8 +198,178 @@ void eqp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) */ /* 4.3.1 equal? */ + /* TEMP: for now it takes only two argument */ -/* TODO */ +/* +** equal? is O(n) where n is the number of pairs. +** Based on [1] "A linear algorithm for testing equivalence of finite automata" +** by J.E. Hopcroft and R.M.Karp +** List merging from [2] "A linear list merging algorithm" +** by J.E. Hopcroft and J.D. Ullman +** Idea to look up these papers from srfi 85: +** "Recursive Equivalence Predicates" by William D. Clinger +*/ + +/* Helper */ +/* compare two objects and check to see if they are "equal?". */ +inline bool equal2p(klisp_State *K, TValue obj1, TValue obj2); + +void equalp(klisp_State *K, TValue *xparas, TValue ptree, TValue denv) +{ + (void) denv; + bind_2p(K, "equal?", ptree, obj1, obj2); + bool res = equal2p(K, obj1, obj2); + kapply_cc(K, b2tv(res)); +} + + +/* +** Helpers +** +** See [2] for details of the list merging algorithm. +** Here are the implementation details: +** The marks of the pairs are used to store the nodes of the trees +** that represent the set of previous comparations of each pair. +** They serve the function of the array in [2]. +** If a pair is unmarked, it was never compared (empty comparison set). +** If a pair is marked, the mark object is either (#f . parent-node) +** if the node is not the root, and (#t . n) where n is the number +** of elements in the set, if the node is the root. +** This pair also doubles as the "name" of the set in [2]. +*/ + +/* find "name" of the set of this obj, if there isn't one create it, + if there is one, flatten its branch */ +inline TValue equal_find(klisp_State *K, TValue obj) +{ + /* GC: should root obj */ + if (kis_unmarked(obj)) { + /* object wasn't compared before, create new set */ + TValue new_node = kcons(K, KTRUE, i2tv(1)); + kset_mark(obj, new_node); + return new_node; + } else { + TValue node = kget_mark(obj); + + /* First obtain the root and a list of all the other objects in this + branch, as said above the root is the one with #t in its car */ + /* NOTE: the stack is being used, so we must remember how many pairs we + push, we can't just pop 'till is empty */ + int np = 0; + while(kis_false(kcar(node))) { + ks_spush(K, node); + node = kcdr(node); + ++np; + } + TValue root = node; + + /* set all parents to root, to flatten the branch */ + while(np--) { + node = ks_spop(K); + kset_cdr(node, root); + } + return root; + } +} + +/* merge the smaller set into the big one, if both are equal just pick one */ +inline void equal_merge(klisp_State *K, TValue root1, TValue root2) +{ + /* K isn't needed but added for consistency */ + (void)K; + int32_t size1 = ivalue(kcdr(root1)); + int32_t size2 = ivalue(kcdr(root2)); + TValue new_size = i2tv(size1 + size2); + + if (size1 < size2) { + /* add root1 set (the smaller one) to root2 */ + kset_cdr(root2, new_size); + kset_car(root1, KFALSE); + kset_cdr(root1, root2); + } else { + /* add root2 set (the smaller one) to root1 */ + kset_cdr(root1, new_size); + kset_car(root2, KFALSE); + kset_cdr(root2, root1); + } +} + +/* check to see if two objects were already compared, and return that. If they + weren't compared yet, merge their sets (and flatten their branches) */ +inline bool equal_find2_mergep(klisp_State *K, TValue obj1, TValue obj2) +{ + /* GC: should root root1 and root2 */ + TValue root1 = equal_find(K, obj1); + TValue root2 = equal_find(K, obj2); + if (tv_equal(root1, root2)) { + /* they are in the same set => they were already compared */ + return true; + } else { + equal_merge(K, root1, root2); + return false; + } +} + +/* +** See [1] for details, in this case the pairs form a possibly infinite "tree" +** structure, and that can be seen as a finite automata, where each node is a +** state, the car and the cdr are the transitions from that state to others, +** and the leaves (the non-pair objects) are the final states. +** Other way to see it is that, the key for determining equalness of two pairs +** is: Check to see if they were already compared to each other. +** If so, return #t, otherwise, mark them as compared to each other and +** recurse on both cars and both cdrs. +** The idea is that if assuming obj1 and obj2 are equal their components are +** equal then they are effectively equal to each other. +*/ +inline bool equal2p(klisp_State *K, TValue obj1, TValue obj2) +{ + assert(ks_sisempty(K)); + + /* the stack has the elements to be compaired, always in pairs. + So the top should be compared with the one below, the third with + the fourth and so on */ + ks_spush(K, obj1); + ks_spush(K, obj2); + + /* if the stacks becomes empty, all pairs of elements were equal */ + bool result = true; + + while(!ks_sisempty(K)) { + obj2 = ks_spop(K); + obj1 = ks_spop(K); + + if (!eq2p(K, obj1, obj2)) { + if (ttispair(obj1) && ttispair(obj2)) { + /* if they were already compaired, consider equal for now + otherwise they are equal if both their cars and cdrs are */ + if (!equal_find2_mergep(K, obj1, obj2)) { + ks_spush(K, kcdr(obj1)); + ks_spush(K, kcdr(obj2)); + ks_spush(K, kcar(obj1)); + ks_spush(K, kcar(obj2)); + } + } else if (ttisstring(obj1) && ttisstring(obj2)) { + if (!kstring_equalp(obj1, obj2)) { + result = false; + break; + } + } else { + result = false; + break; + } + } + } + + /* if result is false, the stack may not be empty */ + ks_sclear(K); + + unmark_tree(K, obj1); + unmark_tree(K, obj2); + + return result; +} + /* ** 4.4 Symbols @@ -788,7 +972,7 @@ TValue kmake_ground_env(klisp_State *K) */ /* 4.3.1 equal? */ - /* TODO */ + add_applicative(K, ground_env, "equal?", equalp, 0); /* ** 4.4 Symbols diff --git a/src/kobject.h b/src/kobject.h @@ -189,6 +189,9 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define ttisenvironment(o) (tbasetype_(o) == K_TAG_ENVIRONMENT) #define ttiscontinuation(o) (tbasetype_(o) == K_TAG_CONTINUATION) +/* macros to easily check boolean values */ +#define kis_true(o_) (tv_equal((o_), KTRUE)) +#define kis_false(o_) (tv_equal((o_), KFALSE)) /* ** Union of all Kernel non heap-allocated values (except doubles) diff --git a/src/kstring.c b/src/kstring.c @@ -5,6 +5,7 @@ */ #include <string.h> +#include <stdbool.h> #include "kstring.h" #include "kobject.h" @@ -61,3 +62,16 @@ TValue kstring_new(klisp_State *K, const char *buf, uint32_t size) return gc2str(new_str); } + +/* both obj1 and obj2 should be strings! */ +bool kstring_equalp(TValue obj1, TValue obj2) +{ + String *str1 = tv2str(obj1); + String *str2 = tv2str(obj2); + + if (str1->size == str2->size) { + return (memcmp(str1->b, str2->b, str1->size) == 0); + } else { + return false; + } +} diff --git a/src/kstring.h b/src/kstring.h @@ -7,6 +7,8 @@ #ifndef kstring_h #define kstring_h +#include <stdbool.h> + #include "kobject.h" #include "kstate.h" @@ -14,9 +16,12 @@ 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) +#define kstring_buf(tv_) (((String *) ((tv_).tv.v.gc))->b) +#define kstring_size(tv_) (((String *) ((tv_).tv.v.gc))->size) #define kstring_is_empty(tv_) (kstring_size(tv_) == 0) +/* both obj1 and obj2 should be strings! */ +bool kstring_equalp(TValue obj1, TValue obj2); + #endif