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 }
68
69 return pointer;
70}
71
72/**
73 * Decrement the reference count of the object at this cons pointer.
74 *
75 * If a count has reached MAXREFERENCE it cannot be decremented.
76 * If a count is decremented to zero the cell should be freed.
77 *
78 * Returns the `pointer`, or, if the cell has been freed, NIL.
79 */
80struct cons_pointer dec_ref( struct cons_pointer pointer ) {
81 struct cons_space_object *cell = &pointer2cell( pointer );
82
83 if ( cell->count > 0 && cell->count != UINT32_MAX ) {
84 cell->count--;
85
86 if ( cell->count == 0 ) {
87 free_cell( pointer );
88 pointer = NIL;
89 }
90 }
91
92 return pointer;
93}
94
95/**
96 * Get the Lisp type of the single argument.
97 * @param pointer a pointer to the object whose type is requested.
98 * @return As a Lisp string, the tag of the object which is at that pointer.
99 */
100struct cons_pointer c_type( struct cons_pointer pointer ) {
101 struct cons_pointer result = NIL;
102 struct cons_space_object cell = pointer2cell( pointer );
103
104 if ( strncmp( ( char * ) &cell.tag.bytes, VECTORPOINTTAG, TAGLENGTH ) ==
105 0 ) {
106 struct vector_space_object *vec = pointer_to_vso( pointer );
107
108 for ( int i = TAGLENGTH - 1; i >= 0; i-- ) {
109 result =
110 make_string( ( wchar_t ) vec->header.tag.bytes[i], result );
111 }
112 } else {
113 for ( int i = TAGLENGTH - 1; i >= 0; i-- ) {
114 result = make_string( ( wchar_t ) cell.tag.bytes[i], result );
115 }
116 }
117
118 return result;
119}
120
121/**
122 * Implementation of car in C. If arg is not a cons, or the current user is not
123 * authorised to read it, does not error but returns nil.
124 */
125struct cons_pointer c_car( struct cons_pointer arg ) {
126 struct cons_pointer result = NIL;
127
128 if ( truep( authorised( arg, NIL ) ) && consp( arg ) ) {
129 result = pointer2cell( arg ).payload.cons.car;
130 }
131
132 return result;
133}
134
135/**
136 * Implementation of cdr in C. If arg is not a sequence, or the current user is
137 * not authorised to read it,does not error but returns nil.
138 */
139struct cons_pointer c_cdr( struct cons_pointer arg ) {
140 struct cons_pointer result = NIL;
141
142 if ( truep( authorised( arg, NIL ) ) ) {
143 struct cons_space_object *cell = &pointer2cell( arg );
144
145 switch ( cell->tag.value ) {
146 case CONSTV:
147 result = cell->payload.cons.cdr;
148 break;
149 case KEYTV:
150 case STRINGTV:
151 case SYMBOLTV:
152 result = cell->payload.string.cdr;
153 break;
154 }
155 }
156
157 return result;
158}
159
160/**
161 * Implementation of `length` in C. If arg is not a cons, does not error but
162 * returns 0.
163 */
164int c_length( struct cons_pointer arg ) {
165 int result = 0;
166
167 for ( struct cons_pointer c = arg; !nilp( c ); c = c_cdr( c ) ) {
168 result++;
169 }
170
171 return result;
172}
173
174/**
175 * Construct a cons cell from this pair of pointers.
176 */
178 struct cons_pointer cdr ) {
179 struct cons_pointer pointer = NIL;
180
181 pointer = allocate_cell( CONSTV );
182
183 struct cons_space_object *cell = &pointer2cell( pointer );
184
185 inc_ref( car );
186 inc_ref( cdr );
187 cell->payload.cons.car = car;
188 cell->payload.cons.cdr = cdr;
189
190 return pointer;
191}
192
193/**
194 * Construct an exception cell.
195 * @param message should be a lisp string describing the problem, but actually
196 * any cons pointer will do;
197 * @param frame_pointer should be the pointer to the frame in which the
198 * exception occurred.
199 */
201 struct cons_pointer frame_pointer ) {
202 struct cons_pointer result = NIL;
203 struct cons_pointer pointer = allocate_cell( EXCEPTIONTV );
204 struct cons_space_object *cell = &pointer2cell( pointer );
205
206 inc_ref( frame_pointer );
207 cell->payload.exception.payload = message;
208 cell->payload.exception.frame = frame_pointer;
209
210 result = pointer;
211
212 return result;
213}
214
215/**
216 * Construct a cell which points to an executable Lisp function.
217 */
219 struct cons_pointer ( *executable ) ( struct
221 *,
222 struct
224 struct
225 cons_pointer ) )
226{
227 struct cons_pointer pointer = allocate_cell( FUNCTIONTV );
228 struct cons_space_object *cell = &pointer2cell( pointer );
229 inc_ref( meta );
230
231 cell->payload.function.meta = meta;
232 cell->payload.function.executable = executable;
233
234 return pointer;
235}
236
237/**
238 * Construct a lambda (interpretable source) cell
239 */
241 struct cons_pointer body ) {
242 struct cons_pointer pointer = allocate_cell( LAMBDATV );
243 struct cons_space_object *cell = &pointer2cell( pointer );
244
245 inc_ref( args );
246 inc_ref( body );
247 cell->payload.lambda.args = args;
248 cell->payload.lambda.body = body;
249
250 return pointer;
251}
252
253/**
254 * Construct an nlambda (interpretable source) cell; to a
255 * lambda as a special form is to a function.
256 */
258 struct cons_pointer body ) {
259 struct cons_pointer pointer = allocate_cell( NLAMBDATV );
260
261 struct cons_space_object *cell = &pointer2cell( pointer );
262 inc_ref( args );
263 inc_ref( body );
264 cell->payload.lambda.args = args;
265 cell->payload.lambda.body = body;
266
267 return pointer;
268}
269
270/**
271 * Return a hash value for this string like thing.
272 *
273 * What's important here is that two strings with the same characters in the
274 * same order should have the same hash value, even if one was created using
275 * `"foobar"` and the other by `(append "foo" "bar")`. I *think* this function
276 * has that property. I doubt that it's the most efficient hash function to
277 * have that property.
278 *
279 * returns 0 for things which are not string like.
280 */
281uint32_t calculate_hash( wint_t c, struct cons_pointer ptr ) {
282 struct cons_space_object *cell = &pointer2cell( ptr );
283 uint32_t result = 0;
284
285 switch ( cell->tag.value ) {
286 case KEYTV:
287 case STRINGTV:
288 case SYMBOLTV:
289 if ( nilp( cell->payload.string.cdr ) ) {
290 result = ( uint32_t ) c;
291 } else {
292 result =
293 ( ( uint32_t ) c *
294 cell->payload.string.hash ) & 0xffffffff;
295 }
296 break;
297 }
298
299 return result;
300}
301
302/**
303 * Construct a string from this character (which later will be UTF) and
304 * this tail. A string is implemented as a flat list of cells each of which
305 * has one character and a pointer to the next; in the last cell the
306 * pointer to next is NIL.
307 */
309 uint32_t tag ) {
310 struct cons_pointer pointer = NIL;
311
312 if ( check_tag( tail, tag ) || check_tag( tail, NILTV ) ) {
313 pointer = allocate_cell( tag );
314 struct cons_space_object *cell = &pointer2cell( pointer );
315
316 cell->payload.string.character = c;
317 cell->payload.string.cdr = tail;
318
319 cell->payload.string.hash = calculate_hash( c, tail );
320 } else {
321 // \todo should throw an exception!
323 L"Warning: only NIL and %4.4s can be prepended to %4.4s\n",
324 tag, tag );
325 }
326
327 return pointer;
328}
329
330/**
331 * Construct a string from the character `c` and this `tail`. A string is
332 * implemented as a flat list of cells each of which has one character and a
333 * pointer to the next; in the last cell the pointer to next is NIL.
334 *
335 * @param c the character to add (prepend);
336 * @param tail the string which is being built.
337 */
338struct cons_pointer make_string( wint_t c, struct cons_pointer tail ) {
339 return make_string_like_thing( c, tail, STRINGTV );
340}
341
342/**
343 * Construct a symbol or keyword from the character `c` and this `tail`.
344 * Each is internally identical to a string except for having a different tag.
345 *
346 * @param c the character to add (prepend);
347 * @param tail the symbol which is being built.
348 * @param tag the tag to use: expected to be "SYMB" or "KEYW"
349 */
350struct cons_pointer make_symbol_or_key( wint_t c, struct cons_pointer tail,
351 uint32_t tag ) {
352 struct cons_pointer result;
353
354 if ( tag == SYMBOLTV || tag == KEYTV ) {
355 result = make_string_like_thing( c, tail, tag );
356
357 if ( tag == KEYTV ) {
358 struct cons_pointer r = internedp( result, oblist );
359
360 if ( nilp( r ) ) {
361 intern( result, oblist );
362 } else {
363 result = r;
364 }
365 }
366 } else {
367 result =
369 ( L"Unexpected tag when making symbol or key." ),
370 NIL );
371 }
372
373 return result;
374}
375
376/**
377 * Construct a cell which points to an executable Lisp special form.
378 */
380 struct cons_pointer ( *executable ) ( struct
382 *frame,
383 struct
385 struct
387 env ) )
388{
389 struct cons_pointer pointer = allocate_cell( SPECIALTV );
390 struct cons_space_object *cell = &pointer2cell( pointer );
391 inc_ref( meta );
392
393 cell->payload.special.meta = meta;
394 cell->payload.special.executable = executable;
395
396 return pointer;
397}
398
399/**
400 * Construct a cell which points to a stream open for reading.
401 * @param input the C stream to wrap.
402 * @param metadata a pointer to an associaton containing metadata on the stream.
403 * @return a pointer to the new read stream.
404 */
406 struct cons_pointer metadata ) {
407 struct cons_pointer pointer = allocate_cell( READTV );
408 struct cons_space_object *cell = &pointer2cell( pointer );
409
410 cell->payload.stream.stream = input;
411 cell->payload.stream.meta = metadata;
412
413 return pointer;
414}
415
416/**
417 * Construct a cell which points to a stream open for writing.
418 * @param output the C stream to wrap.
419 * @param metadata a pointer to an associaton containing metadata on the stream.
420 * @return a pointer to the new read stream.
421 */
423 struct cons_pointer metadata ) {
424 struct cons_pointer pointer = allocate_cell( WRITETV );
425 struct cons_space_object *cell = &pointer2cell( pointer );
426
427 cell->payload.stream.stream = output;
428 cell->payload.stream.meta = metadata;
429
430 return pointer;
431}
432
433/**
434 * Return a lisp keyword representation of this wide character string. In
435 * keywords, I am accepting only lower case characters and numbers.
436 */
437struct cons_pointer c_string_to_lisp_keyword( wchar_t *symbol ) {
438 struct cons_pointer result = NIL;
439
440 for ( int i = wcslen( symbol ) - 1; i >= 0; i-- ) {
441 wchar_t c = towlower( symbol[i] );
442
443 if ( iswalnum( c ) || c == L'-' ) {
444 result = make_keyword( c, result );
445 }
446 }
447
448 return result;
449}
450
451/**
452 * Return a lisp string representation of this wide character string.
453 */
454struct cons_pointer c_string_to_lisp_string( wchar_t *string ) {
455 struct cons_pointer result = NIL;
456
457 for ( int i = wcslen( string ) - 1; i >= 0; i-- ) {
458 if ( iswprint( string[i] ) && string[i] != '"' ) {
459 result = make_string( string[i], result );
460 }
461 }
462
463 return result;
464}
465
466/**
467 * Return a lisp symbol representation of this wide character string.
468 */
469struct cons_pointer c_string_to_lisp_symbol( wchar_t *symbol ) {
470 struct cons_pointer result = NIL;
471
472 for ( int i = wcslen( symbol ); i > 0; i-- ) {
473 result = make_symbol( symbol[i - 1], result );
474 }
475
476 return result;
477}
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:222
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:143
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_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:21
struct cons_pointer internedp(struct cons_pointer key, struct cons_pointer store)
Implementation of interned? in C.
Definition intern.c:281
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
#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...