Skip to content

Instantly share code, notes, and snippets.

@divs1210
Last active August 12, 2021 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save divs1210/cfc17976a0ad85faa0099e4fd0a502fb to your computer and use it in GitHub Desktop.
Save divs1210/cfc17976a0ad85faa0099e4fd0a502fb to your computer and use it in GitHub Desktop.
Simple lisp interpreter in browser
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div>
<center>
<h3>Lisp.js</h3>
<h4>Use the browser console to try it out!</h4>
</center>
<pre style="margin-left: 44%;">
let env = baseEnv();
let code = ['+', 1, 2];
lispEval(code, env);
</pre>
</div>
<script type="text/javascript">
let parentEnvKey = '__parent_env__';
function getTopLevel(env) {
if(parentEnvKey in env)
return getTopLevel(env[parentEnvKey]);
return env;
}
function lookup(env, symbol) {
if(symbol in env)
return env[symbol];
if(parentEnvKey in env)
return lookup(env[parentEnvKey], symbol);
throw 'symbol ' +symbol+ ' is not defined!';
}
function makeFunction(params, body, env) {
let fn_internals = {
params: params,
body: body,
env: env
};
return function(...args) {
return lispApply(fn_internals, args);
}
}
function zipmap(keys, vals) {
var result = {};
keys.forEach((key, i) => {
result[key] = vals[i];
});
return result;
}
function lispApply(fn, args) {
let fnEnv = zipmap(fn.params, args);
fnEnv[parentEnvKey] = fn.env;
return lispEval(fn.body, fnEnv);
}
function lispEval(exp, env) {
if(typeof exp == 'number') { // numbers
return exp;
} else if(typeof exp == 'string') { // symbols
return lookup(env, exp);
} else if(Array.isArray(exp)) { // lists
let [operator, ...operands] = exp;
switch (operator) {
case 'do':
let ops = operands;
let ret = null;
for(let op of ops) {
ret = lispEval(op, env);
}
return ret;
case 'def':
let [symbol, exp] = operands;
let topLevelEnv = getTopLevel(env);
topLevelEnv[symbol] = lispEval(exp, env);
break;
case 'if':
let [testExp, thenExp, elseExp] = operands;
if(lispEval(testExp, env))
return lispEval(thenExp, env);
return lispEval(elseExp, env);
case 'fn':
let [params, body] = operands;
return makeFunction(params, body, env);
case 'quote':
let [quoted] = operands;
return quoted;
case 'js*':
let [jsCode] = operands;
return eval(jsCode);
default:
let fn = lispEval(operator, env);
let args = operands.map((o) => lispEval(o, env));
return fn(...args);
}
}
}
function baseEnv() {
return {
'+' : (a, b) => a + b,
'-' : (a, b) => a - b,
'*' : (a, b) => a * b,
'/' : (a, b) => a / b,
'^' : (a, b) => Math.pow(a, b),
'=' : (a, b) => a === b,
'<' : (a, b) => a < b,
'>' : (a, b) => a > b,
'<=': (a, b) => a <= b,
'>=': (a, b) => a >= b,
'not': (a) => !a
};
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment