Skip to content

Instantly share code, notes, and snippets.

@divs1210
Last active August 13, 2021 11:03
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/69d92d6f3598ded2a38bdb66bec12624 to your computer and use it in GitHub Desktop.
Save divs1210/69d92d6f3598ded2a38bdb66bec12624 to your computer and use it in GitHub Desktop.
<!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) {
return {
isLispFn: true,
params: params,
body: body,
env: env
};
}
function zipmap(keys, vals) {
var result = {};
keys.forEach((key, i) => {
result[key] = vals[i];
});
return result;
}
function lispEval(_exp, _env) {
let [exp, env] = [_exp, _env];
while(true) {
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;
if(operator == 'do') {
let ops = operands;
exp = ops.pop();
for(let op of ops) {
lispEval(op, env);
}
} else if(operator == 'def') {
let [symbol, exp] = operands;
let topLevelEnv = getTopLevel(env);
topLevelEnv[symbol] = lispEval(exp, env);
return;
} else if(operator == 'if') {
let [testExp, thenExp, elseExp] = operands;
exp = lispEval(testExp, env)? thenExp : elseExp;
} else if(operator == 'fn') {
let [params, body] = operands;
return makeFunction(params, body, env);
} else if(operator == 'quote') {
let [quoted] = operands;
return quoted;
} else if(operator == 'js*') {
let [jsCode] = operands;
return eval(jsCode);
} else {
let fn = lispEval(operator, env);
let args = operands.map((o) => lispEval(o, env));
if(fn.isLispFn) {
exp = fn.body;
env = zipmap(fn.params, args);
env[parentEnvKey] = fn.env;
} else {
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>
@divs1210
Copy link
Author

divs1210 commented Aug 13, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment