const STR = (s) => ({type: "string", value: s}); const FN = (f) => ({type: "function", value: f}); const SYM = (s) => ({type: "symbol", value: s}); const UNDEF = () => ({type: "undefined", value: null}); const CODE = (p) => ({type: "code", value: p}); const THUNK = (t) => ({type: "thunk", value: t}); const BOOL = (b) => ({type: "boolean", value: b}); const evaluate_codeblock = (codeBlock, env) => { let result = { result: UNDEF(), passalong: {}, }; const terms = [...codeBlock.value]; while (terms.length) { // console.log('evaluating phrase', terms); result = evaluate_phrase(terms, env, result.passalong); } return result; } const evaluate_phrase = (terms, env, passalong) => { let head = evaluate_term(terms.shift(), env, passalong); let pa = passalong; while (head.result.type === "function") { if (!terms.length) { throw 'Runtime Error: head function has no argument to be called with'; } const tail = evaluate_term(terms.shift(), env, {}); head = head.result.value(tail.result, env, pa); pa = head.passalong; } return head; } const evaluate_term = (term, env, passalong) => { if (term.type === "symbol") { return { result: env[term.value], passalong: {name: STR(term.value)}, // TODO: this should include term.value as a "name" } } else if (term.type === "string") { return { result: term, passalong: {}, } } else if (term.type === "function") { return { result: term, passalong: {}, } } else if (term.type === "boolean") { return { result: term, passalong: {}, } } else if (term.type === "thunk") { return { result: term, passalong: {}, } } return { result: UNDEF(), passalong: {}, } } const my_env = { print: FN((arg, env, passalong) => { console.log('PRINT', arg); return { result: UNDEF(), passalong: {}, } }), if: FN((arg, env, passalong) => { return { result: FN((thunk, e2, pa) => { if (arg.type === "boolean" && arg.value) { return { result: evaluate_codeblock(thunk, e2), passalong: {handled: true}, } } else { return { result: UNDEF(), passalong: {handled: false}, } } }), passalong: {}, } }), else: FN((thunk, env, passalong) => { if (!passalong.handled) { return { result: evaluate_codeblock(thunk, env), passalong: {handled: true}, } } else { return { result: UNDEF(), passalong: {handled: true}, } } }), } ` print "hi" if false { print "hi2" } else { print "hi3" } set x = 10 set y = 20 set f = fn h => { print h print h } f x for value in obj { } ` const my_prog = CODE([ SYM("print"), STR("hi"), SYM("if"), BOOL(false), THUNK([ SYM("print"), STR("hi2"), ]), SYM("else"), THUNK([ SYM("print"), STR("hi3"), ]), ]); console.log(evaluate_codeblock(my_prog, my_env));