rlsl 0.1.0 → 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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +18 -1
  4. data/lib/rlsl/base_translator/call_parser.rb +68 -0
  5. data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
  6. data/lib/rlsl/base_translator/code_scanner.rb +102 -0
  7. data/lib/rlsl/base_translator.rb +159 -63
  8. data/lib/rlsl/code_generator/math_prelude.rb +79 -0
  9. data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +97 -0
  10. data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
  11. data/lib/rlsl/code_generator/template_context.rb +41 -0
  12. data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
  13. data/lib/rlsl/code_generator.rb +26 -201
  14. data/lib/rlsl/compiled_shader.rb +4 -12
  15. data/lib/rlsl/function_context.rb +31 -14
  16. data/lib/rlsl/glsl/translator.rb +19 -38
  17. data/lib/rlsl/msl/shader.rb +8 -38
  18. data/lib/rlsl/msl/translator.rb +19 -36
  19. data/lib/rlsl/msl/uniform_buffer_packer.rb +68 -0
  20. data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +96 -0
  21. data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +79 -0
  22. data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +168 -0
  23. data/lib/rlsl/prism/ast_visitor/scope_context.rb +44 -0
  24. data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
  25. data/lib/rlsl/prism/ast_visitor.rb +54 -298
  26. data/lib/rlsl/prism/builtins/function_registry.rb +114 -0
  27. data/lib/rlsl/prism/builtins/operator_rules.rb +99 -0
  28. data/lib/rlsl/prism/builtins/swizzle_rules.rb +38 -0
  29. data/lib/rlsl/prism/builtins.rb +31 -148
  30. data/lib/rlsl/prism/compilation_unit.rb +7 -0
  31. data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +83 -0
  32. data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +104 -0
  33. data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +92 -0
  34. data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +89 -0
  35. data/lib/rlsl/prism/emitters/base_emitter.rb +68 -423
  36. data/lib/rlsl/prism/emitters/c_emitter.rb +78 -121
  37. data/lib/rlsl/prism/emitters/glsl_emitter.rb +30 -65
  38. data/lib/rlsl/prism/emitters/msl_emitter.rb +35 -62
  39. data/lib/rlsl/prism/emitters/target_emitter.rb +77 -0
  40. data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
  41. data/lib/rlsl/prism/emitters/wgsl_emitter.rb +65 -61
  42. data/lib/rlsl/prism/ir/control_flow.rb +83 -0
  43. data/lib/rlsl/prism/ir/definitions.rb +67 -0
  44. data/lib/rlsl/prism/ir/expressions.rb +197 -0
  45. data/lib/rlsl/prism/ir/node.rb +21 -0
  46. data/lib/rlsl/prism/ir/nodes.rb +4 -371
  47. data/lib/rlsl/prism/ir/traversal.rb +62 -0
  48. data/lib/rlsl/prism/source_extractor/block_locator.rb +52 -0
  49. data/lib/rlsl/prism/source_extractor.rb +14 -133
  50. data/lib/rlsl/prism/source_unit/parser.rb +78 -0
  51. data/lib/rlsl/prism/source_unit.rb +42 -0
  52. data/lib/rlsl/prism/target_capability_validator.rb +85 -0
  53. data/lib/rlsl/prism/transpiler.rb +63 -60
  54. data/lib/rlsl/prism/type_inference/call_type_resolver.rb +35 -0
  55. data/lib/rlsl/prism/type_inference/call_validator.rb +71 -0
  56. data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +80 -0
  57. data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +66 -0
  58. data/lib/rlsl/prism/type_inference/definition_inferer.rb +41 -0
  59. data/lib/rlsl/prism/type_inference/expression_inferer.rb +92 -0
  60. data/lib/rlsl/prism/type_inference/field_type_resolver.rb +17 -0
  61. data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
  62. data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
  63. data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
  64. data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
  65. data/lib/rlsl/prism/type_inference.rb +114 -248
  66. data/lib/rlsl/runtime_shader.rb +47 -0
  67. data/lib/rlsl/shader_builder/build_service.rb +77 -0
  68. data/lib/rlsl/shader_builder/native_extension_compiler.rb +71 -0
  69. data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
  70. data/lib/rlsl/shader_builder/source_resolver.rb +91 -0
  71. data/lib/rlsl/shader_builder.rb +22 -113
  72. data/lib/rlsl/types/catalog.rb +47 -0
  73. data/lib/rlsl/types/target_resolver.rb +15 -0
  74. data/lib/rlsl/types/type_spec.rb +167 -0
  75. data/lib/rlsl/types/value_normalizer.rb +81 -0
  76. data/lib/rlsl/types.rb +11 -29
  77. data/lib/rlsl/uniform_context.rb +6 -12
  78. data/lib/rlsl/version.rb +1 -1
  79. data/lib/rlsl/wgsl/translator.rb +23 -39
  80. data/lib/rlsl.rb +14 -12
  81. metadata +55 -16
@@ -3,17 +3,38 @@
3
3
  require "prism"
4
4
  require "set"
5
5
 
6
+ require_relative "ast_visitor/visitor_registry"
7
+ require_relative "ast_visitor/scope_context"
8
+ require_relative "ast_visitor/expression_visiting"
9
+ require_relative "ast_visitor/control_flow_visiting"
10
+ require_relative "ast_visitor/definition_visiting"
11
+
6
12
  module RLSL
7
13
  module Prism
14
+ class UnsupportedSyntaxError < StandardError; end
15
+
8
16
  class ASTVisitor
9
17
  BINARY_OPERATORS = %w[+ - * / % == != < > <= >= && ||].freeze
10
18
  UNARY_OPERATORS = %w[- !].freeze
19
+ TRANSPARENT_NODES = VisitorRegistry::TRANSPARENT_NODES
20
+ NODE_VISITORS = VisitorRegistry.build(
21
+ {}.tap do |visitors|
22
+ visitors[::Prism::ProgramNode] = :visit_program if defined?(::Prism::ProgramNode)
23
+ visitors[::Prism::StatementsNode] = :visit_statements if defined?(::Prism::StatementsNode)
24
+ end,
25
+ ExpressionVisiting::VISITORS,
26
+ ControlFlowVisiting::VISITORS,
27
+ DefinitionVisiting::VISITORS
28
+ )
29
+
30
+ include ExpressionVisiting
31
+ include ControlFlowVisiting
32
+ include DefinitionVisiting
11
33
 
12
34
  def initialize(context = {})
13
35
  @context = context
14
36
  @uniforms = context[:uniforms] || {}
15
- @params = Set.new(context[:params] || [])
16
- @declared_vars = Set.new
37
+ @scope_context = ScopeContext.new(params: context[:params] || [])
17
38
  end
18
39
 
19
40
  def parse(source)
@@ -31,24 +52,16 @@ module RLSL
31
52
  def visit(node)
32
53
  return nil if node.nil?
33
54
 
34
- method_name = "visit_#{node_type(node)}"
35
- if respond_to?(method_name, true)
36
- send(method_name, node)
37
- else
38
- visit_default(node)
39
- end
40
- end
55
+ method_name = NODE_VISITORS[node.class]
56
+ return send(method_name, node) if method_name
41
57
 
42
- private
58
+ raise UnsupportedSyntaxError, "Unsupported Prism node: #{node.class}" unless transparent_node?(node)
43
59
 
44
- def node_type(node)
45
- node.class.name.split("::").last
46
- .gsub(/Node$/, "")
47
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
48
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
49
- .downcase
60
+ visit_default(node)
50
61
  end
51
62
 
63
+ private
64
+
52
65
  def visit_default(node)
53
66
  children = []
54
67
  node.child_nodes.compact.each do |child|
@@ -58,6 +71,10 @@ module RLSL
58
71
  children.length == 1 ? children.first : children
59
72
  end
60
73
 
74
+ def transparent_node?(node)
75
+ TRANSPARENT_NODES.include?(node.class)
76
+ end
77
+
61
78
  def visit_program(node)
62
79
  visit(node.statements)
63
80
  end
@@ -67,304 +84,43 @@ module RLSL
67
84
  IR::Block.new(statements)
68
85
  end
69
86
 
70
- def visit_block(node)
71
- if node.parameters
72
- node.parameters.parameters&.requireds&.each do |param|
73
- @params.add(param.name.to_sym)
74
- end
75
- end
76
-
77
- visit(node.body)
78
- end
79
-
80
- def visit_lambda(node)
81
- visit_block(node)
82
- end
83
-
84
- def visit_local_variable_write(node)
85
- name = node.name.to_sym
86
- value = visit(node.value)
87
-
88
- if @declared_vars.include?(name) || @params.include?(name)
89
- IR::Assignment.new(IR::VarRef.new(name), value)
90
- else
91
- @declared_vars.add(name)
92
- IR::VarDecl.new(name, value)
93
- end
94
- end
95
-
96
- def visit_local_variable_read(node)
97
- name = node.name.to_sym
98
- type = infer_param_type(name)
99
- IR::VarRef.new(name, type)
100
- end
101
-
102
- def visit_integer(node)
103
- IR::Literal.new(node.value.to_f, :float)
104
- end
105
-
106
- def visit_float(node)
107
- IR::Literal.new(node.value, :float)
108
- end
109
-
110
- def visit_rational(node)
111
- IR::Literal.new(node.value.to_f, :float)
112
- end
113
-
114
- def visit_true(node)
115
- IR::BoolLiteral.new(true)
116
- end
117
-
118
- def visit_false(node)
119
- IR::BoolLiteral.new(false)
87
+ def visit_with_scoped_vars(node, params: [])
88
+ @scope_context.with_scope(params: params) { visit(node) }
120
89
  end
121
90
 
122
- def visit_parentheses(node)
123
- inner = visit(node.body)
124
- if inner.is_a?(IR::Block) && inner.statements.length == 1
125
- inner = inner.statements.first
126
- end
127
- IR::Parenthesized.new(inner)
128
- end
129
-
130
- def visit_call(node)
131
- method_name = node.name.to_s
132
- receiver = visit(node.receiver) if node.receiver
133
- args = node.arguments&.arguments&.map { |arg| visit(arg) } || []
134
-
135
- if !receiver && args.empty? && @params.include?(method_name.to_sym)
136
- type = infer_param_type(method_name.to_sym)
137
- return IR::VarRef.new(method_name.to_sym, type)
138
- end
139
-
140
- if receiver && args.empty? && !node.arguments
141
- if Builtins.single_component_field?(method_name)
142
- return IR::FieldAccess.new(receiver, method_name, :float)
143
- elsif Builtins.swizzle?(method_name)
144
- type = Builtins.swizzle_type(method_name)
145
- return IR::Swizzle.new(receiver, method_name, type)
146
- else
147
- return IR::FieldAccess.new(receiver, method_name)
148
- end
149
- end
150
-
151
- if BINARY_OPERATORS.include?(method_name) && receiver && args.length == 1
152
- return IR::BinaryOp.new(method_name, receiver, args.first)
153
- end
154
-
155
- if method_name == "-@" && receiver
156
- return IR::UnaryOp.new("-", receiver)
157
- end
158
- if method_name == "!" && args.length == 1
159
- return IR::UnaryOp.new("!", args.first)
160
- end
161
-
162
- if method_name == "[]" && receiver && args.length == 1
163
- return IR::ArrayIndex.new(receiver, args.first)
164
- end
165
-
166
- IR::FuncCall.new(method_name.to_sym, args, receiver)
167
- end
168
-
169
- def visit_if(node)
170
- condition = visit(node.predicate)
171
- then_branch = visit_with_scoped_vars(node.statements)
172
- else_branch = node.subsequent ? visit_with_scoped_vars(node.subsequent) : nil
173
-
174
- IR::IfStatement.new(condition, then_branch, else_branch)
175
- end
176
-
177
- def visit_with_scoped_vars(node)
178
- saved_vars = @declared_vars.dup
179
- result = visit(node)
180
- @declared_vars = saved_vars
181
- result
182
- end
183
-
184
- def visit_else(node)
185
- visit(node.statements)
186
- end
187
-
188
- def visit_elsif(node)
189
- visit_if(node)
190
- end
191
-
192
- def visit_if_node(node)
193
- visit_if(node)
194
- end
195
-
196
- def visit_unless(node)
197
- condition = IR::UnaryOp.new("!", visit(node.predicate))
198
- then_branch = visit(node.statements)
199
- else_branch = node.else_clause ? visit(node.else_clause) : nil
200
-
201
- IR::IfStatement.new(condition, then_branch, else_branch)
202
- end
203
-
204
- def visit_return(node)
205
- expr = node.arguments ? visit(node.arguments.arguments.first) : nil
206
- IR::Return.new(expr)
207
- end
208
-
209
- def visit_range(node)
210
- [visit(node.left), visit(node.right)]
211
- end
212
-
213
- def visit_for(node)
214
- var_name = node.index.name.to_sym
215
- range = visit(node.collection)
216
- body = visit(node.statements)
217
-
218
- IR::ForLoop.new(var_name, range[0], range[1], body)
219
- end
220
-
221
- def visit_call_with_block(node)
222
- call_node = node
223
- method_name = call_node.name.to_s
224
-
225
- if method_name == "times" && call_node.receiver
226
- count = visit(call_node.receiver)
227
- block = visit(call_node.block)
228
-
229
- var_name = :i
230
- if call_node.block&.parameters&.parameters&.requireds&.any?
231
- var_name = call_node.block.parameters.parameters.requireds.first.name.to_sym
232
- end
233
-
234
- IR::ForLoop.new(var_name, IR::Literal.new(0, :int), count, block)
235
- else
236
- visit_call(node)
237
- end
238
- end
239
-
240
- def visit_and(node)
241
- left = visit(node.left)
242
- right = visit(node.right)
243
- IR::BinaryOp.new("&&", left, right, :bool)
244
- end
245
-
246
- def visit_or(node)
247
- left = visit(node.left)
248
- right = visit(node.right)
249
- IR::BinaryOp.new("||", left, right, :bool)
250
- end
251
-
252
- def visit_not(node)
253
- operand = visit(node.expression)
254
- IR::UnaryOp.new("!", operand, :bool)
255
- end
256
-
257
- def visit_while(node)
258
- condition = visit(node.predicate)
259
- body = visit(node.statements)
260
- IR::WhileLoop.new(condition, body)
261
- end
262
-
263
- def visit_break(node)
264
- IR::Break.new
265
- end
266
-
267
- def visit_constant_read(node)
268
- name = node.name.to_s
269
- if %w[PI TAU].include?(name)
270
- IR::Constant.new(name.to_sym, :float)
91
+ def infer_param_type(name)
92
+ case name
93
+ when :frag_coord, :resolution
94
+ :vec2
95
+ when :u
96
+ :uniforms
271
97
  else
272
- IR::VarRef.new(name.to_sym)
273
- end
274
- end
275
-
276
- def visit_def(node)
277
- name = node.name.to_sym
278
- params = []
279
-
280
- if node.parameters
281
- node.parameters.requireds&.each do |param|
282
- params << param.name.to_sym
283
- end
284
- end
285
-
286
- saved_params = @params.dup
287
- saved_declared_vars = @declared_vars.dup
288
- @declared_vars = Set.new
289
- params.each { |p| @params.add(p) }
290
-
291
- body = visit(node.body)
292
-
293
- @params = saved_params
294
- @declared_vars = saved_declared_vars
295
-
296
- IR::FunctionDefinition.new(name, params, body)
297
- end
298
-
299
- def visit_array(node)
300
- elements = node.elements.map { |elem| visit(elem) }
301
- IR::ArrayLiteral.new(elements)
302
- end
303
-
304
- def visit_index(node)
305
- array = visit(node.receiver)
306
- index = visit(node.arguments.arguments.first)
307
- IR::ArrayIndex.new(array, index)
308
- end
309
-
310
- def visit_constant_path(node)
311
- path_parts = []
312
- current = node
313
- while current.is_a?(::Prism::ConstantPathNode)
314
- path_parts.unshift(current.name.to_s)
315
- current = current.parent
98
+ nil
316
99
  end
317
- path_parts.unshift(current.name.to_s) if current.respond_to?(:name)
318
-
319
- full_name = path_parts.join("_")
320
- IR::VarRef.new(full_name.to_sym)
321
- end
322
-
323
- def visit_global_variable_read(node)
324
- name = node.name.to_s.sub(/^\$/, "").to_sym
325
- IR::VarRef.new(name)
326
100
  end
327
101
 
328
- def visit_global_variable_write(node)
329
- name = node.name.to_s.sub(/^\$/, "").to_sym
330
- value = visit(node.value)
102
+ def extract_required_params(node)
103
+ return [] unless node
331
104
 
332
- IR::GlobalDecl.new(name, value, is_static: true)
105
+ node.requireds&.map { |param| param.name.to_sym } || []
333
106
  end
334
107
 
335
- def visit_constant_write(node)
336
- name = node.name.to_sym
337
- value = visit(node.value)
108
+ def extract_block_params(node)
109
+ return [] unless node&.parameters
338
110
 
339
- IR::GlobalDecl.new(name, value, is_const: true, is_static: true)
111
+ extract_required_params(node.parameters.parameters)
340
112
  end
341
113
 
342
- def visit_multi_write(node)
343
- targets = node.lefts.map do |target|
344
- name = target.name.to_sym
345
- @declared_vars.add(name)
346
- IR::VarRef.new(name)
347
- end
348
-
349
- value = visit(node.value)
350
- IR::MultipleAssignment.new(targets, value)
114
+ def parameter_reference?(name)
115
+ @scope_context.parameter?(name)
351
116
  end
352
117
 
353
- def visit_local_variable_target(node)
354
- name = node.name.to_sym
355
- @declared_vars.add(name)
356
- IR::VarRef.new(name)
118
+ def known_variable?(name)
119
+ @scope_context.known_variable?(name)
357
120
  end
358
121
 
359
- def infer_param_type(name)
360
- case name
361
- when :frag_coord, :resolution
362
- :vec2
363
- when :u
364
- :uniforms
365
- else
366
- nil
367
- end
122
+ def declare_variable(name)
123
+ @scope_context.declare(name)
368
124
  end
369
125
  end
