rlsl 0.1.1 → 1.0.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/CHANGELOG.md +13 -2
  4. data/README.md +101 -26
  5. data/Rakefile +7 -0
  6. data/lib/rlsl/base_translator/call_parser.rb +79 -0
  7. data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
  8. data/lib/rlsl/base_translator/code_scanner.rb +140 -0
  9. data/lib/rlsl/base_translator.rb +182 -64
  10. data/lib/rlsl/code_generator/math_prelude.rb +172 -0
  11. data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +137 -0
  12. data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
  13. data/lib/rlsl/code_generator/template_context.rb +44 -0
  14. data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
  15. data/lib/rlsl/code_generator.rb +30 -202
  16. data/lib/rlsl/compiled_shader.rb +5 -13
  17. data/lib/rlsl/errors.rb +29 -0
  18. data/lib/rlsl/function_context.rb +31 -14
  19. data/lib/rlsl/glsl/translator.rb +35 -41
  20. data/lib/rlsl/msl/shader.rb +34 -46
  21. data/lib/rlsl/msl/translator.rb +38 -34
  22. data/lib/rlsl/msl/uniform_buffer_packer.rb +72 -0
  23. data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +137 -0
  24. data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +92 -0
  25. data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +172 -0
  26. data/lib/rlsl/prism/ast_visitor/scope_context.rb +48 -0
  27. data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
  28. data/lib/rlsl/prism/ast_visitor.rb +100 -286
  29. data/lib/rlsl/prism/builtins/function_registry.rb +131 -0
  30. data/lib/rlsl/prism/builtins/operator_rules.rb +123 -0
  31. data/lib/rlsl/prism/builtins/swizzle_rules.rb +47 -0
  32. data/lib/rlsl/prism/builtins.rb +39 -148
  33. data/lib/rlsl/prism/compilation_unit.rb +7 -0
  34. data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +126 -0
  35. data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +134 -0
  36. data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +108 -0
  37. data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +95 -0
  38. data/lib/rlsl/prism/emitters/base_emitter.rb +120 -414
  39. data/lib/rlsl/prism/emitters/c_emitter.rb +165 -112
  40. data/lib/rlsl/prism/emitters/glsl_emitter.rb +63 -50
  41. data/lib/rlsl/prism/emitters/msl_emitter.rb +67 -52
  42. data/lib/rlsl/prism/emitters/target_emitter.rb +124 -0
  43. data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
  44. data/lib/rlsl/prism/emitters/wgsl_emitter.rb +217 -58
  45. data/lib/rlsl/prism/errors.rb +9 -0
  46. data/lib/rlsl/prism/ir/control_flow.rb +85 -0
  47. data/lib/rlsl/prism/ir/definitions.rb +82 -0
  48. data/lib/rlsl/prism/ir/expressions.rb +201 -0
  49. data/lib/rlsl/prism/ir/node.rb +21 -0
  50. data/lib/rlsl/prism/ir/nodes.rb +4 -371
  51. data/lib/rlsl/prism/ir/traversal.rb +66 -0
  52. data/lib/rlsl/prism/mutation_analyzer.rb +30 -0
  53. data/lib/rlsl/prism/node_traversal.rb +41 -0
  54. data/lib/rlsl/prism/parameter_list.rb +46 -0
  55. data/lib/rlsl/prism/return_flow_validator.rb +73 -0
  56. data/lib/rlsl/prism/source_extractor/block_locator.rb +50 -0
  57. data/lib/rlsl/prism/source_extractor.rb +19 -137
  58. data/lib/rlsl/prism/source_unit/parser.rb +88 -0
  59. data/lib/rlsl/prism/source_unit.rb +42 -0
  60. data/lib/rlsl/prism/target_capability_validator.rb +110 -0
  61. data/lib/rlsl/prism/transpiler.rb +99 -59
  62. data/lib/rlsl/prism/type_inference/call_type_resolver.rb +59 -0
  63. data/lib/rlsl/prism/type_inference/call_validator.rb +75 -0
  64. data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +106 -0
  65. data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +147 -0
  66. data/lib/rlsl/prism/type_inference/definition_inferer.rb +56 -0
  67. data/lib/rlsl/prism/type_inference/expression_inferer.rb +96 -0
  68. data/lib/rlsl/prism/type_inference/field_type_resolver.rb +38 -0
  69. data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
  70. data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
  71. data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
  72. data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
  73. data/lib/rlsl/prism/type_inference.rb +120 -249
  74. data/lib/rlsl/runtime_shader.rb +47 -0
  75. data/lib/rlsl/shader_builder/build_service.rb +104 -0
  76. data/lib/rlsl/shader_builder/native_extension_compiler.rb +97 -0
  77. data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
  78. data/lib/rlsl/shader_builder/source_resolver.rb +109 -0
  79. data/lib/rlsl/shader_builder.rb +60 -111
  80. data/lib/rlsl/shader_name.rb +18 -0
  81. data/lib/rlsl/types/catalog.rb +47 -0
  82. data/lib/rlsl/types/target_resolver.rb +15 -0
  83. data/lib/rlsl/types/type_spec.rb +167 -0
  84. data/lib/rlsl/types/value_normalizer.rb +86 -0
  85. data/lib/rlsl/types.rb +9 -31
  86. data/lib/rlsl/uniform_context.rb +22 -11
  87. data/lib/rlsl/version.rb +1 -1
  88. data/lib/rlsl/wgsl/translator.rb +46 -39
  89. data/lib/rlsl/wgsl/uniform_layout.rb +25 -0
  90. data/lib/rlsl.rb +38 -15
  91. metadata +76 -11
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "target_profile"
4
+
5
+ module RLSL
6
+ module Prism
7
+ module Emitters
8
+ class TargetEmitter < BaseEmitter
9
+ protected
10
+
11
+ def type_name(type)
12
+ profile.type_map[type&.to_sym] || default_type_name
13
+ end
14
+
15
+ def emit_func_call(node)
16
+ resolved_call = emit_profile_call(node)
17
+ return resolved_call if resolved_call
18
+
19
+ name = node.name.to_sym
20
+
21
+ constructor_name = profile.vector_constructors[name] || profile.matrix_constructors[name]
22
+ if constructor_name
23
+ return emit_named_call(constructor_name, node.args, expected_types: Array.new(node.args.length, :float))
24
+ end
25
+
26
+ texture_call = emit_texture_call(name, node)
27
+ return texture_call if texture_call
28
+
29
+ math_function = profile.math_functions[name]
30
+ return emit_named_call(math_function, node.args, expected_types: node.expected_arg_types) if math_function
31
+
32
+ emit_named_call(name, node.args, receiver: node.receiver, expected_types: node.expected_arg_types)
33
+ end
34
+
35
+ def emit_binary_op(node)
36
+ resolved_binary_op = emit_profile_binary_op(node)
37
+ return resolved_binary_op if resolved_binary_op
38
+ return emit_promoted_binary_op(node) if needs_float_promotion?(node)
39
+
40
+ left = emit_with_precedence(node.left, node.operator, side: :left)
41
+ right = emit_with_precedence(node.right, node.operator, side: :right)
42
+ "#{left} #{node.operator} #{right}"
43
+ end
44
+
45
+ def emit_ternary(node)
46
+ then_expr = emit_typed_argument(node.then_expr, node.type)
47
+ else_expr = emit_typed_argument(node.else_expr, node.type)
48
+ "(#{emit(node.condition)} ? #{then_expr} : #{else_expr})"
49
+ end
50
+
51
+ def emit_assignment(node)
52
+ "#{emit(node.target)} = #{emit_typed_argument(node.value, node.target.type)}"
53
+ end
54
+
55
+ def default_type_name
56
+ profile.default_type_name
57
+ end
58
+
59
+ def emit_texture_call(name, node)
60
+ return unless profile.texture_functions.key?(name)
61
+
62
+ emit_named_call(profile.texture_functions[name], node.args, expected_types: node.expected_arg_types)
63
+ end
64
+
65
+ def emit_named_call(name, args, receiver: nil, expected_types: [])
66
+ rendered_args = []
67
+ rendered_args << emit_typed_argument(receiver, expected_types.first) if receiver
68
+ offset = receiver ? 1 : 0
69
+ rendered_args.concat(args.each_with_index.map do |arg, index|
70
+ emit_typed_argument(arg, expected_types[index + offset])
71
+ end)
72
+ "#{name}(#{rendered_args.join(', ')})"
73
+ end
74
+
75
+ def emit_typed_argument(node, expected_type)
76
+ return emit_float_operand(node) if expected_type == :float
77
+
78
+ emit(node)
79
+ end
80
+
81
+ def needs_float_promotion?(node)
82
+ types = [node.left.type, node.right.type]
83
+ return true if node.operator == "/" && types.all?(:int)
84
+
85
+ types.include?(:int) && types.any? { |type| type == :float || Builtins.vector_type?(type) || Builtins.matrix_type?(type) }
86
+ end
87
+
88
+ def emit_promoted_binary_op(node)
89
+ left = emit_promoted_operand(node.left, node.operator, :left)
90
+ right = emit_promoted_operand(node.right, node.operator, :right)
91
+ "#{left} #{node.operator} #{right}"
92
+ end
93
+
94
+ def emit_float_operand(node)
95
+ return emit(node) unless node.type == :int
96
+
97
+ "#{type_name(:float)}(#{emit(node)})"
98
+ end
99
+
100
+ def emit_promoted_operand(node, operator, side)
101
+ return emit_float_operand(node) if node.type == :int
102
+
103
+ emit_with_precedence(node, operator, side: side)
104
+ end
105
+
106
+ def emit_profile_call(node)
107
+ return unless profile.call_resolver
108
+
109
+ send(profile.call_resolver, node)
110
+ end
111
+
112
+ def emit_profile_binary_op(node)
113
+ return unless profile.binary_op_resolver
114
+
115
+ send(profile.binary_op_resolver, node)
116
+ end
117
+
118
+ def profile
119
+ self.class::PROFILE
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module Prism
5
+ module Emitters
6
+ TargetProfile = Struct.new(
7
+ :type_map,
8
+ :vector_constructors,
9
+ :matrix_constructors,
10
+ :texture_functions,
11
+ :default_type_name,
12
+ :math_functions,
13
+ :vector_ops,
14
+ :call_resolver,
15
+ :binary_op_resolver,
16
+ keyword_init: true
17
+ ) do
18
+ def initialize(**attributes)
19
+ super(
20
+ type_map: attributes.fetch(:type_map, {}).freeze,
21
+ vector_constructors: attributes.fetch(:vector_constructors, {}).freeze,
22
+ matrix_constructors: attributes.fetch(:matrix_constructors, {}).freeze,
23
+ texture_functions: attributes.fetch(:texture_functions, {}).freeze,
24
+ default_type_name: attributes.fetch(:default_type_name, "float"),
25
+ math_functions: attributes.fetch(:math_functions, {}).freeze,
26
+ vector_ops: attributes.fetch(:vector_ops, {}).freeze,
27
+ call_resolver: attributes[:call_resolver],
28
+ binary_op_resolver: attributes[:binary_op_resolver]
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -3,85 +3,244 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class WGSLEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "f32",
9
- int: "i32",
10
- bool: "bool",
11
- vec2: "vec2<f32>",
12
- vec3: "vec3<f32>",
13
- vec4: "vec4<f32>",
14
- mat2: "mat2x2<f32>",
15
- mat3: "mat3x3<f32>",
16
- mat4: "mat4x4<f32>",
17
- sampler2D: "texture_2d<f32>"
18
- }.freeze
19
-
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "vec2<f32>",
22
- vec3: "vec3<f32>",
23
- vec4: "vec4<f32>"
24
- }.freeze
25
-
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "mat2x2<f32>",
28
- mat3: "mat3x3<f32>",
29
- mat4: "mat4x4<f32>"
30
- }.freeze
31
-
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "textureSample",
34
- texture: "textureSample",
35
- textureLod: "textureSampleLevel"
36
- }.freeze
6
+ class WGSLEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "f32",
10
+ int: "i32",
11
+ bool: "bool",
12
+ vec2: "vec2<f32>",
13
+ vec3: "vec3<f32>",
14
+ vec4: "vec4<f32>",
15
+ mat2: "mat2x2<f32>",
16
+ mat3: "mat3x3<f32>",
17
+ mat4: "mat4x4<f32>",
18
+ sampler2D: "texture_2d<f32>"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "vec2<f32>",
22
+ vec3: "vec3<f32>",
23
+ vec4: "vec4<f32>"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "mat2x2<f32>",
27
+ mat3: "mat3x3<f32>",
28
+ mat4: "mat4x4<f32>"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "textureSample",
32
+ texture: "textureSample",
33
+ textureLod: "textureSampleLevel"
34
+ },
35
+ default_type_name: "f32"
36
+ ).freeze
37
37
 
38
38
  protected
39
39
 
40
- def type_name(type)
41
- TYPE_MAP[type&.to_sym] || "f32"
42
- end
43
-
44
40
  def emit_var_decl(node)
45
41
  type = type_name(node.type || :float)
42
+ return "var #{node.name}: #{type}" unless node.initializer
43
+
44
+ if node.initializer.is_a?(IR::ArrayLiteral)
45
+ element_type_symbol = TypeShapes.element_type(node.type) || :float
46
+ element_type = type_name(element_type_symbol)
47
+ size = node.initializer.elements.length
48
+ values = node.initializer.elements.map do |element|
49
+ emit_typed_argument(element, element_type_symbol)
50
+ end.join(", ")
51
+ return "var #{node.name}: array<#{element_type}, #{size}> = array<#{element_type}, #{size}>(#{values})"
52
+ end
53
+
46
54
  value = emit(node.initializer)
47
- "let #{node.name}: #{type} = #{value}"
55
+ binding = node.mutable ? "var" : "let"
56
+ "#{binding} #{node.name}: #{type} = #{value}"
57
+ end
58
+
59
+ def emit_for_loop(node)
60
+ variable = node.variable
61
+ counter = loop_variable_mutated?(node) ? next_temporary_name("i") : variable
62
+ start_val = emit(node.range_start)
63
+ end_val = emit(node.range_end)
64
+ body = emit_indented_block(node.body)
65
+ body = "#{indent} var #{variable}: i32 = #{counter};\n#{body}" if counter != variable
66
+
67
+ comparison = node.exclude_end ? "<" : "<="
68
+ return "for (var #{counter}: i32 = #{start_val}; #{counter} #{comparison} #{end_val}; #{counter}++) {\n#{body}#{indent}}" if node.range_end.is_a?(IR::Literal)
69
+
70
+ bound = next_temporary_name("end")
71
+ "let #{bound}: i32 = #{end_val};\n#{indent}for (var #{counter}: i32 = #{start_val}; #{counter} #{comparison} #{bound}; #{counter}++) {\n#{body}#{indent}}"
72
+ end
73
+
74
+ def emit_ternary(_node)
75
+ raise TargetCapabilityError,
76
+ "WGSL conditional expressions cannot be emitted without eager branch evaluation"
77
+ end
78
+
79
+ def emit_binary_op(node)
80
+ if node.operator == "%" && node.type == :float
81
+ return "rlsl_mod(#{emit_float_operand(node.left)}, #{emit_float_operand(node.right)})"
82
+ end
83
+
84
+ super
48
85
  end
49
86
 
50
87
  def emit_func_call(node)
51
- name = node.name.to_sym
88
+ if node.name.to_sym == :mod
89
+ args = node.args.map { |argument| emit_float_operand(argument) }
90
+ return "rlsl_mod(#{args.join(', ')})"
91
+ end
92
+ if node.name.to_sym == :atan && node.args.length == 2
93
+ return emit_named_call("atan2", node.args, expected_types: node.expected_arg_types)
94
+ end
95
+
96
+ super
97
+ end
98
+
99
+ def emit_function_definition(node)
100
+ name = node.name
101
+ mutable_params = mutated_parameters(node)
102
+ initializers = []
103
+ sampler_params = {}
104
+ params = node.params.flat_map do |param|
105
+ type = node.param_types[param] || :float
106
+ if type == :sampler2D
107
+ if mutable_params.include?(param)
108
+ raise TargetCapabilityError, "WGSL texture parameter #{param} cannot be reassigned"
109
+ end
110
+
111
+ sampler_params[param] = next_temporary_name("sampler")
112
+ ["#{param}: #{type_name(type)}", "#{sampler_params[param]}: sampler"]
113
+ elsif mutable_params.include?(param)
114
+ argument = next_temporary_name("param")
115
+ initializers << "#{indent} var #{param}: #{type_name(type)} = #{argument};\n"
116
+ ["#{argument}: #{type_name(type)}"]
117
+ else
118
+ ["#{param}: #{type_name(type)}"]
119
+ end
120
+ end.join(", ")
121
+
122
+ if node.return_type.is_a?(Array)
123
+ struct_def = emit_result_struct(name, node.return_type)
124
+ body = with_return_struct_name("#{name}_result") do
125
+ with_sampler_parameters(sampler_params) do
126
+ with_return_type(node.return_type) { emit_indented_block(node.body, needs_return: true) }
127
+ end
128
+ end
129
+
130
+ "#{struct_def}fn #{name}(#{params}) -> #{name}_result {\n#{initializers.join}#{body}#{indent}}\n"
131
+ else
132
+ return_type = type_name(node.return_type || :float)
133
+ body = with_sampler_parameters(sampler_params) do
134
+ with_return_type(node.return_type) { emit_indented_block(node.body, needs_return: true) }
135
+ end
52
136
 
53
- if VECTOR_CONSTRUCTORS.key?(name)
54
- args = node.args.map { |arg| emit(arg) }.join(", ")
55
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
137
+ "fn #{name}(#{params}) -> #{return_type} {\n#{initializers.join}#{body}#{indent}}\n"
56
138
  end
139
+ end
140
+
141
+ def emit_result_struct(func_name, types)
142
+ fields = types.each_with_index.map do |type, index|
143
+ "#{indent}v#{index}: #{type_name(type)},"
144
+ end.join("\n")
145
+ "struct #{func_name}_result {\n#{fields}\n}\n"
146
+ end
57
147
 
58
- if MATRIX_CONSTRUCTORS.key?(name)
59
- args = node.args.map { |arg| emit(arg) }.join(", ")
60
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
148
+ def emit_hoisted_declarations(node)
149
+ node.hoisted_variables.map do |name, type|
150
+ "#{indent}var #{name}: #{type_name(type || :float)};\n"
151
+ end.join
152
+ end
153
+
154
+ def emit_global_decl(node)
155
+ if node.initializer.is_a?(IR::ArrayLiteral)
156
+ element_type_symbol = node.element_type || TypeShapes.element_type(node.initializer.type) || :float
157
+ element_type = type_name(element_type_symbol)
158
+ size = node.array_size || node.initializer.elements.length
159
+ values = node.initializer.elements.map do |element|
160
+ emit_typed_argument(element, element_type_symbol)
161
+ end.join(", ")
162
+ prefix = node.is_const ? "const" : "var<private>"
163
+ return "#{prefix} #{node.name}: array<#{element_type}, #{size}> = array<#{element_type}, #{size}>(#{values})"
61
164
  end
62
165
 
63
- if TEXTURE_FUNCTIONS.key?(name)
64
- args = node.args.map { |arg| emit(arg) }.join(", ")
65
- return "#{TEXTURE_FUNCTIONS[name]}(#{args})"
166
+ prefix = node.is_const ? "const" : "var<private>"
167
+ "#{prefix} #{node.name}: #{type_name(node.type || :float)} = #{emit(node.initializer)}"
168
+ end
169
+
170
+ def emit_field_access(node)
171
+ return node.field.to_s if node.receiver.type == :uniforms && node.type == :sampler2D
172
+
173
+ code = super
174
+ return "(#{code} != 0)" if node.receiver.type == :uniforms && node.type == :bool
175
+
176
+ code
177
+ end
178
+
179
+ def emit_texture_call(name, node)
180
+ return unless profile.texture_functions.key?(name) && node.args.length >= 2
181
+
182
+ texture = emit(node.args[0])
183
+ sampler = sampler_name(node.args[0], texture)
184
+ uv = emit(node.args[1])
185
+ lod = node.args[2] ? emit_typed_argument(node.args[2], :float) : "0.0"
186
+ "textureSampleLevel(#{texture}, #{sampler}, #{uv}, #{lod})"
187
+ end
188
+
189
+ def emit_named_call(name, args, receiver: nil, expected_types: [])
190
+ arguments = receiver ? [receiver, *args] : args
191
+ rendered = arguments.each_with_index.flat_map do |argument, index|
192
+ if expected_types[index] == :sampler2D
193
+ texture = emit(argument)
194
+ [texture, sampler_name(argument, texture)]
195
+ else
196
+ [emit_typed_argument(argument, expected_types[index])]
197
+ end
66
198
  end
199
+ "#{name}(#{rendered.join(', ')})"
200
+ end
67
201
 
68
- args = node.args.map { |arg| emit(arg) }.join(", ")
69
- "#{name}(#{args})"
202
+ def emit_multiple_assignment_target(target, declaration)
203
+ declaration ? "var #{target.name}: #{type_name(target.type || :float)}" : target.name.to_s
70
204
  end
71
205
 
72
- def emit_for_loop(node)
73
- var = node.variable
74
- start_val = emit(node.range_start)
75
- end_val = emit(node.range_end)
76
- body = emit_indented_block(node.body)
206
+ def emit_temporary_declaration(type, name, value)
207
+ "let #{name}: #{type} = #{value}"
208
+ end
77
209
 
78
- "for (var #{var}: i32 = #{start_val}; #{var} < #{end_val}; #{var}++) {\n#{body}#{indent}}"
210
+ def emit_tuple_value(node)
211
+ expected_types = Array(current_return_type)
212
+ elements = node.elements.each_with_index.map do |element, index|
213
+ emit_typed_argument(element, expected_types[index])
214
+ end.join(", ")
215
+ "#{current_return_struct_name}(#{elements})"
79
216
  end
80
217
 
81
- def emit_binary_op(node)
82
- left = emit_with_precedence(node.left, node.operator)
83
- right = emit_with_precedence(node.right, node.operator)
84
- "#{left} #{node.operator} #{right}"
218
+ private
219
+
220
+ def mutated_parameters(node)
221
+ parameters = node.params.to_set
222
+ IR::Traversal.each(node.body).each_with_object(Set.new) do |current, names|
223
+ case current
224
+ when IR::Assignment
225
+ names.add(current.target.name) if current.target.is_a?(IR::VarRef) && parameters.include?(current.target.name)
226
+ when IR::MultipleAssignment
227
+ current.targets.each { |target| names.add(target.name) if parameters.include?(target.name) }
228
+ end
229
+ end
230
+ end
231
+
232
+ def with_sampler_parameters(parameters)
233
+ previous = @sampler_parameters
234
+ @sampler_parameters = parameters
235
+ yield
236
+ ensure
237
+ @sampler_parameters = previous
238
+ end
239
+
240
+ def sampler_name(argument, texture)
241
+ return @sampler_parameters[argument.name] if argument.is_a?(IR::VarRef) && @sampler_parameters&.key?(argument.name)
242
+
243
+ "#{texture}_sampler"
85
244
  end
86
245
  end
