Post Scarcity
A prototype for a post scarcity programming environment
Loading...
Searching...
No Matches
intern.c
Go to the documentation of this file.
1/*
2 * intern.c
3 *
4 * For now this implements an oblist and shallow binding; local environments can
5 * be consed onto the front of the oblist. Later, this won't do; bindings will happen
6 * in namespaces, which will probably be implemented as hash tables.
7 *
8 * Doctrine is that cons cells are immutable, and life is a lot more simple if they are;
9 * so when a symbol is rebound in the master oblist, what in fact we do is construct
10 * a new oblist without the previous binding but with the new binding. Anything which,
11 * prior to this action, held a pointer to the old oblist (as all current threads'
12 * environments must do) continues to hold a pointer to the old oblist, and consequently
13 * doesn't see the change. This is probably good but does mean you cannot use bindings
14 * on the oblist to signal between threads.
15 *
16 * (c) 2017 Simon Brooke <simon@journeyman.cc>
17 * Licensed under GPL version 2.0, or, at your option, any later version.
18 */
19
20#include <stdbool.h>
21/*
22 * wide characters
23 */
24#include <wchar.h>
25#include <wctype.h>
26
27#include "authorise.h"
28#include "debug.h"
29#include "io/io.h"
30#include "memory/conspage.h"
32#include "memory/hashmap.h"
33#include "ops/equal.h"
34#include "ops/intern.h"
35#include "ops/lispops.h"
36// #include "print.h"
37
38/**
39 * @brief The global object list/or, to put it differently, the root namespace.
40 * What is added to this during system setup is 'global', that is,
41 * visible to all sessions/threads. What is added during a session/thread is local to
42 * that session/thread (because shallow binding). There must be some way for a user to
43 * make the contents of their own environment persistent between threads but I don't
44 * know what it is yet. At some stage there must be a way to rebind deep values so
45 * they're visible to all users/threads, but again I don't yet have any idea how
46 * that will work.
47 */
49
50/**
51 * @brief the symbol `NIL`, which is special!
52 *
53 */
55
56/**
57 * Return a hash value for the structure indicated by `ptr` such that if
58 * `x`,`y` are two separate structures whose print representation is the same
59 * then `(sxhash x)` and `(sxhash y)` will always be equal.
60 */
61uint32_t sxhash( struct cons_pointer ptr ) {
62 // TODO: Not Yet Implemented
63 /* TODO: should look at the implementation of Common Lisp sxhash?
64 * My current implementation of `print` only addresses URL_FILE
65 * streams. It would be better if it also addressed strings but
66 * currently it doesn't. Creating a print string of the structure
67 * and taking the hash of that would be one simple (but not necessarily
68 * cheap) solution.
69 */
70 /* TODO: sbcl's implementation of `sxhash` is in src/compiler/sxhash.lisp
71 * and is EXTREMELY complex, and essentially has a different dispatch for
72 * every type of object. It's likely we need to do the same.
73 */
74 return 0;
75}
76
77/**
78 * Get the hash value for the cell indicated by this `ptr`; currently only
79 * implemented for string like things and integers.
80 */
81uint32_t get_hash( struct cons_pointer ptr ) {
82 struct cons_space_object *cell = &pointer2cell( ptr );
83 uint32_t result = 0;
84
85 switch ( cell->tag.value ) {
86 case INTEGERTV:
87 /* Note that we're only hashing on the least significant word of an
88 * integer. */
89 result = cell->payload.integer.value & 0xffffffff;
90 break;
91 case KEYTV:
92 case STRINGTV:
93 case SYMBOLTV:
94 result = cell->payload.string.hash;
95 break;
96 case TRUETV:
97 result = 1; // arbitrarily
98 break;
99 default:
100 result = sxhash( ptr );
101 break;
102 }
103
104 return result;
105}
106
107/**
108 * Free the hashmap indicated by this `pointer`.
109 */
110void free_hashmap( struct cons_pointer pointer ) {
111 struct cons_space_object *cell = &pointer2cell( pointer );
112
113 if ( hashmapp( pointer ) ) {
114 struct vector_space_object *vso = cell->payload.vectorp.address;
115
116 dec_ref( vso->payload.hashmap.hash_fn );
117 dec_ref( vso->payload.hashmap.write_acl );
118
119 for ( int i = 0; i < vso->payload.hashmap.n_buckets; i++ ) {
120 if ( !nilp( vso->payload.hashmap.buckets[i] ) ) {
122 L"Decrementing bucket [%d] of hashmap at 0x%lx\n",
123 i, cell->payload.vectorp.address );
124 dec_ref( vso->payload.hashmap.buckets[i] );
125 }
126 }
127 } else {
128 debug_printf( DEBUG_ALLOC, L"Non-hashmap passed to `free_hashmap`\n" );
129 }
130}
131
132
133/**
134 * Make a hashmap with this number of buckets, using this `hash_fn`. If
135 * `hash_fn` is `NIL`, use the standard hash funtion.
136 */
137struct cons_pointer make_hashmap( uint32_t n_buckets,
138 struct cons_pointer hash_fn,
139 struct cons_pointer write_acl ) {
140 struct cons_pointer result = make_vso( HASHTV,
141 ( sizeof( struct cons_pointer ) *
142 ( n_buckets + 2 ) ) +
143 ( sizeof( uint32_t ) * 2 ) );
144
145 struct hashmap_payload *payload =
146 ( struct hashmap_payload * ) &pointer_to_vso( result )->payload;
147
148 payload->hash_fn = inc_ref( hash_fn );
149 payload->write_acl = inc_ref( write_acl );
150
151 payload->n_buckets = n_buckets;
152 for ( int i = 0; i < n_buckets; i++ ) {
153 payload->buckets[i] = NIL;
154 }
155
156 return result;
157}
158
159/**
160 * return a flat list of all the keys in the hashmap indicated by `map`.
161 */
163 struct cons_pointer result = NIL;
164 if ( hashmapp( mapp ) && truep( authorised( mapp, NIL ) ) ) {
165 struct vector_space_object *map = pointer_to_vso( mapp );
166
167 for ( int i = 0; i < map->payload.hashmap.n_buckets; i++ ) {
168 for ( struct cons_pointer c = map->payload.hashmap.buckets[i];
169 !nilp( c ); c = c_cdr( c ) ) {
170 result = make_cons( c_car( c_car( c ) ), result );
171 }
172 }
173 }
174
175 return result;
176}
177
178/**
179 * Copy all key/value pairs in this association list `assoc` into this hashmap `mapp`. If
180 * current user is authorised to write to this hashmap, modifies the hashmap and
181 * returns it; if not, clones the hashmap, modifies the clone, and returns that.
182 */
184 struct cons_pointer assoc ) {
185 // TODO: if current user has write access to this hashmap
186 if ( hashmapp( mapp ) ) {
187 struct vector_space_object *map = pointer_to_vso( mapp );
188
189 if ( consp( assoc ) ) {
190 for ( struct cons_pointer pair = c_car( assoc ); !nilp( pair );
191 pair = c_car( assoc ) ) {
192 /* TODO: this is really hammering the memory management system, because
193 * it will make a new lone for every key/value pair added. Fix. */
194 if ( consp( pair ) ) {
195 mapp = hashmap_put( mapp, c_car( pair ), c_cdr( pair ) );
196 } else if ( hashmapp( pair ) ) {
197 hashmap_put_all( mapp, pair );
198 } else {
199 hashmap_put( mapp, pair, TRUE );
200 }
201 assoc = c_cdr( assoc );
202 }
203 } else if ( hashmapp( assoc ) ) {
204 for ( struct cons_pointer keys = hashmap_keys( assoc );
205 !nilp( keys ); keys = c_cdr( keys ) ) {
206 struct cons_pointer key = c_car( keys );
207 hashmap_put( mapp, key, hashmap_get( assoc, key ) );
208 }
209 }
210 }
211
212 return mapp;
213}
214
215/** Get a value from a hashmap.
216 *
217 * Note that this is here, rather than in memory/hashmap.c, because it is
218 * closely tied in with c_assoc, q.v.
219 */
221 struct cons_pointer key ) {
222 struct cons_pointer result = NIL;
223 if ( hashmapp( mapp ) && truep( authorised( mapp, NIL ) ) && !nilp( key ) ) {
224 struct vector_space_object *map = pointer_to_vso( mapp );
225 uint32_t bucket_no = get_hash( key ) % map->payload.hashmap.n_buckets;
226
227 result = c_assoc( key, map->payload.hashmap.buckets[bucket_no] );
228 }
229
230 return result;
231}
232
233/**
234 * If this `ptr` is a pointer to a hashmap, return a new identical hashmap;
235 * else return an exception.
236 */
238 struct cons_pointer result = NIL;
239
240 if ( truep( authorised( ptr, NIL ) ) ) {
241 if ( hashmapp( ptr ) ) {
242 struct vector_space_object const *from = pointer_to_vso( ptr );
243
244 if ( from != NULL ) {
245 struct hashmap_payload from_pl = from->payload.hashmap;
246 result =
247 make_hashmap( from_pl.n_buckets, from_pl.hash_fn,
248 from_pl.write_acl );
249 struct vector_space_object const *to =
250 pointer_to_vso( result );
251 struct hashmap_payload to_pl = to->payload.hashmap;
252
253 for ( int i = 0; i < to_pl.n_buckets; i++ ) {
254 to_pl.buckets[i] = from_pl.buckets[i];
255 inc_ref( to_pl.buckets[i] );
256 }
257 }
258 }
259 } else {
260 result =
262 ( L"Arg to `clone_hashmap` must "
263 L"be a readable hashmap.`" ), NIL );
264 }
265
266 return result;
267}
268
269// (keys set let quote read equal *out* *log* oblist cons source cond close meta mapcar negative? open subtract eval nλ *in* *sink* cdr set! reverse slurp try assoc eq add list time car t *prompt* absolute append apply divide exception get-hash hashmap inspect metadata multiply print put! put-all! read-char repl throw type + * - / = lambda λ nlambda progn)
270
271/**
272 * Implementation of interned? in C. The final implementation if interned? will
273 * deal with stores which can be association lists or hashtables or hybrids of
274 * the two, but that will almost certainly be implemented in lisp.
275 *
276 * If this key is lexically identical to a key in this store, return the key
277 * from the store (so that later when we want to retrieve a value, an eq test
278 * will work); otherwise return NIL.
279 */
280struct cons_pointer
281internedp( struct cons_pointer key, struct cons_pointer store ) {
282 struct cons_pointer result = NIL;
283
284 if ( symbolp( key ) || keywordp( key ) ) {
285 // TODO: I see what I was doing here and it would be the right thing to
286 // do for stores which are old-fashioned assoc lists, but it will not work
287 // for my new hybrid stores.
288 // for ( struct cons_pointer next = store;
289 // nilp( result ) && consp( next );
290 // next = pointer2cell( next ).payload.cons.cdr ) {
291 // struct cons_space_object entry =
292 // pointer2cell( pointer2cell( next ).payload.cons.car );
293
294 // debug_print( L"Internedp: checking whether `", DEBUG_BIND );
295 // debug_print_object( key, DEBUG_BIND );
296 // debug_print( L"` equals `", DEBUG_BIND );
297 // debug_print_object( entry.payload.cons.car, DEBUG_BIND );
298 // debug_print( L"`\n", DEBUG_BIND );
299
300 // if ( equal( key, entry.payload.cons.car ) ) {
301 // result = entry.payload.cons.car;
302 // }
303 if ( !nilp( c_assoc( key, store ) ) ) {
304 result = key;
305 } else if ( equal( key, privileged_symbol_nil ) ) {
306 result = privileged_symbol_nil;
307 }
308 } else {
309 debug_print( L"`", DEBUG_BIND );
311 debug_print( L"` is a ", DEBUG_BIND );
313 debug_print( L", not a KEYW or SYMB", DEBUG_BIND );
314 }
315
316 return result;
317}
318
319/**
320 * Implementation of assoc in C. Like interned?, the final implementation will
321 * deal with stores which can be association lists or hashtables or hybrids of
322 * the two, but that will almost certainly be implemented in lisp.
323 *
324 * If this key is lexically identical to a key in this store, return the value
325 * of that key from the store; otherwise return NIL.
326 */
328 struct cons_pointer store ) {
329 struct cons_pointer result = NIL;
330
331 debug_print( L"c_assoc; key is `", DEBUG_BIND );
333 debug_print( L"`\n", DEBUG_BIND );
334
335 if ( consp( store ) ) {
336 for ( struct cons_pointer next = store;
337 nilp( result ) && ( consp( next ) || hashmapp( next ) );
338 next = pointer2cell( next ).payload.cons.cdr ) {
339 if ( consp( next ) ) {
340 struct cons_pointer entry_ptr = c_car( next );
341 struct cons_space_object entry = pointer2cell( entry_ptr );
342
343 switch ( entry.tag.value ) {
344 case CONSTV:
345 if ( equal( key, entry.payload.cons.car ) ) {
346 result = entry.payload.cons.cdr;
347 }
348 break;
349 case VECTORPOINTTV:
350 result = hashmap_get( entry_ptr, key );
351 break;
352 default:
355 ( L"Store entry is of unknown type: " ),
356 c_type( entry_ptr ) ), NIL );
357 }
358 }
359 }
360 } else if ( hashmapp( store ) ) {
361 result = hashmap_get( store, key );
362 } else if ( !nilp( store ) ) {
363 debug_print( L"c_assoc; store is of unknown type `", DEBUG_BIND );
365 debug_print( L"`\n", DEBUG_BIND );
366 result =
369 ( L"Store is of unknown type: " ),
370 c_type( store ) ), NIL );
371 }
372
373 debug_print( L"c_assoc returning ", DEBUG_BIND );
376
377 return result;
378}
379
380/**
381 * Store this `val` as the value of this `key` in this hashmap `mapp`. If
382 * current user is authorised to write to this hashmap, modifies the hashmap and
383 * returns it; if not, clones the hashmap, modifies the clone, and returns that.
384 */
386 struct cons_pointer key,
387 struct cons_pointer val ) {
388 if ( hashmapp( mapp ) && !nilp( key ) ) {
389 struct vector_space_object *map = pointer_to_vso( mapp );
390
391 if ( nilp( authorised( mapp, map->payload.hashmap.write_acl ) ) ) {
392 mapp = clone_hashmap( mapp );
393 map = pointer_to_vso( mapp );
394 }
395 uint32_t bucket_no = get_hash( key ) % map->payload.hashmap.n_buckets;
396
397 // TODO: if there are too many values in the bucket, rehash the whole
398 // hashmap to a bigger number of buckets, and return that.
399
400 map->payload.hashmap.buckets[bucket_no] =
401 inc_ref( make_cons( make_cons( key, val ),
402 map->payload.hashmap.buckets[bucket_no] ) );
403 }
404
405 return mapp;
406}
407
408 /**
409 * Return a new key/value store containing all the key/value pairs in this
410 * store with this key/value pair added to the front.
411 */
412struct cons_pointer set( struct cons_pointer key, struct cons_pointer value,
413 struct cons_pointer store ) {
414 struct cons_pointer result = NIL;
415
416 debug_print( L"set: binding `", DEBUG_BIND );
418 debug_print( L"` to `", DEBUG_BIND );
420 debug_print( L"` in store ", DEBUG_BIND );
423
424 debug_printf( DEBUG_BIND, L"set: store is %s\n`",
425 lisp_string_to_c_string( c_type( store ) ) );
426 if ( nilp( value ) ) {
427 result = store;
428 } else if ( nilp( store ) || consp( store ) ) {
429 result = make_cons( make_cons( key, value ), store );
430 } else if ( hashmapp( store ) ) {
431 debug_print( L"set: storing in hashmap\n", DEBUG_BIND );
432 result = hashmap_put( store, key, value );
433 }
434
435 debug_print( L"set returning ", DEBUG_BIND );
438
439 return result;
440}
441
442/**
443 * @brief Binds this key to this value in the global oblist.
444
445 */
446struct cons_pointer
447deep_bind( struct cons_pointer key, struct cons_pointer value ) {
448 debug_print( L"Entering deep_bind\n", DEBUG_BIND );
449
450 struct cons_pointer old = oblist;
451
452 debug_print( L"deep_bind: binding `", DEBUG_BIND );
454 debug_print( L"` to ", DEBUG_BIND );
457
458 oblist = set( key, value, oblist );
459
460 if ( consp( oblist ) ) {
461 inc_ref( oblist );
462 dec_ref( old );
463 }
464
465 debug_print( L"deep_bind returning ", DEBUG_BIND );
468
469 return key;
470}
471
472/**
473 * Ensure that a canonical copy of this key is bound in this environment, and
474 * return that canonical copy. If there is currently no such binding, create one
475 * with the value NIL.
476 */
477struct cons_pointer
478intern( struct cons_pointer key, struct cons_pointer environment ) {
479 struct cons_pointer result = environment;
480 struct cons_pointer canonical = internedp( key, environment );
481 if ( nilp( canonical ) ) {
482 /*
483 * not currently bound
484 */
485 result = set( key, NIL, environment );
486 }
487
488 return result;
489}
struct cons_pointer authorised(struct cons_pointer target, struct cons_pointer acl)
TODO: does nothing, yet.
Definition authorise.c:18
struct cons_pointer dec_ref(struct cons_pointer pointer)
Decrement the reference count of the object at this cons pointer.
#define KEYTV
The string KEYW, considered as an unsigned int.
#define VECTORPOINTTV
The string VECP, considered as an unsigned int.
#define truep(conspoint)
true if conspoint points to something that is truthy, i.e.
#define SYMBOLTV
The string SYMB, considered as an unsigned int.
union cons_space_object::@2 tag
union cons_space_object::@3 payload
#define NIL
a cons pointer which points to the special NIL cell
struct cons_pointer make_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Construct an exception cell.
struct cons_pointer c_cdr(struct cons_pointer arg)
Implementation of cdr in C.
#define STRINGTV
The string STRG, considered as an unsigned int.
#define INTEGERTV
The string INTR, considered as an unsigned int.
#define consp(conspoint)
true if conspoint points to a cons cell, else false
#define CONSTV
The string CONS, considered as an unsigned int.
#define nilp(conspoint)
true if conspoint points to the special cell NIL, else false (there should only be one of these so it...
#define TRUETV
The string TRUE, considered as an unsigned int.
#define symbolp(conspoint)
true if conspoint points to a symbol cell, else false
struct cons_pointer c_string_to_lisp_string(wchar_t *string)
Return a lisp string representation of this wide character string.
struct cons_pointer inc_ref(struct cons_pointer pointer)
increment the reference count of the object at this cons pointer.
#define TRUE
a cons pointer which points to the special T cell
#define keywordp(conspoint)
true if conspoint points to a keyword, else false
struct cons_pointer c_car(struct cons_pointer arg)
Implementation of car in C.
struct cons_pointer c_type(struct cons_pointer pointer)
Get the Lisp type of the single argument.
struct cons_pointer dec_ref(struct cons_pointer pointer)
Decrement the reference count of the object at this cons pointer.
#define pointer2cell(pointer)
given a cons_pointer as argument, return the cell.
struct cons_pointer make_cons(struct cons_pointer car, struct cons_pointer cdr)
Construct a cons cell from this pair of pointers.
An indirect pointer to a cons cell.
an object in cons space.
void debug_println(int level)
print a line feed to stderr, if verbosity matches level.
Definition debug.c:85
void debug_dump_object(struct cons_pointer pointer, int level)
Like dump_object, q.v., but protected by the verbosity mechanism.
Definition debug.c:136
void debug_printf(int level, wchar_t *format,...)
wprintf adapted for the debug logging system.
Definition debug.c:101
void debug_print(wchar_t *message, int level)
print this debug message to stderr, if verbosity matches level.
Definition debug.c:41
void debug_print_object(struct cons_pointer pointer, int level)
print the object indicated by this pointer to stderr, if verbosity matches level.
Definition debug.c:119
#define DEBUG_BIND
Print messages debugging symbol binding.
Definition debug.h:35
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:21
bool equal(struct cons_pointer a, struct cons_pointer b)
Deep, and thus expensive, equality: true if these two objects have identical structure,...
Definition equal.c:247
struct cons_pointer hashmap_get(struct cons_pointer mapp, struct cons_pointer key)
Get a value from a hashmap.
Definition intern.c:220
struct cons_pointer hashmap_put(struct cons_pointer mapp, struct cons_pointer key, struct cons_pointer val)
Store this val as the value of this key in this hashmap mapp.
Definition intern.c:385
struct cons_pointer privileged_symbol_nil
the symbol NIL, which is special!
Definition intern.c:54
void free_hashmap(struct cons_pointer pointer)
Free the hashmap indicated by this pointer.
Definition intern.c:110
struct cons_pointer hashmap_put_all(struct cons_pointer mapp, struct cons_pointer assoc)
Copy all key/value pairs in this association list assoc into this hashmap mapp.
Definition intern.c:183
struct cons_pointer make_hashmap(uint32_t n_buckets, struct cons_pointer hash_fn, struct cons_pointer write_acl)
Make a hashmap with this number of buckets, using this hash_fn.
Definition intern.c:137
struct cons_pointer hashmap_keys(struct cons_pointer mapp)
return a flat list of all the keys in the hashmap indicated by map.
Definition intern.c:162
uint32_t get_hash(struct cons_pointer ptr)
Get the hash value for the cell indicated by this ptr; currently only implemented for string like thi...
Definition intern.c:81
struct cons_pointer internedp(struct cons_pointer key, struct cons_pointer store)
Implementation of interned? in C.
Definition intern.c:281
uint32_t sxhash(struct cons_pointer ptr)
Return a hash value for the structure indicated by ptr such that if x,y are two separate structures w...
Definition intern.c:61
struct cons_pointer c_assoc(struct cons_pointer key, struct cons_pointer store)
Implementation of assoc in C.
Definition intern.c:327
struct cons_pointer intern(struct cons_pointer key, struct cons_pointer environment)
Ensure that a canonical copy of this key is bound in this environment, and return that canonical copy...
Definition intern.c:478
struct cons_pointer oblist
The global object list/or, to put it differently, the root namespace.
Definition intern.c:48
struct cons_pointer clone_hashmap(struct cons_pointer ptr)
If this ptr is a pointer to a hashmap, return a new identical hashmap; else return an exception.
Definition intern.c:237
struct cons_pointer deep_bind(struct cons_pointer key, struct cons_pointer value)
Binds this key to this value in the global oblist.
Definition intern.c:447
struct cons_pointer set(struct cons_pointer key, struct cons_pointer value, struct cons_pointer store)
Return a new key/value store containing all the key/value pairs in this store with this key/value pai...
Definition intern.c:412
char * lisp_string_to_c_string(struct cons_pointer s)
Convert this lisp string-like-thing (also works for symbols, and, later keywords) into a UTF-8 string...
Definition io.c:95
struct cons_pointer throw_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Throw an exception.
Definition lispops.c:1208
struct cons_pointer c_append(struct cons_pointer l1, struct cons_pointer l2)
A version of append which can conveniently be called from C.
Definition lispops.c:1410
struct cons_pointer make_vso(uint32_t tag, uint64_t payload_size)
Allocate a vector space object with this payload_size and tag, and return a cons_pointer which points...
Definition vectorspace.c:75
struct cons_pointer hash_fn
Definition vectorspace.h:89
uint32_t n_buckets
Definition vectorspace.h:95
struct cons_pointer write_acl
Definition vectorspace.h:91
union vector_space_object::@5 payload
we'll malloc size bytes for payload, payload is just the first of these.
#define pointer_to_vso(pointer)
given a pointer to a vector space object, return the object.
Definition vectorspace.h:55
#define HASHTV
Definition vectorspace.h:30
#define hashmapp(conspoint)
Definition vectorspace.h:32
struct cons_pointer buckets[]
Definition vectorspace.h:97
The payload of a hashmap.
Definition vectorspace.h:88
a vector_space_object is just a vector_space_header followed by a lump of bytes; what we deem to be i...