Skip to content

Instantly share code, notes, and snippets.

@jesg
Last active August 29, 2015 13:58
Show Gist options
  • Select an option

  • Save jesg/10146611 to your computer and use it in GitHub Desktop.

Select an option

Save jesg/10146611 to your computer and use it in GitHub Desktop.
module Lisp
def self.lexer(str)
tokens = Array.new
str
.gsub(/\(/, ' ( ')
.gsub(/\)/, ' ) ')
.split(/\s/)
.each do |token|
next if token.empty?
tokens << case
when token =~ /^\d+$/
token.to_i
when token =~ /^\d+\.\d+$/
token.to_f
else
token
end
end
tokens
end
def self.parse(tokens)
first = tokens.shift
if first =~ /\(/
ast = Array.new
until tokens.first =~ /\)/
ast << parse(tokens)
end
tokens.shift
return ast
else
return first
end
end
class Env < Hash
def initialize(params=[], args=[], outer=nil)
merge params.zip(args).to_h
@outer = outer
end
def find(var)
key?(var) ? self : @outer.find(var)
end
end
def self.eval(x, env)
if x.instance_of? String
return env.find(x)[x]
elsif not x.instance_of? Array
return x
elsif x.empty?
return x
elsif 'quote' == x.first
(_, exp) = x
return exp
elsif 'if' == x.first
(_, test, conseq, alt) = x
return Lisp.eval((Lisp.eval(test, env) ? conseq : alt), env)
elsif 'set!' == x.first
(_, var, exp) = x
env.find(var)[var] = Lisp.eval(exp, env)
elsif 'define' == x.first
(_, var, exp) = x
env[var] = Lisp.eval(exp, env)
elsif 'lambda' == x.first # (lambda (arg1 arg2...) exp1 exp2...)
(_, vars) = x
return proc do |args|
result = []
local_env = Lisp.Env.new(vars, args, env)
x.slice(2..-1).each do |exp|
result = Lisp.eval(exp, local_env)
end
result
end
else
exps = x.collect { |exp| Lisp.eval(exp, env) }
proc = exps.shift
return proc.call(*exps)
end
end
def self.repl(env, cursor='> ')
loop do
print cursor
result = Lisp.eval(Lisp.parse(Lisp.lexer(gets())), env)
puts(result.to_s)
end
end
end
global_env = Lisp::Env.new
global_env['car'] = proc {|x| x.last}
global_env['cdr'] = proc {|x| x.slice(0..-2)}
global_env['cons'] = proc {|head, tail| tail.push(head) }
global_env['eq'] = proc {|x, y| x == y }
['+', '-', '*', '/', '%'].each do |msg|
global_env[msg] = proc {|x, *y| x.send msg, *y}
end
Lisp.repl(global_env)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment