Post Scarcity
A prototype for a post scarcity programming environment
Loading...
Searching...
No Matches
consspaceobject.c
Go to the documentation of this file.
1/*
2 * consspaceobject.c
3 *
4 * Structures common to all cons space objects.
5 *
6 *
7 * (c) 2017 Simon Brooke <simon@journeyman.cc>
8 * Licensed under GPL version 2.0, or, at your option, any later version.
9 */
10
11#include <stdint.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15/*
16 * wide characters
17 */
18#include <wchar.h>
19#include <wctype.h>
20
21#include "authorise.h"
22#include "debug.h"
23#include "io/print.h"
24#include "memory/conspage.h"
26#include "memory/stack.h"
27#include "memory/vectorspace.h"
28#include "ops/intern.h"
29
30/**
31 * True if the value of the tag on the cell at this `pointer` is this `value`,
32 * or, if the tag of the cell is `VECP`, if the value of the tag of the
33 * vectorspace object indicated by the cell is this `value`, else false.
34 */
35bool check_tag( struct cons_pointer pointer, uint32_t value ) {
36 bool result = false;
37
38 struct cons_space_object cell = pointer2cell( pointer );
39 result = cell.tag.value == value;
40
41 if ( result == false ) {
42 if ( cell.tag.value == VECTORPOINTTV ) {
43 struct vector_space_object *vec = pointer_to_vso( pointer );
44
45 if ( vec != NULL ) {
46 result = vec->header.tag.value == value;
47 }
48 }
49 }
50
51 return result;
52}
53
54/**
55 * increment the reference count of the object at this cons pointer.
56 *
57 * You can't roll over the reference count. Once it hits the maximum
58 * value you cannot increment further.
59 *
60 * Returns the `pointer`.
61 */
62struct cons_pointer inc_ref( struct cons_pointer pointer ) {
63 struct cons_space_object *cell = &pointer2cell( pointer );
64
65 if ( cell->count < MAXREFERENCE ) {
66 cell->count++;
67#ifdef DEBUG
69 L"\nIncremented cell of type %4.4s at page %d, offset %d to count %d",
70 ( ( char * ) cell->tag.bytes ), pointer.page,
71 pointer.offset, cell->count );
72 if ( strncmp( cell->tag.bytes, VECTORPOINTTAG, TAGLENGTH ) == 0 ) {
74 L"; pointer to vector object of type %4.4s.\n",
75 ( ( char * ) ( cell->payload.vectorp.tag.bytes ) ) );
76 } else {
78 }
79#endif
80 }
81
82 return pointer;
83}
84
85/**
86 * Decrement the reference count of the object at this cons pointer.
87 *
88 * If a count has reached MAXREFERENCE it cannot be decremented.
89 * If a count is decremented to zero the cell should be freed.
90 *
91 * Returns the `pointer`, or, if the cell has been freed, NIL.
92 */
93struct cons_pointer dec_ref( struct cons_pointer pointer ) {
94 struct cons_space_object *cell = &pointer2cell( pointer );
95
96 if ( cell->count > 0 && cell->count != UINT32_MAX ) {
97 cell->count--;
98#ifdef DEBUG
100 L"\nDecremented cell of type %4.4s at page %d, offset %d to count %d",
101 ( ( char * ) cell->tag.bytes ), pointer.page,
102 pointer.offset, cell->count );
103 if ( strncmp( ( char * ) cell->tag.bytes, VECTORPOINTTAG, TAGLENGTH )
104 == 0 ) {
106 L"; pointer to vector object of type %4.4s.\n",
107 ( ( char * ) ( cell->payload.vectorp.tag.bytes ) ) );
108 } else {
110 }
111#endif
112
113 if ( cell->count == 0 ) {
114 free_cell( pointer );
115 pointer = NIL;
116 }
117 }
118
119 return pointer;
120}
121
122/**
123 * Get the Lisp type of the single argument.
124 * @param pointer a pointer to the object whose type is requested.
125 * @return As a Lisp string, the tag of the object which is at that pointer.
126 */
127struct cons_pointer c_type( struct cons_pointer pointer ) {
128 /* Strings read by `read` have the null character termination. This means
129 * that for the same printable string, the hashcode is different from
130 * strings made with NIL termination. The question is which should be
131 * fixed, and actually that's probably strings read by `read`. However,
132 * for now, it was easier to add a null character here. */
133 struct cons_pointer result = make_string( ( wchar_t ) 0, NIL );
134 struct cons_space_object *cell = &pointer2cell( pointer );
135
136 if ( cell->tag.value == VECTORPOINTTV ) {
137 struct vector_space_object *vec = pointer_to_vso( pointer );
138
139 for ( int i = TAGLENGTH - 1; i >= 0; i-- ) {
140 result =
141 make_string( ( wchar_t ) vec->header.tag.bytes[i], result );
142 }
143 } else {
144 for ( int i = TAGLENGTH - 1; i >= 0; i-- ) {
145 result = make_string( ( wchar_t ) cell->tag.bytes[i], result );
146 }
147 }
148
149 return result;
150}
151
152/**
153 * Implementation of car in C. If arg is not a cons, or the current user is not
154 * authorised to read it, does not error but returns nil.
155 */
156struct cons_pointer c_car( struct cons_pointer arg ) {
157 struct cons_pointer result = NIL;
158
159 if ( truep( authorised( arg, NIL ) ) && consp( arg ) ) {
160 result = pointer2cell( arg ).payload.cons.car;
161 }
162
163 return result;
164}
165
166/**
167 * Implementation of cdr in C. If arg is not a sequence, or the current user is
168 * not authorised to read it, does not error but returns nil.
169 */
170struct cons_pointer c_cdr( struct cons_pointer arg ) {
171 struct cons_pointer result = NIL;
172
173 if ( truep( authorised( arg, NIL ) ) ) {
174 struct cons_space_object *cell = &pointer2cell( arg );
175
176 switch ( cell->tag.value ) {
177 case CONSTV:
178 result = cell->payload.cons.cdr;
179 break;
180 case KEYTV:
181 case STRINGTV:
182 case SYMBOLTV:
183 result = cell->payload.string.cdr;
184 break;
185 }
186 }
187
188 return result;
189}
190
191/**
192 * Implementation of `length` in C. If arg is not a cons, does not error but
193 * returns 0.
194 */
195int c_length( struct cons_pointer arg ) {
196 int result = 0;
197
198 for ( struct cons_pointer c = arg; !nilp( c ); c = c_cdr( c ) ) {
199 result++;
200 }
201
202 return result;
203}
204
205/**
206 * Construct a cons cell from this pair of pointers.
207 */
209 struct cons_pointer cdr ) {
210 struct cons_pointer pointer = NIL;
211
212 pointer = allocate_cell( CONSTV );
213
214 struct cons_space_object *cell = &pointer2cell( pointer );
215
216 inc_ref( car );
217 inc_ref( cdr );
218 cell->payload.cons.car = car;
219 cell->payload.cons.cdr = cdr;
220
221 return pointer;
222}
223
224/**
225 * Construct an exception cell.
226 * @param message should be a lisp string describing the problem, but actually
227 * any cons pointer will do;
228 * @param frame_pointer should be the pointer to the frame in which the
229 * exception occurred.
230 */
232 struct cons_pointer frame_pointer ) {
233 struct cons_pointer result = NIL;
234 struct cons_pointer pointer = allocate_cell( EXCEPTIONTV );
235 struct cons_space_object *cell = &pointer2cell( pointer );
236
237 inc_ref( frame_pointer );
238 cell->payload.exception.payload = message;
239 cell->payload.exception.frame = frame_pointer;
240
241 result = pointer;
242
243 return result;
244}
245
246/**
247 * Construct a cell which points to an executable Lisp function.
248 */
250 struct cons_pointer ( *executable ) ( struct
252 *,
253 struct
255 struct
256 cons_pointer ) )
257{
258 struct cons_pointer pointer = allocate_cell( FUNCTIONTV );
259 struct cons_space_object *cell = &pointer2cell( pointer );
260 inc_ref( meta );
261
262 cell->payload.function.meta = meta;
263 cell->payload.function.executable = executable;
264
265 return pointer;
266}
267
268/**
269 * Construct a lambda (interpretable source) cell
270 */
272 struct cons_pointer body ) {
273 struct cons_pointer pointer = allocate_cell( LAMBDATV );
274 struct cons_space_object *cell = &pointer2cell( pointer );
275
276 inc_ref( args );
277 inc_ref( body );
278 cell->payload.lambda.args = args;
279 cell->payload.lambda.body = body;
280
281 return pointer;
282}
283
284/**
285 * Construct an nlambda (interpretable source) cell; to a
286 * lambda as a special form is to a function.
287 */
289 struct cons_pointer body ) {
290 struct cons_pointer pointer = allocate_cell( NLAMBDATV );
291
292 struct cons_space_object *cell = &pointer2cell( pointer );
293 inc_ref( args );
294 inc_ref( body );
295 cell->payload.lambda.args = args;
296 cell->payload.lambda.body = body;
297
298 return pointer;
299}
300
301/**
302 * Return a hash value for this string like thing.
303 *
304 * What's important here is that two strings with the same characters in the
305 * same order should have the same hash value, even if one was created using
306 * `"foobar"` and the other by `(append "foo" "bar")`. I *think* this function
307 * has that property. I doubt that it's the most efficient hash function to
308 * have that property.
309 *
310 * returns 0 for things which are not string like.
311 */
312uint32_t calculate_hash( wint_t c, struct cons_pointer ptr ) {
313 struct cons_space_object *cell = &pointer2cell( ptr );
314 uint32_t result = 0;
315
316 switch ( cell->tag.value ) {
317 case KEYTV:
318 case STRINGTV:
319 case SYMBOLTV:
320 if ( nilp( cell->payload.string.cdr ) ) {
321 result = ( uint32_t ) c;
322 } else {
323 result =
324 ( ( uint32_t ) c *
325 cell->payload.string.hash ) & 0xffffffff;
326 }
327 break;
328 }
329
330 return result;
331}
332
333/**
334 * Construct a string from this character (which later will be UTF) and
335 * this tail. A string is implemented as a flat list of cells each of which
336 * has one character and a pointer to the next; in the last cell the
337 * pointer to next is NIL.
338 */
340 uint32_t tag ) {
341 struct cons_pointer pointer = NIL;
342
343 if ( check_tag( tail, tag ) || check_tag( tail, NILTV ) ) {
344 pointer = allocate_cell( tag );
345 struct cons_space_object *cell = &pointer2cell( pointer );
346
347 cell->payload.string.character = c;
348 cell->payload.string.cdr = tail;
349
350 cell->payload.string.hash = calculate_hash( c, tail );
351 debug_dump_object( pointer, DEBUG_ALLOC );
353 } else {
354 // \todo should throw an exception!
356 L"Warning: only %4.4s can be prepended to %4.4s\n",
357 tag, tag );
358 }
359
360 return pointer;
361}
362
363/**
364 * Construct a string from the character `c` and this `tail`. A string is
365 * implemented as a flat list of cells each of which has one character and a
366 * pointer to the next; in the last cell the pointer to next is NIL.
367 *
368 * @param c the character to add (prepend);
369 * @param tail the string which is being built.
370 */
371struct cons_pointer make_string( wint_t c, struct cons_pointer tail ) {
372 return make_string_like_thing( c, tail, STRINGTV );
373}
374
375/**
376 * Construct a symbol or keyword from the character `c` and this `tail`.
377 * Each is internally identical to a string except for having a different tag.
378 *
379 * @param c the character to add (prepend);
380 * @param tail the symbol which is being built.
381 * @param tag the tag to use: expected to be "SYMB" or "KEYW"
382 */
383struct cons_pointer make_symbol_or_key( wint_t c, struct cons_pointer tail,
384 uint32_t tag ) {
385 struct cons_pointer result;
386
387 if ( tag == SYMBOLTV || tag == KEYTV ) {
388 result = make_string_like_thing( c, tail, tag );
389
390 if ( tag == KEYTV ) {
391 struct cons_pointer r = internedp( result, oblist );
392
393 if ( nilp( r ) ) {
394 intern( result, oblist );
395 } else {
396 result = r;
397 }
398 }
399 } else {
400 result =
402 ( L"Unexpected tag when making symbol or key." ),
403 NIL );
404 }
405
406 return result;
407}
408
409/**
410 * Construct a cell which points to an executable Lisp special form.
411 */
413 struct cons_pointer ( *executable ) ( struct
415 *frame,
416 struct
418 struct
420 env ) )
421{
422 struct cons_pointer pointer = allocate_cell( SPECIALTV );
423 struct cons_space_object *cell = &pointer2cell( pointer );
424 inc_ref( meta );
425
426 cell->payload.special.meta = meta;
427 cell->payload.special.executable = executable;
428
429 return pointer;
430}
431
432/**
433 * Construct a cell which points to a stream open for reading.
434 * @param input the C stream to wrap.
435 * @param metadata a pointer to an associaton containing metadata on the stream.
436 * @return a pointer to the new read stream.
437 */
439 struct cons_pointer metadata ) {
440 struct cons_pointer pointer = allocate_cell( READTV );
441 struct cons_space_object *cell = &pointer2cell( pointer );
442
443 cell->payload.stream.stream = input;
444 cell->payload.stream.meta = metadata;
445
446 return pointer;
447}
448
449/**
450 * Construct a cell which points to a stream open for writing.
451 * @param output the C stream to wrap.
452 * @param metadata a pointer to an associaton containing metadata on the stream.
453 * @return a pointer to the new read stream.
454 */
456 struct cons_pointer metadata ) {
457 struct cons_pointer pointer = allocate_cell( WRITETV );
458 struct cons_space_object *cell = &pointer2cell( pointer );
459
460 cell->payload.stream.stream = output;
461 cell->payload.stream.meta = metadata;
462
463 return pointer;
464}
465
466/**
467 * Return a lisp keyword representation of this wide character string. In
468 * keywords, I am accepting only lower case characters and numbers.
469 */
470struct cons_pointer c_string_to_lisp_keyword( wchar_t *symbol ) {
471 struct cons_pointer result = NIL;
472
473 for ( int i = wcslen( symbol ) - 1; i >= 0; i-- ) {
474 wchar_t c = towlower( symbol[i] );
475
476 if ( iswalnum( c ) || c == L'-' ) {
477 result = make_keyword( c, result );
478 }
479 }
480
481 return result;
482}
483
484/**
485 * Return a lisp string representation of this wide character string.
486 */
487struct cons_pointer c_string_to_lisp_string( wchar_t *string ) {
488 struct cons_pointer result = NIL;
489
490 for ( int i = wcslen( string ) - 1; i >= 0; i-- ) {
491 if ( iswprint( string[i] ) && string[i] != '"' ) {
492 result = make_string( string[i], result );
493 }
494 }
495
496 return result;
497}
498
499/**
500 * Return a lisp symbol representation of this wide character string.
501 */
502struct cons_pointer c_string_to_lisp_symbol( wchar_t *symbol ) {
503 struct cons_pointer result = NIL;
504
505 for ( int i = wcslen( symbol ); i > 0; i-- ) {
506 result = make_symbol( symbol[i - 1], result );
507 }
508
509 return result;
510}
struct cons_pointer authorised(struct cons_pointer target, struct cons_pointer acl)
TODO: does nothing, yet.
Definition authorise.c:18
struct cons_pointer allocate_cell(uint32_t tag)
Allocates a cell with the specified tag.
Definition conspage.c:231
void free_cell(struct cons_pointer pointer)
Frees the cell at the specified pointer; for all the types of cons-space object which point to other ...
Definition conspage.c:152
struct cons_pointer make_special(struct cons_pointer meta, struct cons_pointer(*executable)(struct stack_frame *frame, struct cons_pointer, struct cons_pointer env))
Construct a cell which points to an executable Lisp special form.
uint32_t calculate_hash(wint_t c, struct cons_pointer ptr)
Return a hash value for this string like thing.
struct cons_pointer make_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Construct an exception cell.
struct cons_pointer make_lambda(struct cons_pointer args, struct cons_pointer body)
Construct a lambda (interpretable source) cell.
struct cons_pointer make_symbol_or_key(wint_t c, struct cons_pointer tail, uint32_t tag)
Construct a symbol or keyword from the character c and this tail.
struct cons_pointer c_string_to_lisp_keyword(wchar_t *symbol)
Return a lisp keyword representation of this wide character string.
struct cons_pointer c_cdr(struct cons_pointer arg)
Implementation of cdr in C.
struct cons_pointer make_string_like_thing(wint_t c, struct cons_pointer tail, uint32_t tag)
Construct a string from this character (which later will be UTF) and this tail.
int c_length(struct cons_pointer arg)
Implementation of length in C.
struct cons_pointer make_string(wint_t c, struct cons_pointer tail)
Construct a string from the character c and this tail.
struct cons_pointer make_function(struct cons_pointer meta, struct cons_pointer(*executable)(struct stack_frame *, struct cons_pointer, struct cons_pointer))
Construct a cell which points to an executable Lisp function.
bool check_tag(struct cons_pointer pointer, uint32_t value)
True if the value of the tag on the cell at this pointer is this value, or, if the tag of the cell is...
struct cons_pointer make_write_stream(URL_FILE *output, struct cons_pointer metadata)
Construct a cell which points to a stream open for writing.
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.
struct cons_pointer make_read_stream(URL_FILE *input, struct cons_pointer metadata)
Construct a cell which points to a stream open for reading.
struct cons_pointer c_string_to_lisp_symbol(wchar_t *symbol)
Return a lisp symbol representation of this wide character string.
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 make_nlambda(struct cons_pointer args, struct cons_pointer body)
Construct an nlambda (interpretable source) cell; to a lambda as a special form is to a function.
struct cons_pointer dec_ref(struct cons_pointer pointer)
Decrement the reference count of the object at this cons pointer.
struct cons_pointer make_cons(struct cons_pointer car, struct cons_pointer cdr)
Construct a cons cell from this pair of pointers.
#define KEYTV
The string KEYW, considered as an unsigned int.
#define SPECIALTV
The string SPFM, considered as an unsigned int.
#define VECTORPOINTTV
The string VECP, considered as an unsigned int.
#define VECTORPOINTTAG
A pointer to an object in vector space.
#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
#define FUNCTIONTV
The string FUNC, considered as an unsigned int.
struct cons_pointer make_exception(struct cons_pointer message, struct cons_pointer frame_pointer)
Construct an exception cell.
#define STRINGTV
The string STRG, considered as an unsigned int.
struct cons_pointer make_string_like_thing(wint_t c, struct cons_pointer tail, uint32_t tag)
Construct a string from this character (which later will be UTF) and this tail.
#define TAGLENGTH
The length of a tag, in bytes.
#define consp(conspoint)
true if conspoint points to a cons cell, else false
#define CONSTV
The string CONS, considered as an unsigned int.
struct cons_pointer make_string(wint_t c, struct cons_pointer tail)
Construct a string from the character c and this tail.
#define nilp(conspoint)
true if conspoint points to the special cell NIL, else false (there should only be one of these so it...
#define NLAMBDATV
The string NLMD, considered as an unsigned int.
bool check_tag(struct cons_pointer pointer, uint32_t value)
True if the value of the tag on the cell at this pointer is this value, or, if the tag of the cell is...
uint32_t count
the count of the number of references to this cell
#define make_keyword(c, t)
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 NILTV
The string NIL, considered as an unsigned int.
#define EXCEPTIONTV
The string EXEP, considered as an unsigned int.
#define make_symbol(c, t)
#define MAXREFERENCE
the maximum possible value of a reference count
#define LAMBDATV
The string LMDA, considered as an unsigned int.
#define WRITETV
The string WRIT, considered as an unsigned int.
#define READTV
The string READ, considered as an unsigned int.
#define pointer2cell(pointer)
given a cons_pointer as argument, return the cell.
An indirect pointer to a cons cell.
an object in cons space.
A stack frame.
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
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:24
struct cons_pointer internedp(struct cons_pointer key, struct cons_pointer store)
Implementation of interned? in C.
Definition intern.c:282
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:49
#define pointer_to_vso(pointer)
given a pointer to a vector space object, return the object.
Definition vectorspace.h:55
struct vector_space_header header
the header of this object
union vector_space_header::@4 tag
the tag (type) of this vector-space object.
a vector_space_object is just a vector_space_header followed by a lump of bytes; what we deem to be i...