Post Scarcity 0.0.6
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 * Keywords used when constructing exceptions: `:location`. Instantiated in
32 * `init.c`q.v.
33 */
35
36/**
37 * Keywords used when constructing exceptions: `:payload`. Instantiated in
38 * `init.c`, q.v.
39 */
41
42/**
43 * Keywords used when constructing exceptions: `:payload`. Instantiated in
44 * `init.c`, q.v.
45 */
47
48/**
49 * @brief keywords used in documentation: `:documentation`. Instantiated in
50 * `init.c`, q. v.
51 *
52 */
54
55/**
56 * @brief keywords used in documentation: `:name`. Instantiated in
57 * `init.c`, q. v.
58 */
60
61/**
62 * @brief keywords used in documentation: `:primitive`. Instantiated in
63 * `init.c`, q. v.
64 */
66
67
68/**
69 * True if the value of the tag on the cell at this `pointer` is this `value`,
70 * or, if the tag of the cell is `VECP`, if the value of the tag of the
71 * vectorspace object indicated by the cell is this `value`, else false.
72 */
73bool check_tag( struct cons_pointer pointer, uint32_t value ) {
74 bool result = false;
75
76 struct cons_space_object *cell = &pointer2cell( pointer );
77 result = cell->tag.value == value;
78
79 if ( result == false ) {
80 if ( cell->tag.value == VECTORPOINTTV ) {
81 struct vector_space_object *vec = pointer_to_vso( pointer );
82
83 if ( vec != NULL ) {
84 result = vec->header.tag.value == value;
85 }
86 }
87 }
88
89 return result;
90}
91
92/**
93 * increment the reference count of the object at this cons pointer.
94 *
95 * You can't roll over the reference count. Once it hits the maximum
96 * value you cannot increment further.
97 *
98 * Returns the `pointer`.
99 */
100struct cons_pointer inc_ref( struct cons_pointer pointer ) {
101 struct cons_space_object *cell = &pointer2cell( pointer );
102
103 if ( cell->count < MAXREFERENCE ) {
104 cell->count++;
105#ifdef DEBUG
107 L"\nIncremented cell of type %4.4s at page %u, offset %u to count %u",
108 ( ( char * ) cell->tag.bytes ), pointer.page,
109 pointer.offset, cell->count );
110 if ( strncmp( cell->tag.bytes, VECTORPOINTTAG, TAGLENGTH ) == 0 ) {
112 L"; pointer to vector object of type %4.4s.\n",
113 ( ( char * ) ( cell->payload.vectorp.tag.bytes ) ) );
114 } else {
116 }
117#endif
118 }
119
120 return pointer;
121}
122
123/**
124 * Decrement the reference count of the object at this cons pointer.
125 *
126 * If a count has reached MAXREFERENCE it cannot be decremented.
127 * If a count is decremented to zero the cell should be freed.
128 *
129 * Returns the `pointer`, or, if the cell has been freed, NIL.
130 */
131struct cons_pointer dec_ref( struct cons_pointer pointer ) {
132 struct cons_space_object *cell = &pointer2cell( pointer );
133
134 if ( cell->count > 0 && cell->count != UINT32_MAX ) {
135 cell->count--;
136#ifdef DEBUG
138 L"\nDecremented cell of type %4.4s at page %d, offset %d to count %d",
139 ( ( char * ) cell->tag.bytes ), pointer.page,
140 pointer.offset, cell->count );
141 if ( strncmp( ( char * ) cell->tag.bytes, VECTORPOINTTAG, TAGLENGTH )
142 == 0 ) {
144 L"; pointer to vector object of type %4.4s.\n",
145 ( ( char * ) ( cell->payload.vectorp.tag.bytes ) ) );
146 } else {
148 }
149#endif
150
151 if ( cell->count == 0 ) {
152 free_cell( pointer );
153 pointer = NIL;
154 }
155 }
156
157 return pointer;
158}
159
160/**
161 * given a cons_pointer as argument, return the tag.
162 */
163uint32_t get_tag_value( struct cons_pointer pointer ) {
164 uint32_t result = pointer2cell( pointer ).tag.value;
165
166 if ( result == VECTORPOINTTV ) {
167 result = pointer_to_vso( pointer )->header.tag.value;
168 }
169
170 return result;
171}
172
173/**
174 * Get the Lisp type of the single argument.
175 * @param pointer a pointer to the object whose type is requested.
176 * @return As a Lisp string, the tag of the object which is at that pointer.
177 */
178struct cons_pointer c_type( struct cons_pointer pointer ) {
179 /* Strings read by `read` have the null character termination. This means
180 * that for the same printable string, the hashcode is different from
181 * strings made with NIL termination. The question is which should be
182 * fixed, and actually that's probably strings read by `read`. However,
183 * for now, it was easier to add a null character here. */
184 struct cons_pointer result = make_string( ( wchar_t ) 0, NIL );
185 struct cons_space_object *cell = &pointer2cell( pointer );
186
187 if ( cell->tag.value == VECTORPOINTTV ) {
188 struct vector_space_object *vec = pointer_to_vso( pointer );
189
190 for ( int i = TAGLENGTH - 1; i >= 0; i-- ) {
191 result =
192 make_string( ( wchar_t ) vec->header.tag.bytes[i], result );
193 }
194 } else {
195 for ( int i = TAGLENGTH - 1; i >= 0; i-- ) {
196 result = make_string( ( wchar_t ) cell->tag.bytes[i], result );
197 }
198 }
199
200 return result;
201}
202
203/**
204 * Implementation of car in C. If arg is not a cons, or the current user is not
205 * authorised to read it, does not error but returns nil.
206 */
207struct cons_pointer c_car( struct cons_pointer arg ) {
208 struct cons_pointer result = NIL;
209
210 if ( truep( authorised( arg, NIL ) ) && consp( arg ) ) {
211 result = pointer2cell( arg ).payload.cons.car;
212 }
213
214 return result;
215}
216
217/**
218 * Implementation of cdr in C. If arg is not a sequence, or the current user is
219 * not authorised to read it, does not error but returns nil.
220 */
221struct cons_pointer c_cdr( struct cons_pointer arg ) {
222 struct cons_pointer result = NIL;
223
224 if ( truep( authorised( arg, NIL ) ) ) {
225 struct cons_space_object *cell = &pointer2cell( arg );
226
227 switch ( cell->tag.value ) {
228 case CONSTV:
229 result = cell->payload.cons.cdr;
230 break;
231 case KEYTV:
232 case STRINGTV:
233 case SYMBOLTV:
234 result = cell->payload.string.cdr;
235 break;
236 }
237 }
238
239 return result;
240}
241
242/**
243 * Implementation of `length` in C. If arg is not a cons, does not error but
244 * returns 0.
245 */
246int c_length( struct cons_pointer arg ) {
247 int result = 0;
248
249 for ( struct cons_pointer c = arg; !nilp( c ); c = c_cdr( c ) ) {
250 result++;
251 }
252
253 return result;
254}
255
256/**
257 * Construct a cons cell from this pair of pointers.
258 */
260 struct cons_pointer cdr ) {
261 struct cons_pointer pointer = NIL;
262
263 pointer = allocate_cell( CONSTV );
264
265 struct cons_space_object *cell = &pointer2cell( pointer );
266
267 inc_ref( car );
268 inc_ref( cdr );
269 cell->payload.cons.car = car;
270 cell->payload.cons.cdr = cdr;
271
272 return pointer;
273}
274
275/**
276 * Construct an exception cell.
277 * @param message should be a lisp string describing the problem, but actually
278 * any cons pointer will do;
279 * @param frame_pointer should be the pointer to the frame in which the
280 * exception occurred.
281 */
283 struct cons_pointer frame_pointer ) {
284 struct cons_pointer result = NIL;
285 struct cons_pointer pointer = allocate_cell( EXCEPTIONTV );
286 struct cons_space_object *cell = &pointer2cell( pointer );
287
288 inc_ref( frame_pointer );
289 cell->payload.exception.payload = message;
290 cell->payload.exception.frame = frame_pointer;
291
292 result = pointer;
293
294 return result;
295}
296
297/**
298 * Construct a cell which points to an executable Lisp function.
299 */
301 struct cons_pointer ( *executable ) ( struct
303 *,
304 struct
306 struct
307 cons_pointer ) )
308{
309 struct cons_pointer pointer = allocate_cell( FUNCTIONTV );
310 struct cons_space_object *cell = &pointer2cell( pointer );
311 inc_ref( meta );
312
313 cell->payload.function.meta = meta;
314 cell->payload.function.executable = executable;
315
316 return pointer;
317}
318
319/**
320 * Construct a lambda (interpretable source) cell
321 */
323 struct cons_pointer body ) {
324 struct cons_pointer pointer = allocate_cell( LAMBDATV );
325 struct cons_space_object *cell = &pointer2cell( pointer );
326
327 inc_ref( args );
328 inc_ref( body );
329 cell->payload.lambda.args = args;
330 cell->payload.lambda.body = body;
331
332 return pointer;
333}
334
335/**
336 * Construct an nlambda (interpretable source) cell; to a
337 * lambda as a special form is to a function.
338 */
340 struct cons_pointer body ) {
341 struct cons_pointer pointer = allocate_cell( NLAMBDATV );
342
343 struct cons_space_object *cell = &pointer2cell( pointer );
344 inc_ref( args );
345 inc_ref( body );
346 cell->payload.lambda.args = args;
347 cell->payload.lambda.body = body;
348
349 return pointer;
350}
351
352/**
353 * Return a hash value for this string like thing.
354 *
355 * What's important here is that two strings with the same characters in the
356 * same order should have the same hash value, even if one was created using
357 * `"foobar"` and the other by `(append "foo" "bar")`. I *think* this function
358 * has that property. I doubt that it's the most efficient hash function to
359 * have that property.
360 *
361 * returns 0 for things which are not string like.
362 */
363uint32_t calculate_hash( wint_t c, struct cons_pointer ptr ) {
364 struct cons_space_object *cell = &pointer2cell( ptr );
365 uint32_t result = 0;
366
367 switch ( cell->tag.value ) {
368 case KEYTV:
369 case STRINGTV:
370 case SYMBOLTV:
371 if ( nilp( cell->payload.string.cdr ) ) {
372 result = ( uint32_t ) c;
373 } else {
374 result =
375 ( ( uint32_t ) c *
376 cell->payload.string.hash ) & 0xffffffff;
377 }
378 break;
379 }
380
381 return result;
382}
383
384/**
385 * Construct a string from this character (which later will be UTF) and
386 * this tail. A string is implemented as a flat list of cells each of which
387 * has one character and a pointer to the next; in the last cell the
388 * pointer to next is NIL.
389 */
391 uint32_t tag ) {
392 struct cons_pointer pointer = NIL;
393
394 if ( check_tag( tail, tag ) || check_tag( tail, NILTV ) ) {
395 pointer = allocate_cell( tag );
396 struct cons_space_object *cell = &pointer2cell( pointer );
397
398 cell->payload.string.character = c;
399 cell->payload.string.cdr = tail;
400
401 cell->payload.string.hash = calculate_hash( c, tail );
402 debug_dump_object( pointer, DEBUG_ALLOC );
404 } else {
405 // \todo should throw an exception!
407 L"Warning: only %4.4s can be prepended to %4.4s\n",
408 tag, tag );
409 }
410
411 return pointer;
412}
413
414/**
415 * Construct a string from the character `c` and this `tail`. A string is
416 * implemented as a flat list of cells each of which has one character and a
417 * pointer to the next; in the last cell the pointer to next is NIL.
418 *
419 * @param c the character to add (prepend);
420 * @param tail the string which is being built.
421 */
422struct cons_pointer make_string( wint_t c, struct cons_pointer tail ) {
423 return make_string_like_thing( c, tail, STRINGTV );
424}
425
426/**
427 * Construct a symbol or keyword from the character `c` and this `tail`.
428 * Each is internally identical to a string except for having a different tag.
429 *
430 * @param c the character to add (prepend);
431 * @param tail the symbol which is being built.
432 * @param tag the tag to use: expected to be "SYMB" or "KEYW"
433 */
434struct cons_pointer make_symbol_or_key( wint_t c, struct cons_pointer tail,
435 uint32_t tag ) {
436 struct cons_pointer result;
437
438 if ( tag == SYMBOLTV || tag == KEYTV ) {
439 result = make_string_like_thing( c, tail, tag );
440
441 // if ( tag == KEYTV ) {
442 // struct cons_pointer r = interned( result, oblist );
443
444 // if ( nilp( r ) ) {
445 // intern( result, oblist );
446 // } else {
447 // result = r;
448 // }
449 // }
450 } else {
451 result =
453 ( L"Unexpected tag when making symbol or key." ),
454 NIL );
455 }
456
457 return result;
458}
459
460/**
461 * Construct a cell which points to an executable Lisp special form.
462 */
464 struct cons_pointer ( *executable ) ( struct
466 *frame,
467 struct
469 struct
471 env ) )
472{
473 struct cons_pointer pointer = allocate_cell( SPECIALTV );
474 struct cons_space_object *cell = &pointer2cell( pointer );
475 inc_ref( meta );
476
477 cell->payload.special.meta = meta;
478 cell->payload.special.executable = executable;
479
480 return pointer;
481}
482
483/**
484 * Construct a cell which points to a stream open for reading.
485 * @param input the C stream to wrap.
486 * @param metadata a pointer to an associaton containing metadata on the stream.
487 * @return a pointer to the new read stream.
488 */
490 struct cons_pointer metadata ) {
491 struct cons_pointer pointer = allocate_cell( READTV );
492 struct cons_space_object *cell = &pointer2cell( pointer );
493
494 cell->payload.stream.stream = input;
495 cell->payload.stream.meta = metadata;
496
497 return pointer;
498}
499
500/**
501 * Construct a cell which points to a stream open for writing.
502 * @param output the C stream to wrap.
503 * @param metadata a pointer to an associaton containing metadata on the stream.
504 * @return a pointer to the new read stream.
505 */
507 struct cons_pointer metadata ) {
508 struct cons_pointer pointer = allocate_cell( WRITETV );
509 struct cons_space_object *cell = &pointer2cell( pointer );
510
511 cell->payload.stream.stream = output;
512 cell->payload.stream.meta = metadata;
513
514 return pointer;
515}
516
517/**
518 * Return a lisp keyword representation of this wide character string. In
519 * keywords, I am accepting only lower case characters and numbers.
520 */
521struct cons_pointer c_string_to_lisp_keyword( wchar_t *symbol ) {
522 struct cons_pointer result = NIL;
523
524 for ( int i = wcslen( symbol ) - 1; i >= 0; i-- ) {
525 wchar_t c = towlower( symbol[i] );
526
527 if ( iswalnum( c ) || c == L'-' ) {
528 result = make_keyword( c, result );
529 }
530 }
531
532 return result;
533}
534
535/**
536 * Return a lisp string representation of this wide character string.
537 */
538struct cons_pointer c_string_to_lisp_string( wchar_t *string ) {
539 struct cons_pointer result = NIL;
540
541 for ( int i = wcslen( string ) - 1; i >= 0; i-- ) {
542 if ( iswprint( string[i] ) && string[i] != '"' ) {
543 result = make_string( string[i], result );
544 }
545 }
546
547 return result;
548}
549
550/**
551 * Return a lisp symbol representation of this wide character string.
552 */
553struct cons_pointer c_string_to_lisp_symbol( wchar_t *symbol ) {
554 struct cons_pointer result = NIL;
555
556 for ( int i = wcslen( symbol ); i > 0; i-- ) {
557 result = make_symbol( symbol[i - 1], result );
558 }
559
560 return result;
561}
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:235
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:156
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 privileged_keyword_primitive
keywords used in documentation: :primitive.
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 privileged_keyword_cause
Keywords used when constructing exceptions: :payload.
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.
struct cons_pointer privileged_keyword_name
keywords used in documentation: :name.
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 get_tag_value(struct cons_pointer pointer)
given a cons_pointer as argument, return the tag.
struct cons_pointer privileged_keyword_documentation
keywords used in documentation: :documentation.
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 privileged_keyword_payload
Keywords used when constructing exceptions: :payload.
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 privileged_keyword_location
Keywords used when constructing exceptions: :location.
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:104
void debug_dump_object(struct cons_pointer pointer, int level)
Like dump_object, q.v., but protected by the verbosity mechanism.
Definition debug.c:155
void debug_printf(int level, wchar_t *format,...)
wprintf adapted for the debug logging system.
Definition debug.c:120
#define DEBUG_ALLOC
Print messages debugging memory allocation.
Definition debug.h:24
#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...