syntax_tree 5.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.1.0
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: 2022-12-29 00:00:00.000000000 Z
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.3.21
186
+ rubygems_version: 3.4.1
183
187
  signing_key:
184
188
  specification_version: 4
185
189
  summary: A parser based on ripper