/*
* Copyright (c) 2001-2007 James Bailie.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The name of James Bailie may not be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "lisp.h"
#ifdef DEBUG
#include "intrinsics.c"
#endif
void sigchld_handler( int signo )
{
pid_t pid;
if ( zombies )
return;
while(( pid = waitpid( -1, NULL, WNOHANG )) > 0 )
if ( pid == child_pid )
child_pid = -1;
}
void sigalrm_handler( int signo )
{
sigalrm = 1;
}
void print_err( enum err_types err, char *func, int arg, enum err_types type)
{
fprintf( stderr, error_messages[ err - 13 ], func, arg,
( type < 0 ? "" : err_strings[ type ] ));
}
void *memory( int size )
{
void *ptr;
if ( size == 0 )
return NULL;
if (( ptr = malloc( size )) == NULL )
{
fprintf( stderr, "memory: malloc: %s.\n", strerror( errno ));
exit( 1 );
}
return ptr;
}
char *str_dup( char *str, int len )
{
char *ptr;
ptr = ( char *)memory( len + 1 );
bcopy( str, ptr, len );
ptr[ len ] = '\0';
return ptr;
}
#ifndef DEBUG
struct object *make_object()
{
if ( reclaimed_objects->used )
return stack_pop( reclaimed_objects );
if ( object_pool_free == 0 )
{
stack_push( object_pool_stack, ( void *)object_pool );
#ifdef DEBUG
fprintf( stderr, "Allocating object pool %d.\n", object_pool_stack->used );
#endif
object_pool = ( struct object *)memory( sizeof( struct object ) * POOL_INC );
object_pool_ptr = object_pool;
object_pool_free = POOL_INC;
}
--object_pool_free;
object_pool_ptr->data.head = object_pool_ptr->next = NULL;
object_pool_ptr->flags = 0;
return object_pool_ptr++;
}
struct atom *make_atom()
{
if ( reclaimed_atoms->used )
return ( struct atom *)stack_pop( reclaimed_atoms );
if ( atom_pool_free == 0 )
{
stack_push( atom_pool_stack, ( void *)atom_pool );
#ifdef DEBUG
fprintf( stderr, "Allocating atom pool %d.\n", atom_pool_stack->used );
#endif
atom_pool = ( struct atom *)memory( sizeof( struct atom ) * POOL_INC );
atom_pool_ptr = atom_pool;
atom_pool_free = POOL_INC;
}
--atom_pool_free;
atom_pool_ptr->syntax = NULL;
atom_pool_ptr->len = 0;
atom_pool_ptr->id = 0;
atom_pool_ptr->flags = 0;
atom_pool_ptr->data.hash = NULL;
return atom_pool_ptr++;
}
#endif
struct stack *get_hash_keys( struct hash_elt **table )
{
struct stack *stk;
struct hash_elt *ptr;
int i;
stk = make_stack();
for( i = 0; i < HASH_SIZE; ++i )
{
ptr = table[ i ];
while( ptr != NULL )
{
stack_push( stk, ( struct object *)ptr->binding );
ptr = ptr->next;
}
}
return stk;
}
struct stack *get_hash_values( struct hash_elt **table )
{
struct stack *stk;
struct hash_elt *ptr;
int i;
stk = make_stack();
for( i = 0; i < HASH_SIZE; ++i )
{
ptr = table[ i ];
while( ptr != NULL )
{
stack_push( stk, ( struct object *)ptr->element );
ptr = ptr->next;
}
}
return stk;
}
void insert_elt( struct hash_elt **hash, int id, struct object *elt )
{
int key;
struct hash_elt *ptr = NULL, *ptr2 = NULL;
key = ( id + HASH_SIZE ) % HASH_SIZE;
if ( hash[ key ] == NULL )
{
hash[ key ] = memory( sizeof( struct hash_elt ));
hash[ key ]->element = elt;
hash[ key ]->next = NULL;
hash[ key ]->binding = id;
return;
}
ptr2 = hash[ key ];
for( ptr = ptr2; ptr != NULL; ptr = ptr->next )
if ( ptr->binding == id )
break;
else
ptr2 = ptr;
if ( ptr == NULL )
{
ptr2->next = ( struct hash_elt *)memory( sizeof( struct hash_elt ));
ptr = ptr2->next;
ptr->next = NULL;
}
ptr->element = elt;
ptr->binding = id;
}
void *lookup_elt( struct hash_elt **hash, int id )
{
int key;
struct hash_elt *ptr;
key = ( id + HASH_SIZE ) % HASH_SIZE;
if ( hash[ key ] == NULL )
return NULL;
ptr = hash[ key ];
do
{
if ( ptr->binding == id )
return ptr->element;
ptr = ptr->next;
}
while( ptr != NULL );
return NULL;
}
void remove_elt( struct hash_elt **hash, int id )
{
int key;
struct hash_elt *ptr, *ptr2;
key = ( id + HASH_SIZE ) % HASH_SIZE;
if ( hash[ key ] == NULL )
return;
ptr = hash[ key ];
ptr2 = NULL;
do
{
if ( ptr->binding == id )
break;
ptr2 = ptr;
ptr = ptr->next;
}
while( ptr != NULL );
if ( ptr == NULL )
return;
if ( ptr2 == NULL )
hash[ key ] = ptr->next;
else
ptr2->next = ptr->next;
free( ptr );
}
void remove_id( char *name, int len, int id )
{
int key, i;
char *nptr;
struct hash_elt *ptr, *ptr2;
for( key = 0, i = len, nptr = name; i; --i )
key += ( int )*nptr++;
key = ( abs( key ) + HASH_SIZE ) % HASH_SIZE;
if ( syntax[ key ] == NULL )
return;
ptr = syntax[ key ];
ptr2 = NULL;
do
{
if ( ptr->binding == id )
break;
ptr2 = ptr;
ptr = ptr->next;
}
while( ptr != NULL );
if ( ptr == NULL )
return;
if ( ptr2 == NULL )
syntax[ key ] = ptr->next;
else
ptr2->next = ptr->next;
free_atom( ( struct atom *)ptr->element );
free( ptr );
}
struct atom *get_id( char *name, int len, int duplicate )
{
int key, i;
char *nptr;
struct atom *atom;
struct hash_elt *ptr = NULL, *ptr2 = NULL;
for( key = 0, i = len, nptr = name; i; --i )
key += ( int )*nptr++;
key = abs( ( key + HASH_SIZE ) % HASH_SIZE );
if ( syntax[ key ] == NULL )
{
atom = make_atom();
atom->syntax = ( duplicate ? str_dup( name, len ) : name );
atom->len = len;
if ( reclaimed_ids->used )
atom->id = ( int )stack_pop( reclaimed_ids );
else if ( atom_counter == 0 )
{
fprintf( stderr, "get_id(): unique atomic ids exhausted.\n" );
exit( 1 );
}
else
atom->id = atom_counter++;
syntax[ key ] = memory( sizeof( struct hash_elt ));
syntax[ key ]->element = ( struct object *)atom;
syntax[ key ]->next = NULL;
syntax[ key ]->binding = atom->id;
insert_atom( atom->id, atom );
return atom;
}
ptr2 = syntax[ key ];
for( ptr = ptr2; ptr != NULL; ptr = ptr->next )
{
atom = ( struct atom *)ptr->element;
if ( len == atom->len )
{
char *ptr3 = name, *ptr4 = atom->syntax;
int idx = len;
while( idx-- )
if ( *ptr3++ != *ptr4++ )
break;
if ( idx < 0 )
return atom;
}
ptr2 = ptr;
}
if ( ptr == NULL )
{
ptr2->next = ( struct hash_elt *)memory( sizeof( struct hash_elt ));
ptr = ptr2->next;
ptr->next = NULL;
}
atom = make_atom();
atom->syntax = ( duplicate ? str_dup( name, len ) : name );
atom->len = len;
if ( reclaimed_ids->used )
atom->id = ( int )stack_pop( reclaimed_ids );
else if ( atom_counter == 0 )
{
fprintf( stderr, "get_id(): unique atomic ids exhausted.\n" );
exit( 1 );
}
else
atom->id = atom_counter++;
ptr->element = ( struct object *)atom;
ptr->binding = atom->id;
insert_atom( atom->id, atom );
return atom;
}
void hash_free( struct hash_elt **hash )
{
int i;
struct hash_elt **ptr, *ptr2, *ptr3;
ptr = hash;
for( i = 0; i < HASH_SIZE; ++i )
{
if ( *ptr == NULL )
{
++ptr;
continue;
}
ptr2 = *ptr;
do
{
ptr3 = ptr2->next;
free( ptr2 );
ptr2 = ptr3;
}
while( ptr2 != NULL );
++ptr;
}
}
void free_syntax()
{
struct atom *top;
int i, j;
i = atom_pool_stack->used;
while( i )
{
top = ( struct atom *)atom_pool_stack->values[ --i ];
for( j = 0; j < POOL_INC; ++j, ++top )
free_atom( top );
}
for( top = atom_pool; top < atom_pool_ptr; ++top )
free_atom( top );
}
struct stack *make_stack()
{
struct stack *a;
a = ( struct stack *)memory( sizeof( struct stack ));
a->values = memory( sizeof( void * ) * stack_inc );
a->free = stack_inc;
a->used = 0;
a->top = a->values;
return a;
}
void string_free( struct string *s )
{
free( s->str );
free( s );
}
struct string *make_string()
{
struct string *s;
s = ( struct string *)memory( sizeof( struct string ));
s->str = ( char *)memory( stack_inc + 1 );
*s->str = '\0';
s->free = stack_inc;
s->used = 0;
s->top = s->str;
return s;
}
int load( char *filename )
{
struct object *result = NULL;
int count;
int file;
file = open( filename, O_RDONLY | O_NONBLOCK | O_SHLOCK );
if ( file == -1 )
{
fprintf( stderr, "load: %s: %s\n", filename, strerror( errno ));
return 1;
}
for( count = 1; ; ++count )
{
int depth;
depth = parse( file );
if ( depth > 0 )
break;
else if ( depth < 0 )
fprintf( stderr, "%d extra ')'\n", -depth );
if ( evaluate() )
{
if ( !stop )
fprintf( stderr, "load: evaluation of expression %d in %s "
"failed.\n", count, filename );
close( file );
get_token( 0, -1 );
return 1;
}
result = stack_pop( stack );
}
close( file );
stack_push( stack, result );
return 0;
}
void init_intrinsics()
{
int ( **fp )( char *, struct object *);
char **sp;
struct atom *entry1, *entry2;
struct object *object;
sp = intrinsic_syntax;
for( fp = intrinsics; *fp != NULL; ++fp )
{
entry1 = get_id( *sp, strlen( *sp ), 1 );
entry1->flags = ATOM_INTRINSIC;
set( entry1->flags );
entry1->data.function = *fp;
object = make_object();
mark( object->flags );
object->data.atom = entry1;
++sp;
entry2= get_id( *sp, strlen( *sp ), 1 );
entry2->flags = ATOM_SYMBOL;
set( entry2->flags );
insert_binding( entry2->id, object );
++sp;
}
}
void initialize( int argc, char **argv )
{
int i, result;
struct hash_elt **ptr;
struct atom *atom;
struct string *s;
char err[ 80 ], *ptr2;
struct sigaction sigact;
struct stat stats;
serv_fd = -1;
isdaemon = 0;
/*
* We do our own input buffering. We close the stdin file
* pointer here so that we cannot use it accidentally. If
* we mix up buffered and unbuffered I/O reads we may mess
* up the input stream without being aware we have done
* so. With stdin set to NULL, an error will occur if we
* try to use it.
*/
i = dup( 0 );
fclose( stdin );
stdin = NULL;
if ( dup2( i, 0 ) < 0 )
{
fprintf( stderr, "initialize: dup2: %s.\n", strerror( errno ));
exit( 1 );
}
close( i );
gc = gc_frequency = 16384;
gc_on = 1;
setenv( "MALLOC_OPTIONS", "<<", 1 );
signal( SIGPIPE, SIG_DFL );
signal( SIGCHLD, sigchld_handler );
signal( SIGHUP, SIG_DFL );
signal( SIGTERM, SIG_DFL );
signal( SIGINT, SIG_DFL );
signal( SIGQUIT, SIG_DFL );
signal( SIGTTIN, SIG_DFL );
signal( SIGTTOU, SIG_DFL );
signal( SIGTSTP, SIG_DFL );
sigact.sa_handler = sigwinch_handler;
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
if ( sigaction( SIGWINCH, &sigact, NULL ) < 0 )
fprintf( stderr, "initialize: sigaction: %s.\n", strerror( errno ));
sigact.sa_handler = sigalrm_handler;
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
if ( sigaction( SIGALRM, &sigact, NULL ) < 0 )
fprintf( stderr, "initialize: sigaction: %s.\n", strerror( errno ));
if ( isatty( 1 ) &&
setupterm( NULL, 1, &result ) == OK )
{
cl = tigetstr( "clear" );
ce = tigetstr( "el" );
cm = tigetstr( "cup" );
vi = tigetstr( "civis" );
ve = tigetstr( "cnorm" );
sf = tigetstr( "ind" );
sr = tigetstr( "ri" );
al = tigetstr( "il1" );
sc = tigetstr( "sc" );
rc = tigetstr( "rc" );
bd = tigetstr( "bold" );
me = tigetstr( "sgr0" );
}
history = make_stack();
history_ptr = 0;
string_stack = make_stack();
if ( isatty( 0 ) )
{
if ( tcgetattr( 0, &canon_termios ) < 0 )
{
fprintf( stderr, "initialize: tcgetattr: %s.\n", strerror( errno ));
exit( 1 );
}
}
srandomdev();
first_arg = arg_ptr = argv;
last_arg = &argv[ argc - 1 ];
result = regcomp( &merge_regex, "[^/]+$", REG_EXTENDED );
if ( result )
{
regerror( result, &merge_regex, err, sizeof( err ));
fprintf( stderr, "fatal error: initialize: merge_regex: regcomp(): %s.\n",
err );
exit( 1 );
}
/*
* We tolerate contiguous delimiters because the system does, and has done so
* since at least UNIX(TM) version 6, circa 1975.
*/
result = regcomp( &find_poss_regex, "^(/*([^/]+/+)*)?([^/]+)?", REG_EXTENDED );
if ( result )
{
regerror( result, &find_poss_regex, err, sizeof( err ));
fprintf( stderr, "fatal error: initialize: regcomp(): %s.\n", err );
exit( 1 );
}
ptr = atoms;
for( i = 0; i < HASH_SIZE; ++i )
*ptr++ = NULL;
ptr = env;
for( i = 0; i < HASH_SIZE; ++i )
*ptr++ = NULL;
ptr = syntax;
for( i = 0; i < HASH_SIZE; ++i )
*ptr++ = NULL;
reclaimed_objects = ( struct stack *)memory( sizeof( struct stack ));
reclaimed_objects->values = memory( sizeof( void * ) * POOL_INC );
reclaimed_objects->free = POOL_INC;
reclaimed_objects->used = 0;
reclaimed_objects->top = reclaimed_objects->values;
reclaimed_atoms = ( struct stack *)memory( sizeof( struct stack ));
reclaimed_atoms->values = memory( sizeof( void * ) * POOL_INC );
reclaimed_atoms->free = POOL_INC;
reclaimed_atoms->used = 0;
reclaimed_atoms->top = reclaimed_atoms->values;
reclaimed_ids = ( struct stack *)memory( sizeof( struct stack ));
reclaimed_ids->values = memory( sizeof( void * ) * POOL_INC );
reclaimed_ids->free = POOL_INC;
reclaimed_ids->used = 0;
reclaimed_ids->top = reclaimed_ids->values;
object_pool_stack = make_stack();
object_pool = ( struct object *)memory( sizeof( struct object ) * POOL_INC );
object_pool_ptr = object_pool;
object_pool_free = POOL_INC;
atom_pool_stack = make_stack();
atom_pool = ( struct atom *)memory( sizeof( struct atom ) * POOL_INC );
atom_pool_free = POOL_INC;
atom_pool_ptr = atom_pool;
stack = make_stack();
open_envs = make_stack();
bookmark_stack = make_stack();
buffer_stack = make_stack();
seen = make_stack();
token = make_string();
atom = get_id( "lambda", 6, 1 );
atom->data.record = ( void *)1;
atom->flags = ATOM_SYMBOL;
set( atom->flags );
lambda_id = atom->id;
lambda_syntax = atom->syntax;
atom = get_id( "macro", 5, 1 );
atom->data.record = ( void *)1;
atom->flags = ATOM_SYMBOL;
set( atom->flags );
macro_id = atom->id;
macro_syntax = atom->syntax;
atom = get_id( "_", 1, 1 );
atom->data.record = ( void *)1;
atom->flags = ATOM_SYMBOL;
set( atom->flags );
input_stack = make_stack();
quotes_pending = make_stack();
arg_stack = make_stack();
init_intrinsics();
empty = make_atom_from_string( "", 0 );
input_buffer_stack = make_stack();
descriptors[ 0 ] = make_stack();
descriptors[ 1 ] = make_stack();
descriptors[ 2 ] = make_stack();
result = 0;
if ( stat( DATADIR "/library.munger", &stats ) == 0 )
result = load( DATADIR "/library.munger" );
else if ( stat( "./library.munger", &stats ) == 0 )
result = load( "./library.munger" );
else
result = 1;
if ( result )
{
fprintf( stderr, "munger: cannot load library.munger\n" );
exit( 1 );
}
stack_pop( stack );
ptr2 = getenv( "HOME" );
if ( ptr2 != NULL )
{
s = make_string();
string_assign( s, ptr2, strlen( ptr2 ));
ptr2 = "/.munger";
while( *ptr2 )
string_append( s, *ptr2++ );
if ( stat( s->str, &stats ) == 0 )
{
load( s->str );
stack_pop( stack );
}
string_free( s );
}
if ( argc > 1 && load( argv[ 1 ] ) && fatal )
exit( 1 );
}
void stack_free( struct stack *a )
{
free( a->values );
free( a );
}
void free_objects()
{
void *ptr;
free( object_pool );
while(( ptr = stack_pop( object_pool_stack )) != NULL )
free( ptr );
stack_free( object_pool_stack );
}
void free_atoms()
{
struct atom *ptr;
free( atom_pool );
while(( ptr = ( struct atom *)stack_pop( atom_pool_stack )) != NULL )
free( ptr );
stack_free( atom_pool_stack );
}
void close_descriptors()
{
int i;
for( i = 0; i < 3; ++i )
while( descriptors[ i ]->used )
resume( "cleanup", i );
}
void free_executables ()
{
if ( path != NULL )
{
free( path );
path = NULL;
}
if ( executables != NULL )
{
while( executables->used )
{
stack_pop( executables );
stack_pop( executables );
free( stack_pop( executables ));
}
stack_free( executables );
executables = NULL;
}
}
void free_history()
{
while( history->used )
free( stack_pop( history ));
stack_free( history );
}
/*
* This function is not strictly necessary, as all resources will
* be released upon exit.
*/
void cleanup()
{
int i;
free_executables();
free_history();
if ( syslog_name != NULL )
free( syslog_name );
do_child_close( "cleanup", NULL );
display_line( NULL, NULL, NULL, 0, 0, NULL );
close_descriptors();
while( input_buffer_stack->used )
{
stack_pop( input_buffer_stack );
free( stack_pop( input_buffer_stack ));
}
stack_free( input_buffer_stack );
for( i = 0; i < 3; ++i )
{
stack_free( descriptors[ i ] );
/*
* Closes current std descriptors. If redirected onto processes,
* they should exit.
*/
close( i );
}
hash_free( env );
hash_free( atoms );
hash_free( syntax );
free_objects();
free_syntax();
free_atoms();
stack_free( stack );
stack_free( open_envs );
stack_free( input_stack );
stack_free( quotes_pending );
stack_free( arg_stack );
stack_free( reclaimed_ids );
stack_free( reclaimed_objects );
stack_free( reclaimed_atoms );
string_free( token );
stack_free( seen );
stack_free( string_stack );
while( bookmark_stack->used )
{
if ( *bookmark_stack->top != NULL )
{
hash_free( *( struct hash_elt ***)bookmark_stack->top );
free( *( struct hash_elt ***)bookmark_stack->top );
}
stack_pop( bookmark_stack );
}
stack_free( bookmark_stack );
while( buffer_stack->used )
{
DB *db;
db = ( DB *)stack_pop( buffer_stack );
if ( db != NULL )
db->close( db );
}
stack_free( buffer_stack );
regfree( &find_poss_regex );
regfree( &merge_regex );
}
#ifndef DEBUG
void stack_push( struct stack *a, void *o )
{
if ( a->free == 0 )
{
a->values = realloc( a->values, sizeof( void * ) * ( a->used + stack_inc ) );
if ( a->values == NULL )
{
fprintf( stderr, "realloc: %s.\n", strerror( errno ));
exit( 1 );
}
a->free = stack_inc;
a->top = &a->values[ a->used - 1 ];
}
if ( a->used )
++a->top;
*a->top = o;
--a->free;
++a->used;
}
struct object *stack_pop( struct stack *a )
{
struct object *ptr;
if ( a->used )
{
ptr = *a->top;
--a->used;
++a->free;
if ( a->used )
--a->top;
return ptr;
}
return NULL;
}
void stack_truncate( struct stack *a, int i )
{
while( i-- )
stack_pop( a );
}
#endif
void string_prepend( struct string *s, char c )
{
char *ptr, *ptr2;
if ( s->used == 0 )
{
string_append( s, c );
return;
}
if ( s->free == 0 )
{
s->str = ( char *)realloc( s->str, s->used + 1 + stack_inc );
if ( s->str == NULL )
{
fprintf( stderr, "string_prepend: realloc: %s.\n", strerror( errno ));
exit( 1 );
}
/* Leave room for end-of-string sentinel. */
s->free = stack_inc - 1;
s->top = &s->str[ s->used ];
}
ptr2 = &s->str[ s->used + 1 ];
for( ptr = &s->str[ s->used ]; ptr >= s->str; --ptr )
*ptr2-- = *ptr;
s->str[ 0 ] = c;
++s->used;
++s->top;
--s->free;
}
void string_append( struct string *s, char c )
{
if ( s->free == 0 )
{
s->str = ( char *)realloc( s->str, s->used + 1 + stack_inc );
if ( s->str == NULL )
{
fprintf( stderr, "string_append: realloc: %s.\n", strerror( errno ));
exit( 1 );
}
/* Leave room for end-of-string sentinel */
s->free = stack_inc;
s->top = &s->str[ s->used ];
}
++s->used;
--s->free;
*s->top++ = c;
*s->top = '\0';
}
void string_erase( struct string *s, int idx )
{
char *ptr, *ptr2;
ptr = &s->str[ idx ];
ptr2 = &s->str[ idx + 1 ];
while( *ptr2 )
*ptr++ = *ptr2++;
*ptr = '\0';
--s->used;
++s->free;
--s->top;
}
void string_chop( struct string *s )
{
if ( s->used )
{
*--s->top = '\0';
--s->used;
++s->free;
}
}
void string_truncate( struct string *s )
{
if ( s->used )
{
s->free += s->used;
s->used = 0;
*s->str = '\0';
s->top = s->str;
}
}
void string_assign( struct string *s, char *c, int len )
{
string_truncate( s );
while( len-- )
string_append( s, *c++ );
}
int get_token( int depth, int file )
{
static char characters[ 102400 ] = "", stdin_buffer[ 102400 ] ="";
static char *ptr = characters;
static int count = 1;
static int old_file = -1;
static int escape = 0;
int type;
if ( file == -1 )
{
*characters = '\0';
ptr = characters;
old_file = -1;
return -1;
}
type = -1;
string_truncate( token );
if ( old_file != file )
{
if ( old_file == 0 )
strcpy( stdin_buffer, ptr );
else if ( old_file > 0 )
{
int pos = -( strlen( ptr ));
if ( lseek( old_file, pos, SEEK_CUR ) < 0 &&
errno != EBADF &&
errno != ESPIPE )
{
fprintf( stderr, "get_token: fatal error: lseek: %s.\n",
strerror( errno ));
exit( 1 );
}
}
{
char *tmp = NULL;
if ( old_file < -1 )
tmp = ptr;
if ( file == 0 )
ptr = stdin_buffer;
else if ( file < -1 )
ptr = ( char *)stack_pop( string_stack );
else
{
*characters = '\0';
ptr = characters;
}
if ( old_file < -1 )
stack_push( string_stack, ( void *)tmp );
}
}
old_file = file;
for( ; ; )
{
int i;
if ( *ptr == '\0' )
{
if ( file == 0 )
{
if ( isatty( fileno( stdout )))
{
fputs( ">", stdout );
i = depth;
while( i-- > 0 )
fputs( ">", stdout );
fputs( " ", stdout );
}
fflush( stdout );
}
AGAIN:
if ( file < -1 ||
( count = read( file, characters, sizeof( characters ) - 1 ))
== 0 )
{
if ( type >= 0 )
return type;
old_file = -1;
return ( type = TOK_END );
}
else if ( count == -1 )
{
if ( errno == EINTR || errno == EAGAIN )
goto AGAIN;
fprintf( stderr, "read: %s.\n", strerror( errno ));
exit( 1 );
}
else
{
characters[ count ] = '\0';
ptr = characters;
}
}
for( ; *ptr; )
{
if ( type == TOK_COMMENT )
{
if ( *ptr == '\n' || *ptr == '\r' )
{
string_truncate( token );
type = -1;
}
++ptr;
continue;
}
if ( *ptr == '\\' )
{
if ( type == TOK_STRING )
{
if ( !( escape ^= 1 ) )
string_chop( token );
}
else
{
++ptr;
continue;
}
}
else if ( *ptr != '"' )
escape = 0;
if ( *ptr == ';' || *ptr == '#' )
{
if ( type < 0 )
type = TOK_COMMENT;
else if ( type != TOK_STRING )
return type;
string_append( token, *ptr++ );
}
else if ( *ptr == '(' || *ptr == ')' )
{
if ( type >= 0 )
{
if ( type == TOK_STRING )
{
string_append( token, *ptr++ );
continue;
}
return type;
}
type = ( *ptr == '(' ? TOK_OPEN : TOK_CLOSE );
string_append( token, *ptr++ );
return type;
}
else if ( *ptr == '"' )
{
if ( type < 0 )
{
type = TOK_STRING;
string_append( token, *ptr++ );
}
else if ( type != TOK_STRING )
return type;
else
{
if ( escape )
string_chop( token );
string_append( token, *ptr++ );
if ( !escape )
return type;
escape = 0;
}
}
else if (( *ptr >= 'A' && *ptr <= 'Z' ) ||
( *ptr >= 'a' && *ptr <= 'z' ) || *ptr == '_' )
{
if ( type == -1 )
type = TOK_SYMBOL;
else if ( type != TOK_STRING && type != TOK_SYMBOL )
return type;
string_append( token, *ptr++ );
}
else if ( *ptr >= '0' && *ptr <= '9' )
{
if ( type == -1 || type == TOK_MINUS )
type = TOK_FIXNUM ;
else if ( type != TOK_STRING && type != TOK_SYMBOL &&
type != TOK_FIXNUM )
return type;
string_append( token, *ptr++ );
}
else if ( !isspace( *ptr ))
{
if ( type == -1 )
{
if ( *ptr == '\'' )
type = TOK_QUOTE;
else if ( *ptr == '-' )
type = TOK_MINUS;
else
type = TOK_SPECIAL;
}
else if ( type != TOK_STRING &&
type != TOK_SPECIAL )
return type;
string_append( token, *ptr++ );
}
else
{
if ( type >= 0 )
{
if ( type != TOK_STRING )
return type;
else if ( *ptr == '\r' )
{
if ( isatty( 0 ))
string_append( token, '\n' );
else
string_append( token, *ptr++ );
}
else
string_append( token, *ptr++ );
}
else
++ptr;
}
}
}
return type;
}
int process_token( int type, int depth )
{
struct object *ptr;
static int quote = -2;
if ( type == TOK_OPEN )
{
if ( quote == -1 )
quote = depth;
++depth;
ptr = make_object();
ptr->flags = -1;
stack_push( input_stack, ptr );
}
else if ( type == TOK_QUOTE )
{
string_assign( token, "(", 1 );
depth = process_token( TOK_OPEN, depth );
string_assign( token, "quote", 5 );
process_token( TOK_SYMBOL, depth );
if ( quote > 0 )
stack_push( quotes_pending, ( void *)quote );
quote = -1;
}
else if ( type == TOK_CLOSE )
{
if ( --depth < 0 )
return depth;
if ( ( *( struct object **)input_stack->top )->flags == -1 )
( *( struct object **)input_stack->top )->flags = 1;
else
{
for( ; ; )
{
ptr = stack_pop( input_stack );
if (( *( struct object **)input_stack->top )->flags == -1 )
{
( *( struct object **)input_stack->top )->flags = 1;
( *( struct object **)input_stack->top )->data.head = ptr;
break;
}
else
( *( struct object **)input_stack->top )->next = ptr;
}
}
if ( quote == depth )
{
if ( quotes_pending->used )
quote = ( int)stack_pop( quotes_pending );
else
quote = -2;
string_assign( token, ")", 1 );
depth = process_token( TOK_CLOSE, depth );
}
else if ( depth == 0 )
stack_push( stack, stack_pop( input_stack ));
}
else
{
struct atom *entry;
if ( type == TOK_FIXNUM )
entry = toptr( atoi( token->str ));
else
{
if ( type == TOK_STRING )
string_chop( token );
entry = get_id( token->str, token->used, 1 );
switch( type )
{
case TOK_STRING:
entry->flags = ATOM_STRING;
entry->data.string = memory( sizeof( struct lstring ));
entry->data.string->string = &entry->syntax[ 1 ];
entry->data.string->length = token->used - 1;
break;
case TOK_SYMBOL:
case TOK_SPECIAL:
entry->flags = ATOM_SYMBOL;
entry->data.record = NULL;
break;
}
}
ptr = make_object();
ptr->data.atom = entry;
if ( type == TOK_FIXNUM )
setnumber( ptr->flags );
stack_push( ( depth ? input_stack : stack ), ptr );
if ( quote == -1 )
{
if ( quotes_pending->used )
quote = ( int )stack_pop( quotes_pending );
else
quote = -2;
string_assign( token, ")", 1 );
depth = process_token( TOK_CLOSE, depth );
}
}
return depth;
}
int parse( int file )
{
int depth = 0;
for( ; ; )
{
int type;
type = get_token( depth, file );
if ( type == TOK_END )
return 1;
depth = process_token( type, depth );
if ( depth <= 0 )
break;
}
return depth;
}
void do_printing( struct object *ptr, int recursive )
{
while( ptr != NULL )
{
if ( islist( ptr->flags ) )
{
fputc( '(', stdout );
do_printing( ptr->data.head, 1 );
fputc( ')', stdout );
}
else if ( numberp( ptr->flags ))
printf( "%i", number( ptr->data.atom ));
else
{
fwrite( ptr->data.atom->syntax, ptr->data.atom->len, 1, stdout );
if ( type( ptr->data.atom->flags ) == ATOM_STRING )
fputc( '"', stdout );
}
if ( recursive == 0 )
break;
if (( ptr = ptr->next ) != NULL )
fputc( ' ', stdout );
}
}
struct object *make_atom_from_closure( struct closure *closure, int macro )
{
struct atom *entry;
struct object *obj;
char buffer[ 128 ], *t;
t = ( macro ? "<MACRO_CLOSURE#%d>" : "<CLOSURE#%d>" );
snprintf( buffer, sizeof( buffer ), t, closure_counter++ );
entry = get_id( buffer, strlen( buffer ), 1 );
entry->flags = ( macro ? ATOM_MACRO : ATOM_CLOSURE );
entry->data.closure = closure;
obj = make_object();
obj->data.atom = entry;
return obj;
}
struct object *make_atom_from_act_record( struct stack *act_record )
{
struct atom *entry;
struct object *obj;
char buffer[ 128 ];
snprintf( buffer, sizeof( buffer ), "<ACT_RECORD#%d>", act_record_counter++ );
entry = get_id( buffer, strlen( buffer ), 1 );
entry->flags = ATOM_ACT_RECORD;
entry->data.act_record = act_record;
obj = make_object();
obj->data.atom = entry;
return obj;
}
int make_act_record( struct object *args, struct closure *closure, struct stack *act_record,
char *name, int eval_args )
{
int i, j, sym_count = 0, arg_count = 0;
char *more = "";
struct object *ptr, *ptr2;
stack_push( act_record, closure->env );
for( ptr = closure->text->data.head; ptr != NULL; ptr = ptr->next )
{
if ( ptr->next == NULL && islist( ptr->flags ))
more = "(at least)";
++sym_count;
}
if ( sym_count && args == NULL && *more == '\0' )
{
fprintf( stderr, "%s: 0 argument(s) given to closure "
"expecting %s %d argument(s).\n", name, more, sym_count );
return 1;
}
for( ptr = args; ptr != NULL; ptr = ptr->next )
++arg_count;
if ( sym_count != arg_count )
{
if ( *more == '\0' || ( arg_count < sym_count - 1 ))
{
fprintf( stderr, "%s: %d argument(s) given to closure"
" expecting %s %d argument(s).\n",
name, arg_count, more, sym_count );
return 1;
}
}
if ( *more == '\0' )
{
if ( arg_count )
{
for( i = 1, ptr = closure->text->data.head, ptr2 = args;
i <= arg_count;
ptr = ptr->next, ptr2 = ptr2->next, ++i )
{
if ( eval_args )
{
stack_push( stack, ptr2 );
if ( evaluate() )
{
if ( !stop )
fprintf( stderr, "%s: evaluation of argument %d failed.\n",
name, i );
return 1;
}
}
else
{
stack_push( act_record, ptr2 );
stack_push( act_record, ( void *)ptr->data.atom->id );
}
}
if ( eval_args )
{
for( j = i - 1, i = 1, ptr = closure->text->data.head; i <= arg_count; ptr = ptr->next, ++i, --j )
{
stack_push( act_record, stack->values[ stack->used - j ] );
stack_push( act_record, ( void *)ptr->data.atom->id );
}
stack_truncate( stack, i - 1 );
}
}
}
else
{
struct object *result;
if ( arg_count == 0 )
{
result = make_object();
setlist( result->flags );
stack_push( act_record, result );
stack_push( act_record, ( void *)closure->text->data.head->data.head->data.atom->id );
}
else
{
struct object **ptr3;
for( i = 1, ptr = closure->text->data.head, ptr2 = args;
i < sym_count;
ptr = ptr->next, ptr2 = ptr2->next, ++i )
{
if ( eval_args )
{
stack_push( stack, ptr2 );
if ( evaluate() )
{
if ( !stop )
fprintf( stderr, "%s: evaluation of argument %d failed.\n", name, i );
return 1;
}
}
else
{
stack_push( act_record, ptr2 );
stack_push( act_record, ( void *)ptr->data.atom->id );
}
}
if ( i > 1 && eval_args )
{
for( j = i - 1, i = 1, ptr = closure->text->data.head; i < sym_count; ptr = ptr->next, ++i, --j )
{
stack_push( act_record, stack->values[ stack->used - j ] );
stack_push( act_record, ( void *)ptr->data.atom->id );
}
stack_truncate( stack, i - 1 );
}
result = make_object();
setlist( result->flags );
stack_push( stack, result );
stack_push( act_record, result );
stack_push( act_record, ( void *)ptr->data.head->data.atom->id );
ptr3 = &result->data.head;
if ( ptr2 != NULL )
{
for( j = 1; ptr2 != NULL; ptr2 = ptr2->next, ++i, ++j )
{
if ( eval_args )
{
stack_push( stack, ptr2 );
if ( evaluate() )
{
if ( !stop )
fprintf( stderr, "%s: evaluation of argument %d failed.\n", name, i );
return 1;
}
}
else
{
*ptr3 = ptr2;
ptr3 = &( *ptr3 )->next;
}
}
if ( eval_args )
{
ptr3 = &result->data.head;
i = --j;
for( ; j > 0; --j )
{
*ptr3 = duplicate_object( stack->values[ stack->used - j ] );
ptr3 = &( *ptr3 )->next;
}
stack_truncate( stack, i );
}
}
stack_pop( stack );
*ptr3 = NULL;
}
}
return 0;
}
int apply_closure( char *name, struct closure *closure, struct object *args, int eval_args )
{
struct object *obj, *ptr, *result = NULL;
struct closure *old_closure;
struct stack *act_record;
int returned = 0, i, j;
act_record = make_stack();
if ( make_act_record( args, closure, act_record, name, eval_args ))
return 1;
obj = make_atom_from_act_record( act_record );
old_closure = current_closure;
current_closure = closure;
stack_push( open_envs, local_env );
local_env = obj;
i = stack->used;
TAILCALL:
returned = 0;
for( j = 1, ptr = current_closure->text->next; ptr != NULL; ptr = ptr->next, ++j )
{
stack_push( stack, ptr );
if ( evaluate() )
{
if ( !stop )
fprintf( stderr, "%s: error evaluating body expression %d.\n",
name, j );
returned = 1;
break;
}
result = stack_pop( stack );
}
if ( returned && tailcall )
{
stack_truncate( stack, stack->used - i );
tailcall = 0;
name = tailcall_syntax;
tailcall_syntax = NULL;
stop = 0;
goto TAILCALL;
}
if ( returned == 0 )
stack_push( stack, result );
local_env = stack_pop( open_envs );
current_closure = old_closure;
return returned;
}
struct object *lookup_local( id )
{
struct stack *local;
struct object *env_ptr, **ptr;
for( env_ptr = local_env;
env_ptr != NULL;
env_ptr = ( struct object *)local->values[ 0 ] )
{
local = env_ptr->data.atom->data.act_record;
for( ptr = ( struct object **)local->top;
ptr > ( struct object **)local->values;
ptr -= 2 )
if ( *( int *)ptr == id )
return ( struct object *) *--ptr;
}
return NULL;
}
int set_local( int id, struct object *obj )
{
struct stack *local;
struct object *env_ptr, **ptr;
for( env_ptr = local_env;
env_ptr != NULL;
env_ptr = ( struct object *)local->values[ 0 ] )
{
local = env_ptr->data.atom->data.act_record;
for( ptr = ( struct object **)local->top;
ptr > ( struct object **)local->values;
ptr -= 2 )
if ( *( int *)ptr == id )
{
*--ptr = obj;
return 0;
}
}
return 1;
}
int create_closure( struct object *func, int macro )
{
struct closure *closure;
struct object *ptr;
char *syntax = "create_closure";
if ( islist( func->flags ) == 0 )
{
fprintf( stderr, "%s: missing parameter list.\n", syntax );
return 1;
}
if ( func->next == NULL )
{
fprintf( stderr, "%s: missing function body.\n", syntax );
return 1;
}
for( ptr = func->data.head; ptr != NULL; ptr = ptr->next )
{
if ( islist( ptr->flags ))
{
struct object *ptr2;
if ( ptr->next != NULL )
{
fprintf( stderr, "%s: sublist in non-terminal position "
"of parameter list", syntax );
return 1;
}
ptr2 = ptr->data.head;
if ( islist( ptr2->flags ) ||
type( ptr2->data.atom->flags ) != ATOM_SYMBOL )
{
fprintf( stderr, "%s: non-symbol in optional parameter sublist.\n",
syntax );
return 1;
}
if ( ptr2->next != NULL )
{
fprintf( stderr, "%s: optional parameter sublist has more than 1"
"element.\n", syntax );
return 1;
}
}
else if ( type( ptr->data.atom->flags ) != ATOM_SYMBOL )
{
fprintf( stderr, "%s: non-symbol in parameter list.\n", syntax );
return 1;
}
}
closure = ( struct closure *)memory( sizeof( struct closure ) );
closure->text = func;
closure->env = local_env;
stack_push( stack, make_atom_from_closure( closure, macro ));
return 0;
}
void func_err( char *str, struct object *car )
{
fputs( "evaluate: ", stderr );
fputs( car->data.atom->syntax, stderr );
if ( type( car->data.atom->flags ) == ATOM_STRING )
fputc( '"', stderr );
fprintf( stderr, " %s.\n", str );
}
int evaluate()
{
int result = 0;
struct object *item;
if ( gc_on && --gc <= 0 )
{
#ifdef DEBUG
fputs( "Collecting garbage...", stderr );
#endif
collect_garbage();
gc = gc_frequency;
}
if ( stack->used == 0 )
return 1;
if ( ( islist( (( struct object *)( *stack->top ))->flags ) &&
(( struct object *)( *stack->top ))->data.head == NULL )
||
( islist( (( struct object *)( *stack->top ))->flags ) == 0 &&
( numberp( (( struct object *)( *stack->top ))->flags ) ||
(( struct object *)( *stack->top ))->data.atom->id == lambda_id ||
(( struct object * )( *stack->top ))->data.atom->id == macro_id ||
type( (( struct object *)( *stack->top ))->data.atom->flags ) != ATOM_SYMBOL )))
return 0;
item = stack_pop( stack );
if ( islist( item->flags ) == 0 )
{
struct object *value;
if (( value = lookup_local( item->data.atom->id )) == NULL )
{
value = lookup_binding( item->data.atom->id );
if ( value == NULL )
{
fprintf( stderr, "evaluate: symbol %s not bound.\n",
item->data.atom->syntax );
return 1;
}
}
stack_push( stack, value );
}
else
{
char t, *name = NULL;
struct object *car, *cdr;
car = item->data.head;
cdr = item->data.head->next;
if ( islist( car->flags ) == 0 &&
numberp( car->flags ) == 0 &&
type( car->data.atom->flags ) == ATOM_SYMBOL )
name = car->data.atom->syntax;
stack_push( stack, item );
stack_push( stack, car );
if ( evaluate() )
return 1;
car = *( struct object **)stack->top;
if ( islist( car->flags ) || numberp( car->flags ))
{
fprintf( stderr, "evaluate: function position did not evaluate"
" to an intrinsic nor a closure.\n" );
return 1;
}
if ( car->data.atom->id == lambda_id )
result = create_closure( cdr, 0 );
else if ( car->data.atom->id == macro_id )
result = create_closure( cdr, 1 );
else
{
#ifdef DEBUG
int i;
i = stack->used;
#endif
t = type( car->data.atom->flags );
switch( t )
{
case ATOM_INTRINSIC:
result = car->data.atom->data.function( car->data.atom->syntax, cdr );
break;
case ATOM_CLOSURE:
result = apply_closure( ( name == NULL ? car->data.atom->syntax : name ),
car->data.atom->data.closure,
cdr,
1 );
break;
case ATOM_MACRO:
if (( result = apply_closure( ( name == NULL ? car->data.atom->syntax : name ),
car->data.atom->data.closure,
cdr,
0 )) == 0 &&
( result = evaluate() ))
{
if ( !stop )
func_err( "secondary evaluation of macro failed", car );
return 1;
}
break;
default:
func_err( "is not an intrinsic nor a closure", car );
return 1;
}
#ifdef DEBUG
if ( i != stack->used - 1 && result != 1 )
{
fprintf( stderr, "stack corrupted by: %s: before: %d, after: %d\n",
( name == NULL ? car->data.atom->syntax : name ),
i, stack->used );
exit( 1 );
}
#endif
}
if ( result == 0 || next_iteration )
{
item = stack_pop( stack );
stack_pop( stack );
stack_pop( stack );
stack_push( stack, item );
}
}
return result;
}
void toplevel()
{
char *t = "toplevel";
int result;
if ( isatty( 0 ) )
printf( "--------------------------------------\n"
"Munger %d.%d\nCopyright 2001-2007, James Bailie\nhttp://www.mammothcheese.ca/munger.html\n"
"--------------------------------------\n\n",
VERSION_MAJOR, VERSION_MINOR );
for( ; ; )
{
int depth;
arg_stack->free += arg_stack->used;
arg_stack->used = 0;
arg_stack->top = arg_stack->values;
stack->free += stack->used;
stack->used = 0;
stack->top = stack->values;
if ( mode == 0 )
canon( t );
fflush( stdout );
depth = parse( 0 );
if ( depth > 0 )
break;
else if ( depth < 0 )
{
fprintf( stderr, "%s: %d extra ')'\n", t, -depth );
if ( fatal )
exit( 1 );
}
result = evaluate();
close_descriptors();
if ( result == 0 )
{
if ( printer )
{
print_object( *( struct object **)stack->top );
putchar( '\n' );
}
}
else if ( thrown != NULL )
{
fprintf( stderr, "%s: uncaught \"throw\"\n", t );
if ( fatal )
exit( 1 );
thrown = NULL;
}
else if ( next_iteration )
{
fprintf( stderr, "%s: \"continue\" used outside of loop\n", t );
if ( fatal )
exit( 1 );
}
else if ( fatal )
exit( 1 );
stop = next_iteration = 0;
collect_garbage();
}
}
void mark_record( struct object **record )
{
int total, i;
struct object **ptr;
for( i = 0; i < seen->used; ++i )
if ( seen->values[ i ] == record )
return;
total = *( int *)record;
ptr = record;
for( i = 0; i < total; ++i )
mark_object( *++ptr );
}
void mark_object( struct object *obj )
{
if ( obj == NULL )
return;
mark( obj->flags );
if ( islist( obj->flags ) )
mark_list( obj->data.head );
else if ( numberp( obj->flags ) == 0 )
{
set( obj->data.atom->flags );
switch( type( obj->data.atom->flags ))
{
case ATOM_TABLE:
mark_table( obj->data.atom->data.hash );
break;
case ATOM_STACK:
mark_stack( obj->data.atom->data.stack );
break;
case ATOM_CLOSURE:
case ATOM_MACRO:
mark_closure( obj->data.atom->data.closure );
break;
case ATOM_ACT_RECORD:
mark_act_record( obj->data.atom->data.act_record );
break;
case ATOM_RECORD:
mark_record( obj->data.atom->data.record );
}
}
}
void mark_act_record( struct stack *act_record )
{
int i;
for( i = 0; i < seen->used; ++i )
if ( seen->values[ i ] == act_record )
return;
stack_push( seen, act_record );
mark_object( ( struct object *)act_record->values[ 0 ] );
for( i = 1; i < act_record->used; i += 2 )
mark_object( ( struct object *)act_record->values[ i ] );
}
void mark_closure( struct closure *closure )
{
if ( closure == NULL )
return;
mark_list( closure->text );
mark_object( closure->env );
}
void mark_stack( struct stack *stk )
{
int i;
if ( stk->used == 0 )
return;
for( i = 0; i < seen->used; ++i )
if ( seen->values[ i ] == stk )
return;
stack_push( seen, stk );
for( i = 0; i < stk->used; ++i )
mark_object( ( struct object *)stk->values[ i ] );
}
void mark_table( struct hash_elt **ptr )
{
struct stack *values;
struct atom *key;
int i;
for( i = 0; i < seen->used; ++i )
if ( seen->values[ i ] == ptr )
return;
stack_push( seen, ptr );
values = get_hash_keys( ptr );
while( values->used )
{
key = lookup_atom( ( int)stack_pop( values ));
set( key->flags );
}
stack_free( values );
values = get_hash_values( ptr );
while( values->used )
mark_object( ( struct object *)stack_pop( values ));
stack_free( values );
}
void mark_list( struct object *ptr )
{
while( ptr != NULL )
{
mark_object( ptr );
ptr = ptr->next;
}
}
void sweep_objects()
{
struct object *top;
int i, j, old_stack_inc;
old_stack_inc = stack_inc;
stack_inc = POOL_INC;
i = object_pool_stack->used;
while( i )
{
top = ( struct object *)object_pool_stack->values[ --i ];
for( j = 0; j < POOL_INC; ++j, ++top )
if (( islist( top->flags ) && ismarked( top->flags ) == 0 ) ||
( numberp( top->flags ) && ismarked( top->flags ) == 0 ) ||
( top->flags == 0 && top->data.atom ))
{
bzero( top, sizeof( struct object ));
stack_push( reclaimed_objects, top );
}
else
unmark( top->flags );
}
for( top = object_pool; top < object_pool_ptr; ++top )
{
if (( islist( top->flags ) && ismarked( top->flags ) == 0 ) ||
( numberp( top->flags ) && ismarked( top->flags ) == 0 ) ||
( top->flags == 0 && top->data.atom ))
{
bzero( top, sizeof( struct object ));
stack_push( reclaimed_objects, top );
}
else
unmark( top->flags );
}
stack_inc = old_stack_inc;
}
void free_atom( struct atom *atom )
{
free( atom->syntax );
switch( type( atom->flags ))
{
case ATOM_STRING:
free( atom->data.string );
break;
case ATOM_REGEXP:
regfree( atom->data.regexp );
free( atom->data.regexp );
break;
case ATOM_TABLE:
hash_free( atom->data.hash );
free( atom->data.hash );
break;
case ATOM_DB:
#ifdef SQL
if ( atom->data.db != NULL )
sqlite3_close( atom->data.db );
#endif
break;
case ATOM_STACK:
stack_free( atom->data.stack );
break;
case ATOM_CLOSURE:
case ATOM_MACRO:
free( atom->data.closure );
break;
case ATOM_ACT_RECORD:
stack_free( atom->data.act_record );
break;
case ATOM_RECORD:
free( atom->data.record );
break;
case ATOM_SQL:
#ifdef SQL
if ( atom->data.sql != NULL )
sqlite3_finalize( atom->data.sql );
#endif
break;
}
}
void sweep_atoms()
{
struct atom *top;
int i, j, old_stack_inc;
old_stack_inc = stack_inc;
stack_inc = POOL_INC;
i = atom_pool_stack->used;
while( i )
{
top = ( struct atom *)atom_pool_stack->values[ --i ];
for( j = 0; j < POOL_INC; ++j, ++top )
{
if ( type( top->flags ) == ATOM_INTRINSIC )
continue;
else if ( top->id && isitset( top->flags ) == 0 )
{
stack_push( reclaimed_ids, ( void *)top->id );
remove_atom( top->id );
remove_id( top->syntax, top->len, top->id );
bzero( top, sizeof( struct atom ));
stack_push( reclaimed_atoms, top );
}
else
unset( top->flags );
}
}
for( top = atom_pool; top < atom_pool_ptr; ++top )
{
if ( type( top->flags ) == ATOM_INTRINSIC )
continue;
else if ( top->id && isitset( top->flags ) == 0 )
{
stack_push( reclaimed_ids, ( void *)top->id );
remove_atom( top->id );
remove_id( top->syntax, top->len, top->id );
bzero( top, sizeof( struct atom ));
stack_push( reclaimed_atoms, top );
}
else
unset( top->flags );
}
stack_inc = old_stack_inc;
}
void mark_bookmarks()
{
int i;
struct stack *keys;
struct atom *key;
for( i = 0; i < bookmark_stack->used; ++i )
{
if ( bookmark_stack->values[ i ] == NULL )
continue;
keys = get_hash_keys( ( struct hash_elt **)bookmark_stack->values[ i ] );
while( keys->used )
{
key = lookup_atom( ( int)stack_pop( keys ));
set( key->flags );
}
stack_free( keys );
}
}
void collect_garbage()
{
seen->free += seen->used;
seen->used = 0;
seen->top = seen->values;
mark_bookmarks();
mark_object( empty );
mark_object( local_env );
mark_stack( open_envs );
mark_table( env );
mark_stack( stack );
sweep_objects();
sweep_atoms();
}
void reset_term()
{
if ( mode )
canon( "" );
}
int main( int argc, char **argv )
{
atexit( reset_term );
initialize( argc, argv );
toplevel();
cleanup();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1