Skip to content

Instantly share code, notes, and snippets.

@DuoSRX
Created December 12, 2011 22:51
Show Gist options
  • Save DuoSRX/1469525 to your computer and use it in GitHub Desktop.
Save DuoSRX/1469525 to your computer and use it in GitHub Desktop.
Schemy
# Partial Scheme interpreter
# Heavily inspired by Peter Norvig's Lispy (http://norvig.com/lispy.html)
# TODO: add more datatypes (strings, floats)
# TODO: allow multiple arguments, like (+ 1 2 3 4 5)
class Env < Hash
def initialize(ks = [], vs = [], outer = nil)
@outer = outer
ks.zip(vs).each {|item| store(item[0], item[1])}
end
def [](k)
super(k) || @outer[k]
end
def set(k, v)
key?(k) ? store(k, v) : @outer.set(k, v)
end
end
class Schery
def eval(x, env)
return env[x] if x.is_a? Symbol
return x unless x.is_a? Array
case x[0]
when :quote
x[1]
when :if
eval(eval(x[1], env) ? x[2] : x[3], env)
when :define
env[x[1]] = eval(x[2], env)
when :lambda
->(*args) { eval(x[2], Env.new(x[1], args, env)) }
when :set!
env.set(x[1], eval(x[2], env))
when :begin
x[1..-1].inject(nil) {|val, exp| eval(exp, env)}
else
exps = x.collect {|exp| eval(exp, env)}
exps[0].call(*exps[1..-1])
end
end
def parse(string)
tokens = string.gsub('(', ' ( ').gsub(')', ' ) ').split
read_from(tokens)
end
def read_from(tokens)
raise "Parse error ..." if tokens.length == 0
token = tokens.shift
case token
when '('
l = []
while tokens[0] != ')'
l << read_from(tokens)
end
tokens.shift # remove the closing paren
l
when ')'
raise "Parse error : unmatched parenthesis"
else
atom(token)
end
end
def atom(token)
return token.to_i if token =~ /\d+/
token.to_sym
end
end
def add_globals(env)
[:+, :-, :*, :/, :<, :>, :<=, :>=].each do |op|
env[op] = ->(x,y) { x.send(op, y)}
end
env.update({
car: ->(x) { x.first },
cdr: ->(x) { x[1..-1]},
length: ->(x) { x.length },
append: ->(x,y) { x << y },
})
end
def repl
schery = Schery.new
env = add_globals(Env.new)
input = ""
while true && input != "exit"
print "schery> "
input = STDIN.gets.chomp
break if input == "exit"
begin
code = schery.parse(input)
val = schery.eval(code, env)
puts val
rescue => e
puts "ERROR : Holy cow batman, something wrong happened !"
puts e.message
puts e.backtrace
end
end
puts "bye bye"
end
repl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment