syntax_tree 5.1.0 → 5.3.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/gh-pages.yml +1 -1
- data/.github/workflows/main.yml +5 -2
- data/.gitmodules +9 -0
- data/.rubocop.yml +11 -1
- data/CHANGELOG.md +29 -1
- data/Gemfile.lock +10 -10
- data/README.md +1 -0
- data/Rakefile +7 -0
- data/exe/yarv +63 -0
- data/lib/syntax_tree/cli.rb +3 -2
- data/lib/syntax_tree/formatter.rb +23 -2
- data/lib/syntax_tree/index.rb +374 -0
- data/lib/syntax_tree/node.rb +146 -107
- data/lib/syntax_tree/parser.rb +20 -3
- data/lib/syntax_tree/plugin/disable_ternary.rb +7 -0
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +17 -13
- data/lib/syntax_tree/yarv/bf.rb +13 -16
- data/lib/syntax_tree/yarv/compiler.rb +25 -14
- data/lib/syntax_tree/yarv/decompiler.rb +10 -1
- data/lib/syntax_tree/yarv/disassembler.rb +3 -2
- data/lib/syntax_tree/yarv/instruction_sequence.rb +191 -87
- data/lib/syntax_tree/yarv/instructions.rb +1011 -42
- data/lib/syntax_tree/yarv/legacy.rb +59 -3
- data/lib/syntax_tree/yarv/vm.rb +628 -0
- data/lib/syntax_tree/yarv.rb +0 -269
- data/lib/syntax_tree.rb +16 -0
- metadata +9 -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
@@ -26,6 +26,7 @@ require_relative "syntax_tree/visitor/with_environment"
|
|
26
26
|
require_relative "syntax_tree/parser"
|
27
27
|
require_relative "syntax_tree/pattern"
|
28
28
|
require_relative "syntax_tree/search"
|
29
|
+
require_relative "syntax_tree/index"
|
29
30
|
|
30
31
|
require_relative "syntax_tree/yarv"
|
31
32
|
require_relative "syntax_tree/yarv/bf"
|
@@ -37,6 +38,7 @@ require_relative "syntax_tree/yarv/instructions"
|
|
37
38
|
require_relative "syntax_tree/yarv/legacy"
|
38
39
|
require_relative "syntax_tree/yarv/local_table"
|
39
40
|
require_relative "syntax_tree/yarv/assembler"
|
41
|
+
require_relative "syntax_tree/yarv/vm"
|
40
42
|
|
41
43
|
# Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
|
42
44
|
# provides the ability to generate a syntax tree from source, as well as the
|
@@ -115,4 +117,18 @@ module SyntaxTree
|
|
115
117
|
def self.search(source, query, &block)
|
116
118
|
Search.new(Pattern.new(query).compile).scan(parse(source), &block)
|
117
119
|
end
|
120
|
+
|
121
|
+
# Indexes the given source code to return a list of all class, module, and
|
122
|
+
# method definitions. Used to quickly provide indexing capability for IDEs or
|
123
|
+
# documentation generation.
|
124
|
+
def self.index(source)
|
125
|
+
Index.index(source)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Indexes the given file to return a list of all class, module, and method
|
129
|
+
# definitions. Used to quickly provide indexing capability for IDEs or
|
130
|
+
# documentation generation.
|
131
|
+
def self.index_file(filepath)
|
132
|
+
Index.index_file(filepath)
|
133
|
+
end
|
118
134
|
end
|
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.3.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-26 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,16 +124,19 @@ 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
|
128
131
|
- lib/syntax_tree/dsl.rb
|
129
132
|
- lib/syntax_tree/formatter.rb
|
133
|
+
- lib/syntax_tree/index.rb
|
130
134
|
- lib/syntax_tree/language_server.rb
|
131
135
|
- lib/syntax_tree/language_server/inlay_hints.rb
|
132
136
|
- lib/syntax_tree/node.rb
|
133
137
|
- lib/syntax_tree/parser.rb
|
134
138
|
- lib/syntax_tree/pattern.rb
|
139
|
+
- lib/syntax_tree/plugin/disable_ternary.rb
|
135
140
|
- lib/syntax_tree/plugin/single_quotes.rb
|
136
141
|
- lib/syntax_tree/plugin/trailing_comma.rb
|
137
142
|
- lib/syntax_tree/rake/check_task.rb
|
@@ -158,6 +163,7 @@ files:
|
|
158
163
|
- lib/syntax_tree/yarv/instructions.rb
|
159
164
|
- lib/syntax_tree/yarv/legacy.rb
|
160
165
|
- lib/syntax_tree/yarv/local_table.rb
|
166
|
+
- lib/syntax_tree/yarv/vm.rb
|
161
167
|
- syntax_tree.gemspec
|
162
168
|
homepage: https://github.com/kddnewton/syntax_tree
|
163
169
|
licenses:
|
@@ -179,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
185
|
- !ruby/object:Gem::Version
|
180
186
|
version: '0'
|
181
187
|
requirements: []
|
182
|
-
rubygems_version: 3.
|
188
|
+
rubygems_version: 3.4.1
|
183
189
|
signing_key:
|
184
190
|
specification_version: 4
|
185
191
|
summary: A parser based on ripper
|