Created
December 12, 2011 22:51
-
-
Save DuoSRX/1469525 to your computer and use it in GitHub Desktop.
Schemy
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
# 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