Skip to content

Instantly share code, notes, and snippets.

@mattsan
Created January 6, 2011 14:04
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 mattsan/767917 to your computer and use it in GitHub Desktop.
Save mattsan/767917 to your computer and use it in GitHub Desktop.
lisp by ruby
Global_env =
{
'+' => :+,
'-' => :-,
'*' => :*,
'/' => :/,
'not' => :not,
'>' => :>,
'<' => :<,
'>=' => :>=,
'<=' => :<=,
'=' => :==,
'equal?' => :==,
'eq?' => :instance_of?,
'length' => :length,
'cons' => Proc.new { |x, xs| [x] + xs },
'car' => Proc.new { |xs| xs[0] },
'cdr' => Proc.new { |xs| xs[1..-1] },
'append' => :+,
'list' => Proc.new { |*xs| xs },
'list?' => Proc.new { |xs| xs.instance_of? Array },
'null?' => :empty?,
'symbol?' => Proc.new { |x| x.is_a? String }
}
def eval(x, env = Global_env)
if x.instance_of? String
env[x]
elsif not x.instance_of? Array
x
elsif x[0] == 'quote'
_, exp = x
exp
elsif x[0] == 'if'
_, test, conseq, alt = x
eval(if eval(test, env) then conseq else alt end, env)
elsif x[0] == 'set!'
_, var, exp = x
raise "undefined variable" unless env.key?(var)
env[var] = eval(exp, env)
elsif x[0] == 'define'
_, var, exp = x
env[var] = eval(exp, env)
elsif x[0] == 'lambda'
_, vars, exp = x
Proc.new { |*args| eval(exp, Hash[vars.zip(args)].merge(env)) }
elsif x[0] == 'begin'
result = nil
x[1..-1].each { |exp| result = eval(exp, env) }
result
else
exps = x.map{ |exp| eval(exp, env) }
fn = exps.shift
if fn.instance_of? Proc
fn.call(*exps)
else
op1 = exps.shift
op1.method(fn).call(*exps)
end
end
end
def read(s)
read_from tokenize(s)
end
def tokenize(s)
s.gsub('(', ' ( ').gsub(')', ' ) ').split
end
def read_from(tokens)
raise 'unexpected EOF while reading' if tokens.empty?
token = tokens.shift
if token == '('
l = []
while tokens[0] != ')'
l.push read_from(tokens)
end
tokens.shift
l
elsif token == ')'
raise 'unexpected'
else
atom(token)
end
end
def atom(token)
begin
Integer(token)
rescue
begin
Float(token)
rescue
token
end
end
end
class Array
def to_s
'(' + join(' ') + ')'
end
end
def repl(prompt = 'lis.rb> ')
while true
print prompt
val = read(STDIN.gets)
result = eval(val)
print "#{result}\n"
end
end
repl if __FILE__ == $0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment