-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterpreter.rb
139 lines (119 loc) · 3.53 KB
/
interpreter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# Simple lambda calculus interpreter to evaluate arithmetic expressions
# in the form of abstract syntax trees. Has support for anonymous
# functions, closures, and let expressions.
#
# Implemented in functional way, using multi_dispatch gem.
require 'sxp'
require 'multi_dispatch'
# helpers
module Camelizer
def camelize
return self.to_s if self !~ /_/ && self =~ /[A-Z]+.*/
to_s.split('_').map { |e| e.capitalize }.join
end
end
Symbol.send(:include, Camelizer)
# Lambda Calculus Interpreter
class LCI
include MultiDispatch
attr_accessor :env
def initialize
@env = {}
@lang = {
Numeric => lambda { |e| e },
:true => lambda { |e| true },
:false => lambda { |e| false },
:+ => lambda { |e| e.shift
Plus.new(parse(e.first), parse(e.last))
},
Symbol => lambda { |e|
Var.new(e)
},
:- => lambda { |e| e.shift
Minus.new(parse(e.first), parse(e.last))
},
:lambda => lambda { |e| e.shift
Func.new(e.first, parse(e.last))
},
:call => lambda { |e| e.shift
Call.new(parse(e.first), parse(e.last))
},
:if => lambda { |e| e.shift
If.new(parse(e.shift), parse(e.first), parse(e.last))
},
:let => lambda { |e| e.shift
LetDirect.new(e.shift, parse(e.first), parse(e.last))
},
:letf => lambda { |e| e.shift
LetByFunc.new(e.shift, parse(e.first), parse(e.last))
}
}
class << @lang
def [](key)
return super(Numeric) if key.is_a? Numeric
return super(Symbol) if key.is_a?(Symbol) && !self.values_at(key).first
super(key)
end
end
end
def self.def_expr(name, *args)
klass = Object.const_set(name.camelize, Class.new)
klass.class_eval do
attr_accessor *args
define_method :initialize do |*values|
args.zip(values).each do |var, value|
instance_variable_set("@#{var}", value)
end
end
end
end
def_expr(:var, :name)
def_expr(:plus, :left, :right)
def_expr(:minus, :left, :right)
def_expr(:func, :var_name, :body)
def_expr(:closure, :var_name, :body)
def_expr(:call, :func, :param)
def_expr(:let_direct, :var, :expr, :body)
def_expr(:let_by_func, :var, :expr, :body)
def_expr(:if, :cond, :if_true, :if_false)
def_multi :evaluate, Numeric do |num| ; num end
def_multi :evaluate, true do ; true end
def_multi :evaluate, false do ; false end
def_multi :evaluate, Plus do |plus|
evaluate(plus.left) + evaluate(plus.right)
end
def_multi :evaluate, Minus do |minus|
evaluate(minus.left) - evaluate(minus.right)
end
def_multi :evaluate, Var do |var|
@env[var.name]
end
def_multi :evaluate, Func do |func|
Closure.new(func.var_name, func.body)
end
def_multi :evaluate, Call do |call|
closure = evaluate(call.func)
@env[closure.var_name] = evaluate(call.param)
evaluate(closure.body)
end
def_multi :evaluate, If do |stat|
evaluate(stat.cond) ? evaluate(stat.if_true) : evaluate(stat.if_false)
end
def_multi :evaluate, LetDirect do |let_dir|
@env[let_dir.var] = evaluate(let_dir.expr)
ret = evaluate(let_dir.body) ; @env.delete(let_dir.var) ; ret
end
def_multi :evaluate, LetByFunc do |let_func|
evaluate(Call.new(Func.new(let_func.var, let_func.body), let_func.expr))
end
def parse(expr)
p expr
# if s-expr
if expr.is_a? Array
@lang[expr.first].call(expr)
else # if atom
@lang[expr].call(expr)
end
end
class << self ; undef :def_expr end
end