#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) stmt->toks[stmt->ntoks-1] = *lookup(stmt->toks[stmt->ntoks-1]);; 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 && stmt->toks[offset-1].tag == NameTag){ stmt->toks[offset-1] = *lookup(stmt->toks[offset-1]); 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", EParse); else{ Datum new = fn(stmt->toks[offset],stmt->toks[offset+1]); traceprint("handler fn done\n"); if(stmt->toks[offset].tag == ArrayTag) freearray(stmt->toks[offset].array); if(stmt->toks[offset+1].tag == ArrayTag) freearray(stmt->toks[offset+1].array); 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->toks[0].array->size != 1) guardOK = 0; else if(stmt->toks[0].array->type != AtypeInt) guardOK = 0; else if(stmt->toks[0].array->intdata[0] != 0 && stmt->toks[0].array->intdata[0] != 1) guardOK = 0; if(!guardOK) throwerror(L"Guard expected single valued boolean", EDomain); else{ stop = 0; if(stmt->toks[0].array->intdata[0] == 1) return eval(stmt->guard, toplevel); } } if(stmt->next && !stop) return eval(stmt->next, toplevel); else return stmt->toks; }else return nil; } Datum * lookup(Datum var) { traceprint("VAR LOOKUP %S\n", var.name); Symbol *symbol = getsym(var.name, 0); if(symbol->undefined){ Rune *msg = runesmprint("Undefined name: %S", var.name); throwerror(msg, EValue); } Datum *val; if(symbol->getfn != nil) val = symbol->getfn(); else{ val = &symbol->value; if(val->tag == ArrayTag) incarrayref(val->array); /* since the value is now in the var AND in the code */ } 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", left.array->stranded, right.array->stranded); Datum result; result.shy = 0; Array *leftarr = left.array->stranded ? fnSame(left.array) : fnEnclose(left.array); Array *rightarr = right.array->stranded ? fnSame(right.array) : fnEnclose(right.array); Array *tmp = fnCatenateFirst(leftarr, rightarr); result.tag = ArrayTag; result.array = simplifyarray(tmp); result.array->stranded = 1; freearray(tmp); freearray(leftarr); freearray(rightarr); return result; } Datum monadfun(Datum left, Datum right) { traceprint("Monadic function application\n"); Datum result; result.tag = ArrayTag; result.shy = 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; result.shy = 0; result.tag = BoundFunctionTag; if(right.tag == FunctionTag) result.func = right.func; else if(right.tag == HybridTag){ result.func.type = FunctypeHybrid; result.func.code = right.hybrid; } result.func.left = left.array; incarrayref(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); if(result[0].tag == ArrayTag) result[0].array->stranded = 0; result[0].shy = 0; return result[0]; /* TODO handle error if ntoks != 1 */ } Datum nameis(Datum left, Datum right) { traceprint("NAMEIS %S←\n", left.name); right.tag = AssignmentTag; right.names.ntoks = 1; right.names.toks = malloc(sizeof(Datum)); right.names.toks[0] = left; return right; } Datum namesis(Datum left, Datum right) { if(left.tag == RParTag) return right; traceprint("NAMES IS %S %S\n", ppdatum(left), ppdatum(right)); right.tag = AssignmentTag; right.names = left.stmt; return right; } Datum assign(Datum left, Datum right) { traceprint("ASSIGN %S %S\n", ppdatum(left), ppdatum(right)); if(left.names.ntoks == 1){ 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); if(symbol->undefined == 0 && symbol->value.tag == ArrayTag) freearray(symbol->value.array); symbol->value = right; symbol->undefined = 0; if(symbol->value.tag == ArrayTag){ symbol->value.array->stranded = 0; incarrayref(right.array); /* for the binding */ } } }else{ if(right.tag != ArrayTag) throwerror(nil, ESyntax); if(right.array->rank != 1 && right.array->rank != 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 = realloc(locations, sizeof(Datum) * nlocs); locations[nlocs-1].tag = AssignmentTag; locations[nlocs-1].names.ntoks = 1; locations[nlocs-1].names.toks = malloc(sizeof(Datum)); locations[nlocs-1].names.toks[0] = loc; }else if(loc.tag == LParTag){ i++; nlocs++; locations = realloc(locations, sizeof(Datum) * nlocs); locations[nlocs-1].tag = AssignmentTag; locations[nlocs-1].names = loc.stmt; } } if(right.array->rank == 1 && right.array->size != nlocs) throwerror(nil, ELength); for(int i = 0; i < nlocs; i++){ if(right.array->rank == 0) assign(locations[i], right); else{ Datum item; item.tag = ArrayTag; item.array = arrayitem(right.array, i); assign(locations[i], item); freearray(item.array); } } } right.shy = 1; if(right.tag == ArrayTag) incarrayref(right.array); /* for the returned array */ return right; } Datum monadop(Datum left, Datum right) { traceprint("Applying left argument to operator\n"); Datum *arg = emalloc(sizeof(Datum)); *arg = left; Datum result; result.shy = 0; result.tag = FunctionTag, result.func.type = FunctypeOp; if(right.tag == MonadicOpTag || right.tag == DyadicOpTag) result.func.operator = right.operator; else{ result.func.operator.type = OperatortypeHybrid; result.func.operator.code = right.hybrid; } result.func.operator.left = arg; result.func.left = nil; if(arg->tag == ArrayTag) incarrayref(arg->array); return result; } Datum dyadop(Datum left, Datum right) { traceprint("Applying right argument to operator\n"); Datum *arg = emalloc(sizeof(Datum)); *arg = right; Datum result; result.shy = 0; result.tag = MonadicOpTag, result.operator = left.operator; result.operator.right = arg; if(arg->tag == ArrayTag) incarrayref(arg->array); return result; } Datum train(Datum left, Datum right) { Datum result; result.shy = 0; result.tag = FunctionTag; 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 = malloc(sizeof(Function)); result.func.train.funcs[0] = 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 = realloc(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] = right.func.train.funcs[i]; }else{ result.func.train.nfuncs++; result.func.train.funcs = realloc(result.func.train.funcs, sizeof(Function) * result.func.train.nfuncs); result.func.train.funcs[result.func.train.nfuncs-1] = right.func; } return result; }