141 lines
2.8 KiB
JavaScript
141 lines
2.8 KiB
JavaScript
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)); |