Skip to content

Instantly share code, notes, and snippets.

@staydecent
Last active February 15, 2020 01:29
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 staydecent/b47cab071fc5076c53a2895a01e55d77 to your computer and use it in GitHub Desktop.
Save staydecent/b47cab071fc5076c53a2895a01e55d77 to your computer and use it in GitHub Desktop.
Lisp interpretor in JS, based on http://www.norvig.com/lispy.html
const check = require('check-arg-types')
const type = check.prototype.toType
const tokenize = str =>
str.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').split(' ').filter(x => !!x)
const atom = token =>
isNaN(Number(token)) ? String(token) : Number(token)
const readFromTokens = tokens => {
if (!tokens || !tokens.length) throw new Error('unexpected EOF')
let token = tokens.shift()
if (token === ')') {
throw new Error('unexpected )')
} else if (token === '(') {
let L = []
while (tokens[0] !== ')') {
L.push(readFromTokens(tokens))
}
tokens.shift()
return L
} else {
return atom(token)
}
}
const parse = program => readFromTokens(tokenize(program))
let ENV = {
begin: (...args) => args[args.length - 1],
pi: Math.PI,
'*': (x, y) => x * y
}
const run = (exp, env = ENV) => {
if (type(exp) === 'string') {
return env[exp]
} else if (type(exp) === 'number') {
return exp
} else if (exp[0] === 'if') {
let [, predicate, result, alt] = exp
let x = run(predicate, env) ? result : alt
return run(x, env)
} else if (exp[0] === 'def') {
let [, symbol, x] = exp
ENV[symbol] = run(x, env)
} else {
let [head, ...tail] = exp
let proc = run(head, env)
let args = tail.map(arg => run(arg, env))
return proc.apply(null, args)
}
}
// --
const program = '(begin (def r 10) (* pi (* r r)))'
console.log(
run(parse(program))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment