syntax_tree 5.0.1 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +4 -0
- data/.github/workflows/auto-merge.yml +1 -1
- data/.github/workflows/main.yml +5 -2
- data/.gitmodules +6 -0
- data/.rubocop.yml +58 -1
- data/CHANGELOG.md +28 -1
- data/Gemfile.lock +12 -12
- data/README.md +5 -5
- data/Rakefile +7 -0
- data/exe/yarv +63 -0
- data/lib/syntax_tree/dsl.rb +1004 -0
- data/lib/syntax_tree/formatter.rb +2 -2
- data/lib/syntax_tree/language_server.rb +2 -0
- data/lib/syntax_tree/node.rb +26 -17
- data/lib/syntax_tree/parser.rb +21 -22
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +459 -0
- data/lib/syntax_tree/yarv/bf.rb +176 -0
- data/lib/syntax_tree/yarv/compiler.rb +2298 -0
- data/lib/syntax_tree/yarv/decompiler.rb +263 -0
- data/lib/syntax_tree/yarv/disassembler.rb +212 -0
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1275 -0
- data/lib/syntax_tree/yarv/instructions.rb +5372 -0
- data/lib/syntax_tree/yarv/legacy.rb +215 -0
- data/lib/syntax_tree/yarv/local_table.rb +89 -0
- data/lib/syntax_tree/yarv/vm.rb +624 -0
- data/lib/syntax_tree/yarv.rb +18 -0
- data/lib/syntax_tree.rb +20 -1
- data/syntax_tree.gemspec +1 -1
- metadata +20 -5
@@ -0,0 +1,215 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module YARV
|
5
|
+
# This module contains the instructions that used to be a part of YARV but
|
6
|
+
# have been replaced or removed in more recent versions.
|
7
|
+
module Legacy
|
8
|
+
# ### Summary
|
9
|
+
#
|
10
|
+
# `getclassvariable` looks for a class variable in the current class and
|
11
|
+
# pushes its value onto the stack.
|
12
|
+
#
|
13
|
+
# This version of the `getclassvariable` instruction is no longer used
|
14
|
+
# since in Ruby 3.0 it gained an inline cache.`
|
15
|
+
#
|
16
|
+
# ### Usage
|
17
|
+
#
|
18
|
+
# ~~~ruby
|
19
|
+
# @@class_variable
|
20
|
+
# ~~~
|
21
|
+
#
|
22
|
+
class GetClassVariable
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
def initialize(name)
|
26
|
+
@name = name
|
27
|
+
end
|
28
|
+
|
29
|
+
def disasm(fmt)
|
30
|
+
fmt.instruction("getclassvariable", [fmt.object(name)])
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_a(_iseq)
|
34
|
+
[:getclassvariable, name]
|
35
|
+
end
|
36
|
+
|
37
|
+
def length
|
38
|
+
2
|
39
|
+
end
|
40
|
+
|
41
|
+
def pops
|
42
|
+
0
|
43
|
+
end
|
44
|
+
|
45
|
+
def pushes
|
46
|
+
1
|
47
|
+
end
|
48
|
+
|
49
|
+
def canonical
|
50
|
+
YARV::GetClassVariable.new(name, nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
def call(vm)
|
54
|
+
canonical.call(vm)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# ### Summary
|
59
|
+
#
|
60
|
+
# `opt_getinlinecache` is a wrapper around a series of `putobject` and
|
61
|
+
# `getconstant` instructions that allows skipping past them if the inline
|
62
|
+
# cache is currently set. It pushes the value of the cache onto the stack
|
63
|
+
# if it is set, otherwise it pushes `nil`.
|
64
|
+
#
|
65
|
+
# This instruction is no longer used since in Ruby 3.2 it was replaced by
|
66
|
+
# the consolidated `opt_getconstant_path` instruction.
|
67
|
+
#
|
68
|
+
# ### Usage
|
69
|
+
#
|
70
|
+
# ~~~ruby
|
71
|
+
# Constant
|
72
|
+
# ~~~
|
73
|
+
#
|
74
|
+
class OptGetInlineCache
|
75
|
+
attr_reader :label, :cache
|
76
|
+
|
77
|
+
def initialize(label, cache)
|
78
|
+
@label = label
|
79
|
+
@cache = cache
|
80
|
+
end
|
81
|
+
|
82
|
+
def disasm(fmt)
|
83
|
+
fmt.instruction(
|
84
|
+
"opt_getinlinecache",
|
85
|
+
[fmt.label(label), fmt.inline_storage(cache)]
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_a(_iseq)
|
90
|
+
[:opt_getinlinecache, label.name, cache]
|
91
|
+
end
|
92
|
+
|
93
|
+
def length
|
94
|
+
3
|
95
|
+
end
|
96
|
+
|
97
|
+
def pops
|
98
|
+
0
|
99
|
+
end
|
100
|
+
|
101
|
+
def pushes
|
102
|
+
1
|
103
|
+
end
|
104
|
+
|
105
|
+
def canonical
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def call(vm)
|
110
|
+
vm.push(nil)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# ### Summary
|
115
|
+
#
|
116
|
+
# `opt_setinlinecache` sets an inline cache for a constant lookup. It pops
|
117
|
+
# the value it should set off the top of the stack. It uses this value to
|
118
|
+
# set the cache. It then pushes that value back onto the top of the stack.
|
119
|
+
#
|
120
|
+
# This instruction is no longer used since in Ruby 3.2 it was replaced by
|
121
|
+
# the consolidated `opt_getconstant_path` instruction.
|
122
|
+
#
|
123
|
+
# ### Usage
|
124
|
+
#
|
125
|
+
# ~~~ruby
|
126
|
+
# Constant
|
127
|
+
# ~~~
|
128
|
+
#
|
129
|
+
class OptSetInlineCache
|
130
|
+
attr_reader :cache
|
131
|
+
|
132
|
+
def initialize(cache)
|
133
|
+
@cache = cache
|
134
|
+
end
|
135
|
+
|
136
|
+
def disasm(fmt)
|
137
|
+
fmt.instruction("opt_setinlinecache", [fmt.inline_storage(cache)])
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_a(_iseq)
|
141
|
+
[:opt_setinlinecache, cache]
|
142
|
+
end
|
143
|
+
|
144
|
+
def length
|
145
|
+
2
|
146
|
+
end
|
147
|
+
|
148
|
+
def pops
|
149
|
+
1
|
150
|
+
end
|
151
|
+
|
152
|
+
def pushes
|
153
|
+
1
|
154
|
+
end
|
155
|
+
|
156
|
+
def canonical
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
def call(vm)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# ### Summary
|
165
|
+
#
|
166
|
+
# `setclassvariable` looks for a class variable in the current class and
|
167
|
+
# sets its value to the value it pops off the top of the stack.
|
168
|
+
#
|
169
|
+
# This version of the `setclassvariable` instruction is no longer used
|
170
|
+
# since in Ruby 3.0 it gained an inline cache.
|
171
|
+
#
|
172
|
+
# ### Usage
|
173
|
+
#
|
174
|
+
# ~~~ruby
|
175
|
+
# @@class_variable = 1
|
176
|
+
# ~~~
|
177
|
+
#
|
178
|
+
class SetClassVariable
|
179
|
+
attr_reader :name
|
180
|
+
|
181
|
+
def initialize(name)
|
182
|
+
@name = name
|
183
|
+
end
|
184
|
+
|
185
|
+
def disasm(fmt)
|
186
|
+
fmt.instruction("setclassvariable", [fmt.object(name)])
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_a(_iseq)
|
190
|
+
[:setclassvariable, name]
|
191
|
+
end
|
192
|
+
|
193
|
+
def length
|
194
|
+
2
|
195
|
+
end
|
196
|
+
|
197
|
+
def pops
|
198
|
+
1
|
199
|
+
end
|
200
|
+
|
201
|
+
def pushes
|
202
|
+
0
|
203
|
+
end
|
204
|
+
|
205
|
+
def canonical
|
206
|
+
YARV::SetClassVariable.new(name, nil)
|
207
|
+
end
|
208
|
+
|
209
|
+
def call(vm)
|
210
|
+
canonical.call(vm)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module YARV
|
5
|
+
# This represents every local variable associated with an instruction
|
6
|
+
# sequence. There are two kinds of locals: plain locals that are what you
|
7
|
+
# expect, and block proxy locals, which represent local variables
|
8
|
+
# associated with blocks that were passed into the current instruction
|
9
|
+
# sequence.
|
10
|
+
class LocalTable
|
11
|
+
# A local representing a block passed into the current instruction
|
12
|
+
# sequence.
|
13
|
+
class BlockLocal
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
def initialize(name)
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# A regular local variable.
|
22
|
+
class PlainLocal
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
def initialize(name)
|
26
|
+
@name = name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# The result of looking up a local variable in the current local table.
|
31
|
+
class Lookup
|
32
|
+
attr_reader :local, :index, :level
|
33
|
+
|
34
|
+
def initialize(local, index, level)
|
35
|
+
@local = local
|
36
|
+
@index = index
|
37
|
+
@level = level
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :locals
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@locals = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def empty?
|
48
|
+
locals.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def find(name, level = 0)
|
52
|
+
index = locals.index { |local| local.name == name }
|
53
|
+
Lookup.new(locals[index], index, level) if index
|
54
|
+
end
|
55
|
+
|
56
|
+
def has?(name)
|
57
|
+
locals.any? { |local| local.name == name }
|
58
|
+
end
|
59
|
+
|
60
|
+
def names
|
61
|
+
locals.map(&:name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def name_at(index)
|
65
|
+
locals[index].name
|
66
|
+
end
|
67
|
+
|
68
|
+
def size
|
69
|
+
locals.length
|
70
|
+
end
|
71
|
+
|
72
|
+
# Add a BlockLocal to the local table.
|
73
|
+
def block(name)
|
74
|
+
locals << BlockLocal.new(name) unless has?(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add a PlainLocal to the local table.
|
78
|
+
def plain(name)
|
79
|
+
locals << PlainLocal.new(name) unless has?(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
# This is the offset from the top of the stack where this local variable
|
83
|
+
# lives.
|
84
|
+
def offset(index)
|
85
|
+
size - (index - 3) - 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|