/* symbols.c -- Lisp symbol handling Copyright (C) 1993, 1994 John Harper $Id: symbols.c,v 1.82 2001/08/08 06:15:32 jsh Exp $ This file is part of Jade. Jade is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Jade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jade; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define NDEBUG /* AIX requires this to be the first thing in the file. */ #include #ifdef __GNUC__ # define alloca __builtin_alloca #else # if HAVE_ALLOCA_H # include # else # ifdef _AIX #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca (); # endif # endif # endif #endif #include "repint.h" #include #include #include #include /* The number of hash buckets in each rep_obarray, this is a prime number. */ #define rep_OBSIZE 509 #define rep_KEY_OBSIZE 127 #define rep_FUNARGBLK_SIZE 204 /* ~4k */ /* Closure allocation blocks */ typedef struct rep_funarg_block_struct { struct rep_funarg_block_struct *next; rep_ALIGN_CELL(rep_funarg data[rep_FUNARGBLK_SIZE]); } rep_funarg_block; /* Main storage of symbols. */ repv rep_obarray, rep_keyword_obarray; /* Plist storage */ static repv plist_structure; DEFSYM(t, "t"); DEFSYM(documentation, "documentation"); DEFSYM(permanent_local, "permanent-local"); /* Function vectors to implement local symbols through. */ repv (*rep_deref_local_symbol_fun)(repv sym) = 0; repv (*rep_set_local_symbol_fun)(repv sym, repv val) = 0; /* This value is stored in the cells of a symbol to denote a void object. */ rep_ALIGN_CELL(static rep_cell void_object) = { rep_Void }; repv rep_void_value = rep_VAL(&void_object); /* The special value which signifies the end of a hash-bucket chain. It can be any Lisp object which isn't a symbol. */ #define OB_NIL rep_VAL(&void_object) /* Used to mark lexical bindings */ rep_ALIGN_CELL(static rep_cell lextag) = { rep_Void }; #define LEXTAG rep_VAL(&lextag) static rep_funarg_block *funarg_block_chain; static rep_funarg *funarg_freelist; int rep_allocated_funargs, rep_used_funargs; /* support for scheme boolean type */ repv rep_scm_t, rep_scm_f; repv rep_undefined_value; /* Symbol management */ DEFUN("make-symbol", Fmake_symbol, Smake_symbol, (repv name), rep_Subr1) /* ::doc:rep.lang.symbols#make-symbol:: make-symbol NAME Returns a new, uninterned, symbol with print-name NAME. It's value and function definition are both void and it has a nil property-list. ::end:: */ { rep_DECLARE1(name, rep_STRINGP); return rep_make_tuple (rep_Symbol, rep_NULL, name); } static void symbol_sweep(void) { /* Need to clear mark bits of dumped symbols, since they're mutable */ if (rep_dumped_symbols_start != rep_dumped_symbols_end) { rep_symbol *s; for(s = rep_dumped_symbols_start; s < rep_dumped_symbols_end; s++) { if(rep_GC_CELL_MARKEDP(rep_VAL(s))) rep_GC_CLR_CELL(rep_VAL(s)); } } } static int symbol_cmp(repv v1, repv v2) { if(rep_TYPE(v1) == rep_TYPE(v2)) { if (v1 == v2) return 0; else return rep_value_cmp (rep_SYM(v1)->name, rep_SYM(v2)->name); } else return 1; } static void symbol_princ(repv strm, repv obj) { rep_stream_puts(strm, rep_PTR(rep_SYM(obj)->name), -1, rep_TRUE); } static void symbol_print(repv strm, repv obj) { /* output a maximum of 2n chars for a symbol name of length n */ u_char *buf = alloca (rep_STRING_LEN (rep_SYM (obj)->name) * 2); register u_char *out = buf; register u_char *s; rep_bool seen_digit = rep_FALSE; if (rep_SYMBOL_LITERAL_P (obj)) { symbol_princ (strm, obj); return; } s = rep_STR (rep_SYM (obj)->name); switch (*s++) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': seen_digit = rep_TRUE; case '-': case '+': case '.': pass1: switch (*s++) { case 0: if (seen_digit) *out++ = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': seen_digit = rep_TRUE; case '/': case '.': goto pass1; } } s = rep_STR (rep_SYM (obj)->name); while (1) { u_char c = *s++; switch (c) { case 0: goto out; case ' ': case '\t': case '\n': case '\f': case '(': case ')': case '[': case ']': case '\'': case '"': case ';': case '\\': case '|': case ',': case '`': *out++ = '\\'; break; case '#': if (!(rep_KEYWORDP (obj) && s-1 == rep_STR (rep_SYM (obj)->name))) *out++ = '\\'; break; default: if (iscntrl (c)) *out++ = '\\'; break; } *out++ = c; } out: rep_stream_puts (strm, buf, out - buf, rep_FALSE); } void rep_intern_static(repv *symp, repv name) { if((*symp = Fintern(name, Qnil))) rep_mark_static(symp); else abort(); } static inline u_long hash(u_char *str) { register u_long value = 0; while(*str) value = (value * 33) + *str++; return(value); } DEFUN("make-obarray", Fmake_obarray, Smake_obarray, (repv size), rep_Subr1) /* ::doc:rep.lang.symbols#make-obarray:: make-obarray SIZE Creates a new structure for storing symbols in. This is basically a vector with a few slight differences (all elements initialised to a special value). ::end:: */ { rep_DECLARE1(size, rep_INTP); return(Fmake_vector(size, OB_NIL)); } DEFUN("find-symbol", Ffind_symbol, Sfind_symbol, (repv name, repv ob), rep_Subr2) /* ::doc:rep.lang.symbols#find-symbol:: find-symbol NAME [OBARRAY] Returns the symbol with print-name NAME, found by searching OBARRAY (or the default `rep_obarray' if nil), or nil if no such symbol exists. ::end:: */ { int vsize; rep_DECLARE1(name, rep_STRINGP); if(!rep_VECTORP(ob)) ob = rep_obarray; if((vsize = rep_VECT_LEN(ob)) == 0) return(Qnil); ob = rep_VECT(ob)->array[hash(rep_STR(name)) % vsize]; while(rep_SYMBOLP(ob)) { if(!strcmp(rep_STR(name), rep_STR(rep_SYM(ob)->name))) return(ob); ob = rep_SYM(ob)->next; } return(Qnil); } DEFSTRING(already_interned, "Symbol is already interned"); DEFUN("intern-symbol", Fintern_symbol, Sintern_symbol, (repv sym, repv ob), rep_Subr2) /* ::doc:rep.lang.symbols#intern-symbol:: intern-symbol SYMBOL [OBARRAY] Stores SYMBOL in OBARRAY (or the default). If SYMBOL has already been interned somewhere an error is signalled. ::end:: */ { int vsize, hashid; rep_DECLARE1(sym, rep_SYMBOLP); if(rep_SYM(sym)->next != rep_NULL) { Fsignal(Qerror, rep_list_2(rep_VAL(&already_interned), sym)); return rep_NULL; } if(!rep_VECTORP(ob)) ob = rep_obarray; if((vsize = rep_VECT_LEN(ob)) == 0) return rep_NULL; hashid = hash(rep_STR(rep_SYM(sym)->name)) % vsize; rep_SYM(sym)->next = rep_VECT(ob)->array[hashid]; rep_VECT(ob)->array[hashid] = sym; return(sym); } DEFUN("intern", Fintern, Sintern, (repv name, repv ob), rep_Subr2) /* ::doc:rep.lang.symbols#intern:: intern NAME [OBARRAY] If a symbol with print-name exists in OBARRAY (or the default) return it. Else use `(make-symbol NAME)' to create a new symbol, intern that into the OBARRAY, then return it. ::end:: */ { repv sym; rep_DECLARE1(name, rep_STRINGP); if(!(sym = Ffind_symbol(name, ob)) || (rep_NILP(sym))) { sym = Fmake_symbol(name); if(sym) return(Fintern_symbol(sym, ob)); } return(sym); } DEFUN("unintern", Funintern, Sunintern, (repv sym, repv ob), rep_Subr2) /* ::doc:rep.lang.symbols#unintern:: unintern SYMBOL [OBARRAY] Removes SYMBOL from OBARRAY (or the default). Use this with caution. ::end:: */ { repv list; int vsize, hashid; rep_DECLARE1(sym, rep_SYMBOLP); if(!rep_VECTORP(ob)) ob = rep_obarray; if((vsize = rep_VECT_LEN(ob)) == 0) return rep_NULL; hashid = hash(rep_STR(rep_SYM(sym)->name)) % vsize; list = rep_VECT(ob)->array[hashid]; rep_VECT(ob)->array[hashid] = OB_NIL; while(rep_SYMBOLP(list)) { repv nxt = rep_SYM(list)->next; if(list != sym) { rep_SYM(list)->next = rep_VECT(ob)->array[hashid]; rep_VECT(ob)->array[hashid] = rep_VAL(list); } list = nxt; } rep_SYM(sym)->next = rep_NULL; return(sym); } /* Closures */ DEFUN("make-closure", Fmake_closure, Smake_closure, (repv fun, repv name), rep_Subr2) /* ::doc:rep.lang.interpreter#make-closure:: make-closure FUNCTION &optional NAME Return a functional object which makes the closure of FUNCTION and the current environment. ::end:: */ { rep_funarg *f; if(!funarg_freelist) { rep_funarg_block *sb = rep_ALLOC_CELL(sizeof(rep_funarg_block)); if(sb) { int i; rep_allocated_funargs += rep_FUNARGBLK_SIZE; sb->next = funarg_block_chain; funarg_block_chain = sb; for(i = 0; i < (rep_FUNARGBLK_SIZE - 1); i++) sb->data[i].car = rep_VAL(&sb->data[i + 1]); sb->data[i].car = rep_VAL(funarg_freelist); funarg_freelist = sb->data; } } f = funarg_freelist; funarg_freelist = rep_FUNARG (f->car); rep_data_after_gc += sizeof (rep_funarg); f->car = rep_Funarg; f->fun = fun; f->name = name; f->env = rep_env; f->structure = rep_structure; return rep_VAL (f); } DEFUN("closure-function", Fclosure_function, Sclosure_function, (repv funarg), rep_Subr1) /* ::doc:rep.lang.interpreter#closure-function:: closure-function FUNARG Return the function value associated with the closure FUNARG. ::end:: */ { rep_DECLARE1(funarg, rep_FUNARGP); return rep_FUNARG(funarg)->fun; } DEFUN("set-closure-function", Fset_closure_function, Sset_closure_function, (repv funarg, repv fun), rep_Subr2) /* ::doc:rep.lang.interpreter#set-closure-function:: set-closure-function FUNARG FUNCTION Set the function value in the closure FUNARG to FUNCTION. ::end:: */ { rep_DECLARE1(funarg, rep_FUNARGP); rep_FUNARG(funarg)->fun = fun; return fun; } DEFUN("closure-structure", Fclosure_structure, Sclosure_structure, (repv funarg), rep_Subr1) /* ::doc:rep.structures#closure-function:: closure-function FUNARG Return the structure associated with the closure FUNARG. ::end:: */ { rep_DECLARE1(funarg, rep_FUNARGP); return rep_FUNARG(funarg)->structure; } DEFUN ("set-closure-structure", Fset_closure_structure, Sset_closure_structure, (repv closure, repv structure), rep_Subr2) { rep_DECLARE1 (closure, rep_FUNARGP); rep_DECLARE2 (structure, rep_STRUCTUREP); rep_FUNARG (closure)->structure = structure; return Qnil; } DEFUN("closure-name", Fclosure_name, Sclosure_name, (repv funarg), rep_Subr1) /* ::doc:rep.lang.interpreter#closure-name:: closure-name FUNARG Return the name associated with the closure FUNARG. ::end:: */ { rep_DECLARE1(funarg, rep_FUNARGP); return rep_FUNARG(funarg)->name; } DEFUN("closurep", Fclosurep, Sclosurep, (repv arg), rep_Subr1) /* ::doc:rep.lang.interpreter#closurep:: funargp ARG Returns t if ARG is a closure ::end:: */ { return rep_FUNARGP(arg) ? Qt : Qnil; } DEFUN("set-special-environment", Fset_special_environment, Sset_special_environment, (repv env, repv structure), rep_Subr2) /* ::doc:rep.structures#set-special-environment:: set-special-environment ENV STRUCTURE ::end:: */ { rep_DECLARE2 (structure, rep_STRUCTUREP); rep_STRUCTURE (structure)->special_env = env; return Qt; } static void funarg_sweep (void) { rep_funarg_block *sb = funarg_block_chain; funarg_freelist = NULL; rep_used_funargs = 0; while(sb) { int i; rep_funarg_block *nxt = sb->next; for(i = 0; i < rep_FUNARGBLK_SIZE; i++) { /* if on the freelist then the CELL_IS_8 bit will be unset (since the pointer is long aligned) */ if (rep_CELL_CONS_P(rep_VAL(&sb->data[i])) || !rep_GC_CELL_MARKEDP(rep_VAL(&sb->data[i]))) { sb->data[i].car = rep_VAL(funarg_freelist); funarg_freelist = &sb->data[i]; } else { rep_GC_CLR_CELL(rep_VAL(&sb->data[i])); rep_used_funargs++; } } sb = nxt; } } /* Returns (SYM . VALUE) if a lexical binding, or nil */ static repv search_environment (repv sym) { register repv env; for (env = rep_env; env != Qnil; env = rep_CDR (env)) { if (rep_CONSP (rep_CAR (env)) && rep_CAAR(env) == LEXTAG && rep_CADAR(env) == sym) { return rep_CDAR (env); } } return Qnil; } /* this is also in lispmach.c and fluids.c */ static inline repv inlined_search_special_bindings (repv sym) { register repv env; for (env = rep_special_bindings; env != Qnil; env = rep_CDR (env)) { if (rep_CAAR(env) == sym) return rep_CAR (env); } return Qnil; } static repv search_special_bindings (repv sym) { return inlined_search_special_bindings (sym); } static inline int inlined_search_special_environment (repv sym) { register repv env = rep_SPECIAL_ENV; while (rep_CONSP(env) && rep_CAR(env) != sym) env = rep_CDR(env); if (rep_CONSP(env)) return 1; else if (env == Qt) return -1; else return 0; } static int search_special_environment__ (repv sym) { return inlined_search_special_environment (sym); } static inline int search_special_environment (repv sym) { if (rep_SPECIAL_ENV == Qt) return -1; else return search_special_environment__ (sym); } repv rep_call_with_closure (repv closure, repv (*fun)(repv arg), repv arg) { repv ret = rep_NULL; if (rep_FUNARGP (closure)) { struct rep_Call lc; lc.fun = lc.args = Qnil; rep_PUSH_CALL (lc); rep_USE_FUNARG (closure); ret = fun (arg); rep_POP_CALL (lc); } return ret; } /* Symbol binding */ repv rep_bind_special (repv oldList, repv symbol, repv newVal) { if (inlined_search_special_environment (symbol)) { rep_special_bindings = Fcons (Fcons (symbol, newVal), rep_special_bindings); oldList = rep_MARK_SPEC_BINDING (oldList); } else Fsignal (Qvoid_value, rep_LIST_1(symbol)); return oldList; } /* This give SYMBOL a new value, saving the old one onto the front of the list OLDLIST. OLDLIST is structured like (NSPECIALS . NLEXICALS) Returns the new version of OLDLIST. */ repv rep_bind_symbol(repv oldList, repv symbol, repv newVal) { if (oldList == Qnil) oldList = rep_NEW_FRAME; if (rep_SYM(symbol)->car & rep_SF_SPECIAL) { /* special binding */ oldList = rep_bind_special (oldList, symbol, newVal); } else { rep_env = Fcons (Fcons (LEXTAG, Fcons (symbol, newVal)), rep_env); oldList = rep_MARK_LEX_BINDING (oldList); } return oldList; } /* Undoes what the above function does. Returns the number of special bindings undone. */ int rep_unbind_symbols(repv oldList) { if (oldList != Qnil) { register repv tem; int lexicals, specials; int i; assert (rep_INTP(oldList)); lexicals = rep_LEX_BINDINGS (oldList); specials = rep_SPEC_BINDINGS (oldList); tem = rep_env; for (i = lexicals; i > 0; i--) tem = rep_CDR (tem); rep_env = tem; tem = rep_special_bindings; for (i = specials; i > 0; i--) { tem = rep_CDR (tem); } rep_special_bindings = tem; assert (rep_special_bindings != rep_void_value); assert (rep_env != rep_void_value); return specials; } else return 0; } repv rep_add_binding_to_env (repv env, repv sym, repv value) { return Fcons (Fcons (LEXTAG, Fcons (sym, value)), env); } /* More lisp functions */ DEFUN("defvar", Fdefvar, Sdefvar, (repv args, repv tail_posn), rep_SF) /* ::doc:rep.lang.interpreter#defvar:: defvar NAME [DEFAULT-VALUE [DOC-STRING]] Define a special variable called NAME whose standard value is DEFAULT- VALUE. If NAME is already bound to a value (that's not an autoload definition) it is left as it is, otherwise DEFAULT-VALUE is evaluated and the special value of NAME is bound to the result. If DOC-STRING is given, and is a string, it will be used to set the `documentation' property of the symbol NAME. (If the symbol NAME is marked buffer-local the default value of the variable will be set (if necessary) not the local value.) ::end:: */ { if(rep_CONSP(args)) { int spec; repv sym = rep_CAR(args), val; rep_bool need_to_eval; repv tmp = Fdefault_boundp(sym); if(!tmp) return rep_NULL; if (rep_CONSP(rep_CDR(args))) { val = rep_CADR(args); args = rep_CDDR (args); } else { val = Qnil; args = Qnil; } need_to_eval = rep_TRUE; if(!rep_NILP(tmp)) { /* Variable is bound, see if it's an autoload defn to overwrite. */ repv val = Fsymbol_value (sym, Qt); if (rep_FUNARGP(val)) { val = rep_FUNARG(val)->fun; if(rep_CONSP(val) && rep_CAR(val) == Qautoload) { Fmakunbound (sym); tmp = Qnil; } } } /* Only allowed to defvar in restricted environments if the symbol hasn't yet been defvar'd or it's weak */ spec = search_special_environment (sym); if (spec == 0 && (rep_SYM(sym)->car & rep_SF_DEFVAR) && !(rep_SYM(sym)->car & rep_SF_WEAK)) { return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ } /* if initially making it special, check for a lexical binding in the current module */ if (!(rep_SYM(sym)->car & rep_SF_SPECIAL)) { repv tem = rep_get_initial_special_value (sym); if (tem) { val = tem; need_to_eval = rep_FALSE; tmp = Qnil; } } /* Only set the [default] value if its not boundp or the definition is weak and we're currently unrestricted */ if(rep_NILP(tmp) || ((rep_SYM(sym)->car & rep_SF_WEAK) && !(rep_SYM(sym)->car & rep_SF_WEAK_MOD) && rep_SPECIAL_ENV == Qt)) { if (need_to_eval) { rep_GC_root gc_sym, gc_args; rep_PUSHGC (gc_sym, sym); rep_PUSHGC (gc_args, args); val = Feval (val); rep_POPGC; rep_POPGC; if (!val) return rep_NULL; } Fstructure_define (rep_specials_structure, sym, val); } rep_SYM(sym)->car |= rep_SF_SPECIAL | rep_SF_DEFVAR; if (spec == 0) { /* defvar'ing an undefvar'd variable from a restricted environment sets it as weak, and adds it to the env */ rep_SYM(sym)->car |= rep_SF_WEAK; rep_SPECIAL_ENV = Fcons (sym, rep_SPECIAL_ENV); } else if (rep_SPECIAL_ENV == Qt && (rep_SYM(sym)->car & rep_SF_WEAK)) { /* defvar'ing a weak variable from an unrestricted environment removes the weak status, but marks it as `was weak, but now strong'. This prevents exploits such as: [restricted special environment] (defvar special-var "/bin/rm") [unrestricted environment] (defvar special-var "ls") [back in restricted environment] (setq special-var "/bin/rm") --> error Setting the variable the first time (since it's unbound) adds it to the restricted environment, but defvar'ing effectively removes it */ rep_SYM(sym)->car &= ~rep_SF_WEAK; rep_SYM(sym)->car |= rep_SF_WEAK_MOD; } if(rep_CONSP(args)) { repv doc = rep_CAR(args); if (rep_STRINGP (doc)) { if (Fput(sym, Qdocumentation, doc) == rep_NULL) return rep_NULL; } } return sym; } else return rep_signal_missing_arg (1); } DEFUN("symbol-value", Fsymbol_value, Ssymbol_value, (repv sym, repv no_err), rep_Subr2) /* ::doc:rep.lang.symbols#symbol-value:: symbol-value SYMBOL Returns the value of SYMBOL, if SYMBOL is flagged as having buffer-local values look for one of those first. ::end:: */ /* Second argument (NO-ERR) means don't signal an error if the value is void. */ { /* Some of this function is hardcoded into the OP_REFQ instruction in lispmach.c */ repv val = rep_void_value; rep_DECLARE1(sym, rep_SYMBOLP); if (rep_SYM(sym)->car & rep_SF_SPECIAL) { int spec = inlined_search_special_environment (sym); /* modified-weak specials can only be accessed from an unrestricted environment */ if (spec < 0 || (spec > 0 && !(rep_SYM(sym)->car & rep_SF_WEAK_MOD))) { if(rep_SYM(sym)->car & rep_SF_LOCAL) val = (*rep_deref_local_symbol_fun)(sym); if (val == rep_void_value) { repv tem = inlined_search_special_bindings (sym); if (tem != Qnil) val = rep_CDR (tem); else val = F_structure_ref (rep_specials_structure, sym); } } } else { /* lexical variable */ repv tem = search_environment (sym); if (tem != Qnil) val = rep_CDR(tem); else val = F_structure_ref (rep_structure, sym); } if (rep_SYM(sym)->car & rep_SF_DEBUG) rep_single_step_flag = rep_TRUE; if(no_err == Qnil && rep_VOIDP(val)) return Fsignal(Qvoid_value, rep_LIST_1(sym)); else return val; } DEFUN("default-value", Fdefault_value, Sdefault_value, (repv sym, repv no_err), rep_Subr2) /* ::doc:rep.lang.symbols#default-value:: default-value SYMBOL Returns the default value of the symbol SYMBOL. This will be the value of SYMBOL in buffers or windows which do not have their own local value. ::end:: */ { repv val = rep_void_value; rep_DECLARE1(sym, rep_SYMBOLP); if (rep_SYM(sym)->car & rep_SF_SPECIAL) { int spec = search_special_environment (sym); if (spec < 0 || (spec > 0 && !(rep_SYM(sym)->car & rep_SF_WEAK_MOD))) { repv tem = search_special_bindings (sym); if (tem != Qnil) val = rep_CDR (tem); else val = F_structure_ref (rep_specials_structure, sym); } } else val = F_structure_ref (rep_structure, sym); if(no_err == Qnil && rep_VOIDP(val)) return Fsignal(Qvoid_value, rep_LIST_1(sym)); else return val; } static repv do_set (repv sym, repv val, repv (*setter)(repv st, repv var, repv val)) { /* Some of this function is hardcoded into the OP_SETQ instruction in lispmach.c */ rep_DECLARE1(sym, rep_SYMBOLP); if (rep_SYM(sym)->car & rep_SF_SPECIAL) { int spec = inlined_search_special_environment (sym); if (spec) { repv tem; /* Not allowed to set `modified' variables unless our environment includes all variables implicitly */ if (spec > 0 && rep_SYM(sym)->car & rep_SF_WEAK_MOD) return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ if(rep_SYM(sym)->car & rep_SF_LOCAL) { repv tem = (*rep_set_local_symbol_fun)(sym, val); if (tem != rep_NULL) return tem; /* Fall through and set the default value. */ } tem = inlined_search_special_bindings (sym); if (tem != Qnil) rep_CDR (tem) = val; else val = Fstructure_define (rep_specials_structure, sym, val); } else val = Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ } else { /* lexical binding */ repv tem = search_environment (sym); if (tem != Qnil) rep_CDR(tem) = val; else val = setter (rep_structure, sym, val); } return val; } /* backwards compatibility for C callers */ repv Fset (repv s, repv v) { return do_set (s, v, Fstructure_define); }; DEFUN_INT("set", Freal_set, Sset, (repv s, repv v), rep_Subr2, "vVariable:" rep_DS_NL "xNew value of %s:") /* ::doc:rep.lang.symbols#set:: set SYMBOL repv Sets the value of SYMBOL to repv. If SYMBOL has a buffer-local binding in the current buffer or `make-variable-buffer-local' has been called on SYMBOL the buffer-local value in the current buffer is set. Returns repv. ::end:: */ { return do_set (s, v, Fstructure_set); } DEFUN("set-default", Fset_default, Sset_default, (repv sym, repv val), rep_Subr2) /* ::doc:rep.lang.symbols#set-default:: set-default SYMBOL VALUE Sets the default value of SYMBOL to VALUE, then returns VALUE. ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); if (rep_SYM (sym)->car & rep_SF_SPECIAL) { int spec = search_special_environment (sym); if (spec) { repv tem; if (spec > 0 && rep_SYM(sym)->car & rep_SF_WEAK_MOD) return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ tem = search_special_bindings (sym); if (tem != Qnil) rep_CDR (tem) = val; else val = Fstructure_define (rep_specials_structure, sym, val); } else return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ } else Fstructure_set (rep_structure, sym, val); return val; } DEFUN("setplist", Fsetplist, Ssetplist, (repv sym, repv prop), rep_Subr2) /* ::doc:rep.lang.symbols#setplist:: setplist SYMBOL PROP-LIST Sets the property list of SYMBOL to PROP-LIST, returns PROP-LIST. ::end:: */ { int spec; rep_DECLARE1(sym, rep_SYMBOLP); spec = search_special_environment (sym); if (spec == 0) return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ Fstructure_define (plist_structure, sym, prop); return prop; } DEFUN("symbol-name", Fsymbol_name, Ssymbol_name, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#symbol-name:: symbol-name SYMBOL Returns the print-name of SYMBOL. ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); return(rep_SYM(sym)->name); } DEFUN("default-boundp", Fdefault_boundp, Sdefault_boundp, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#default-boundp:: default-boundp SYMBOL Returns t if SYMBOL has a default value. ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); if (rep_SYM(sym)->car & rep_SF_SPECIAL) { repv tem = search_special_bindings (sym); if (tem != Qnil) return rep_VOIDP (rep_CDR (tem)) ? Qnil : Qt; else { tem = F_structure_ref (rep_specials_structure, sym); return rep_VOIDP (tem) ? Qnil : Qt; } } else return Fstructure_bound_p (rep_structure, sym); } DEFUN("boundp", Fboundp, Sboundp, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#boundp:: boundp SYMBOL Returns t if SYMBOL has a value as a variable. ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); return(rep_VOIDP(Fsymbol_value(sym, Qt)) ? Qnil : Qt); } DEFUN("symbol-plist", Fsymbol_plist, Ssymbol_plist, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#symbol-plist:: symbol-plist SYMBOL Returns the property-list of SYMBOL. ::end:: */ { int spec; repv plist; rep_DECLARE1(sym, rep_SYMBOLP); spec = search_special_environment (sym); if (spec == 0) return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ plist = F_structure_ref (plist_structure, sym); return rep_VOIDP (plist) ? Qnil : plist; } DEFUN("gensym", Fgensym, Sgensym, (void), rep_Subr0) /* ::doc:rep.lang.symbols#gensym:: gensym Returns a new (non-interned) symbol with a unique print name. ::end:: */ { static int counter; char buf[20]; counter++; #ifdef HAVE_SNPRINTF snprintf(buf, sizeof(buf), "G%04d", counter); #else sprintf(buf, "G%04d", counter); #endif return(Fmake_symbol(rep_string_dup(buf))); } DEFUN("symbolp", Fsymbolp, Ssymbolp, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#symbolp:: symbolp ARG Returns t if ARG is a symbol. ::end:: */ { return(rep_SYMBOLP(sym) ? Qt : Qnil); } DEFUN("setq", Fsetq, Ssetq, (repv args, repv tail_posn), rep_SF) /* ::doc:rep.lang.interpreter#setq:: setq [SYMBOL FORM] ... Sets the value of each SYMBOL to the value of its corresponding FORM evaluated, returns the value of the last evaluation. ::end:: */ { repv res = Qnil; rep_GC_root gc_args; rep_PUSHGC(gc_args, args); while(rep_CONSP(args) && rep_CONSP(rep_CDR(args)) && rep_SYMBOLP(rep_CAR(args))) { if(!(res = Feval(rep_CAR(rep_CDR(args))))) goto end; if(!Freal_set(rep_CAR(args), res)) { res = rep_NULL; goto end; } args = rep_CDR(rep_CDR(args)); } end: rep_POPGC; return(res); } DEFUN ("%define", F_define, S_define, (repv args, repv tail_posn), rep_SF) /* ::doc:rep.lang.interpreter#%define:: %define SYMBOL FORM [DOC-STRING] Evaluate FORM, then create a top-level binding of SYMBOL whose value is the result of the evaluation. If such a binding already exists, it will be overwritten. ::end:: */ { repv var, value, doc = Qnil; rep_GC_root gc_var, gc_doc; if (!rep_assign_args (args, 2, 3, &var, &value, &doc)) return rep_NULL; rep_PUSHGC (gc_var, var); rep_PUSHGC (gc_doc, doc); value = Feval (value); rep_POPGC; rep_POPGC; if (value == rep_NULL) return rep_NULL; value = Fstructure_define (rep_structure, var, value); if (value != rep_NULL) { if (doc != Qnil) { repv prop = rep_documentation_property (rep_structure); if (prop != Qnil) { if (Fput (var, prop, doc) == rep_NULL) value = rep_NULL; } } } return rep_undefined_value; } DEFUN("makunbound", Fmakunbound, Smakunbound, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#makunbound:: makunbound SYMBOL Make SYMBOL have no value as a variable. ::end:: */ { return Freal_set (sym, rep_void_value); } DEFUN("get", Fget, Sget, (repv sym, repv prop), rep_Subr2) /* ::doc:rep.lang.symbols#get:: get SYMBOL PROPERTY Returns the value of SYMBOL's property PROPERTY. See `put'. ::end:: */ { repv plist; rep_DECLARE1(sym, rep_SYMBOLP); plist = F_structure_ref (plist_structure, sym); if (rep_VOIDP (plist)) return Qnil; while(rep_CONSP(plist) && rep_CONSP(rep_CDR(plist))) { if(rep_CAR(plist) == prop || (!rep_SYMBOLP(prop) && rep_value_cmp (rep_CAR(plist), prop) == 0)) { return(rep_CAR(rep_CDR(plist))); } plist = rep_CDR(rep_CDR(plist)); } return(Qnil); } DEFUN("put", Fput, Sput, (repv sym, repv prop, repv val), rep_Subr3) /* ::doc:rep.lang.symbols#put:: put SYMBOL PROPERTY repv Sets the value of SYMBOL's property PROPERTY to repv, this value can be retrieved with the `get' function. ::end:: */ { repv plist, old; int spec; rep_DECLARE1(sym, rep_SYMBOLP); spec = search_special_environment (sym); if (spec == 0) return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ old = F_structure_ref (plist_structure, sym); if (rep_VOIDP (old)) old = Qnil; plist = old; while(rep_CONSP(plist) && rep_CONSP(rep_CDR(plist))) { if(rep_CAR(plist) == prop || (!rep_SYMBOLP(prop) && rep_value_cmp (rep_CAR(plist), prop) == 0)) { if(!rep_CONS_WRITABLE_P(rep_CDR(plist))) { /* Can't write into a dumped cell; need to cons onto the head. */ break; } rep_CAR(rep_CDR(plist)) = val; return val; } plist = rep_CDR(rep_CDR(plist)); } Fstructure_define (plist_structure, sym, Fcons (prop, Fcons (val, old))); return val; } DEFUN("apropos", Fapropos, Sapropos, (repv re, repv pred, repv ob), rep_Subr3) /* ::doc:rep.lang.symbols#apropos:: apropos REGEXP [PREDICATE] [OBARRAY] Returns a list of symbols from OBARRAY (or the default) whose print-name matches the regular-expression REGEXP. If PREDICATE is given and non-nil, each symbol which matches is applied to the function PREDICATE, if the value is non-nil it is considered a match. ::end:: */ { rep_regexp *prog; rep_DECLARE1(re, rep_STRINGP); if(!rep_VECTORP(ob)) ob = rep_obarray; prog = rep_regcomp(rep_STR(re)); if(prog) { repv last = Qnil; int i, len = rep_VECT_LEN(ob); rep_GC_root gc_last, gc_ob, gc_pred; rep_PUSHGC(gc_last, last); rep_PUSHGC(gc_ob, ob); rep_PUSHGC(gc_pred, pred); for(i = 0; i < len; i++) { repv chain = rep_VECT(ob)->array[i]; while(rep_SYMBOLP(chain)) { if(rep_regexec(prog, rep_STR(rep_SYM(chain)->name))) { if(pred && !rep_NILP(pred)) { repv tmp; if(!(tmp = rep_funcall(pred, rep_LIST_1(chain), rep_FALSE)) || rep_NILP(tmp)) { goto next; } } last = Fcons(chain, last); } next: chain = rep_SYM(chain)->next; } } rep_POPGC; rep_POPGC; rep_POPGC; free(prog); return(last); } return rep_NULL; } DEFUN("make-variable-special", Fmake_variable_special, Smake_variable_special, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#make-variable-special:: make-variable-special SYMBOL Mark SYMBOL as being a special (dynamically-bound) variable. ::end:: */ { int spec; rep_DECLARE1(sym, rep_SYMBOLP); spec = search_special_environment (sym); if (spec == 0) return Fsignal (Qvoid_value, rep_LIST_1(sym)); /* XXX */ if (!(rep_SYM(sym)->car & rep_SF_SPECIAL)) { repv tem = rep_get_initial_special_value (sym); if (tem) Fstructure_define (rep_specials_structure, sym, tem); } rep_SYM(sym)->car |= rep_SF_SPECIAL; return sym; } DEFUN("special-variable-p", Fspecial_variable_p, Sspecial_variable_p, (repv sym), rep_Subr1) /* ::doc:rep.lang.symbols#special-variable-p:: special-variable-p SYMBOL Returns t if SYMBOL is a special variable (dynamically scoped). ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); return (rep_SYM(sym)->car & rep_SF_SPECIAL) ? Qt : Qnil; } DEFUN_INT("trace", Ftrace, Strace, (repv sym), rep_Subr1, "aFunction to trace") /* ::doc:rep.lang.debug#trace:: trace SYMBOL Flag that whenever SYMBOL is evaluated (as a variable or a function) the debugger is entered. ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); rep_SYM(sym)->car |= rep_SF_DEBUG; return(sym); } DEFUN_INT("untrace", Funtrace, Suntrace, (repv sym), rep_Subr1, "aFunction to untrace") /* ::doc:rep.lang.debug#untrace:: untrace SYMBOL Cancel the effect of (trace SYMBOL). ::end:: */ { rep_DECLARE1(sym, rep_SYMBOLP); rep_SYM(sym)->car &= ~rep_SF_DEBUG; return(sym); } DEFUN("obarray", Fobarray, Sobarray, (repv val), rep_Subr1) /* ::doc:rep.lang.symbols#obarray:: obarray [NEW-VALUE] ::end:: */ { if(val != Qnil) { rep_DECLARE1(val, rep_VECTORP); rep_obarray = val; } return rep_obarray; } DEFUN("make-keyword", Fmake_keyword, Smake_keyword, (repv in), rep_Subr1) /* ::doc:rep.lang.symbols#make-keyword:: make-keyword SYMBOL Return the keyword symbol that should be used in argument lists to provide the mark the value of the argument called SYMBOL. An error is signalled if SYMBOL is itself a keyword. ::end:: */ { repv str, name, key; int name_len; rep_DECLARE (1, in, rep_SYMBOLP (in) && !rep_KEYWORDP (in)); name = rep_SYM (in)->name; name_len = rep_STRING_LEN (name); str = rep_make_string (name_len + 3); rep_STR (str)[0] = '#'; rep_STR (str)[1] = ':'; memcpy (rep_STR (str) + 2, rep_STR (name), name_len); rep_STR (str)[name_len+2] = 0; key = Fintern (str, rep_keyword_obarray); rep_SYM (key)->car |= rep_SF_KEYWORD; return key; } DEFUN ("keywordp", Fkeywordp, Skeywordp, (repv arg), rep_Subr1) /* ::doc:rep.lang.symbols#keywordp:: keywordp ARG Return true if ARG is a keyword symbol. ::end:: */ { return rep_KEYWORDP (arg) ? Qt : Qnil; } int rep_pre_symbols_init(void) { rep_register_type(rep_Symbol, "symbol", symbol_cmp, symbol_princ, symbol_print, symbol_sweep, 0, 0, 0, 0, 0, 0, 0, 0); rep_obarray = Fmake_obarray(rep_MAKE_INT(rep_OBSIZE)); rep_keyword_obarray = Fmake_obarray(rep_MAKE_INT(rep_KEY_OBSIZE)); rep_register_type(rep_Funarg, "funarg", rep_ptr_cmp, rep_lisp_prin, rep_lisp_prin, funarg_sweep, 0, 0, 0, 0, 0, 0, 0, 0); if(rep_obarray && rep_keyword_obarray) { rep_mark_static(&rep_obarray); rep_mark_static(&rep_keyword_obarray); return rep_TRUE; } else return rep_FALSE; } void rep_symbols_init(void) { DEFSTRING (hash_f, "#f"); DEFSTRING (hash_t, "#t"); DEFSTRING (hash_undefined, "#undefined"); repv tem; rep_pre_datums_init (); /* initializes Qnil */ rep_INTERN(t); rep_pre_structures_init (); rep_USE_DEFAULT_ENV; rep_special_bindings = Qnil; rep_mark_static (&rep_env); rep_mark_static (&rep_special_bindings); plist_structure = Fmake_structure (Qnil, Qnil, Qnil, Qnil); rep_mark_static (&plist_structure); rep_INTERN(documentation); rep_INTERN(permanent_local); rep_scm_f = Fmake_symbol (rep_VAL (&hash_f)); rep_scm_t = Fmake_symbol (rep_VAL (&hash_t)); rep_undefined_value = Fmake_symbol (rep_VAL (&hash_undefined)); rep_SYM(rep_scm_f)->car |= rep_SF_LITERAL; rep_SYM(rep_scm_t)->car |= rep_SF_LITERAL; rep_SYM(rep_undefined_value)->car |= rep_SF_LITERAL; rep_mark_static (&rep_scm_f); rep_mark_static (&rep_scm_t); rep_mark_static (&rep_undefined_value); tem = rep_push_structure ("rep.lang.symbols"); rep_ADD_SUBR(Smake_symbol); rep_ADD_SUBR(Smake_obarray); rep_ADD_SUBR(Sfind_symbol); rep_ADD_SUBR(Sintern_symbol); rep_ADD_SUBR(Sintern); rep_ADD_SUBR(Sunintern); rep_ADD_SUBR(Ssymbol_value); rep_ADD_SUBR_INT(Sset); rep_ADD_SUBR(Ssetplist); rep_ADD_SUBR(Ssymbol_name); rep_ADD_SUBR(Sdefault_value); rep_ADD_SUBR(Sdefault_boundp); rep_ADD_SUBR(Sset_default); rep_ADD_SUBR(Sboundp); rep_ADD_SUBR(Ssymbol_plist); rep_ADD_SUBR(Sgensym); rep_ADD_SUBR(Ssymbolp); rep_ADD_SUBR(Smakunbound); rep_ADD_SUBR(Sget); rep_ADD_SUBR(Sput); rep_ADD_SUBR(Sapropos); rep_ADD_SUBR(Smake_variable_special); rep_ADD_SUBR(Sspecial_variable_p); rep_ADD_SUBR(Sobarray); rep_ADD_SUBR(Smake_keyword); rep_ADD_SUBR(Skeywordp); rep_pop_structure (tem); tem = rep_push_structure ("rep.lang.interpreter"); rep_ADD_SUBR(Ssetq); rep_ADD_SUBR(S_define); rep_ADD_SUBR(Sdefvar); rep_ADD_SUBR(Smake_closure); rep_ADD_SUBR(Sclosure_function); rep_ADD_SUBR(Sset_closure_function); rep_ADD_SUBR(Sclosure_name); rep_ADD_SUBR(Sclosurep); rep_pop_structure (tem); tem = rep_push_structure ("rep.structures"); rep_ADD_SUBR(Sclosure_structure); rep_ADD_SUBR(Sset_closure_structure); rep_ADD_SUBR(Sset_special_environment); rep_pop_structure (tem); tem = rep_push_structure ("rep.lang.debug"); rep_ADD_SUBR_INT(Strace); rep_ADD_SUBR_INT(Suntrace); rep_pop_structure (tem); }