Skip to content

Instantly share code, notes, and snippets.

@dahlia
Created August 31, 2011 09:04
Show Gist options
  • Save dahlia/1183129 to your computer and use it in GitHub Desktop.
Save dahlia/1183129 to your computer and use it in GitHub Desktop.
30 miniute Lisp, 2011
# Lisp
class Symbol
def evaluate(env)
env[self]
end
end
[Fixnum, NilClass, TrueClass, FalseClass, String].each do |cls|
cls.class_eval %{
def evaluate(env)
self
end
}
end
class Array
SPECIAL_FORMS = {
:quote => lambda {|env, form| form },
:define => lambda {|env, name, value| env[name] = value.evaluate(env)},
:lambda => lambda do |env, params, body|
lambda do |*args|
new_env = env.clone
for var, arg in params.zip(args)
new_env[var] = arg
end
body.evaluate(new_env)
end
end,
:if => lambda do |env, cond, true_val, false_val|
if cond.evaluate(env)
true_val.evaluate(env)
else
false_val.evaluate(env)
end
end
}
def evaluate(env)
if SPECIAL_FORMS.has_key?(self.first)
macro = SPECIAL_FORMS[self.first]
return macro.call(env, *self[1..-1])
end
func = self.first.evaluate(env)
args = self[1..-1].map {|arg| arg.evaluate(env)}
func.call(*args)
end
end
INITIAL_ENVIRONMENT = {
:eval => lambda {|frm, env| frm.evaluate(env) },
:car => lambda {|l| l.first },
:cdr => lambda {|l| l[1..-1] },
:+ => lambda {|*args| args.inject {|a, b| a + b} },
:- => lambda {|*args| args.inject {|a, b| a - b} },
:* => lambda {|*args| args.inject {|a, b| a * b} },
:/ => lambda {|*args| args.inject {|a, b| a / b} },
:% => lambda {|a, b| a % b },
:map => lambda {|f, l| l.map(&f) },
:display => lambda {|*s| puts(*s) }
}
if __FILE__ == $0
env = INITIAL_ENVIRONMENT.merge({:test => 123, :pi => 3.14})
puts 'running tests...'
raise '123 should be evaluated into 123' unless 123.evaluate(env) == 123
raise ':pi should be evaluated into 3.14' unless :pi.evaluate(env) == 3.14
unless [:+, 1, 2, 3].evaluate(env) == 6
raise '[:+, 1, 2, 3] should be evaluated into 6'
end
[:define, :test_abc, 123].evaluate(env)
unless env.has_key?(:test_abc)
raise '[:define, :test_abc, 123] should define a new variable :test_abc'
end
unless env[:test_abc] == 123
raise '[:define, :test_abc, 123] should set 123 into :test_abc'
end
unless [[:lambda, [:a, :b], [:+, :a, :b]], 1, 2].evaluate(env) == 3
raise 'lambda error'
end
env = INITIAL_ENVIRONMENT.clone
while true
print '> '
puts eval(gets).evaluate(env).inspect
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment