aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Brooksby2012-09-11 10:54:29 +0100
committerRichard Brooksby2012-09-11 10:54:29 +0100
commitc856f16c96f65709d3f0eb11cdeb0b4dfad3cafd (patch)
tree6b24a8ee9e4d3041671e7ab65d026822b266631e
parentfc79c1f1a49414d7038ee8d190a54b842c7fceb6 (diff)
downloademacs-c856f16c96f65709d3f0eb11cdeb0b4dfad3cafd.tar.gz
emacs-c856f16c96f65709d3f0eb11cdeb0b4dfad3cafd.zip
Fixed line endings. they were classic mac (cr)!
Added MPS TO DO list. Added printing of GC messages. Further documentation improvements. Copied from Perforce Change: 179415 ServerID: perforce.ravenbrook.com
-rw-r--r--mps/example/scheme/scheme.c2832
1 files changed, 2831 insertions, 1 deletions
diff --git a/mps/example/scheme/scheme.c b/mps/example/scheme/scheme.c
index 5751d5aee71..72f1251d4d3 100644
--- a/mps/example/scheme/scheme.c
+++ b/mps/example/scheme/scheme.c
@@ -1 +1,2831 @@
1/* scheme.c -- SCHEME INTERPRETER EXAMPLE FOR THE MEMORY POOL SYSTEM * * $Id$ * Copyright (c) 2001-2012 Ravenbrook Limited. See end of file for license. * * This is a simple interpreter for a subset of the Scheme programming * language <http://en.wikipedia.org/wiki/Scheme_%28programming_language%29)>. * It is by no means the best or even the right way to implement Scheme, * but it serves the purpose of showing how the Memory Pool System can be * used as part of a programming language run-time system. * * TO DO * - unbounded integers, other number types. * - do, named let. * - Quasiquote implementation is messy. * - Lots of library. * - \#foo unsatisfactory in read and print * - tail recursion (pass current function to eval) */ #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <stdarg.h> #include <ctype.h> #include <string.h> #include <assert.h> #include <setjmp.h> #include "mps.h" #include "mpsavm.h" #include "mpscamc.h" /* LANGUAGE EXTENSION */ #define unless(c) if(!(c)) #define LENGTH(array) (sizeof(array) / sizeof(array[0])) /* CONFIGURATION PARAMETERS */ #define SYMMAX ((size_t)255) /* max length of a symbol */ #define MSGMAX ((size_t)255) /* max length of error message */ #define STRMAX ((size_t)255) /* max length of a string */ /* DATA TYPES */ /* obj_t -- scheme object type * * obj_t is a pointer to a union, obj_u, which has members for * each scheme representation. * * The obj_u also has a "type" member. Each representation * structure also has a "type" field first. ANSI C guarantees * that these type fields correspond [section?]. * * Objects are allocated by allocating one of the representation * structures and casting the pointer to it to type obj_t. This * allows objects of different sizes to be represented by the * same type. * * To access an object, check its type by reading TYPE(obj), then * access the fields of the representation, e.g. * if(TYPE(obj) == TYPE_PAIR) fiddle_with(CAR(obj)); */ typedef union obj_u *obj_t; typedef obj_t (*entry_t)(obj_t env, obj_t op_env, obj_t operator, obj_t rands); typedef int type_t; enum { TYPE_PAIR, TYPE_INTEGER, TYPE_SYMBOL, TYPE_SPECIAL, TYPE_OPERATOR, TYPE_STRING, TYPE_PORT, TYPE_PROMISE, TYPE_CHARACTER, TYPE_VECTOR, TYPE_FWD2, /* two-word broken heart */ TYPE_FWD, /* three-words and up broken heart */ TYPE_PAD1 /* one-word padding object */ }; typedef struct type_s { type_t type; } type_s; typedef struct pair_s { type_t type; /* TYPE_PAIR */ obj_t car, cdr; /* first and second projections */ } pair_s; typedef struct symbol_s { type_t type; /* TYPE_SYMBOL */ size_t length; /* length of symbol string (excl. NUL) */ char string[1]; /* symbol string, NUL terminated */ } symbol_s; typedef struct integer_s { type_t type; /* TYPE_INTEGER */ long integer; /* the integer */ } integer_s; typedef struct special_s { type_t type; /* TYPE_SPECIAL */ char *name; /* printed representation, NUL terminated */ } special_s; typedef struct operator_s { type_t type; /* TYPE_OPERATOR */ char *name; /* printed name, NUL terminated */ entry_t entry; /* entry point -- see eval() */ obj_t arguments, body; /* function arguments and code */ obj_t env, op_env; /* closure environments */ } operator_s; typedef struct string_s { type_t type; /* TYPE_STRING */ size_t length; /* number of chars in string */ char string[1]; /* string, NUL terminated */ } string_s; typedef struct port_s { type_t type; /* TYPE_PORT */ obj_t name; /* name of stream */ FILE *stream; } port_s; typedef struct character_s { type_t type; /* TYPE_CHARACTER */ char c; /* the character */ } character_s; typedef struct vector_s { type_t type; /* TYPE_VECTOR */ size_t length; /* number of elements */ obj_t vector[1]; /* vector elements */ } vector_s; /* fwd, fwd2, pad1 -- MPS forwarding and padding objects * * These object types are here to satisfy the MPS Format Protocol for * format variant "A". * * The MPS needs to be able to replace any object with forwarding object * or [broken heart](http://www.memorymanagement.org/glossary/b.html#broken.heart) * and since the smallest normal object defined above is two words long, * we have two kinds of forwarding objects: FWD2 is exactly two words * long, and FWD stores a size for larger objects. There are cleverer * ways to do this with bit twiddling, of course. * * The MPS needs to be able to pad out any area of memory that's a * multiple of the pool alignment. We've chosen an single word alignment * for this Scheme, so we have to have a special padding object, PAD1, * for single words. For larger objects we can just use forwarding objects * with NULL in their `fwd` fields. See `obj_isfwd` for details. * * See obj_pad, obj_fwd etc. to see how these are used. */ typedef struct fwd2_s { type_t type; /* TYPE_FWD2 */ obj_t fwd; /* forwarded object */ } fwd2_s; typedef struct fwd_s { type_t type; /* TYPE_FWD */ obj_t fwd; /* forwarded object */ size_t size; /* total size of this object */ } fwd_s; typedef struct pad1_s { type_t type; /* TYPE_PAD1 */ } pad1_s; typedef union obj_u { type_s type; /* one of TYPE_* */ pair_s pair; symbol_s symbol; integer_s integer; special_s special; operator_s operator; string_s string; port_s port; character_s character; vector_s vector; fwd2_s fwd2; fwd_s fwd; } obj_s; /* structure macros */ #define TYPE(obj) ((obj)->type.type) #define CAR(obj) ((obj)->pair.car) #define CDR(obj) ((obj)->pair.cdr) #define CAAR(obj) CAR(CAR(obj)) #define CADR(obj) CAR(CDR(obj)) #define CDAR(obj) CDR(CAR(obj)) #define CDDR(obj) CDR(CDR(obj)) #define CADDR(obj) CAR(CDDR(obj)) #define CDDDR(obj) CDR(CDDR(obj)) #define CDDAR(obj) CDR(CDAR(obj)) #define CADAR(obj) CAR(CDAR(obj)) /* GLOBAL DATA */ /* total -- total allocated bytes */ static size_t total; /* symtab -- symbol table * * The symbol table is a hash-table containing objects of TYPE_SYMBOL. * When a string is "interned" it is looked up in the table, and added * only if it is not there. This guarantees that all symbols which * are equal are actually the same object. * * The symbol table is simply a malloc'd array of obj_t pointers. Since * it's outside the MPS and refers to objects we want the MPS to keep * alive, it must be declared to the MPS as a root. Search for * occurrences of `symtab_root` to see how this is done. */ static obj_t *symtab; static size_t symtab_size; static mps_root_t symtab_root; /* special objects * * These global variables are initialized to point to objects of * TYPE_SPECIAL by main. They are used as markers for various * special purposes. */ static obj_t obj_empty; /* (), the empty list */ static obj_t obj_eof; /* end of file */ static obj_t obj_error; /* error indicator */ static obj_t obj_true; /* #t, boolean true */ static obj_t obj_false; /* #f, boolean false */ static obj_t obj_undefined; /* undefined result indicator */ /* predefined symbols * * These global variables are initialized to point to interned * objects of TYPE_SYMBOL. They have special meaning in the * Scheme language, and are used by the evaluator to parse code. */ static obj_t obj_quote; /* "quote" symbol */ static obj_t obj_quasiquote; /* "quasiquote" symbol */ static obj_t obj_lambda; /* "lambda" symbol */ static obj_t obj_begin; /* "begin" symbol */ static obj_t obj_else; /* "else" symbol */ static obj_t obj_unquote; /* "unquote" symbol */ static obj_t obj_unquote_splic; /* "unquote-splicing" symbol */ /* error handler * * The error_handler variable is initialized to point at a * jmp_buf to which the "error" function longjmps if there is * any kind of error during evaluation. It can be set up by * any enclosing function that wants to catch errors. There * is a default error handler in main, in the read-eval-print * loop. The error function also writes an error message * into "error_message" before longjmping, and this can be * displayed to the user when catching the error. * * [An error code should also be passed so that the error can * be decoded by enclosing code.] */ static jmp_buf *error_handler; static char error_message[MSGMAX+1]; /* MPS pools */ mps_arena_t arena; mps_pool_t obj_pool; mps_ap_t obj_ap; /* SUPPORT FUNCTIONS */ /* error -- throw an error condition * * The "error" function takes a printf-style format string * and arguments, writes the message into error_message and * longjmps to *error_handler. There must be a setjmp at * the other end to catch the condition and display the * message. */ static void error(char *format, ...) { va_list args; assert(error_handler != NULL); va_start(args, format); vsprintf(error_message, format, args); va_end(args); longjmp(*error_handler, 1); } /* make_* -- object constructors * * Each object type has a function here that allocates an instance of * that type. * * These functions illustrate the two-phase MPS Allocation Point Protocol * with `reserve` and `commmit`. This protocol allows very fast in-line * allocation without locking, but there is a very tiny chance that the * object must be re-initialized. In nearly all cases, however, it's * just a pointer bump. */ #define ALIGN(size) \ (((size) + sizeof(mps_word_t) - 1) & ~(sizeof(mps_word_t) - 1)) static obj_t make_pair(obj_t car, obj_t cdr) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(sizeof(pair_s)); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_pair"); obj = addr; obj->pair.type = TYPE_PAIR; CAR(obj) = car; CDR(obj) = cdr; } while(!mps_commit(obj_ap, addr, size)); total += sizeof(pair_s); return obj; } static obj_t make_integer(long integer) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(sizeof(integer_s)); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_integer"); obj = addr; obj->integer.type = TYPE_INTEGER; obj->integer.integer = integer; } while(!mps_commit(obj_ap, addr, size)); total += sizeof(integer_s); return obj; } static obj_t make_symbol(size_t length, char string[]) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(offsetof(symbol_s, string) + length+1); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_symbol"); obj = addr; obj->symbol.type = TYPE_SYMBOL; obj->symbol.length = length; memcpy(obj->symbol.string, string, length+1); } while(!mps_commit(obj_ap, addr, size)); total += size; return obj; } static obj_t make_string(size_t length, char string[]) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(offsetof(string_s, string) + length+1); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_string"); obj = addr; obj->string.type = TYPE_STRING; obj->string.length = length; memcpy(obj->string.string, string, length+1); } while(!mps_commit(obj_ap, addr, size)); total += size; return obj; } static obj_t make_special(char *string) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(sizeof(special_s)); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_special"); obj = addr; obj->special.type = TYPE_SPECIAL; obj->special.name = string; } while(!mps_commit(obj_ap, addr, size)); total += sizeof(special_s); return obj; } static obj_t make_operator(char *name, entry_t entry, obj_t arguments, obj_t body, obj_t env, obj_t op_env) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(sizeof(operator_s)); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_operator"); obj = addr; obj->operator.type = TYPE_OPERATOR; obj->operator.name = name; obj->operator.entry = entry; obj->operator.arguments = arguments; obj->operator.body = body; obj->operator.env = env; obj->operator.op_env = op_env; } while(!mps_commit(obj_ap, addr, size)); total += sizeof(operator_s); return obj; } static obj_t make_port(obj_t name, FILE *stream) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(sizeof(port_s)); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_operator"); obj = addr; obj->port.type = TYPE_PORT; obj->port.name = name; obj->port.stream = stream; } while(!mps_commit(obj_ap, addr, size)); total += sizeof(port_s); return obj; } static obj_t make_character(char c) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(sizeof(character_s)); do { /* FIXME: Alignment! */ mps_res_t res = mps_reserve(&addr, obj_ap, size); if (res != MPS_RES_OK) error("out of memory in make_character"); obj = addr; obj->character.type = TYPE_CHARACTER; obj->character.c = c; } while(!mps_commit(obj_ap, addr, size)); total += sizeof(character_s); return obj; } static obj_t make_vector(size_t length, obj_t fill) { obj_t obj; mps_addr_t addr; size_t size = ALIGN(offsetof(vector_s, vector) + length * sizeof(obj_t)); do { mps_res_t res = mps_reserve(&addr, obj_ap, size); size_t i; if (res != MPS_RES_OK) error("out of memory in make_vector"); obj = addr; obj->vector.type = TYPE_VECTOR; obj->vector.length = length; for(i = 0; i < length; ++i) obj->vector.vector[i] = fill; } while(!mps_commit(obj_ap, addr, size)); total += size; return obj; } /* getnbc -- get next non-blank char from stream */ static int getnbc(FILE *stream) { int c; do c = getc(stream); while(isspace(c)); return c; } /* isealpha -- test for "extended alphabetic" char * * Scheme symbols may contain any "extended alphabetic" * character (see section 2.1 of R4RS). This function * returns non-zero if a character is in the set of * extended characters. */ static int isealpha(int c) { return strchr("+-.*/<=>!?:$%_&~^", c) != NULL; } /* hash -- hash a string to an unsigned long * * This hash function was derived (with permission) from * Paul Haahr's hash in the most excellent rc 1.4. */ static unsigned long hash(const char *s) { char c; unsigned long h=0; do { c=*s++; if(c=='\0') break; else h+=(c<<17)^(c<<11)^(c<<5)^(c>>1); c=*s++; if(c=='\0') break; else h^=(c<<14)+(c<<7)+(c<<4)+c; c=*s++; if(c=='\0') break; else h^=(~c<<11)|((c<<3)^(c>>1)); c=*s++; if(c=='\0') break; else h-=(c<<16)|(c<<9)|(c<<2)|(c&3); } while(c); return h; } /* find -- find entry for symbol in symbol table * * Look for a symbol matching the string in the symbol table. * If the symbol was found, returns the address of the symbol * table entry which points to the symbol. Otherwise it * either returns the address of a NULL entry into which the * new symbol should be inserted, or NULL if the symbol table * is full. */ static obj_t *find(char *string) { unsigned long i, h; h = hash(string) & (symtab_size-1); i = h; do { if(symtab[i] == NULL || strcmp(string, symtab[i]->symbol.string) == 0) return &symtab[i]; i = (i+h+1) & (symtab_size-1); } while(i != h); return NULL; } /* rehash -- double size of symbol table */ static void rehash(void) { obj_t *old_symtab = symtab; unsigned old_symtab_size = symtab_size; mps_root_t old_symtab_root = symtab_root; unsigned i; mps_res_t res; symtab_size *= 2; symtab = malloc(sizeof(obj_t) * symtab_size); if(symtab == NULL) error("out of memory"); /* Initialize the new table to NULL so that "find" will work. */ for(i = 0; i < symtab_size; ++i) symtab[i] = NULL; /* Once the symbol table is initialized with scannable references (NULL in this case) we must register it as a root before we copy objects across from the old symbol table. The MPS might be moving objects in memory at any time, and will arrange that both copies are updated atomically to the mutator (this Scheme interpreter). */ res = mps_root_create_table(&symtab_root, arena, mps_rank_exact(), 0, (mps_addr_t *)symtab, symtab_size); if(res != MPS_RES_OK) error("Couldn't register new symtab root"); for(i = 0; i < old_symtab_size; ++i) if(old_symtab[i] != NULL) { obj_t *where = find(old_symtab[i]->symbol.string); assert(where != NULL); /* new table shouldn't be full */ assert(*where == NULL); /* shouldn't be in new table */ *where = old_symtab[i]; } mps_root_destroy(old_symtab_root); free(old_symtab); } /* union-find string in symbol table, rehashing if necessary */ static obj_t intern(char *string) { obj_t *where; where = find(string); if(where == NULL) { rehash(); where = find(string); assert(where != NULL); /* shouldn't be full after rehash */ } if(*where == NULL) /* symbol not found in table */ *where = make_symbol(strlen(string), string); return *where; } static void print(obj_t obj, unsigned depth, FILE *stream) { switch(TYPE(obj)) { case TYPE_INTEGER: { fprintf(stream, "%ld", obj->integer.integer); } break; case TYPE_SYMBOL: { fputs(obj->symbol.string, stream); } break; case TYPE_SPECIAL: { fputs(obj->special.name, stream); } break; case TYPE_PORT: { assert(TYPE(obj->port.name) == TYPE_STRING); fprintf(stream, "#[port \"%s\"]", obj->port.name->string.string); } break; case TYPE_STRING: { size_t i; putc('"', stream); for(i = 0; i < obj->string.length; ++i) { char c = obj->string.string[i]; switch(c) { case '\\': fputs("\\\\", stream); break; case '"': fputs("\\\"", stream); break; default: putc(c, stream); break; } } putc('"', stream); } break; case TYPE_PROMISE: { assert(CAR(obj) == obj_true || CAR(obj) == obj_false); fprintf(stream, "#[%sevaluated promise ", CAR(obj) == obj_false ? "un" : ""); print(CDR(obj), depth - 1, stream); putc(']', stream); } break; case TYPE_PAIR: { if(TYPE(CAR(obj)) == TYPE_SYMBOL && TYPE(CDR(obj)) == TYPE_PAIR && CDDR(obj) == obj_empty) { if(CAR(obj) == obj_quote) { putc('\'', stream); if(depth == 0) fputs("...", stream); else print(CADR(obj), depth - 1, stream); break; } if(CAR(obj) == obj_quasiquote) { putc('`', stream); if(depth == 0) fputs("...", stream); else print(CADR(obj), depth - 1, stream); break; } if(CAR(obj) == obj_unquote) { putc(',', stream); if(depth == 0) fputs("...", stream); else print(CADR(obj), depth - 1, stream); break; } if(CAR(obj) == obj_unquote_splic) { fputs(",@", stream); if(depth == 0) fputs("...", stream); else print(CADR(obj), depth - 1, stream); break; } } putc('(', stream); if(depth == 0) fputs("...", stream); else { for(;;) { print(CAR(obj), depth - 1, stream); obj = CDR(obj); if(TYPE(obj) != TYPE_PAIR) break; putc(' ', stream); } if(obj != obj_empty) { fputs(" . ", stream); print(obj, depth - 1, stream); } } putc(')', stream); } break; case TYPE_VECTOR: { fputs("#(", stream); if(depth == 0) fputs("...", stream); else { size_t i; for(i = 0; i < obj->vector.length; ++i) { if(i > 0) putc(' ', stream); print(obj->vector.vector[i], depth - 1, stream); } } putc(')', stream); } break; case TYPE_OPERATOR: { fprintf(stream, "#[operator \"%s\" %p %p ", obj->operator.name, (void *)obj, (void *)obj->operator.entry); if(depth == 0) fputs("...", stream); else { print(obj->operator.arguments, depth - 1, stream); putc(' ', stream); print(obj->operator.body, depth - 1, stream); putc(' ', stream); print(obj->operator.env, depth - 1, stream); putc(' ', stream); print(obj->operator.op_env, depth - 1, stream); } putc(']', stream); } break; case TYPE_CHARACTER: { fprintf(stream, "#\\%c", obj->character.c); } break; default: assert(0); abort(); } } static obj_t read_integer(FILE *stream, int c) { long integer = 0; do { integer = integer*10 + c-'0'; c = getc(stream); } while(isdigit(c)); ungetc(c, stream); return make_integer(integer); } static obj_t read_symbol(FILE *stream, int c) { int length = 0; char string[SYMMAX+1]; do { string[length++] = tolower(c); c = getc(stream); } while(length < SYMMAX && (isalnum(c) || isealpha(c))); if(isalnum(c) || isealpha(c)) error("read: symbol too long"); string[length] = '\0'; ungetc(c, stream); return intern(string); } static obj_t read_string(FILE *stream, int c) { int length = 0; char string[STRMAX+1]; for(;;) { c = getc(stream); if(c == EOF) error("read: end of file during string"); if(c == '"') break; if(length >= STRMAX) error("read: string too long"); if(c == '\\') { c = getc(stream); switch(c) { case '\\': break; case '"': break; case 'n': c = '\n'; break; case 't': c = '\t'; break; case EOF: error("read: end of file in escape sequence in string"); default: error("read: unknown escape '%c'", c); } } string[length++] = c; } string[length] = '\0'; return make_string(length, string); } static obj_t read(FILE *stream); static obj_t read_quote(FILE *stream, int c) { return make_pair(obj_quote, make_pair(read(stream), obj_empty)); } static obj_t read_quasiquote(FILE *stream, int c) { return make_pair(obj_quasiquote, make_pair(read(stream), obj_empty)); } static obj_t read_unquote(FILE *stream, int c) { c = getc(stream); if(c == '@') return make_pair(obj_unquote_splic, make_pair(read(stream), obj_empty)); ungetc(c, stream); return make_pair(obj_unquote, make_pair(read(stream), obj_empty)); } static obj_t read_list(FILE *stream, int c) { obj_t list, new, end; list = obj_empty; for(;;) { c = getnbc(stream); if(c == ')' || c == '.') break; ungetc(c, stream); new = make_pair(read(stream), obj_empty); if(list == obj_empty) { list = new; end = new; } else { CDR(end) = new; end = new; } } if(c == '.') { if(list == obj_empty) error("read: unexpected dot"); CDR(end) = read(stream); c = getnbc(stream); } if(c != ')') error("read: expected close parenthesis"); return list; } static obj_t list_to_vector(obj_t list) { size_t i; obj_t l, vector; i = 0; l = list; while(TYPE(l) == TYPE_PAIR) { ++i; l = CDR(l); } if(l != obj_empty) return obj_error; vector = make_vector(i, obj_undefined); i = 0; l = list; while(TYPE(l) == TYPE_PAIR) { vector->vector.vector[i] = CAR(l); ++i; l = CDR(l); } return vector; } static obj_t read_special(FILE *stream, int c) { c = getnbc(stream); switch(tolower(c)) { case 't': return obj_true; case 'f': return obj_false; case '\\': { /* character (R4RS 6.6) */ c = getc(stream); if(c == EOF) error("read: end of file reading character literal"); return make_character(c); } case '(': { /* vector (R4RS 6.8) */ obj_t list = read_list(stream, c); obj_t vector = list_to_vector(list); if(vector == obj_error) error("read: illegal vector syntax"); return vector; } } error("read: unknown special '%c'", c); return obj_error; } static obj_t read(FILE *stream) { int c; c = getnbc(stream); if(c == EOF) return obj_eof; if(isdigit(c)) return read_integer(stream, c); switch(c) { case '\'': return read_quote(stream, c); case '`': return read_quasiquote(stream, c); case ',': return read_unquote(stream, c); case '(': return read_list(stream, c); case '#': return read_special(stream, c); case '"': return read_string(stream, c); case '-': case '+': { int next = getc(stream); if(isdigit(next)) { obj_t integer = read_integer(stream, next); if(c == '-') integer->integer.integer = -integer->integer.integer; return integer; } ungetc(next, stream); } break; /* fall through to read as symbol */ } if(isalpha(c) || isealpha(c)) return read_symbol(stream, c); error("read: illegal char '%c'", c); return obj_error; } /* lookup_in_frame -- look up a symbol in single frame * * Search a single frame of the environment for a symbol binding. */ static obj_t lookup_in_frame(obj_t frame, obj_t symbol) { while(frame != obj_empty) { assert(TYPE(frame) == TYPE_PAIR); assert(TYPE(CAR(frame)) == TYPE_PAIR); assert(TYPE(CAAR(frame)) == TYPE_SYMBOL); if(CAAR(frame) == symbol) return CAR(frame); frame = CDR(frame); } return obj_undefined; } /* lookup -- look up symbol in environment * * Search an entire environment for a binding of a symbol. */ static obj_t lookup(obj_t env, obj_t symbol) { obj_t binding; while(env != obj_empty) { assert(TYPE(env) == TYPE_PAIR); binding = lookup_in_frame(CAR(env), symbol); if(binding != obj_undefined) return binding; env = CDR(env); } return obj_undefined; } /* define -- define symbol in environment * * In Scheme, define will actually rebind (i.e. set) a symbol in the * same frame of the environment, or add a binding if it wasn't already * set. This has the effect of making bindings local to functions * (see how entry_interpret adds an empty frame to the environments), * allowing recursion, and allowing redefinition at the top level. * See R4R2 section 5.2 for details. */ static void define(obj_t env, obj_t symbol, obj_t value) { obj_t binding; assert(TYPE(env) == TYPE_PAIR); /* always at least one frame */ binding = lookup_in_frame(CAR(env), symbol); if(binding != obj_undefined) CDR(binding) = value; else CAR(env) = make_pair(make_pair(symbol, value), CAR(env)); } static obj_t eval(obj_t env, obj_t op_env, obj_t exp); static obj_t eval(obj_t env, obj_t op_env, obj_t exp) { /* self-evaluating */ if(TYPE(exp) == TYPE_INTEGER || (TYPE(exp) == TYPE_SPECIAL && exp != obj_empty) || TYPE(exp) == TYPE_STRING || TYPE(exp) == TYPE_CHARACTER) return exp; /* symbol lookup */ if(TYPE(exp) == TYPE_SYMBOL) { obj_t binding = lookup(env, exp); if(binding == obj_undefined) error("eval: unbound symbol \"%s\"", exp->symbol.string); return CDR(binding); } /* apply operator or function */ if(TYPE(exp) == TYPE_PAIR) { obj_t operator; if(TYPE(CAR(exp)) == TYPE_SYMBOL) { obj_t binding = lookup(op_env, CAR(exp)); if(binding != obj_undefined) { operator = CDR(binding); assert(TYPE(operator) == TYPE_OPERATOR); return (*operator->operator.entry)(env, op_env, operator, CDR(exp)); } } operator = eval(env, op_env, CAR(exp)); unless(TYPE(operator) == TYPE_OPERATOR) error("eval: application of non-function"); return (*operator->operator.entry)(env, op_env, operator, CDR(exp)); } error("eval: unknown syntax"); return obj_error; } /* OPERATOR UTILITIES */ /* eval_list -- evaluate list of expressions giving list of results * * eval_list evaluates a list of expresions and yields a list of their * results, in order. If the list is badly formed, an error is thrown * using the message given. */ static obj_t eval_list(obj_t env, obj_t op_env, obj_t list, char *message) { obj_t result, end, pair; result = obj_empty; while(list != obj_empty) { if(TYPE(list) != TYPE_PAIR) error(message); pair = make_pair(eval(env, op_env, CAR(list)), obj_empty); if(result == obj_empty) result = pair; else CDR(end) = pair; end = pair; list = CDR(list); } return result; } /* eval_args1 -- evaluate some operator arguments * * See eval_args and eval_args_rest for usage. */ static obj_t eval_args1(char *name, obj_t env, obj_t op_env, obj_t operands, unsigned n, va_list args) { unsigned i; for(i = 0; i < n; ++i) { unless(TYPE(operands) == TYPE_PAIR) error("eval: too few arguments to %s", name); *va_arg(args, obj_t *) = eval(env, op_env, CAR(operands)); operands = CDR(operands); } return operands; } /* eval_args -- evaluate operator arguments without rest list * * eval_args evaluates the first "n" expressions from the list of * expressions in "operands", returning the rest of the operands * unevaluated. It puts the results of evaluation in the addresses * passed in the vararg list. If the operands list is badly formed * an error is thrown using the operator name passed. For example: * * eval_args("foo", env, op_env, operands, 2, &arg1, &arg2); */ static void eval_args(char *name, obj_t env, obj_t op_env, obj_t operands, unsigned n, ...) { va_list args; va_start(args, n); operands = eval_args1(name, env, op_env, operands, n, args); unless(operands == obj_empty) error("eval: too many arguments to %s", name); va_end(args); } /* eval_args_rest -- evaluate operator arguments with rest list * * eval_args_rest evaluates the first "n" expressions from the list of * expressions in "operands", then evaluates the rest of the operands * using eval_list and puts the result at *restp. It puts the results * of evaluating the first "n" operands in the addresses * passed in the vararg list. If the operands list is badly formed * an error is thrown using the operator name passed. For example: * * eval_args_rest("foo", env, op_env, operands, &rest, 2, &arg1, &arg2); */ static void eval_args_rest(char *name, obj_t env, obj_t op_env, obj_t operands, obj_t *restp, unsigned n, ...) { va_list args; va_start(args, n); operands = eval_args1(name, env, op_env, operands, n, args); va_end(args); *restp = eval_list(env, op_env, operands, "eval: badly formed argument list"); } /* BUILT-IN OPERATORS */ /* entry_interpret -- interpreted function entry point * * When a function is made using lambda (see entry_lambda) an operator * is created with entry_interpret as its entry point, and the arguments * and body of the function. The entry_interpret function evaluates * the operands of the function and binds them to the argument names * in a new frame added to the lambda's closure environment. It then * evaluates the body in that environment, executing the function. */ static obj_t entry_interpret(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arguments, fun_env, fun_op_env; assert(TYPE(operator) == TYPE_OPERATOR); /* Make a new frame so that bindings are local to the function. */ /* Arguments will be bound in this new frame. */ fun_env = make_pair(obj_empty, operator->operator.env); fun_op_env = make_pair(obj_empty, operator->operator.op_env); arguments = operator->operator.arguments; while(operands != obj_empty) { if(arguments == obj_empty) error("eval: function applied to too many arguments"); if(TYPE(arguments) == TYPE_SYMBOL) { define(fun_env, arguments, eval_list(env, op_env, operands, "eval: badly formed argument list")); operands = obj_empty; arguments = obj_empty; } else { assert(TYPE(arguments) == TYPE_PAIR && TYPE(CAR(arguments)) == TYPE_SYMBOL); define(fun_env, CAR(arguments), eval(env, op_env, CAR(operands))); operands = CDR(operands); arguments = CDR(arguments); } } if(arguments != obj_empty) error("eval: function applied to too few arguments"); return eval(fun_env, fun_op_env, operator->operator.body); } /* entry_quote -- return operands unevaluated * * In Scheme, (quote foo) evaluates to foo (i.e. foo is not evaluated). * See R4RS 4.1.2. The reader expands "'x" to "(quote x)". */ static obj_t entry_quote(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { unless(TYPE(operands) == TYPE_PAIR && CDR(operands) == obj_empty) error("%s: illegal syntax", operator->operator.name); return CAR(operands); } /* entry_define -- bind a symbol in the top frame of the environment * * In Scheme, "(define <symbol> <expression>)" evaluates expressions * and binds it to symbol in the top frame of the environment (see * R4RS 5.2). This code also allows the non-essential syntax for * define, "(define (<symbol> <formals>) <body>)" as a short-hand for * "(define <symbol> (lambda (<formals>) <body>))". */ static obj_t entry_define(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t symbol, value; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR && CDDR(operands) == obj_empty) error("%s: illegal syntax", operator->operator.name); if(TYPE(CAR(operands)) == TYPE_SYMBOL) { symbol = CAR(operands); value = eval(env, op_env, CADR(operands)); } else if(TYPE(CAR(operands)) == TYPE_PAIR && TYPE(CAAR(operands)) == TYPE_SYMBOL) { symbol = CAAR(operands); value = eval(env, op_env, make_pair(obj_lambda, make_pair(CDAR(operands), CDR(operands)))); } else error("%s: applied to binder", operator->operator.name); define(env, symbol, value); return symbol; } /* entry_if -- one- or two-armed conditional * * "(if <test> <consequent> <alternate>)" and "(if <test> <consequent>)". * See R4RS 4.1.5. */ static obj_t entry_if(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t test; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR && (CDDR(operands) == obj_empty || (TYPE(CDDR(operands)) == TYPE_PAIR && CDDDR(operands) == obj_empty))) error("%s: illegal syntax", operator->operator.name); test = eval(env, op_env, CAR(operands)); /* Anything which is not #f counts as true [R4RS 6.1]. */ if(test != obj_false) return eval(env, op_env, CADR(operands)); if(TYPE(CDDR(operands)) == TYPE_PAIR) return eval(env, op_env, CADDR(operands)); return obj_undefined; } /* entry_cond -- general conditional * * "(cond (<test1> <exp1.1> ...) (<test2> <exp2.1> ...) ... [(else <expe.1> ...)])" */ static obj_t entry_cond(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { unless(TYPE(operands) == TYPE_PAIR) error("%s: illegal syntax", operator->operator.name); while(TYPE(operands) == TYPE_PAIR) { obj_t clause = CAR(operands); obj_t result; unless(TYPE(clause) == TYPE_PAIR && TYPE(CDR(clause)) == TYPE_PAIR) error("%s: illegal clause syntax", operator->operator.name); if(CAR(clause) == obj_else) { unless(CDR(operands) == obj_empty) error("%s: else clause must come last", operator->operator.name); result = obj_true; } else result = eval(env, op_env, CAR(clause)); if(result != obj_false) { for(;;) { clause = CDR(clause); if(TYPE(clause) != TYPE_PAIR) break; result = eval(env, op_env, CAR(clause)); } if(clause != obj_empty) error("%s: illegal clause syntax", operator->operator.name); return result; } operands = CDR(operands); } return obj_undefined; } /* entry_and -- (and <test1> ...) */ static obj_t entry_and(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { while(TYPE(operands) == TYPE_PAIR) { obj_t test = eval(env, op_env, CAR(operands)); if(test == obj_false) return obj_false; operands = CDR(operands); } if(operands != obj_empty) error("%s: illegal syntax", operator->operator.name); return obj_true; } /* entry_or -- (or <test1> ...) */ static obj_t entry_or(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { while(TYPE(operands) == TYPE_PAIR) { obj_t test = eval(env, op_env, CAR(operands)); if(test != obj_false) return obj_true; operands = CDR(operands); } if(operands != obj_empty) error("%s: illegal syntax", operator->operator.name); return obj_false; } /* entry_let -- (let <bindings> <body>) */ /* @@@@ Too much common code with let* */ static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t inner_env, bindings, result; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR) error("%s: illegal syntax", operator->operator.name); inner_env = make_pair(obj_empty, env); /* @@@@ common with interpret */ bindings = CAR(operands); while(TYPE(bindings) == TYPE_PAIR) { obj_t binding = CAR(bindings); unless(TYPE(binding) == TYPE_PAIR && TYPE(CAR(binding)) == TYPE_SYMBOL && TYPE(CDR(binding)) == TYPE_PAIR && CDDR(binding) == obj_empty) error("%s: illegal binding", operator->operator.name); define(inner_env, CAR(binding), eval(env, op_env, CADR(binding))); bindings = CDR(bindings); } if(bindings != obj_empty) error("%s: illegal bindings list", operator->operator.name); operands = CDR(operands); while(TYPE(operands) == TYPE_PAIR) { result = eval(inner_env, op_env, CAR(operands)); operands = CDR(operands); } if(operands != obj_empty) error("%s: illegal expression list", operator->operator.name); return result; } /* entry_let_star -- (let* <bindings> <body>) */ /* @@@@ Too much common code with let */ static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t inner_env, bindings, result; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR) error("%s: illegal syntax", operator->operator.name); inner_env = make_pair(obj_empty, env); /* @@@@ common with interpret */ bindings = CAR(operands); while(TYPE(bindings) == TYPE_PAIR) { obj_t binding = CAR(bindings); unless(TYPE(binding) == TYPE_PAIR && TYPE(CAR(binding)) == TYPE_SYMBOL && TYPE(CDR(binding)) == TYPE_PAIR && CDDR(binding) == obj_empty) error("%s: illegal binding", operator->operator.name); define(inner_env, CAR(binding), eval(inner_env, op_env, CADR(binding))); bindings = CDR(bindings); } if(bindings != obj_empty) error("%s: illegal bindings list", operator->operator.name); operands = CDR(operands); while(TYPE(operands) == TYPE_PAIR) { result = eval(inner_env, op_env, CAR(operands)); operands = CDR(operands); } if(operands != obj_empty) error("%s: illegal expression list", operator->operator.name); return result; } /* entry_letrec -- (letrec <bindings> <body>) */ /* @@@@ Too much common code with let and let* */ static obj_t entry_letrec(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t inner_env, bindings, result; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR) error("%s: illegal syntax", operator->operator.name); inner_env = make_pair(obj_empty, env); /* @@@@ common with interpret */ bindings = CAR(operands); while(TYPE(bindings) == TYPE_PAIR) { obj_t binding = CAR(bindings); unless(TYPE(binding) == TYPE_PAIR && TYPE(CAR(binding)) == TYPE_SYMBOL && TYPE(CDR(binding)) == TYPE_PAIR && CDDR(binding) == obj_empty) error("%s: illegal binding", operator->operator.name); define(inner_env, CAR(binding), obj_undefined); bindings = CDR(bindings); } if(bindings != obj_empty) error("%s: illegal bindings list", operator->operator.name); bindings = CAR(operands); while(TYPE(bindings) == TYPE_PAIR) { obj_t binding = CAR(bindings); define(inner_env, CAR(binding), eval(inner_env, op_env, CADR(binding))); bindings = CDR(bindings); } operands = CDR(operands); while(TYPE(operands) == TYPE_PAIR) { result = eval(inner_env, op_env, CAR(operands)); operands = CDR(operands); } if(operands != obj_empty) error("%s: illegal expression list", operator->operator.name); return result; } /* entry_do -- (do ((<var> <init> <step1>) ...) (<test> <exp> ...) <command> ...) */ static obj_t entry_do(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { error("%s: unimplemented", operator->operator.name); return obj_error; } /* entry_delay -- (delay <exp>) */ static obj_t entry_delay(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t promise; unless(TYPE(operands) == TYPE_PAIR && CDR(operands) == obj_empty) error("%s: illegal syntax", operator->operator.name); promise = make_pair(obj_false, make_operator("anonymous promise", entry_interpret, obj_empty, CAR(operands), env, op_env)); TYPE(promise) = TYPE_PROMISE; return promise; } /* entry_quasiquote -- (quasiquote <template>) or `<template> */ /* @@@@ blech. */ static obj_t entry_quasiquote(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t list, result = obj_empty, pair, end, insert; unless(TYPE(operands) == TYPE_PAIR && CDR(operands) == obj_empty) error("%s: illegal syntax", operator->operator.name); list = CAR(operands); while(TYPE(list) == TYPE_PAIR) { if(TYPE(CAR(list)) == TYPE_PAIR && TYPE(CAAR(list)) == TYPE_SYMBOL && (CAAR(list) == obj_unquote || CAAR(list) == obj_unquote_splic)) { unless(TYPE(CDAR(list)) == TYPE_PAIR && CDDAR(list) == obj_empty) error("%s: illegal %s syntax", operator->operator.name, CAAR(list)->symbol.string); insert = eval(env, op_env, CADAR(list)); if(CAAR(list) == obj_unquote) { pair = make_pair(insert, obj_empty); if(result == obj_empty) result = pair; else CDR(end) = pair; end = pair; } else if(CAAR(list) == obj_unquote_splic) { if(insert != obj_empty) { if(TYPE(insert) != TYPE_PAIR) error("%s: unquote-splicing expression must return list", operator->operator.name); if(result == obj_empty) result = insert; else CDR(end) = insert; while(TYPE(CDR(insert)) == TYPE_PAIR) insert = CDR(insert); if(CDR(insert) != obj_empty) error("%s: unquote-splicing expression must return list", operator->operator.name); end = insert; } } } else { pair = make_pair(CAR(list), obj_empty); if(result == obj_empty) result = pair; else CDR(end) = pair; end = pair; } list = CDR(list); } if(list != obj_empty) error("%s: illegal syntax", operator->operator.name); return result; } /* entry_set -- assignment * * (set! <variable> <expression>) * See R4RS 4.1.6. */ static obj_t entry_set(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t symbol, binding, value; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR && CDDR(operands) == obj_empty) error("%s: illegal syntax", operator->operator.name); unless(TYPE(CAR(operands)) == TYPE_SYMBOL) error("%s: applied to non-symbol", operator->operator.name); symbol = CAR(operands); binding = lookup(env, symbol); if(binding == obj_undefined) error("%s: applied to unbound symbol \"%s\"", operator->operator.name, symbol->symbol.string); value = eval(env, op_env, CADR(operands)); CDR(binding) = value; return value; } /* entry_lambda -- lambda expressions * * (lambda <formals> <body>) * See R4RS 4.1.4. * * The entry_lambda function creates a new object of TYPE_OPERATOR * which captures the current environments, and contains the lambda * formals and body. This operator has an entry point at * entry_interpret, which will evaluate and bind the arguments when * the operator is applied. * * [Capturing the whole environment is bad for GC because it means * that everything defined when the lambda is evaluated will survive * for as long as the operator survives. It would be better to * examine the lambda body and determine which variables it references, * and either create a new environment or build a new body with just * those variables bound.] */ static obj_t entry_lambda(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t list; unless(TYPE(operands) == TYPE_PAIR && TYPE(CDR(operands)) == TYPE_PAIR) error("%s: illegal syntax", operator->operator.name); /* check syntax of argument list to save time in apply */ list = CAR(operands); while(list != obj_empty && TYPE(list) != TYPE_SYMBOL) { unless(TYPE(list) == TYPE_PAIR && TYPE(CAR(list)) == TYPE_SYMBOL) error("%s: illegal argument list", operator->operator.name); list = CDR(list); } return make_operator("anonymous function", entry_interpret, CAR(operands), make_pair(obj_begin, CDR(operands)), env, op_env); } /* entry_begin -- sequencing * * (begin <expression1> <expression2> ...) * See R4RS 4.2.3. */ static obj_t entry_begin(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t result; do { unless(TYPE(operands) == TYPE_PAIR) error("%s: illegal syntax", operator->operator.name); result = eval(env, op_env, CAR(operands)); operands = CDR(operands); } while(operands != obj_empty); return result; } /* BUILT-IN FUNCTIONS */ /* entry_not -- (not <obj>) * * Not returns #t if obj is false, and return #f otherwise. R4RS 6.1. */ static obj_t entry_not(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return arg == obj_false ? obj_true : obj_false; } /* entry_booleanp -- (boolean? <obj>) * * Boolean? return #t if obj is either #t or #f, and #f otherwise. R4RS 6.1. */ static obj_t entry_booleanp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return arg == obj_true || arg == obj_false ? obj_true : obj_false; } /* entry_eqvp -- (eqv? <obj1> <obj2>) */ static int eqvp(obj_t obj1, obj_t obj2) { return obj1 == obj2 || (TYPE(obj1) == TYPE_INTEGER && TYPE(obj2) == TYPE_INTEGER && obj1->integer.integer == obj2->integer.integer); } static obj_t entry_eqvp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg1, arg2; eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2); return eqvp(arg1, arg2) ? obj_true : obj_false; } /* entry_eqp -- (eq? <obj1> <obj2>) */ static obj_t entry_eqp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg1, arg2; eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2); return arg1 == arg2 ? obj_true : obj_false; } /* entry_equalp -- (equal? <obj1> <obj2>) */ static int equalp(obj_t obj1, obj_t obj2) { if(TYPE(obj1) != TYPE(obj2)) return 0; if(TYPE(obj1) == TYPE_PAIR) return equalp(CAR(obj1), CAR(obj2)) && equalp(CDR(obj1), CDR(obj2)); /* @@@@ Similar recursion for vectors. */ return eqvp(obj1, obj2); } static obj_t entry_equalp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg1, arg2; eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2); return equalp(arg1, arg2) ? obj_true : obj_false; } /* entry_pairp -- (pair? <obj>) */ static obj_t entry_pairp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return TYPE(arg) == TYPE_PAIR ? obj_true : obj_false; } /* entry_cons -- create pair * * (cons <obj1> <obj2>) * See R4RS 6.3. * * Returns a newly allocated pair whose car is obj1 and whose cdr is obj2. * The pair is guaranteed to be different (in the sense of eqv?) from every * existing object. */ static obj_t entry_cons(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t car, cdr; eval_args(operator->operator.name, env, op_env, operands, 2, &car, &cdr); return make_pair(car, cdr); } /* entry_car -- R4RS 6.3 */ static obj_t entry_car(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t pair; eval_args(operator->operator.name, env, op_env, operands, 1, &pair); unless(TYPE(pair) == TYPE_PAIR) error("%s: argument must be a pair", operator->operator.name); return CAR(pair); } static obj_t entry_cdr(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t pair; eval_args(operator->operator.name, env, op_env, operands, 1, &pair); unless(TYPE(pair) == TYPE_PAIR) error("%s: argument must be a pair", operator->operator.name); return CDR(pair); } static obj_t entry_setcar(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t pair, value; eval_args(operator->operator.name, env, op_env, operands, 2, &pair, &value); unless(TYPE(pair) == TYPE_PAIR) error("%s: first argument must be a pair", operator->operator.name); CAR(pair) = value; return obj_undefined; } static obj_t entry_setcdr(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t pair, value; eval_args(operator->operator.name, env, op_env, operands, 2, &pair, &value); unless(TYPE(pair) == TYPE_PAIR) error("%s: first argument must be a pair", operator->operator.name); CDR(pair) = value; return obj_undefined; } static obj_t entry_nullp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return arg == obj_empty ? obj_true : obj_false; } static obj_t entry_listp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); while(TYPE(arg) == TYPE_PAIR) arg = CDR(arg); return arg == obj_empty ? obj_true : obj_false; } static obj_t entry_list(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t rest; eval_args_rest(operator->operator.name, env, op_env, operands, &rest, 0); return rest; } static obj_t entry_length(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; long length; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); length = 0; while(TYPE(arg) == TYPE_PAIR) { ++length; arg = CDR(arg); } if(arg != obj_empty) error("%s: applied to non-list", operator->operator.name); return make_integer(length); } static obj_t entry_append(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg1, arg2, result, pair, end; eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2); result = obj_empty; while(TYPE(arg1) == TYPE_PAIR) { pair = make_pair(CAR(arg1), obj_empty); if(result == obj_empty) result = pair; else CDR(end) = pair; end = pair; arg1 = CDR(arg1); } if(arg1 != obj_empty) error("%s: applied to non-list", operator->operator.name); CDR(end) = arg2; return result; } static obj_t entry_integerp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return TYPE(arg) == TYPE_INTEGER ? obj_true : obj_false; } static obj_t entry_zerop(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); unless(TYPE(arg) == TYPE_INTEGER) error("%s: argument must be an integer", operator->operator.name); return arg->integer.integer == 0 ? obj_true : obj_false; } static obj_t entry_positivep(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); unless(TYPE(arg) == TYPE_INTEGER) error("%s: argument must be an integer", operator->operator.name); return arg->integer.integer > 0 ? obj_true : obj_false; } static obj_t entry_negativep(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); unless(TYPE(arg) == TYPE_INTEGER) error("%s: argument must be an integer", operator->operator.name); return arg->integer.integer < 0 ? obj_true : obj_false; } static obj_t entry_symbolp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return TYPE(arg) == TYPE_SYMBOL ? obj_true : obj_false; } static obj_t entry_add(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t args; long result; eval_args_rest(operator->operator.name, env, op_env, operands, &args, 0); result = 0; while(TYPE(args) == TYPE_PAIR) { unless(TYPE(CAR(args)) == TYPE_INTEGER) error("%s: arguments must be integers", operator->operator.name); result += CAR(args)->integer.integer; args = CDR(args); } assert(args == obj_empty); /* eval_args_rest always returns a list */ return make_integer(result); } static obj_t entry_multiply(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t args; long result; eval_args_rest(operator->operator.name, env, op_env, operands, &args, 0); result = 1; while(TYPE(args) == TYPE_PAIR) { unless(TYPE(CAR(args)) == TYPE_INTEGER) error("%s: arguments must be integers", operator->operator.name); result *= CAR(args)->integer.integer; args = CDR(args); } assert(args == obj_empty); /* eval_args_rest always returns a list */ return make_integer(result); } static obj_t entry_subtract(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg, args; long result; eval_args_rest(operator->operator.name, env, op_env, operands, &args, 1, &arg); unless(TYPE(arg) == TYPE_INTEGER) error("%s: first argument must be an integer", operator->operator.name); result = arg->integer.integer; if(args == obj_empty) result = -result; else { while(TYPE(args) == TYPE_PAIR) { unless(TYPE(CAR(args)) == TYPE_INTEGER) error("%s: arguments must be integers", operator->operator.name); result -= CAR(args)->integer.integer; args = CDR(args); } assert(args == obj_empty); /* eval_args_rest always returns a list */ } return make_integer(result); } static obj_t entry_divide(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg, args; long result; eval_args_rest(operator->operator.name, env, op_env, operands, &args, 1, &arg); unless(TYPE(arg) == TYPE_INTEGER) error("%s: first argument must be an integer", operator->operator.name); result = arg->integer.integer; if(args == obj_empty) { if(result == 0) error("%s: reciprocal of zero", operator->operator.name); result = 1/result; /* @@@@ pretty meaningless for integers */ } else { while(TYPE(args) == TYPE_PAIR) { unless(TYPE(CAR(args)) == TYPE_INTEGER) error("%s: arguments must be integers", operator->operator.name); if(CAR(args)->integer.integer == 0) error("%s: divide by zero", operator->operator.name); result /= CAR(args)->integer.integer; args = CDR(args); } assert(args == obj_empty); /* eval_args_rest always returns a list */ } return make_integer(result); } static obj_t entry_reverse(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg, result; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); result = obj_empty; while(arg != obj_empty) { unless(TYPE(arg) == TYPE_PAIR) error("%s: argument must be a list", operator->operator.name); result = make_pair(CAR(arg), result); arg = CDR(arg); } return result; } static obj_t entry_environment(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { eval_args(operator->operator.name, env, op_env, operands, 0); return env; } static obj_t entry_open_in(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t filename; FILE *stream; eval_args(operator->operator.name, env, op_env, operands, 1, &filename); unless(TYPE(filename) == TYPE_STRING) error("%s: argument must be a string", operator->operator.name); stream = fopen(filename->string.string, "r"); if(stream == NULL) error("%s: cannot open input file", operator->operator.name); /* @@@@ return error */ return make_port(filename, stream); } /* @@@@ This doesn't work if the promise refers to its own value. */ static obj_t entry_force(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t promise; eval_args(operator->operator.name, env, op_env, operands, 1, &promise); unless(TYPE(promise) == TYPE_PROMISE) error("%s: argument must be a promise", operator->operator.name); assert(CAR(promise) == obj_false || CAR(promise) == obj_true); /* If the promise is unevaluated then apply the CDR. */ if(CAR(promise) == obj_false) { obj_t closure = CDR(promise); assert(TYPE(closure) == TYPE_OPERATOR); assert(closure->operator.arguments == obj_empty); CDR(promise) = (*closure->operator.entry)(env, op_env, closure, obj_empty); CAR(promise) = obj_true; } return CDR(promise); } static obj_t entry_vectorp(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t arg; eval_args(operator->operator.name, env, op_env, operands, 1, &arg); return TYPE(arg) == TYPE_VECTOR ? obj_true : obj_false; } static obj_t entry_make_vector(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t length, rest, fill; eval_args_rest(operator->operator.name, env, op_env, operands, &rest, 1, &length); unless(TYPE(length) == TYPE_INTEGER) error("%s: first argument must be an integer", operator->operator.name); if(rest == obj_empty) fill = obj_undefined; else { unless(CDR(rest) == obj_empty) error("%s: too many arguments", operator->operator.name); fill = CAR(rest); } return make_vector(length->integer.integer, fill); } static obj_t entry_vector(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t rest, vector; eval_args_rest(operator->operator.name, env, op_env, operands, &rest, 0); vector = list_to_vector(rest); assert(vector != obj_error); return vector; } static obj_t entry_vector_length(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t vector; eval_args(operator->operator.name, env, op_env, operands, 1, &vector); unless(TYPE(vector) == TYPE_VECTOR) error("%s: argument must be a vector", operator->operator.name); return make_integer(vector->vector.length); } static obj_t entry_vector_ref(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t vector, index; eval_args(operator->operator.name, env, op_env, operands, 2, &vector, &index); unless(TYPE(vector) == TYPE_VECTOR) error("%s: first argument must be a vector", operator->operator.name); unless(TYPE(index) == TYPE_INTEGER) error("%s: second argument must be an integer", operator->operator.name); unless(0 <= index->integer.integer && index->integer.integer < vector->vector.length) error("%s: index %ld out of bounds of vector length %ld", operator->operator.name, index->integer.integer, vector->vector.length); return vector->vector.vector[index->integer.integer]; } static obj_t entry_vector_set(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t vector, index, obj; eval_args(operator->operator.name, env, op_env, operands, 3, &vector, &index, &obj); unless(TYPE(vector) == TYPE_VECTOR) error("%s: first argument must be a vector", operator->operator.name); unless(TYPE(index) == TYPE_INTEGER) error("%s: second argument must be an integer", operator->operator.name); unless(0 <= index->integer.integer && index->integer.integer < vector->vector.length) error("%s: index %ld out of bounds of vector length %ld", operator->operator.name, index->integer.integer, vector->vector.length); vector->vector.vector[index->integer.integer] = obj; return obj_undefined; } static obj_t entry_vector_to_list(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t vector, list; size_t i; eval_args(operator->operator.name, env, op_env, operands, 1, &vector); unless(TYPE(vector) == TYPE_VECTOR) error("%s: argument must be a vector", operator->operator.name); list = obj_empty; i = vector->vector.length; while(i > 0) { --i; list = make_pair(vector->vector.vector[i], list); } return list; } static obj_t entry_list_to_vector(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t list, vector; eval_args(operator->operator.name, env, op_env, operands, 1, &list); vector = list_to_vector(list); if(vector == obj_error) error("%s: argument must be a list", operator->operator.name); return vector; } static obj_t entry_vector_fill(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t vector, obj; size_t i; eval_args(operator->operator.name, env, op_env, operands, 2, &vector, &obj); unless(TYPE(vector) == TYPE_VECTOR) error("%s: first argument must be a vector", operator->operator.name); for(i = 0; i < vector->vector.length; ++i) vector->vector.vector[i] = obj; return obj_undefined; } static obj_t entry_eval(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t exp; eval_args(operator->operator.name, env, op_env, operands, 1, &exp); return eval(env, op_env, exp); } static obj_t entry_symbol_to_string(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t symbol; eval_args(operator->operator.name, env, op_env, operands, 1, &symbol); unless(TYPE(symbol) == TYPE_SYMBOL) error("%s: argument must be a symbol", operator->operator.name); return make_string(symbol->symbol.length, symbol->symbol.string); } static obj_t entry_string_to_symbol(obj_t env, obj_t op_env, obj_t operator, obj_t operands) { obj_t string; eval_args(operator->operator.name, env, op_env, operands, 1, &string); unless(TYPE(string) == TYPE_STRING) error("%s: argument must be a string", operator->operator.name); /* @@@@ Should pass length to intern to avoid problems with NUL termination. */ return intern(string->string.string); } /* INITIALIZATION */ /* special table */ static struct {char *name; obj_t *varp;} sptab[] = { {"()", &obj_empty}, {"#[eof]", &obj_eof}, {"#[error]", &obj_error}, {"#t", &obj_true}, {"#f", &obj_false}, {"#[undefined]", &obj_undefined} }; /* initial symbol table */ static struct {char *name; obj_t *varp;} isymtab[] = { {"quote", &obj_quote}, {"lambda", &obj_lambda}, {"begin", &obj_begin}, {"else", &obj_else}, {"quasiquote", &obj_quasiquote}, {"unquote", &obj_unquote}, {"unquote-splicing", &obj_unquote_splic} }; /* operator table */ static struct {char *name; entry_t entry;} optab[] = { {"quote", entry_quote}, {"define", entry_define}, {"set!", entry_set}, {"lambda", entry_lambda}, {"begin", entry_begin}, {"cond", entry_cond}, {"if", entry_if}, {"and", entry_and}, {"or", entry_or}, {"let", entry_let}, {"let*", entry_let_star}, {"letrec", entry_letrec}, {"do", entry_do}, {"delay", entry_delay}, {"quasiquote", entry_quasiquote} }; /* function table */ static struct {char *name; entry_t entry;} funtab[] = { {"not", entry_not}, {"boolean?", entry_booleanp}, {"eqv?", entry_eqvp}, {"eq?", entry_eqp}, {"equal?", entry_equalp}, {"pair?", entry_pairp}, {"cons", entry_cons}, {"car", entry_car}, {"cdr", entry_cdr}, {"set-car!", entry_setcar}, {"set-cdr!", entry_setcdr}, {"null?", entry_nullp}, {"list?", entry_listp}, {"list", entry_list}, {"length", entry_length}, {"append", entry_append}, {"integer?", entry_integerp}, {"zero?", entry_zerop}, {"positive?", entry_positivep}, {"negative?", entry_negativep}, {"symbol?", entry_symbolp}, {"+", entry_add}, {"-", entry_subtract}, {"*", entry_multiply}, {"/", entry_divide}, {"reverse", entry_reverse}, {"the-environment", entry_environment}, {"open-input-file", entry_open_in}, {"force", entry_force}, {"vector?", entry_vectorp}, {"make-vector", entry_make_vector}, {"vector", entry_vector}, {"vector-length", entry_vector_length}, {"vector-ref", entry_vector_ref}, {"vector-set!", entry_vector_set}, {"vector->list", entry_vector_to_list}, {"list->vector", entry_list_to_vector}, {"vector-fill!", entry_vector_fill}, {"eval", entry_eval}, {"symbol->string", entry_symbol_to_string}, {"string->symbol", entry_string_to_symbol} }; /* MPS Format * * These functions satisfy the MPS Format Protocol for format variant "A". * * In general, MPS format methods are performance critical, as they're used * on the MPS [critical path](..\..\design\critical-path.txt). * * Format methods might also be called at any time from the MPS, including * in signal handlers, exception handlers, interrupts, or other special * contexts. They must avoid touching any memory except the object they're * asked about, and possibly some static volatile data. * * Because these methods are critical, there are considerable gains in * performance if you mix them with the MPS source code and allow the * compiler to optimize globally. See [Building the Memory Pool * System](../../manual/build.txt). */ /* obj_scan -- object format scanner * * The job of the scanner is to identify references in a contiguous group * of objects in memory. */ static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { #define FIX(ref) \ do { \ mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \ mps_res_t res = MPS_FIX12(ss, &_addr); \ if (res != MPS_RES_OK) return res; \ (ref) = _addr; \ } while(0) MPS_SCAN_BEGIN(ss) { while (base < limit) { obj_t obj = base; switch (obj->type.type) { case TYPE_PAIR: FIX(obj->pair.car); FIX(obj->pair.cdr); base = (char *)base + ALIGN(sizeof(pair_s)); break; case TYPE_INTEGER: base = (char *)base + ALIGN(sizeof(integer_s)); break; case TYPE_SYMBOL: base = (char *)base + ALIGN(offsetof(symbol_s, string) + obj->symbol.length + 1); break; case TYPE_SPECIAL: base = (char *)base + ALIGN(sizeof(special_s)); break; case TYPE_OPERATOR: FIX(obj->operator.arguments); FIX(obj->operator.body); FIX(obj->operator.env); FIX(obj->operator.op_env); base = (char *)base + ALIGN(sizeof(operator_s)); break; case TYPE_STRING: base = (char *)base + ALIGN(offsetof(string_s, string) + obj->string.length + 1); break; case TYPE_PORT: FIX(obj->port.name); base = (char *)base + ALIGN(sizeof(port_s)); break; case TYPE_CHARACTER: base = (char *)base + ALIGN(sizeof(character_s)); break; case TYPE_VECTOR: { size_t i; for (i = 0; i < obj->vector.length; ++i) FIX(obj->vector.vector[i]); } base = (char *)base + ALIGN(offsetof(vector_s, vector) + obj->vector.length * sizeof(obj->vector.vector[0])); break; case TYPE_FWD2: base = (char *)base + ALIGN(sizeof(fwd2_s)); break; case TYPE_FWD: base = (char *)base + ALIGN(obj->fwd.size); break; case TYPE_PAD1: base = (char *)base + ALIGN(sizeof(pad1_s)); break; default: assert(0); fprintf(stderr, "Unexpected object on the heap\n"); abort(); return MPS_RES_FAIL; } } } MPS_SCAN_END(ss); return MPS_RES_OK; } /* obj_skip -- object format skip method * * The job of skip is to return the address where the next object would * be allocated. This isn't quite the same as the size of the object, * since there may be some rounding according to the memory pool alignment * chosen. This Scheme has chosen to align to single words. */ static mps_addr_t obj_skip(mps_addr_t base) { obj_t obj = base; switch (obj->type.type) { case TYPE_PAIR: base = (char *)base + ALIGN(sizeof(pair_s)); break; case TYPE_INTEGER: base = (char *)base + ALIGN(sizeof(integer_s)); break; case TYPE_SYMBOL: base = (char *)base + ALIGN(offsetof(symbol_s, string) + obj->symbol.length + 1); break; case TYPE_SPECIAL: base = (char *)base + ALIGN(sizeof(special_s)); break; case TYPE_OPERATOR: base = (char *)base + ALIGN(sizeof(operator_s)); break; case TYPE_STRING: base = (char *)base + ALIGN(offsetof(string_s, string) + obj->string.length + 1); break; case TYPE_PORT: base = (char *)base + ALIGN(sizeof(port_s)); break; case TYPE_CHARACTER: base = (char *)base + ALIGN(sizeof(character_s)); break; case TYPE_VECTOR: base = (char *)base + ALIGN(offsetof(vector_s, vector) + obj->vector.length * sizeof(obj->vector.vector[0])); break; case TYPE_FWD2: base = (char *)base + ALIGN(sizeof(fwd2_s)); break; case TYPE_FWD: base = (char *)base + ALIGN(obj->fwd.size); break; case TYPE_PAD1: base = (char *)base + ALIGN(sizeof(pad1_s)); break; default: assert(0); fprintf(stderr, "Unexpected object on the heap\n"); abort(); return NULL; } return base; } /* obj_isfwd -- object format forwarded test * * The job of obj_isfwd is to detect whether an object has been replaced * by a forwarding object, and return the address of the new copy if it has, * otherwise NULL. Note that this will return NULL for padding objects * because their `fwd` field is set to NULL. */ static mps_addr_t obj_isfwd(mps_addr_t addr) { obj_t obj = addr; switch (obj->type.type) { case TYPE_FWD2: return obj->fwd2.fwd; case TYPE_FWD: return obj->fwd.fwd; } return NULL; } /* obj_fwd -- object format forwarding method * * The job of obj_fwd is to replace an object by a forwarding object that * points at a new copy of the object. The object must be detected by * `obj_isfwd`. In this case, we have to be careful to replace two-word * objects with a FWD2 object, because the FWD object won't fit. */ static void obj_fwd(mps_addr_t old, mps_addr_t new) { obj_t obj = old; mps_addr_t limit = obj_skip(old); size_t size = (char *)limit - (char *)old; assert(size >= ALIGN(sizeof(fwd2_s))); if (size == ALIGN(sizeof(fwd2_s))) { obj->type.type = TYPE_FWD2; obj->fwd2.fwd = new; } else { obj->type.type = TYPE_FWD; obj->fwd.fwd = new; obj->fwd.size = size; } } /* obj_pad -- object format padding method * * The job of obj_pad is to fill in a block of memory with a padding * object that will be skipped by `obj_scan` or `obj_skip` but does * nothing else. Because we've chosen to align to single words, we may * have to pad a single word, so we have a special single-word padding * object, PAD1 for that purpose. Otherwise we can use forwarding objects * with their `fwd` fields set to NULL. */ static void obj_pad(mps_addr_t addr, size_t size) { obj_t obj = addr; assert(size >= ALIGN(sizeof(pad1_s))); if (size == ALIGN(sizeof(pad1_s))) { obj->type.type = TYPE_PAD1; } else if (size == ALIGN(sizeof(fwd2_s))) { obj->type.type = TYPE_FWD2; obj->fwd2.fwd = NULL; } else { obj->type.type = TYPE_FWD; obj->fwd.fwd = NULL; obj->fwd.size = size; } } /* obj_copy -- object format copy method * * The job of obj_copy is to make a copy of an object. * TODO: Explain why this exists. */ static void obj_copy(mps_addr_t old, mps_addr_t new) { mps_addr_t limit = obj_skip(old); size_t size = (char *)limit - (char *)old; (void)memcpy(new, old, size); } /* start -- the main program * * This is the main body of the Scheme interpreter program, invoked by * `mps_tramp` so that its stack and exception handling can be managed * by the MPS. */ static void *start(void *p, size_t s) { size_t i; volatile obj_t env, op_env, obj; jmp_buf jb; mps_res_t res; puts("Scheme Test Harness"); total = (size_t)0; symtab_size = 16; symtab = malloc(sizeof(obj_t) * symtab_size); if(symtab == NULL) error("out of memory"); for(i = 0; i < symtab_size; ++i) symtab[i] = NULL; /* Note that since the symbol table is an exact root we must register it with the MPS only after it has been initialized with scannable pointers -- NULL in this case. Random values look like false references into MPS memory and cause undefined behaviour (most likely assertion failures). */ res = mps_root_create_table(&symtab_root, arena, mps_rank_exact(), 0, (mps_addr_t *)symtab, symtab_size); if(res != MPS_RES_OK) error("Couldn't register symtab root"); error_handler = &jb; if(!setjmp(*error_handler)) { for(i = 0; i < LENGTH(sptab); ++i) *sptab[i].varp = make_special(sptab[i].name); for(i = 0; i < LENGTH(isymtab); ++i) *isymtab[i].varp = intern(isymtab[i].name); env = make_pair(obj_empty, obj_empty); op_env = make_pair(obj_empty, obj_empty); for(i = 0; i < LENGTH(funtab); ++i) define(env, intern(funtab[i].name), make_operator(funtab[i].name, funtab[i].entry, obj_empty, obj_empty, env, op_env)); for(i = 0; i < LENGTH(optab); ++i) define(op_env, intern(optab[i].name), make_operator(optab[i].name, optab[i].entry, obj_empty, obj_empty, env, op_env)); } else { fprintf(stderr, "Fatal error during initialization: %s\n", error_message); abort(); } /* The read-eval-print loop */ for(;;) { if(setjmp(*error_handler) != 0) { fprintf(stderr, "%s\n", error_message); } printf("%lu> ", (unsigned long)total); obj = read(stdin); if(obj == obj_eof) break; obj = eval(env, op_env, obj); print(obj, 6, stdout); putc('\n', stdout); } puts("Bye."); return 0; } /* obj_fmt_s -- object format parameter structure * * This is simply a gathering of the object format methods and the chosen * pool alignment for passing to `mps_fmt_create_A`. */ struct mps_fmt_A_s obj_fmt_s = { sizeof(mps_word_t), obj_scan, obj_skip, obj_copy, obj_fwd, obj_isfwd, obj_pad }; /* main -- program entry point and MPS initialization */ int main(int argc, char *argv[]) { mps_res_t res; mps_chain_t obj_chain; mps_fmt_t obj_fmt; mps_gen_param_s obj_gen_params[] = { { 150, 0.85 }, { 170, 0.45 } }; mps_thr_t thread; mps_root_t reg_root; void *r; void *marker = &marker; /* Create an MPS arena. There is usually only one of these in a process. It holds all the MPS "global" state and is where everything happens. */ res = mps_arena_create(&arena, mps_arena_class_vm(), (size_t)(1024 * 1024)); if (res != MPS_RES_OK) error("Couldn't create arena"); /* Create the object format. */ res = mps_fmt_create_A(&obj_fmt, arena, &obj_fmt_s); if (res != MPS_RES_OK) error("Couldn't create obj format"); /* Create a chain controlling GC strategy. */ res = mps_chain_create(&obj_chain, arena, LENGTH(obj_gen_params), obj_gen_params); if (res != MPS_RES_OK) error("Couldn't create obj chain"); /* Create an Automatic Mostly-Copying (AMC) pool to manage the Scheme objects. This is a kind of copying garbage collector. */ res = mps_pool_create(&obj_pool, arena, mps_class_amc(), obj_fmt, obj_chain); if (res != MPS_RES_OK) error("Couldn't create obj pool"); /* Create an allocation point for fast in-line allocation of objects from the `obj_pool`. You'd usually want one of these per thread for your primary pools. This Scheme is single threaded, though, so we just have it in a global. */ res = mps_ap_create(&obj_ap, obj_pool, mps_rank_exact()); if (res != MPS_RES_OK) error("Couldn't create obj allocation point"); /* Register the current thread with the MPS. The MPS must sometimes control or examine threads to ensure consistency when it is scanning or updating object references, so any threads that access the MPS memory need to be registered. */ res = mps_thread_reg(&thread, arena); if (res != MPS_RES_OK) error("Couldn't register thread"); /* Register the thread as a root. This thread's stack and registers will need to be scanned by the MPS because we are passing references to objects around in C parameters, return values, and keeping them in automatic local variables. */ res = mps_root_create_reg(&reg_root, arena, mps_rank_ambig(), 0, thread, mps_stack_scan_ambig, marker, 0); if (res != MPS_RES_OK) error("Couldn't create root"); /* Trampoline into the main program. The MPS trampoline is unfortunately required to mark the top of the stack of the main thread, and on some platforms it must also catch exceptions in order to implement hardware memory barriers. */ mps_tramp(&r, start, NULL, 0); /* Cleaning up the MPS object with destroy methods will allow the MPS to check final consistency and warn you about bugs. It also allows the MPS to flush buffers for debugging data, etc. It's good practise to destroy MPS objects on exit if possible rather than just quitting. */ mps_root_destroy(reg_root); mps_thread_dereg(thread); mps_ap_destroy(obj_ap); mps_pool_destroy(obj_pool); mps_chain_destroy(obj_chain); mps_fmt_destroy(obj_fmt); mps_arena_destroy(arena); return 0; } /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2012 Ravenbrook Limited <http://www.ravenbrook.com/>. * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 3. Redistributions in any form must be accompanied by information on how * to obtain complete source code for this software and any accompanying * software that uses this software. The source code must either be * included in the distribution or be available for no more than the cost * of distribution plus a nominal fee, and must be freely redistributable * under reasonable conditions. For an executable file, complete source * code means the source code for all modules it contains. It does not * include source code for modules or files that typically accompany the * major components of the operating system on which the executable file * runs. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS AND 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. */ \ No newline at end of file 1/* scheme.c -- SCHEME INTERPRETER EXAMPLE FOR THE MEMORY POOL SYSTEM
2 *
3 * $Id$
4 * Copyright (c) 2001-2012 Ravenbrook Limited. See end of file for license.
5 *
6 * This is a toy interpreter for a subset of the Scheme programming
7 * language <http://en.wikipedia.org/wiki/Scheme_%28programming_language%29)>.
8 * It is by no means the best or even the right way to implement Scheme,
9 * but it serves the purpose of showing how the Memory Pool System can be
10 * used as part of a programming language run-time system.
11 *
12 * To try it out, "make scheme" then
13 *
14 * $ ./scheme
15 * (define (triangle n) (if (eqv? n 0) 0 (+ n (triangle (- n 1)))))
16 * (define (church n f a) (if (eqv? n 0) a (church (- n 1) f (f a))))
17 * (church 1000 triangle 0)
18 *
19 * This won't produce interesting results but it will cause a garbage
20 * collection cycles. Note that there's never any waiting for the MPS.
21 * THAT'S THE POINT.
22 *
23 *
24 * MPS TO DO LIST
25 * - make the symbol table weak to show how to use weak references
26 * - make Scheme ports finalized to show how to use finalization
27 * - add Scheme operators for talking to the MPS, forcing GC etc.
28 * - cross-references to documentation
29 * - make an mps_perror
30 *
31 *
32 * SCHEME TO DO LIST
33 * - unbounded integers, other number types.
34 * - do, named let.
35 * - Quasiquote implementation is messy.
36 * - Lots of library.
37 * - \#foo unsatisfactory in read and print
38 * - tail recursion (pass current function to eval)
39 */
40
41#include <stdio.h>
42#include <stdlib.h>
43#include <stddef.h>
44#include <stdarg.h>
45#include <ctype.h>
46#include <string.h>
47#include <assert.h>
48#include <setjmp.h>
49
50#include "mps.h"
51#include "mpsavm.h"
52#include "mpscamc.h"
53
54
55/* LANGUAGE EXTENSION */
56
57#define unless(c) if(!(c))
58#define LENGTH(array) (sizeof(array) / sizeof(array[0]))
59
60
61/* CONFIGURATION PARAMETERS */
62
63
64#define SYMMAX ((size_t)255) /* max length of a symbol */
65#define MSGMAX ((size_t)255) /* max length of error message */
66#define STRMAX ((size_t)255) /* max length of a string */
67
68
69/* DATA TYPES */
70
71
72/* obj_t -- scheme object type
73 *
74 * obj_t is a pointer to a union, obj_u, which has members for
75 * each scheme representation.
76 *
77 * The obj_u also has a "type" member. Each representation
78 * structure also has a "type" field first. ANSI C guarantees
79 * that these type fields correspond [section?].
80 *
81 * Objects are allocated by allocating one of the representation
82 * structures and casting the pointer to it to type obj_t. This
83 * allows objects of different sizes to be represented by the
84 * same type.
85 *
86 * To access an object, check its type by reading TYPE(obj), then
87 * access the fields of the representation, e.g.
88 * if(TYPE(obj) == TYPE_PAIR) fiddle_with(CAR(obj));
89 */
90
91typedef union obj_u *obj_t;
92
93typedef obj_t (*entry_t)(obj_t env, obj_t op_env, obj_t operator, obj_t rands);
94
95typedef int type_t;
96enum {
97 TYPE_PAIR,
98 TYPE_INTEGER,
99 TYPE_SYMBOL,
100 TYPE_SPECIAL,
101 TYPE_OPERATOR,
102 TYPE_STRING,
103 TYPE_PORT,
104 TYPE_PROMISE,
105 TYPE_CHARACTER,
106 TYPE_VECTOR,
107 TYPE_FWD2, /* two-word broken heart */
108 TYPE_FWD, /* three-words and up broken heart */
109 TYPE_PAD1 /* one-word padding object */
110};
111
112typedef struct type_s {
113 type_t type;
114} type_s;
115
116typedef struct pair_s {
117 type_t type; /* TYPE_PAIR */
118 obj_t car, cdr; /* first and second projections */
119} pair_s;
120
121typedef struct symbol_s {
122 type_t type; /* TYPE_SYMBOL */
123 size_t length; /* length of symbol string (excl. NUL) */
124 char string[1]; /* symbol string, NUL terminated */
125} symbol_s;
126
127typedef struct integer_s {
128 type_t type; /* TYPE_INTEGER */
129 long integer; /* the integer */
130} integer_s;
131
132typedef struct special_s {
133 type_t type; /* TYPE_SPECIAL */
134 char *name; /* printed representation, NUL terminated */
135} special_s;
136
137typedef struct operator_s {
138 type_t type; /* TYPE_OPERATOR */
139 char *name; /* printed name, NUL terminated */
140 entry_t entry; /* entry point -- see eval() */
141 obj_t arguments, body; /* function arguments and code */
142 obj_t env, op_env; /* closure environments */
143} operator_s;
144
145typedef struct string_s {
146 type_t type; /* TYPE_STRING */
147 size_t length; /* number of chars in string */
148 char string[1]; /* string, NUL terminated */
149} string_s;
150
151typedef struct port_s {
152 type_t type; /* TYPE_PORT */
153 obj_t name; /* name of stream */
154 FILE *stream;
155} port_s;
156
157typedef struct character_s {
158 type_t type; /* TYPE_CHARACTER */
159 char c; /* the character */
160} character_s;
161
162typedef struct vector_s {
163 type_t type; /* TYPE_VECTOR */
164 size_t length; /* number of elements */
165 obj_t vector[1]; /* vector elements */
166} vector_s;
167
168
169/* fwd, fwd2, pad1 -- MPS forwarding and padding objects
170 *
171 * These object types are here to satisfy the MPS Format Protocol for
172 * format variant "A".
173 *
174 * The MPS needs to be able to replace any object with forwarding object
175 * or [broken heart](http://www.memorymanagement.org/glossary/b.html#broken.heart)
176 * and since the smallest normal object defined above is two words long,
177 * we have two kinds of forwarding objects: FWD2 is exactly two words
178 * long, and FWD stores a size for larger objects. There are cleverer
179 * ways to do this with bit twiddling, of course.
180 *
181 * The MPS needs to be able to pad out any area of memory that's a
182 * multiple of the pool alignment. We've chosen an single word alignment
183 * for this interpreter, so we have to have a special padding object, PAD1,
184 * for single words. For larger objects we can just use forwarding objects
185 * with NULL in their `fwd` fields. See `obj_isfwd` for details.
186 *
187 * See obj_pad, obj_fwd etc. to see how these are used.
188 */
189
190typedef struct fwd2_s {
191 type_t type; /* TYPE_FWD2 */
192 obj_t fwd; /* forwarded object */
193} fwd2_s;
194
195typedef struct fwd_s {
196 type_t type; /* TYPE_FWD */
197 obj_t fwd; /* forwarded object */
198 size_t size; /* total size of this object */
199} fwd_s;
200
201typedef struct pad1_s {
202 type_t type; /* TYPE_PAD1 */
203} pad1_s;
204
205
206typedef union obj_u {
207 type_s type; /* one of TYPE_* */
208 pair_s pair;
209 symbol_s symbol;
210 integer_s integer;
211 special_s special;
212 operator_s operator;
213 string_s string;
214 port_s port;
215 character_s character;
216 vector_s vector;
217 fwd2_s fwd2;
218 fwd_s fwd;
219} obj_s;
220
221
222/* structure macros */
223
224#define TYPE(obj) ((obj)->type.type)
225#define CAR(obj) ((obj)->pair.car)
226#define CDR(obj) ((obj)->pair.cdr)
227#define CAAR(obj) CAR(CAR(obj))
228#define CADR(obj) CAR(CDR(obj))
229#define CDAR(obj) CDR(CAR(obj))
230#define CDDR(obj) CDR(CDR(obj))
231#define CADDR(obj) CAR(CDDR(obj))
232#define CDDDR(obj) CDR(CDDR(obj))
233#define CDDAR(obj) CDR(CDAR(obj))
234#define CADAR(obj) CAR(CDAR(obj))
235
236
237/* GLOBAL DATA */
238
239
240/* total -- total allocated bytes */
241
242static size_t total;
243
244
245/* symtab -- symbol table
246 *
247 * The symbol table is a hash-table containing objects of TYPE_SYMBOL.
248 * When a string is "interned" it is looked up in the table, and added
249 * only if it is not there. This guarantees that all symbols which
250 * are equal are actually the same object.
251 *
252 * The symbol table is simply a malloc'd array of obj_t pointers. Since
253 * it's outside the MPS and refers to objects we want the MPS to keep
254 * alive, it must be declared to the MPS as a root. Search for
255 * occurrences of `symtab_root` to see how this is done.
256 */
257
258static obj_t *symtab;
259static size_t symtab_size;
260static mps_root_t symtab_root;
261
262
263/* special objects
264 *
265 * These global variables are initialized to point to objects of
266 * TYPE_SPECIAL by main. They are used as markers for various
267 * special purposes.
268 */
269
270static obj_t obj_empty; /* (), the empty list */
271static obj_t obj_eof; /* end of file */
272static obj_t obj_error; /* error indicator */
273static obj_t obj_true; /* #t, boolean true */
274static obj_t obj_false; /* #f, boolean false */
275static obj_t obj_undefined; /* undefined result indicator */
276
277
278/* predefined symbols
279 *
280 * These global variables are initialized to point to interned
281 * objects of TYPE_SYMBOL. They have special meaning in the
282 * Scheme language, and are used by the evaluator to parse code.
283 */
284
285static obj_t obj_quote; /* "quote" symbol */
286static obj_t obj_quasiquote; /* "quasiquote" symbol */
287static obj_t obj_lambda; /* "lambda" symbol */
288static obj_t obj_begin; /* "begin" symbol */
289static obj_t obj_else; /* "else" symbol */
290static obj_t obj_unquote; /* "unquote" symbol */
291static obj_t obj_unquote_splic; /* "unquote-splicing" symbol */
292
293
294/* error handler
295 *
296 * The error_handler variable is initialized to point at a
297 * jmp_buf to which the "error" function longjmps if there is
298 * any kind of error during evaluation. It can be set up by
299 * any enclosing function that wants to catch errors. There
300 * is a default error handler in main, in the read-eval-print
301 * loop. The error function also writes an error message
302 * into "error_message" before longjmping, and this can be
303 * displayed to the user when catching the error.
304 *
305 * [An error code should also be passed so that the error can
306 * be decoded by enclosing code.]
307 */
308
309static jmp_buf *error_handler;
310static char error_message[MSGMAX+1];
311
312
313/* MPS pools */
314
315mps_arena_t arena;
316mps_pool_t obj_pool;
317mps_ap_t obj_ap;
318
319
320/* SUPPORT FUNCTIONS */
321
322
323/* error -- throw an error condition
324 *
325 * The "error" function takes a printf-style format string
326 * and arguments, writes the message into error_message and
327 * longjmps to *error_handler. There must be a setjmp at
328 * the other end to catch the condition and display the
329 * message.
330 */
331
332static void error(char *format, ...)
333{
334 va_list args;
335
336 assert(error_handler != NULL);
337
338 va_start(args, format);
339 vsprintf(error_message, format, args);
340 va_end(args);
341
342 longjmp(*error_handler, 1);
343}
344
345
346/* make_* -- object constructors
347 *
348 * Each object type has a function here that allocates an instance of
349 * that type.
350 *
351 * These functions illustrate the two-phase MPS Allocation Point Protocol
352 * with `reserve` and `commmit`. This protocol allows very fast in-line
353 * allocation without locking, but there is a very tiny chance that the
354 * object must be re-initialized. In nearly all cases, however, it's
355 * just a pointer bump.
356 */
357
358#define ALIGN(size) \
359 (((size) + sizeof(mps_word_t) - 1) & ~(sizeof(mps_word_t) - 1))
360
361static obj_t make_pair(obj_t car, obj_t cdr)
362{
363 obj_t obj;
364 mps_addr_t addr;
365 size_t size = ALIGN(sizeof(pair_s));
366 do {
367 mps_res_t res = mps_reserve(&addr, obj_ap, size);
368 if (res != MPS_RES_OK) error("out of memory in make_pair");
369 obj = addr;
370 obj->pair.type = TYPE_PAIR;
371 CAR(obj) = car;
372 CDR(obj) = cdr;
373 } while(!mps_commit(obj_ap, addr, size));
374 total += sizeof(pair_s);
375 return obj;
376}
377
378static obj_t make_integer(long integer)
379{
380 obj_t obj;
381 mps_addr_t addr;
382 size_t size = ALIGN(sizeof(integer_s));
383 do {
384 mps_res_t res = mps_reserve(&addr, obj_ap, size);
385 if (res != MPS_RES_OK) error("out of memory in make_integer");
386 obj = addr;
387 obj->integer.type = TYPE_INTEGER;
388 obj->integer.integer = integer;
389 } while(!mps_commit(obj_ap, addr, size));
390 total += sizeof(integer_s);
391 return obj;
392}
393
394static obj_t make_symbol(size_t length, char string[])
395{
396 obj_t obj;
397 mps_addr_t addr;
398 size_t size = ALIGN(offsetof(symbol_s, string) + length+1);
399 do {
400 mps_res_t res = mps_reserve(&addr, obj_ap, size);
401 if (res != MPS_RES_OK) error("out of memory in make_symbol");
402 obj = addr;
403 obj->symbol.type = TYPE_SYMBOL;
404 obj->symbol.length = length;
405 memcpy(obj->symbol.string, string, length+1);
406 } while(!mps_commit(obj_ap, addr, size));
407 total += size;
408 return obj;
409}
410
411static obj_t make_string(size_t length, char string[])
412{
413 obj_t obj;
414 mps_addr_t addr;
415 size_t size = ALIGN(offsetof(string_s, string) + length+1);
416 do {
417 mps_res_t res = mps_reserve(&addr, obj_ap, size);
418 if (res != MPS_RES_OK) error("out of memory in make_string");
419 obj = addr;
420 obj->string.type = TYPE_STRING;
421 obj->string.length = length;
422 memcpy(obj->string.string, string, length+1);
423 } while(!mps_commit(obj_ap, addr, size));
424 total += size;
425 return obj;
426}
427
428static obj_t make_special(char *string)
429{
430 obj_t obj;
431 mps_addr_t addr;
432 size_t size = ALIGN(sizeof(special_s));
433 do {
434 mps_res_t res = mps_reserve(&addr, obj_ap, size);
435 if (res != MPS_RES_OK) error("out of memory in make_special");
436 obj = addr;
437 obj->special.type = TYPE_SPECIAL;
438 obj->special.name = string;
439 } while(!mps_commit(obj_ap, addr, size));
440 total += sizeof(special_s);
441 return obj;
442}
443
444static obj_t make_operator(char *name,
445 entry_t entry, obj_t arguments,
446 obj_t body, obj_t env, obj_t op_env)
447{
448 obj_t obj;
449 mps_addr_t addr;
450 size_t size = ALIGN(sizeof(operator_s));
451 do {
452 mps_res_t res = mps_reserve(&addr, obj_ap, size);
453 if (res != MPS_RES_OK) error("out of memory in make_operator");
454 obj = addr;
455 obj->operator.type = TYPE_OPERATOR;
456 obj->operator.name = name;
457 obj->operator.entry = entry;
458 obj->operator.arguments = arguments;
459 obj->operator.body = body;
460 obj->operator.env = env;
461 obj->operator.op_env = op_env;
462 } while(!mps_commit(obj_ap, addr, size));
463 total += sizeof(operator_s);
464 return obj;
465}
466
467static obj_t make_port(obj_t name, FILE *stream)
468{
469 obj_t obj;
470 mps_addr_t addr;
471 size_t size = ALIGN(sizeof(port_s));
472 do {
473 mps_res_t res = mps_reserve(&addr, obj_ap, size);
474 if (res != MPS_RES_OK) error("out of memory in make_operator");
475 obj = addr;
476 obj->port.type = TYPE_PORT;
477 obj->port.name = name;
478 obj->port.stream = stream;
479 } while(!mps_commit(obj_ap, addr, size));
480 total += sizeof(port_s);
481 return obj;
482}
483
484static obj_t make_character(char c)
485{
486 obj_t obj;
487 mps_addr_t addr;
488 size_t size = ALIGN(sizeof(character_s));
489 do {
490 /* FIXME: Alignment! */
491 mps_res_t res = mps_reserve(&addr, obj_ap, size);
492 if (res != MPS_RES_OK) error("out of memory in make_character");
493 obj = addr;
494 obj->character.type = TYPE_CHARACTER;
495 obj->character.c = c;
496 } while(!mps_commit(obj_ap, addr, size));
497 total += sizeof(character_s);
498 return obj;
499}
500
501static obj_t make_vector(size_t length, obj_t fill)
502{
503 obj_t obj;
504 mps_addr_t addr;
505 size_t size = ALIGN(offsetof(vector_s, vector) + length * sizeof(obj_t));
506 do {
507 mps_res_t res = mps_reserve(&addr, obj_ap, size);
508 size_t i;
509 if (res != MPS_RES_OK) error("out of memory in make_vector");
510 obj = addr;
511 obj->vector.type = TYPE_VECTOR;
512 obj->vector.length = length;
513 for(i = 0; i < length; ++i)
514 obj->vector.vector[i] = fill;
515 } while(!mps_commit(obj_ap, addr, size));
516 total += size;
517 return obj;
518}
519
520
521/* getnbc -- get next non-blank char from stream */
522
523static int getnbc(FILE *stream)
524{
525 int c;
526 do
527 c = getc(stream);
528 while(isspace(c));
529 return c;
530}
531
532
533/* isealpha -- test for "extended alphabetic" char
534 *
535 * Scheme symbols may contain any "extended alphabetic"
536 * character (see section 2.1 of R4RS). This function
537 * returns non-zero if a character is in the set of
538 * extended characters.
539 */
540
541static int isealpha(int c)
542{
543 return strchr("+-.*/<=>!?:$%_&~^", c) != NULL;
544}
545
546
547/* hash -- hash a string to an unsigned long
548 *
549 * This hash function was derived (with permission) from
550 * Paul Haahr's hash in the most excellent rc 1.4.
551 */
552
553static unsigned long hash(const char *s) {
554 char c;
555 unsigned long h=0;
556
557 do {
558 c=*s++; if(c=='\0') break; else h+=(c<<17)^(c<<11)^(c<<5)^(c>>1);
559 c=*s++; if(c=='\0') break; else h^=(c<<14)+(c<<7)+(c<<4)+c;
560 c=*s++; if(c=='\0') break; else h^=(~c<<11)|((c<<3)^(c>>1));
561 c=*s++; if(c=='\0') break; else h-=(c<<16)|(c<<9)|(c<<2)|(c&3);
562 } while(c);
563
564 return h;
565}
566
567
568/* find -- find entry for symbol in symbol table
569 *
570 * Look for a symbol matching the string in the symbol table.
571 * If the symbol was found, returns the address of the symbol
572 * table entry which points to the symbol. Otherwise it
573 * either returns the address of a NULL entry into which the
574 * new symbol should be inserted, or NULL if the symbol table
575 * is full.
576 */
577
578static obj_t *find(char *string) {
579 unsigned long i, h;
580
581 h = hash(string) & (symtab_size-1);
582 i = h;
583 do {
584 if(symtab[i] == NULL ||
585 strcmp(string, symtab[i]->symbol.string) == 0)
586 return &symtab[i];
587 i = (i+h+1) & (symtab_size-1);
588 } while(i != h);
589
590 return NULL;
591}
592
593
594/* rehash -- double size of symbol table */
595
596static void rehash(void) {
597 obj_t *old_symtab = symtab;
598 unsigned old_symtab_size = symtab_size;
599 mps_root_t old_symtab_root = symtab_root;
600 unsigned i;
601 mps_res_t res;
602
603 symtab_size *= 2;
604 symtab = malloc(sizeof(obj_t) * symtab_size);
605 if(symtab == NULL) error("out of memory");
606
607 /* Initialize the new table to NULL so that "find" will work. */
608 for(i = 0; i < symtab_size; ++i)
609 symtab[i] = NULL;
610
611 /* Once the symbol table is initialized with scannable references (NULL
612 in this case) we must register it as a root before we copy objects
613 across from the old symbol table. The MPS might be moving objects
614 in memory at any time, and will arrange that both copies are updated
615 atomically to the mutator (this interpreter). */
616 res = mps_root_create_table(&symtab_root, arena, mps_rank_exact(), 0,
617 (mps_addr_t *)symtab, symtab_size);
618 if(res != MPS_RES_OK) error("Couldn't register new symtab root");
619
620 for(i = 0; i < old_symtab_size; ++i)
621 if(old_symtab[i] != NULL) {
622 obj_t *where = find(old_symtab[i]->symbol.string);
623 assert(where != NULL); /* new table shouldn't be full */
624 assert(*where == NULL); /* shouldn't be in new table */
625 *where = old_symtab[i];
626 }
627
628 mps_root_destroy(old_symtab_root);
629 free(old_symtab);
630}
631
632/* union-find string in symbol table, rehashing if necessary */
633static obj_t intern(char *string) {
634 obj_t *where;
635
636 where = find(string);
637
638 if(where == NULL) {
639 rehash();
640 where = find(string);
641 assert(where != NULL); /* shouldn't be full after rehash */
642 }
643
644 if(*where == NULL) /* symbol not found in table */
645 *where = make_symbol(strlen(string), string);
646
647 return *where;
648}
649
650
651static void print(obj_t obj, unsigned depth, FILE *stream)
652{
653 switch(TYPE(obj)) {
654 case TYPE_INTEGER: {
655 fprintf(stream, "%ld", obj->integer.integer);
656 } break;
657
658 case TYPE_SYMBOL: {
659 fputs(obj->symbol.string, stream);
660 } break;
661
662 case TYPE_SPECIAL: {
663 fputs(obj->special.name, stream);
664 } break;
665
666 case TYPE_PORT: {
667 assert(TYPE(obj->port.name) == TYPE_STRING);
668 fprintf(stream, "#[port \"%s\"]",
669 obj->port.name->string.string);
670 } break;
671
672 case TYPE_STRING: {
673 size_t i;
674 putc('"', stream);
675 for(i = 0; i < obj->string.length; ++i) {
676 char c = obj->string.string[i];
677 switch(c) {
678 case '\\': fputs("\\\\", stream); break;
679 case '"': fputs("\\\"", stream); break;
680 default: putc(c, stream); break;
681 }
682 }
683 putc('"', stream);
684 } break;
685
686 case TYPE_PROMISE: {
687 assert(CAR(obj) == obj_true || CAR(obj) == obj_false);
688 fprintf(stream, "#[%sevaluated promise ",
689 CAR(obj) == obj_false ? "un" : "");
690 print(CDR(obj), depth - 1, stream);
691 putc(']', stream);
692 } break;
693
694 case TYPE_PAIR: {
695 if(TYPE(CAR(obj)) == TYPE_SYMBOL &&
696 TYPE(CDR(obj)) == TYPE_PAIR &&
697 CDDR(obj) == obj_empty) {
698 if(CAR(obj) == obj_quote) {
699 putc('\'', stream);
700 if(depth == 0)
701 fputs("...", stream);
702 else
703 print(CADR(obj), depth - 1, stream);
704 break;
705 }
706 if(CAR(obj) == obj_quasiquote) {
707 putc('`', stream);
708 if(depth == 0)
709 fputs("...", stream);
710 else
711 print(CADR(obj), depth - 1, stream);
712 break;
713 }
714 if(CAR(obj) == obj_unquote) {
715 putc(',', stream);
716 if(depth == 0)
717 fputs("...", stream);
718 else
719 print(CADR(obj), depth - 1, stream);
720 break;
721 }
722 if(CAR(obj) == obj_unquote_splic) {
723 fputs(",@", stream);
724 if(depth == 0)
725 fputs("...", stream);
726 else
727 print(CADR(obj), depth - 1, stream);
728 break;
729 }
730 }
731 putc('(', stream);
732 if(depth == 0)
733 fputs("...", stream);
734 else {
735 for(;;) {
736 print(CAR(obj), depth - 1, stream);
737 obj = CDR(obj);
738 if(TYPE(obj) != TYPE_PAIR) break;
739 putc(' ', stream);
740 }
741 if(obj != obj_empty) {
742 fputs(" . ", stream);
743 print(obj, depth - 1, stream);
744 }
745 }
746 putc(')', stream);
747 } break;
748
749 case TYPE_VECTOR: {
750 fputs("#(", stream);
751 if(depth == 0)
752 fputs("...", stream);
753 else {
754 size_t i;
755 for(i = 0; i < obj->vector.length; ++i) {
756 if(i > 0) putc(' ', stream);
757 print(obj->vector.vector[i], depth - 1, stream);
758 }
759 }
760 putc(')', stream);
761 } break;
762
763 case TYPE_OPERATOR: {
764 fprintf(stream, "#[operator \"%s\" %p %p ",
765 obj->operator.name,
766 (void *)obj,
767 (void *)obj->operator.entry);
768 if(depth == 0)
769 fputs("...", stream);
770 else {
771 print(obj->operator.arguments, depth - 1, stream);
772 putc(' ', stream);
773 print(obj->operator.body, depth - 1, stream);
774 putc(' ', stream);
775 print(obj->operator.env, depth - 1, stream);
776 putc(' ', stream);
777 print(obj->operator.op_env, depth - 1, stream);
778 }
779 putc(']', stream);
780 } break;
781
782 case TYPE_CHARACTER: {
783 fprintf(stream, "#\\%c", obj->character.c);
784 } break;
785
786 default:
787 assert(0);
788 abort();
789 }
790}
791
792
793static obj_t read_integer(FILE *stream, int c)
794{
795 long integer = 0;
796
797 do {
798 integer = integer*10 + c-'0';
799 c = getc(stream);
800 } while(isdigit(c));
801 ungetc(c, stream);
802
803 return make_integer(integer);
804}
805
806
807static obj_t read_symbol(FILE *stream, int c)
808{
809 int length = 0;
810 char string[SYMMAX+1];
811
812 do {
813 string[length++] = tolower(c);
814 c = getc(stream);
815 } while(length < SYMMAX && (isalnum(c) || isealpha(c)));
816
817 if(isalnum(c) || isealpha(c))
818 error("read: symbol too long");
819
820 string[length] = '\0';
821
822 ungetc(c, stream);
823
824 return intern(string);
825}
826
827
828static obj_t read_string(FILE *stream, int c)
829{
830 int length = 0;
831 char string[STRMAX+1];
832
833 for(;;) {
834 c = getc(stream);
835 if(c == EOF)
836 error("read: end of file during string");
837 if(c == '"') break;
838 if(length >= STRMAX)
839 error("read: string too long");
840 if(c == '\\') {
841 c = getc(stream);
842 switch(c) {
843 case '\\': break;
844 case '"': break;
845 case 'n': c = '\n'; break;
846 case 't': c = '\t'; break;
847 case EOF:
848 error("read: end of file in escape sequence in string");
849 default:
850 error("read: unknown escape '%c'", c);
851 }
852 }
853 string[length++] = c;
854 }
855
856 string[length] = '\0';
857
858 return make_string(length, string);
859}
860
861
862static obj_t read(FILE *stream);
863
864
865static obj_t read_quote(FILE *stream, int c)
866{
867 return make_pair(obj_quote, make_pair(read(stream), obj_empty));
868}
869
870
871static obj_t read_quasiquote(FILE *stream, int c)
872{
873 return make_pair(obj_quasiquote, make_pair(read(stream), obj_empty));
874}
875
876
877static obj_t read_unquote(FILE *stream, int c)
878{
879 c = getc(stream);
880 if(c == '@')
881 return make_pair(obj_unquote_splic, make_pair(read(stream), obj_empty));
882 ungetc(c, stream);
883 return make_pair(obj_unquote, make_pair(read(stream), obj_empty));
884}
885
886
887static obj_t read_list(FILE *stream, int c)
888{
889 obj_t list, new, end;
890
891 list = obj_empty;
892
893 for(;;) {
894 c = getnbc(stream);
895 if(c == ')' || c == '.') break;
896 ungetc(c, stream);
897 new = make_pair(read(stream), obj_empty);
898 if(list == obj_empty) {
899 list = new;
900 end = new;
901 } else {
902 CDR(end) = new;
903 end = new;
904 }
905 }
906
907 if(c == '.') {
908 if(list == obj_empty)
909 error("read: unexpected dot");
910 CDR(end) = read(stream);
911 c = getnbc(stream);
912 }
913
914 if(c != ')')
915 error("read: expected close parenthesis");
916
917 return list;
918}
919
920
921static obj_t list_to_vector(obj_t list)
922{
923 size_t i;
924 obj_t l, vector;
925 i = 0;
926 l = list;
927 while(TYPE(l) == TYPE_PAIR) {
928 ++i;
929 l = CDR(l);
930 }
931 if(l != obj_empty)
932 return obj_error;
933 vector = make_vector(i, obj_undefined);
934 i = 0;
935 l = list;
936 while(TYPE(l) == TYPE_PAIR) {
937 vector->vector.vector[i] = CAR(l);
938 ++i;
939 l = CDR(l);
940 }
941 return vector;
942}
943
944
945static obj_t read_special(FILE *stream, int c)
946{
947 c = getnbc(stream);
948 switch(tolower(c)) {
949 case 't': return obj_true;
950 case 'f': return obj_false;
951 case '\\': { /* character (R4RS 6.6) */
952 c = getc(stream);
953 if(c == EOF)
954 error("read: end of file reading character literal");
955 return make_character(c);
956 }
957 case '(': { /* vector (R4RS 6.8) */
958 obj_t list = read_list(stream, c);
959 obj_t vector = list_to_vector(list);
960 if(vector == obj_error)
961 error("read: illegal vector syntax");
962 return vector;
963 }
964 }
965 error("read: unknown special '%c'", c);
966 return obj_error;
967}
968
969
970static obj_t read(FILE *stream)
971{
972 int c;
973
974 c = getnbc(stream);
975 if(c == EOF) return obj_eof;
976
977 if(isdigit(c))
978 return read_integer(stream, c);
979
980 switch(c) {
981 case '\'': return read_quote(stream, c);
982 case '`': return read_quasiquote(stream, c);
983 case ',': return read_unquote(stream, c);
984 case '(': return read_list(stream, c);
985 case '#': return read_special(stream, c);
986 case '"': return read_string(stream, c);
987 case '-': case '+': {
988 int next = getc(stream);
989 if(isdigit(next)) {
990 obj_t integer = read_integer(stream, next);
991 if(c == '-')
992 integer->integer.integer = -integer->integer.integer;
993 return integer;
994 }
995 ungetc(next, stream);
996 } break; /* fall through to read as symbol */
997 }
998
999 if(isalpha(c) || isealpha(c))
1000 return read_symbol(stream, c);
1001
1002 error("read: illegal char '%c'", c);
1003 return obj_error;
1004}
1005
1006
1007/* lookup_in_frame -- look up a symbol in single frame
1008 *
1009 * Search a single frame of the environment for a symbol binding.
1010 */
1011
1012static obj_t lookup_in_frame(obj_t frame, obj_t symbol)
1013{
1014 while(frame != obj_empty) {
1015 assert(TYPE(frame) == TYPE_PAIR);
1016 assert(TYPE(CAR(frame)) == TYPE_PAIR);
1017 assert(TYPE(CAAR(frame)) == TYPE_SYMBOL);
1018 if(CAAR(frame) == symbol)
1019 return CAR(frame);
1020 frame = CDR(frame);
1021 }
1022 return obj_undefined;
1023}
1024
1025
1026/* lookup -- look up symbol in environment
1027 *
1028 * Search an entire environment for a binding of a symbol.
1029 */
1030
1031static obj_t lookup(obj_t env, obj_t symbol)
1032{
1033 obj_t binding;
1034 while(env != obj_empty) {
1035 assert(TYPE(env) == TYPE_PAIR);
1036 binding = lookup_in_frame(CAR(env), symbol);
1037 if(binding != obj_undefined)
1038 return binding;
1039 env = CDR(env);
1040 }
1041 return obj_undefined;
1042}
1043
1044
1045/* define -- define symbol in environment
1046 *
1047 * In Scheme, define will actually rebind (i.e. set) a symbol in the
1048 * same frame of the environment, or add a binding if it wasn't already
1049 * set. This has the effect of making bindings local to functions
1050 * (see how entry_interpret adds an empty frame to the environments),
1051 * allowing recursion, and allowing redefinition at the top level.
1052 * See R4R2 section 5.2 for details.
1053 */
1054
1055static void define(obj_t env, obj_t symbol, obj_t value)
1056{
1057 obj_t binding;
1058 assert(TYPE(env) == TYPE_PAIR); /* always at least one frame */
1059 binding = lookup_in_frame(CAR(env), symbol);
1060 if(binding != obj_undefined)
1061 CDR(binding) = value;
1062 else
1063 CAR(env) = make_pair(make_pair(symbol, value), CAR(env));
1064}
1065
1066
1067static obj_t eval(obj_t env, obj_t op_env, obj_t exp);
1068
1069static obj_t eval(obj_t env, obj_t op_env, obj_t exp)
1070{
1071 /* self-evaluating */
1072 if(TYPE(exp) == TYPE_INTEGER ||
1073 (TYPE(exp) == TYPE_SPECIAL && exp != obj_empty) ||
1074 TYPE(exp) == TYPE_STRING ||
1075 TYPE(exp) == TYPE_CHARACTER)
1076 return exp;
1077
1078 /* symbol lookup */
1079 if(TYPE(exp) == TYPE_SYMBOL) {
1080 obj_t binding = lookup(env, exp);
1081 if(binding == obj_undefined)
1082 error("eval: unbound symbol \"%s\"", exp->symbol.string);
1083 return CDR(binding);
1084 }
1085
1086 /* apply operator or function */
1087 if(TYPE(exp) == TYPE_PAIR) {
1088 obj_t operator;
1089 if(TYPE(CAR(exp)) == TYPE_SYMBOL) {
1090 obj_t binding = lookup(op_env, CAR(exp));
1091 if(binding != obj_undefined) {
1092 operator = CDR(binding);
1093 assert(TYPE(operator) == TYPE_OPERATOR);
1094 return (*operator->operator.entry)(env, op_env, operator, CDR(exp));
1095 }
1096 }
1097 operator = eval(env, op_env, CAR(exp));
1098 unless(TYPE(operator) == TYPE_OPERATOR)
1099 error("eval: application of non-function");
1100 return (*operator->operator.entry)(env, op_env, operator, CDR(exp));
1101 }
1102
1103 error("eval: unknown syntax");
1104 return obj_error;
1105}
1106
1107
1108/* OPERATOR UTILITIES */
1109
1110
1111/* eval_list -- evaluate list of expressions giving list of results
1112 *
1113 * eval_list evaluates a list of expresions and yields a list of their
1114 * results, in order. If the list is badly formed, an error is thrown
1115 * using the message given.
1116 */
1117
1118static obj_t eval_list(obj_t env, obj_t op_env, obj_t list, char *message)
1119{
1120 obj_t result, end, pair;
1121 result = obj_empty;
1122 while(list != obj_empty) {
1123 if(TYPE(list) != TYPE_PAIR)
1124 error(message);
1125 pair = make_pair(eval(env, op_env, CAR(list)), obj_empty);
1126 if(result == obj_empty)
1127 result = pair;
1128 else
1129 CDR(end) = pair;
1130 end = pair;
1131 list = CDR(list);
1132 }
1133 return result;
1134}
1135
1136
1137/* eval_args1 -- evaluate some operator arguments
1138 *
1139 * See eval_args and eval_args_rest for usage.
1140 */
1141
1142static obj_t eval_args1(char *name, obj_t env, obj_t op_env,
1143 obj_t operands, unsigned n, va_list args)
1144{
1145 unsigned i;
1146 for(i = 0; i < n; ++i) {
1147 unless(TYPE(operands) == TYPE_PAIR)
1148 error("eval: too few arguments to %s", name);
1149 *va_arg(args, obj_t *) = eval(env, op_env, CAR(operands));
1150 operands = CDR(operands);
1151 }
1152 return operands;
1153}
1154
1155
1156/* eval_args -- evaluate operator arguments without rest list
1157 *
1158 * eval_args evaluates the first "n" expressions from the list of
1159 * expressions in "operands", returning the rest of the operands
1160 * unevaluated. It puts the results of evaluation in the addresses
1161 * passed in the vararg list. If the operands list is badly formed
1162 * an error is thrown using the operator name passed. For example:
1163 *
1164 * eval_args("foo", env, op_env, operands, 2, &arg1, &arg2);
1165 */
1166
1167static void eval_args(char *name, obj_t env, obj_t op_env,
1168 obj_t operands, unsigned n, ...)
1169{
1170 va_list args;
1171 va_start(args, n);
1172 operands = eval_args1(name, env, op_env, operands, n, args);
1173 unless(operands == obj_empty)
1174 error("eval: too many arguments to %s", name);
1175 va_end(args);
1176}
1177
1178
1179/* eval_args_rest -- evaluate operator arguments with rest list
1180 *
1181 * eval_args_rest evaluates the first "n" expressions from the list of
1182 * expressions in "operands", then evaluates the rest of the operands
1183 * using eval_list and puts the result at *restp. It puts the results
1184 * of evaluating the first "n" operands in the addresses
1185 * passed in the vararg list. If the operands list is badly formed
1186 * an error is thrown using the operator name passed. For example:
1187 *
1188 * eval_args_rest("foo", env, op_env, operands, &rest, 2, &arg1, &arg2);
1189 */
1190
1191static void eval_args_rest(char *name, obj_t env, obj_t op_env,
1192 obj_t operands, obj_t *restp, unsigned n, ...)
1193{
1194 va_list args;
1195 va_start(args, n);
1196 operands = eval_args1(name, env, op_env, operands, n, args);
1197 va_end(args);
1198 *restp = eval_list(env, op_env, operands, "eval: badly formed argument list");
1199}
1200
1201
1202/* BUILT-IN OPERATORS */
1203
1204
1205/* entry_interpret -- interpreted function entry point
1206 *
1207 * When a function is made using lambda (see entry_lambda) an operator
1208 * is created with entry_interpret as its entry point, and the arguments
1209 * and body of the function. The entry_interpret function evaluates
1210 * the operands of the function and binds them to the argument names
1211 * in a new frame added to the lambda's closure environment. It then
1212 * evaluates the body in that environment, executing the function.
1213 */
1214
1215static obj_t entry_interpret(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1216{
1217 obj_t arguments, fun_env, fun_op_env;
1218
1219 assert(TYPE(operator) == TYPE_OPERATOR);
1220
1221 /* Make a new frame so that bindings are local to the function. */
1222 /* Arguments will be bound in this new frame. */
1223 fun_env = make_pair(obj_empty, operator->operator.env);
1224 fun_op_env = make_pair(obj_empty, operator->operator.op_env);
1225
1226 arguments = operator->operator.arguments;
1227 while(operands != obj_empty) {
1228 if(arguments == obj_empty)
1229 error("eval: function applied to too many arguments");
1230 if(TYPE(arguments) == TYPE_SYMBOL) {
1231 define(fun_env, arguments,
1232 eval_list(env, op_env, operands, "eval: badly formed argument list"));
1233 operands = obj_empty;
1234 arguments = obj_empty;
1235 } else {
1236 assert(TYPE(arguments) == TYPE_PAIR &&
1237 TYPE(CAR(arguments)) == TYPE_SYMBOL);
1238 define(fun_env,
1239 CAR(arguments),
1240 eval(env, op_env, CAR(operands)));
1241 operands = CDR(operands);
1242 arguments = CDR(arguments);
1243 }
1244 }
1245 if(arguments != obj_empty)
1246 error("eval: function applied to too few arguments");
1247
1248 return eval(fun_env, fun_op_env, operator->operator.body);
1249}
1250
1251
1252/* entry_quote -- return operands unevaluated
1253 *
1254 * In Scheme, (quote foo) evaluates to foo (i.e. foo is not evaluated).
1255 * See R4RS 4.1.2. The reader expands "'x" to "(quote x)".
1256 */
1257
1258static obj_t entry_quote(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1259{
1260 unless(TYPE(operands) == TYPE_PAIR &&
1261 CDR(operands) == obj_empty)
1262 error("%s: illegal syntax", operator->operator.name);
1263 return CAR(operands);
1264}
1265
1266
1267/* entry_define -- bind a symbol in the top frame of the environment
1268 *
1269 * In Scheme, "(define <symbol> <expression>)" evaluates expressions
1270 * and binds it to symbol in the top frame of the environment (see
1271 * R4RS 5.2). This code also allows the non-essential syntax for
1272 * define, "(define (<symbol> <formals>) <body>)" as a short-hand for
1273 * "(define <symbol> (lambda (<formals>) <body>))".
1274 */
1275
1276static obj_t entry_define(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1277{
1278 obj_t symbol, value;
1279 unless(TYPE(operands) == TYPE_PAIR &&
1280 TYPE(CDR(operands)) == TYPE_PAIR &&
1281 CDDR(operands) == obj_empty)
1282 error("%s: illegal syntax", operator->operator.name);
1283 if(TYPE(CAR(operands)) == TYPE_SYMBOL) {
1284 symbol = CAR(operands);
1285 value = eval(env, op_env, CADR(operands));
1286 } else if(TYPE(CAR(operands)) == TYPE_PAIR &&
1287 TYPE(CAAR(operands)) == TYPE_SYMBOL) {
1288 symbol = CAAR(operands);
1289 value = eval(env, op_env,
1290 make_pair(obj_lambda,
1291 make_pair(CDAR(operands), CDR(operands))));
1292 } else
1293 error("%s: applied to binder", operator->operator.name);
1294 define(env, symbol, value);
1295 return symbol;
1296}
1297
1298
1299/* entry_if -- one- or two-armed conditional
1300 *
1301 * "(if <test> <consequent> <alternate>)" and "(if <test> <consequent>)".
1302 * See R4RS 4.1.5.
1303 */
1304
1305static obj_t entry_if(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1306{
1307 obj_t test;
1308 unless(TYPE(operands) == TYPE_PAIR &&
1309 TYPE(CDR(operands)) == TYPE_PAIR &&
1310 (CDDR(operands) == obj_empty ||
1311 (TYPE(CDDR(operands)) == TYPE_PAIR &&
1312 CDDDR(operands) == obj_empty)))
1313 error("%s: illegal syntax", operator->operator.name);
1314 test = eval(env, op_env, CAR(operands));
1315 /* Anything which is not #f counts as true [R4RS 6.1]. */
1316 if(test != obj_false)
1317 return eval(env, op_env, CADR(operands));
1318 if(TYPE(CDDR(operands)) == TYPE_PAIR)
1319 return eval(env, op_env, CADDR(operands));
1320 return obj_undefined;
1321}
1322
1323
1324/* entry_cond -- general conditional
1325 *
1326 * "(cond (<test1> <exp1.1> ...) (<test2> <exp2.1> ...) ... [(else <expe.1> ...)])"
1327 */
1328
1329static obj_t entry_cond(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1330{
1331 unless(TYPE(operands) == TYPE_PAIR)
1332 error("%s: illegal syntax", operator->operator.name);
1333 while(TYPE(operands) == TYPE_PAIR) {
1334 obj_t clause = CAR(operands);
1335 obj_t result;
1336 unless(TYPE(clause) == TYPE_PAIR &&
1337 TYPE(CDR(clause)) == TYPE_PAIR)
1338 error("%s: illegal clause syntax", operator->operator.name);
1339 if(CAR(clause) == obj_else) {
1340 unless(CDR(operands) == obj_empty)
1341 error("%s: else clause must come last", operator->operator.name);
1342 result = obj_true;
1343 } else
1344 result = eval(env, op_env, CAR(clause));
1345 if(result != obj_false) {
1346 for(;;) {
1347 clause = CDR(clause);
1348 if(TYPE(clause) != TYPE_PAIR) break;
1349 result = eval(env, op_env, CAR(clause));
1350 }
1351 if(clause != obj_empty)
1352 error("%s: illegal clause syntax", operator->operator.name);
1353 return result;
1354 }
1355 operands = CDR(operands);
1356 }
1357 return obj_undefined;
1358}
1359
1360
1361/* entry_and -- (and <test1> ...) */
1362
1363static obj_t entry_and(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1364{
1365 while(TYPE(operands) == TYPE_PAIR) {
1366 obj_t test = eval(env, op_env, CAR(operands));
1367 if(test == obj_false)
1368 return obj_false;
1369 operands = CDR(operands);
1370 }
1371 if(operands != obj_empty)
1372 error("%s: illegal syntax", operator->operator.name);
1373 return obj_true;
1374}
1375
1376
1377/* entry_or -- (or <test1> ...) */
1378
1379static obj_t entry_or(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1380{
1381 while(TYPE(operands) == TYPE_PAIR) {
1382 obj_t test = eval(env, op_env, CAR(operands));
1383 if(test != obj_false)
1384 return obj_true;
1385 operands = CDR(operands);
1386 }
1387 if(operands != obj_empty)
1388 error("%s: illegal syntax", operator->operator.name);
1389 return obj_false;
1390}
1391
1392
1393/* entry_let -- (let <bindings> <body>) */
1394/* @@@@ Too much common code with let* */
1395
1396static obj_t entry_let(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1397{
1398 obj_t inner_env, bindings, result;
1399 unless(TYPE(operands) == TYPE_PAIR &&
1400 TYPE(CDR(operands)) == TYPE_PAIR)
1401 error("%s: illegal syntax", operator->operator.name);
1402 inner_env = make_pair(obj_empty, env); /* @@@@ common with interpret */
1403 bindings = CAR(operands);
1404 while(TYPE(bindings) == TYPE_PAIR) {
1405 obj_t binding = CAR(bindings);
1406 unless(TYPE(binding) == TYPE_PAIR &&
1407 TYPE(CAR(binding)) == TYPE_SYMBOL &&
1408 TYPE(CDR(binding)) == TYPE_PAIR &&
1409 CDDR(binding) == obj_empty)
1410 error("%s: illegal binding", operator->operator.name);
1411 define(inner_env, CAR(binding), eval(env, op_env, CADR(binding)));
1412 bindings = CDR(bindings);
1413 }
1414 if(bindings != obj_empty)
1415 error("%s: illegal bindings list", operator->operator.name);
1416 operands = CDR(operands);
1417 while(TYPE(operands) == TYPE_PAIR) {
1418 result = eval(inner_env, op_env, CAR(operands));
1419 operands = CDR(operands);
1420 }
1421 if(operands != obj_empty)
1422 error("%s: illegal expression list", operator->operator.name);
1423 return result;
1424}
1425
1426
1427/* entry_let_star -- (let* <bindings> <body>) */
1428/* @@@@ Too much common code with let */
1429
1430static obj_t entry_let_star(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1431{
1432 obj_t inner_env, bindings, result;
1433 unless(TYPE(operands) == TYPE_PAIR &&
1434 TYPE(CDR(operands)) == TYPE_PAIR)
1435 error("%s: illegal syntax", operator->operator.name);
1436 inner_env = make_pair(obj_empty, env); /* @@@@ common with interpret */
1437 bindings = CAR(operands);
1438 while(TYPE(bindings) == TYPE_PAIR) {
1439 obj_t binding = CAR(bindings);
1440 unless(TYPE(binding) == TYPE_PAIR &&
1441 TYPE(CAR(binding)) == TYPE_SYMBOL &&
1442 TYPE(CDR(binding)) == TYPE_PAIR &&
1443 CDDR(binding) == obj_empty)
1444 error("%s: illegal binding", operator->operator.name);
1445 define(inner_env, CAR(binding), eval(inner_env, op_env, CADR(binding)));
1446 bindings = CDR(bindings);
1447 }
1448 if(bindings != obj_empty)
1449 error("%s: illegal bindings list", operator->operator.name);
1450 operands = CDR(operands);
1451 while(TYPE(operands) == TYPE_PAIR) {
1452 result = eval(inner_env, op_env, CAR(operands));
1453 operands = CDR(operands);
1454 }
1455 if(operands != obj_empty)
1456 error("%s: illegal expression list", operator->operator.name);
1457 return result;
1458}
1459
1460
1461/* entry_letrec -- (letrec <bindings> <body>) */
1462/* @@@@ Too much common code with let and let* */
1463
1464static obj_t entry_letrec(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1465{
1466 obj_t inner_env, bindings, result;
1467 unless(TYPE(operands) == TYPE_PAIR &&
1468 TYPE(CDR(operands)) == TYPE_PAIR)
1469 error("%s: illegal syntax", operator->operator.name);
1470 inner_env = make_pair(obj_empty, env); /* @@@@ common with interpret */
1471 bindings = CAR(operands);
1472 while(TYPE(bindings) == TYPE_PAIR) {
1473 obj_t binding = CAR(bindings);
1474 unless(TYPE(binding) == TYPE_PAIR &&
1475 TYPE(CAR(binding)) == TYPE_SYMBOL &&
1476 TYPE(CDR(binding)) == TYPE_PAIR &&
1477 CDDR(binding) == obj_empty)
1478 error("%s: illegal binding", operator->operator.name);
1479 define(inner_env, CAR(binding), obj_undefined);
1480 bindings = CDR(bindings);
1481 }
1482 if(bindings != obj_empty)
1483 error("%s: illegal bindings list", operator->operator.name);
1484 bindings = CAR(operands);
1485 while(TYPE(bindings) == TYPE_PAIR) {
1486 obj_t binding = CAR(bindings);
1487 define(inner_env, CAR(binding), eval(inner_env, op_env, CADR(binding)));
1488 bindings = CDR(bindings);
1489 }
1490 operands = CDR(operands);
1491 while(TYPE(operands) == TYPE_PAIR) {
1492 result = eval(inner_env, op_env, CAR(operands));
1493 operands = CDR(operands);
1494 }
1495 if(operands != obj_empty)
1496 error("%s: illegal expression list", operator->operator.name);
1497 return result;
1498}
1499
1500
1501/* entry_do -- (do ((<var> <init> <step1>) ...) (<test> <exp> ...) <command> ...) */
1502
1503static obj_t entry_do(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1504{
1505 error("%s: unimplemented", operator->operator.name);
1506 return obj_error;
1507}
1508
1509
1510/* entry_delay -- (delay <exp>) */
1511
1512static obj_t entry_delay(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1513{
1514 obj_t promise;
1515 unless(TYPE(operands) == TYPE_PAIR &&
1516 CDR(operands) == obj_empty)
1517 error("%s: illegal syntax", operator->operator.name);
1518 promise = make_pair(obj_false,
1519 make_operator("anonymous promise",
1520 entry_interpret, obj_empty,
1521 CAR(operands), env, op_env));
1522 TYPE(promise) = TYPE_PROMISE;
1523 return promise;
1524}
1525
1526
1527/* entry_quasiquote -- (quasiquote <template>) or `<template> */
1528/* @@@@ blech. */
1529
1530static obj_t entry_quasiquote(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1531{
1532 obj_t list, result = obj_empty, pair, end, insert;
1533 unless(TYPE(operands) == TYPE_PAIR &&
1534 CDR(operands) == obj_empty)
1535 error("%s: illegal syntax", operator->operator.name);
1536 list = CAR(operands);
1537 while(TYPE(list) == TYPE_PAIR) {
1538 if(TYPE(CAR(list)) == TYPE_PAIR &&
1539 TYPE(CAAR(list)) == TYPE_SYMBOL &&
1540 (CAAR(list) == obj_unquote ||
1541 CAAR(list) == obj_unquote_splic)) {
1542 unless(TYPE(CDAR(list)) == TYPE_PAIR &&
1543 CDDAR(list) == obj_empty)
1544 error("%s: illegal %s syntax", operator->operator.name, CAAR(list)->symbol.string);
1545 insert = eval(env, op_env, CADAR(list));
1546 if(CAAR(list) == obj_unquote) {
1547 pair = make_pair(insert, obj_empty);
1548 if(result == obj_empty)
1549 result = pair;
1550 else
1551 CDR(end) = pair;
1552 end = pair;
1553 } else if(CAAR(list) == obj_unquote_splic) {
1554 if(insert != obj_empty) {
1555 if(TYPE(insert) != TYPE_PAIR)
1556 error("%s: unquote-splicing expression must return list",
1557 operator->operator.name);
1558 if(result == obj_empty)
1559 result = insert;
1560 else
1561 CDR(end) = insert;
1562 while(TYPE(CDR(insert)) == TYPE_PAIR)
1563 insert = CDR(insert);
1564 if(CDR(insert) != obj_empty)
1565 error("%s: unquote-splicing expression must return list",
1566 operator->operator.name);
1567 end = insert;
1568 }
1569 }
1570 } else {
1571 pair = make_pair(CAR(list), obj_empty);
1572 if(result == obj_empty)
1573 result = pair;
1574 else
1575 CDR(end) = pair;
1576 end = pair;
1577 }
1578 list = CDR(list);
1579 }
1580 if(list != obj_empty)
1581 error("%s: illegal syntax", operator->operator.name);
1582 return result;
1583}
1584
1585
1586/* entry_set -- assignment
1587 *
1588 * (set! <variable> <expression>)
1589 * See R4RS 4.1.6.
1590 */
1591
1592static obj_t entry_set(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1593{
1594 obj_t symbol, binding, value;
1595 unless(TYPE(operands) == TYPE_PAIR &&
1596 TYPE(CDR(operands)) == TYPE_PAIR &&
1597 CDDR(operands) == obj_empty)
1598 error("%s: illegal syntax", operator->operator.name);
1599 unless(TYPE(CAR(operands)) == TYPE_SYMBOL)
1600 error("%s: applied to non-symbol", operator->operator.name);
1601 symbol = CAR(operands);
1602 binding = lookup(env, symbol);
1603 if(binding == obj_undefined)
1604 error("%s: applied to unbound symbol \"%s\"",
1605 operator->operator.name, symbol->symbol.string);
1606 value = eval(env, op_env, CADR(operands));
1607 CDR(binding) = value;
1608 return value;
1609}
1610
1611
1612/* entry_lambda -- lambda expressions
1613 *
1614 * (lambda <formals> <body>)
1615 * See R4RS 4.1.4.
1616 *
1617 * The entry_lambda function creates a new object of TYPE_OPERATOR
1618 * which captures the current environments, and contains the lambda
1619 * formals and body. This operator has an entry point at
1620 * entry_interpret, which will evaluate and bind the arguments when
1621 * the operator is applied.
1622 *
1623 * [Capturing the whole environment is bad for GC because it means
1624 * that everything defined when the lambda is evaluated will survive
1625 * for as long as the operator survives. It would be better to
1626 * examine the lambda body and determine which variables it references,
1627 * and either create a new environment or build a new body with just
1628 * those variables bound.]
1629 */
1630
1631static obj_t entry_lambda(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1632{
1633 obj_t list;
1634 unless(TYPE(operands) == TYPE_PAIR &&
1635 TYPE(CDR(operands)) == TYPE_PAIR)
1636 error("%s: illegal syntax", operator->operator.name);
1637 /* check syntax of argument list to save time in apply */
1638 list = CAR(operands);
1639 while(list != obj_empty && TYPE(list) != TYPE_SYMBOL) {
1640 unless(TYPE(list) == TYPE_PAIR &&
1641 TYPE(CAR(list)) == TYPE_SYMBOL)
1642 error("%s: illegal argument list", operator->operator.name);
1643 list = CDR(list);
1644 }
1645 return make_operator("anonymous function",
1646 entry_interpret, CAR(operands),
1647 make_pair(obj_begin, CDR(operands)),
1648 env, op_env);
1649}
1650
1651
1652/* entry_begin -- sequencing
1653 *
1654 * (begin <expression1> <expression2> ...)
1655 * See R4RS 4.2.3.
1656 */
1657
1658static obj_t entry_begin(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1659{
1660 obj_t result;
1661 do {
1662 unless(TYPE(operands) == TYPE_PAIR)
1663 error("%s: illegal syntax", operator->operator.name);
1664 result = eval(env, op_env, CAR(operands));
1665 operands = CDR(operands);
1666 } while(operands != obj_empty);
1667 return result;
1668}
1669
1670
1671/* BUILT-IN FUNCTIONS */
1672
1673
1674/* entry_not -- (not <obj>)
1675 *
1676 * Not returns #t if obj is false, and return #f otherwise. R4RS 6.1.
1677 */
1678
1679static obj_t entry_not(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1680{
1681 obj_t arg;
1682 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1683 return arg == obj_false ? obj_true : obj_false;
1684}
1685
1686
1687/* entry_booleanp -- (boolean? <obj>)
1688 *
1689 * Boolean? return #t if obj is either #t or #f, and #f otherwise. R4RS 6.1.
1690 */
1691
1692static obj_t entry_booleanp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1693{
1694 obj_t arg;
1695 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1696 return arg == obj_true || arg == obj_false ? obj_true : obj_false;
1697}
1698
1699
1700/* entry_eqvp -- (eqv? <obj1> <obj2>) */
1701
1702static int eqvp(obj_t obj1, obj_t obj2)
1703{
1704 return obj1 == obj2 ||
1705 (TYPE(obj1) == TYPE_INTEGER &&
1706 TYPE(obj2) == TYPE_INTEGER &&
1707 obj1->integer.integer == obj2->integer.integer);
1708}
1709
1710static obj_t entry_eqvp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1711{
1712 obj_t arg1, arg2;
1713 eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2);
1714 return eqvp(arg1, arg2) ? obj_true : obj_false;
1715}
1716
1717
1718/* entry_eqp -- (eq? <obj1> <obj2>) */
1719
1720static obj_t entry_eqp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1721{
1722 obj_t arg1, arg2;
1723 eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2);
1724 return arg1 == arg2 ? obj_true : obj_false;
1725}
1726
1727
1728/* entry_equalp -- (equal? <obj1> <obj2>) */
1729
1730static int equalp(obj_t obj1, obj_t obj2)
1731{
1732 if(TYPE(obj1) != TYPE(obj2))
1733 return 0;
1734 if(TYPE(obj1) == TYPE_PAIR)
1735 return equalp(CAR(obj1), CAR(obj2)) && equalp(CDR(obj1), CDR(obj2));
1736 /* @@@@ Similar recursion for vectors. */
1737 return eqvp(obj1, obj2);
1738}
1739
1740static obj_t entry_equalp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1741{
1742 obj_t arg1, arg2;
1743 eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2);
1744 return equalp(arg1, arg2) ? obj_true : obj_false;
1745}
1746
1747
1748/* entry_pairp -- (pair? <obj>) */
1749
1750static obj_t entry_pairp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1751{
1752 obj_t arg;
1753 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1754 return TYPE(arg) == TYPE_PAIR ? obj_true : obj_false;
1755}
1756
1757
1758/* entry_cons -- create pair
1759 *
1760 * (cons <obj1> <obj2>)
1761 * See R4RS 6.3.
1762 *
1763 * Returns a newly allocated pair whose car is obj1 and whose cdr is obj2.
1764 * The pair is guaranteed to be different (in the sense of eqv?) from every
1765 * existing object.
1766 */
1767
1768static obj_t entry_cons(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1769{
1770 obj_t car, cdr;
1771 eval_args(operator->operator.name, env, op_env, operands, 2, &car, &cdr);
1772 return make_pair(car, cdr);
1773}
1774
1775
1776/* entry_car -- R4RS 6.3 */
1777
1778static obj_t entry_car(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1779{
1780 obj_t pair;
1781 eval_args(operator->operator.name, env, op_env, operands, 1, &pair);
1782 unless(TYPE(pair) == TYPE_PAIR)
1783 error("%s: argument must be a pair", operator->operator.name);
1784 return CAR(pair);
1785}
1786
1787
1788static obj_t entry_cdr(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1789{
1790 obj_t pair;
1791 eval_args(operator->operator.name, env, op_env, operands, 1, &pair);
1792 unless(TYPE(pair) == TYPE_PAIR)
1793 error("%s: argument must be a pair", operator->operator.name);
1794 return CDR(pair);
1795}
1796
1797
1798static obj_t entry_setcar(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1799{
1800 obj_t pair, value;
1801 eval_args(operator->operator.name, env, op_env, operands, 2, &pair, &value);
1802 unless(TYPE(pair) == TYPE_PAIR)
1803 error("%s: first argument must be a pair", operator->operator.name);
1804 CAR(pair) = value;
1805 return obj_undefined;
1806}
1807
1808
1809static obj_t entry_setcdr(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1810{
1811 obj_t pair, value;
1812 eval_args(operator->operator.name, env, op_env, operands, 2, &pair, &value);
1813 unless(TYPE(pair) == TYPE_PAIR)
1814 error("%s: first argument must be a pair", operator->operator.name);
1815 CDR(pair) = value;
1816 return obj_undefined;
1817}
1818
1819
1820static obj_t entry_nullp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1821{
1822 obj_t arg;
1823 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1824 return arg == obj_empty ? obj_true : obj_false;
1825}
1826
1827
1828static obj_t entry_listp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1829{
1830 obj_t arg;
1831 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1832 while(TYPE(arg) == TYPE_PAIR)
1833 arg = CDR(arg);
1834 return arg == obj_empty ? obj_true : obj_false;
1835}
1836
1837
1838static obj_t entry_list(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1839{
1840 obj_t rest;
1841 eval_args_rest(operator->operator.name, env, op_env, operands, &rest, 0);
1842 return rest;
1843}
1844
1845
1846static obj_t entry_length(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1847{
1848 obj_t arg;
1849 long length;
1850 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1851 length = 0;
1852 while(TYPE(arg) == TYPE_PAIR) {
1853 ++length;
1854 arg = CDR(arg);
1855 }
1856 if(arg != obj_empty)
1857 error("%s: applied to non-list", operator->operator.name);
1858 return make_integer(length);
1859}
1860
1861
1862static obj_t entry_append(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1863{
1864 obj_t arg1, arg2, result, pair, end;
1865 eval_args(operator->operator.name, env, op_env, operands, 2, &arg1, &arg2);
1866 result = obj_empty;
1867 while(TYPE(arg1) == TYPE_PAIR) {
1868 pair = make_pair(CAR(arg1), obj_empty);
1869 if(result == obj_empty)
1870 result = pair;
1871 else
1872 CDR(end) = pair;
1873 end = pair;
1874 arg1 = CDR(arg1);
1875 }
1876 if(arg1 != obj_empty)
1877 error("%s: applied to non-list", operator->operator.name);
1878 CDR(end) = arg2;
1879 return result;
1880}
1881
1882
1883static obj_t entry_integerp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1884{
1885 obj_t arg;
1886 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1887 return TYPE(arg) == TYPE_INTEGER ? obj_true : obj_false;
1888}
1889
1890
1891static obj_t entry_zerop(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1892{
1893 obj_t arg;
1894 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1895 unless(TYPE(arg) == TYPE_INTEGER)
1896 error("%s: argument must be an integer", operator->operator.name);
1897 return arg->integer.integer == 0 ? obj_true : obj_false;
1898}
1899
1900
1901static obj_t entry_positivep(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1902{
1903 obj_t arg;
1904 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1905 unless(TYPE(arg) == TYPE_INTEGER)
1906 error("%s: argument must be an integer", operator->operator.name);
1907 return arg->integer.integer > 0 ? obj_true : obj_false;
1908}
1909
1910
1911static obj_t entry_negativep(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1912{
1913 obj_t arg;
1914 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1915 unless(TYPE(arg) == TYPE_INTEGER)
1916 error("%s: argument must be an integer", operator->operator.name);
1917 return arg->integer.integer < 0 ? obj_true : obj_false;
1918}
1919
1920
1921static obj_t entry_symbolp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1922{
1923 obj_t arg;
1924 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
1925 return TYPE(arg) == TYPE_SYMBOL ? obj_true : obj_false;
1926}
1927
1928
1929static obj_t entry_add(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1930{
1931 obj_t args;
1932 long result;
1933 eval_args_rest(operator->operator.name, env, op_env, operands, &args, 0);
1934 result = 0;
1935 while(TYPE(args) == TYPE_PAIR) {
1936 unless(TYPE(CAR(args)) == TYPE_INTEGER)
1937 error("%s: arguments must be integers", operator->operator.name);
1938 result += CAR(args)->integer.integer;
1939 args = CDR(args);
1940 }
1941 assert(args == obj_empty); /* eval_args_rest always returns a list */
1942 return make_integer(result);
1943}
1944
1945
1946static obj_t entry_multiply(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1947{
1948 obj_t args;
1949 long result;
1950 eval_args_rest(operator->operator.name, env, op_env, operands, &args, 0);
1951 result = 1;
1952 while(TYPE(args) == TYPE_PAIR) {
1953 unless(TYPE(CAR(args)) == TYPE_INTEGER)
1954 error("%s: arguments must be integers", operator->operator.name);
1955 result *= CAR(args)->integer.integer;
1956 args = CDR(args);
1957 }
1958 assert(args == obj_empty); /* eval_args_rest always returns a list */
1959 return make_integer(result);
1960}
1961
1962
1963static obj_t entry_subtract(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1964{
1965 obj_t arg, args;
1966 long result;
1967 eval_args_rest(operator->operator.name, env, op_env, operands, &args, 1, &arg);
1968 unless(TYPE(arg) == TYPE_INTEGER)
1969 error("%s: first argument must be an integer", operator->operator.name);
1970 result = arg->integer.integer;
1971 if(args == obj_empty)
1972 result = -result;
1973 else {
1974 while(TYPE(args) == TYPE_PAIR) {
1975 unless(TYPE(CAR(args)) == TYPE_INTEGER)
1976 error("%s: arguments must be integers", operator->operator.name);
1977 result -= CAR(args)->integer.integer;
1978 args = CDR(args);
1979 }
1980 assert(args == obj_empty); /* eval_args_rest always returns a list */
1981 }
1982 return make_integer(result);
1983}
1984
1985
1986static obj_t entry_divide(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
1987{
1988 obj_t arg, args;
1989 long result;
1990 eval_args_rest(operator->operator.name, env, op_env, operands, &args, 1, &arg);
1991 unless(TYPE(arg) == TYPE_INTEGER)
1992 error("%s: first argument must be an integer", operator->operator.name);
1993 result = arg->integer.integer;
1994 if(args == obj_empty) {
1995 if(result == 0)
1996 error("%s: reciprocal of zero", operator->operator.name);
1997 result = 1/result; /* @@@@ pretty meaningless for integers */
1998 } else {
1999 while(TYPE(args) == TYPE_PAIR) {
2000 unless(TYPE(CAR(args)) == TYPE_INTEGER)
2001 error("%s: arguments must be integers", operator->operator.name);
2002 if(CAR(args)->integer.integer == 0)
2003 error("%s: divide by zero", operator->operator.name);
2004 result /= CAR(args)->integer.integer;
2005 args = CDR(args);
2006 }
2007 assert(args == obj_empty); /* eval_args_rest always returns a list */
2008 }
2009 return make_integer(result);
2010}
2011
2012
2013static obj_t entry_reverse(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2014{
2015 obj_t arg, result;
2016 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
2017 result = obj_empty;
2018 while(arg != obj_empty) {
2019 unless(TYPE(arg) == TYPE_PAIR)
2020 error("%s: argument must be a list", operator->operator.name);
2021 result = make_pair(CAR(arg), result);
2022 arg = CDR(arg);
2023 }
2024 return result;
2025}
2026
2027
2028static obj_t entry_environment(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2029{
2030 eval_args(operator->operator.name, env, op_env, operands, 0);
2031 return env;
2032}
2033
2034
2035static obj_t entry_open_in(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2036{
2037 obj_t filename;
2038 FILE *stream;
2039 eval_args(operator->operator.name, env, op_env, operands, 1, &filename);
2040 unless(TYPE(filename) == TYPE_STRING)
2041 error("%s: argument must be a string", operator->operator.name);
2042 stream = fopen(filename->string.string, "r");
2043 if(stream == NULL)
2044 error("%s: cannot open input file", operator->operator.name); /* @@@@ return error */
2045 return make_port(filename, stream);
2046}
2047
2048
2049/* @@@@ This doesn't work if the promise refers to its own value. */
2050
2051static obj_t entry_force(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2052{
2053 obj_t promise;
2054 eval_args(operator->operator.name, env, op_env, operands, 1, &promise);
2055 unless(TYPE(promise) == TYPE_PROMISE)
2056 error("%s: argument must be a promise", operator->operator.name);
2057 assert(CAR(promise) == obj_false || CAR(promise) == obj_true);
2058 /* If the promise is unevaluated then apply the CDR. */
2059 if(CAR(promise) == obj_false) {
2060 obj_t closure = CDR(promise);
2061 assert(TYPE(closure) == TYPE_OPERATOR);
2062 assert(closure->operator.arguments == obj_empty);
2063 CDR(promise) = (*closure->operator.entry)(env, op_env, closure, obj_empty);
2064 CAR(promise) = obj_true;
2065 }
2066 return CDR(promise);
2067}
2068
2069
2070static obj_t entry_vectorp(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2071{
2072 obj_t arg;
2073 eval_args(operator->operator.name, env, op_env, operands, 1, &arg);
2074 return TYPE(arg) == TYPE_VECTOR ? obj_true : obj_false;
2075}
2076
2077
2078static obj_t entry_make_vector(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2079{
2080 obj_t length, rest, fill;
2081 eval_args_rest(operator->operator.name, env, op_env, operands, &rest, 1, &length);
2082 unless(TYPE(length) == TYPE_INTEGER)
2083 error("%s: first argument must be an integer", operator->operator.name);
2084 if(rest == obj_empty)
2085 fill = obj_undefined;
2086 else {
2087 unless(CDR(rest) == obj_empty)
2088 error("%s: too many arguments", operator->operator.name);
2089 fill = CAR(rest);
2090 }
2091 return make_vector(length->integer.integer, fill);
2092}
2093
2094
2095static obj_t entry_vector(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2096{
2097 obj_t rest, vector;
2098 eval_args_rest(operator->operator.name, env, op_env, operands, &rest, 0);
2099 vector = list_to_vector(rest);
2100 assert(vector != obj_error);
2101 return vector;
2102}
2103
2104
2105static obj_t entry_vector_length(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2106{
2107 obj_t vector;
2108 eval_args(operator->operator.name, env, op_env, operands, 1, &vector);
2109 unless(TYPE(vector) == TYPE_VECTOR)
2110 error("%s: argument must be a vector", operator->operator.name);
2111 return make_integer(vector->vector.length);
2112}
2113
2114
2115static obj_t entry_vector_ref(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2116{
2117 obj_t vector, index;
2118 eval_args(operator->operator.name, env, op_env, operands, 2, &vector, &index);
2119 unless(TYPE(vector) == TYPE_VECTOR)
2120 error("%s: first argument must be a vector", operator->operator.name);
2121 unless(TYPE(index) == TYPE_INTEGER)
2122 error("%s: second argument must be an integer", operator->operator.name);
2123 unless(0 <= index->integer.integer && index->integer.integer < vector->vector.length)
2124 error("%s: index %ld out of bounds of vector length %ld",
2125 operator->operator.name, index->integer.integer, vector->vector.length);
2126 return vector->vector.vector[index->integer.integer];
2127}
2128
2129
2130static obj_t entry_vector_set(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2131{
2132 obj_t vector, index, obj;
2133 eval_args(operator->operator.name, env, op_env, operands, 3, &vector, &index, &obj);
2134 unless(TYPE(vector) == TYPE_VECTOR)
2135 error("%s: first argument must be a vector", operator->operator.name);
2136 unless(TYPE(index) == TYPE_INTEGER)
2137 error("%s: second argument must be an integer", operator->operator.name);
2138 unless(0 <= index->integer.integer && index->integer.integer < vector->vector.length)
2139 error("%s: index %ld out of bounds of vector length %ld",
2140 operator->operator.name, index->integer.integer, vector->vector.length);
2141 vector->vector.vector[index->integer.integer] = obj;
2142 return obj_undefined;
2143}
2144
2145
2146static obj_t entry_vector_to_list(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2147{
2148 obj_t vector, list;
2149 size_t i;
2150 eval_args(operator->operator.name, env, op_env, operands, 1, &vector);
2151 unless(TYPE(vector) == TYPE_VECTOR)
2152 error("%s: argument must be a vector", operator->operator.name);
2153 list = obj_empty;
2154 i = vector->vector.length;
2155 while(i > 0) {
2156 --i;
2157 list = make_pair(vector->vector.vector[i], list);
2158 }
2159 return list;
2160}
2161
2162
2163static obj_t entry_list_to_vector(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2164{
2165 obj_t list, vector;
2166 eval_args(operator->operator.name, env, op_env, operands, 1, &list);
2167 vector = list_to_vector(list);
2168 if(vector == obj_error)
2169 error("%s: argument must be a list", operator->operator.name);
2170 return vector;
2171}
2172
2173
2174static obj_t entry_vector_fill(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2175{
2176 obj_t vector, obj;
2177 size_t i;
2178 eval_args(operator->operator.name, env, op_env, operands, 2, &vector, &obj);
2179 unless(TYPE(vector) == TYPE_VECTOR)
2180 error("%s: first argument must be a vector", operator->operator.name);
2181 for(i = 0; i < vector->vector.length; ++i)
2182 vector->vector.vector[i] = obj;
2183 return obj_undefined;
2184}
2185
2186
2187static obj_t entry_eval(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2188{
2189 obj_t exp;
2190 eval_args(operator->operator.name, env, op_env, operands, 1, &exp);
2191 return eval(env, op_env, exp);
2192}
2193
2194
2195static obj_t entry_symbol_to_string(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2196{
2197 obj_t symbol;
2198 eval_args(operator->operator.name, env, op_env, operands, 1, &symbol);
2199 unless(TYPE(symbol) == TYPE_SYMBOL)
2200 error("%s: argument must be a symbol", operator->operator.name);
2201 return make_string(symbol->symbol.length, symbol->symbol.string);
2202}
2203
2204
2205static obj_t entry_string_to_symbol(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
2206{
2207 obj_t string;
2208 eval_args(operator->operator.name, env, op_env, operands, 1, &string);
2209 unless(TYPE(string) == TYPE_STRING)
2210 error("%s: argument must be a string", operator->operator.name);
2211 /* @@@@ Should pass length to intern to avoid problems with NUL termination. */
2212 return intern(string->string.string);
2213}
2214
2215
2216/* INITIALIZATION */
2217
2218
2219/* special table */
2220
2221static struct {char *name; obj_t *varp;} sptab[] = {
2222 {"()", &obj_empty},
2223 {"#[eof]", &obj_eof},
2224 {"#[error]", &obj_error},
2225 {"#t", &obj_true},
2226 {"#f", &obj_false},
2227 {"#[undefined]", &obj_undefined}
2228};
2229
2230
2231/* initial symbol table */
2232
2233static struct {char *name; obj_t *varp;} isymtab[] = {
2234 {"quote", &obj_quote},
2235 {"lambda", &obj_lambda},
2236 {"begin", &obj_begin},
2237 {"else", &obj_else},
2238 {"quasiquote", &obj_quasiquote},
2239 {"unquote", &obj_unquote},
2240 {"unquote-splicing", &obj_unquote_splic}
2241};
2242
2243
2244/* operator table */
2245
2246static struct {char *name; entry_t entry;} optab[] = {
2247 {"quote", entry_quote},
2248 {"define", entry_define},
2249 {"set!", entry_set},
2250 {"lambda", entry_lambda},
2251 {"begin", entry_begin},
2252 {"cond", entry_cond},
2253 {"if", entry_if},
2254 {"and", entry_and},
2255 {"or", entry_or},
2256 {"let", entry_let},
2257 {"let*", entry_let_star},
2258 {"letrec", entry_letrec},
2259 {"do", entry_do},
2260 {"delay", entry_delay},
2261 {"quasiquote", entry_quasiquote}
2262};
2263
2264
2265/* function table */
2266
2267static struct {char *name; entry_t entry;} funtab[] = {
2268 {"not", entry_not},
2269 {"boolean?", entry_booleanp},
2270 {"eqv?", entry_eqvp},
2271 {"eq?", entry_eqp},
2272 {"equal?", entry_equalp},
2273 {"pair?", entry_pairp},
2274 {"cons", entry_cons},
2275 {"car", entry_car},
2276 {"cdr", entry_cdr},
2277 {"set-car!", entry_setcar},
2278 {"set-cdr!", entry_setcdr},
2279 {"null?", entry_nullp},
2280 {"list?", entry_listp},
2281 {"list", entry_list},
2282 {"length", entry_length},
2283 {"append", entry_append},
2284 {"integer?", entry_integerp},
2285 {"zero?", entry_zerop},
2286 {"positive?", entry_positivep},
2287 {"negative?", entry_negativep},
2288 {"symbol?", entry_symbolp},
2289 {"+", entry_add},
2290 {"-", entry_subtract},
2291 {"*", entry_multiply},
2292 {"/", entry_divide},
2293 {"reverse", entry_reverse},
2294 {"the-environment", entry_environment},
2295 {"open-input-file", entry_open_in},
2296 {"force", entry_force},
2297 {"vector?", entry_vectorp},
2298 {"make-vector", entry_make_vector},
2299 {"vector", entry_vector},
2300 {"vector-length", entry_vector_length},
2301 {"vector-ref", entry_vector_ref},
2302 {"vector-set!", entry_vector_set},
2303 {"vector->list", entry_vector_to_list},
2304 {"list->vector", entry_list_to_vector},
2305 {"vector-fill!", entry_vector_fill},
2306 {"eval", entry_eval},
2307 {"symbol->string", entry_symbol_to_string},
2308 {"string->symbol", entry_string_to_symbol}
2309};
2310
2311
2312/* MPS Format
2313 *
2314 * These functions satisfy the MPS Format Protocol for format variant "A".
2315 *
2316 * In general, MPS format methods are performance critical, as they're used
2317 * on the MPS [critical path](..\..\design\critical-path.txt).
2318 *
2319 * Format methods might also be called at any time from the MPS, including
2320 * in signal handlers, exception handlers, interrupts, or other special
2321 * contexts. They must avoid touching any memory except the object they're
2322 * asked about, and possibly some static volatile data.
2323 *
2324 * Because these methods are critical, there are considerable gains in
2325 * performance if you mix them with the MPS source code and allow the
2326 * compiler to optimize globally. See [Building the Memory Pool
2327 * System](../../manual/build.txt).
2328 */
2329
2330
2331/* obj_scan -- object format scanner
2332 *
2333 * The job of the scanner is to identify references in a contiguous group
2334 * of objects in memory.
2335 */
2336
2337static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
2338{
2339#define FIX(ref) \
2340 do { \
2341 mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \
2342 mps_res_t res = MPS_FIX12(ss, &_addr); \
2343 if (res != MPS_RES_OK) return res; \
2344 (ref) = _addr; \
2345 } while(0)
2346
2347 MPS_SCAN_BEGIN(ss) {
2348 while (base < limit) {
2349 obj_t obj = base;
2350 switch (obj->type.type) {
2351 case TYPE_PAIR:
2352 FIX(obj->pair.car);
2353 FIX(obj->pair.cdr);
2354 base = (char *)base + ALIGN(sizeof(pair_s));
2355 break;
2356 case TYPE_INTEGER:
2357 base = (char *)base + ALIGN(sizeof(integer_s));
2358 break;
2359 case TYPE_SYMBOL:
2360 base = (char *)base +
2361 ALIGN(offsetof(symbol_s, string) + obj->symbol.length + 1);
2362 break;
2363 case TYPE_SPECIAL:
2364 base = (char *)base + ALIGN(sizeof(special_s));
2365 break;
2366 case TYPE_OPERATOR:
2367 FIX(obj->operator.arguments);
2368 FIX(obj->operator.body);
2369 FIX(obj->operator.env);
2370 FIX(obj->operator.op_env);
2371 base = (char *)base + ALIGN(sizeof(operator_s));
2372 break;
2373 case TYPE_STRING:
2374 base = (char *)base +
2375 ALIGN(offsetof(string_s, string) + obj->string.length + 1);
2376 break;
2377 case TYPE_PORT:
2378 FIX(obj->port.name);
2379 base = (char *)base + ALIGN(sizeof(port_s));
2380 break;
2381 case TYPE_CHARACTER:
2382 base = (char *)base + ALIGN(sizeof(character_s));
2383 break;
2384 case TYPE_VECTOR:
2385 {
2386 size_t i;
2387 for (i = 0; i < obj->vector.length; ++i)
2388 FIX(obj->vector.vector[i]);
2389 }
2390 base = (char *)base +
2391 ALIGN(offsetof(vector_s, vector) +
2392 obj->vector.length * sizeof(obj->vector.vector[0]));
2393 break;
2394 case TYPE_FWD2:
2395 base = (char *)base + ALIGN(sizeof(fwd2_s));
2396 break;
2397 case TYPE_FWD:
2398 base = (char *)base + ALIGN(obj->fwd.size);
2399 break;
2400 case TYPE_PAD1:
2401 base = (char *)base + ALIGN(sizeof(pad1_s));
2402 break;
2403 default:
2404 assert(0);
2405 fprintf(stderr, "Unexpected object on the heap\n");
2406 abort();
2407 return MPS_RES_FAIL;
2408 }
2409 }
2410 } MPS_SCAN_END(ss);
2411 return MPS_RES_OK;
2412}
2413
2414
2415/* obj_skip -- object format skip method
2416 *
2417 * The job of skip is to return the address where the next object would
2418 * be allocated. This isn't quite the same as the size of the object,
2419 * since there may be some rounding according to the memory pool alignment
2420 * chosen. This interpreter has chosen to align to single words.
2421 */
2422
2423static mps_addr_t obj_skip(mps_addr_t base)
2424{
2425 obj_t obj = base;
2426 switch (obj->type.type) {
2427 case TYPE_PAIR:
2428 base = (char *)base + ALIGN(sizeof(pair_s));
2429 break;
2430 case TYPE_INTEGER:
2431 base = (char *)base + ALIGN(sizeof(integer_s));
2432 break;
2433 case TYPE_SYMBOL:
2434 base = (char *)base +
2435 ALIGN(offsetof(symbol_s, string) + obj->symbol.length + 1);
2436 break;
2437 case TYPE_SPECIAL:
2438 base = (char *)base + ALIGN(sizeof(special_s));
2439 break;
2440 case TYPE_OPERATOR:
2441 base = (char *)base + ALIGN(sizeof(operator_s));
2442 break;
2443 case TYPE_STRING:
2444 base = (char *)base +
2445 ALIGN(offsetof(string_s, string) + obj->string.length + 1);
2446 break;
2447 case TYPE_PORT:
2448 base = (char *)base + ALIGN(sizeof(port_s));
2449 break;
2450 case TYPE_CHARACTER:
2451 base = (char *)base + ALIGN(sizeof(character_s));
2452 break;
2453 case TYPE_VECTOR:
2454 base = (char *)base +
2455 ALIGN(offsetof(vector_s, vector) +
2456 obj->vector.length * sizeof(obj->vector.vector[0]));
2457 break;
2458 case TYPE_FWD2:
2459 base = (char *)base + ALIGN(sizeof(fwd2_s));
2460 break;
2461 case TYPE_FWD:
2462 base = (char *)base + ALIGN(obj->fwd.size);
2463 break;
2464 case TYPE_PAD1:
2465 base = (char *)base + ALIGN(sizeof(pad1_s));
2466 break;
2467 default:
2468 assert(0);
2469 fprintf(stderr, "Unexpected object on the heap\n");
2470 abort();
2471 return NULL;
2472 }
2473 return base;
2474}
2475
2476
2477/* obj_isfwd -- object format forwarded test
2478 *
2479 * The job of obj_isfwd is to detect whether an object has been replaced
2480 * by a forwarding object, and return the address of the new copy if it has,
2481 * otherwise NULL. Note that this will return NULL for padding objects
2482 * because their `fwd` field is set to NULL.
2483 */
2484
2485static mps_addr_t obj_isfwd(mps_addr_t addr)
2486{
2487 obj_t obj = addr;
2488 switch (obj->type.type) {
2489 case TYPE_FWD2:
2490 return obj->fwd2.fwd;
2491 case TYPE_FWD:
2492 return obj->fwd.fwd;
2493 }
2494 return NULL;
2495}
2496
2497
2498/* obj_fwd -- object format forwarding method
2499 *
2500 * The job of obj_fwd is to replace an object by a forwarding object that
2501 * points at a new copy of the object. The object must be detected by
2502 * `obj_isfwd`. In this case, we have to be careful to replace two-word
2503 * objects with a FWD2 object, because the FWD object won't fit.
2504 */
2505
2506static void obj_fwd(mps_addr_t old, mps_addr_t new)
2507{
2508 obj_t obj = old;
2509 mps_addr_t limit = obj_skip(old);
2510 size_t size = (char *)limit - (char *)old;
2511 assert(size >= ALIGN(sizeof(fwd2_s)));
2512 if (size == ALIGN(sizeof(fwd2_s))) {
2513 obj->type.type = TYPE_FWD2;
2514 obj->fwd2.fwd = new;
2515 } else {
2516 obj->type.type = TYPE_FWD;
2517 obj->fwd.fwd = new;
2518 obj->fwd.size = size;
2519 }
2520}
2521
2522
2523/* obj_pad -- object format padding method
2524 *
2525 * The job of obj_pad is to fill in a block of memory with a padding
2526 * object that will be skipped by `obj_scan` or `obj_skip` but does
2527 * nothing else. Because we've chosen to align to single words, we may
2528 * have to pad a single word, so we have a special single-word padding
2529 * object, PAD1 for that purpose. Otherwise we can use forwarding objects
2530 * with their `fwd` fields set to NULL.
2531 */
2532
2533static void obj_pad(mps_addr_t addr, size_t size)
2534{
2535 obj_t obj = addr;
2536 assert(size >= ALIGN(sizeof(pad1_s)));
2537 if (size == ALIGN(sizeof(pad1_s))) {
2538 obj->type.type = TYPE_PAD1;
2539 } else if (size == ALIGN(sizeof(fwd2_s))) {
2540 obj->type.type = TYPE_FWD2;
2541 obj->fwd2.fwd = NULL;
2542 } else {
2543 obj->type.type = TYPE_FWD;
2544 obj->fwd.fwd = NULL;
2545 obj->fwd.size = size;
2546 }
2547}
2548
2549
2550/* obj_copy -- object format copy method
2551 *
2552 * The job of obj_copy is to make a copy of an object.
2553 * TODO: Explain why this exists.
2554 */
2555
2556static void obj_copy(mps_addr_t old, mps_addr_t new)
2557{
2558 mps_addr_t limit = obj_skip(old);
2559 size_t size = (char *)limit - (char *)old;
2560 (void)memcpy(new, old, size);
2561}
2562
2563
2564/* mps_chat -- get and display MPS messages */
2565
2566static void mps_chat(void)
2567{
2568 mps_message_type_t type;
2569
2570 while (mps_message_queue_type(&type, arena)) {
2571 mps_message_t message;
2572 mps_bool_t b;
2573 b = mps_message_get(&message, arena, type);
2574 assert(b); /* we just checked there was one */
2575 switch (type) {
2576 case mps_message_type_gc_start():
2577 printf("Collection %lu started.\n", (unsigned long)mps_collections(arena));
2578 printf(" Why: %s\n", mps_message_gc_start_why(arena, message));
2579 printf(" Clock: %lu\n", (unsigned long)mps_message_clock(arena, message));
2580 break;
2581 case mps_message_type_gc():
2582 printf("Collection finished.\n");
2583 /* FIXME: Print statistics */
2584 break;
2585 default:
2586 printf("Unknown message from MPS!\n");
2587 break;
2588 }
2589 }
2590}
2591
2592
2593/* start -- the main program
2594 *
2595 * This is the main body of the Scheme interpreter program, invoked by
2596 * `mps_tramp` so that its stack and exception handling can be managed
2597 * by the MPS.
2598 */
2599
2600static void *start(void *p, size_t s)
2601{
2602 size_t i;
2603 volatile obj_t env, op_env, obj;
2604 jmp_buf jb;
2605 mps_res_t res;
2606
2607 puts("MPS Toy Scheme Example");
2608
2609 total = (size_t)0;
2610
2611 symtab_size = 16;
2612 symtab = malloc(sizeof(obj_t) * symtab_size);
2613 if(symtab == NULL) error("out of memory");
2614 for(i = 0; i < symtab_size; ++i)
2615 symtab[i] = NULL;
2616
2617 /* Note that since the symbol table is an exact root we must register
2618 it with the MPS only after it has been initialized with scannable
2619 pointers -- NULL in this case. Random values look like false
2620 references into MPS memory and cause undefined behaviour (most likely
2621 assertion failures). */
2622 res = mps_root_create_table(&symtab_root, arena, mps_rank_exact(), 0,
2623 (mps_addr_t *)symtab, symtab_size);
2624 if(res != MPS_RES_OK) error("Couldn't register symtab root");
2625
2626 error_handler = &jb;
2627
2628 if(!setjmp(*error_handler)) {
2629 for(i = 0; i < LENGTH(sptab); ++i)
2630 *sptab[i].varp = make_special(sptab[i].name);
2631 for(i = 0; i < LENGTH(isymtab); ++i)
2632 *isymtab[i].varp = intern(isymtab[i].name);
2633 env = make_pair(obj_empty, obj_empty);
2634 op_env = make_pair(obj_empty, obj_empty);
2635 for(i = 0; i < LENGTH(funtab); ++i)
2636 define(env,
2637 intern(funtab[i].name),
2638 make_operator(funtab[i].name, funtab[i].entry,
2639 obj_empty, obj_empty, env, op_env));
2640 for(i = 0; i < LENGTH(optab); ++i)
2641 define(op_env,
2642 intern(optab[i].name),
2643 make_operator(optab[i].name, optab[i].entry,
2644 obj_empty, obj_empty, env, op_env));
2645 } else {
2646 fprintf(stderr,
2647 "Fatal error during initialization: %s\n",
2648 error_message);
2649 abort();
2650 }
2651
2652 /* The read-eval-print loop */
2653
2654 for(;;) {
2655 if(setjmp(*error_handler) != 0) {
2656 fprintf(stderr, "%s\n", error_message);
2657 }
2658
2659 mps_chat();
2660
2661 printf("%lu, %lu> ", (unsigned long)total,
2662 (unsigned long)mps_collections(arena));
2663 obj = read(stdin);
2664 if(obj == obj_eof) break;
2665 obj = eval(env, op_env, obj);
2666 print(obj, 6, stdout);
2667 putc('\n', stdout);
2668 }
2669
2670 puts("Bye.");
2671
2672 return 0;
2673}
2674
2675
2676/* obj_fmt_s -- object format parameter structure
2677 *
2678 * This is simply a gathering of the object format methods and the chosen
2679 * pool alignment for passing to `mps_fmt_create_A`.
2680 */
2681
2682struct mps_fmt_A_s obj_fmt_s = {
2683 sizeof(mps_word_t),
2684 obj_scan,
2685 obj_skip,
2686 obj_copy,
2687 obj_fwd,
2688 obj_isfwd,
2689 obj_pad
2690};
2691
2692
2693/* main -- program entry point and MPS initialization */
2694
2695int main(int argc, char *argv[])
2696{
2697 mps_res_t res;
2698 mps_chain_t obj_chain;
2699 mps_fmt_t obj_fmt;
2700 /* FIXME: explain this */
2701 mps_gen_param_s obj_gen_params[] = {
2702 { 150, 0.85 },
2703 { 170, 0.45 }
2704 };
2705 mps_thr_t thread;
2706 mps_root_t reg_root;
2707 void *r;
2708 void *marker = &marker;
2709
2710 /* Create an MPS arena. There is usually only one of these in a process.
2711 It holds all the MPS "global" state and is where everything happens. */
2712 res = mps_arena_create(&arena,
2713 mps_arena_class_vm(),
2714 (size_t)(1024 * 1024));
2715 if (res != MPS_RES_OK) error("Couldn't create arena");
2716
2717 /* Create the object format. */
2718 res = mps_fmt_create_A(&obj_fmt, arena, &obj_fmt_s);
2719 if (res != MPS_RES_OK) error("Couldn't create obj format");
2720
2721 /* Create a chain controlling GC strategy. FIXME: explain! */
2722 res = mps_chain_create(&obj_chain,
2723 arena,
2724 LENGTH(obj_gen_params),
2725 obj_gen_params);
2726 if (res != MPS_RES_OK) error("Couldn't create obj chain");
2727
2728 /* Create an Automatic Mostly-Copying (AMC) pool to manage the Scheme
2729 objects. This is a kind of copying garbage collector. */
2730 res = mps_pool_create(&obj_pool,
2731 arena,
2732 mps_class_amc(),
2733 obj_fmt,
2734 obj_chain);
2735 if (res != MPS_RES_OK) error("Couldn't create obj pool");
2736
2737 /* Create an allocation point for fast in-line allocation of objects
2738 from the `obj_pool`. You'd usually want one of these per thread
2739 for your primary pools. This interpreter is single threaded, though,
2740 so we just have it in a global. */
2741 res = mps_ap_create(&obj_ap, obj_pool, mps_rank_exact());
2742 if (res != MPS_RES_OK) error("Couldn't create obj allocation point");
2743
2744 /* Register the current thread with the MPS. The MPS must sometimes
2745 control or examine threads to ensure consistency when it is scanning
2746 or updating object references, so any threads that access the MPS
2747 memory need to be registered. */
2748 res = mps_thread_reg(&thread, arena);
2749 if (res != MPS_RES_OK) error("Couldn't register thread");
2750
2751 /* Register the thread as a root. This thread's stack and registers will
2752 need to be scanned by the MPS because we are passing references to
2753 objects around in C parameters, return values, and keeping them in
2754 automatic local variables. */
2755 res = mps_root_create_reg(&reg_root,
2756 arena,
2757 mps_rank_ambig(),
2758 0,
2759 thread,
2760 mps_stack_scan_ambig,
2761 marker,
2762 0);
2763 if (res != MPS_RES_OK) error("Couldn't create root");
2764
2765 /* Ask the MPS to tell us when it's garbage collecting so that we can
2766 print some messages. Completely optional. */
2767 mps_message_type_enable(arena, mps_message_type_gc());
2768 mps_message_type_enable(arena, mps_message_type_gc_start());
2769
2770 /* Trampoline into the main program. The MPS trampoline is unfortunately
2771 required to mark the top of the stack of the main thread, and on some
2772 platforms it must also catch exceptions in order to implement hardware
2773 memory barriers. */
2774 mps_tramp(&r, start, NULL, 0);
2775
2776 /* Cleaning up the MPS object with destroy methods will allow the MPS to
2777 check final consistency and warn you about bugs. It also allows the
2778 MPS to flush buffers for debugging data, etc. It's good practise
2779 to destroy MPS objects on exit if possible rather than just quitting. */
2780 mps_root_destroy(reg_root);
2781 mps_thread_dereg(thread);
2782 mps_ap_destroy(obj_ap);
2783 mps_pool_destroy(obj_pool);
2784 mps_chain_destroy(obj_chain);
2785 mps_fmt_destroy(obj_fmt);
2786 mps_arena_destroy(arena);
2787
2788 return 0;
2789}
2790
2791
2792/* C. COPYRIGHT AND LICENSE
2793 *
2794 * Copyright (C) 2001-2012 Ravenbrook Limited <http://www.ravenbrook.com/>.
2795 * All rights reserved. This is an open source license. Contact
2796 * Ravenbrook for commercial licensing options.
2797 *
2798 * Redistribution and use in source and binary forms, with or without
2799 * modification, are permitted provided that the following conditions are
2800 * met:
2801 *
2802 * 1. Redistributions of source code must retain the above copyright
2803 * notice, this list of conditions and the following disclaimer.
2804 *
2805 * 2. Redistributions in binary form must reproduce the above copyright
2806 * notice, this list of conditions and the following disclaimer in the
2807 * documentation and/or other materials provided with the distribution.
2808 *
2809 * 3. Redistributions in any form must be accompanied by information on how
2810 * to obtain complete source code for this software and any accompanying
2811 * software that uses this software. The source code must either be
2812 * included in the distribution or be available for no more than the cost
2813 * of distribution plus a nominal fee, and must be freely redistributable
2814 * under reasonable conditions. For an executable file, complete source
2815 * code means the source code for all modules it contains. It does not
2816 * include source code for modules or files that typically accompany the
2817 * major components of the operating system on which the executable file
2818 * runs.
2819 *
2820 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
2821 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2822 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
2823 * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
2824 * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2825 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2826 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2827 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
2828 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2829 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2830 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2831 */