this is the evaluation function for binary expressions (addition only) according to ES2015 spec, expressed in my JS CESK machine.
eval(fp, store, kont) {
let lref = this._left.eval(fp, store, kont);
let lval = es6.GetValue(lref, store);
let rref = this._right.eval(fp, store, kont);
let rval = es6.GetValue(rref, store);
switch (this.operator) {
case '+': {
// 7. Let lprim be ToPrimitive(lval).
// 8. ReturnIfAbrupt(lprim).
let lprim = es6.ToPrimitive(lval);
// 9. Let rprim be ToPrimitive(rval).
// 10. ReturnIfAbrupt(rprim).
let rprim = es6.ToPrimitive(rval);
// 11. If Type(lprim) is String or Type(rprim) is String, then
if ((lprim instanceof CStr) || (rprim instanceof CStr)) {
// a. Let lstr be ToString(lprim).
// b. ReturnIfAbrupt(lstr).
let lstr = es6.ToString(lprim);
// c. Let rstr be ToString(rprim).
// d. ReturnIfAbrupt(rstr).
let rstr = es6.ToString(rprim);
// e. Return the String that is the result of concatenating lstr and rstr.
return new CStr(lstr.value + rstr.value);
}
// 12. Let lnum be ToNumber(lprim).
// 13. ReturnIfAbrupt(lnum).
let lnum = es6.ToNumber(lprim);
// 14. Let rnum be ToNumber(rprim).
// 15. ReturnIfAbrupt(rnum).
let rnum = es6.ToNumber(rprim);
// 16. Return the result of applying the addition operation to lnum and rnum. See the Note below 12.7.5.
return new CNum(lnum.value + rnum.value);
}
of course that doesn't work, because ToNumber
, ToPrimitive
, and even eval
really need to pass the store for possible side effects, and also pass continuations because those functions can call back into user-defined JS.
So we end up with something like this:
eval(fp, store, kont) {
this._left.eval(fp, store, EvKont((lref, store) => {
let lval = es6.GetValue(lref, store);
this._right.eval(fp, store, EvKont((rref, store) => {
let rval = es6.GetValue(rref, store);
switch (this.operator) {
case '+': {
es6.ToPrimitive(lval, fp, store, EvKont((lprim, store) => {
es6.ToPrimitive(rval, fp, store, EvKont((rprim, store) => {
if ((lprim instanceof CStr) || (rprim instanceof CStr)) {
es6.ToString(lprim, fp, store, EvKont((lstr, store) => {
es6.ToString(rprim, fp, store, EvKont((rstr, store) => {
kont.ApplyValue(CStr(lstr.value + rstr.value), store);
}));
}));
}
else {
es6.ToNumber(lprim, fp, store, EvKont((lnum, store) => {
es6.ToNumber(rprim, fp, store, EvKont((rnum, store) => {
let rnum = es6.ToNumber(rprim);
kont.ApplyValue(CNum(lnum.value + rnum.value), store);
}));
}));
}
}));
}));
}
}
}));
}));