Skip to content

Commit 5298b00

Browse files
add buggy hashmap implementation
it can't find the nodes by hash search [skip actions]
1 parent b6fef74 commit 5298b00

File tree

7 files changed

+1948
-1239
lines changed

7 files changed

+1948
-1239
lines changed

pickle.cpp

Lines changed: 194 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,159 @@ object* parse(pvm* vm, object* cookie, object* inst_type) {
413413
return errors ? vm->sym("error") : nil;
414414
}
415415

416+
// ------------------------- HASHMAPS (OBJECTS) -------------------------------
417+
418+
// Returns the found node or nil if the hash is not found.
419+
static object* hashmap_find(pvm* vm, object* map, uint64_t hash) {
420+
// Each hashmap node is a 4-cons tree ((hash . (key . value)) . (left . right))
421+
uint64_t hh = hash;
422+
DBG("Searching hashmap for hash %" PRId64 " {", hash);
423+
recurse:
424+
if (!map) {
425+
DBG("Node is nil -- not found. }");
426+
return nil;
427+
}
428+
object* hash_pair = car(map);
429+
if (hash_pair) {
430+
printf("hash_pair = ");
431+
vm->dump(hash_pair);
432+
putchar(' ');
433+
vm->dump(car(hash_pair));
434+
int64_t this_hash = vm->intof(car(hash_pair));
435+
if (this_hash == hash) {
436+
DBG("Found matching key for hash %" PRId64, hash);
437+
return map;
438+
}
439+
}
440+
bool ll = hh & 1;
441+
object* children = cdr(map);
442+
if (!children) {
443+
DBG("Reached node with no children -- Not found. }");
444+
return nil;
445+
}
446+
if (ll) map = car(children);
447+
else map = cdr(children);
448+
hh >>= 1;
449+
DBG("Recursing on %s", ll ? "LEFT" : "RIGHT");
450+
goto recurse;
451+
}
452+
453+
// Returns the new node, *map is updated to point to the root node if it changed.
454+
static object* hashmap_set(pvm* vm, object** map, object* key, uint64_t hash, object* val) {
455+
DBG("Setting hash %" PRId64 " on hashmap. {", hash);
456+
uint64_t hh = hash;
457+
recurse:
458+
if (*map == nil) {
459+
DBG("Tree is terminated -- add new node. }");
460+
*map = vm->cons(vm->cons(vm->integer(hash), vm->cons(key, val)), nil);
461+
return *map;
462+
}
463+
// Map is not empty at this level. Check to see if it is a free node or the target node.
464+
object* hash_pair = car(*map);
465+
bool ll = hh & 1;
466+
object* children = cdr(*map);
467+
if (!hash_pair) {
468+
DBG("Found tombstoned node. Inserting key.");
469+
car(*map) = vm->cons(vm->integer(hash), vm->cons(key, val));
470+
goto killshadow;
471+
} else {
472+
// Check if the hashes match
473+
int64_t z = vm->intof(car(hash_pair));
474+
if (z == hash) {
475+
DBG("Found matching node. Re-setting it. }");
476+
if (!cdr(hash_pair)) cdr(hash_pair) = vm->cons(nil, nil);
477+
car(cdr(hash_pair)) = key;
478+
cdr(cdr(hash_pair)) = val;
479+
return *map;
480+
}
481+
}
482+
if (!children) {
483+
DBG("Reached node with no children cons. Adding children cons.");
484+
cdr(*map) = children = vm->cons(nil, nil);
485+
}
486+
if (ll) map = &car(children);
487+
else map = &cdr(children);
488+
hh >>= 1;
489+
DBG("Recursing on %s", ll ? "LEFT" : "RIGHT");
490+
goto recurse;
491+
//-------------
492+
killshadow:
493+
DBG("Now searching for shadower nodes in search path and killing them.");
494+
if (ll) map = &car(children);
495+
else map = &cdr(children);
496+
hh >>= 1;
497+
DBG("Continuing on %s", ll ? "LEFT" : "RIGHT");
498+
hash_pair = car(*map);
499+
ll = hh & 1;
500+
children = cdr(*map);
501+
if (hash_pair) {
502+
int64_t bad = vm->intof(car(hash_pair));
503+
if (bad == hash) {
504+
DBG("Found shadowing node, killing it.");
505+
car(*map) = nil;
506+
}
507+
}
508+
if (!children) {
509+
DBG("Reached node with no children. Stopping }");
510+
return *map;
511+
}
512+
if (ll) map = &car(children);
513+
else map = &cdr(children);
514+
hh >>= 1;
515+
DBG("Shadow recursing on %s", ll ? "LEFT" : "RIGHT");
516+
goto killshadow;
517+
}
518+
519+
object* pvm::get_property(object* obj, uint64_t hash, bool recurse) {
520+
// Nil has no properties
521+
if (!obj) return nil;
522+
if (recurse) {
523+
// Try to find it directly.
524+
object* val = this->get_property(obj, hash, false);
525+
if (val) return val;
526+
// Not found, traverse prototypes list.
527+
for (object* p = car(obj); p; p = cdr(p)) {
528+
val = this->get_property(car(p), hash, true);
529+
if (val) return val;
530+
}
531+
return nil;
532+
}
533+
// Check if it is an object-object (primitives have no own properties)
534+
if (obj->type != &obj_type) return nil;
535+
// Search the hashmap.
536+
object* hashmap = cdr(obj);
537+
object* node = hashmap_find(this, obj, hash);
538+
if (node) return cdr(car(node));
539+
return nil;
540+
}
541+
542+
bool pvm::set_property(object* obj, object* val, uint64_t hash, object* value) {
543+
// Nil has no properties
544+
if (!obj) return false;
545+
// Check if it is an object-object (primitives have no own properties)
546+
if (obj->type != &obj_type) return false;
547+
hashmap_set(this, &cdr(obj), val, hash, value);
548+
return true;
549+
}
550+
551+
bool pvm::remove_property(object* obj, uint64_t hash) {
552+
// Nil has no properties
553+
if (!obj) return false;
554+
// Check if it is an object-object (primitives have no own properties)
555+
if (obj->type != &obj_type) return false;
556+
bool had = hashmap_find(this, cdr(obj), hash) != nil;
557+
// Try to set the node to nil, which will kill the shadow references
558+
object* node = hashmap_set(this, &cdr(obj), nil, hash, nil);
559+
// Then kill this node too
560+
ASSERT(node, "hashmap_set() failed");
561+
car(node) = nil;
562+
return had;
563+
}
564+
565+
// ------------------ PATTERN MATCHING -----------------------------
566+
416567
static object* get_best_match(pvm* vm, object* ast, object** env) {
417-
return NULL;
568+
return nil;
418569
}
419570

