Last active
December 29, 2015 17:29
-
-
Save fand/7704086 to your computer and use it in GitHub Desktop.
((coffeescriptで) 書く (Lisp) インタプリタ)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!usr/bin/env coffee | |
# coding: utf-8 | |
########################## | |
# Utilities | |
########################## | |
type = do -> | |
classToType = {} | |
for name in 'Boolean Number String Function Array Date RegExp Undefined Null'.split(' ') | |
classToType['[object ' + name + ']'] = name.toLowerCase() | |
(obj) -> | |
strType = Object::toString.call(obj) | |
classToType[strType] or 'object' | |
is_list = (l) -> Array.isArray(l) and l.length > 1 | |
is_symbol = (s) -> type(s) == 'string' | |
zip = (keys, values) -> | |
dic = {} | |
lim = Math.min(keys.length, values.length) - 1 | |
for i in [0..lim] | |
dic[keys[i]] = values[i] | |
return dic | |
########################## | |
# Environments | |
########################## | |
class Env | |
constructor: (params = [], args = [], outer = null) -> | |
this.update( zip(params, args) or {} ) | |
this.outer = outer | |
find: (key) -> | |
if this[key]? then this else this.outer.find(key) | |
update: (dic) -> | |
this[k] = v for k,v of dic | |
global_env = new Env | |
global_env.update( | |
'+': ((x,y) -> x+y), | |
'-': ((x,y) -> x-y), | |
'*': ((x,y) -> x*y), | |
'/': ((x,y) -> x/y), | |
'not': ((x) -> !x), | |
'>': ((x,y) -> x>y), | |
'<': ((x,y) -> x<y), | |
'>=': ((x,y) -> x>=y), | |
'<=': ((x,y) -> x<=y), | |
'=': ((x,y) -> x==y), | |
'equal?': ((x,y) -> x==y), | |
'eq?': ((x,y) -> x==y), | |
'length': ((x) -> x.length), | |
'cons': ((x,y) -> y.unshift x), | |
'car': ((x) -> x[0]), | |
'cdr': ((x) -> x.slice(1)), | |
'append': ((x,y) -> x.concat y), | |
'list': ((args...) -> args), | |
'list?': ((x) -> is_list(x)), | |
'null?': ((x) -> x?), | |
'symbol?': ((x) -> is_symbol(x)) | |
) | |
########################## | |
# Interpreters | |
########################## | |
evaluate = (x, env = global_env) -> | |
if is_symbol(x) # Variable | |
env.find(x)[x] | |
else if not is_list(x) # Constant literal | |
x[0] or x | |
else if x[0] == 'quote' # Quote | |
x[1] | |
else if x[0] == 'if' # if | |
[_, test, t, f] = x | |
evaluate((if evaluate(test, env) then t else f), env) | |
else if x[0] == 'set!' # set! | |
[_, v, exp] = x | |
env.find(v)[v] = evaluate(exp, env) | |
else if x[0] == 'define' # define | |
[_, v, exp] = x | |
env[v] = evaluate(exp, env) | |
else if x[0] == 'lambda' # lambda | |
[_, vars, exp] = x | |
(args...) -> | |
evaluate(exp, new Env(vars, args, env)) | |
else if x[0] == 'begin' # begin | |
for exp in x.slice(1) | |
val = evaluate(exp, env) | |
val | |
else | |
exps = (evaluate(exp, env) for exp in x) | |
proc = exps.shift() | |
proc(exps...) | |
parse = (str) -> read_from(tokenize(str)) | |
tokenize = (str) -> | |
str.replace(/([()])/g, ' $1 ').split(' ').filter((c) -> c != '') | |
read_from = (tokens) -> | |
if tokens.length == 0 | |
console.error('invalid token!') | |
process.exit() | |
token = tokens.shift() | |
switch token | |
when '(' | |
L = [] | |
while tokens[0] != ')' | |
L.push(read_from(tokens)) | |
tokens.shift() | |
L | |
when ')' | |
console.error("unexpected ')' !") | |
process.exit() | |
else | |
atom(token) | |
atom = (token) -> if isNaN +token then token else +token | |
to_string = (exp) -> | |
if is_list(exp) | |
'(' + exp.map((e)->to_string(e)).join(' ') + ')' | |
else | |
exp.toString() | |
repl = (line)-> | |
val = evaluate(parse(line)) | |
val if val? | |
exports.repl = repl | |
########################## | |
# Console | |
########################## | |
lines = [] | |
reader = require('readline').createInterface( | |
input: process.stdin, | |
output: process.stdout | |
) | |
reader.on('close', () -> console.log('bye^^')) | |
reader.on('line', (line) -> console.log(repl(line))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment