syntax_tree 5.1.0 → 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 +7 -1
- data/CHANGELOG.md +12 -1
- data/Gemfile.lock +7 -7
- data/Rakefile +7 -0
- data/exe/yarv +63 -0
- data/lib/syntax_tree/node.rb +19 -10
- data/lib/syntax_tree/parser.rb +1 -1
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +7 -7
- data/lib/syntax_tree/yarv/bf.rb +13 -16
- data/lib/syntax_tree/yarv/compiler.rb +24 -13
- data/lib/syntax_tree/yarv/decompiler.rb +9 -0
- data/lib/syntax_tree/yarv/disassembler.rb +3 -2
- data/lib/syntax_tree/yarv/instruction_sequence.rb +190 -86
- data/lib/syntax_tree/yarv/instructions.rb +211 -42
- data/lib/syntax_tree/yarv/legacy.rb +26 -3
- data/lib/syntax_tree/yarv/vm.rb +624 -0
- data/lib/syntax_tree/yarv.rb +0 -269
- data/lib/syntax_tree.rb +1 -0
- metadata +7 -3
data/lib/syntax_tree/yarv.rb
CHANGED
@@ -1,277 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "forwardable"
|
4
|
-
|
5
3
|
module SyntaxTree
|
6
4
|
# This module provides an object representation of the YARV bytecode.
|
7
5
|
module YARV
|
8
|
-
class VM
|
9
|
-
class Jump
|
10
|
-
attr_reader :name
|
11
|
-
|
12
|
-
def initialize(name)
|
13
|
-
@name = name
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class Leave
|
18
|
-
attr_reader :value
|
19
|
-
|
20
|
-
def initialize(value)
|
21
|
-
@value = value
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Frame
|
26
|
-
attr_reader :iseq, :parent, :stack_index, :_self, :nesting, :svars
|
27
|
-
|
28
|
-
def initialize(iseq, parent, stack_index, _self, nesting)
|
29
|
-
@iseq = iseq
|
30
|
-
@parent = parent
|
31
|
-
@stack_index = stack_index
|
32
|
-
@_self = _self
|
33
|
-
@nesting = nesting
|
34
|
-
@svars = {}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class TopFrame < Frame
|
39
|
-
def initialize(iseq)
|
40
|
-
super(iseq, nil, 0, TOPLEVEL_BINDING.eval("self"), [Object])
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class BlockFrame < Frame
|
45
|
-
def initialize(iseq, parent, stack_index)
|
46
|
-
super(iseq, parent, stack_index, parent._self, parent.nesting)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class MethodFrame < Frame
|
51
|
-
attr_reader :name, :block
|
52
|
-
|
53
|
-
def initialize(iseq, parent, stack_index, _self, name, block)
|
54
|
-
super(iseq, parent, stack_index, _self, parent.nesting)
|
55
|
-
@name = name
|
56
|
-
@block = block
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class ClassFrame < Frame
|
61
|
-
def initialize(iseq, parent, stack_index, _self)
|
62
|
-
super(iseq, parent, stack_index, _self, parent.nesting + [_self])
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class FrozenCore
|
67
|
-
define_method("core#hash_merge_kwd") { |left, right| left.merge(right) }
|
68
|
-
|
69
|
-
define_method("core#hash_merge_ptr") do |hash, *values|
|
70
|
-
hash.merge(values.each_slice(2).to_h)
|
71
|
-
end
|
72
|
-
|
73
|
-
define_method("core#set_method_alias") do |clazz, new_name, old_name|
|
74
|
-
clazz.alias_method(new_name, old_name)
|
75
|
-
end
|
76
|
-
|
77
|
-
define_method("core#set_variable_alias") do |new_name, old_name|
|
78
|
-
# Using eval here since there isn't a reflection API to be able to
|
79
|
-
# alias global variables.
|
80
|
-
eval("alias #{new_name} #{old_name}", binding, __FILE__, __LINE__)
|
81
|
-
end
|
82
|
-
|
83
|
-
define_method("core#set_postexe") { |&block| END { block.call } }
|
84
|
-
|
85
|
-
define_method("core#undef_method") do |clazz, name|
|
86
|
-
clazz.undef_method(name)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
FROZEN_CORE = FrozenCore.new.freeze
|
91
|
-
|
92
|
-
extend Forwardable
|
93
|
-
|
94
|
-
attr_reader :stack
|
95
|
-
def_delegators :stack, :push, :pop
|
96
|
-
|
97
|
-
attr_reader :frame
|
98
|
-
def_delegators :frame, :_self
|
99
|
-
|
100
|
-
def initialize
|
101
|
-
@stack = []
|
102
|
-
@frame = nil
|
103
|
-
end
|
104
|
-
|
105
|
-
##########################################################################
|
106
|
-
# Helper methods for frames
|
107
|
-
##########################################################################
|
108
|
-
|
109
|
-
def run_frame(frame)
|
110
|
-
# First, set the current frame to the given value.
|
111
|
-
@frame = frame
|
112
|
-
|
113
|
-
# Next, set up the local table for the frame. This is actually incorrect
|
114
|
-
# as it could use the values already on the stack, but for now we're
|
115
|
-
# just doing this for simplicity.
|
116
|
-
frame.iseq.local_table.size.times { push(nil) }
|
117
|
-
|
118
|
-
# Yield so that some frame-specific setup can be done.
|
119
|
-
yield if block_given?
|
120
|
-
|
121
|
-
# This hash is going to hold a mapping of label names to their
|
122
|
-
# respective indices in our instruction list.
|
123
|
-
labels = {}
|
124
|
-
|
125
|
-
# This array is going to hold our instructions.
|
126
|
-
insns = []
|
127
|
-
|
128
|
-
# Here we're going to preprocess the instruction list from the
|
129
|
-
# instruction sequence to set up the labels hash and the insns array.
|
130
|
-
frame.iseq.insns.each do |insn|
|
131
|
-
case insn
|
132
|
-
when Integer, Symbol
|
133
|
-
# skip
|
134
|
-
when InstructionSequence::Label
|
135
|
-
labels[insn.name] = insns.length
|
136
|
-
else
|
137
|
-
insns << insn
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Finally we can execute the instructions one at a time. If they return
|
142
|
-
# jumps or leaves we will handle those appropriately.
|
143
|
-
pc = 0
|
144
|
-
while pc < insns.length
|
145
|
-
insn = insns[pc]
|
146
|
-
pc += 1
|
147
|
-
|
148
|
-
case (result = insn.call(self))
|
149
|
-
when Jump
|
150
|
-
pc = labels[result.name]
|
151
|
-
when Leave
|
152
|
-
return result.value
|
153
|
-
end
|
154
|
-
end
|
155
|
-
ensure
|
156
|
-
@stack = stack[0...frame.stack_index]
|
157
|
-
@frame = frame.parent
|
158
|
-
end
|
159
|
-
|
160
|
-
def run_top_frame(iseq)
|
161
|
-
run_frame(TopFrame.new(iseq))
|
162
|
-
end
|
163
|
-
|
164
|
-
def run_block_frame(iseq, *args, &block)
|
165
|
-
run_frame(BlockFrame.new(iseq, frame, stack.length)) do
|
166
|
-
locals = [*args, block]
|
167
|
-
iseq.local_table.size.times do |index|
|
168
|
-
local_set(index, 0, locals.shift)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
def run_class_frame(iseq, clazz)
|
174
|
-
run_frame(ClassFrame.new(iseq, frame, stack.length, clazz))
|
175
|
-
end
|
176
|
-
|
177
|
-
def run_method_frame(name, iseq, _self, *args, **kwargs, &block)
|
178
|
-
run_frame(
|
179
|
-
MethodFrame.new(iseq, frame, stack.length, _self, name, block)
|
180
|
-
) do
|
181
|
-
locals = [*args, block]
|
182
|
-
|
183
|
-
if iseq.argument_options[:keyword]
|
184
|
-
# First, set up the keyword bits array.
|
185
|
-
keyword_bits =
|
186
|
-
iseq.argument_options[:keyword].map do |config|
|
187
|
-
kwargs.key?(config.is_a?(Array) ? config[0] : config)
|
188
|
-
end
|
189
|
-
|
190
|
-
iseq.local_table.locals.each_with_index do |local, index|
|
191
|
-
# If this is the keyword bits local, then set it appropriately.
|
192
|
-
if local.name == 2
|
193
|
-
locals.insert(index, keyword_bits)
|
194
|
-
next
|
195
|
-
end
|
196
|
-
|
197
|
-
# First, find the configuration for this local in the keywords
|
198
|
-
# list if it exists.
|
199
|
-
name = local.name
|
200
|
-
config =
|
201
|
-
iseq.argument_options[:keyword].find do |keyword|
|
202
|
-
keyword.is_a?(Array) ? keyword[0] == name : keyword == name
|
203
|
-
end
|
204
|
-
|
205
|
-
# If the configuration doesn't exist, then the local is not a
|
206
|
-
# keyword local.
|
207
|
-
next unless config
|
208
|
-
|
209
|
-
if !config.is_a?(Array)
|
210
|
-
# required keyword
|
211
|
-
locals.insert(index, kwargs.fetch(name))
|
212
|
-
elsif !config[1].nil?
|
213
|
-
# optional keyword with embedded default value
|
214
|
-
locals.insert(index, kwargs.fetch(name, config[1]))
|
215
|
-
else
|
216
|
-
# optional keyword with expression default value
|
217
|
-
locals.insert(index, nil)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
iseq.local_table.size.times do |index|
|
223
|
-
local_set(index, 0, locals.shift)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
##########################################################################
|
229
|
-
# Helper methods for instructions
|
230
|
-
##########################################################################
|
231
|
-
|
232
|
-
def const_base
|
233
|
-
frame.nesting.last
|
234
|
-
end
|
235
|
-
|
236
|
-
def frame_at(level)
|
237
|
-
current = frame
|
238
|
-
level.times { current = current.parent }
|
239
|
-
current
|
240
|
-
end
|
241
|
-
|
242
|
-
def frame_svar
|
243
|
-
current = frame
|
244
|
-
current = current.parent while current.is_a?(BlockFrame)
|
245
|
-
current
|
246
|
-
end
|
247
|
-
|
248
|
-
def frame_yield
|
249
|
-
current = frame
|
250
|
-
current = current.parent until current.is_a?(MethodFrame)
|
251
|
-
current
|
252
|
-
end
|
253
|
-
|
254
|
-
def frozen_core
|
255
|
-
FROZEN_CORE
|
256
|
-
end
|
257
|
-
|
258
|
-
def jump(label)
|
259
|
-
Jump.new(label.name)
|
260
|
-
end
|
261
|
-
|
262
|
-
def leave
|
263
|
-
Leave.new(pop)
|
264
|
-
end
|
265
|
-
|
266
|
-
def local_get(index, level)
|
267
|
-
stack[frame_at(level).stack_index + index]
|
268
|
-
end
|
269
|
-
|
270
|
-
def local_set(index, level, value)
|
271
|
-
stack[frame_at(level).stack_index + index] = value
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
6
|
# Compile the given source into a YARV instruction sequence.
|
276
7
|
def self.compile(source, options = Compiler::Options.new)
|
277
8
|
SyntaxTree.parse(source).accept(Compiler.new(options))
|
data/lib/syntax_tree.rb
CHANGED
@@ -37,6 +37,7 @@ require_relative "syntax_tree/yarv/instructions"
|
|
37
37
|
require_relative "syntax_tree/yarv/legacy"
|
38
38
|
require_relative "syntax_tree/yarv/local_table"
|
39
39
|
require_relative "syntax_tree/yarv/assembler"
|
40
|
+
require_relative "syntax_tree/yarv/vm"
|
40
41
|
|
41
42
|
# Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
|
42
43
|
# provides the ability to generate a syntax tree from source, as well as the
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syntax_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prettier_print
|
@@ -99,6 +99,7 @@ email:
|
|
99
99
|
- kddnewton@gmail.com
|
100
100
|
executables:
|
101
101
|
- stree
|
102
|
+
- yarv
|
102
103
|
extensions: []
|
103
104
|
extra_rdoc_files: []
|
104
105
|
files:
|
@@ -108,6 +109,7 @@ files:
|
|
108
109
|
- ".github/workflows/gh-pages.yml"
|
109
110
|
- ".github/workflows/main.yml"
|
110
111
|
- ".gitignore"
|
112
|
+
- ".gitmodules"
|
111
113
|
- ".rubocop.yml"
|
112
114
|
- CHANGELOG.md
|
113
115
|
- CODE_OF_CONDUCT.md
|
@@ -122,6 +124,7 @@ files:
|
|
122
124
|
- config/rubocop.yml
|
123
125
|
- doc/logo.svg
|
124
126
|
- exe/stree
|
127
|
+
- exe/yarv
|
125
128
|
- lib/syntax_tree.rb
|
126
129
|
- lib/syntax_tree/basic_visitor.rb
|
127
130
|
- lib/syntax_tree/cli.rb
|
@@ -158,6 +161,7 @@ files:
|
|
158
161
|
- lib/syntax_tree/yarv/instructions.rb
|
159
162
|
- lib/syntax_tree/yarv/legacy.rb
|
160
163
|
- lib/syntax_tree/yarv/local_table.rb
|
164
|
+
- lib/syntax_tree/yarv/vm.rb
|
161
165
|
- syntax_tree.gemspec
|
162
166
|
homepage: https://github.com/kddnewton/syntax_tree
|
163
167
|
licenses:
|
@@ -179,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
183
|
- !ruby/object:Gem::Version
|
180
184
|
version: '0'
|
181
185
|
requirements: []
|
182
|
-
rubygems_version: 3.
|
186
|
+
rubygems_version: 3.4.1
|
183
187
|
signing_key:
|
184
188
|
specification_version: 4
|
185
189
|
summary: A parser based on ripper
|