rlsl 0.1.1 → 1.0.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/CHANGELOG.md +10 -1
- data/README.md +2 -0
- data/lib/rlsl/base_translator/call_parser.rb +68 -0
- data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
- data/lib/rlsl/base_translator/code_scanner.rb +102 -0
- data/lib/rlsl/base_translator.rb +159 -63
- data/lib/rlsl/code_generator/math_prelude.rb +79 -0
- data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +97 -0
- data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
- data/lib/rlsl/code_generator/template_context.rb +41 -0
- data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
- data/lib/rlsl/code_generator.rb +26 -201
- data/lib/rlsl/compiled_shader.rb +4 -12
- data/lib/rlsl/function_context.rb +31 -14
- data/lib/rlsl/glsl/translator.rb +19 -38
- data/lib/rlsl/msl/shader.rb +8 -38
- data/lib/rlsl/msl/translator.rb +19 -36
- data/lib/rlsl/msl/uniform_buffer_packer.rb +68 -0
- data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +96 -0
- data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +79 -0
- data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +168 -0
- data/lib/rlsl/prism/ast_visitor/scope_context.rb +44 -0
- data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
- data/lib/rlsl/prism/ast_visitor.rb +54 -298
- data/lib/rlsl/prism/builtins/function_registry.rb +114 -0
- data/lib/rlsl/prism/builtins/operator_rules.rb +99 -0
- data/lib/rlsl/prism/builtins/swizzle_rules.rb +38 -0
- data/lib/rlsl/prism/builtins.rb +31 -148
- data/lib/rlsl/prism/compilation_unit.rb +7 -0
- data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +83 -0
- data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +104 -0
- data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +92 -0
- data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +89 -0
- data/lib/rlsl/prism/emitters/base_emitter.rb +68 -423
- data/lib/rlsl/prism/emitters/c_emitter.rb +78 -121
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +30 -65
- data/lib/rlsl/prism/emitters/msl_emitter.rb +35 -62
- data/lib/rlsl/prism/emitters/target_emitter.rb +77 -0
- data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +65 -61
- data/lib/rlsl/prism/ir/control_flow.rb +83 -0
- data/lib/rlsl/prism/ir/definitions.rb +67 -0
- data/lib/rlsl/prism/ir/expressions.rb +197 -0
- data/lib/rlsl/prism/ir/node.rb +21 -0
- data/lib/rlsl/prism/ir/nodes.rb +4 -371
- data/lib/rlsl/prism/ir/traversal.rb +62 -0
- data/lib/rlsl/prism/source_extractor/block_locator.rb +52 -0
- data/lib/rlsl/prism/source_extractor.rb +14 -133
- data/lib/rlsl/prism/source_unit/parser.rb +78 -0
- data/lib/rlsl/prism/source_unit.rb +42 -0
- data/lib/rlsl/prism/target_capability_validator.rb +85 -0
- data/lib/rlsl/prism/transpiler.rb +63 -60
- data/lib/rlsl/prism/type_inference/call_type_resolver.rb +35 -0
- data/lib/rlsl/prism/type_inference/call_validator.rb +71 -0
- data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +80 -0
- data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +66 -0
- data/lib/rlsl/prism/type_inference/definition_inferer.rb +41 -0
- data/lib/rlsl/prism/type_inference/expression_inferer.rb +92 -0
- data/lib/rlsl/prism/type_inference/field_type_resolver.rb +17 -0
- data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
- data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
- data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
- data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
- data/lib/rlsl/prism/type_inference.rb +114 -248
- data/lib/rlsl/runtime_shader.rb +47 -0
- data/lib/rlsl/shader_builder/build_service.rb +77 -0
- data/lib/rlsl/shader_builder/native_extension_compiler.rb +71 -0
- data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
- data/lib/rlsl/shader_builder/source_resolver.rb +91 -0
- data/lib/rlsl/shader_builder.rb +22 -113
- data/lib/rlsl/types/catalog.rb +47 -0
- data/lib/rlsl/types/target_resolver.rb +15 -0
- data/lib/rlsl/types/type_spec.rb +167 -0
- data/lib/rlsl/types/value_normalizer.rb +81 -0
- data/lib/rlsl/types.rb +11 -29
- data/lib/rlsl/uniform_context.rb +6 -12
- data/lib/rlsl/version.rb +1 -1
- data/lib/rlsl/wgsl/translator.rb +23 -39
- data/lib/rlsl.rb +14 -12
- metadata +55 -2
data/lib/rlsl/prism/ir/nodes.rb
CHANGED
|
@@ -1,373 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
attr_accessor :type
|
|
8
|
-
|
|
9
|
-
def accept(visitor)
|
|
10
|
-
raise NotImplementedError
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
class Block < Node
|
|
15
|
-
attr_reader :statements
|
|
16
|
-
|
|
17
|
-
def initialize(statements = [])
|
|
18
|
-
super()
|
|
19
|
-
@statements = statements
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def accept(visitor)
|
|
23
|
-
visitor.visit_block(self)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
class VarDecl < Node
|
|
28
|
-
attr_reader :name, :initializer
|
|
29
|
-
|
|
30
|
-
def initialize(name, initializer, type = nil)
|
|
31
|
-
super()
|
|
32
|
-
@name = name
|
|
33
|
-
@initializer = initializer
|
|
34
|
-
@type = type
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def accept(visitor)
|
|
38
|
-
visitor.visit_var_decl(self)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
class VarRef < Node
|
|
43
|
-
attr_reader :name
|
|
44
|
-
|
|
45
|
-
def initialize(name, type = nil)
|
|
46
|
-
super()
|
|
47
|
-
@name = name
|
|
48
|
-
@type = type
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def accept(visitor)
|
|
52
|
-
visitor.visit_var_ref(self)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
class Literal < Node
|
|
57
|
-
attr_reader :value
|
|
58
|
-
|
|
59
|
-
def initialize(value, type = nil)
|
|
60
|
-
super()
|
|
61
|
-
@value = value
|
|
62
|
-
@type = type || (value.is_a?(Float) ? :float : :int)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def accept(visitor)
|
|
66
|
-
visitor.visit_literal(self)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
class BoolLiteral < Node
|
|
71
|
-
attr_reader :value
|
|
72
|
-
|
|
73
|
-
def initialize(value)
|
|
74
|
-
super()
|
|
75
|
-
@value = value
|
|
76
|
-
@type = :bool
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def accept(visitor)
|
|
80
|
-
visitor.visit_bool_literal(self)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
class BinaryOp < Node
|
|
85
|
-
attr_reader :operator, :left, :right
|
|
86
|
-
|
|
87
|
-
def initialize(operator, left, right, type = nil)
|
|
88
|
-
super()
|
|
89
|
-
@operator = operator
|
|
90
|
-
@left = left
|
|
91
|
-
@right = right
|
|
92
|
-
@type = type
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def accept(visitor)
|
|
96
|
-
visitor.visit_binary_op(self)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
class UnaryOp < Node
|
|
101
|
-
attr_reader :operator, :operand
|
|
102
|
-
|
|
103
|
-
def initialize(operator, operand, type = nil)
|
|
104
|
-
super()
|
|
105
|
-
@operator = operator
|
|
106
|
-
@operand = operand
|
|
107
|
-
@type = type
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def accept(visitor)
|
|
111
|
-
visitor.visit_unary_op(self)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
class FuncCall < Node
|
|
116
|
-
attr_reader :name, :args, :receiver
|
|
117
|
-
|
|
118
|
-
def initialize(name, args = [], receiver = nil, type = nil)
|
|
119
|
-
super()
|
|
120
|
-
@name = name
|
|
121
|
-
@args = args
|
|
122
|
-
@receiver = receiver
|
|
123
|
-
@type = type
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def accept(visitor)
|
|
127
|
-
visitor.visit_func_call(self)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
class FieldAccess < Node
|
|
132
|
-
attr_reader :receiver, :field
|
|
133
|
-
|
|
134
|
-
def initialize(receiver, field, type = nil)
|
|
135
|
-
super()
|
|
136
|
-
@receiver = receiver
|
|
137
|
-
@field = field
|
|
138
|
-
@type = type
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def accept(visitor)
|
|
142
|
-
visitor.visit_field_access(self)
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
class Swizzle < Node
|
|
147
|
-
attr_reader :receiver, :components
|
|
148
|
-
|
|
149
|
-
def initialize(receiver, components, type = nil)
|
|
150
|
-
super()
|
|
151
|
-
@receiver = receiver
|
|
152
|
-
@components = components
|
|
153
|
-
@type = type
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def accept(visitor)
|
|
157
|
-
visitor.visit_swizzle(self)
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
class IfStatement < Node
|
|
162
|
-
attr_reader :condition, :then_branch, :else_branch
|
|
163
|
-
|
|
164
|
-
def initialize(condition, then_branch, else_branch = nil, type = nil)
|
|
165
|
-
super()
|
|
166
|
-
@condition = condition
|
|
167
|
-
@then_branch = then_branch
|
|
168
|
-
@else_branch = else_branch
|
|
169
|
-
@type = type
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def accept(visitor)
|
|
173
|
-
visitor.visit_if_statement(self)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
class Return < Node
|
|
178
|
-
attr_reader :expression
|
|
179
|
-
|
|
180
|
-
def initialize(expression)
|
|
181
|
-
super()
|
|
182
|
-
@expression = expression
|
|
183
|
-
@type = expression&.type
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def accept(visitor)
|
|
187
|
-
visitor.visit_return(self)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
class Assignment < Node
|
|
192
|
-
attr_reader :target, :value
|
|
193
|
-
|
|
194
|
-
def initialize(target, value)
|
|
195
|
-
super()
|
|
196
|
-
@target = target
|
|
197
|
-
@value = value
|
|
198
|
-
@type = value&.type
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def accept(visitor)
|
|
202
|
-
visitor.visit_assignment(self)
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
class ForLoop < Node
|
|
207
|
-
attr_reader :variable, :range_start, :range_end, :body
|
|
208
|
-
|
|
209
|
-
def initialize(variable, range_start, range_end, body)
|
|
210
|
-
super()
|
|
211
|
-
@variable = variable
|
|
212
|
-
@range_start = range_start
|
|
213
|
-
@range_end = range_end
|
|
214
|
-
@body = body
|
|
215
|
-
@type = nil
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def accept(visitor)
|
|
219
|
-
visitor.visit_for_loop(self)
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
class WhileLoop < Node
|
|
224
|
-
attr_reader :condition, :body
|
|
225
|
-
|
|
226
|
-
def initialize(condition, body)
|
|
227
|
-
super()
|
|
228
|
-
@condition = condition
|
|
229
|
-
@body = body
|
|
230
|
-
@type = nil
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def accept(visitor)
|
|
234
|
-
visitor.visit_while_loop(self)
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
class Break < Node
|
|
239
|
-
def initialize
|
|
240
|
-
super()
|
|
241
|
-
@type = nil
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def accept(visitor)
|
|
245
|
-
visitor.visit_break(self)
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
class Constant < Node
|
|
250
|
-
attr_reader :name
|
|
251
|
-
|
|
252
|
-
def initialize(name, type = :float)
|
|
253
|
-
super()
|
|
254
|
-
@name = name
|
|
255
|
-
@type = type
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def accept(visitor)
|
|
259
|
-
visitor.visit_constant(self)
|
|
260
|
-
end
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
class Parenthesized < Node
|
|
264
|
-
attr_reader :expression
|
|
265
|
-
|
|
266
|
-
def initialize(expression)
|
|
267
|
-
super()
|
|
268
|
-
@expression = expression
|
|
269
|
-
@type = expression&.type
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
def accept(visitor)
|
|
273
|
-
visitor.visit_parenthesized(self)
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
class ArrayLiteral < Node
|
|
278
|
-
attr_reader :elements
|
|
279
|
-
|
|
280
|
-
def initialize(elements, type = nil)
|
|
281
|
-
super()
|
|
282
|
-
@elements = elements
|
|
283
|
-
@type = type
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def accept(visitor)
|
|
287
|
-
visitor.visit_array_literal(self)
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
class ArrayIndex < Node
|
|
292
|
-
attr_reader :array, :index
|
|
293
|
-
|
|
294
|
-
def initialize(array, index, type = nil)
|
|
295
|
-
super()
|
|
296
|
-
@array = array
|
|
297
|
-
@index = index
|
|
298
|
-
@type = type
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
def accept(visitor)
|
|
302
|
-
visitor.visit_array_index(self)
|
|
303
|
-
end
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
class GlobalDecl < Node
|
|
307
|
-
attr_reader :name, :initializer
|
|
308
|
-
attr_accessor :is_const, :is_static, :array_size, :element_type
|
|
309
|
-
|
|
310
|
-
def initialize(name, initializer, type: nil, is_const: false, is_static: true, array_size: nil, element_type: nil)
|
|
311
|
-
super()
|
|
312
|
-
@name = name
|
|
313
|
-
@initializer = initializer
|
|
314
|
-
@type = type
|
|
315
|
-
@is_const = is_const
|
|
316
|
-
@is_static = is_static
|
|
317
|
-
@array_size = array_size
|
|
318
|
-
@element_type = element_type
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def accept(visitor)
|
|
322
|
-
visitor.visit_global_decl(self)
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
class FunctionDefinition < Node
|
|
327
|
-
attr_reader :name, :params, :body
|
|
328
|
-
attr_accessor :return_type, :param_types
|
|
329
|
-
|
|
330
|
-
def initialize(name, params, body, return_type: nil, param_types: {})
|
|
331
|
-
super()
|
|
332
|
-
@name = name
|
|
333
|
-
@params = params
|
|
334
|
-
@body = body
|
|
335
|
-
@return_type = return_type
|
|
336
|
-
@param_types = param_types
|
|
337
|
-
@type = return_type
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def accept(visitor)
|
|
341
|
-
visitor.visit_function_definition(self)
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
class MultipleAssignment < Node
|
|
346
|
-
attr_reader :targets, :value
|
|
347
|
-
|
|
348
|
-
def initialize(targets, value)
|
|
349
|
-
super()
|
|
350
|
-
@targets = targets
|
|
351
|
-
@value = value
|
|
352
|
-
@type = nil
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
def accept(visitor)
|
|
356
|
-
visitor.visit_multiple_assignment(self)
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
class TupleType
|
|
361
|
-
attr_reader :types
|
|
362
|
-
|
|
363
|
-
def initialize(*types)
|
|
364
|
-
@types = types
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
def to_sym
|
|
368
|
-
:"tuple_#{types.map(&:to_s).join('_')}"
|
|
369
|
-
end
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
end
|
|
3
|
+
require_relative "node"
|
|
4
|
+
require_relative "expressions"
|
|
5
|
+
require_relative "control_flow"
|
|
6
|
+
require_relative "definitions"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
module IR
|
|
6
|
+
module Traversal
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def each(node, &block)
|
|
10
|
+
return enum_for(:each, node) unless block_given?
|
|
11
|
+
return if node.nil?
|
|
12
|
+
|
|
13
|
+
yield node
|
|
14
|
+
child_nodes(node).each { |child| each(child, &block) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def child_nodes(node)
|
|
18
|
+
case node
|
|
19
|
+
when Block
|
|
20
|
+
node.statements
|
|
21
|
+
when VarDecl
|
|
22
|
+
[node.initializer]
|
|
23
|
+
when BinaryOp
|
|
24
|
+
[node.left, node.right]
|
|
25
|
+
when UnaryOp
|
|
26
|
+
[node.operand]
|
|
27
|
+
when FuncCall
|
|
28
|
+
[node.receiver, *node.args]
|
|
29
|
+
when FieldAccess, Swizzle
|
|
30
|
+
[node.receiver]
|
|
31
|
+
when IfStatement
|
|
32
|
+
[node.condition, node.then_branch, node.else_branch]
|
|
33
|
+
when Ternary
|
|
34
|
+
[node.condition, node.then_expr, node.else_expr]
|
|
35
|
+
when Return
|
|
36
|
+
[node.expression]
|
|
37
|
+
when Assignment
|
|
38
|
+
[node.target, node.value]
|
|
39
|
+
when ForLoop
|
|
40
|
+
[node.range_start, node.range_end, node.body]
|
|
41
|
+
when WhileLoop
|
|
42
|
+
[node.condition, node.body]
|
|
43
|
+
when Parenthesized
|
|
44
|
+
[node.expression]
|
|
45
|
+
when ArrayLiteral
|
|
46
|
+
node.elements
|
|
47
|
+
when ArrayIndex
|
|
48
|
+
[node.array, node.index]
|
|
49
|
+
when GlobalDecl
|
|
50
|
+
[node.initializer]
|
|
51
|
+
when FunctionDefinition
|
|
52
|
+
[node.body]
|
|
53
|
+
when MultipleAssignment
|
|
54
|
+
[*node.targets, node.value]
|
|
55
|
+
else
|
|
56
|
+
[]
|
|
57
|
+
end.compact
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class SourceExtractor
|
|
6
|
+
class BlockLocator
|
|
7
|
+
def extract(source, start_line)
|
|
8
|
+
extract_unit(source, start_line).to_source
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def extract_unit(source, start_line)
|
|
12
|
+
parsed = ::Prism.parse(source)
|
|
13
|
+
raise SourceNotAvailable, "Unable to parse block source" unless parsed.success?
|
|
14
|
+
|
|
15
|
+
block = block_at_line(parsed.value, start_line)
|
|
16
|
+
raise SourceNotAvailable, "Unable to locate block source" unless block
|
|
17
|
+
|
|
18
|
+
SourceUnit.from_block(block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def block_at_line(node, start_line)
|
|
24
|
+
each_node(node) do |current|
|
|
25
|
+
next unless current.is_a?(::Prism::BlockNode)
|
|
26
|
+
return current if current.location.start_line == start_line
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
def each_node(node)
|
|
32
|
+
return enum_for(:each_node, node) unless block_given?
|
|
33
|
+
return unless node
|
|
34
|
+
|
|
35
|
+
stack = [node]
|
|
36
|
+
|
|
37
|
+
until stack.empty?
|
|
38
|
+
current = stack.pop
|
|
39
|
+
yield current
|
|
40
|
+
|
|
41
|
+
children = if current.respond_to?(:compact_child_nodes)
|
|
42
|
+
current.compact_child_nodes
|
|
43
|
+
else
|
|
44
|
+
Array(current.child_nodes).compact
|
|
45
|
+
end
|
|
46
|
+
stack.concat(children.reverse)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -1,152 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "source_unit"
|
|
6
|
+
require_relative "source_extractor/block_locator"
|
|
7
|
+
|
|
3
8
|
module RLSL
|
|
4
9
|
module Prism
|
|
5
10
|
class SourceExtractor
|
|
6
11
|
class SourceNotAvailable < StandardError; end
|
|
7
12
|
|
|
13
|
+
def initialize(block_locator = BlockLocator.new)
|
|
14
|
+
@block_locator = block_locator
|
|
15
|
+
end
|
|
16
|
+
|
|
8
17
|
def extract(block)
|
|
18
|
+
extract_unit(block).to_source
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def extract_unit(block)
|
|
9
22
|
file, line_num = block.source_location
|
|
10
23
|
raise SourceNotAvailable, "Block source location not available" unless file && File.exist?(file)
|
|
11
24
|
|
|
12
|
-
|
|
13
|
-
extract_block_source(lines, line_num - 1)
|
|
25
|
+
@block_locator.extract_unit(File.read(file), line_num)
|
|
14
26
|
end
|
|
15
27
|
|
|
16
28
|
def extract_from_string(source)
|
|
17
29
|
source
|
|
18
30
|
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def extract_block_source(lines, start_line)
|
|
23
|
-
source = +""
|
|
24
|
-
depth = 0
|
|
25
|
-
in_block = false
|
|
26
|
-
block_start_found = false
|
|
27
|
-
|
|
28
|
-
lines[start_line..].each_with_index do |line, idx|
|
|
29
|
-
tokens = tokenize_for_blocks(line)
|
|
30
|
-
|
|
31
|
-
tokens.each do |token|
|
|
32
|
-
case token
|
|
33
|
-
when :do, :brace_open
|
|
34
|
-
if !block_start_found
|
|
35
|
-
block_start_found = true
|
|
36
|
-
in_block = true
|
|
37
|
-
end
|
|
38
|
-
depth += 1
|
|
39
|
-
when :block_start
|
|
40
|
-
depth += 1
|
|
41
|
-
when :end, :brace_close
|
|
42
|
-
depth -= 1
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
if block_start_found
|
|
47
|
-
if idx == 0
|
|
48
|
-
source << extract_first_line(line)
|
|
49
|
-
else
|
|
50
|
-
source << line
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
break if in_block && depth == 0
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
clean_block_source(source)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def tokenize_for_blocks(line)
|
|
61
|
-
tokens = []
|
|
62
|
-
in_string = nil
|
|
63
|
-
i = 0
|
|
64
|
-
|
|
65
|
-
while i < line.length
|
|
66
|
-
char = line[i]
|
|
67
|
-
|
|
68
|
-
if in_string
|
|
69
|
-
if char == in_string && (i == 0 || line[i - 1] != "\\")
|
|
70
|
-
in_string = nil
|
|
71
|
-
end
|
|
72
|
-
i += 1
|
|
73
|
-
next
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
if char == '"' || char == "'"
|
|
77
|
-
in_string = char
|
|
78
|
-
i += 1
|
|
79
|
-
next
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
break if char == "#"
|
|
83
|
-
|
|
84
|
-
prev_is_boundary = i == 0 || !line[i - 1].match?(/[a-zA-Z0-9_]/)
|
|
85
|
-
|
|
86
|
-
if char == "{"
|
|
87
|
-
tokens << :brace_open
|
|
88
|
-
elsif char == "}"
|
|
89
|
-
tokens << :brace_close
|
|
90
|
-
elsif prev_is_boundary && line[i..].match?(/\Ado\b/)
|
|
91
|
-
tokens << :do
|
|
92
|
-
i += 1
|
|
93
|
-
elsif prev_is_boundary && line[i..].match?(/\Aelsif\b/)
|
|
94
|
-
i += 4
|
|
95
|
-
elsif prev_is_boundary && line[i..].match?(/\Aelse\b/)
|
|
96
|
-
i += 3
|
|
97
|
-
elsif prev_is_boundary && (m = line[i..].match(/\A(if|unless|while|for|case|def|class|module)\b/))
|
|
98
|
-
tokens << :block_start
|
|
99
|
-
i += m[1].length - 1
|
|
100
|
-
elsif prev_is_boundary && line[i..].match?(/\Aend\b/)
|
|
101
|
-
tokens << :end
|
|
102
|
-
i += 2
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
i += 1
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
tokens
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def extract_first_line(line)
|
|
112
|
-
if line.include?(" do")
|
|
113
|
-
match = line.match(/do\s*(\|[^|]*\|)?\s*(.*)$/)
|
|
114
|
-
if match
|
|
115
|
-
params = match[1] || ""
|
|
116
|
-
rest = match[2] || ""
|
|
117
|
-
"#{params}\n#{rest}\n"
|
|
118
|
-
else
|
|
119
|
-
"\n"
|
|
120
|
-
end
|
|
121
|
-
elsif line.include?("{")
|
|
122
|
-
match = line.match(/\{\s*(\|[^|]*\|)?\s*(.*)$/)
|
|
123
|
-
if match
|
|
124
|
-
params = match[1] || ""
|
|
125
|
-
rest = match[2] || ""
|
|
126
|
-
"#{params}\n#{rest}\n"
|
|
127
|
-
else
|
|
128
|
-
"\n"
|
|
129
|
-
end
|
|
130
|
-
else
|
|
131
|
-
line
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def clean_block_source(source)
|
|
136
|
-
lines = source.lines
|
|
137
|
-
return "" if lines.empty?
|
|
138
|
-
|
|
139
|
-
last_line = lines.last.strip
|
|
140
|
-
if last_line == "end" || last_line == "}"
|
|
141
|
-
lines.pop
|
|
142
|
-
elsif last_line.end_with?("end") || last_line.end_with?("}")
|
|
143
|
-
lines[-1] = lines[-1].sub(/\s*(end|\})\s*$/, "\n")
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
lines.shift if lines.first&.strip&.empty?
|
|
147
|
-
|
|
148
|
-
lines.join
|
|
149
|
-
end
|
|
150
31
|
end
|
|
151
32
|
end
|
|
152
33
|
end
|