Skip to content

Instantly share code, notes, and snippets.

@thomasvrgn
Created April 5, 2021 14:51
Show Gist options
  • Select an option

  • Save thomasvrgn/b0ca8722bc12ad5ae005272657e7502f to your computer and use it in GitHub Desktop.

Select an option

Save thomasvrgn/b0ca8722bc12ad5ae005272657e7502f to your computer and use it in GitHub Desktop.
Lisp interpreter
// Based on other parser gist
const tokens = tokenize(code);
const ast = parse(tokens);
run(ast);
const stack = [[]];
const frame = (stack) => peek(stack).slice(-1)[0];
const peek = (stack) => stack.slice(-1)[0];
const variables = (frame) => {
const vars = new Map();
for (const _frame of frame) {
for (const variable of _frame) {
vars.set(variable[0], variable[1]);
}
}
return vars;
}
const isContainer = (ast) => Array.isArray(ast)
&& ast.every((child) => Array.isArray(child));
function run(ast) {
if (isContainer(ast)) {
let ret;
for (const child of ast) {
ret = run(child);
if (ret && ret[0] === true) {
break;
}
}
return ret;
} else if (Array.isArray(ast)) {
const [_expr, ...args] = ast;
const expr = _expr.value;
if (expr === 'begin') {
peek(stack).push(new Map());
let ret = run(args);
peek(stack).pop();
return ret && ret[0] === 1 ? ret[1] : ret;
} else if (expr === 'print') {
console.log(...args.map((x) => run(x).value))
} else if (expr === 'let') {
frame(stack).set(args[0].value, run(args[1]));
} else if (expr === 'fn') {
return {
type: 'Function',
args: args[0],
body: args[1],
scope: frame(stack),
}
} else if (expr === 'return') {
return [1, args[0]];
} else {
const func = run(_expr);
if (func !== undefined) {
stack.push([func.scope]);
for (const index in func.args) {
const arg = func.args[index].value;
const callArg = args[index];
frame(stack).set(arg, run(callArg));
}
let ret = run(func.body);
stack.pop();
return ret;
}
}
} else if ('value' in ast) {
if (ast.type === 'Word') {
return variables(peek(stack)).get(ast.value);
} else return ast;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment