From: Lui Kore on
# Just added another example: a Scheme interpreter.

require "rsec"

class Scheme
include Rsec::Helpers

def initialize
bra = /\(\s*/.r.skip
ket = /\s*\)/.r.skip
boolean = /\#[tf]/. r.map {|n| ValueNode[n=='#t'] }
integer = /0|[1-9]\d*/.r.map {|n| ValueNode[n.to_i] }
id = /[^\s\(\)\[\]]+/.r.on {|n|
def n.eval bind, *xs
bind[self]
end
}
atom = boolean | integer | id
list = nil # declare for lazy
cell = atom | lazy{list}
list = ( bra < cell.join_(spacee) < ket ).map {|(n)|
ListNode[*n] }
@parser = (spacee < cell.join_(spacee) < spacee).map {|(n)|
ListNode[*n] }
end

def run source
@ctx = Rsec::ParseContext.new source, 'scm'
res = @parser._parse @ctx
if !res or !@ctx.eos?
raise Rsec::ParseError['syntax error', @ctx]
end
res.eval Runtime.new
end

ValueNode = Struct.new :val
class ValueNode
def eval *xs
val
end
def pretty_print q
q.text "<#{val}>"
end
end

class ListNode < Array
def eval bind
head, *tail = self
case head
when String
pr = bind[head]
pr.is_a?(Proc) ? pr[bind, tail] : pr
when ListNode
map{|n| n.eval bind }.last
end
end
end

class Bind < Hash
def initialize parent = {}
@parent = parent
end

def [] key
super(key) || @parent[key]
end

# define a function
def define id, &p
self[id] = proc do |bind, xs|
p[* xs.map{|x| x.eval bind }]
end
end
end

class Runtime < Bind
def initialize
super()

self['define'] = proc do |bind, (param, body)|
case param
# (define (name plist[0]) body)
when ListNode
func, *xs = param
self[func] = self['lambda'][bind, [xs, body]]
# (define param body)
when String
self[param] = body.eval bind
end
end

# declare:
# (lambda (xs[0] xs[1]) body)
self['lambda'] = proc do |bind_def, (xs, body)|
xs = [xs] if xs.is_a?(String)
new_bind = Bind.new bind_def
# calling:
# (some vs[0] vs[1])
proc do |bind_call, vs|
vs = vs.map{|v| v.eval bind_call}
new_bind.merge! Hash[xs.zip vs]
body.eval new_bind
end
end

# lazy (short cut)
self['if'] = proc do |bind, (p, left, right)|
p.eval(bind) ? left.eval(bind) : right.eval(bind)
end

# misc
%w|+ - * / ** % > <|.each do |s|
define s, &s.to_sym
end
define '=', &:==
define 'display' do |x|
puts x
end
end
end
end

s = Scheme.new
s.run File.read ARGV[0]

--
Posted via http://www.ruby-forum.com/.

 | 
Pages: 1
Prev: deploy issues
Next: Dia 1.1 released!