420571
// Eval(list) ::= apply_first_pattern(list), then eval(remaining list), else list if no patterns match
@@ -448,24 +599,27 @@ object* eval(pvm* vm, object* cookie, object* inst_type) {
448599
// // No matches so return unchanged
449600
// vm->set_retval(vm->list(1, ast), env, cont, fail_cont);
450601
// }
602+
return nil;
451603
}
452604

453605
object* splice_match(pvm* vm, object* cookie, object* inst_type) {
454606
// TODO(sm);
607+
return nil;
455608
}
456609

457610
// ------------------- Circular-reference-proof object dumper -----------------------
458611
// ---------- (based on https://stackoverflow.com/a/78169673/23626926) --------------
459612

460613
static void make_refs_list(pvm* vm, object* obj, object** alist) {
461614
again:
462-
if (obj == NULL || obj->type != &cons_type) return;
615+
if (obj == NULL || (obj->type != &cons_type && obj->type != &obj_type)) return;
463616
object* entry = assoc(*alist, obj);
464617
if (entry) {
465618
cdr(entry) = vm->integer(2);
466619
return;
467620
}
468621
vm->push(vm->cons(obj, vm->integer(1)), *alist);
622+
if (obj->type == &obj_type) return; // hashmaps are guaranteed non disjoint, i guess
469623
make_refs_list(vm, car(obj), alist);
470624
obj = cdr(obj);
471625
goto again;
@@ -493,6 +647,22 @@ static int64_t reffed(pvm* vm, object* obj, object* alist, int64_t* counter) {
493647
return 0;
494648
}
495649

650+
static void print_with_refs(pvm*, object*, object*, int64_t*);
651+
652+
static void print_hashmap(pvm* vm, object* node, object* alist, int64_t* counter) {
653+
recur:
654+
if (node) {
655+
print_with_refs(vm, car(cdr(car(node))), alist, counter);
656+
printf(": ");
657+
print_with_refs(vm, cdr(cdr(car(node))), alist, counter);
658+
printf(", ");
659+
if (!cdr(node)) return;
660+
print_hashmap(vm, car(cdr(node)), alist, counter);
661+
node = cdr(cdr(node));
662+
goto recur;
663+
}
664+
}
665+
496666
static void print_with_refs(pvm* vm, object* obj, object* alist, int64_t* counter) {
497667
if (obj == nil) {
498668
printf("NIL");
@@ -517,8 +687,7 @@ static void print_with_refs(pvm* vm, object* obj, object* alist, int64_t* counte
517687
PRINTTYPE(&c_function_type, as_ptr, "<function %p>");
518688
PRINTTYPE(NULL, as_ptr, "<garbage %p>");
519689
#undef PRINTTYPE
520-
else if (obj->type != &cons_type) printf("<%s:%p>", obj->type->name, obj->as_ptr);
521-
else {
690+
else if (obj->type == &cons_type) {
522691
// it's a cons
523692
// test if it's in the table
524693
int64_t ref = reffed(vm, obj, alist, counter);
@@ -552,6 +721,21 @@ static void print_with_refs(pvm* vm, object* obj, object* alist, int64_t* counte
552721
}
553722
putchar(')');
554723
}
724+
else if (obj->type == &obj_type) {
725+
// Try to find the class name
726+
// TODO: String/symbol/int hash.
727+
const char* nm = "object";
728+
// if (car(obj) && !cdr(car(obj)) && car(car(obj))) {
729+
// object* super = car(car(obj));
730+
// object* name = vm->get_property(obj, vm->static_hash(vm->string("__name__")));
731+
// if (name->type == &symbol_type) nm = vm->stringof(name);
732+
// }
733+
printf("%s { ", nm);
734+
print_hashmap(vm, cdr(obj), alist, counter);
735+
putchar('}');
736+
}
737+
else printf("<%s:%p>", obj->type->name, obj->as_ptr);
738+
555739
}
556740

557741
void pvm::dump(object* obj) {
@@ -561,4 +745,10 @@ void pvm::dump(object* obj) {
561745
print_with_refs(this, obj, alist, &counter);
562746
}
563747

748+
749+
size_t pvm::gc() {
750+
DBG("TODO: garbage collect all of the hashmaps");
751+
return tinobsy::vm::gc();
752+
}
753+
564754
}

pickle.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,24 @@ class pvm : public tinobsy::vm {
164164
return x->as_double;
165165
}
166166

167+
// Returns a new empty object, of the specified prototypes (which must be a cons list)
168+
inline object* newobject(object* prototypes = nil) {
169+
object* o = this->alloc(&obj_type);
170+
car(o) = prototypes;
171+
cdr(o) = nil;
172+
return o;
173+
}
174+
175+
// Looks up the property on the object, optionally recursing into prototypes if it's not found directly.
176+
// If it is not found anywhere return nil.
177+
object* get_property(object* obj, uint64_t hash, bool recurse = false);
178+
179+
// Sets the property directly on the object. Returns true if setting succeeded.
180+
bool set_property(object* obj, object* key, uint64_t hash, object* value);
181+
182+
// Sets the property directly on the object. Returns true if something was removed.
183+
bool remove_property(object* obj, uint64_t hash);
184+
167185
// execute one instruction on the current thread and go to the next thread
168186
void step();
169187

@@ -174,6 +192,11 @@ class pvm : public tinobsy::vm {
174192
void dump(object*);
175193

176194

195+
196+
// overridden garbage collect
197+
size_t gc();
198+
199+
177200
private:
178201
// marks reachable objects
179202
void mark_globals();

pickle_test.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//#define TINOBSY_DEBUG
1+
#define TINOBSY_DEBUG
22
#include "pickle.hpp"
33

44
#include <stdio.h>
@@ -38,6 +38,8 @@ lambda x
3838
3939
)=";
4040

41+
#define SEPARATOR printf("\n\n----------------------------------------------------------------------------------------\n\n")
42+
4143
int main() {
4244
pvm vm;
4345
vm.defop("parse", pickle::parse);
@@ -66,6 +68,27 @@ int main() {
6668
vm.dump(vm.queue);
6769
printf("\n\n");
6870
}
71+
SEPARATOR;
72+
printf("hashmap test\n");
73+
auto foo = vm.newobject();
74+
for (size_t i = 0; i < 10; i++) {
75+
vm.set_property(foo, vm.integer(i), i, vm.integer(i));
76+
printf("Dump of object: ");
77+
vm.dump(foo);
78+
putchar('\n');
79+
}
80+
for (size_t i = 0; i < 10; i++) {
81+
vm.set_property(foo, vm.integer(i), i, vm.integer(i));
82+
printf("Dump of object: ");
83+
vm.dump(foo);
84+
putchar('\n');
85+
}
86+
ASSERT(vm.get_property(foo, 0) != nil);
87+
auto pair = vm.get_property(foo, 0);
88+
ASSERT(car(foo) == pair);
89+
putchar('\n');
90+
vm.dump(foo);
91+
SEPARATOR;
6992
printf("all done -- cleaning up\n");
7093
// implicit destruction of vm;
7194
return 0;

0 commit comments

Comments
 (0)