/* * Cyclone Scheme * This file contains the C runtime used by compiled programs. */ // If this is set, GC is called every function call. // Only turn this on for debugging!!! #define DEBUG_ALWAYS_GC 1 // Debug GC flag #define DEBUG_GC 0 // Show diagnostic information for the GC when program terminates #define DEBUG_SHOW_DIAG 0 // Maximum number of args that GC will accept #define NUM_GC_ANS 100 /* STACK_GROWS_DOWNWARD is a machine-specific preprocessor switch. */ /* It is true for the Macintosh 680X0 and the Intel 80860. */ #define STACK_GROWS_DOWNWARD 1 /* STACK_SIZE is the size of the stack buffer, in bytes. */ /* Some machines like a smallish stack--i.e., 4k-16k, while others */ /* like a biggish stack--i.e., 100k-500k. */ #define STACK_SIZE 100000 /* HEAP_SIZE is the size of the 2nd generation, in bytes. */ /* HEAP_SIZE should be at LEAST 225000*sizeof(cons_type). */ #define HEAP_SIZE 6000000 long global_stack_size; long global_heap_size; /* Define size of Lisp tags. Options are "short" or "long". */ typedef long tag_type; #include #include #include #include #include #include #ifndef CLOCKS_PER_SEC /* gcc doesn't define this, even though ANSI requires it in .. */ #define CLOCKS_PER_SEC 0 #define setjmp _setjmp #define longjmp _longjmp #endif /* The following sparc hack is courtesy of Roger Critchlow. */ /* It speeds up the output by more than a factor of THREE. */ /* Do 'gcc -O -S cboyer13.c'; 'perlscript >cboyer.s'; 'gcc cboyer.s'. */ #ifdef __GNUC__ #ifdef sparc #define never_returns __attribute__ ((noreturn)) #else #define never_returns /* __attribute__ ((noreturn)) */ #endif #else #define never_returns /* __attribute__ ((noreturn)) */ #endif #if STACK_GROWS_DOWNWARD #define check_overflow(x,y) ((x) < (y)) #else #define check_overflow(x,y) ((x) > (y)) #endif /* Define tag values. (I don't trust compilers to optimize enums.) */ #define cons_tag 0 #define symbol_tag 1 #define forward_tag 2 #define closure0_tag 3 #define closure1_tag 4 #define closure2_tag 5 #define closure3_tag 6 #define closure4_tag 7 #define closureN_tag 8 #define integer_tag 9 #define double_tag 10 #define string_tag 11 #define primitive_tag 12 #define eof_tag 13 #define port_tag 14 #define boolean_tag 15 #define cvar_tag 16 #define nil NULL #define eq(x,y) (x == y) #define nullp(x) (x == NULL) #define or(x,y) (x || y) #define and(x,y) (x && y) /* Define general object type. */ typedef void *object; #define type_of(x) (((list) x)->tag) #define forward(x) (((list) x)->cons_car) /* Define value types. Depending on the underlying architecture, compiler, etc these types have extra least significant bits that can be used to mark them as values instead of objects (IE, pointer to a tagged object). On many machines, addresses are multiples of four, leaving the two least significant bits free - according to lisp in small pieces. experimenting with chars below: */ #define obj_is_char(x) ((unsigned long)(x) & (unsigned long)1) #define obj_obj2char(x) (char)((long)(x)>>1) #define obj_char2obj(c) ((void *)(((c)<<1) | 1)) #define is_value_type(x) obj_is_char(x) #define is_object_type(x) (x && !is_value_type(x)) /* Define function type. */ typedef void (*function_type)(); /* Define C-variable integration type */ typedef struct {tag_type tag; object *pvar;} cvar_type; typedef cvar_type *cvar; #define make_cvar(n,v) cvar_type n; n.tag = cvar_tag; n.pvar = v; /* Define boolean type. */ typedef struct {const tag_type tag; const char *pname;} boolean_type; typedef boolean_type *boolean; #define boolean_pname(x) (((boolean_type *) x)->pname) #define defboolean(name,pname) \ static boolean_type name##_boolean = {boolean_tag, #pname}; \ static const object boolean_##name = &name##_boolean /* Define symbol type. */ typedef struct {const tag_type tag; const char *pname; object plist;} symbol_type; typedef symbol_type *symbol; #define symbol_pname(x) (((symbol_type *) x)->pname) #define symbol_plist(x) (((symbol_type *) x)->plist) #define defsymbol(name,pname) \ static symbol_type name##_symbol = {symbol_tag, #pname, nil}; \ static const object quote_##name = &name##_symbol /* Define numeric types */ typedef struct {tag_type tag; int value;} integer_type; #define make_int(n,v) integer_type n; n.tag = integer_tag; n.value = v; typedef struct {tag_type tag; double value;} double_type; #define make_double(n,v) double_type n; n.tag = double_tag; n.value = v; /* Define string type */ typedef struct {tag_type tag; char *str;} string_type; #define make_string(cv,s) string_type cv; cv.tag = string_tag; \ { int len = strlen(s); cv.str = dhallocp; \ if ((dhallocp + len + 1) >= dhbottom + global_heap_size) { \ printf("Fatal error: data heap overflow\n"); exit(1); } \ memcpy(dhallocp, s, len + 1); dhallocp += len + 1; } /* I/O types */ // TODO: FILE* may not be good enough // consider http://stackoverflow.com/questions/6206893/how-to-implement-char-ready-in-c // TODO: a simple wrapper around FILE may not be good enough long-term // TODO: how exactly mode will be used. need to know r/w, bin/txt typedef struct {tag_type tag; FILE *fp; int mode;} port_type; #define make_port(p,f,m) port_type p; p.tag = port_tag; p.fp = f; p.mode = m; static symbol_type __EOF = {eof_tag, "", nil}; // symbol_type in lieu of custom type static const object Cyc_EOF = &__EOF; /* Define cons type. */ typedef struct {tag_type tag; object cons_car,cons_cdr;} cons_type; typedef cons_type *list; #define car(x) (((list) x)->cons_car) #define cdr(x) (((list) x)->cons_cdr) #define caar(x) (car(car(x))) #define cadr(x) (car(cdr(x))) #define cdar(x) (cdr(car(x))) #define cddr(x) (cdr(cdr(x))) #define caaar(x) (car(car(car(x)))) #define caadr(x) (car(car(cdr(x)))) #define cadar(x) (car(cdr(car(x)))) #define caddr(x) (car(cdr(cdr(x)))) #define cdaar(x) (cdr(car(car(x)))) #define cdadr(x) (cdr(car(cdr(x)))) #define cddar(x) (cdr(cdr(car(x)))) #define cdddr(x) (cdr(cdr(cdr(x)))) #define caaaar(x) (car(car(car(car(x))))) #define caaadr(x) (car(car(car(cdr(x))))) #define caadar(x) (car(car(cdr(car(x))))) #define caaddr(x) (car(car(cdr(cdr(x))))) #define cadaar(x) (car(cdr(car(car(x))))) #define cadadr(x) (car(cdr(car(cdr(x))))) #define caddar(x) (car(cdr(cdr(car(x))))) #define cadddr(x) (car(cdr(cdr(cdr(x))))) #define cdaaar(x) (cdr(car(car(car(x))))) #define cdaadr(x) (cdr(car(car(cdr(x))))) #define cdadar(x) (cdr(car(cdr(car(x))))) #define cdaddr(x) (cdr(car(cdr(cdr(x))))) #define cddaar(x) (cdr(cdr(car(car(x))))) #define cddadr(x) (cdr(cdr(car(cdr(x))))) #define cdddar(x) (cdr(cdr(cdr(car(x))))) #define cddddr(x) (cdr(cdr(cdr(cdr(x))))) #define make_cons(n,a,d) \ cons_type n; n.tag = cons_tag; n.cons_car = a; n.cons_cdr = d; #define atom(x) ((x == NULL) || (((cons_type *) x)->tag != cons_tag)) /* Closure types. (I don't trust compilers to optimize vector refs.) */ typedef struct {tag_type tag; function_type fn;} closure0_type; typedef struct {tag_type tag; function_type fn; object elt1;} closure1_type; typedef struct {tag_type tag; function_type fn; object elt1,elt2;} closure2_type; typedef struct {tag_type tag; function_type fn; object elt1,elt2,elt3;} closure3_type; typedef struct {tag_type tag; function_type fn; object elt1,elt2,elt3,elt4;} closure4_type; typedef struct {tag_type tag; function_type fn; int num_elt; object *elts;} closureN_type; typedef closure0_type *closure0; typedef closure1_type *closure1; typedef closure2_type *closure2; typedef closure3_type *closure3; typedef closure4_type *closure4; typedef closureN_type *closureN; typedef closure0_type *closure; #define mclosure0(c,f) closure0_type c; c.tag = closure0_tag; c.fn = f; #define mclosure1(c,f,a) closure1_type c; c.tag = closure1_tag; \ c.fn = f; c.elt1 = a; #define mclosure2(c,f,a1,a2) closure2_type c; c.tag = closure2_tag; \ c.fn = f; c.elt1 = a1; c.elt2 = a2; #define mclosure3(c,f,a1,a2,a3) closure3_type c; c.tag = closure3_tag; \ c.fn = f; c.elt1 = a1; c.elt2 = a2; c.elt3 = a3; #define mclosure4(c,f,a1,a2,a3,a4) closure4_type c; c.tag = closure4_tag; \ c.fn = f; c.elt1 = a1; c.elt2 = a2; c.elt3 = a3; c.elt4 = a4; #define setq(x,e) x = e #define DEBUG_mclosure0(c, f) closureN_type c; c.tag = closureN_tag; c.fn = f; \ c.num_elt = 0; c.elts = (object *)alloca(sizeof(object) * c.num_elt); #define DEBUG_mclosure1(c, f, a1) closureN_type c; c.tag = closureN_tag; c.fn = f; \ c.num_elt = 1; c.elts = (object *)alloca(sizeof(object) * c.num_elt); c.elts[0] = a1; #define DEBUG_mclosure2(c, f, a1, a2) closureN_type c; c.tag = closureN_tag; c.fn = f; \ c.num_elt = 2; c.elts = (object *)alloca(sizeof(object) * c.num_elt); c.elts[0] = a1; c.elts[1] = a2; #define DEBUG_mclosure3(c, f, a1, a2, a3) closureN_type c; c.tag = closureN_tag; c.fn = f; \ c.num_elt = 3; c.elts = (object *)alloca(sizeof(object) * c.num_elt); c.elts[0] = a1; c.elts[1] = a2; c.elts[2] = a3; #define mlist1(e1) (mcons(e1,nil)) #define mlist2(e2,e1) (mcons(e2,mlist1(e1))) #define mlist3(e3,e2,e1) (mcons(e3,mlist2(e2,e1))) #define mlist4(e4,e3,e2,e1) (mcons(e4,mlist3(e3,e2,e1))) #define mlist5(e5,e4,e3,e2,e1) (mcons(e5,mlist4(e4,e3,e2,e1))) #define mlist6(e6,e5,e4,e3,e2,e1) (mcons(e6,mlist5(e5,e4,e3,e2,e1))) #define mlist7(e7,e6,e5,e4,e3,e2,e1) (mcons(e7,mlist6(e6,e5,e4,e3,e2,e1))) #define rule(lhs,rhs) (mlist3(quote_equal,lhs,rhs)) #define make_cell(n,a) make_cons(n,a,nil); static object cell_get(object cell){ return car(cell); } static object cell_set(object cell, object value){ ((list) cell)->cons_car = value; return cell; } #define global_set(glo,value) (glo=value) /* Variable argument count support This macro is intended to be executed at the top of a function that is passed 'var' as a variable-length argument. 'count' is the number of varargs that were passed. EG: - C definition: f(object a, ...) - C call: f(1, 2, 3) - var: a - count: 3 Argument count would need to be passed by the caller of f. Presumably our compiler will compute the difference between the number of required args and the number of provided ones, and pass the difference as 'count' */ #define load_varargs(var, count) { \ int i; \ object tmp; \ list args = nil; \ va_list va; \ if (count > 0) { \ args = alloca(sizeof(cons_type)*count); \ va_start(va, var); \ for (i = 0; i < count; i++) { \ if (i) { \ tmp = va_arg(va, object); \ } else { \ tmp = var; \ } \ args[i].tag = cons_tag; \ args[i].cons_car = tmp; \ args[i].cons_cdr = (i == (count-1)) ? nil : &args[i + 1]; \ } \ va_end(va); \ } \ var = args; \ } /* Prototypes for Lisp built-in functions. */ static object Cyc_global_variables = nil; static object Cyc_get_global_variables(); static object Cyc_get_cvar(object var); static object Cyc_set_cvar(object var, object value); static object apply(object cont, object func, object args); static void Cyc_apply(int argc, closure cont, object prim, ...); static list mcons(object,object); static object terpri(void); static object Cyc_display(object); static int equal(object,object); static list assq(object,list); static object get(object,object); static object equalp(object,object); static object memberp(object,list); static char *transport(char *,int); static void GC(closure,object*,int) never_returns; static void main_main(long stack_size,long heap_size,char *stack_base) never_returns; static long long_arg(int argc,char **argv,char *name,long dval); /* Symbol Table */ /* Notes for the symbol table string->symbol can: - lookup symbol in the table - if found, return that pointer - otherwise, allocate symbol in table and return ptr to it For now, GC of symbols is missing. long-term it probably would be desirable */ char *_strdup (const char *s); static object add_symbol(symbol_type *psym); static object add_symbol_by_name(const char *name); static object find_symbol_by_name(const char *name); static object find_or_add_symbol(const char *name); list symbol_table = nil; char *_strdup (const char *s) { char *d = malloc (strlen (s) + 1); if (d) { strcpy (d,s); } return d; } static object find_symbol_by_name(const char *name) { list l = symbol_table; for (; !nullp(l); l = cdr(l)) { const char *str = symbol_pname(car(l)); if (strcmp(str, name) == 0) return car(l); } return nil; } static object add_symbol(symbol_type *psym) { symbol_table = mcons(psym, symbol_table); return psym; } static object add_symbol_by_name(const char *name) { symbol_type sym = {symbol_tag, _strdup(name), nil}; symbol_type *psym = malloc(sizeof(symbol_type)); memcpy(psym, &sym, sizeof(symbol_type)); return add_symbol(psym); } static object find_or_add_symbol(const char *name){ object sym = find_symbol_by_name(name); if (sym){ return sym; } else { return add_symbol_by_name(name); } } /* Global variables. */ static clock_t start; /* Starting time. */ static char *stack_begin; /* Initialized by main. */ static char *stack_limit1; /* Initialized by main. */ static char *stack_limit2; static char *bottom; /* Bottom of tospace. */ static char *allocp; /* Cheney allocate pointer. */ static char *alloc_end; /* TODO: not sure this is the best strategy for strings, especially if there are a lot of long, later gen strings because that will cause a lot of copying to occur during GC */ static char *dhbottom; /* Bottom of data heap */ static char *dhallocp; /* Current place in data heap */ static char *dhalloc_end; static long no_gcs = 0; /* Count the number of GC's. */ static long no_major_gcs = 0; /* Count the number of GC's. */ static volatile object gc_cont; /* GC continuation closure. */ static volatile object gc_ans[NUM_GC_ANS]; /* argument for GC continuation closure. */ static volatile int gc_num_ans; static jmp_buf jmp_main; /* Where to jump to. */ //static object test_exp1, test_exp2; /* Expressions used within test. */ /* Define the Lisp atoms that we need. */ defboolean(f,f); defboolean(t,t); defsymbol(Cyc_191procedure, procedure); //static object quote_list_f; /* Initialized by main to '(f) */ //static object quote_list_t; /* Initialized by main to '(t) */ //static volatile object unify_subst = nil; /* This is a global Lisp variable. */ DECLARE_GLOBALS /* These (crufty) printing functions are used for debugging. */ static object terpri() {printf("\n"); return nil;} static int equal(x, y) object x, y; { if (nullp(x)) return nullp(y); if (nullp(y)) return nullp(x); if (obj_is_char(x)) return obj_is_char(y) && x == y; switch(type_of(x)) { case integer_tag: return (type_of(y) == integer_tag && ((integer_type *) x)->value == ((integer_type *) y)->value); case double_tag: return (type_of(y) == double_tag && ((double_type *) x)->value == ((double_type *) y)->value); case string_tag: return (type_of(y) == string_tag && strcmp(((string_type *) x)->str, ((string_type *) y)->str) == 0); default: return x == y; } } static object Cyc_get_global_variables(){ return Cyc_global_variables; } static object Cyc_get_cvar(object var) { if (is_object_type(var) && type_of(var) == cvar_tag) { return *(((cvar_type *)var)->pvar); } return var; } static object Cyc_set_cvar(object var, object value) { if (is_object_type(var) && type_of(var) == cvar_tag) { *(((cvar_type *)var)->pvar) = value; } return var;} static object Cyc_has_cycle(object lst) { object slow_lst, fast_lst; int is_obj = is_object_type(lst); int type = type_of(lst); if (nullp(lst) || is_value_type(lst) || (is_object_type(lst) && type_of(lst) != cons_tag)) { return (boolean_f); } slow_lst = lst; fast_lst = cdr(lst); while(1) { if (nullp(fast_lst)) return boolean_f; if (nullp(cdr(fast_lst))) return boolean_f; if (eq(car(slow_lst), car(fast_lst))) return boolean_t; slow_lst = cdr(slow_lst); fast_lst = cddr(fast_lst); } } static object Cyc_display(x) object x; {object tmp = nil; object has_cycle = boolean_f; int i = 0; if (nullp(x)) {printf("()"); return x;} if (obj_is_char(x)) {printf("%c", obj_obj2char(x)); return x;} switch (type_of(x)) {case closure0_tag: case closure1_tag: case closure2_tag: case closure3_tag: case closure4_tag: case closureN_tag: printf("<%p>",(void *)((closure) x)->fn); break; case eof_tag: printf(""); break; case port_tag: printf(""); break; case primitive_tag: printf(""); break; case cvar_tag: Cyc_display(Cyc_get_cvar(x)); break; case boolean_tag: printf("#%s",((boolean_type *) x)->pname); break; case symbol_tag: printf("%s",((symbol_type *) x)->pname); break; case integer_tag: printf("%d", ((integer_type *) x)->value); break; case double_tag: printf("%lf", ((double_type *) x)->value); break; case string_tag: printf("%s", ((string_type *) x)->str); break; case cons_tag: has_cycle = Cyc_has_cycle(x); printf("("); Cyc_display(car(x)); // Experimenting with displaying lambda defs in REPL // not good enough but this is a start. would probably need // the same code in write() if (equal(quote_Cyc_191procedure, car(x))) { printf(" "); Cyc_display(cadr(x)); printf(")"); break; } for (tmp = cdr(x); tmp && ((closure) tmp)->tag == cons_tag; tmp = cdr(tmp)) { if (has_cycle == boolean_t) { if (i++ > 20) break; /* arbitrary number, for now */ } printf(" "); Cyc_display(car(tmp)); } if (has_cycle == boolean_t) { printf(" ..."); } else if (tmp) { printf(" . "); Cyc_display(tmp); } printf(")"); break; default: printf("Cyc_display: bad tag x=%ld\n", ((closure)x)->tag); getchar(); exit(0);} return x;} static object Cyc_write(x) object x; {object tmp = nil; if (nullp(x)) {printf("()\n"); return x;} if (obj_is_char(x)) {printf("#\\%c\n", obj_obj2char(x)); return x;} switch (type_of(x)) {case string_tag: printf("\"%s\"", ((string_type *) x)->str); break; default: Cyc_display(x);} printf("\n"); return x;} /* Some of these non-consing functions have been optimized from CPS. */ static object memberp(x,l) object x; list l; {for (; !nullp(l); l = cdr(l)) if (boolean_f != equalp(x,car(l))) return boolean_t; return boolean_f;} static object get(x,i) object x,i; {register object plist; register object plistd; if (nullp(x)) return x; if (type_of(x)!=symbol_tag) {printf("get: bad x=%ld\n",((closure)x)->tag); exit(0);} plist = symbol_plist(x); for (; !nullp(plist); plist = cdr(plistd)) {plistd = cdr(plist); if (eq(car(plist),i)) return car(plistd);} return nil;} static object equalp(x,y) object x,y; {for (; ; x = cdr(x), y = cdr(y)) {if (equal(x,y)) return boolean_t; if (obj_is_char(x) || obj_is_char(y) || nullp(x) || nullp(y) || type_of(x)!=cons_tag || type_of(y)!=cons_tag) return boolean_f; if (boolean_f == equalp(car(x),car(y))) return boolean_f;}} static list assq(x,l) object x; list l; {for (; !nullp(l); l = cdr(l)) {register list la = car(l); if (eq(x,car(la))) return la;} return boolean_f;} static list assoc(x,l) object x; list l; {for (; !nullp(l); l = cdr(l)) {register list la = car(l); if (boolean_f != equalp(x,car(la))) return la;} return boolean_f;} // TODO: generate these using macros??? static object __num_eq(x, y) object x, y; {if (x && y && ((integer_type *)x)->value == ((integer_type *)y)->value) return boolean_t; return boolean_f;} static object __num_gt(x, y) object x, y; {//printf("DEBUG cmp %d, x %d, y %d, x tag %d, y tag %d\n", // (((integer_type *)x)->value > ((integer_type *)y)->value), // ((integer_type *)x)->value, ((integer_type *)y)->value, // ((list)x)->tag, ((list)y)->tag); //exit(1); if (((integer_type *)x)->value > ((integer_type *)y)->value) return boolean_t; return boolean_f;} static object __num_lt(x, y) object x, y; {if (((integer_type *)x)->value < ((integer_type *)y)->value) return boolean_t; return boolean_f;} static object __num_gte(x, y) object x, y; {if (((integer_type *)x)->value >= ((integer_type *)y)->value) return boolean_t; return boolean_f;} static object __num_lte(x, y) object x, y; {if (((integer_type *)x)->value <= ((integer_type *)y)->value) return boolean_t; return boolean_f;} // TODO: static object Cyc_is_eq(x, y) object x, y) static object Cyc_is_boolean(object o){ if (!nullp(o) && !is_value_type(o) && ((list)o)->tag == boolean_tag && (eq(boolean_f, o) || eq(boolean_t, o))) return boolean_t; return boolean_f;} static object Cyc_is_cons(object o){ if (!nullp(o) && !is_value_type(o) && ((list)o)->tag == cons_tag) return boolean_t; return boolean_f;} static object Cyc_is_null(object o){ if (nullp(o)) return boolean_t; return boolean_f;} static object Cyc_is_number(object o){ if (!nullp(o) && !is_value_type(o) && ((list)o)->tag == integer_tag) return boolean_t; return boolean_f;} static object Cyc_is_symbol(object o){ if (!nullp(o) && !is_value_type(o) && ((list)o)->tag == symbol_tag) return boolean_t; return boolean_f;} static object Cyc_is_string(object o){ if (!nullp(o) && !is_value_type(o) && ((list)o)->tag == string_tag) return boolean_t; return boolean_f;} static object Cyc_is_char(object o){ if (obj_is_char(o)) return boolean_t; return boolean_f;} static object Cyc_is_procedure(object o) { int tag; if (!nullp(o) && !is_value_type(o)) { tag = type_of(o); if (tag == closure0_tag || tag == closure1_tag || tag == closure2_tag || tag == closure3_tag || tag == closure4_tag || tag == closureN_tag || tag == primitive_tag) { return boolean_t; } } return boolean_f; } static object Cyc_is_eof_object(object o) { if (!nullp(o) && !is_value_type(o) && type_of(o) == eof_tag) return boolean_t; return boolean_f;} static object Cyc_is_cvar(object o) { if (!nullp(o) && !is_value_type(o) && type_of(o) == cvar_tag) return boolean_t; return boolean_f;} static object Cyc_eq(object x, object y) { if (eq(x, y)) return boolean_t; return boolean_f; } static object Cyc_set_car(object l, object val) { ((list)l)->cons_car = val; return l; } static object Cyc_set_cdr(object l, object val) { ((list)l)->cons_cdr = val; return l; } static integer_type Cyc_length(object l){ make_int(len, 0); while(!nullp(l)){ if (((list)l)->tag != cons_tag){ printf("length - invalid parameter, expected list\n"); exit(1); } l = cdr(l); len.value++; } return len; } static string_type Cyc_number2string(object n) { char buffer[1024]; int num = ((integer_type *) n)->value; snprintf(buffer, 1024, "%d", num); make_string(str, buffer); return str; } static string_type Cyc_symbol2string(object sym) { make_string(str, symbol_pname(sym)); return str; } static object Cyc_string2symbol(object str) { object sym = find_symbol_by_name(symbol_pname(str)); if (!sym) { sym = add_symbol_by_name(symbol_pname(str)); } return sym; } static string_type Cyc_list2string(object lst){ char *buf; int i = 0; integer_type len = Cyc_length(lst); // Inefficient, walks whole list buf = alloca(sizeof(char) * (len.value + 1)); while(!nullp(lst)){ buf[i++] = obj_obj2char(car(lst)); lst = cdr(lst); } buf[i] = '\0'; make_string(str, buf); return str; } #define string2list(c,s) object c = nil; { \ char *str = ((string_type *)s)->str; \ int len = strlen(str); \ cons_type *buf; \ if (len > 0) { \ buf = alloca(sizeof(cons_type) * len); \ __string2list(str, buf, len); \ c = (object)&(buf[0]); \ } \ } static void __string2list(const char *str, cons_type *buf, int buflen){ int i = 0; while (str[i]){ buf[i].tag = cons_tag; buf[i].cons_car = obj_char2obj(str[i]); buf[i].cons_cdr = (i == buflen - 1) ? nil : buf + (i + 1); i++; } } static integer_type Cyc_string2number(object str){ make_int(n, 0); if (type_of(str) == string_tag && ((string_type *) str)->str){ // TODO: not good enough long-term since it doesn't parse floats n.value = atoi(((string_type *) str)->str); } return n; } // TODO: static string_type Cyc_string_append(int argc, object str1, ...) { // TODO: one way to do this, perhaps not the most efficient: // compute lengths of the strings, // store lens and str ptrs // allocate buffer, memcpy each str to buffer // make_string using buffer va_list ap; int i = 0, total_len = 1; // for null char int *len = alloca(sizeof(int) * argc); char *buffer, *bufferp, **str = alloca(sizeof(char *) * argc); object tmp; va_start(ap, str1); str[i] = ((string_type *)str1)->str; len[i] = strlen(str[i]); total_len += len[i]; for (i = 1; i < argc; i++) { tmp = va_arg(ap, object); str[i] = ((string_type *)tmp)->str; len[i] = strlen(str[i]); total_len += len[i]; } va_end(ap); buffer = bufferp = alloca(sizeof(char) * total_len); for (i = 0; i < argc; i++) { memcpy(bufferp, str[i], len[i]); bufferp += len[i]; } *bufferp = '\0'; make_string(result, buffer); return result; } static integer_type Cyc_char2integer(object chr){ make_int(n, obj_obj2char(chr)); return n; } static object Cyc_integer2char(object n){ int val = 0; if (!nullp(n)) { val = ((integer_type *) n)->value; } return obj_char2obj(val); } /*static object sum(object x, object y) {}*/ static void my_exit(closure) never_returns; static void my_exit(env) closure env; { #if DEBUG_SHOW_DIAG printf("my_exit: heap bytes allocated=%d time=%ld ticks no_gcs=%ld no_m_gcs=%ld\n", allocp-bottom,clock()-start,no_gcs,no_major_gcs); printf("my_exit: ticks/second=%ld\n",(long) CLOCKS_PER_SEC); #endif exit(0);} static object Cyc_error(int count, object obj1, ...) { va_list ap; object tmp; int i; va_start(ap, obj1); printf("Error: "); Cyc_display(obj1); printf("\n"); for (i = 1; i < count; i++) { tmp = va_arg(ap, object); Cyc_display(tmp); printf("\n"); } va_end(ap); exit(1); return boolean_f; } static void __halt(object obj) { #if DEBUG_SHOW_DIAG printf("\nhalt: "); Cyc_display(obj); printf("\n"); #endif my_exit(obj); } #define __sum(c,x,y) integer_type c; c.tag = integer_tag; c.value = (((integer_type *)(x))->value + ((integer_type *)(y))->value); #define __mul(c,x,y) integer_type c; c.tag = integer_tag; c.value = (((integer_type *)(x))->value * ((integer_type *)(y))->value); #define __sub(c,x,y) integer_type c; c.tag = integer_tag; c.value = (((integer_type *)(x))->value - ((integer_type *)(y))->value); #define __div(c,x,y) integer_type c; c.tag = integer_tag; c.value = (((integer_type *)(x))->value / ((integer_type *)(y))->value); /* I/O functions */ static port_type Cyc_io_current_input_port() { make_port(p, stdin, 0); return p; } static port_type Cyc_io_open_input_file(object str) { const char *fname = ((string_type *)str)->str; make_port(p, NULL, 0); p.fp = fopen(fname, "r"); return p; } static object Cyc_io_close_input_port(object port) { if (port && type_of(port) == port_tag) { FILE *stream = ((port_type *)port)->fp; if (stream) fclose(stream); ((port_type *)port)->fp = NULL; } return port; } // TODO: port arg is optional! (maybe handle that in expansion section??) static object Cyc_io_read_char(object port) { if (type_of(port) == port_tag) { int c = fgetc(((port_type *) port)->fp); if (c != EOF) { return obj_char2obj(c); } } return Cyc_EOF; } static object Cyc_io_peek_char(object port) { FILE *stream; int c; if (type_of(port) == port_tag) { stream = ((port_type *) port)->fp; c = fgetc(stream); ungetc(c, stream); if (c != EOF) { return obj_char2obj(c); } } return Cyc_EOF; } /* Primitive types */ //typedef common_type (*prim_function_type)(); //typedef void (*prim_function_type)(); typedef struct {tag_type tag; const char *pname; /*prim_function_type fn;*/} primitive_type; typedef primitive_type *primitive; #define defprimitive(name/*, fnc*/) \ static primitive_type name##_primitive = {primitive_tag, #name /*, &fnc*/}; \ static const object primitive_##name = &name##_primitive #define prim(x) (x && ((primitive)x)->tag == primitive_tag) // TODO: there has to be a better way: defprimitive(cons /*, Cyc_length*/); defprimitive(length /*, Cyc_length*/); defprimitive(car); defprimitive(cdr); defprimitive(cadr); defprimitive(set_91car_67); defprimitive(set_91cdr_67); defprimitive(eq_127); defprimitive(equal_127); defprimitive(null_127); defprimitive(_87); // The plus symbol: + defprimitive(Cyc_91global_91vars); defprimitive(has_91cycle_127); /* All constant-size objects */ typedef union { cons_type cons_t; symbol_type symbol_t; primitive_type primitive_t; integer_type integer_t; double_type double_t; string_type string_t; } common_type; /* * * @param cont - Continuation for the function to call into * @param func - Function to execute * @param args - A list of arguments to the function */ static object apply(object cont, object func, object args){ object result; common_type buf; // TODO: consider passing an argc for just this purpose // if (nullp(args)) { // printf("Error: no arguments passed to apply\n"); // exit(1); // } switch(type_of(func)) { case primitive_tag: if (func == primitive_cons) { make_cons(c, car(args), cadr(args)); buf.cons_t = c; result = &buf; } else if (func == primitive_length) { buf.integer_t = Cyc_length(car (args)); result = &buf; } else if (func == primitive_eq_127) { result = Cyc_eq(car(args), cadr(args)); } else if (func == primitive_equal_127) { result = equalp(car(args), cadr(args)); } else if (func == primitive_null_127) { object tmp = car(args); result = Cyc_is_null(tmp); } else if (func == primitive__87) { __sum(i, car(args), cadr(args)); buf.integer_t = i; result = &buf; } else if (func == primitive_car) { result = car(car(args)); } else if (func == primitive_cdr) { result = cdr(car(args)); } else if (func == primitive_cadr) { result = cadr(car(args)); } else if (func == primitive_set_91car_67) { result = Cyc_set_car(car(args), cadr(args)); } else if (func == primitive_set_91cdr_67) { result = Cyc_set_cdr(car(args), cadr(args)); } else if (func == primitive_Cyc_91global_91vars) { result = Cyc_global_variables; } else if (func == primitive_has_91cycle_127) { result = Cyc_has_cycle(car(args)); // caar(x) (car(car(x))) // cdar(x) (cdr(car(x))) // cddr(x) (cdr(cdr(x))) // caaar(x) (car(car(car(x)))) // caadr(x) (car(car(cdr(x)))) // cadar(x) (car(cdr(car(x)))) // caddr(x) (car(cdr(cdr(x)))) // cdaar(x) (cdr(car(car(x)))) // cdadr(x) (cdr(car(cdr(x)))) // cddar(x) (cdr(cdr(car(x)))) // cdddr(x) (cdr(cdr(cdr(x)))) // caaaar(x) (car(car(car(car(x))))) // caaadr(x) (car(car(car(cdr(x))))) // caadar(x) (car(car(cdr(car(x))))) // caaddr(x) (car(car(cdr(cdr(x))))) // cadaar(x) (car(cdr(car(car(x))))) // cadadr(x) (car(cdr(car(cdr(x))))) // caddar(x) (car(cdr(cdr(car(x))))) // cadddr(x) (car(cdr(cdr(cdr(x))))) // cdaaar(x) (cdr(car(car(car(x))))) // cdaadr(x) (cdr(car(car(cdr(x))))) // cdadar(x) (cdr(car(cdr(car(x))))) // cdaddr(x) (cdr(car(cdr(cdr(x))))) // cddaar(x) (cdr(cdr(car(car(x))))) // cddadr(x) (cdr(cdr(car(cdr(x))))) // cdddar(x) (cdr(cdr(cdr(car(x))))) // cddddr(x) (cdr(cdr(cdr(cdr(x))))) } else { printf("Unrecognized primitive function: %s\n", ((symbol_type *)func)->pname); exit(1); } break; default: printf("Invalid object type %ld\n", type_of(func)); exit(1); } return_funcall1(cont, result); return nil; // TODO: restructure to avoid this? // would require emitting apply's such that they are not assigning a val, // but instead they replace the final call to return_X. // Like at the end of Cyc_apply } // Version of apply meant to be called from within compiled code static void Cyc_apply(int argc, closure cont, object prim, ...){ va_list ap; object tmp; int i; list args = alloca(sizeof(cons_type) * argc); va_start(ap, prim); for (i = 0; i < argc; i++) { tmp = va_arg(ap, object); args[i].tag = cons_tag; args[i].cons_car = tmp; args[i].cons_cdr = (i == (argc-1)) ? nil : &args[i + 1]; } //printf("DEBUG applying primitive to "); //Cyc_display((object)&args[0]); //printf("\n"); va_end(ap); apply(cont, prim, (object)&args[0]); } // END apply static char *transport(x, gcgen) char *x; int gcgen; /* Transport one object. WARNING: x cannot be nil!!! */ { if (nullp(x)) return x; if (obj_is_char(x)) return x; #if DEBUG_GC printf("entered transport "); printf("transport %ld\n", type_of(x)); #endif switch (type_of(x)) {case cons_tag: {register list nx = (list) allocp; type_of(nx) = cons_tag; car(nx) = car(x); cdr(nx) = cdr(x); forward(x) = nx; type_of(x) = forward_tag; allocp = ((char *) nx)+sizeof(cons_type); return (char *) nx;} case closure0_tag: {register closure0 nx = (closure0) allocp; type_of(nx) = closure0_tag; nx->fn = ((closure0) x)->fn; forward(x) = nx; type_of(x) = forward_tag; allocp = ((char *) nx)+sizeof(closure0_type); return (char *) nx;} case closure1_tag: {register closure1 nx = (closure1) allocp; type_of(nx) = closure1_tag; nx->fn = ((closure1) x)->fn; nx->elt1 = ((closure1) x)->elt1; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(closure1_type); return (char *) nx;} case closure2_tag: {register closure2 nx = (closure2) allocp; type_of(nx) = closure2_tag; nx->fn = ((closure2) x)->fn; nx->elt1 = ((closure2) x)->elt1; nx->elt2 = ((closure2) x)->elt2; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(closure2_type); return (char *) nx;} case closure3_tag: {register closure3 nx = (closure3) allocp; type_of(nx) = closure3_tag; nx->fn = ((closure3) x)->fn; nx->elt1 = ((closure3) x)->elt1; nx->elt2 = ((closure3) x)->elt2; nx->elt3 = ((closure3) x)->elt3; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(closure3_type); return (char *) nx;} case closure4_tag: {register closure4 nx = (closure4) allocp; type_of(nx) = closure4_tag; nx->fn = ((closure4) x)->fn; nx->elt1 = ((closure4) x)->elt1; nx->elt2 = ((closure4) x)->elt2; nx->elt3 = ((closure4) x)->elt3; nx->elt4 = ((closure4) x)->elt4; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(closure4_type); return (char *) nx;} case closureN_tag: {register closureN nx = (closureN) allocp; int i; type_of(nx) = closureN_tag; nx->fn = ((closureN) x)->fn; nx->num_elt = ((closureN) x)->num_elt; nx->elts = (object *)(((char *)nx) + sizeof(closureN_type)); for (i = 0; i < nx->num_elt; i++) { nx->elts[i] = ((closureN) x)->elts[i]; } forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(closureN_type) + sizeof(object) * nx->num_elt; return (char *) nx;} case string_tag: {register string_type *nx = (string_type *) allocp; type_of(nx) = string_tag; if (gcgen == 0) { // Minor, data heap is not relocated nx->str = ((string_type *)x)->str; } else { // Major collection, data heap is moving nx->str = dhallocp; int len = strlen(((string_type *) x)->str); memcpy(dhallocp, ((string_type *) x)->str, len + 1); dhallocp += len + 1; } forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(integer_type); return (char *) nx;} case integer_tag: {register integer_type *nx = (integer_type *) allocp; type_of(nx) = integer_tag; nx->value = ((integer_type *) x)->value; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(integer_type); return (char *) nx;} case port_tag: {register port_type *nx = (port_type *) allocp; type_of(nx) = port_tag; nx->fp = ((port_type *) x)->fp; nx->mode = ((port_type *) x)->mode; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(port_type); return (char *) nx;} case cvar_tag: {register cvar_type *nx = (cvar_type *) allocp; type_of(nx) = cvar_tag; nx->pvar = ((cvar_type *) x)->pvar; forward(x) = nx; type_of(x) = forward_tag; x = (char *) nx; allocp = ((char *) nx)+sizeof(cvar_type); return (char *) nx;} case forward_tag: return (char *) forward(x); case eof_tag: break; case primitive_tag: break; case boolean_tag: break; case symbol_tag: break; // JAE TODO: raise an error here? Should not be possible in real code, though (IE, without GC DEBUG flag) default: printf("transport: bad tag x=%p x.tag=%ld\n",(void *)x,type_of(x)); exit(0);} return x;} /* Use overflow macro which already knows which way the stack goes. */ /* Major collection, transport objects on stack or old heap */ #define transp(p) \ temp = (p); \ if (DEBUG_ALWAYS_GC || \ (check_overflow(low_limit,temp) && \ check_overflow(temp,high_limit)) || \ (check_overflow(old_heap_low_limit - 1, temp) && \ check_overflow(temp,old_heap_high_limit + 1))) \ (p) = (object) transport(temp,major); static void GC_loop(int major, closure cont, object *ans, int num_ans) {char foo; int i; register object temp; register object low_limit = &foo; /* Move live data above us. */ register object high_limit = stack_begin; register char *scanp = allocp; /* Cheney scan pointer. */ register object old_heap_low_limit = low_limit; // Minor-GC default register object old_heap_high_limit = high_limit; // Minor-GC default char *tmp_bottom = bottom; /* Bottom of tospace. */ char *tmp_allocp = allocp; /* Cheney allocate pointer. */ char *tmp_alloc_end = alloc_end; char *tmp_dhbottom = dhbottom; char *tmp_dhallocp = dhallocp; char *tmp_dhallocp_end = dhalloc_end; if (major) { // Initialize new heap (TODO: make a function for this) bottom = calloc(1,global_heap_size); allocp = (char *) ((((long) bottom)+7) & -8); alloc_end = allocp + global_heap_size - 8; scanp = allocp; old_heap_low_limit = tmp_bottom; old_heap_high_limit = tmp_alloc_end; dhallocp = dhbottom = calloc(1, global_heap_size); dhalloc_end = dhallocp + global_heap_size - 8; } #if DEBUG_GC printf("\n=== started GC type = %d === \n", major); #endif /* Transport GC's continuation and its argument. */ transp(cont); gc_cont = cont; gc_num_ans = num_ans; #if DEBUG_GC printf("DEBUG done transporting cont\n"); #endif /* Prevent overrunning buffer */ if (num_ans > NUM_GC_ANS) { printf("Fatal error - too many arguments (%d) to GC\n", num_ans); exit(1); } for (i = 0; i < num_ans; i++){ transp(ans[i]); gc_ans[i] = ans[i]; } #if DEBUG_GC printf("DEBUG done transporting gc_ans\n"); #endif /* Transport global variables. */ //transp(unify_subst); transp(Cyc_global_variables); // Internal global used by the runtime GC_GLOBALS while (scanpelt1); scanp += sizeof(closure1_type); break; case closure2_tag: #if DEBUG_GC printf("DEBUG transport closure2 \n"); #endif transp(((closure2) scanp)->elt1); transp(((closure2) scanp)->elt2); scanp += sizeof(closure2_type); break; case closure3_tag: #if DEBUG_GC printf("DEBUG transport closure3 \n"); #endif transp(((closure3) scanp)->elt1); transp(((closure3) scanp)->elt2); transp(((closure3) scanp)->elt3); scanp += sizeof(closure3_type); break; case closure4_tag: #if DEBUG_GC printf("DEBUG transport closure4 \n"); #endif transp(((closure4) scanp)->elt1); transp(((closure4) scanp)->elt2); transp(((closure4) scanp)->elt3); transp(((closure4) scanp)->elt4); scanp += sizeof(closure4_type); break; case closureN_tag: #if DEBUG_GC printf("DEBUG transport closureN \n"); #endif {int i; int n = ((closureN) scanp)->num_elt; for (i = 0; i < n; i++) { transp(((closureN) scanp)->elts[i]); } scanp += sizeof(closureN_type) + sizeof(object) * n; } break; case string_tag: #if DEBUG_GC printf("DEBUG transport string \n"); #endif scanp += sizeof(string_type); break; case integer_tag: #if DEBUG_GC printf("DEBUG transport integer \n"); #endif scanp += sizeof(integer_type); break; case port_tag: #if DEBUG_GC printf("DEBUG transport port \n"); #endif scanp += sizeof(port_type); break; case cvar_tag: #if DEBUG_GC printf("DEBUG transport cvar \n"); #endif scanp += sizeof(cvar_type); break; case eof_tag: case primitive_tag: case symbol_tag: case boolean_tag: default: printf("GC: bad tag scanp=%p scanp.tag=%ld\n",(void *)scanp,type_of(scanp)); exit(0);} if (major) { free(tmp_bottom); free(tmp_dhbottom); } } static void GC(cont,ans,num_ans) closure cont; object *ans; int num_ans; { /* Only room for one more minor-GC, so do a major one. * Not sure this is the best strategy, it may be better to do major * ones sooner, perhaps after every x minor GC's. * * Also may need to consider dynamically increasing heap size, but * by how much (1.3x, 1.5x, etc) and when? I suppose when heap usage * after a collection is above a certain percentage, then it would be * necessary to increase heap size the next time. */ if (allocp >= (bottom + (global_heap_size - global_stack_size))) { //printf("Possibly only room for one more minor GC. no_gcs = %ld\n", no_gcs); no_major_gcs++; GC_loop(1, cont, ans, num_ans); } else { no_gcs++; /* Count the number of minor GC's. */ GC_loop(0, cont, ans, num_ans); } /* You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind... */ longjmp(jmp_main,1); /* Return globals gc_cont, gc_ans. */ } /* This heap cons is used only for initialization. */ static list mcons(a,d) object a,d; {register cons_type *c = malloc(sizeof(cons_type)); c->tag = cons_tag; c->cons_car = a; c->cons_cdr = d; return c;} static void c_entry_pt(int,closure,closure); static void main_main (stack_size,heap_size,stack_base) long stack_size,heap_size; char *stack_base; {char in_my_frame; mclosure0(clos_exit,&my_exit); /* Create a closure for exit function. */ gc_ans[0] = &clos_exit; /* It becomes the argument to test. */ gc_num_ans = 1; /* Allocate stack buffer. */ stack_begin = stack_base; #if STACK_GROWS_DOWNWARD stack_limit1 = stack_begin - stack_size; stack_limit2 = stack_limit1 - 2000; #else stack_limit1 = stack_begin + stack_size; stack_limit2 = stack_limit1 + 2000; #endif #if DEBUG_SHOW_DIAG printf("main: sizeof(cons_type)=%ld\n",(long) sizeof(cons_type)); #endif if (check_overflow(stack_base,&in_my_frame)) {printf("main: Recompile with STACK_GROWS_DOWNWARD set to %ld\n", (long) (1-STACK_GROWS_DOWNWARD)); exit(0);} #if DEBUG_SHOW_DIAG printf("main: stack_size=%ld stack_base=%p stack_limit1=%p\n", stack_size,(void *)stack_base,(void *)stack_limit1); printf("main: Try different stack sizes from 4 K to 1 Meg.\n"); #endif /* Do initializations of Lisp objects and rewrite rules. quote_list_f = mlist1(boolean_f); quote_list_t = mlist1(boolean_t); */ /* Make temporary short names for certain atoms. */ { /* Define the rules, but only those that are actually referenced. */ /* Create closure for the test function. */ mclosure0(run_test,&c_entry_pt); gc_cont = &run_test; /* Initialize constant expressions for the test runs. */ /* Allocate heap area for second generation. */ /* Use calloc instead of malloc to assure pages are in main memory. */ #if DEBUG_SHOW_DIAG printf("main: Allocating and initializing heap...\n"); #endif bottom = calloc(1,heap_size); allocp = (char *) ((((long) bottom)+7) & -8); alloc_end = allocp + heap_size - 8; dhallocp = dhbottom = calloc(1, heap_size); dhalloc_end = dhallocp + heap_size - 8; #if DEBUG_SHOW_DIAG printf("main: heap_size=%ld allocp=%p alloc_end=%p\n", (long) heap_size,(void *)allocp,(void *)alloc_end); printf("main: Try a larger heap_size if program bombs.\n"); printf("Starting...\n"); #endif start = clock(); /* Start the timing clock. */ /* Tank, load the jump program... */ setjmp(jmp_main); AFTER_LONGJMP /* */ printf("main: your setjmp and/or longjmp are broken.\n"); exit(0);}} static long long_arg(argc,argv,name,dval) int argc; char **argv; char *name; long dval; {int j; for(j=1;(j+1)