rlsl 0.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 +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +21 -0
- data/README.md +170 -0
- data/Rakefile +12 -0
- data/lib/rlsl/base_translator.rb +109 -0
- data/lib/rlsl/code_generator.rb +231 -0
- data/lib/rlsl/compiled_shader.rb +24 -0
- data/lib/rlsl/function_context.rb +35 -0
- data/lib/rlsl/glsl/translator.rb +97 -0
- data/lib/rlsl/msl/shader.rb +83 -0
- data/lib/rlsl/msl/translator.rb +89 -0
- data/lib/rlsl/prism/ast_visitor.rb +371 -0
- data/lib/rlsl/prism/builtins.rb +197 -0
- data/lib/rlsl/prism/emitters/base_emitter.rb +480 -0
- data/lib/rlsl/prism/emitters/c_emitter.rb +153 -0
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +74 -0
- data/lib/rlsl/prism/emitters/msl_emitter.rb +76 -0
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +89 -0
- data/lib/rlsl/prism/ir/nodes.rb +373 -0
- data/lib/rlsl/prism/source_extractor.rb +152 -0
- data/lib/rlsl/prism/transpiler.rb +128 -0
- data/lib/rlsl/prism/type_inference.rb +289 -0
- data/lib/rlsl/shader_builder.rb +165 -0
- data/lib/rlsl/types.rb +36 -0
- data/lib/rlsl/uniform_context.rb +28 -0
- data/lib/rlsl/version.rb +5 -0
- data/lib/rlsl/wgsl/translator.rb +93 -0
- data/lib/rlsl.rb +57 -0
- metadata +100 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
module IR
|
|
6
|
+
class Node
|
|
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
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class SourceExtractor
|
|
6
|
+
class SourceNotAvailable < StandardError; end
|
|
7
|
+
|
|
8
|
+
def extract(block)
|
|
9
|
+
file, line_num = block.source_location
|
|
10
|
+
raise SourceNotAvailable, "Block source location not available" unless file && File.exist?(file)
|
|
11
|
+
|
|
12
|
+
lines = File.readlines(file)
|
|
13
|
+
extract_block_source(lines, line_num - 1)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def extract_from_string(source)
|
|
17
|
+
source
|
|
18
|
+
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
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "ir/nodes"
|
|
6
|
+
require_relative "source_extractor"
|
|
7
|
+
require_relative "builtins"
|
|
8
|
+
require_relative "ast_visitor"
|
|
9
|
+
require_relative "type_inference"
|
|
10
|
+
require_relative "emitters/base_emitter"
|
|
11
|
+
require_relative "emitters/c_emitter"
|
|
12
|
+
require_relative "emitters/msl_emitter"
|
|
13
|
+
require_relative "emitters/wgsl_emitter"
|
|
14
|
+
require_relative "emitters/glsl_emitter"
|
|
15
|
+
|
|
16
|
+
module RLSL
|
|
17
|
+
module Prism
|
|
18
|
+
class Transpiler
|
|
19
|
+
TARGETS = {
|
|
20
|
+
c: Emitters::CEmitter,
|
|
21
|
+
msl: Emitters::MSLEmitter,
|
|
22
|
+
wgsl: Emitters::WGSLEmitter,
|
|
23
|
+
glsl: Emitters::GLSLEmitter
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
attr_reader :ir, :uniforms, :custom_functions
|
|
27
|
+
|
|
28
|
+
def initialize(uniforms = {}, custom_functions = {})
|
|
29
|
+
@uniforms = uniforms
|
|
30
|
+
@custom_functions = custom_functions
|
|
31
|
+
@source_extractor = SourceExtractor.new
|
|
32
|
+
@ir = nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse_block(block)
|
|
36
|
+
source = @source_extractor.extract(block)
|
|
37
|
+
parse_source(source)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def parse_source(source)
|
|
41
|
+
params, body = extract_block_body(source)
|
|
42
|
+
|
|
43
|
+
visitor = ASTVisitor.new(uniforms: @uniforms, params: params)
|
|
44
|
+
@ir = visitor.parse(body)
|
|
45
|
+
|
|
46
|
+
inference = TypeInference.new(@uniforms, @custom_functions)
|
|
47
|
+
inference.register(:frag_coord, :vec2)
|
|
48
|
+
inference.register(:resolution, :vec2)
|
|
49
|
+
inference.infer(@ir)
|
|
50
|
+
|
|
51
|
+
@ir
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def emit(target, needs_return: true)
|
|
55
|
+
raise "No IR parsed yet. Call parse_block or parse_source first." unless @ir
|
|
56
|
+
|
|
57
|
+
emitter_class = TARGETS[target.to_sym]
|
|
58
|
+
raise "Unknown target: #{target}" unless emitter_class
|
|
59
|
+
|
|
60
|
+
emitter = emitter_class.new
|
|
61
|
+
emitter.emit(@ir, needs_return: needs_return)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def transpile(block, target)
|
|
65
|
+
parse_block(block)
|
|
66
|
+
emit(target)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def transpile_source(source, target)
|
|
70
|
+
parse_source(source)
|
|
71
|
+
emit(target)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def transpile_helpers(block, target, function_signatures = {})
|
|
75
|
+
source = @source_extractor.extract(block)
|
|
76
|
+
_, body = extract_block_body(source)
|
|
77
|
+
|
|
78
|
+
visitor = ASTVisitor.new(uniforms: @uniforms)
|
|
79
|
+
@ir = visitor.parse(body)
|
|
80
|
+
|
|
81
|
+
apply_function_signatures(@ir, function_signatures)
|
|
82
|
+
|
|
83
|
+
inference = TypeInference.new(@uniforms, @custom_functions)
|
|
84
|
+
inference.infer(@ir)
|
|
85
|
+
|
|
86
|
+
emit(target, needs_return: false)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def apply_function_signatures(ir, signatures)
|
|
92
|
+
return unless ir.is_a?(IR::Block)
|
|
93
|
+
|
|
94
|
+
ir.statements.each do |stmt|
|
|
95
|
+
next unless stmt.is_a?(IR::FunctionDefinition)
|
|
96
|
+
|
|
97
|
+
sig = signatures[stmt.name]
|
|
98
|
+
next unless sig
|
|
99
|
+
|
|
100
|
+
stmt.return_type = sig[:returns]
|
|
101
|
+
stmt.param_types = sig[:params] || {}
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def extract_block_body(source)
|
|
106
|
+
lines = source.strip.lines
|
|
107
|
+
params = []
|
|
108
|
+
|
|
109
|
+
first_line = lines.first&.strip || ""
|
|
110
|
+
|
|
111
|
+
if first_line.start_with?("|")
|
|
112
|
+
param_end = first_line.index("|", 1)
|
|
113
|
+
if param_end
|
|
114
|
+
param_str = first_line[1...param_end]
|
|
115
|
+
params = param_str.split(",").map { |p| p.strip.to_sym }
|
|
116
|
+
lines[0] = first_line[(param_end + 1)..]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
lines.shift while lines.first&.strip&.empty?
|
|
121
|
+
lines.pop while lines.last&.strip&.empty?
|
|
122
|
+
|
|
123
|
+
body = lines.join.strip
|
|
124
|
+
[params, body]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|