-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiler.rb
77 lines (67 loc) · 1.55 KB
/
compiler.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
require 'dl'
require 'fiddle'
class RubyVM
class InstructionSequence
handle = DL::Handle::DEFAULT
address = handle['rb_iseq_load']
func = Fiddle::Function.new(address, [DL::TYPE_VOIDP] * 3,
DL::TYPE_VOIDP)
define_singleton_method(:load) do |data, parent = nil, opt = nil|
func.call(DL.dlwrap(data), parent, opt).to_value
end
end
end
class Compiler
def compile ast
code = _compile(ast, {}) + [[:leave]]
iseq = [
"YARVInstructionSequence/SimpleDataFormat", 2, 0, 1, {},
"<compiled>", "<compiled>", nil, 1, :top, [], 0, [],
code
]
RubyVM::InstructionSequence.load iseq
end
def _compile ast, args
node_type = ast[0]
case node_type
when :call
fn = ast[1]
arg = ast[2]
_compile(fn, args) + _compile(arg, args) +
[
[:send, :call, 1, nil, 0, 0]
]
when :deref
arg_name = ast[1]
[
[:getdynamic, 2, args.fetch(arg_name)]
]
when :fn
param_name = ast[1]
body = ast[2]
args = Hash[args.merge(param_name => -1).map {|k,v| [k, v+1]}]
[
[:putnil],
[:send, :lambda, 0, [
"YARVInstructionSequence/SimpleDataFormat",
1,
2,
1,
{},
"<compiled>",
"<compiled>",
nil,
1,
:block,
[param_name],
1,
[],
_compile(body, args) + [[:leave]]
], 8, 0]
]
end
end
def eval ast
compile(ast).eval
end
end