lisp/lisp.rb
@buf = []
@fenv = [] #関数用環境
@genv = [] #大域環境
def getToken
while (x = @buf.shift) == nil
print "> "
l = gets
return nil if !l #eof
while l.gsub!(/[^\010][\010]/,"") do end
if l =~ /^;.*$/ #コメント
print "-"
next
end
l.chomp.scan(/\s+|;.*$|([^()' ]+|.)/){|s,| @buf.push s if s}
end
x
end
def parse0
case r = parse
when "(" then [parse0]
when ")" then []
when "."
result = parse
raise "err" if parse != ")"
result
else [r, parse0]
end
end
def parse
case x = getToken
when "(" then parse0
when "'" then ["quote", [parse,[]]]
when /\A-?\d+\z/ then x.to_i #数字
else x #数字以外は文字列
end
end
def pr(x)
case x
when Integer, String then x.to_s #数字 アトム
when Array #リスト
res = "("
if x != []
res << pr(x[0])
x = x[1]
while x.class == Array && x != [] do
res << " #{pr(x[0])}"
x = x[1]
end
res << " . #{pr(x)}" if x != []
end
res << ")"
end
end
def define(x) #関数定義
@fenv = [[x[1][0], [x[1][1][0], x[1][1][1][0]]]].concat(@fenv)
x[1][0]
end
def findVar(x, env)
env.assoc(x)
end
def evlis(l, env)
if l == [] then [] else [eval(l[0], env), evlis(l[1], env)] end
end
def eval(x, env)
case x
# when NilClass then []
when Integer then x
when String
if v = env.assoc(x) then v[1]
elsif v = @genv.assoc(x) then v[1]
elsif x == "t" then "T"
else raise "Undefined variable: #{x}"
end
when Array
return [] if x == []
case x[0]
when "if"
if eval(x[1][0], env) != []
eval(x[1][1][0], env)
else
eval(x[1][1][1][0], env)
end
when "while"
while eval(x[1][0], env) != [] do
eval(x[1][1][0], env)
end
when "quote" then x[1][0]
when "set"
result = eval(x[1][1][0], env)
if (v = findVar(x[1][0], env)) then v[1] = result
elsif (v = findVar(x[1][0], @genv)) then v[1] = result
else
@genv = [[x[1][0], result]].concat(@genv)
result
end
when "begin"
x = x[1]
while x != [] do
result = eval(x[0], env)
x = x[1]
end
result
else
l = evlis(x[1], env)
case x[0]
when "+" then l[0] + l[1][0]
when "-" then l[0] - l[1][0]
when "*" then l[0] * l[1][0]
when "/" then l[0] / l[1][0]
when "=" then l[0] == l[1][0] ? "T" : []
when ">" then l[0] > l[1][0] ? "T" : []
when "<" then l[0] < l[1][0] ? "T" : []
when "print"
print "#{pr(l[0])}\n"
l[0]
when "car" then l[0][0]
when "cdr" then l[0][1]
when "cons" then [l[0], l[1][0]]
when "eq" then l[0] == l[1][0] ? "T" : []
when "null?" then l[0] == [] ? "T" : []
when "number?" then l[0].kind_of?(Integer) ? "T" : []
when "symbol?" then l[0].class != Array ? "T" : []
when "list?" then l[0].class == Array ? "T" : []
when nil then []
else
if f = @fenv.assoc(x[0])
local_env = []
x = f[1][0]
while x != [] do
local_env = [[x[0], l[0]]].concat(local_env)
x = x[1]
l = l[1]
end
eval(f[1][1], local_env.concat(env))
else
raise "Undefined function: #{x[0]}"
end
end
end
else
raise "class = #{x.class}"
end
end
trap("INT","EXIT")
loop {
begin
print "-"
break if (x = parse) == "quit" || x == nil
if x.class == Array && x[0] == "define"
print "#{define(x)}\n"
else
print "#{pr(eval(x, []))}\n\n"
end
rescue => evar
print "error: ", evar, "\n"
retry
end
}