157 lines
5.5 KiB
C
157 lines
5.5 KiB
C
/*
|
|
* intern.c
|
|
*
|
|
* For now this implements an oblist and shallow binding; local environments can
|
|
* be consed onto the front of the oblist. Later, this won't do; bindings will happen
|
|
* in namespaces, which will probably be implemented as hash tables.
|
|
*
|
|
* Doctrine is that cons cells are immutable, and life is a lot more simple if they are;
|
|
* so when a symbol is rebound in the master oblist, what in fact we do is construct
|
|
* a new oblist without the previous binding but with the new binding. Anything which,
|
|
* prior to this action, held a pointer to the old oblist (as all current threads'
|
|
* environments must do) continues to hold a pointer to the old oblist, and consequently
|
|
* doesn't see the change. This is probably good but does mean you cannot use bindings
|
|
* on the oblist to signal between threads.
|
|
*
|
|
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
|
* Licensed under GPL version 2.0, or, at your option, any later version.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include "conspage.h"
|
|
#include "consspaceobject.h"
|
|
#include "debug.h"
|
|
#include "equal.h"
|
|
#include "lispops.h"
|
|
#include "print.h"
|
|
|
|
/**
|
|
* The object list. What is added to this during system setup is 'global', that is,
|
|
* visible to all sessions/threads. What is added during a session/thread is local to
|
|
* that session/thread (because shallow binding). There must be some way for a user to
|
|
* make the contents of their own environment persistent between threads but I don't
|
|
* know what it is yet. At some stage there must be a way to rebind deep values so
|
|
* they're visible to all users/threads, but again I don't yet have any idea how
|
|
* that will work.
|
|
*/
|
|
struct cons_pointer oblist = NIL;
|
|
|
|
/**
|
|
* Implementation of interned? in C. The final implementation if interned? will
|
|
* deal with stores which can be association lists or hashtables or hybrids of
|
|
* the two, but that will almost certainly be implemented in lisp.
|
|
*
|
|
* If this key is lexically identical to a key in this store, return the key
|
|
* from the store (so that later when we want to retrieve a value, an eq test
|
|
* will work); otherwise return NIL.
|
|
*/
|
|
struct cons_pointer
|
|
internedp( struct cons_pointer key, struct cons_pointer store ) {
|
|
struct cons_pointer result = NIL;
|
|
|
|
if ( symbolp( key ) ) {
|
|
for ( struct cons_pointer next = store;
|
|
nilp( result ) && consp( next );
|
|
next = pointer2cell( next ).payload.cons.cdr ) {
|
|
struct cons_space_object entry =
|
|
pointer2cell( pointer2cell( next ).payload.cons.car );
|
|
|
|
debug_print( L"Internedp: checking whether `", DEBUG_BIND );
|
|
debug_print_object( key, DEBUG_BIND );
|
|
debug_print( L"` equals `", DEBUG_BIND );
|
|
debug_print_object( entry.payload.cons.car, DEBUG_BIND );
|
|
debug_print( L"`\n", DEBUG_BIND );
|
|
|
|
if ( equal( key, entry.payload.cons.car ) ) {
|
|
result = entry.payload.cons.car;
|
|
}
|
|
}
|
|
} else {
|
|
debug_print( L"`", DEBUG_BIND );
|
|
debug_print_object( key, DEBUG_BIND );
|
|
debug_print( L"` is a ", DEBUG_BIND );
|
|
debug_print_object( c_type( key ), DEBUG_BIND );
|
|
debug_print( L", not a SYMB", DEBUG_BIND );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Implementation of assoc in C. Like interned?, the final implementation will
|
|
* deal with stores which can be association lists or hashtables or hybrids of
|
|
* the two, but that will almost certainly be implemented in lisp.
|
|
*
|
|
* If this key is lexically identical to a key in this store, return the value
|
|
* of that key from the store; otherwise return NIL.
|
|
*/
|
|
struct cons_pointer c_assoc( struct cons_pointer key,
|
|
struct cons_pointer store ) {
|
|
struct cons_pointer result = NIL;
|
|
|
|
for ( struct cons_pointer next = store;
|
|
consp( next ); next = pointer2cell( next ).payload.cons.cdr ) {
|
|
struct cons_space_object entry =
|
|
pointer2cell( pointer2cell( next ).payload.cons.car );
|
|
|
|
if ( equal( key, entry.payload.cons.car ) ) {
|
|
result = entry.payload.cons.cdr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return a new key/value store containing all the key/value pairs in this store
|
|
* with this key/value pair added to the front.
|
|
*/
|
|
struct cons_pointer
|
|
bind( struct cons_pointer key, struct cons_pointer value,
|
|
struct cons_pointer store ) {
|
|
debug_print(L"Binding ", DEBUG_BIND);
|
|
debug_print_object(key, DEBUG_BIND);
|
|
debug_print(L" to ", DEBUG_BIND);
|
|
debug_print_object(value, DEBUG_BIND);
|
|
debug_println( DEBUG_BIND);
|
|
|
|
return make_cons( make_cons( key, value ), store );
|
|
}
|
|
|
|
/**
|
|
* Binds this key to this value in the global oblist, but doesn't affect the
|
|
* current environment. May not be useful except in bootstrapping (and even
|
|
* there it may not be especially useful).
|
|
*/
|
|
struct cons_pointer
|
|
deep_bind( struct cons_pointer key, struct cons_pointer value ) {
|
|
debug_print( L"Entering deep_bind\n", DEBUG_BIND );
|
|
|
|
oblist = bind( key, value, oblist );
|
|
|
|
debug_print( L"Leaving deep_bind\n", DEBUG_BIND );
|
|
|
|
return oblist;
|
|
}
|
|
|
|
/**
|
|
* Ensure that a canonical copy of this key is bound in this environment, and
|
|
* return that canonical copy. If there is currently no such binding, create one
|
|
* with the value NIL.
|
|
*/
|
|
struct cons_pointer
|
|
intern( struct cons_pointer key, struct cons_pointer environment ) {
|
|
struct cons_pointer result = environment;
|
|
struct cons_pointer canonical = internedp( key, environment );
|
|
if ( nilp( canonical ) ) {
|
|
/*
|
|
* not currently bound
|
|
*/
|
|
result = bind( key, NIL, environment );
|
|
}
|
|
|
|
return result;
|
|
}
|