/* * * s y n t a x . c -- Syntaxic forms implementation * * Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI * * * Permission to use, copy, modify, distribute,and license this * software and its documentation for any purpose is hereby granted, * provided that existing copyright notices are retained in all * copies and that this notice is included verbatim in any * distributions. No written agreement, license, or royalty fee is * required for any of the authorized uses. * This software is provided ``AS IS'' without express or implied * warranty. * * Author: Erick Gallesio [eg@kaolin.unice.fr] * Creation date: 25-Oct-1993 23:39 * Last file update: 3-Sep-1999 20:59 (eg) */ /* Notes: * ------ * C functions syntax_xxx implement the scheme syntax xxx. A syntax function * returns its work in its first argument (SCM *pform). The function result * is a boolean. If false, it indicates to eval that this result is * a final one (eval can return it unmodified). Otherwise, the eval function * take the result stored in *pform and evaluates it again (in the same eval * frame). This mechanaism permits to treat tail recursive calls as jump in * the eval function. * * Syntax function which returns EVALCAR(zzzz) are not tail recursive in debug * mode (in normal mode only the first call is non tail recursive, since this * first call will replace the original code by an equivalent code which is * clean on tail recursive calls. * */ #include "stk.h" #include "module.h" #include "extend.h" #ifdef COMPACT_SMALL_CST # define makecell(type) ((SCM) MAKE_SMALL_CST(0, type)) #else static SCM makecell(int type) { register SCM z; NEWCELL(z, type); return z; } #endif static SCM define2lambda(SCM l, int len) { /* transform (define (f p) b) in (define f (lambda (p) b)) */ if (CONSP(l) && CONSP(CAR(l))){ if (len < 2) goto Error; return Cons(CAR(CAR(l)), Cons(Cons(Sym_lambda, Cons(CDR(CAR(l)), CDR(l))), NIL)); } else if (len == 2) return l; Error: Err("define: bad definition", l); return UNDEFINED; /* never reached */ } PRIMITIVE STk_syntax_quote(SCM *pform, SCM env, int len) { SCM args = CDR(*pform); if (len != 1) Err("quote: Bad syntax", *pform); if (ModifyCode()) CAR(*pform) = makecell(tc_quote); SYNTAX_RETURN(CAR(args), Ntruth); } PRIMITIVE STk_syntax_lambda(SCM *pform, SCM env, int len) { register SCM args=CDR(*pform); if (len < 2) Err("lambda: bad syntax", *pform); if(ModifyCode()) CAR(*pform) = makecell(tc_lambda); SYNTAX_RETURN(STk_makeclosure(args, env), Ntruth); } PRIMITIVE STk_syntax_if(SCM *pform, SCM env, int len) { SCM args = CDR(*pform); switch (len) { case 2: args = LIST3(CAR(args), CAR(CDR(args)), UNDEFINED); case 3: break; default: Err("if: bad syntax", *pform); } if (ModifyCode()) { CAR(*pform) = makecell(tc_if); CDR(*pform) = args; /* will always contain a else part */ } SYNTAX_RETURN(NEQ(EVALCAR(args), Ntruth) ? CAR(CDR(args)):CAR(CDR(CDR(args))), Truth); } PRIMITIVE STk_syntax_setq(SCM *pform, SCM env, int len) { SCM var, *tmp, args = CDR(*pform); if (len != 2) Err("set!: bad assignement", args); if (NSYMBOLP(var=CAR(args))) Err("set!: first argument is not a symbol", var); tmp = STk_varlookup(var, env, 0); if (*tmp == UNBOUND) { /* C variables are always seen as unbound variables. This tends to * make them slower than standard variables but, in counterpart, this * doesn't slow down accesses to Scheme variables */ if (var->cell_info & CELL_INFO_C_VAR) { /* This is not an unbound variable but rather a C variable */ STk_apply_setter_C_variable(PNAME(var), EVALCAR(CDR(args))); goto Out; } else Err("set!: variable not defined", var); } if (ModifyCode()) CAR(*pform) = makecell(tc_setq); *tmp = EVALCAR(CDR(args)); Out: if (TRACED_VARP(var)) STk_change_value(var, env); SYNTAX_RETURN(UNDEFINED, Ntruth); } PRIMITIVE STk_syntax_cond(SCM *pform, SCM env, int len) /* len unused here */ { SCM l, tmp; for (l=CDR(*pform); CONSP(l); l=CDR(l)) { if (NCONSP(CAR(l))) Err("cond: malformed clause", CAR(l)); if (EQ(CAR(CAR(l)), Sym_else) && NNULLP(CDR(l))) Err("cond: else clause must be the last", *pform); } if (NNULLP(l)) Err("cond: malformed clauses list", CDR(*pform)); tmp = Cons(makecell(tc_cond), CDR(*pform)); if (ModifyCode()) CAR(*pform) = CAR(tmp); SYNTAX_RETURN(tmp, Truth); } PRIMITIVE STk_syntax_and(SCM *pform, SCM env, int len) { SCM l = CDR(*pform); if (ModifyCode()) CAR(*pform) = makecell(tc_and); if (len == 0) SYNTAX_RETURN(Truth, Ntruth); for (--len; len; len--, l=CDR(l)) { if (EVALCAR(l) == Ntruth) SYNTAX_RETURN(Ntruth, Ntruth); } SYNTAX_RETURN(CAR(l), Truth); } PRIMITIVE STk_syntax_or(SCM *pform, SCM env, int len) { SCM l = CDR(*pform); SCM val; if (ModifyCode()) CAR(*pform) = makecell(tc_or); if (len == 0) SYNTAX_RETURN(Ntruth, Ntruth); for (--len; len; len--, l=CDR(l)) { if ((val=EVALCAR(l)) != Ntruth) SYNTAX_RETURN(val, Ntruth); } SYNTAX_RETURN(CAR(l), Truth); } static SCM syntax_let_family(SCM *pform, SCM env, char *who, int type, int len) { register SCM p, tmp, fl=NIL, al=NIL; char buffer[100]; int named_let = 0; if (len < 2) goto Error; p = CAR(CDR(*pform)); if (SYMBOLP(p) && type == tc_let) { /* It's a named let. Re-initialize to a correct value */ if (len < 3) goto Error; named_let = 1; p = CAR(CDR(CDR(*pform))); } for( ; NNULLP(p); p=CDR(p)) { if (STk_llength(tmp=CAR(p)) != 2 || NSYMBOLP(CAR(tmp))) { sprintf(buffer, "%s: badly formed binding", who); Err(buffer, CONSP(p)? tmp: p); } /* Verify that this binding doesn't already exist in fl * except for let* which aloows it (at least the formal semantics * given in R4RS). Problem shown by Brent Knight */ if (type!=tc_letstar && STk_memv(CAR(tmp),fl)!=Ntruth) { sprintf(buffer, "%s: duplicate binding", who); Err(buffer, CAR(CDR(*pform))); } fl = Cons(CAR(tmp),fl); al = Cons(CAR(CDR(tmp)),al); } /* Things are a tricky for named let: the scope of the name of a * named let encloses only the body of the let and not the * initialization values of the bound variables. Thus we have: * (let ((f -)) * (let f ((n (f 1))) n)) => -1 * and not 1 (the exemple comes from a message from Allegro * Petrofsky in comp.lang.scheme. * That means that the previous let is equivalent to * (let ((f -)) * ((letrec ((f (lambda (n) n))) f) * (f 1))) * rather than * (let ((f -)) * (letrec ((f (lambda (n) n))) * (f 1))) */ tmp = named_let ? /* named let */ Cons(LIST4(makecell(tc_letrec), LIST1(CAR(CDR(*pform))), LIST1(Cons(Sym_lambda, Cons(Reverse(fl), CDR(CDR(CDR(*pform)))))), CAR(CDR(*pform))), Reverse(al)) : /* normal case */ Cons(makecell(type), Cons(Reverse(fl), Cons(Reverse(al), CDR(CDR(*pform))))); if (ModifyCode()) { CAR(*pform) = CAR(tmp); CDR(*pform) = CDR(tmp); } SYNTAX_RETURN(tmp, Truth); Error: sprintf(buffer, "%s: incorrect number of subforms", who); Err(buffer, *pform); return UNDEFINED; /* never reached */ } PRIMITIVE STk_syntax_let(SCM *pform, SCM env, int len) { return syntax_let_family(pform, env, "let", tc_let, len); } PRIMITIVE STk_syntax_letstar(SCM *pform, SCM env, int len) { return syntax_let_family(pform, env, "let*", tc_letstar, len); } PRIMITIVE STk_syntax_letrec(SCM *pform, SCM env, int len) { return syntax_let_family(pform, env, "letrec", tc_letrec, len); } PRIMITIVE STk_syntax_begin(SCM *pform, SCM env, int len) { register SCM l = CDR(*pform); if (len == 0) { SYNTAX_RETURN(UNDEFINED, Ntruth); } else { for ( ; NNULLP(CDR(l)); l = CDR(l)) EVALCAR(l); if (ModifyCode()) CAR(*pform) = makecell(tc_begin); SYNTAX_RETURN(CAR(l), Truth); } } PRIMITIVE STk_syntax_delay(SCM *pform, SCM env, int len) { SCM z, tmp; if (len != 1) Err("delay: Bad expression", *pform); /* Build (lambda () expr) in tmp */ tmp = STk_makeclosure(Cons(NIL, CDR(*pform)), env); /* save this closure in the promise */ NEWCELL(z, tc_promise); z->storage_as.promise.expr = tmp; z->storage_as.promise.resultknown = 0; SYNTAX_RETURN(z, Ntruth); } static SCM backquotify(SCM x, SCM env, int level) { if (VECTORP(x)) return STk_list2vector(backquotify(STk_vector2list(x), env, level)); if (NCONSP(x)) return x; if (EQ(CAR(x), Sym_quasiquote)) return LIST2(Sym_quasiquote, backquotify(CAR(CDR(x)), env, level+1)); if (EQ(CAR(x), Sym_unquote)) return (level == 1) ? EVALCAR(CDR(x)) : LIST2(Sym_unquote, backquotify(CAR(CDR(x)), env, level-1)); if (CONSP(CAR(x)) && EQ(CAR(CAR(x)), Sym_unq_splicing)) return NULLP(CDR(x)) ? EVALCAR(CDR(CAR(x))) : STk_append2(EVALCAR(CDR(CAR(x))), backquotify(CDR(x), env, level)); /* Otherwise */ return Cons(backquotify(CAR(x), env, level), backquotify(CDR(x), env, level)); } PRIMITIVE STk_syntax_quasiquote(SCM *pform, SCM env, int len) { if (len == 0) Err("quasiquote: no form", NIL); SYNTAX_RETURN(backquotify(CAR(CDR(*pform)), env, 1), Ntruth); } PRIMITIVE STk_syntax_define(SCM *pform, SCM env, int len) { SCM *tmp, var, args, expr; args = define2lambda(CDR(*pform), len); var = CAR(args); if (NSYMBOLP(var)) Err("define: bad variable name", var); expr = EVALCAR(CDR(args)); if (NULLP(env)) { /* Global var */ STk_define_public_var(NIL, var, expr); STk_last_defined = var; } else { if (MODULEP(CAR(env))) { /* Public variable */ STk_define_public_var(CAR(env), var, expr); STk_last_defined = var; } else { /* Local var */ tmp = STk_value_in_env(var, env); if (tmp != &UNBOUND) { /* This symbol was already defined at current level. Just do an assignment*/ *tmp = expr; } else { /* Extend current environment for that definition */ SCM vars, vals, tmp = CAR(env); /* We add new definition at the end of current environment (since * some code has possbly be re-written using tc_localvar). This avoid * re-numbering the code. * * Note: The first append cannot be destructive because vars are direct * references on the procedure environmment. */ vars = STk_append2(CAR(tmp), LIST1(var)); vals = STk_dappend2(CDR(tmp), LIST1(expr)); CAR(tmp) = vars; CDR(tmp) = vals; } } } if (TRACED_VARP(var)) STk_change_value(var, env); SYNTAX_RETURN(UNDEFINED, Ntruth); } /* * * STk special syntaxic forms * */ PRIMITIVE STk_syntax_extend_env(SCM *pform, SCM env, int len) { if (len < 2) Err("extend-environement: Bad syntax", *pform); if (ModifyCode()) CAR(*pform) = makecell(tc_extend_env); SYNTAX_RETURN(*pform, Truth); } PRIMITIVE STk_while(SCM l, SCM env, int len) { register SCM test, body; if (len < 2) Err("while: bad argument list", NIL); for (test = EVALCAR(l); test != Ntruth; test = EVAL(CAR(l))) { /* Evaluate each expression of the body */ for (body = CDR(l); !NULLP(body); body = CDR(body)) EVALCAR(body); } return UNDEFINED; } PRIMITIVE STk_until(SCM l, SCM env, int len) { register SCM test, body; if (len < 2) Err("until: bad argument list", NIL); for (test = EVALCAR(l); test == Ntruth; test = EVAL(CAR(l))) { /* Evaluate each expression of the body */ for (body = CDR(l); !NULLP(body); body = CDR(body)) EVALCAR(body); } return UNDEFINED; }