87
246
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../errors"
4
+
5
+ module RLSL
6
+ module Prism
7
+ class UnsupportedSyntaxError < RLSL::Error; end
8
+ end
9
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module Prism
5
+ module IR
6
+ class IfStatement < Node
7
+ attr_reader :condition, :then_branch, :else_branch, :hoisted_variables
8
+
9
+ visits :visit_if_statement
10
+
11
+ def initialize(condition, then_branch, else_branch = nil, type = nil, hoisted_variables: {})
12
+ super()
13
+ @condition = condition
14
+ @then_branch = then_branch
15
+ @else_branch = else_branch
16
+ @hoisted_variables = hoisted_variables
17
+ @type = type
18
+ end
19
+ end
20
+
21
+ class Return < Node
22
+ attr_reader :expression
23
+
24
+ visits :visit_return
25
+
26
+ def initialize(expression)
27
+ super()
28
+ @expression = expression
29
+ @type = expression&.type
30
+ end
31
+ end
32
+
33
+ class Assignment < Node
34
+ attr_reader :target, :value
35
+
36
+ visits :visit_assignment
37
+
38
+ def initialize(target, value)
39
+ super()
40
+ @target = target
41
+ @value = value
42
+ @type = value&.type
43
+ end
44
+ end
45
+
46
+ class ForLoop < Node
47
+ attr_reader :variable, :range_start, :range_end, :body, :exclude_end
48
+
49
+ visits :visit_for_loop
50
+
51
+ def initialize(variable, range_start, range_end, body, exclude_end: true)
52
+ super()
53
+ @variable = variable
54
+ @range_start = range_start
55
+ @range_end = range_end
56
+ @body = body
57
+ @exclude_end = exclude_end
58
+ @type = nil
59
+ end
60
+ end
61
+
62
+ class WhileLoop < Node
63
+ attr_reader :condition, :body
64
+
65
+ visits :visit_while_loop
66
+
67
+ def initialize(condition, body)
68
+ super()
69
+ @condition = condition
70
+ @body = body
71
+ @type = nil
72
+ end
73
+ end
74
+
75
+ class Break < Node
76
+ visits :visit_break
77
+
78
+ def initialize
79
+ super()
80
+ @type = nil
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RLSL
4
+ module Prism
5
+ module IR
6
+ class GlobalDecl < Node
7
+ attr_reader :name, :initializer
8
+ attr_accessor :is_const, :is_static, :array_size, :element_type
9
+
10
+ visits :visit_global_decl
11
+
12
+ def initialize(name, initializer, type: nil, is_const: false, is_static: true, array_size: nil, element_type: nil)
13
+ super()
14
+ @name = name
15
+ @initializer = initializer
16
+ @type = type
17
+ @is_const = is_const
18
+ @is_static = is_static
19
+ @array_size = array_size
20
+ @element_type = element_type
21
+ end
22
+ end
23
+
24
+ class FunctionDefinition < Node
25
+ attr_reader :name, :params, :body
26
+ attr_accessor :return_type, :param_types
27
+
28
+ visits :visit_function_definition
29
+
30
+ def initialize(name, params, body, return_type: nil, param_types: {})
31
+ super()
32
+ @name = name
33
+ @params = params
34
+ @body = body
35
+ @return_type = return_type
36
+ @param_types = param_types
37
+ @type = return_type
38
+ end
39
+ end
40
+
41
+ class MultipleAssignment < Node
42
+ attr_reader :targets, :value, :declarations
43
+
44
+ visits :visit_multiple_assignment
45
+
46
+ def initialize(targets, value, declarations: nil)
47
+ super()
48
+ @targets = targets
49
+ @value = value
50
+ @declarations = declarations || Array.new(targets.length, true)
51
+ @type = nil
52
+ end
53
+ end
54
+
55
+ class TupleType
56
+ attr_reader :types
57
+
58
+ def initialize(*types)
59
+ @types = types
60
+ end
61
+
62
+ def to_sym
63
+ :"tuple_#{types.map(&:to_s).join('_')}"
64
+ end
65
+
66
+ def ==(other)
67
+ other.is_a?(self.class) && types == other.types
68
+ end
69
+
70
+ alias eql? ==
71
+
72
+ def hash
73
+ [self.class, types].hash
74
+ end
75
+
76
+ def inspect
77
+ "#<#{self.class.name} #{types.inspect}>"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end