Reorganised source files to make navigation easier
All tests still pass (slightly to my surprise)
This commit is contained in:
parent
f6ff403249
commit
a5e1d3ccd8
24 changed files with 73 additions and 72 deletions
246
src/memory/conspage.c
Normal file
246
src/memory/conspage.c
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* conspage.c
|
||||
*
|
||||
* Setup and tear down cons pages, and (FOR NOW) do primitive
|
||||
* allocation/deallocation of cells.
|
||||
* NOTE THAT before we go multi-threaded, these functions must be
|
||||
* aggressively
|
||||
* thread safe.
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "consspaceobject.h"
|
||||
#include "conspage.h"
|
||||
|
||||
/**
|
||||
* Flag indicating whether conspage initialisation has been done.
|
||||
*/
|
||||
bool conspageinitihasbeencalled = false;
|
||||
|
||||
/**
|
||||
* the number of cons pages which have thus far been initialised.
|
||||
*/
|
||||
int initialised_cons_pages = 0;
|
||||
|
||||
/**
|
||||
* The (global) pointer to the (global) freelist. Not sure whether this ultimately
|
||||
* belongs in this file.
|
||||
*/
|
||||
struct cons_pointer freelist = NIL;
|
||||
|
||||
/**
|
||||
* An array of pointers to cons pages.
|
||||
*/
|
||||
struct cons_page *conspages[NCONSPAGES];
|
||||
|
||||
/**
|
||||
* Make a cons page whose serial number (i.e. index in the conspages directory) is pageno.
|
||||
* Initialise all cells and prepend each to the freelist; if pageno is zero, do not prepend
|
||||
* cells 0 and 1 to the freelist but initialise them as NIL and T respectively.
|
||||
*/
|
||||
void make_cons_page( ) {
|
||||
struct cons_page *result = malloc( sizeof( struct cons_page ) );
|
||||
|
||||
if ( result != NULL ) {
|
||||
conspages[initialised_cons_pages] = result;
|
||||
|
||||
for ( int i = 0; i < CONSPAGESIZE; i++ ) {
|
||||
struct cons_space_object *cell =
|
||||
&conspages[initialised_cons_pages]->cell[i];
|
||||
if ( initialised_cons_pages == 0 && i < 2 ) {
|
||||
switch ( i ) {
|
||||
case 0:
|
||||
/*
|
||||
* initialise cell as NIL
|
||||
*/
|
||||
strncpy( &cell->tag.bytes[0], NILTAG, TAGLENGTH );
|
||||
cell->count = MAXREFERENCE;
|
||||
cell->payload.free.car = NIL;
|
||||
cell->payload.free.cdr = NIL;
|
||||
fwprintf( stderr, L"Allocated special cell NIL\n" );
|
||||
break;
|
||||
case 1:
|
||||
/*
|
||||
* initialise cell as T
|
||||
*/
|
||||
strncpy( &cell->tag.bytes[0], TRUETAG, TAGLENGTH );
|
||||
cell->count = MAXREFERENCE;
|
||||
cell->payload.free.car = ( struct cons_pointer ) {
|
||||
0, 1
|
||||
};
|
||||
cell->payload.free.cdr = ( struct cons_pointer ) {
|
||||
0, 1
|
||||
};
|
||||
fwprintf( stderr, L"Allocated special cell T\n" );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* otherwise, standard initialisation
|
||||
*/
|
||||
strncpy( &cell->tag.bytes[0], FREETAG, TAGLENGTH );
|
||||
cell->payload.free.car = NIL;
|
||||
cell->payload.free.cdr = freelist;
|
||||
freelist.page = initialised_cons_pages;
|
||||
freelist.offset = i;
|
||||
}
|
||||
}
|
||||
|
||||
initialised_cons_pages++;
|
||||
} else {
|
||||
fwprintf( stderr,
|
||||
L"FATAL: Failed to allocate memory for cons page %d\n",
|
||||
initialised_cons_pages );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dump the allocated pages to this output stream.
|
||||
*/
|
||||
void dump_pages( FILE * output ) {
|
||||
for ( int i = 0; i < initialised_cons_pages; i++ ) {
|
||||
fwprintf( output, L"\nDUMPING PAGE %d\n", i );
|
||||
|
||||
for ( int j = 0; j < CONSPAGESIZE; j++ ) {
|
||||
dump_object( output, ( struct cons_pointer ) {
|
||||
i, j
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the cell at the specified pointer. Dangerous, primitive, low
|
||||
* level.
|
||||
*
|
||||
* @pointer the cell to free
|
||||
*/
|
||||
void free_cell( struct cons_pointer pointer ) {
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
fwprintf( stderr, L"Freeing cell " );
|
||||
dump_object( stderr, pointer );
|
||||
|
||||
switch ( cell->tag.value ) {
|
||||
/* for all the types of cons-space object which point to other
|
||||
* cons-space objects, cascade the decrement. */
|
||||
case CONSTV:
|
||||
dec_ref( cell->payload.cons.car );
|
||||
dec_ref( cell->payload.cons.cdr );
|
||||
break;
|
||||
case EXCEPTIONTV:
|
||||
dec_ref( cell->payload.exception.message );
|
||||
break;
|
||||
case FUNCTIONTV:
|
||||
dec_ref( cell->payload.function.source );
|
||||
break;
|
||||
case LAMBDATV:
|
||||
case NLAMBDATV:
|
||||
dec_ref( cell->payload.lambda.args );
|
||||
dec_ref( cell->payload.lambda.body );
|
||||
break;
|
||||
case RATIOTV:
|
||||
dec_ref( cell->payload.ratio.dividend );
|
||||
dec_ref( cell->payload.ratio.divisor );
|
||||
break;
|
||||
case SPECIALTV:
|
||||
dec_ref( cell->payload.special.source );
|
||||
break;
|
||||
case STRINGTV:
|
||||
case SYMBOLTV:
|
||||
dec_ref( cell->payload.string.cdr );
|
||||
break;
|
||||
case VECTORPOINTTV:
|
||||
/* for vector space pointers, free the actual vector-space
|
||||
* object. Dangerous! */
|
||||
#ifdef DEBUG
|
||||
fwprintf( stderr, L"About to free vector-space object at %ld\n",
|
||||
cell->payload.vectorp.address );
|
||||
#endif
|
||||
free( ( void * ) cell->payload.vectorp.address );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( !check_tag( pointer, FREETAG ) ) {
|
||||
if ( cell->count == 0 ) {
|
||||
strncpy( &cell->tag.bytes[0], FREETAG, 4 );
|
||||
cell->payload.free.car = NIL;
|
||||
cell->payload.free.cdr = freelist;
|
||||
freelist = pointer;
|
||||
} else {
|
||||
fwprintf( stderr,
|
||||
L"ERROR: Attempt to free cell with %d dangling references at page %d, offset %d\n",
|
||||
cell->count, pointer.page, pointer.offset );
|
||||
}
|
||||
} else {
|
||||
fwprintf( stderr,
|
||||
L"ERROR: Attempt to free cell which is already FREE at page %d, offset %d\n",
|
||||
pointer.page, pointer.offset );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a cell with the specified tag. Dangerous, primitive, low
|
||||
* level.
|
||||
*
|
||||
* @param tag the tag of the cell to allocate - must be a valid cons space tag.
|
||||
* @return the cons pointer which refers to the cell allocated.
|
||||
*/
|
||||
struct cons_pointer allocate_cell( char *tag ) {
|
||||
struct cons_pointer result = freelist;
|
||||
|
||||
|
||||
if ( result.page == NIL.page && result.offset == NIL.offset ) {
|
||||
make_cons_page( );
|
||||
result = allocate_cell( tag );
|
||||
} else {
|
||||
struct cons_space_object *cell = &pointer2cell( result );
|
||||
|
||||
if ( strncmp( &cell->tag.bytes[0], FREETAG, TAGLENGTH ) == 0 ) {
|
||||
freelist = cell->payload.free.cdr;
|
||||
|
||||
strncpy( &cell->tag.bytes[0], tag, 4 );
|
||||
|
||||
cell->count = 0;
|
||||
cell->payload.cons.car = NIL;
|
||||
cell->payload.cons.cdr = NIL;
|
||||
|
||||
#ifdef DEBUG
|
||||
fwprintf( stderr,
|
||||
L"Allocated cell of type '%s' at %d, %d \n", tag,
|
||||
result.page, result.offset );
|
||||
#endif
|
||||
} else {
|
||||
fwprintf( stderr, L"WARNING: Allocating non-free cell!" );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialise the cons page system; to be called exactly once during startup.
|
||||
*/
|
||||
void initialise_cons_pages( ) {
|
||||
if ( conspageinitihasbeencalled == false ) {
|
||||
for ( int i = 0; i < NCONSPAGES; i++ ) {
|
||||
conspages[i] = ( struct cons_page * ) NULL;
|
||||
}
|
||||
|
||||
make_cons_page( );
|
||||
conspageinitihasbeencalled = true;
|
||||
} else {
|
||||
fwprintf( stderr,
|
||||
L"WARNING: initialise_cons_pages() called a second or subsequent time\n" );
|
||||
}
|
||||
}
|
||||
78
src/memory/conspage.h
Normal file
78
src/memory/conspage.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#include "consspaceobject.h"
|
||||
|
||||
#ifndef __conspage_h
|
||||
#define __conspage_h
|
||||
|
||||
/**
|
||||
* the number of cons cells on a cons page. The maximum value this can
|
||||
* be (and consequently, the size which, by version 1, it will default
|
||||
* to) is the maximum value of an unsigned 32 bit integer, which is to
|
||||
* say 4294967296. However, we'll start small.
|
||||
*/
|
||||
#define CONSPAGESIZE 8
|
||||
|
||||
/**
|
||||
* the number of cons pages we will initially allow for. For
|
||||
* convenience we'll set up an array of cons pages this big; however,
|
||||
* later we will want a mechanism for this to be able to grow
|
||||
* dynamically to the maximum we can currently allow, which is
|
||||
* 4294967296.
|
||||
*
|
||||
* Note that this means the total number of addressable cons cells is
|
||||
* 1.8e19, each of 20 bytes; or 3e20 bytes in total; and there are
|
||||
* up to a maximum of 4e9 of heap space objects, each of potentially
|
||||
* 4e9 bytes. So we're talking about a potential total of 8e100 bytes
|
||||
* of addressable memory, which is only slightly more than the
|
||||
* number of atoms in the universe.
|
||||
*/
|
||||
#define NCONSPAGES 8
|
||||
|
||||
/**
|
||||
* a cons page is essentially just an array of cons space objects. It
|
||||
* might later have a local free list (i.e. list of free cells on this
|
||||
* page) and a pointer to the next cons page, but my current view is
|
||||
* that that's probably unneccessary.
|
||||
*/
|
||||
struct cons_page {
|
||||
struct cons_space_object cell[CONSPAGESIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* The (global) pointer to the (global) freelist. Not sure whether this ultimately
|
||||
* belongs in this file.
|
||||
*/
|
||||
extern struct cons_pointer freelist;
|
||||
|
||||
/**
|
||||
* An array of pointers to cons pages.
|
||||
*/
|
||||
extern struct cons_page *conspages[NCONSPAGES];
|
||||
|
||||
/**
|
||||
* Frees the cell at the specified pointer. Dangerous, primitive, low
|
||||
* level.
|
||||
*
|
||||
* @pointer the cell to free
|
||||
*/
|
||||
void free_cell( struct cons_pointer pointer );
|
||||
|
||||
/**
|
||||
* Allocates a cell with the specified tag. Dangerous, primitive, low
|
||||
* level.
|
||||
*
|
||||
* @param tag the tag of the cell to allocate - must be a valid cons space tag.
|
||||
* @return the cons pointer which refers to the cell allocated.
|
||||
*/
|
||||
struct cons_pointer allocate_cell( char *tag );
|
||||
|
||||
/**
|
||||
* initialise the cons page system; to be called exactly once during startup.
|
||||
*/
|
||||
void initialise_cons_pages( );
|
||||
|
||||
/**
|
||||
* dump the allocated pages to this output stream.
|
||||
*/
|
||||
void dump_pages( FILE * output );
|
||||
|
||||
#endif
|
||||
386
src/memory/consspaceobject.c
Normal file
386
src/memory/consspaceobject.c
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* consspaceobject.c
|
||||
*
|
||||
* Structures common to all cons space objects.
|
||||
*
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
/*
|
||||
* wide characters
|
||||
*/
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "conspage.h"
|
||||
#include "consspaceobject.h"
|
||||
#include "print.h"
|
||||
#include "stack.h"
|
||||
|
||||
/**
|
||||
* Check that the tag on the cell at this pointer is this tag
|
||||
*/
|
||||
int check_tag( struct cons_pointer pointer, char *tag ) {
|
||||
struct cons_space_object cell = pointer2cell( pointer );
|
||||
return strncmp( &cell.tag.bytes[0], tag, TAGLENGTH ) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* increment the reference count of the object at this cons pointer.
|
||||
*
|
||||
* You can't roll over the reference count. Once it hits the maximum
|
||||
* value you cannot increment further.
|
||||
*/
|
||||
void inc_ref( struct cons_pointer pointer ) {
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
if ( cell->count < MAXREFERENCE ) {
|
||||
cell->count++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the reference count of the object at this cons pointer.
|
||||
*
|
||||
* If a count has reached MAXREFERENCE it cannot be decremented.
|
||||
* If a count is decremented to zero the cell should be freed.
|
||||
*/
|
||||
void dec_ref( struct cons_pointer pointer ) {
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
if ( cell->count > 0 ) {
|
||||
cell->count--;
|
||||
|
||||
if ( cell->count == 0 ) {
|
||||
free_cell( pointer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_string_cell( FILE * output, wchar_t *prefix,
|
||||
struct cons_pointer pointer ) {
|
||||
struct cons_space_object cell = pointer2cell( pointer );
|
||||
if ( cell.payload.string.character == 0 ) {
|
||||
fwprintf( output,
|
||||
L"\t\t%ls cell: termination; next at page %d offset %d, count %u\n",
|
||||
prefix,
|
||||
cell.payload.string.cdr.page, cell.payload.string.cdr.offset,
|
||||
cell.count );
|
||||
} else {
|
||||
fwprintf( output,
|
||||
L"\t\t%ls cell: character '%lc' (%d) next at page %d offset %d, count %u\n",
|
||||
prefix,
|
||||
( wint_t ) cell.payload.string.character,
|
||||
cell.payload.string.character,
|
||||
cell.payload.string.cdr.page,
|
||||
cell.payload.string.cdr.offset, cell.count );
|
||||
fwprintf( output, L"\t\t value: " );
|
||||
print( output, pointer );
|
||||
fwprintf( output, L"\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dump the object at this cons_pointer to this output stream.
|
||||
*/
|
||||
void dump_object( FILE * output, struct cons_pointer pointer ) {
|
||||
struct cons_space_object cell = pointer2cell( pointer );
|
||||
fwprintf( output,
|
||||
L"\t%c%c%c%c (%d) at page %d, offset %d count %u\n",
|
||||
cell.tag.bytes[0],
|
||||
cell.tag.bytes[1],
|
||||
cell.tag.bytes[2],
|
||||
cell.tag.bytes[3],
|
||||
cell.tag.value, pointer.page, pointer.offset, cell.count );
|
||||
|
||||
switch ( cell.tag.value ) {
|
||||
case CONSTV:
|
||||
fwprintf( output,
|
||||
L"\t\tCons cell: car at page %d offset %d, cdr at page %d offset %d, count %u\n",
|
||||
cell.payload.cons.car.page,
|
||||
cell.payload.cons.car.offset,
|
||||
cell.payload.cons.cdr.page,
|
||||
cell.payload.cons.cdr.offset, cell.count );
|
||||
break;
|
||||
case EXCEPTIONTV:
|
||||
fwprintf( output, L"\t\tException cell: " );
|
||||
print( output, cell.payload.exception.message );
|
||||
fwprintf( output, L"\n" );
|
||||
for ( struct stack_frame * frame = cell.payload.exception.frame;
|
||||
frame != NULL; frame = frame->previous ) {
|
||||
dump_frame( output, frame );
|
||||
}
|
||||
break;
|
||||
case FREETV:
|
||||
fwprintf( output, L"\t\tFree cell: next at page %d offset %d\n",
|
||||
cell.payload.cons.cdr.page,
|
||||
cell.payload.cons.cdr.offset );
|
||||
break;
|
||||
case INTEGERTV:
|
||||
fwprintf( output,
|
||||
L"\t\tInteger cell: value %ld, count %u\n",
|
||||
cell.payload.integer.value, cell.count );
|
||||
break;
|
||||
case LAMBDATV:
|
||||
fwprintf( output, L"\t\tLambda cell; args: " );
|
||||
print( output, cell.payload.lambda.args );
|
||||
fwprintf( output, L";\n\t\t\tbody: " );
|
||||
print( output, cell.payload.lambda.body );
|
||||
break;
|
||||
case RATIOTV:
|
||||
fwprintf( output,
|
||||
L"\t\tRational cell: value %ld/%ld, count %u\n",
|
||||
pointer2cell( cell.payload.ratio.dividend ).
|
||||
payload.integer.value,
|
||||
pointer2cell( cell.payload.ratio.divisor ).
|
||||
payload.integer.value, cell.count );
|
||||
break;
|
||||
case READTV:
|
||||
fwprintf( output, L"\t\tInput stream\n" );
|
||||
case REALTV:
|
||||
fwprintf( output, L"\t\tReal cell: value %Lf, count %u\n",
|
||||
cell.payload.real.value, cell.count );
|
||||
break;
|
||||
case STRINGTV:
|
||||
dump_string_cell( output, L"String", pointer );
|
||||
break;
|
||||
case SYMBOLTV:
|
||||
dump_string_cell( output, L"Symbol", pointer );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cons cell from this pair of pointers.
|
||||
*/
|
||||
struct cons_pointer make_cons( struct cons_pointer car,
|
||||
struct cons_pointer cdr ) {
|
||||
struct cons_pointer pointer = NIL;
|
||||
|
||||
pointer = allocate_cell( CONSTAG );
|
||||
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
inc_ref( car );
|
||||
inc_ref( cdr );
|
||||
cell->payload.cons.car = car;
|
||||
cell->payload.cons.cdr = cdr;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception cell.
|
||||
* @param message should be a lisp string describing the problem, but actually any cons pointer will do;
|
||||
* @param frame should be the frame in which the exception occurred.
|
||||
*/
|
||||
struct cons_pointer make_exception( struct cons_pointer message,
|
||||
struct stack_frame *frame ) {
|
||||
struct cons_pointer pointer = allocate_cell( EXCEPTIONTAG );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
inc_ref( pointer ); /* this is a hack; I don't know why it's necessary to do this, but if I don't the cell gets freed */
|
||||
|
||||
inc_ref( message );
|
||||
cell->payload.exception.message = message;
|
||||
cell->payload.exception.frame = frame;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a cell which points to an executable Lisp special form.
|
||||
*/
|
||||
struct cons_pointer
|
||||
make_function( struct cons_pointer src, struct cons_pointer ( *executable )
|
||||
( struct stack_frame *, struct cons_pointer ) ) {
|
||||
struct cons_pointer pointer = allocate_cell( FUNCTIONTAG );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
cell->payload.function.source = src;
|
||||
cell->payload.function.executable = executable;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a lambda (interpretable source) cell
|
||||
*/
|
||||
struct cons_pointer make_lambda( struct cons_pointer args,
|
||||
struct cons_pointer body ) {
|
||||
struct cons_pointer pointer = allocate_cell( LAMBDATAG );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
inc_ref( pointer ); /* this is a hack; I don't know why it's necessary to do this, but if I don't the cell gets freed */
|
||||
|
||||
inc_ref( args );
|
||||
inc_ref( body );
|
||||
cell->payload.lambda.args = args;
|
||||
cell->payload.lambda.body = body;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an nlambda (interpretable source) cell; to a
|
||||
* lambda as a special form is to a function.
|
||||
*/
|
||||
struct cons_pointer make_nlambda( struct cons_pointer args,
|
||||
struct cons_pointer body ) {
|
||||
struct cons_pointer pointer = allocate_cell( NLAMBDATAG );
|
||||
|
||||
inc_ref( pointer ); /* this is a hack; I don't know why it's necessary to do this, but if I don't the cell gets freed */
|
||||
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
inc_ref( args );
|
||||
inc_ref( body );
|
||||
cell->payload.lambda.args = args;
|
||||
cell->payload.lambda.body = body;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a ratio frame from these two pointers, expected to be integers
|
||||
* or (later) bignums, in the context of this stack_frame.
|
||||
*/
|
||||
struct cons_pointer make_ratio( struct stack_frame *frame,
|
||||
struct cons_pointer dividend,
|
||||
struct cons_pointer divisor ) {
|
||||
struct cons_pointer result;
|
||||
if ( integerp( dividend ) && integerp( divisor ) ) {
|
||||
inc_ref( dividend );
|
||||
inc_ref( divisor );
|
||||
result = allocate_cell( RATIOTAG );
|
||||
struct cons_space_object *cell = &pointer2cell( result );
|
||||
cell->payload.ratio.dividend = dividend;
|
||||
cell->payload.ratio.divisor = divisor;
|
||||
} else {
|
||||
result =
|
||||
make_exception( c_string_to_lisp_string
|
||||
( "Dividend and divisor of a ratio must be integers" ),
|
||||
frame );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a string from this character (which later will be UTF) and
|
||||
* this tail. A string is implemented as a flat list of cells each of which
|
||||
* has one character and a pointer to the next; in the last cell the
|
||||
* pointer to next is NIL.
|
||||
*/
|
||||
struct cons_pointer
|
||||
make_string_like_thing( wint_t c, struct cons_pointer tail, char *tag ) {
|
||||
struct cons_pointer pointer = NIL;
|
||||
|
||||
if ( check_tag( tail, tag ) || check_tag( tail, NILTAG ) ) {
|
||||
pointer = allocate_cell( tag );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
inc_ref( tail );
|
||||
cell->payload.string.character = c;
|
||||
cell->payload.string.cdr.page = tail.page;
|
||||
/* TODO: There's a problem here. Sometimes the offsets on
|
||||
* strings are quite massively off. */
|
||||
cell->payload.string.cdr.offset = tail.offset;
|
||||
} else {
|
||||
fwprintf( stderr,
|
||||
L"Warning: only NIL and %s can be appended to %s\n",
|
||||
tag, tag );
|
||||
}
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a string from this character and
|
||||
* this tail. A string is implemented as a flat list of cells each of which
|
||||
* has one character and a pointer to the next; in the last cell the
|
||||
* pointer to next is NIL.
|
||||
*/
|
||||
struct cons_pointer make_string( wint_t c, struct cons_pointer tail ) {
|
||||
return make_string_like_thing( c, tail, STRINGTAG );
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a symbol from this character and this tail.
|
||||
*/
|
||||
struct cons_pointer make_symbol( wint_t c, struct cons_pointer tail ) {
|
||||
return make_string_like_thing( c, tail, SYMBOLTAG );
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cell which points to an executable Lisp special form.
|
||||
*/
|
||||
struct cons_pointer
|
||||
make_special( struct cons_pointer src, struct cons_pointer ( *executable )
|
||||
( struct stack_frame * frame, struct cons_pointer env ) ) {
|
||||
struct cons_pointer pointer = allocate_cell( SPECIALTAG );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
cell->payload.special.source = src;
|
||||
cell->payload.special.executable = executable;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cell which points to a stream open for reading.
|
||||
* @param input the C stream to wrap.
|
||||
*/
|
||||
struct cons_pointer make_read_stream( FILE * input ) {
|
||||
struct cons_pointer pointer = allocate_cell( READTAG );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
cell->payload.stream.stream = input;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cell which points to a stream open for writeing.
|
||||
* @param output the C stream to wrap.
|
||||
*/
|
||||
struct cons_pointer make_write_stream( FILE * output ) {
|
||||
struct cons_pointer pointer = allocate_cell( WRITETAG );
|
||||
struct cons_space_object *cell = &pointer2cell( pointer );
|
||||
|
||||
cell->payload.stream.stream = output;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a lisp string representation of this old skool ASCII string.
|
||||
*/
|
||||
struct cons_pointer c_string_to_lisp_string( char *string ) {
|
||||
struct cons_pointer result = NIL;
|
||||
|
||||
for ( int i = strlen( string ); i > 0; i-- ) {
|
||||
result = make_string( ( wint_t ) string[i - 1], result );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a lisp symbol representation of this old skool ASCII string.
|
||||
*/
|
||||
struct cons_pointer c_string_to_lisp_symbol( char *symbol ) {
|
||||
struct cons_pointer result = NIL;
|
||||
|
||||
for ( int i = strlen( symbol ); i > 0; i-- ) {
|
||||
result = make_symbol( ( wint_t ) symbol[i - 1], result );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
582
src/memory/consspaceobject.h
Normal file
582
src/memory/consspaceobject.h
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
/**
|
||||
* consspaceobject.h
|
||||
*
|
||||
* Declarations common to all cons space objects.
|
||||
*
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
/*
|
||||
* wide characters
|
||||
*/
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#ifndef __consspaceobject_h
|
||||
#define __consspaceobject_h
|
||||
|
||||
/**
|
||||
* The length of a tag, in bytes.
|
||||
*/
|
||||
#define TAGLENGTH 4
|
||||
|
||||
/**
|
||||
* tag values, all of which must be 4 bytes. Must not collide with vector space tag values
|
||||
*/
|
||||
/**
|
||||
* An ordinary cons cell: 1397641027
|
||||
*/
|
||||
#define CONSTAG "CONS"
|
||||
#define CONSTV 1397641027
|
||||
|
||||
/**
|
||||
* An exception.
|
||||
*/
|
||||
#define EXCEPTIONTAG "EXEP"
|
||||
/* TODO: this is wrong */
|
||||
#define EXCEPTIONTV 1346721861
|
||||
|
||||
/**
|
||||
* An unallocated cell on the free list - should never be encountered by a Lisp
|
||||
* function. 1162170950
|
||||
*/
|
||||
#define FREETAG "FREE"
|
||||
#define FREETV 1162170950
|
||||
|
||||
/**
|
||||
* An ordinary Lisp function - one whose arguments are pre-evaluated and passed as
|
||||
* a stack frame. 1129207110
|
||||
*/
|
||||
#define FUNCTIONTAG "FUNC"
|
||||
#define FUNCTIONTV 1129207110
|
||||
/**
|
||||
* An integer number. 1381256777
|
||||
*/
|
||||
#define INTEGERTAG "INTR"
|
||||
#define INTEGERTV 1381256777
|
||||
|
||||
/**
|
||||
* A lambda cell.
|
||||
*/
|
||||
#define LAMBDATAG "LMDA"
|
||||
#define LAMBDATV 1094995276
|
||||
|
||||
/**
|
||||
* The special cons cell at address {0,0} whose car and cdr both point to itself.
|
||||
* 541870414
|
||||
*/
|
||||
#define NILTAG "NIL "
|
||||
#define NILTV 541870414
|
||||
|
||||
/**
|
||||
* An nlambda cell.
|
||||
*/
|
||||
#define NLAMBDATAG "NLMD"
|
||||
#define NLAMBDATV 1145916494
|
||||
|
||||
/**
|
||||
* An open read stream.
|
||||
*/
|
||||
#define READTAG "READ"
|
||||
#define READTV 1145128274
|
||||
|
||||
/**
|
||||
* A real number.
|
||||
*/
|
||||
#define REALTAG "REAL"
|
||||
#define REALTV 1279346002
|
||||
|
||||
/**
|
||||
* A ratio.
|
||||
*/
|
||||
#define RATIOTAG "RTIO"
|
||||
#define RATIOTV 1330205778
|
||||
|
||||
/**
|
||||
* A special form - one whose arguments are not pre-evaluated but passed as a
|
||||
* s-expression. 1296453715
|
||||
*/
|
||||
#define SPECIALTAG "SPFM"
|
||||
#define SPECIALTV 1296453715
|
||||
|
||||
/**
|
||||
* A string of characters, organised as a linked list. 1196577875
|
||||
*/
|
||||
#define STRINGTAG "STRG"
|
||||
#define STRINGTV 1196577875
|
||||
|
||||
/**
|
||||
* A symbol is just like a string except not self-evaluating. 1112365395
|
||||
*/
|
||||
#define SYMBOLTAG "SYMB"
|
||||
#define SYMBOLTV 1112365395
|
||||
|
||||
/**
|
||||
* The special cons cell at address {0,1} which is canonically different from NIL.
|
||||
* 1163219540
|
||||
*/
|
||||
#define TRUETAG "TRUE"
|
||||
#define TRUETV 1163219540
|
||||
|
||||
/**
|
||||
* A pointer to an object in vector space.
|
||||
*/
|
||||
#define VECTORPOINTTAG "VECP"
|
||||
#define VECTORPOINTTV 0
|
||||
/**
|
||||
* An open write stream.
|
||||
*/
|
||||
#define WRITETAG "WRIT"
|
||||
#define WRITETV 1414091351
|
||||
|
||||
/**
|
||||
* a cons pointer which points to the special NIL cell
|
||||
*/
|
||||
#define NIL (struct cons_pointer){ 0, 0}
|
||||
|
||||
/**
|
||||
* a cons pointer which points to the special T cell
|
||||
*/
|
||||
#define TRUE (struct cons_pointer){ 0, 1}
|
||||
|
||||
/**
|
||||
* the maximum possible value of a reference count
|
||||
*/
|
||||
#define MAXREFERENCE 4294967295
|
||||
|
||||
/**
|
||||
* a macro to convert a tag into a number
|
||||
*/
|
||||
#define tag2uint(tag) ((uint32_t)*tag)
|
||||
|
||||
#define pointer2cell(pointer) ((conspages[pointer.page]->cell[pointer.offset]))
|
||||
|
||||
/**
|
||||
* true if conspointer points to the special cell NIL, else false
|
||||
* (there should only be one of these so it's slightly redundant).
|
||||
*/
|
||||
#define nilp(conspoint) (check_tag(conspoint,NILTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a cons cell, else false
|
||||
*/
|
||||
#define consp(conspoint) (check_tag(conspoint,CONSTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to an exception, else false
|
||||
*/
|
||||
#define exceptionp(conspoint) (check_tag(conspoint,EXCEPTIONTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a function cell, else false
|
||||
*/
|
||||
#define functionp(conspoint) (check_tag(conspoint,FUNCTIONTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a special Lambda cell, else false
|
||||
*/
|
||||
#define lambdap(conspoint) (check_tag(conspoint,LAMBDATAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a special form cell, else false
|
||||
*/
|
||||
#define specialp(conspoint) (check_tag(conspoint,SPECIALTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a string cell, else false
|
||||
*/
|
||||
#define stringp(conspoint) (check_tag(conspoint,STRINGTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a symbol cell, else false
|
||||
*/
|
||||
#define symbolp(conspoint) (check_tag(conspoint,SYMBOLTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to an integer cell, else false
|
||||
*/
|
||||
#define integerp(conspoint) (check_tag(conspoint,INTEGERTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a rational number cell, else false
|
||||
*/
|
||||
#define ratiop(conspoint) (check_tag(conspoint,RATIOTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a read stream cell, else false
|
||||
*/
|
||||
#define readp(conspoint) (check_tag(conspoint,READTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a real number cell, else false
|
||||
*/
|
||||
#define realp(conspoint) (check_tag(conspoint,REALTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to some sort of a number cell,
|
||||
* else false
|
||||
*/
|
||||
#define numberp(conspoint) (check_tag(conspoint,INTEGERTAG)||check_tag(conspoint,RATIOTAG)||heck_tag(conspoint,REALTAG))
|
||||
|
||||
/**
|
||||
* true if thr conspointer points to a vector pointer.
|
||||
*/
|
||||
#define vectorpointp(conspoint) (check_tag(conspoint,VECTORPOINTTAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a write stream cell, else false.
|
||||
*/
|
||||
#define writep(conspoint) (check_tag(conspoint,WRITETAG))
|
||||
|
||||
/**
|
||||
* true if conspointer points to a true cell, else false
|
||||
* (there should only be one of these so it's slightly redundant).
|
||||
* Also note that anything that is not NIL is truthy.
|
||||
*/
|
||||
#define tp(conspoint) (checktag(conspoint,TRUETAG))
|
||||
|
||||
/**
|
||||
* true if conspoint points to something that is truthy, i.e.
|
||||
* anything but NIL.
|
||||
*/
|
||||
#define truep(conspoint) (!checktag(conspoint,NILTAG))
|
||||
|
||||
/**
|
||||
* An indirect pointer to a cons cell
|
||||
*/
|
||||
struct cons_pointer {
|
||||
uint32_t page; /* the index of the page on which this cell
|
||||
* resides */
|
||||
uint32_t offset; /* the index of the cell within the page */
|
||||
};
|
||||
|
||||
/*
|
||||
* number of arguments stored in a stack frame
|
||||
*/
|
||||
#define args_in_frame 8
|
||||
|
||||
/**
|
||||
* A stack frame. Yes, I know it isn't a cons-space object, but it's defined
|
||||
* here to avoid circularity. TODO: refactor.
|
||||
*/
|
||||
struct stack_frame {
|
||||
struct stack_frame *previous; /* the previous frame */
|
||||
struct cons_pointer arg[args_in_frame];
|
||||
/*
|
||||
* first 8 arument bindings
|
||||
*/
|
||||
struct cons_pointer more; /* list of any further argument bindings */
|
||||
struct cons_pointer function; /* the function to be called */
|
||||
};
|
||||
|
||||
/**
|
||||
* payload of a cons cell.
|
||||
*/
|
||||
struct cons_payload {
|
||||
struct cons_pointer car;
|
||||
struct cons_pointer cdr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Payload of an exception.
|
||||
* Message should be a Lisp string; frame should be a pointer to an (unfreed) stack frame.
|
||||
*/
|
||||
struct exception_payload {
|
||||
struct cons_pointer message;
|
||||
struct stack_frame *frame;
|
||||
};
|
||||
|
||||
/**
|
||||
* Payload of a function cell.
|
||||
* source points to the source from which the function was compiled, or NIL
|
||||
* if it is a primitive.
|
||||
* executable points to a function which takes a pointer to a stack frame
|
||||
* (representing its stack frame) and a cons pointer (representing its
|
||||
* environment) as arguments and returns a cons pointer (representing its
|
||||
* result).
|
||||
*/
|
||||
struct function_payload {
|
||||
struct cons_pointer source;
|
||||
struct cons_pointer ( *executable ) ( struct stack_frame *,
|
||||
struct cons_pointer );
|
||||
};
|
||||
|
||||
/**
|
||||
* payload of a free cell. For the time being identical to a cons cell,
|
||||
* but it may not be so in future.
|
||||
*/
|
||||
struct free_payload {
|
||||
struct cons_pointer car;
|
||||
struct cons_pointer cdr;
|
||||
};
|
||||
|
||||
/**
|
||||
* payload of an integer cell. For the time being just a signed integer;
|
||||
* later might be a signed 128 bit integer, or might have some flag to point to an
|
||||
* optional bignum object.
|
||||
*/
|
||||
struct integer_payload {
|
||||
long int value;
|
||||
};
|
||||
|
||||
/**
|
||||
* payload for lambda and nlambda cells
|
||||
*/
|
||||
struct lambda_payload {
|
||||
struct cons_pointer args;
|
||||
struct cons_pointer body;
|
||||
};
|
||||
|
||||
/**
|
||||
* payload for ratio cells. Both dividend and divisor must point to integer (or, later, bignum) cells.
|
||||
*/
|
||||
struct ratio_payload {
|
||||
struct cons_pointer dividend;
|
||||
struct cons_pointer divisor;
|
||||
};
|
||||
|
||||
/**
|
||||
* payload for a real number cell. Internals of this liable to change to give 128 bits
|
||||
* precision, but I'm not sure of the detail.
|
||||
*/
|
||||
struct real_payload {
|
||||
long double value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Payload of a special form cell.
|
||||
* source points to the source from which the function was compiled, or NIL
|
||||
* if it is a primitive.
|
||||
* executable points to a function which takes a cons pointer (representing
|
||||
* its argument list) and a cons pointer (representing its environment) and a
|
||||
* stack frame (representing the previous stack frame) as arguments and returns
|
||||
* a cons pointer (representing its result).
|
||||
*
|
||||
* NOTE that this means that special forms do not appear on the lisp stack,
|
||||
* which may be confusing. TODO: think about this.
|
||||
*/
|
||||
struct special_payload {
|
||||
struct cons_pointer source;
|
||||
struct cons_pointer ( *executable ) ( struct stack_frame *,
|
||||
struct cons_pointer );
|
||||
};
|
||||
|
||||
/**
|
||||
* payload of a read or write stream cell.
|
||||
*/
|
||||
struct stream_payload {
|
||||
FILE *stream;
|
||||
};
|
||||
|
||||
/**
|
||||
* payload of a string cell. At least at first, only one UTF character will
|
||||
* be stored in each cell. The doctrine that 'a symbol is just a string'
|
||||
* didn't work; however, the payload of a symbol cell is identical to the
|
||||
* payload of a string cell.
|
||||
*/
|
||||
struct string_payload {
|
||||
wint_t character; /* the actual character stored in this cell */
|
||||
uint32_t padding; /* unused padding to word-align the cdr */
|
||||
struct cons_pointer cdr;
|
||||
};
|
||||
|
||||
/**
|
||||
* payload of a vector pointer cell.
|
||||
*/
|
||||
struct vectorp_payload {
|
||||
union {
|
||||
char bytes[TAGLENGTH]; /* the tag (type) of the
|
||||
* vector-space object this cell
|
||||
* points to, considered as bytes.
|
||||
* NOTE that the vector space object
|
||||
* should itself have the identical
|
||||
* tag. */
|
||||
uint32_t value; /* the tag considered as a number */
|
||||
} tag;
|
||||
uint64_t address; /* the address of the actual vector space
|
||||
* object (TODO: will change when I actually
|
||||
* implement vector space) */
|
||||
};
|
||||
|
||||
/**
|
||||
* an object in cons space.
|
||||
*/
|
||||
struct cons_space_object {
|
||||
union {
|
||||
char bytes[TAGLENGTH]; /* the tag (type) of this cell,
|
||||
* considered as bytes */
|
||||
uint32_t value; /* the tag considered as a number */
|
||||
} tag;
|
||||
uint32_t count; /* the count of the number of references to
|
||||
* this cell */
|
||||
struct cons_pointer access; /* cons pointer to the access control list of
|
||||
* this cell */
|
||||
union {
|
||||
/*
|
||||
* if tag == CONSTAG
|
||||
*/
|
||||
struct cons_payload cons;
|
||||
/*
|
||||
* if tag == EXCEPTIONTAG
|
||||
*/
|
||||
struct exception_payload exception;
|
||||
/*
|
||||
* if tag == FREETAG
|
||||
*/
|
||||
struct free_payload free;
|
||||
/*
|
||||
* if tag == FUNCTIONTAG
|
||||
*/
|
||||
struct function_payload function;
|
||||
/*
|
||||
* if tag == INTEGERTAG
|
||||
*/
|
||||
struct integer_payload integer;
|
||||
/*
|
||||
* if tag == LAMBDATAG or NLAMBDATAG
|
||||
*/
|
||||
struct lambda_payload lambda;
|
||||
/*
|
||||
* if tag == NILTAG; we'll treat the special cell NIL as just a cons
|
||||
*/
|
||||
struct cons_payload nil;
|
||||
/*
|
||||
* if tag == RATIOTAG
|
||||
*/
|
||||
struct ratio_payload ratio;
|
||||
/*
|
||||
* if tag == READTAG || tag == WRITETAG
|
||||
*/
|
||||
struct stream_payload stream;
|
||||
/*
|
||||
* if tag == REALTAG
|
||||
*/
|
||||
struct real_payload real;
|
||||
/*
|
||||
* if tag == SPECIALTAG
|
||||
*/
|
||||
struct special_payload special;
|
||||
/*
|
||||
* if tag == STRINGTAG || tag == SYMBOLTAG
|
||||
*/
|
||||
struct string_payload string;
|
||||
/*
|
||||
* if tag == TRUETAG; we'll treat the special cell T as just a cons
|
||||
*/
|
||||
struct cons_payload t;
|
||||
/*
|
||||
* if tag == VECTORPTAG
|
||||
*/
|
||||
struct vectorp_payload vectorp;
|
||||
} payload;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that the tag on the cell at this pointer is this tag
|
||||
*/
|
||||
int check_tag( struct cons_pointer pointer, char *tag );
|
||||
|
||||
/**
|
||||
* increment the reference count of the object at this cons pointer
|
||||
*/
|
||||
void inc_ref( struct cons_pointer pointer );
|
||||
|
||||
/**
|
||||
* decrement the reference count of the object at this cons pointer
|
||||
*/
|
||||
void dec_ref( struct cons_pointer pointer );
|
||||
|
||||
/**
|
||||
* dump the object at this cons_pointer to this output stream.
|
||||
*/
|
||||
void dump_object( FILE * output, struct cons_pointer pointer );
|
||||
|
||||
struct cons_pointer make_cons( struct cons_pointer car,
|
||||
struct cons_pointer cdr );
|
||||
/**
|
||||
* Construct an exception cell.
|
||||
* @param message should be a lisp string describing the problem, but actually any cons pointer will do;
|
||||
* @param frame should be the frame in which the exception occurred.
|
||||
*/
|
||||
struct cons_pointer make_exception( struct cons_pointer message,
|
||||
struct stack_frame *frame );
|
||||
|
||||
/**
|
||||
* Construct a cell which points to an executable Lisp special form.
|
||||
*/
|
||||
struct cons_pointer make_function( struct cons_pointer src,
|
||||
struct cons_pointer ( *executable )
|
||||
( struct stack_frame *,
|
||||
struct cons_pointer ) );
|
||||
|
||||
/**
|
||||
* Construct a lambda (interpretable source) cell
|
||||
*/
|
||||
struct cons_pointer make_lambda( 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 make_nlambda( struct cons_pointer args,
|
||||
struct cons_pointer body );
|
||||
|
||||
/**
|
||||
* Construct a ratio frame from these two pointers, expected to be integers
|
||||
* or (later) bignums, in the context of this stack_frame.
|
||||
*/
|
||||
struct cons_pointer make_ratio( struct stack_frame *frame,
|
||||
struct cons_pointer dividend,
|
||||
struct cons_pointer divisor );
|
||||
|
||||
/**
|
||||
* Construct a cell which points to an executable Lisp special form.
|
||||
*/
|
||||
struct cons_pointer make_special( struct cons_pointer src,
|
||||
struct cons_pointer ( *executable )
|
||||
( struct stack_frame *,
|
||||
struct cons_pointer ) );
|
||||
|
||||
/**
|
||||
* Construct a string from this character and this tail. A string is
|
||||
* implemented as a flat list of cells each of which has one character and a
|
||||
* pointer to the next; in the last cell the pointer to next is NIL.
|
||||
*/
|
||||
struct cons_pointer make_string( wint_t c, struct cons_pointer tail );
|
||||
|
||||
/**
|
||||
* Construct a symbol from this character and this tail. A symbol is identical
|
||||
* to a string except for having a different tag.
|
||||
*/
|
||||
struct cons_pointer make_symbol( wint_t c, struct cons_pointer tail );
|
||||
|
||||
/**
|
||||
* Construct a cell which points to a stream open for reading.
|
||||
* @param input the C stream to wrap.
|
||||
*/
|
||||
struct cons_pointer make_read_stream( FILE * input );
|
||||
|
||||
/**
|
||||
* Construct a cell which points to a stream open for writeing.
|
||||
* @param output the C stream to wrap.
|
||||
*/
|
||||
struct cons_pointer make_write_stream( FILE * output );
|
||||
|
||||
|
||||
/**
|
||||
* Return a lisp string representation of this old skool ASCII string.
|
||||
*/
|
||||
struct cons_pointer c_string_to_lisp_string( char *string );
|
||||
|
||||
/**
|
||||
* Return a lisp symbol representation of this old skool ASCII string.
|
||||
*/
|
||||
struct cons_pointer c_string_to_lisp_symbol( char *symbol );
|
||||
|
||||
#endif
|
||||
203
src/memory/stack.c
Normal file
203
src/memory/stack.c
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* stack.c
|
||||
*
|
||||
* The Lisp evaluation stack.
|
||||
*
|
||||
* Stack frames could be implemented in cons space; indeed, the stack
|
||||
* could simply be an assoc list consed onto the front of the environment.
|
||||
* But such a stack would be costly to search. The design sketched here,
|
||||
* with stack frames as special objects, SHOULD be substantially more
|
||||
* efficient, but does imply we need to generalise the idea of cons pages
|
||||
* with freelists to a more general 'equal sized object pages', so that
|
||||
* allocating/freeing stack frames can be more efficient.
|
||||
*
|
||||
* Stack frames are not yet a first class object; they have no VECP pointer
|
||||
* in cons space.
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "consspaceobject.h"
|
||||
#include "conspage.h"
|
||||
#include "lispops.h"
|
||||
#include "print.h"
|
||||
#include "stack.h"
|
||||
|
||||
/**
|
||||
* Make an empty stack frame, and return it.
|
||||
* @param previous the current top-of-stack;
|
||||
* @param env the environment in which evaluation happens.
|
||||
* @return the new frame.
|
||||
*/
|
||||
struct stack_frame *make_empty_frame( struct stack_frame *previous,
|
||||
struct cons_pointer env ) {
|
||||
struct stack_frame *result = malloc( sizeof( struct stack_frame ) );
|
||||
/*
|
||||
* TODO: later, pop a frame off a free-list of stack frames
|
||||
*/
|
||||
|
||||
result->previous = previous;
|
||||
|
||||
/*
|
||||
* clearing the frame with memset would probably be slightly quicker, but
|
||||
* this is clear.
|
||||
*/
|
||||
result->more = NIL;
|
||||
result->function = NIL;
|
||||
|
||||
for ( int i = 0; i < args_in_frame; i++ ) {
|
||||
set_reg( result, i, NIL );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a new stack frame with its previous pointer set to this value,
|
||||
* its arguments set up from these args, evaluated in this env.
|
||||
* @param previous the current top-of-stack;
|
||||
* @args the arguments to load into this frame;
|
||||
* @param env the environment in which evaluation happens.
|
||||
* @return the new frame.
|
||||
*/
|
||||
struct stack_frame *make_stack_frame( struct stack_frame *previous,
|
||||
struct cons_pointer args,
|
||||
struct cons_pointer env,
|
||||
struct cons_pointer *exception ) {
|
||||
struct stack_frame *result = make_empty_frame( previous, env );
|
||||
|
||||
for ( int i = 0; i < args_in_frame && consp( args ); i++ ) {
|
||||
/* iterate down the arg list filling in the arg slots in the
|
||||
* frame. When there are no more slots, if there are still args,
|
||||
* stash them on more */
|
||||
struct cons_space_object cell = pointer2cell( args );
|
||||
|
||||
/*
|
||||
* TODO: if we were running on real massively parallel hardware,
|
||||
* each arg except the first should be handed off to another
|
||||
* processor to be evaled in parallel; but see notes here:
|
||||
* https://github.com/simon-brooke/post-scarcity/wiki/parallelism
|
||||
*/
|
||||
struct stack_frame *arg_frame = make_empty_frame( result, env );
|
||||
set_reg( arg_frame, 0, cell.payload.cons.car );
|
||||
|
||||
struct cons_pointer val = lisp_eval( arg_frame, env );
|
||||
if ( exceptionp( val ) ) {
|
||||
exception = &val;
|
||||
break;
|
||||
} else {
|
||||
set_reg( result, i, val );
|
||||
}
|
||||
|
||||
free_stack_frame( arg_frame );
|
||||
|
||||
args = cell.payload.cons.cdr;
|
||||
}
|
||||
if ( consp( args ) ) {
|
||||
/* if we still have args, eval them and stick the values on `more` */
|
||||
struct cons_pointer more = eval_forms( previous, args, env );
|
||||
result->more = more;
|
||||
inc_ref( more );
|
||||
}
|
||||
|
||||
dump_frame( stderr, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A 'special' frame is exactly like a normal stack frame except that the
|
||||
* arguments are unevaluated.
|
||||
* @param previous the previous stack frame;
|
||||
* @param args a list of the arguments to be stored in this stack frame;
|
||||
* @param env the execution environment;
|
||||
* @return a new special frame.
|
||||
*/
|
||||
struct stack_frame *make_special_frame( struct stack_frame *previous,
|
||||
struct cons_pointer args,
|
||||
struct cons_pointer env ) {
|
||||
struct stack_frame *result = make_empty_frame( previous, env );
|
||||
|
||||
for ( int i = 0; i < args_in_frame && !nilp( args ); i++ ) {
|
||||
/* iterate down the arg list filling in the arg slots in the
|
||||
* frame. When there are no more slots, if there are still args,
|
||||
* stash them on more */
|
||||
struct cons_space_object cell = pointer2cell( args );
|
||||
|
||||
set_reg( result, i, cell.payload.cons.car );
|
||||
|
||||
args = cell.payload.cons.cdr;
|
||||
}
|
||||
if ( consp( args ) ) {
|
||||
result->more = args;
|
||||
inc_ref( args );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free this stack frame.
|
||||
*/
|
||||
void free_stack_frame( struct stack_frame *frame ) {
|
||||
/*
|
||||
* TODO: later, push it back on the stack-frame freelist
|
||||
*/
|
||||
for ( int i = 0; i < args_in_frame; i++ ) {
|
||||
dec_ref( frame->arg[i] );
|
||||
}
|
||||
if ( !nilp( frame->more ) ) {
|
||||
dec_ref( frame->more );
|
||||
}
|
||||
|
||||
free( frame );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump a stackframe to this stream for debugging
|
||||
* @param output the stream
|
||||
* @param frame the frame
|
||||
*/
|
||||
void dump_frame( FILE * output, struct stack_frame *frame ) {
|
||||
fputws( L"Dumping stack frame\n", output );
|
||||
for ( int arg = 0; arg < args_in_frame; arg++ ) {
|
||||
struct cons_space_object cell = pointer2cell( frame->arg[arg] );
|
||||
|
||||
fwprintf( output, L"Arg %d:\t%c%c%c%c\tcount: %10u\tvalue: ", arg,
|
||||
cell.tag.bytes[0],
|
||||
cell.tag.bytes[1], cell.tag.bytes[2], cell.tag.bytes[3],
|
||||
cell.count );
|
||||
|
||||
print( output, frame->arg[arg] );
|
||||
fputws( L"\n", output );
|
||||
}
|
||||
fputws( L"More: \t", output );
|
||||
print( output, frame->more );
|
||||
fputws( L"\n", output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch a pointer to the value of the local variable at this index.
|
||||
*/
|
||||
struct cons_pointer fetch_arg( struct stack_frame *frame, unsigned int index ) {
|
||||
struct cons_pointer result = NIL;
|
||||
|
||||
if ( index < args_in_frame ) {
|
||||
result = frame->arg[index];
|
||||
} else {
|
||||
struct cons_pointer p = frame->more;
|
||||
|
||||
for ( int i = args_in_frame; i < index; i++ ) {
|
||||
p = pointer2cell( p ).payload.cons.cdr;
|
||||
}
|
||||
|
||||
result = pointer2cell( p ).payload.cons.car;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
75
src/memory/stack.h
Normal file
75
src/memory/stack.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* stack.h
|
||||
*
|
||||
* The Lisp evaluation stack.
|
||||
*
|
||||
* Stack frames could be implemented in cons space; indeed, the stack
|
||||
* could simply be an assoc list consed onto the front of the environment.
|
||||
* But such a stack would be costly to search. The design sketched here,
|
||||
* with stack frames as special objects, SHOULD be substantially more
|
||||
* efficient, but does imply we need to generalise the idea of cons pages
|
||||
* with freelists to a more general 'equal sized object pages', so that
|
||||
* allocating/freeing stack frames can be more efficient.
|
||||
*
|
||||
* Stack frames are not yet a first class object; they have no VECP pointer
|
||||
* in cons space.
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include "consspaceobject.h"
|
||||
#include "conspage.h"
|
||||
|
||||
#ifndef __stack_h
|
||||
#define __stack_h
|
||||
|
||||
/**
|
||||
* set a register in a stack frame. Alwaye use this macro to do so,
|
||||
• because that way we can be sure the inc_ref happens!
|
||||
*/
|
||||
#define set_reg(frame,register,value)frame->arg[register]=value; inc_ref(value)
|
||||
|
||||
|
||||
/**
|
||||
* Make an empty stack frame, and return it.
|
||||
* @param previous the current top-of-stack;
|
||||
* @param env the environment in which evaluation happens.
|
||||
* @return the new frame.
|
||||
*/
|
||||
struct stack_frame *make_empty_frame( struct stack_frame *previous,
|
||||
struct cons_pointer env );
|
||||
|
||||
struct stack_frame *make_stack_frame( struct stack_frame *previous,
|
||||
struct cons_pointer args,
|
||||
struct cons_pointer env,
|
||||
struct cons_pointer *exception );
|
||||
void free_stack_frame( struct stack_frame *frame );
|
||||
|
||||
/**
|
||||
* Dump a stackframe to this stream for debugging
|
||||
* @param output the stream
|
||||
* @param frame the frame
|
||||
*/
|
||||
void dump_frame( FILE * output, struct stack_frame *frame );
|
||||
|
||||
struct cons_pointer fetch_arg( struct stack_frame *frame, unsigned int n );
|
||||
|
||||
/**
|
||||
* A 'special' frame is exactly like a normal stack frame except that the
|
||||
* arguments are unevaluated.
|
||||
* @param previous the previous stack frame;
|
||||
* @param args a list of the arguments to be stored in this stack frame;
|
||||
* @param env the execution environment;
|
||||
* @return a new special frame.
|
||||
*/
|
||||
struct stack_frame *make_special_frame( struct stack_frame *previous,
|
||||
struct cons_pointer args,
|
||||
struct cons_pointer env );
|
||||
|
||||
/*
|
||||
* struct stack_frame is defined in consspaceobject.h to break circularity
|
||||
* TODO: refactor.
|
||||
*/
|
||||
|
||||
#endif
|
||||
69
src/memory/vectorspace.c
Normal file
69
src/memory/vectorspace.c
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* vectorspace.c
|
||||
*
|
||||
* Structures common to all vector space objects.
|
||||
*
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
/*
|
||||
* wide characters
|
||||
*/
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "conspage.h"
|
||||
#include "consspaceobject.h"
|
||||
#include "vectorspace.h"
|
||||
|
||||
|
||||
/**
|
||||
* make a cons-space object which points to the vector space object
|
||||
* with this `tag` at this `address`.
|
||||
* NOTE that `tag` should be the vector-space tag of the particular type of
|
||||
* vector-space object, NOT `VECTORPOINTTAG`.
|
||||
*/
|
||||
struct cons_pointer make_vec_pointer( char *tag, uint64_t address ) {
|
||||
struct cons_pointer pointer = allocate_cell( VECTORPOINTTAG );
|
||||
struct cons_space_object cell = pointer2cell( pointer );
|
||||
|
||||
strncpy( &cell.payload.vectorp.tag.bytes[0], tag, 4 );
|
||||
cell.payload.vectorp.address = address;
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* allocate a vector space object with this `payload_size` and `tag`,
|
||||
* and return a `cons_pointer` which points to an object whigh points to it.
|
||||
* NOTE that `tag` should be the vector-space tag of the particular type of
|
||||
* vector-space object, NOT `VECTORPOINTTAG`.
|
||||
*/
|
||||
struct cons_pointer make_vso( char *tag, long int payload_size ) {
|
||||
struct cons_pointer result = NIL;
|
||||
long int total_size = sizeof( struct vector_space_header ) + payload_size;
|
||||
|
||||
struct vector_space_header *vso = malloc( total_size );
|
||||
|
||||
if ( vso != NULL ) {
|
||||
strncpy( &vso->tag.bytes[0], tag, TAGLENGTH );
|
||||
vso->vecp = make_vec_pointer( tag, ( uint64_t ) vso );
|
||||
vso->size = payload_size;
|
||||
|
||||
#ifdef DEBUG
|
||||
fwprintf( stderr,
|
||||
L"Allocated vector-space object of type %s, total size %ld, payload size %ld\n",
|
||||
tag, total_size, payload_size );
|
||||
#endif
|
||||
|
||||
result = vso->vecp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
68
src/memory/vectorspace.h
Normal file
68
src/memory/vectorspace.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* vectorspace.h
|
||||
*
|
||||
* Declarations common to all vector space objects.
|
||||
*
|
||||
*
|
||||
* (c) 2017 Simon Brooke <simon@journeyman.cc>
|
||||
* Licensed under GPL version 2.0, or, at your option, any later version.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
/*
|
||||
* wide characters
|
||||
*/
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "consspaceobject.h"
|
||||
|
||||
#ifndef __vectorspace_h
|
||||
#define __vectorspace_h
|
||||
|
||||
/*
|
||||
* part of the implementation structure of a namespace.
|
||||
*/
|
||||
#define HASHTAG "HASH"
|
||||
#define HASHTV 0
|
||||
|
||||
/*
|
||||
* a namespace (i.e. a binding of names to values, implemented as a hashmap)
|
||||
*/
|
||||
#define NAMESPACETAG "NMSP"
|
||||
#define NAMESPACETV 0
|
||||
|
||||
/*
|
||||
* a vector of cons pointers.
|
||||
*/
|
||||
#define VECTORTAG "VECT"
|
||||
#define VECTORTV 0
|
||||
|
||||
#define pointer_to_vso(pointer)(vectorpointp(pointer)? pointer2cell(pointer).payload.vectorp.address : 0)
|
||||
|
||||
struct cons_pointer make_vso( char *tag, long int payload_size );
|
||||
|
||||
struct vector_space_header {
|
||||
union {
|
||||
char bytes[TAGLENGTH]; /* the tag (type) of the
|
||||
* vector-space object this cell
|
||||
* points to, considered as bytes.
|
||||
* NOTE that the vector space object
|
||||
* should itself have the identical
|
||||
* tag. */
|
||||
uint32_t value; /* the tag considered as a number */
|
||||
} tag;
|
||||
struct cons_pointer vecp; /* back pointer to the vector pointer
|
||||
* which uniquely points to this vso */
|
||||
uint64_t size; /* the size of my payload, in bytes */
|
||||
char mark; /* mark bit for marking/sweeping the
|
||||
* heap (not in this version) */
|
||||
char payload; /* we'll malloc `size` bytes for payload,
|
||||
* `payload` is just the first of these.
|
||||
* TODO: this is almost certainly not
|
||||
* idiomatic C. */
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue