/* walk -- walk through a tree, evaluating nodes */ extern List *walk(Tree *tree0, Binding *binding0, int flags) { Tree *volatile tree = tree0; Binding *volatile binding = binding0; SIGCHK(); top: if (tree == NULL) return true; switch (tree->kind) { case nConcat: case nList: case nQword: case nVar: case nVarsub: case nWord: case nThunk: case nLambda: case nCall: case nPrim: { List *list; Ref(Binding *, bp, binding); list = glom(tree, binding, TRUE); binding = bp; RefEnd(bp); return eval(list, binding, flags); } case nAssign: return assign(tree->u[0].p, tree->u[1].p, binding); case nLet: case nClosure: Ref(Tree *, body, tree->u[1].p); binding = letbindings(tree->u[0].p, binding, binding, flags); tree = body; RefEnd(body); goto top; case nLocal: return local(tree->u[0].p, tree->u[1].p, binding, flags); case nFor: return forloop(tree->u[0].p, tree->u[1].p, binding, flags); case nMatch: return matchpattern(tree->u[0].p, tree->u[1].p, binding); case nExtract: return extractpattern(tree->u[0].p, tree->u[1].p, binding); default: panic("walk: bad node kind %d", tree->kind); } NOTREACHED; }
/* glob -- globbing prepass (glob if we need to, and dispatch for tilde expansion) */ extern List *glob(List *list, StrList *quote) { List *lp; StrList *qp; Boolean doglobbing = FALSE; for (lp = list, qp = quote; lp != NULL; lp = lp->next, qp = qp->next) if (qp->str != QUOTED) { assert(lp->term != NULL); assert(!isclosure(lp->term)); Ref(char *, str, getstr(lp->term)); assert(qp->str == UNQUOTED || strlen(qp->str) == strlen(str)); if (hastilde(str, qp->str)) { Ref(List *, l0, list); Ref(List *, lr, lp); Ref(StrList *, q0, quote); Ref(StrList *, qr, qp); str = expandhome(str, qp); lr->term = mkstr(str); lp = lr; qp = qr; list = l0; quote = q0; RefEnd4(qr, q0, lr, l0); } if (haswild(str, qp->str)) doglobbing = TRUE; RefEnd(str); }
/* assign -- bind a list of values to a list of variables */ static List *assign(Tree *varform, Tree *valueform0, Binding *binding0) { Ref(List *, result, NULL); Ref(Tree *, valueform, valueform0); Ref(Binding *, binding, binding0); Ref(List *, vars, glom(varform, binding, FALSE)); if (vars == NULL) fail("es:assign", "null variable name"); Ref(List *, values, glom(valueform, binding, TRUE)); result = values; for (; vars != NULL; vars = vars->next) { List *value; Ref(char *, name, getstr(vars->term)); if (values == NULL) value = NULL; else if (vars->next == NULL || values->next == NULL) { value = values; values = NULL; } else { value = mklist(values->term, NULL); values = values->next; } vardef(name, binding, value); RefEnd(name); } RefEnd4(values, vars, binding, valueform); RefReturn(result); }
extern Term *mkstr(char *str) { Term *term; Ref(char *, string, str); term = gcnew(Term); term->str = string; term->closure = NULL; RefEnd(string); return term; }
extern List *fsplit(const char *sep, List *list, Boolean coalesce) { Ref(List *, lp, list); startsplit(sep, coalesce); for (; lp != NULL; lp = lp->next) { char *s = getstr(lp->term); splitstring(s, strlen(s), TRUE); } RefEnd(lp); return endsplit(); }
static Var *mkvar(List *defn) { Ref(Var *, var, NULL); Ref(List *, lp, defn); var = gcnew(Var); var->env = NULL; var->defn = lp; var->flags = hasbindings(lp) ? var_hasbindings : 0; RefEnd(lp); RefReturn(var); }
/* sortlist */ extern List *sortlist(List *list) { if (length(list) > 1) { Vector *v = vectorize(list); sortvector(v); gcdisable(0); Ref(List *, lp, listify(v->count, v->vector)); gcenable(); list = lp; RefEnd(lp); }
static List *callsettor(char *name, List *defn) { Push p; List *settor; if (specialvar(name) || (settor = varlookup2("set-", name, NULL)) == NULL) return defn; Ref(List *, lp, defn); Ref(List *, fn, settor); varpush(&p, "0", mklist(mkstr(name), NULL)); lp = listcopy(eval(append(fn, lp), NULL, 0)); varpop(&p); RefEnd(fn); RefReturn(lp); }
static noreturn failexec(char *file, List *args) { List *fn; assert(gcisblocked()); fn = varlookup("fn-%exec-failure", NULL); if (fn != NULL) { int olderror = errno; Ref(List *, list, append(fn, mklist(mkstr(file), args))); RefAdd(file); gcenable(); RefRemove(file); eval(list, NULL, 0); RefEnd(list); errno = olderror; } eprint("%s: %s\n", file, esstrerror(errno)); exit(1); }
extern char *getstr(Term *term) { char *s = term->str; Closure *closure = term->closure; assert((s == NULL) != (closure == NULL)); if (s != NULL) return s; #if 0 /* TODO: decide whether getstr() leaves term in closure or string form */ Ref(Term *, tp, term); s = str("%C", closure); tp->str = s; tp->closure = NULL; RefEnd(tp); return s; #else return str("%C", closure); #endif }
/* letbindings -- create a new Binding containing let-bound variables */ static Binding *letbindings(Tree *defn0, Binding *outer0, Binding *context0, int evalflags) { Ref(Binding *, binding, outer0); Ref(Binding *, context, context0); Ref(Tree *, defn, defn0); for (; defn != NULL; defn = defn->u[1].p) { assert(defn->kind == nList); if (defn->u[0].p == NULL) continue; Ref(Tree *, assign, defn->u[0].p); assert(assign->kind == nAssign); Ref(List *, vars, glom(assign->u[0].p, context, FALSE)); Ref(List *, values, glom(assign->u[1].p, context, TRUE)); if (vars == NULL) fail("es:let", "null variable name"); for (; vars != NULL; vars = vars->next) { List *value; Ref(char *, name, getstr(vars->term)); if (values == NULL) value = NULL; else if (vars->next == NULL || values->next == NULL) { value = values; values = NULL; } else { value = mklist(values->term, NULL); values = values->next; } binding = mkbinding(name, value, binding); RefEnd(name); } RefEnd3(values, vars, assign); } RefEnd2(defn, context); RefReturn(binding); }
/* expandhome -- do tilde expansion by calling fn %home */ static char *expandhome(char *s, StrList *qp) { int c; size_t slash; List *fn = varlookup("fn-%home", NULL); assert(*s == '~'); assert(qp->str == UNQUOTED || *qp->str == 'r'); if (fn == NULL) return s; for (slash = 1; (c = s[slash]) != '/' && c != '\0'; slash++) ; Ref(char *, string, s); Ref(StrList *, quote, qp); Ref(List *, list, NULL); RefAdd(fn); if (slash > 1) list = mklist(mkstr(gcndup(s + 1, slash - 1)), NULL); RefRemove(fn); list = eval(append(fn, list), NULL, 0); if (list != NULL) { if (list->next != NULL) fail("es:expandhome", "%%home returned more than one value"); Ref(char *, home, getstr(list->term)); if (c == '\0') { string = home; quote->str = QUOTED; } else { char *q; size_t pathlen = strlen(string); size_t homelen = strlen(home); size_t len = pathlen - slash + homelen; s = gcalloc(len + 1, &StringTag); memcpy(s, home, homelen); memcpy(&s[homelen], &string[slash], pathlen - slash); s[len] = '\0'; string = s; q = quote->str; if (q == UNQUOTED) { q = gcalloc(len + 1, &StringTag); memset(q, 'q', homelen); memset(&q[homelen], 'r', pathlen - slash); q[len] = '\0'; } else if (strchr(q, 'r') == NULL) q = QUOTED; else { q = gcalloc(len + 1, &StringTag); memset(q, 'q', homelen); memcpy(&q[homelen], "e->str[slash], pathlen - slash); q[len] = '\0'; } quote->str = q; } RefEnd(home); } RefEnd2(list, quote); RefReturn(string); }
/* eval -- evaluate a list, producing a list */ extern List *eval(List *list0, Binding *binding0, int flags) { Closure *volatile cp; List *fn; if (++evaldepth >= maxevaldepth) fail("es:eval", "max-eval-depth exceeded"); Ref(List *, list, list0); Ref(Binding *, binding, binding0); Ref(char *, funcname, NULL); restart: if (list == NULL) { RefPop3(funcname, binding, list); --evaldepth; return true; } assert(list->term != NULL); if ((cp = getclosure(list->term)) != NULL) { switch (cp->tree->kind) { case nPrim: assert(cp->binding == NULL); list = prim(cp->tree->u[0].s, list->next, binding, flags); break; case nThunk: list = walk(cp->tree->u[0].p, cp->binding, flags); break; case nLambda: ExceptionHandler Push p; Ref(Tree *, tree, cp->tree); Ref(Binding *, context, bindargs(tree->u[0].p, list->next, cp->binding)); if (funcname != NULL) varpush(&p, "0", mklist(mkterm(funcname, NULL), NULL)); list = walk(tree->u[1].p, context, flags); if (funcname != NULL) varpop(&p); RefEnd2(context, tree); CatchException (e) if (termeq(e->term, "return")) { list = e->next; goto done; } throw(e); EndExceptionHandler break; case nList: { list = glom(cp->tree, cp->binding, TRUE); list = append(list, list->next); goto restart; } default: panic("eval: bad closure node kind %d", cp->tree->kind); } goto done; } /* the logic here is duplicated in $&whatis */ Ref(char *, name, getstr(list->term)); fn = varlookup2("fn-", name, binding); if (fn != NULL) { funcname = name; list = append(fn, list->next); RefPop(name); goto restart; } if (isabsolute(name)) { char *error = checkexecutable(name); if (error != NULL) fail("$&whatis", "%s: %s", name, error); list = forkexec(name, list, flags & eval_inchild); RefPop(name); goto done; } RefEnd(name); fn = pathsearch(list->term); if (fn != NULL && fn->next == NULL && (cp = getclosure(fn->term)) == NULL) { char *name = getstr(fn->term); list = forkexec(name, list, flags & eval_inchild); goto done; } list = append(fn, list->next); goto restart; done: --evaldepth; if ((flags & eval_exitonfalse) && !istrue(list)) exit(exitstatus(list)); RefEnd2(funcname, binding); RefReturn(list); }
/* forloop -- evaluate a for loop */ static List *forloop(Tree *defn0, Tree *body0, Binding *binding, int evalflags) { static List MULTIPLE = { NULL, NULL }; Ref(List *, result, true); Ref(Binding *, outer, binding); Ref(Binding *, looping, NULL); Ref(Tree *, body, body0); Ref(Tree *, defn, defn0); for (; defn != NULL; defn = defn->u[1].p) { assert(defn->kind == nList); if (defn->u[0].p == NULL) continue; Ref(Tree *, assign, defn->u[0].p); assert(assign->kind == nAssign); Ref(List *, vars, glom(assign->u[0].p, outer, FALSE)); Ref(List *, list, glom(assign->u[1].p, outer, TRUE)); if (vars == NULL) fail("es:for", "null variable name"); for (; vars != NULL; vars = vars->next) { char *var = getstr(vars->term); looping = mkbinding(var, list, looping); list = &MULTIPLE; } RefEnd3(list, vars, assign); SIGCHK(); } looping = reversebindings(looping); RefEnd(defn); ExceptionHandler for (;;) { Boolean allnull = TRUE; Ref(Binding *, bp, outer); Ref(Binding *, lp, looping); Ref(Binding *, sequence, NULL); for (; lp != NULL; lp = lp->next) { Ref(List *, value, NULL); if (lp->defn != &MULTIPLE) sequence = lp; assert(sequence != NULL); if (sequence->defn != NULL) { value = mklist(sequence->defn->term, NULL); sequence->defn = sequence->defn->next; allnull = FALSE; } bp = mkbinding(lp->name, value, bp); RefEnd(value); } RefEnd2(sequence, lp); if (allnull) { RefPop(bp); break; } result = walk(body, bp, evalflags & eval_exitonfalse); RefEnd(bp); SIGCHK(); } CatchException (e) if (!termeq(e->term, "break")) throw(e); result = e->next; EndExceptionHandler RefEnd3(body, looping, outer); RefReturn(result); }