Skip to content

Instantly share code, notes, and snippets.

@toshok
Last active March 6, 2016 18:29
Show Gist options
  • Save toshok/152f143a950f563ef92f to your computer and use it in GitHub Desktop.
Save toshok/152f143a950f563ef92f to your computer and use it in GitHub Desktop.
the problem with a CESK style machine for javascript

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);
                                    }));
                                }));
                            }
                    }));
                }));
                }
                }
            }));
        }));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment