syntax_tree 5.0.0 → 5.1.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/.rubocop.yml +51 -0
- data/CHANGELOG.md +24 -1
- data/Gemfile.lock +9 -9
- data/README.md +5 -5
- data/lib/syntax_tree/cli.rb +8 -6
- 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 +7 -7
- data/lib/syntax_tree/parser.rb +20 -21
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +459 -0
- data/lib/syntax_tree/yarv/bf.rb +179 -0
- data/lib/syntax_tree/yarv/compiler.rb +2287 -0
- data/lib/syntax_tree/yarv/decompiler.rb +254 -0
- data/lib/syntax_tree/yarv/disassembler.rb +211 -0
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1171 -0
- data/lib/syntax_tree/yarv/instructions.rb +5203 -0
- data/lib/syntax_tree/yarv/legacy.rb +192 -0
- data/lib/syntax_tree/yarv/local_table.rb +89 -0
- data/lib/syntax_tree/yarv.rb +287 -0
- data/lib/syntax_tree.rb +23 -1
- data/syntax_tree.gemspec +1 -1
- metadata +15 -4
@@ -0,0 +1,192 @@
|
|
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
|
+
end
|
49
|
+
|
50
|
+
# ### Summary
|
51
|
+
#
|
52
|
+
# `opt_getinlinecache` is a wrapper around a series of `putobject` and
|
53
|
+
# `getconstant` instructions that allows skipping past them if the inline
|
54
|
+
# cache is currently set. It pushes the value of the cache onto the stack
|
55
|
+
# if it is set, otherwise it pushes `nil`.
|
56
|
+
#
|
57
|
+
# This instruction is no longer used since in Ruby 3.2 it was replaced by
|
58
|
+
# the consolidated `opt_getconstant_path` instruction.
|
59
|
+
#
|
60
|
+
# ### Usage
|
61
|
+
#
|
62
|
+
# ~~~ruby
|
63
|
+
# Constant
|
64
|
+
# ~~~
|
65
|
+
#
|
66
|
+
class OptGetInlineCache
|
67
|
+
attr_reader :label, :cache
|
68
|
+
|
69
|
+
def initialize(label, cache)
|
70
|
+
@label = label
|
71
|
+
@cache = cache
|
72
|
+
end
|
73
|
+
|
74
|
+
def disasm(fmt)
|
75
|
+
fmt.instruction(
|
76
|
+
"opt_getinlinecache",
|
77
|
+
[fmt.label(label), fmt.inline_storage(cache)]
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_a(_iseq)
|
82
|
+
[:opt_getinlinecache, label.name, cache]
|
83
|
+
end
|
84
|
+
|
85
|
+
def length
|
86
|
+
3
|
87
|
+
end
|
88
|
+
|
89
|
+
def pops
|
90
|
+
0
|
91
|
+
end
|
92
|
+
|
93
|
+
def pushes
|
94
|
+
1
|
95
|
+
end
|
96
|
+
|
97
|
+
def call(vm)
|
98
|
+
vm.push(nil)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# ### Summary
|
103
|
+
#
|
104
|
+
# `opt_setinlinecache` sets an inline cache for a constant lookup. It pops
|
105
|
+
# the value it should set off the top of the stack. It then pushes that
|
106
|
+
# value back onto the top of the stack.
|
107
|
+
#
|
108
|
+
# This instruction is no longer used since in Ruby 3.2 it was replaced by
|
109
|
+
# the consolidated `opt_getconstant_path` instruction.
|
110
|
+
#
|
111
|
+
# ### Usage
|
112
|
+
#
|
113
|
+
# ~~~ruby
|
114
|
+
# Constant
|
115
|
+
# ~~~
|
116
|
+
#
|
117
|
+
class OptSetInlineCache
|
118
|
+
attr_reader :cache
|
119
|
+
|
120
|
+
def initialize(cache)
|
121
|
+
@cache = cache
|
122
|
+
end
|
123
|
+
|
124
|
+
def disasm(fmt)
|
125
|
+
fmt.instruction("opt_setinlinecache", [fmt.inline_storage(cache)])
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_a(_iseq)
|
129
|
+
[:opt_setinlinecache, cache]
|
130
|
+
end
|
131
|
+
|
132
|
+
def length
|
133
|
+
2
|
134
|
+
end
|
135
|
+
|
136
|
+
def pops
|
137
|
+
1
|
138
|
+
end
|
139
|
+
|
140
|
+
def pushes
|
141
|
+
1
|
142
|
+
end
|
143
|
+
|
144
|
+
def call(vm)
|
145
|
+
vm.push(vm.pop)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# ### Summary
|
150
|
+
#
|
151
|
+
# `setclassvariable` looks for a class variable in the current class and
|
152
|
+
# sets its value to the value it pops off the top of the stack.
|
153
|
+
#
|
154
|
+
# This version of the `setclassvariable` instruction is no longer used
|
155
|
+
# since in Ruby 3.0 it gained an inline cache.
|
156
|
+
#
|
157
|
+
# ### Usage
|
158
|
+
#
|
159
|
+
# ~~~ruby
|
160
|
+
# @@class_variable = 1
|
161
|
+
# ~~~
|
162
|
+
#
|
163
|
+
class SetClassVariable
|
164
|
+
attr_reader :name
|
165
|
+
|
166
|
+
def initialize(name)
|
167
|
+
@name = name
|
168
|
+
end
|
169
|
+
|
170
|
+
def disasm(fmt)
|
171
|
+
fmt.instruction("setclassvariable", [fmt.object(name)])
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_a(_iseq)
|
175
|
+
[:setclassvariable, name]
|
176
|
+
end
|
177
|
+
|
178
|
+
def length
|
179
|
+
2
|
180
|
+
end
|
181
|
+
|
182
|
+
def pops
|
183
|
+
1
|
184
|
+
end
|
185
|
+
|
186
|
+
def pushes
|
187
|
+
0
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
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
|
@@ -0,0 +1,287 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module SyntaxTree
|
6
|
+
# This module provides an object representation of the YARV bytecode.
|
7
|
+
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
|
+
# Compile the given source into a YARV instruction sequence.
|
276
|
+
def self.compile(source, options = Compiler::Options.new)
|
277
|
+
SyntaxTree.parse(source).accept(Compiler.new(options))
|
278
|
+
end
|
279
|
+
|
280
|
+
# Compile and interpret the given source.
|
281
|
+
def self.interpret(source, options = Compiler::Options.new)
|
282
|
+
iseq = RubyVM::InstructionSequence.compile(source, **options)
|
283
|
+
iseq = InstructionSequence.from(iseq.to_a)
|
284
|
+
VM.new.run_top_frame(iseq)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
data/lib/syntax_tree.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "etc"
|
4
|
+
require "fiddle"
|
4
5
|
require "json"
|
5
6
|
require "pp"
|
6
7
|
require "prettier_print"
|
@@ -9,6 +10,7 @@ require "stringio"
|
|
9
10
|
|
10
11
|
require_relative "syntax_tree/formatter"
|
11
12
|
require_relative "syntax_tree/node"
|
13
|
+
require_relative "syntax_tree/dsl"
|
12
14
|
require_relative "syntax_tree/version"
|
13
15
|
|
14
16
|
require_relative "syntax_tree/basic_visitor"
|
@@ -25,6 +27,17 @@ require_relative "syntax_tree/parser"
|
|
25
27
|
require_relative "syntax_tree/pattern"
|
26
28
|
require_relative "syntax_tree/search"
|
27
29
|
|
30
|
+
require_relative "syntax_tree/yarv"
|
31
|
+
require_relative "syntax_tree/yarv/bf"
|
32
|
+
require_relative "syntax_tree/yarv/compiler"
|
33
|
+
require_relative "syntax_tree/yarv/decompiler"
|
34
|
+
require_relative "syntax_tree/yarv/disassembler"
|
35
|
+
require_relative "syntax_tree/yarv/instruction_sequence"
|
36
|
+
require_relative "syntax_tree/yarv/instructions"
|
37
|
+
require_relative "syntax_tree/yarv/legacy"
|
38
|
+
require_relative "syntax_tree/yarv/local_table"
|
39
|
+
require_relative "syntax_tree/yarv/assembler"
|
40
|
+
|
28
41
|
# Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
|
29
42
|
# provides the ability to generate a syntax tree from source, as well as the
|
30
43
|
# tools necessary to inspect and manipulate that syntax tree. It can be used to
|
@@ -40,6 +53,14 @@ module SyntaxTree
|
|
40
53
|
# optional second argument to ::format.
|
41
54
|
DEFAULT_PRINT_WIDTH = 80
|
42
55
|
|
56
|
+
# This is the default ruby version that we're going to target for formatting.
|
57
|
+
# It shouldn't really be changed except in very niche circumstances.
|
58
|
+
DEFAULT_RUBY_VERSION = Formatter::SemanticVersion.new(RUBY_VERSION).freeze
|
59
|
+
|
60
|
+
# The default indentation level for formatting. We allow changing this so
|
61
|
+
# that Syntax Tree can format arbitrary parts of a document.
|
62
|
+
DEFAULT_INDENTATION = 0
|
63
|
+
|
43
64
|
# This is a hook provided so that plugins can register themselves as the
|
44
65
|
# handler for a particular file type.
|
45
66
|
def self.register_handler(extension, handler)
|
@@ -57,12 +78,13 @@ module SyntaxTree
|
|
57
78
|
def self.format(
|
58
79
|
source,
|
59
80
|
maxwidth = DEFAULT_PRINT_WIDTH,
|
81
|
+
base_indentation = DEFAULT_INDENTATION,
|
60
82
|
options: Formatter::Options.new
|
61
83
|
)
|
62
84
|
formatter = Formatter.new(source, [], maxwidth, options: options)
|
63
85
|
parse(source).format(formatter)
|
64
86
|
|
65
|
-
formatter.flush
|
87
|
+
formatter.flush(base_indentation)
|
66
88
|
formatter.output.join
|
67
89
|
end
|
68
90
|
|
data/syntax_tree.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
26
|
spec.require_paths = %w[lib]
|
27
27
|
|
28
|
-
spec.add_dependency "prettier_print", ">= 1.
|
28
|
+
spec.add_dependency "prettier_print", ">= 1.2.0"
|
29
29
|
|
30
30
|
spec.add_development_dependency "bundler"
|
31
31
|
spec.add_development_dependency "minitest"
|
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.1.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: 2022-
|
11
|
+
date: 2022-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prettier_print
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.2.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- lib/syntax_tree.rb
|
126
126
|
- lib/syntax_tree/basic_visitor.rb
|
127
127
|
- lib/syntax_tree/cli.rb
|
128
|
+
- lib/syntax_tree/dsl.rb
|
128
129
|
- lib/syntax_tree/formatter.rb
|
129
130
|
- lib/syntax_tree/language_server.rb
|
130
131
|
- lib/syntax_tree/language_server/inlay_hints.rb
|
@@ -147,6 +148,16 @@ files:
|
|
147
148
|
- lib/syntax_tree/visitor/mutation_visitor.rb
|
148
149
|
- lib/syntax_tree/visitor/pretty_print_visitor.rb
|
149
150
|
- lib/syntax_tree/visitor/with_environment.rb
|
151
|
+
- lib/syntax_tree/yarv.rb
|
152
|
+
- lib/syntax_tree/yarv/assembler.rb
|
153
|
+
- lib/syntax_tree/yarv/bf.rb
|
154
|
+
- lib/syntax_tree/yarv/compiler.rb
|
155
|
+
- lib/syntax_tree/yarv/decompiler.rb
|
156
|
+
- lib/syntax_tree/yarv/disassembler.rb
|
157
|
+
- lib/syntax_tree/yarv/instruction_sequence.rb
|
158
|
+
- lib/syntax_tree/yarv/instructions.rb
|
159
|
+
- lib/syntax_tree/yarv/legacy.rb
|
160
|
+
- lib/syntax_tree/yarv/local_table.rb
|
150
161
|
- syntax_tree.gemspec
|
151
162
|
homepage: https://github.com/kddnewton/syntax_tree
|
152
163
|
licenses:
|