#include #include #include #include "apl9.h" #define traceprint if(traceeval)print int traceeval; typedef Datum *(*evalfn)(Datum *, Datum *); Datum *strand(Datum *, Datum *); Datum *monadfun(Datum *, Datum *); Datum *dyadfun(Datum *, Datum *); Datum *parens(Datum *, Datum *); Datum *nameis(Datum *, Datum *); Datum *namesis(Datum *, Datum *); Datum *assign(Datum *, Datum *); Datum *monadop(Datum *, Datum *); Datum *dyadop(Datum *, Datum *); Datum *train(Datum *, Datum *); Datum *lookup(Datum *); int bindingstrengths[11][11] = { /* A F H MO DO AF ( ) ← IS N */ 7, 4, 4, 5, 0, 0, 0, 0, 0, 0, 0, /* A */ 3, 2, 5, 5, 0, 0, 0, 0, 0, 0, 0, /* F */ 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, /* H */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* MO */ 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, /* DO */ 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* AF */ 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, /* ( */ 8, 8, 8, 8, 8, 8, 0, 8, 9, 8, 8, /* ) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ← */ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* IS */ 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, /* N */ }; evalfn evalfns[11][11] = { /* A F H MO DO AF ( ) ← IS N */ strand, dyadfun, dyadfun, monadop, 0, 0, 0, 0, 0, 0, 0, /* A */ monadfun, train, monadop, monadop, 0, 0, 0, 0, 0, 0, 0, /* F */ 0, 0, 0, monadop, 0, 0, 0, 0, 0, 0, 0, /* H */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* MO */ dyadop, dyadop, dyadop, 0, 0, 0, 0, 0, 0, 0, 0, /* DO */ monadfun, train, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* AF */ 0, 0, 0, 0, 0, 0, 0, parens, namesis, 0, 0, /* ( */ 0, 0, 0, 0, 0, 0, 0, 0, namesis, 0, 0, /* ) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ← */ assign, assign, assign, assign, assign, 0, assign, 0, 0, 0, 0, /* IS */ 0, 0, 0, 0, 0, 0, 0, 0, nameis, 0, 0, /* N */ }; Datum * eval(Statement *stmt, int toplevel) { /* start by looking up first variable if needed */ if(stmt->ntoks > 0 && stmt->toks[stmt->ntoks-1]->tag == NameTag){ Datum *var = stmt->toks[stmt->ntoks-1]; stmt->toks[stmt->ntoks-1] = lookup(var); freedatum(var); } while(stmt->ntoks > 1){ int maxlevel = 0; int offset; evalfn fn = nil; traceprint("CURRENT: %S\n", ppdatums(stmt->toks, stmt->ntoks)); for(offset = stmt->ntoks-1; offset >= 0; offset--){ int level; retry: if(offset == 0) level = 0; else{ Datum *left = stmt->toks[offset-1]; Datum *right = stmt->toks[offset]; level = bindingstrengths[left->tag][right->tag]; } if(level == 0 && offset > 0 && stmt->toks[offset-1]->tag == NameTag){ Datum *var = stmt->toks[offset-1]; stmt->toks[offset-1] = lookup(var); freedatum(var); goto retry; }else if(level < maxlevel){ Datum *left = stmt->toks[offset]; Datum *right = stmt->toks[offset+1]; fn = evalfns[left->tag][right->tag]; traceprint("Reducing %S and %S (fn=%p, level=%d, max=%d)\n", ppdatum(left), ppdatum(right), fn, level, maxlevel); break; }else if(level > maxlevel) maxlevel = level; } if(maxlevel == 0) throwerror(L"No reduce rule", ESyntax); else{ Datum *new = fn(stmt->toks[offset], stmt->toks[offset+1]); traceprint("handler fn done\n"); freedatum(stmt->toks[offset]); freedatum(stmt->toks[offset+1]); stmt->toks[offset] = new; for(int i = offset+1; i < stmt->ntoks-1; i++) stmt->toks[i] = stmt->toks[i+1]; stmt->ntoks--; } } if(stmt->ntoks == 1){ int stop = 0; if(toplevel == 0 && !stmt->toks[0]->shy) stop = 1; if(stmt->guard){ int guardOK = 1; if(stmt->toks[0]->tag != ArrayTag) guardOK = 0; else if(!stmt->errorguard && GetSize(stmt->toks[0]->array) != 1) guardOK = 0; else if(stmt->errorguard && GetSize(stmt->toks[0]->array) < 1) guardOK = 0; else if(GetType(stmt->toks[0]->array) != AtypeInt) guardOK = 0; else if(!stmt->errorguard && stmt->toks[0]->array->intdata[0] != 0 && stmt->toks[0]->array->intdata[0] != 1) guardOK = 0; if(!guardOK){ if(!stmt->errorguard) throwerror(L"Guard expected single valued boolean", EDomain); else throwerror(L"Error guard expected an array of error numbers", EDomain); }else{ stop = 0; if(!stmt->errorguard){ if(stmt->toks[0]->array->intdata[0] == 1) return eval(stmt->guard, toplevel); }else{ ErrorGuard *eg = newerrorguard(stmt->toks[0]->array, stmt->guard); if(setjmp(eg->jmp)){ eg->active = 0; /* Replace the old dfnframe with the one in eg->frame */ ThreadData *td = getthreaddata(); DfnFrame *old = td->currentdfn; td->currentdfn = eg->frame; freedfnframe(old, 1); return eval(eg->guard, toplevel); } } } } if(stmt->next && !stop) return eval(stmt->next, toplevel); else if(!stmt->guard) return stmt->toks[0]; else{ throwerror(L"No value produced", EValue); return nil; } }else return nil; } Datum * lookup(Datum *var) { traceprint("VAR LOOKUP %S\n", var->name); Symbol *symbol = getsym(var->name, 0); if(symbol->value == nil && symbol->getfn == nil){ Rune *msg = runesmprint("Undefined name: %S", var->name); throwerror(msg, EValue); } Datum *val; if(symbol->getfn != nil) val = symbol->getfn(); else{ val = symbol->value; incdatumref(val); } val->shy = 0; traceprint("VAR %S = %S\n", var->name, ppdatum(val)); return val; } Datum * strand(Datum *left, Datum *right) { traceprint("Stranding (%d %d)\n", GetStrand(left->array), GetStrand(right->array)); Datum *result = allocdatum(ArrayTag, 0); Array *leftarr = GetStrand(left->array) ? fnSame(left->array) : fnEnclose(left->array); Array *rightarr = GetStrand(right->array) ? fnSame(right->array) : fnEnclose(right->array); Array *tmp = fnCatenateFirst(leftarr, rightarr); result->array = simplifyarray(tmp); SetStrand(result->array, 1); freearray(tmp); freearray(leftarr); freearray(rightarr); return result; } Datum * monadfun(Datum *left, Datum *right) { traceprint("Monadic function application\n"); Datum *result = allocdatum(ArrayTag, 0); result->array = runfunc(left->func, left->func.left, right->array); return result; } Datum * dyadfun(Datum *left, Datum *right) { traceprint("Applying left argument to function\n"); Datum *result = allocdatum(BoundFunctionTag, 0); if(right->tag == FunctionTag) result->func = dupfunction(right->func); else if(right->tag == HybridTag){ result->func.type = FunctypeHybrid; result->func.code = right->hybrid; } result->func.left = fnSame(left->array); return result; } Datum * parens(Datum *left, Datum *right) { /* evaluate a parenthesis expression and return the result */ USED(right); traceprint("PARENS: %S\n", ppdatums(left->stmt.toks, left->stmt.ntoks)); Datum *result = eval(&left->stmt, 1); incdatumref(result); if(result->tag == ArrayTag) SetStrand(result->array, 0); result->shy = 0; return result; /* TODO handle error if ntoks != 1 */ } Datum * nameis(Datum *left, Datum *right) { USED(right); traceprint("NAMEIS %S←\n", left->name); Datum *result = allocdatum(AssignmentTag, 0); result->names.ntoks = 1; result->names.toks = emalloc(sizeof(Datum*)); result->names.toks[0] = left; incdatumref(left); return result; } Datum * namesis(Datum *left, Datum *right) { if(left->tag == RParTag){ incdatumref(right); return right; } traceprint("NAMES IS %S %S\n", ppdatum(left), ppdatum(right)); Datum *result = allocdatum(AssignmentTag, 0); result->names.ntoks = left->stmt.ntoks; result->names.toks = emalloc(sizeof(Datum*) * result->names.ntoks); for(int i = 0; i < result->names.ntoks; i++){ result->names.toks[i] = left->stmt.toks[i]; incdatumref(result->names.toks[i]); } return result; } Datum * assign(Datum *left, Datum *right) { traceprint("ASSIGN %S %S\n", ppdatum(left), ppdatum(right)); if(left->names.ntoks == 1){ traceprint("Assign single\n"); if(left->names.toks[0]->tag != NameTag) throwerror(nil, ESyntax); Symbol *symbol = getsym(left->names.toks[0]->name, 0); if(symbol->setfn != nil) symbol->setfn(right); else{ /* re-assign the symbol to one that is sure to be local. This enables shadowing */ symbol = getsym(symbol->name, 1); freedatum(symbol->value); symbol->value = right; incdatumref(right); if(symbol->value->tag == ArrayTag) SetStrand(symbol->value->array, 0); } }else{ if(right->tag != ArrayTag) throwerror(nil, ESyntax); if(GetRank(right->array) != 1 && GetRank(right->array) != 0) throwerror(nil, ERank); int nlocs = 0; Datum **locations = nil; for(int i = 0; i < left->names.ntoks; i++){ Datum *loc = left->names.toks[i]; if(loc->tag == NameTag){ nlocs++; locations = erealloc(locations, sizeof(Datum*) * nlocs); locations[nlocs-1] = allocdatum(AssignmentTag, 0); locations[nlocs-1]->names.ntoks = 1; locations[nlocs-1]->names.toks = emalloc(sizeof(Datum*)); locations[nlocs-1]->names.toks[0] = loc; incdatumref(loc); }else if(loc->tag == LParTag){ i++; nlocs++; locations = erealloc(locations, sizeof(Datum*) * nlocs); locations[nlocs-1] = allocdatum(AssignmentTag, 0); locations[nlocs-1]->names.ntoks = loc->stmt.ntoks; locations[nlocs-1]->names.toks = emalloc(sizeof(Datum*) * loc->stmt.ntoks); for(int j = 0; j < loc->stmt.ntoks; j++){ locations[nlocs-1]->names.toks[j] = loc->stmt.toks[j]; incdatumref(loc->stmt.toks[j]); } } } if(GetRank(right->array) == 1 && GetSize(right->array) != nlocs) throwerror(nil, ELength); for(int i = 0; i < nlocs; i++){ Datum *item; if(GetRank(right->array) == 0) item = right; else{ item = allocdatum(ArrayTag, 0); item->array = arrayitem(right->array, i); } assign(locations[i], item); freedatum(item); /* free the returned item */ if(item != right) freedatum(item); freedatum(locations[i]); } } Datum *result = right; incdatumref(right); result->shy = 1; return result; } Datum * monadop(Datum *left, Datum *right) { traceprint("Applying left argument to operator\n"); Datum *result = allocdatum(FunctionTag, 0); result->func.type = FunctypeOp; if(right->tag == MonadicOpTag || right->tag == DyadicOpTag) result->func.operator = dupoperator(right->operator); else{ result->func.operator.type = OperatortypeHybrid; result->func.operator.code = right->hybrid; } result->func.operator.left = left; incdatumref(left); result->func.left = nil; result->func.scope = right->operator.scope; return result; } Datum * dyadop(Datum *left, Datum *right) { traceprint("Applying right argument to operator\n"); Datum *result = allocdatum(MonadicOpTag, 0); result->operator = dupoperator(left->operator); result->operator.right = right; incdatumref(right); return result; } Datum * train(Datum *left, Datum *right) { traceprint("Creating train from %S and %S\n", ppdatum(left), ppdatum(right)); Datum *result = allocdatum(FunctionTag, 0); result->func.type = FunctypeTrain; result->func.left = nil; if(left->func.type == FunctypeTrain) result->func = left->func; else{ result->func.train.nfuncs = 1; result->func.train.funcs = emalloc(sizeof(Function)); result->func.train.funcs[0] = dupfunction(left->func); } if(right->func.type == FunctypeTrain){ int oldn = result->func.train.nfuncs; result->func.train.nfuncs = oldn + right->func.train.nfuncs; result->func.train.funcs = erealloc(result->func.train.funcs, sizeof(Function) * result->func.train.nfuncs); for(int i = 0; i < right->func.train.nfuncs; i++) result->func.train.funcs[oldn + i] = dupfunction(right->func.train.funcs[i]); }else{ result->func.train.nfuncs++; result->func.train.funcs = erealloc(result->func.train.funcs, sizeof(Function) * result->func.train.nfuncs); result->func.train.funcs[result->func.train.nfuncs-1] = dupfunction(right->func); } return result; }