exec(ex: ref Exec, code: ref Code): Completion{ ssp := ex.sp; r := estmt(ex, code, 0, code.npc); if(r.kind == CThrow) ex.sp = ssp; if(ssp != ex.sp) runtime(ex, InternalError, "internal error: exec stack not balanced"); if(r.lab != nil) runtime(ex, InternalError, "internal error: label out of stack"); return r;} estmt(ex: ref Exec, code: ref Code, pc, epc: int): Completion{ e: ref Ref; ev: ref Val; k, apc, pc2, apc2, pc3, apc3, c: int; lab: string; labs: list of string; osp := ex.sp; { v : ref Val = nil; k1 := CNormal; while(pc < epc){ v1 : ref Val = nil; labs = nil; op := int code.ops[pc++]; while(op == Llabel){ (pc, c) = getconst(code.ops, pc); labs = code.strs[c] :: labs; op = int code.ops[pc++]; } if(debug['e'] > 1) print("estmt(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op)); case op { Lbreak => return (CBreak, v, nil); Lcontinue => return (CContinue, v, nil); Lbreaklab => (pc, c) = getconst(code.ops, pc); return (CBreak, v, code.strs[c]); Lcontinuelab => (pc, c) = getconst(code.ops, pc); return (CContinue, v, code.strs[c]); Lreturn => (pc, v) = eexpval(ex, code, pc, code.npc); return (CReturn, v, nil); '{' => (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = estmt(ex, code, pc, apc); pc = apc; Lif => (pc, apc) = getjmp(code.ops, pc); (pc, ev) = eexpval(ex, code, pc, apc); (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); if(toBoolean(ex, ev) != false) (k1, v1, lab) = estmt(ex, code, pc, apc); else if(pc2 != apc2) (k1, v1, lab) = estmt(ex, code, pc2, apc2); pc = apc2; Lwhile => (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); for(;;){ (nil, ev) = eexpval(ex, code, pc, apc); if(toBoolean(ex, ev) == false) break; (k, v1, lab) = estmt(ex, code, pc2, apc2); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break; else continue; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); } pc = apc2; Ldo => (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); for(;;){ (k, v1, lab) = estmt(ex, code, pc, apc); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break; else continue; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); (nil, ev) = eexpval(ex, code, pc2, apc2); if(toBoolean(ex, ev) == false) break; } pc = apc2; Lfor or Lforvar => (pc, apc) = getjmp(code.ops, pc); (pc, nil) = eexpval(ex, code, pc, apc); (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); (pc3, apc3) = getjmp(code.ops, apc2); for(;;){ (nil, e) = eexp(ex, code, pc, apc); if(e != nil && toBoolean(ex, getValue(ex, e)) == false) break; (k, v1, lab) = estmt(ex, code, pc3, apc3); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break; else continue; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); eexpval(ex, code, pc2, apc2); } pc = apc3; Lforin or Lforvarin => (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); (pc3, apc3) = getjmp(code.ops, apc2); if(op == Lforvarin){ (nil, nil) = eexp(ex, code, pc, apc); # during for only evaluate the id, not the initializer apc = pc + 1; } (nil, ev) = eexpval(ex, code, pc2, apc2); bo := toObject(ex, ev); # # note this won't enumerate host properties # enum: for(o := bo; o != nil; o = o.prototype){ if(o.host != nil && o.host != me) continue; for(i := 0; i < len o.props; i++){ if(o.props[i] == nil || (o.props[i].attr & DontEnum) || propshadowed(bo, o, o.props[i].name)) continue; (nil, e) = eexp(ex, code, pc, apc); putValue(ex, e, strval(o.props[i].name)); (k, v1, lab) = estmt(ex, code, pc3, apc3); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break enum; else continue enum; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); } } pc = apc3; Lwith => (pc, apc) = getjmp(code.ops, pc); (pc, ev) = eexpval(ex, code, pc, apc); pushscope(ex, toObject(ex, ev)); (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = estmt(ex, code, pc, apc); popscope(ex); pc = apc; ';' => ; Lvar => (pc, apc) = getjmp(code.ops, pc); (pc, nil) = eexp(ex, code, pc, apc); Lswitch => (pc, apc) = getjmp(code.ops, pc); (pc, ev) = eexpval(ex, code, pc, apc); (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = ecaseblk(ex, code, ev, pc, apc, labs); pc = apc; Lthrow => (pc, v) = eexpval(ex, code, pc, code.npc); ex.error = toString(ex, v); return (CThrow, v, nil); Lprint => (pc, v1) = eexpval(ex, code, pc, code.npc); print("%s\n", toString(ex, v1)); Ltry => (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = estmt(ex, code, pc, apc); (kc, vc) := (k1, v1); (pc, apc) = getjmp(code.ops, apc); if(pc != apc){ (pc, c) = getconst(code.ops, ++pc); if(k1 == CThrow){ o := mkobj(ex.objproto, "Object"); valinstant(o, DontDelete, code.strs[c], v1); pushscope(ex, o); (k1, v1, lab) = estmt(ex, code, pc, apc); popscope(ex); if(k1 != CNormal) (kc, vc) = (k1, v1); } } (pc, apc) = getjmp(code.ops, apc); if(pc != apc){ (k, v, lab) = estmt(ex, code, pc, apc); if(k == CNormal) (k1, v1) = (kc, vc); else (k1, v1) = (k, v); } pc = apc; * => (pc, e) = eexp(ex, code, pc-1, code.npc); if(e != nil) v1 = getValue(ex, e); if(debug['v']) print("%s\n", toString(ex, v1)); } if(v1 != nil) v = v1; if(k1 == CBreak && lab != nil && inlabs(lab, labs)) (k1, lab) = (CNormal, nil); if(k1 != CNormal) return (k1, v, lab); } return (CNormal, v, nil);}exception{ "throw" => ex.sp = osp; return (CThrow, ex.errval, nil);}} ecaseblk(ex : ref Exec, code : ref Code, sv : ref Val, pc, epc : int, labs: list of string) : Completion{ defpc, nextpc, clausepc, apc : int; ev : ref Val; lab: string; k := CNormal; v := undefined; matched := 0; (pc, defpc) = getjmp(code.ops, pc); clausepc = pc; (pc, nextpc) = getjmp(code.ops, pc); for (; pc <= epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) { if (nextpc == epc) { if (matched || defpc == epc) break; # do the default matched = 1; nextpc = defpc; continue; } if (!matched && clausepc == defpc) # skip default case - still scanning guards continue; if (clausepc != defpc) { # only case clauses have guard exprs (pc, apc) = getjmp(code.ops, pc); if (matched) pc = apc; else { (pc, ev) = eexpval(ex, code, pc, apc); if (identical(sv, ev)) matched = 1; else continue; } } (k, v, lab) = estmt(ex, code, pc, nextpc); if(k == CBreak && initlabs(lab, labs)) return (CNormal, v, nil); if(k == CBreak || k == CContinue || k == CReturn || k == CThrow) return (k, v, lab); } return (k, v, lab);} identical(v1, v2 : ref Val) : int{ if (v1.ty != v2.ty) return 0; ret := 0; case v1.ty{ TUndef or TNull => ret = 1; TNum => if(v1.num == v2.num) ret = 1; TBool => if(v1 == v2) ret = 1; TStr => if(v1.str == v2.str) ret = 1; TObj => if(v1.obj == v2.obj) ret = 1; TRegExp => if(v1.rev == v2.rev) ret = 1; } return ret;} eexpval(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Val){ e: ref Ref; (pc, e) = eexp(ex, code, pc, epc); if(e == nil) v := undefined; else v = getValue(ex, e); return (pc, v);} eexp(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Ref){ o, th: ref Obj; a1: ref Ref; v, v1, v2: ref Val; s: string; r1, r2: real; c, apc, i1, i2: int; savesp := ex.sp;out: while(pc < epc){ op := int code.ops[pc++]; if(debug['e'] > 1){ case op{ Lid or Lstr or Lregexp => (nil, c) = getconst(code.ops, pc); print("eexp(pc %d, sp %d) %s '%s'\n", pc-1, ex.sp, tokname(op), code.strs[c]); Lnum => (nil, c) = getconst(code.ops, pc); print("eexp(pc %d, sp %d) %s '%g'\n", pc-1, ex.sp, tokname(op), code.nums[c]); * => print("eexp(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op)); } } case op{ Lthis => v1 = objval(ex.this); Lnum => (pc, c) = getconst(code.ops, pc); v1 = numval(code.nums[c]); Lstr => (pc, c) = getconst(code.ops, pc); v1 = strval(code.strs[c]); Lregexp => (pc, c) = getconst(code.ops, pc); (p, f) := rsplit(code.strs[c]); o = nregexp(ex, nil, array[] of { strval(p), strval(f) }); v1 = objval(o); # v1 = regexpval(p, f, 0); Lid => (pc, c) = getconst(code.ops, pc); epush(ex, esprimid(ex, code.strs[c])); continue; Lnoval => v1 = undefined; '.' => a1 = epop(ex); v1 = epopval(ex); epush(ex, ref Ref(1, nil, toObject(ex, v1), a1.name)); continue; '[' => v2 = epopval(ex); v1 = epopval(ex); epush(ex, ref Ref(1, nil, toObject(ex, v1), toString(ex, v2))); continue; Lpostinc or Lpostdec => a1 = epop(ex); r1 = toNumber(ex, getValue(ex, a1)); v1 = numval(r1); if(op == Lpostinc) r1++; else r1--; putValue(ex, a1, numval(r1)); Linc or Ldec or Lpreadd or Lpresub => a1 = epop(ex); r1 = toNumber(ex, getValue(ex, a1)); case op{ Linc => r1++; Ldec => r1--; Lpresub => r1 = -r1; } v1 = numval(r1); if(op == Linc || op == Ldec) putValue(ex, a1, v1); '~' => v = epopval(ex); i1 = toInt32(ex, v); i1 = ~i1; v1 = numval(real i1); '!' => v = epopval(ex); v1 = toBoolean(ex, v); if(v1 == true) v1 = false; else v1 = true; Ltypeof => a1 = epop(ex); if(a1.isref && getBase(ex, a1) == nil) s = "undefined"; else case (v1 = getValue(ex, a1)).ty{ TUndef => s = "undefined"; TNull => s = "object"; TBool => s = "boolean"; TNum => s = "number"; TStr => s = "string"; TObj => if(v1.obj.call != nil) s = "function"; else s = "object"; TRegExp => s = "regexp"; } v1 = strval(s); Ldelete => a1 = epop(ex); o = getBase(ex, a1); s = getPropertyName(ex, a1); if(o != nil) esdelete(ex, o, s, 0); v1 = undefined; Lvoid => epopval(ex); v = undefined; '*' or '/' or '%' or '-' => v2 = epopval(ex); a1 = epop(ex); r1 = toNumber(ex, getValue(ex, a1)); r2 = toNumber(ex, v2); case op{ '*' => r1 = r1 * r2; '/' => r1 = r1 / r2; '%' => r1 = fmod(r1, r2); '-' =>Lines 1–500 of 863. Load more
exec(ex: ref Exec, code: ref Code): Completion { ssp := ex.sp; r := estmt(ex, code, 0, code.npc); if(r.kind == CThrow) ex.sp = ssp; if(ssp != ex.sp) runtime(ex, InternalError, "internal error: exec stack not balanced"); if(r.lab != nil) runtime(ex, InternalError, "internal error: label out of stack"); return r; } estmt(ex: ref Exec, code: ref Code, pc, epc: int): Completion { e: ref Ref; ev: ref Val; k, apc, pc2, apc2, pc3, apc3, c: int; lab: string; labs: list of string; osp := ex.sp; { v : ref Val = nil; k1 := CNormal; while(pc < epc){ v1 : ref Val = nil; labs = nil; op := int code.ops[pc++]; while(op == Llabel){ (pc, c) = getconst(code.ops, pc); labs = code.strs[c] :: labs; op = int code.ops[pc++]; } if(debug['e'] > 1) print("estmt(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op)); case op { Lbreak => return (CBreak, v, nil); Lcontinue => return (CContinue, v, nil); Lbreaklab => (pc, c) = getconst(code.ops, pc); return (CBreak, v, code.strs[c]); Lcontinuelab => (pc, c) = getconst(code.ops, pc); return (CContinue, v, code.strs[c]); Lreturn => (pc, v) = eexpval(ex, code, pc, code.npc); return (CReturn, v, nil); '{' => (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = estmt(ex, code, pc, apc); pc = apc; Lif => (pc, apc) = getjmp(code.ops, pc); (pc, ev) = eexpval(ex, code, pc, apc); (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); if(toBoolean(ex, ev) != false) (k1, v1, lab) = estmt(ex, code, pc, apc); else if(pc2 != apc2) (k1, v1, lab) = estmt(ex, code, pc2, apc2); pc = apc2; Lwhile => (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); for(;;){ (nil, ev) = eexpval(ex, code, pc, apc); if(toBoolean(ex, ev) == false) break; (k, v1, lab) = estmt(ex, code, pc2, apc2); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break; else continue; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); } pc = apc2; Ldo => (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); for(;;){ (k, v1, lab) = estmt(ex, code, pc, apc); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break; else continue; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); (nil, ev) = eexpval(ex, code, pc2, apc2); if(toBoolean(ex, ev) == false) break; } pc = apc2; Lfor or Lforvar => (pc, apc) = getjmp(code.ops, pc); (pc, nil) = eexpval(ex, code, pc, apc); (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); (pc3, apc3) = getjmp(code.ops, apc2); for(;;){ (nil, e) = eexp(ex, code, pc, apc); if(e != nil && toBoolean(ex, getValue(ex, e)) == false) break; (k, v1, lab) = estmt(ex, code, pc3, apc3); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break; else continue; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); eexpval(ex, code, pc2, apc2); } pc = apc3; Lforin or Lforvarin => (pc, apc) = getjmp(code.ops, pc); (pc2, apc2) = getjmp(code.ops, apc); (pc3, apc3) = getjmp(code.ops, apc2); if(op == Lforvarin){ (nil, nil) = eexp(ex, code, pc, apc); # during for only evaluate the id, not the initializer apc = pc + 1; } (nil, ev) = eexpval(ex, code, pc2, apc2); bo := toObject(ex, ev); # # note this won't enumerate host properties # enum: for(o := bo; o != nil; o = o.prototype){ if(o.host != nil && o.host != me) continue; for(i := 0; i < len o.props; i++){ if(o.props[i] == nil || (o.props[i].attr & DontEnum) || propshadowed(bo, o, o.props[i].name)) continue; (nil, e) = eexp(ex, code, pc, apc); putValue(ex, e, strval(o.props[i].name)); (k, v1, lab) = estmt(ex, code, pc3, apc3); if(v1 != nil) v = v1; if(k == CBreak || k == CContinue){ if(initlabs(lab, labs)){ if(k == CBreak) break enum; else continue enum; } else return (k, v1, lab); } if(k == CReturn || k == CThrow) return (k, v1, nil); } } pc = apc3; Lwith => (pc, apc) = getjmp(code.ops, pc); (pc, ev) = eexpval(ex, code, pc, apc); pushscope(ex, toObject(ex, ev)); (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = estmt(ex, code, pc, apc); popscope(ex); pc = apc; ';' => ; Lvar => (pc, apc) = getjmp(code.ops, pc); (pc, nil) = eexp(ex, code, pc, apc); Lswitch => (pc, apc) = getjmp(code.ops, pc); (pc, ev) = eexpval(ex, code, pc, apc); (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = ecaseblk(ex, code, ev, pc, apc, labs); pc = apc; Lthrow => (pc, v) = eexpval(ex, code, pc, code.npc); ex.error = toString(ex, v); return (CThrow, v, nil); Lprint => (pc, v1) = eexpval(ex, code, pc, code.npc); print("%s\n", toString(ex, v1)); Ltry => (pc, apc) = getjmp(code.ops, pc); (k1, v1, lab) = estmt(ex, code, pc, apc); (kc, vc) := (k1, v1); (pc, apc) = getjmp(code.ops, apc); if(pc != apc){ (pc, c) = getconst(code.ops, ++pc); if(k1 == CThrow){ o := mkobj(ex.objproto, "Object"); valinstant(o, DontDelete, code.strs[c], v1); pushscope(ex, o); (k1, v1, lab) = estmt(ex, code, pc, apc); popscope(ex); if(k1 != CNormal) (kc, vc) = (k1, v1); } } (pc, apc) = getjmp(code.ops, apc); if(pc != apc){ (k, v, lab) = estmt(ex, code, pc, apc); if(k == CNormal) (k1, v1) = (kc, vc); else (k1, v1) = (k, v); } pc = apc; * => (pc, e) = eexp(ex, code, pc-1, code.npc); if(e != nil) v1 = getValue(ex, e); if(debug['v']) print("%s\n", toString(ex, v1)); } if(v1 != nil) v = v1; if(k1 == CBreak && lab != nil && inlabs(lab, labs)) (k1, lab) = (CNormal, nil); if(k1 != CNormal) return (k1, v, lab); } return (CNormal, v, nil); } exception{ "throw" => ex.sp = osp; return (CThrow, ex.errval, nil); } } ecaseblk(ex : ref Exec, code : ref Code, sv : ref Val, pc, epc : int, labs: list of string) : Completion { defpc, nextpc, clausepc, apc : int; ev : ref Val; lab: string; k := CNormal; v := undefined; matched := 0; (pc, defpc) = getjmp(code.ops, pc); clausepc = pc; (pc, nextpc) = getjmp(code.ops, pc); for (; pc <= epc; (clausepc, (pc, nextpc)) = (nextpc, getjmp(code.ops, nextpc))) { if (nextpc == epc) { if (matched || defpc == epc) break; # do the default matched = 1; nextpc = defpc; continue; } if (!matched && clausepc == defpc) # skip default case - still scanning guards continue; if (clausepc != defpc) { # only case clauses have guard exprs (pc, apc) = getjmp(code.ops, pc); if (matched) pc = apc; else { (pc, ev) = eexpval(ex, code, pc, apc); if (identical(sv, ev)) matched = 1; else continue; } } (k, v, lab) = estmt(ex, code, pc, nextpc); if(k == CBreak && initlabs(lab, labs)) return (CNormal, v, nil); if(k == CBreak || k == CContinue || k == CReturn || k == CThrow) return (k, v, lab); } return (k, v, lab); } identical(v1, v2 : ref Val) : int { if (v1.ty != v2.ty) return 0; ret := 0; case v1.ty{ TUndef or TNull => ret = 1; TNum => if(v1.num == v2.num) ret = 1; TBool => if(v1 == v2) ret = 1; TStr => if(v1.str == v2.str) ret = 1; TObj => if(v1.obj == v2.obj) ret = 1; TRegExp => if(v1.rev == v2.rev) ret = 1; } return ret; } eexpval(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Val) { e: ref Ref; (pc, e) = eexp(ex, code, pc, epc); if(e == nil) v := undefined; else v = getValue(ex, e); return (pc, v); } eexp(ex: ref Exec, code: ref Code, pc, epc: int): (int, ref Ref) { o, th: ref Obj; a1: ref Ref; v, v1, v2: ref Val; s: string; r1, r2: real; c, apc, i1, i2: int; savesp := ex.sp; out: while(pc < epc){ op := int code.ops[pc++]; if(debug['e'] > 1){ case op{ Lid or Lstr or Lregexp => (nil, c) = getconst(code.ops, pc); print("eexp(pc %d, sp %d) %s '%s'\n", pc-1, ex.sp, tokname(op), code.strs[c]); Lnum => (nil, c) = getconst(code.ops, pc); print("eexp(pc %d, sp %d) %s '%g'\n", pc-1, ex.sp, tokname(op), code.nums[c]); * => print("eexp(pc %d, sp %d) %s\n", pc-1, ex.sp, tokname(op)); } } case op{ Lthis => v1 = objval(ex.this); Lnum => (pc, c) = getconst(code.ops, pc); v1 = numval(code.nums[c]); Lstr => (pc, c) = getconst(code.ops, pc); v1 = strval(code.strs[c]); Lregexp => (pc, c) = getconst(code.ops, pc); (p, f) := rsplit(code.strs[c]); o = nregexp(ex, nil, array[] of { strval(p), strval(f) }); v1 = objval(o); # v1 = regexpval(p, f, 0); Lid => (pc, c) = getconst(code.ops, pc); epush(ex, esprimid(ex, code.strs[c])); continue; Lnoval => v1 = undefined; '.' => a1 = epop(ex); v1 = epopval(ex); epush(ex, ref Ref(1, nil, toObject(ex, v1), a1.name)); continue; '[' => v2 = epopval(ex); v1 = epopval(ex); epush(ex, ref Ref(1, nil, toObject(ex, v1), toString(ex, v2))); continue; Lpostinc or Lpostdec => a1 = epop(ex); r1 = toNumber(ex, getValue(ex, a1)); v1 = numval(r1); if(op == Lpostinc) r1++; else r1--; putValue(ex, a1, numval(r1)); Linc or Ldec or Lpreadd or Lpresub => a1 = epop(ex); r1 = toNumber(ex, getValue(ex, a1)); case op{ Linc => r1++; Ldec => r1--; Lpresub => r1 = -r1; } v1 = numval(r1); if(op == Linc || op == Ldec) putValue(ex, a1, v1); '~' => v = epopval(ex); i1 = toInt32(ex, v); i1 = ~i1; v1 = numval(real i1); '!' => v = epopval(ex); v1 = toBoolean(ex, v); if(v1 == true) v1 = false; else v1 = true; Ltypeof => a1 = epop(ex); if(a1.isref && getBase(ex, a1) == nil) s = "undefined"; else case (v1 = getValue(ex, a1)).ty{ TUndef => s = "undefined"; TNull => s = "object"; TBool => s = "boolean"; TNum => s = "number"; TStr => s = "string"; TObj => if(v1.obj.call != nil) s = "function"; else s = "object"; TRegExp => s = "regexp"; } v1 = strval(s); Ldelete => a1 = epop(ex); o = getBase(ex, a1); s = getPropertyName(ex, a1); if(o != nil) esdelete(ex, o, s, 0); v1 = undefined; Lvoid => epopval(ex); v = undefined; '*' or '/' or '%' or '-' => v2 = epopval(ex); a1 = epop(ex); r1 = toNumber(ex, getValue(ex, a1)); r2 = toNumber(ex, v2); case op{ '*' => r1 = r1 * r2; '/' => r1 = r1 / r2; '%' => r1 = fmod(r1, r2); '-' =>