function interpret(stmt, ctx) {
if (typeof ctx === 'undefined') {
return interpret(stmt, getDefaultCtx());
} else if (!Array.isArray(stmt)) {
throw new Error('Expecting statement to be an array');
} else if (stmt[0] === 'call') {
const head = interpret(stmt[1], ctx);
const args = stmt.slice(2).map(arg => interpret(arg, ctx));
const argsTypes = args.map(arg => arg.type.name);
const argsSignature = '(' + argsTypes.join(', ') + ')';
if (!head instanceof Typed) throw new Error();
if (!args.every(x => x instanceof Typed)) throw new Error();
if (head.type === types.polymorphic) {
for (const entry of head.value) {
if (checkSignature(entry.args, args)) {
return new Typed(entry.resType, entry.js(...args.map(arg => arg.value)));
}
}
const funName = head.value[0].name;
throw new Error(`No signature matches ${argsSignature} for function ${funName}`);
} else if (head.type === types.function) {
return head.value(...args);
} else {
throw new Error();
}
} else if (stmt[0] === 'fun') {
return new Typed(types.function, (...args) => {
const ctx = getDefaultCtx();
Object.entries(stmt[1].meta.imports).forEach(([importAs, fun]) => {
ctx[importAs] = interpret(['fun', fun]);
});
if (!checkSignature(stmt[1].meta.args, args)) {
const argsTypes = args.map(arg => arg.type.name);
const argsSignature = '(' + argsTypes.join(', ') + ')';
throw new Error(`Function ${stmt[1].meta.name} signature mismatch: got ${argsSignature}`);
}
Object.entries(stmt[1].meta.args).forEach(([argName, argType], argNum) => {
ctx[argName] = args[argNum];
});
const res = interpret(stmt[1].body, ctx);
if (res.type === types.assignments) {
return Object.values(res.value(ctx)).at(-1);
} else {
return res;
}
});
} else if (stmt[0] === 'name') {
return ctx[stmt[1]];
} else if (stmt[0] === 'const') {
const s = stmt[1];
if (/^[0-9]*\.[0-9]*$/g.test(s)) {
return new Typed(types.float, Number(s));
} else if (/^[0-9]+$/g.test(s)) {
return new Typed(types.int, Number(s));
} else {
throw new Error(`Unexpected constant: "${s}"`);
}
} else if (stmt[0] === 'assignments') {
return new Typed(types.assignments, ctx => {
const res = {...ctx};
stmt.slice(1).forEach(([name, expr]) => {
const curRes = interpret(expr, res);
if (curRes.type === types.assignments) {
res = curRes(res);
} else {
res[name] = curRes;
}
});
return res;
});
} else {
throw new Error(`Unknown statement: "${stmt[0]}"`);
}
}