370
126
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module Prism
5
+ module Builtins
6
+ module FunctionRegistry
7
+ ALL_TARGETS = %i[c glsl wgsl msl].freeze
8
+ META_TYPES = %i[any same first second third].freeze
9
+
10
+ FUNCTIONS = {
11
+ vec2: { args: %i[any any], returns: :vec2, variadic: true, min_args: 1 },
12
+ vec3: { args: %i[any any any], returns: :vec3, variadic: true, min_args: 1 },
13
+ vec4: { args: %i[any any any any], returns: :vec4, variadic: true, min_args: 1 },
14
+
15
+ mat2: { args: %i[any any any any], returns: :mat2, variadic: true, min_args: 1 },
16
+ mat3: { args: %i[any any any any any any any any any], returns: :mat3, variadic: true, min_args: 1 },
17
+ mat4: { args: %i[any any any any any any any any any any any any any any any any], returns: :mat4, variadic: true, min_args: 1 },
18
+
19
+ sin: { args: [:float], returns: :float },
20
+ cos: { args: [:float], returns: :float },
21
+ tan: { args: [:float], returns: :float },
22
+ asin: { args: [:float], returns: :float },
23
+ acos: { args: [:float], returns: :float },
24
+ atan: { args: %i[float float], returns: :float, variadic: true, min_args: 1 },
25
+ atan2: { args: %i[float float], returns: :float },
26
+
27
+ pow: { args: %i[float float], returns: :float },
28
+ exp: { args: [:float], returns: :float },
29
+ log: { args: [:float], returns: :float },
30
+ sqrt: { args: [:any], returns: :same },
31
+
32
+ abs: { args: [:any], returns: :same },
33
+ sign: { args: [:any], returns: :same },
34
+ floor: { args: [:any], returns: :same },
35
+ ceil: { args: [:any], returns: :same },
36
+ fract: { args: [:any], returns: :same },
37
+ mod: { args: %i[any float], returns: :first },
38
+ min: { args: %i[any any], returns: :first },
39
+ max: { args: %i[any any], returns: :first },
40
+ clamp: { args: %i[any any any], returns: :first },
41
+ mix: { args: %i[any any float], returns: :first },
42
+ step: { args: %i[float any], returns: :second },
43
+ smoothstep: { args: %i[float float any], returns: :third },
44
+
45
+ length: { args: [:any], returns: :float },
46
+ distance: { args: %i[any any], returns: :float },
47
+ dot: { args: %i[any any], returns: :float },
48
+ cross: { args: %i[vec3 vec3], returns: :vec3 },
49
+ normalize: { args: [:any], returns: :same },
50
+ reflect: { args: %i[any any], returns: :first },
51
+ refract: { args: %i[any any float], returns: :first },
52
+
53
+ hash21: { args: [:vec2], returns: :float },
54
+ hash22: { args: [:vec2], returns: :vec2 },
55
+
56
+ lessThan: { args: %i[any any], returns: :bool },
57
+ lessThanEqual: { args: %i[any any], returns: :bool },
58
+ greaterThan: { args: %i[any any], returns: :bool },
59
+ greaterThanEqual: { args: %i[any any], returns: :bool },
60
+ equal: { args: %i[any any], returns: :bool },
61
+ notEqual: { args: %i[any any], returns: :bool },
62
+
63
+ inverse: { args: [:any], returns: :same, targets: %i[glsl wgsl msl] },
64
+ transpose: { args: [:any], returns: :same, targets: %i[glsl wgsl msl] },
65
+ determinant: { args: [:any], returns: :float, targets: %i[glsl wgsl msl] },
66
+
67
+ texture2D: { args: %i[sampler2D vec2], returns: :vec4 },
68
+ texture: { args: %i[sampler2D vec2], returns: :vec4 },
69
+ textureLod: { args: %i[sampler2D vec2 float], returns: :vec4 }
70
+ }.freeze
71
+
72
+ module_function
73
+
74
+ def function?(name)
75
+ FUNCTIONS.key?(name.to_sym)
76
+ end
77
+
78
+ def function_signature(name)
79
+ FUNCTIONS[name.to_sym]
80
+ end
81
+
82
+ def supported_on_target?(name, target)
83
+ signature = function_signature(name)
84
+ return false unless signature
85
+
86
+ Array(signature[:targets] || ALL_TARGETS).include?(target.to_sym)
87
+ end
88
+
89
+ def explicit_types(name)
90
+ signature = function_signature(name)
91
+ return [] unless signature
92
+
93
+ ([signature[:returns]] + Array(signature[:args])).filter_map do |type|
94
+ type if explicit_type?(type)
95
+ end.uniq
96
+ end
97
+
98
+ def resolve_return_type(rule, arg_types)
99
+ case rule
100
+ when :same then arg_types.first
101
+ when :first then arg_types.first
102
+ when :second then arg_types[1]
103
+ when :third then arg_types[2]
104
+ when Symbol then rule
105
+ end
106
+ end
107
+
108
+ def explicit_type?(type)
109
+ type.is_a?(Symbol) && !META_TYPES.include?(type)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module Prism
5
+ module Builtins
6
+ module OperatorRules
7
+ BINARY_OPERATORS = {
8
+ "+" => :arithmetic,
9
+ "-" => :arithmetic,
10
+ "*" => :arithmetic,
11
+ "/" => :arithmetic,
12
+ "%" => :arithmetic,
13
+
14
+ "==" => :comparison,
15
+ "!=" => :comparison,
16
+ "<" => :comparison,
17
+ ">" => :comparison,
18
+ "<=" => :comparison,
19
+ ">=" => :comparison,
20
+
21
+ "&&" => :logical,
22
+ "||" => :logical
23
+ }.freeze
24
+
25
+ UNARY_OPERATORS = {
26
+ "-" => :negate,
27
+ "!" => :not
28
+ }.freeze
29
+
30
+ module_function
31
+
32
+ def binary_operator?(op)
33
+ BINARY_OPERATORS.key?(op.to_s)
34
+ end
35
+
36
+ def unary_operator?(op)
37
+ UNARY_OPERATORS.key?(op.to_s)
38
+ end
39
+
40
+ def binary_op_result_type(op, left_type, right_type)
41
+ op_kind = BINARY_OPERATORS[op.to_s]
42
+
43
+ case op_kind
44
+ when :comparison, :logical
45
+ :bool
46
+ when :arithmetic
47
+ if matrix_type?(left_type) && vector_type?(right_type)
48
+ matrix_vector_result(left_type)
49
+ elsif vector_type?(left_type) && matrix_type?(right_type)
50
+ matrix_vector_result(right_type)
51
+ elsif matrix_type?(left_type) && matrix_type?(right_type)
52
+ left_type
53
+ elsif matrix_type?(left_type) && scalar_type?(right_type)
54
+ left_type
55
+ elsif scalar_type?(left_type) && matrix_type?(right_type)
56
+ right_type
57
+ elsif vector_type?(left_type) && vector_type?(right_type)
58
+ left_type
59
+ elsif vector_type?(left_type) && scalar_type?(right_type)
60
+ left_type
61
+ elsif scalar_type?(left_type) && vector_type?(right_type)
62
+ right_type
63
+ else
64
+ scalar_arithmetic_result_type(op, left_type, right_type)
65
+ end
66
+ end
67
+ end
68
+
69
+ def vector_type?(type)
70
+ %i[vec2 vec3 vec4].include?(type)
71
+ end
72
+
73
+ def matrix_type?(type)
74
+ %i[mat2 mat3 mat4].include?(type)
75
+ end
76
+
77
+ def scalar_type?(type)
78
+ %i[float int].include?(type)
79
+ end
80
+
81
+ def scalar_arithmetic_result_type(op, left_type, right_type)
82
+ return :float unless scalar_type?(left_type) && scalar_type?(right_type)
83
+ return :float if op.to_s == "/"
84
+ return :int if left_type == :int && right_type == :int
85
+
86
+ :float
87
+ end
88
+
89
+ def matrix_vector_result(matrix_type)
90
+ case matrix_type
91
+ when :mat2 then :vec2
92
+ when :mat3 then :vec3
93
+ when :mat4 then :vec4
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module Prism
5
+ module Builtins
6
+ module SwizzleRules
7
+ SWIZZLE_COMPONENTS = {
8
+ "x" => 0, "r" => 0, "s" => 0,
9
+ "y" => 1, "g" => 1, "t" => 1,
10
+ "z" => 2, "b" => 2, "p" => 2,
11
+ "w" => 3, "a" => 3, "q" => 3
12
+ }.freeze
13
+
14
+ SINGLE_COMPONENT_FIELDS = %w[x y z w r g b a s t p q].freeze
15
+ SWIZZLE_PATTERNS = /\A[xyzwrgba]{2,4}\z/
16
+
17
+ module_function
18
+
19
+ def single_component_field?(name)
20
+ SINGLE_COMPONENT_FIELDS.include?(name.to_s)
21
+ end
22
+
23
+ def swizzle?(name)
24
+ name.to_s.match?(SWIZZLE_PATTERNS)
25
+ end
26
+
27
+ def swizzle_type(components)
28
+ case components.length
29
+ when 2 then :vec2
30
+ when 3 then :vec3
31
+ when 4 then :vec4
32
+ else :float
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end