rlsl 1.0.0 → 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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/CHANGELOG.md +3 -1
  4. data/README.md +101 -28
  5. data/Rakefile +7 -0
  6. data/lib/rlsl/base_translator/call_parser.rb +13 -2
  7. data/lib/rlsl/base_translator/code_scanner.rb +39 -1
  8. data/lib/rlsl/base_translator.rb +24 -2
  9. data/lib/rlsl/code_generator/math_prelude.rb +103 -10
  10. data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +52 -12
  11. data/lib/rlsl/code_generator/template_context.rb +6 -3
  12. data/lib/rlsl/code_generator/uniform_struct_generator.rb +1 -1
  13. data/lib/rlsl/code_generator.rb +7 -4
  14. data/lib/rlsl/compiled_shader.rb +1 -1
  15. data/lib/rlsl/errors.rb +29 -0
  16. data/lib/rlsl/glsl/translator.rb +19 -6
  17. data/lib/rlsl/msl/shader.rb +27 -9
  18. data/lib/rlsl/msl/translator.rb +25 -4
  19. data/lib/rlsl/msl/uniform_buffer_packer.rb +11 -7
  20. data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +59 -18
  21. data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +22 -9
  22. data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +18 -14
  23. data/lib/rlsl/prism/ast_visitor/scope_context.rb +4 -0
  24. data/lib/rlsl/prism/ast_visitor.rb +78 -20
  25. data/lib/rlsl/prism/builtins/function_registry.rb +41 -24
  26. data/lib/rlsl/prism/builtins/operator_rules.rb +24 -0
  27. data/lib/rlsl/prism/builtins/swizzle_rules.rb +10 -1
  28. data/lib/rlsl/prism/builtins.rb +8 -0
  29. data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +51 -8
  30. data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +42 -12
  31. data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +20 -4
  32. data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +7 -1
  33. data/lib/rlsl/prism/emitters/base_emitter.rb +66 -5
  34. data/lib/rlsl/prism/emitters/c_emitter.rb +103 -7
  35. data/lib/rlsl/prism/emitters/glsl_emitter.rb +48 -0
  36. data/lib/rlsl/prism/emitters/msl_emitter.rb +43 -1
  37. data/lib/rlsl/prism/emitters/target_emitter.rb +56 -9
  38. data/lib/rlsl/prism/emitters/wgsl_emitter.rb +173 -18
  39. data/lib/rlsl/prism/errors.rb +9 -0
  40. data/lib/rlsl/prism/ir/control_flow.rb +6 -4
  41. data/lib/rlsl/prism/ir/definitions.rb +17 -2
  42. data/lib/rlsl/prism/ir/expressions.rb +5 -1
  43. data/lib/rlsl/prism/ir/node.rb +1 -1
  44. data/lib/rlsl/prism/ir/traversal.rb +6 -2
  45. data/lib/rlsl/prism/mutation_analyzer.rb +30 -0
  46. data/lib/rlsl/prism/node_traversal.rb +41 -0
  47. data/lib/rlsl/prism/parameter_list.rb +46 -0
  48. data/lib/rlsl/prism/return_flow_validator.rb +73 -0
  49. data/lib/rlsl/prism/source_extractor/block_locator.rb +23 -25
  50. data/lib/rlsl/prism/source_extractor.rb +7 -6
  51. data/lib/rlsl/prism/source_unit/parser.rb +42 -32
  52. data/lib/rlsl/prism/source_unit.rb +10 -10
  53. data/lib/rlsl/prism/target_capability_validator.rb +27 -2
  54. data/lib/rlsl/prism/transpiler.rb +40 -3
  55. data/lib/rlsl/prism/type_inference/call_type_resolver.rb +28 -4
  56. data/lib/rlsl/prism/type_inference/call_validator.rb +5 -1
  57. data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +45 -19
  58. data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +88 -7
  59. data/lib/rlsl/prism/type_inference/definition_inferer.rb +16 -1
  60. data/lib/rlsl/prism/type_inference/expression_inferer.rb +4 -0
  61. data/lib/rlsl/prism/type_inference/field_type_resolver.rb +23 -2
  62. data/lib/rlsl/prism/type_inference/scope_stack.rb +1 -1
  63. data/lib/rlsl/prism/type_inference/type_shapes.rb +3 -3
  64. data/lib/rlsl/prism/type_inference.rb +10 -5
  65. data/lib/rlsl/shader_builder/build_service.rb +34 -7
  66. data/lib/rlsl/shader_builder/native_extension_compiler.rb +50 -24
  67. data/lib/rlsl/shader_builder/shader_definition.rb +3 -3
  68. data/lib/rlsl/shader_builder/source_resolver.rb +19 -1
  69. data/lib/rlsl/shader_builder.rb +50 -10
  70. data/lib/rlsl/shader_name.rb +18 -0
  71. data/lib/rlsl/types/type_spec.rb +3 -3
  72. data/lib/rlsl/types/value_normalizer.rb +16 -11
  73. data/lib/rlsl/types.rb +0 -4
  74. data/lib/rlsl/uniform_context.rb +18 -1
  75. data/lib/rlsl/version.rb +1 -1
  76. data/lib/rlsl/wgsl/translator.rb +29 -6
  77. data/lib/rlsl/wgsl/uniform_layout.rb +25 -0
  78. data/lib/rlsl.rb +24 -3
  79. metadata +23 -11
@@ -3,6 +3,9 @@
3
3
  require "prism"
4
4
  require "set"
5
5
 
6
+ require_relative "node_traversal"
7
+ require_relative "errors"
8
+ require_relative "parameter_list"
6
9
  require_relative "ast_visitor/visitor_registry"
7
10
  require_relative "ast_visitor/scope_context"
8
11
  require_relative "ast_visitor/expression_visiting"
@@ -11,9 +14,8 @@ require_relative "ast_visitor/definition_visiting"
11
14
 
12
15
  module RLSL
13
16
  module Prism
14
- class UnsupportedSyntaxError < StandardError; end
15
-
16
17
  class ASTVisitor
18
+ MAX_AST_DEPTH = 512
17
19
  BINARY_OPERATORS = %w[+ - * / % == != < > <= >= && ||].freeze
18
20
  UNARY_OPERATORS = %w[- !].freeze
19
21
  TRANSPARENT_NODES = VisitorRegistry::TRANSPARENT_NODES
@@ -34,7 +36,20 @@ module RLSL
34
36
  def initialize(context = {})
35
37
  @context = context
36
38
  @uniforms = context[:uniforms] || {}
37
- @scope_context = ScopeContext.new(params: context[:params] || [])
39
+ @source_name = context[:source_name] || "(shader source)"
40
+ @line_offset = context[:line_offset].to_i
41
+ params = context[:params] || []
42
+ @scope_context = ScopeContext.new(params: params)
43
+ positional_types = params.each_with_index.to_h do |name, index|
44
+ [name.to_sym, %i[vec2 vec2 uniforms][index]]
45
+ end
46
+ @parameter_types = { frag_coord: :vec2, resolution: :vec2, u: :uniforms }.merge(positional_types)
47
+ @parameter_bindings = { frag_coord: :frag_coord, resolution: :resolution, u: :u }
48
+ @implicit_loop_index = 0
49
+ @source_identifiers = Set.new
50
+ params.each_with_index do |name, index|
51
+ @parameter_bindings[name.to_sym] = %i[frag_coord resolution u][index]
52
+ end
38
53
  end
39
54
 
40
55
  def parse(source)
@@ -42,10 +57,17 @@ module RLSL
42
57
 
43
58
  unless result.success?
44
59
  errors = result.errors.map(&:message).join(", ")
45
- raise "Parse error: #{errors}"
60
+ raise RLSL::ParseError, "Parse error: #{errors}"
46
61
  end
47
62
 
48
63
  program = result.value
64
+ if NodeTraversal.depth_exceeds?(program, MAX_AST_DEPTH)
65
+ raise UnsupportedSyntaxError, "Shader syntax nesting exceeds #{MAX_AST_DEPTH} nodes"
66
+ end
67
+ @source_identifiers = NodeTraversal.each(program).filter_map do |node|
68
+ node.name.to_sym if node.respond_to?(:name)
69
+ end.to_set
70
+
49
71
  visit(program)
50
72
  end
51
73
 
@@ -53,11 +75,17 @@ module RLSL
53
75
  return nil if node.nil?
54
76
 
55
77
  method_name = NODE_VISITORS[node.class]
56
- return send(method_name, node) if method_name
57
-
58
- raise UnsupportedSyntaxError, "Unsupported Prism node: #{node.class}" unless transparent_node?(node)
59
-
60
- visit_default(node)
78
+ result = if method_name
79
+ send(method_name, node)
80
+ else
81
+ raise UnsupportedSyntaxError, "Unsupported Prism node: #{node.class}" unless transparent_node?(node)
82
+
83
+ visit_default(node)
84
+ end
85
+ attach_source_location(result, node.location)
86
+ rescue RLSL::Error => error
87
+ error.with_source_location(source_location(node.location))
88
+ raise
61
89
  end
62
90
 
63
91
  private
@@ -89,20 +117,15 @@ module RLSL
89
117
  end
90
118
 
91
119
  def infer_param_type(name)
92
- case name
93
- when :frag_coord, :resolution
94
- :vec2
95
- when :u
96
- :uniforms
97
- else
98
- nil
99
- end
120
+ @parameter_types[name.to_sym]
100
121
  end
101
122
 
102
- def extract_required_params(node)
103
- return [] unless node
123
+ def emitted_parameter_name(name)
124
+ @parameter_bindings[name.to_sym] || name.to_sym
125
+ end
104
126
 
105
- node.requireds&.map { |param| param.name.to_sym } || []
127
+ def extract_required_params(node)
128
+ ParameterList.required_names(node).map(&:to_sym)
106
129
  end
107
130
 
108
131
  def extract_block_params(node)
@@ -115,6 +138,18 @@ module RLSL
115
138
  @scope_context.parameter?(name)
116
139
  end
117
140
 
141
+ def fragment_parameter_reference?(name)
142
+ @scope_context.root_parameter?(name)
143
+ end
144
+
145
+ def emitted_assignment_name(name)
146
+ if fragment_parameter_reference?(name) && infer_param_type(name) == :uniforms
147
+ raise UnsupportedSyntaxError, "The fragment uniform parameter cannot be reassigned"
148
+ end
149
+
150
+ fragment_parameter_reference?(name) ? emitted_parameter_name(name) : name
151
+ end
152
+
118
153
  def known_variable?(name)
119
154
  @scope_context.known_variable?(name)
120
155
  end
@@ -122,6 +157,29 @@ module RLSL
122
157
  def declare_variable(name)
123
158
  @scope_context.declare(name)
124
159
  end
160
+
161
+ def next_implicit_loop_variable
162
+ loop do
163
+ name = :"_rlsl_i#{@implicit_loop_index}"
164
+ @implicit_loop_index += 1
165
+ return name unless @source_identifiers.include?(name)
166
+ end
167
+ end
168
+
169
+ def attach_source_location(result, prism_location)
170
+ return result unless result.is_a?(IR::Node)
171
+
172
+ result.location ||= source_location(prism_location)
173
+ result
174
+ end
175
+
176
+ def source_location(prism_location)
177
+ RLSL::SourceLocation.new(
178
+ source_name: @source_name,
179
+ line: @line_offset + prism_location.start_line,
180
+ column: prism_location.start_column + 1
181
+ )
182
+ end
125
183
  end
126
184
  end
127
185
  end
@@ -5,16 +5,16 @@ module RLSL
5
5
  module Builtins
6
6
  module FunctionRegistry
7
7
  ALL_TARGETS = %i[c glsl wgsl msl].freeze
8
- META_TYPES = %i[any same first second third].freeze
8
+ META_TYPES = %i[any same first second third common floating interpolated].freeze
9
9
 
10
10
  FUNCTIONS = {
11
11
  vec2: { args: %i[any any], returns: :vec2, variadic: true, min_args: 1 },
12
12
  vec3: { args: %i[any any any], returns: :vec3, variadic: true, min_args: 1 },
13
13
  vec4: { args: %i[any any any any], returns: :vec4, variadic: true, min_args: 1 },
14
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 },
15
+ mat2: { args: %i[any any any any], returns: :mat2, variadic: true, min_args: 1, targets: %i[glsl wgsl msl] },
16
+ mat3: { args: %i[any any any any any any any any any], returns: :mat3, variadic: true, min_args: 1, targets: %i[glsl wgsl msl] },
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, targets: %i[glsl wgsl msl] },
18
18
 
19
19
  sin: { args: [:float], returns: :float },
20
20
  cos: { args: [:float], returns: :float },
@@ -27,18 +27,18 @@ module RLSL
27
27
  pow: { args: %i[float float], returns: :float },
28
28
  exp: { args: [:float], returns: :float },
29
29
  log: { args: [:float], returns: :float },
30
- sqrt: { args: [:any], returns: :same },
30
+ sqrt: { args: [:any], returns: :floating },
31
31
 
32
32
  abs: { args: [:any], returns: :same },
33
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 },
34
+ floor: { args: [:any], returns: :floating },
35
+ ceil: { args: [:any], returns: :floating },
36
+ fract: { args: [:any], returns: :floating },
37
+ mod: { args: %i[float float], returns: :float },
38
+ min: { args: %i[any any], returns: :common },
39
+ max: { args: %i[any any], returns: :common },
40
+ clamp: { args: %i[any any any], returns: :common },
41
+ mix: { args: %i[any any float], returns: :interpolated },
42
42
  step: { args: %i[float any], returns: :second },
43
43
  smoothstep: { args: %i[float float any], returns: :third },
44
44
 
@@ -50,23 +50,23 @@ module RLSL
50
50
  reflect: { args: %i[any any], returns: :first },
51
51
  refract: { args: %i[any any float], returns: :first },
52
52
 
53
- hash21: { args: [:vec2], returns: :float },
54
- hash22: { args: [:vec2], returns: :vec2 },
53
+ hash21: { args: [:vec2], returns: :float, targets: [:c] },
54
+ hash22: { args: [:vec2], returns: :vec2, targets: [:c] },
55
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 },
56
+ lessThan: { args: %i[any any], returns: :bool, targets: [] },
57
+ lessThanEqual: { args: %i[any any], returns: :bool, targets: [] },
58
+ greaterThan: { args: %i[any any], returns: :bool, targets: [] },
59
+ greaterThanEqual: { args: %i[any any], returns: :bool, targets: [] },
60
+ equal: { args: %i[any any], returns: :bool, targets: [] },
61
+ notEqual: { args: %i[any any], returns: :bool, targets: [] },
62
62
 
63
63
  inverse: { args: [:any], returns: :same, targets: %i[glsl wgsl msl] },
64
64
  transpose: { args: [:any], returns: :same, targets: %i[glsl wgsl msl] },
65
65
  determinant: { args: [:any], returns: :float, targets: %i[glsl wgsl msl] },
66
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 }
67
+ texture2D: { args: %i[sampler2D vec2], returns: :vec4, targets: %i[glsl wgsl msl] },
68
+ texture: { args: %i[sampler2D vec2], returns: :vec4, targets: %i[glsl wgsl msl] },
69
+ textureLod: { args: %i[sampler2D vec2 float], returns: :vec4, targets: %i[glsl wgsl msl] }
70
70
  }.freeze
71
71
 
72
72
  module_function
@@ -101,6 +101,19 @@ module RLSL
101
101
  when :first then arg_types.first
102
102
  when :second then arg_types[1]
103
103
  when :third then arg_types[2]
104
+ when :common
105
+ type = OperatorRules.common_type(arg_types)
106
+ type if numeric_type?(type)
107
+ when :floating
108
+ type = arg_types.first
109
+ return :float if type == :int
110
+
111
+ type if type == :float || OperatorRules.vector_type?(type)
112
+ when :interpolated
113
+ type = OperatorRules.common_type(arg_types.first(2))
114
+ return :float if type == :int
115
+
116
+ type if numeric_type?(type)
104
117
  when Symbol then rule
105
118
  end
106
119
  end
@@ -108,6 +121,10 @@ module RLSL
108
121
  def explicit_type?(type)
109
122
  type.is_a?(Symbol) && !META_TYPES.include?(type)
110
123
  end
124
+
125
+ def numeric_type?(type)
126
+ OperatorRules.scalar_type?(type) || OperatorRules.vector_type?(type)
127
+ end
111
128
  end
112
129
  end
113
130
  end
@@ -39,6 +39,7 @@ module RLSL
39
39
 
40
40
  def binary_op_result_type(op, left_type, right_type)
41
41
  op_kind = BINARY_OPERATORS[op.to_s]
42
+ validate_operand_shapes!(op, left_type, right_type)
42
43
 
43
44
  case op_kind
44
45
  when :comparison, :logical
@@ -78,6 +79,14 @@ module RLSL
78
79
  %i[float int].include?(type)
79
80
  end
80
81
 
82
+ def common_type(types)
83
+ types = Array(types).compact.uniq
84
+ return types.first if types.length == 1
85
+ return :float if types.sort == %i[float int]
86
+
87
+ nil
88
+ end
89
+
81
90
  def scalar_arithmetic_result_type(op, left_type, right_type)
82
91
  return :float unless scalar_type?(left_type) && scalar_type?(right_type)
83
92
  return :float if op.to_s == "/"
@@ -93,6 +102,21 @@ module RLSL
93
102
  when :mat4 then :vec4
94
103
  end
95
104
  end
105
+
106
+ def validate_operand_shapes!(op, left_type, right_type)
107
+ if vector_type?(left_type) && vector_type?(right_type) && left_type != right_type
108
+ raise SignatureError, "Vector size mismatch for #{op}: #{left_type} and #{right_type}"
109
+ end
110
+
111
+ if matrix_type?(left_type) && vector_type?(right_type) && matrix_vector_result(left_type) != right_type
112
+ raise SignatureError, "Matrix/vector size mismatch for #{op}: #{left_type} and #{right_type}"
113
+ end
114
+
115
+ return unless vector_type?(left_type) && matrix_type?(right_type)
116
+ return if left_type == matrix_vector_result(right_type)
117
+
118
+ raise SignatureError, "Vector/matrix size mismatch for #{op}: #{left_type} and #{right_type}"
119
+ end
96
120
  end
97
121
  end
98
122
  end
@@ -12,7 +12,7 @@ module RLSL
12
12
  }.freeze
13
13
 
14
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/
15
+ SWIZZLE_PATTERNS = /\A(?:[xyzw]{2,4}|[rgba]{2,4}|[stpq]{2,4})\z/
16
16
 
17
17
  module_function
18
18
 
@@ -32,6 +32,15 @@ module RLSL
32
32
  else :float
33
33
  end
34
34
  end
35
+
36
+ def valid_for_type?(components, receiver_type)
37
+ vector_size = { vec2: 2, vec3: 3, vec4: 4 }[receiver_type]
38
+ return false unless vector_size
39
+
40
+ components.to_s.each_char.all? do |component|
41
+ SWIZZLE_COMPONENTS.fetch(component) < vector_size
42
+ end
43
+ end
35
44
  end
36
45
  end
37
46
  end
@@ -51,6 +51,10 @@ module RLSL
51
51
  SwizzleRules.swizzle_type(components)
52
52
  end
53
53
 
54
+ def valid_swizzle_for_type?(components, receiver_type)
55
+ SwizzleRules.valid_for_type?(components, receiver_type)
56
+ end
57
+
54
58
  def resolve_return_type(rule, arg_types)
55
59
  FunctionRegistry.resolve_return_type(rule, arg_types)
56
60
  end
@@ -71,6 +75,10 @@ module RLSL
71
75
  OperatorRules.scalar_type?(type)
72
76
  end
73
77
 
78
+ def common_type(types)
79
+ OperatorRules.common_type(types)
80
+ end
81
+
74
82
  def matrix_vector_result(matrix_type)
75
83
  OperatorRules.matrix_vector_result(matrix_type)
76
84
  end
@@ -6,8 +6,7 @@ module RLSL
6
6
  class BaseEmitter
7
7
  module ControlFlowEmission
8
8
  def emit_tuple_return(node)
9
- elements = node.elements.map { |elem| emit(elem) }.join(", ")
10
- "#{indent}return (#{current_return_struct_name}){#{elements}};\n"
9
+ "#{indent}return #{emit_tuple_value(node)};\n"
11
10
  end
12
11
 
13
12
  def emit_if_statement(node)
@@ -17,15 +16,18 @@ module RLSL
17
16
  def emit_conditional(node, needs_return:)
18
17
  condition = emit(node.condition)
19
18
  then_code = emit_branch(node.then_branch, needs_return: needs_return)
19
+ declarations = emit_hoisted_declarations(node)
20
20
 
21
- return "#{indent}if (#{condition}) {\n#{then_code}#{indent}}#{needs_return ? "\n" : ""}" unless node.else_branch
21
+ unless node.else_branch
22
+ return "#{declarations}#{indent}if (#{condition}) {\n#{then_code}#{indent}}#{needs_return ? "\n" : ""}"
23
+ end
22
24
 
23
25
  if elsif_node?(node.else_branch)
24
26
  elsif_code = emit_elsif(node.else_branch, needs_return: needs_return)
25
- "#{indent}if (#{condition}) {\n#{then_code}#{indent}} #{elsif_code}#{needs_return ? "\n" : ""}"
27
+ "#{declarations}#{indent}if (#{condition}) {\n#{then_code}#{indent}} #{elsif_code}#{needs_return ? "\n" : ""}"
26
28
  else
27
29
  else_code = emit_branch(node.else_branch, needs_return: needs_return)
28
- "#{indent}if (#{condition}) {\n#{then_code}#{indent}} else {\n#{else_code}#{indent}}#{needs_return ? "\n" : ""}"
30
+ "#{declarations}#{indent}if (#{condition}) {\n#{then_code}#{indent}} else {\n#{else_code}#{indent}}#{needs_return ? "\n" : ""}"
29
31
  end
30
32
  end
31
33
 
@@ -53,16 +55,26 @@ module RLSL
53
55
  end
54
56
 
55
57
  def emit_return(node)
56
- node.expression ? "return #{emit(node.expression)}" : "return"
58
+ if node.expression.is_a?(IR::ArrayLiteral) && @return_struct_name_stack.any?
59
+ return "return #{emit_tuple_value(node.expression)}"
60
+ end
61
+
62
+ node.expression ? "return #{emit_return_expression(node.expression)}" : "return"
57
63
  end
58
64
 
59
65
  def emit_for_loop(node)
60
- var = node.variable
66
+ variable = node.variable
67
+ counter = loop_variable_mutated?(node) ? next_temporary_name("i") : variable
61
68
  start_val = emit(node.range_start)
62
69
  end_val = emit(node.range_end)
63
70
  body = emit_indented_block(node.body)
71
+ body = "#{indent} #{type_name(:int)} #{variable} = #{counter};\n#{body}" if counter != variable
72
+
73
+ comparison = node.exclude_end ? "<" : "<="
74
+ return "for (int #{counter} = #{start_val}; #{counter} #{comparison} #{end_val}; #{counter}++) {\n#{body}#{indent}}" if node.range_end.is_a?(IR::Literal)
64
75
 
65
- "for (int #{var} = #{start_val}; #{var} < #{end_val}; #{var}++) {\n#{body}#{indent}}"
76
+ bound = next_temporary_name("end")
77
+ "int #{bound} = #{end_val};\n#{indent}for (int #{counter} = #{start_val}; #{counter} #{comparison} #{bound}; #{counter}++) {\n#{body}#{indent}}"
66
78
  end
67
79
 
68
80
  def emit_while_loop(node)
@@ -76,6 +88,37 @@ module RLSL
76
88
  "break"
77
89
  end
78
90
 
91
+ def emit_hoisted_declarations(node)
92
+ node.hoisted_variables.map do |name, type|
93
+ "#{indent}#{type_name(type || :float)} #{name};\n"
94
+ end.join
95
+ end
96
+
97
+ def emit_tuple_value(node)
98
+ expected_types = Array(current_return_type)
99
+ elements = node.elements.each_with_index.map do |element, index|
100
+ emit_typed_argument(element, expected_types[index])
101
+ end.join(", ")
102
+ "(#{current_return_struct_name}){#{elements}}"
103
+ end
104
+
105
+ def emit_return_expression(node)
106
+ emit_typed_argument(node, current_return_type)
107
+ end
108
+
109
+ def loop_variable_mutated?(node)
110
+ IR::Traversal.each(node.body).any? do |current|
111
+ case current
112
+ when IR::Assignment
113
+ current.target.is_a?(IR::VarRef) && current.target.name == node.variable
114
+ when IR::MultipleAssignment
115
+ current.targets.any? { |target| target.name == node.variable }
116
+ else
117
+ false
118
+ end
119
+ end
120
+ end
121
+
79
122
  end
80
123
  end
81
124
  end
@@ -12,14 +12,14 @@ module RLSL
12
12
  end.join(", ")
13
13
 
14
14
  if node.return_type.is_a?(Array)
15
- @current_return_struct_name = "#{name}_result"
16
15
  struct_def = emit_result_struct(name, node.return_type)
17
- body = emit_indented_block(node.body, needs_return: true)
18
- @current_return_struct_name = nil
19
- "#{struct_def}static inline #{name}_result #{name}(#{params}) {\n#{body}\n#{indent}}\n"
16
+ body = with_return_struct_name("#{name}_result") do
17
+ with_return_type(node.return_type) { emit_indented_block(node.body, needs_return: true) }
18
+ end
19
+ "#{struct_def}#{function_qualifier}#{name}_result #{name}(#{params}) {\n#{body}#{indent}}\n"
20
20
  else
21
- body = emit_indented_block(node.body, needs_return: true)
22
- "static inline #{type_name(node.return_type || :float)} #{name}(#{params}) {\n#{body}\n#{indent}}\n"
21
+ body = with_return_type(node.return_type) { emit_indented_block(node.body, needs_return: true) }
22
+ "#{function_qualifier}#{type_name(node.return_type || :float)} #{name}(#{params}) {\n#{body}#{indent}}\n"
23
23
  end
24
24
  end
25
25
 
@@ -29,11 +29,18 @@ module RLSL
29
29
  end
30
30
 
31
31
  def current_return_struct_name
32
- @current_return_struct_name || "result"
32
+ @return_struct_name_stack.last || "result"
33
33
  end
34
34
 
35
35
  def emit_array_literal(node, for_static_init: false)
36
- elements = node.elements.map { |elem| emit_for_static_init(elem, for_static_init) }.join(", ")
36
+ element_type = TypeShapes.element_type(node.type)
37
+ elements = node.elements.map do |element|
38
+ if for_static_init
39
+ emit_for_static_init(element, true)
40
+ else
41
+ emit_typed_argument(element, element_type)
42
+ end
43
+ end.join(", ")
37
44
  "{#{elements}}"
38
45
  end
39
46
 
@@ -66,7 +73,7 @@ module RLSL
66
73
  elements = emit_array_literal(node.initializer, for_static_init: true)
67
74
  "#{prefix}#{elem_type} #{node.name}[#{size}] = #{elements}"
68
75
  else
69
- value = node.is_const ? emit_for_static_init(node.initializer, true) : emit(node.initializer)
76
+ value = node.is_static ? emit_for_static_init(node.initializer, true) : emit(node.initializer)
70
77
  "#{prefix}#{type_name(node.type || :float)} #{node.name} = #{value}"
71
78
  end
72
79
  end
@@ -76,6 +83,8 @@ module RLSL
76
83
 
77
84
  if node.value.is_a?(IR::FuncCall)
78
85
  emit_multi_return_assignment(node, value_code)
86
+ elsif node.value.is_a?(IR::ArrayLiteral)
87
+ emit_literal_assignment(node)
79
88
  else
80
89
  emit_indexed_assignment(node, value_code)
81
90
  end
@@ -85,18 +94,39 @@ module RLSL
85
94
 
86
95
  def emit_multi_return_assignment(node, value_code)
87
96
  func_name = node.value.name
88
- lines = ["#{func_name}_result _tmp_#{func_name} = #{value_code}"]
97
+ temporary = next_temporary_name("result")
98
+ lines = [emit_temporary_declaration("#{func_name}_result", temporary, value_code)]
89
99
  node.targets.each_with_index do |target, index|
90
- lines << "#{type_name(target.type || :float)} #{target.name} = _tmp_#{func_name}.v#{index}"
100
+ lines << "#{emit_multiple_assignment_target(target, node.declarations[index])} = #{temporary}.v#{index}"
91
101
  end
92
102
  lines.join(";\n#{indent}")
93
103
  end
94
104
 
95
105
  def emit_indexed_assignment(node, value_code)
96
106
  node.targets.each_with_index.map do |target, index|
97
- "#{type_name(target.type || :float)} #{target.name} = #{value_code}[#{index}]"
107
+ "#{emit_multiple_assignment_target(target, node.declarations[index])} = #{value_code}[#{index}]"
98
108
  end.join(";\n#{indent}")
99
109
  end
110
+
111
+ def emit_literal_assignment(node)
112
+ temporaries = node.value.elements.map do |element|
113
+ name = next_temporary_name("value")
114
+ [name, emit_temporary_declaration(type_name(element.type || :float), name, emit(element))]
115
+ end
116
+ lines = temporaries.map(&:last)
117
+ node.targets.each_with_index do |target, index|
118
+ lines << "#{emit_multiple_assignment_target(target, node.declarations[index])} = #{temporaries[index].first}"
119
+ end
120
+ lines.join(";\n#{indent}")
121
+ end
122
+
123
+ def emit_multiple_assignment_target(target, declaration)
124
+ declaration ? "#{type_name(target.type || :float)} #{target.name}" : target.name.to_s
125
+ end
126
+
127
+ def emit_temporary_declaration(type, name, value)
128
+ "#{type} #{name} = #{value}"
129
+ end
100
130
  end
101
131
  end
102
132
  end
@@ -7,6 +7,14 @@ module RLSL
7
7
  module ExpressionEmission
8
8
  def emit_var_decl(node)
9
9
  type = type_name(node.type || :float)
10
+ return "#{type} #{node.name}" unless node.initializer
11
+
12
+ if node.initializer.is_a?(IR::ArrayLiteral)
13
+ value = emit(node.initializer)
14
+ element_type = type_name(TypeShapes.element_type(node.type) || :float)
15
+ return "#{element_type} #{node.name}[#{node.initializer.elements.length}] = #{value}"
16
+ end
17
+
10
18
  value = emit(node.initializer)
11
19
  "#{type} #{node.name} = #{value}"
12
20
  end
@@ -16,7 +24,7 @@ module RLSL
16
24
  end
17
25
 
18
26
  def emit_literal(node)
19
- format_number(node.value)
27
+ format_number(node.value, type: node.type)
20
28
  end
21
29
 
22
30
  def emit_bool_literal(node)
@@ -24,13 +32,17 @@ module RLSL
24
32
  end
25
33
 
26
34
  def emit_binary_op(node)
27
- left = emit_with_precedence(node.left, node.operator)
28
- right = emit_with_precedence(node.right, node.operator)
35
+ left = emit_with_precedence(node.left, node.operator, side: :left)
36
+ right = emit_with_precedence(node.right, node.operator, side: :right)
29
37
  "#{left} #{node.operator} #{right}"
30
38
  end
31
39
 
32
40
  def emit_unary_op(node)
33
- "#{node.operator}#{emit(node.operand)}"
41
+ operand = emit(node.operand)
42
+ operand = "(#{operand})" if node.operand.is_a?(IR::BinaryOp) ||
43
+ node.operand.is_a?(IR::Ternary) ||
44
+ node.operand.is_a?(IR::UnaryOp)
45
+ "#{node.operator}#{operand}"
34
46
  end
35
47
 
36
48
  def emit_func_call(node)
@@ -85,6 +97,10 @@ module RLSL
85
97
  end
86
98
  "#{array}[#{index}]"
87
99
  end
100
+
101
+ def emit_typed_argument(node, _expected_type)
102
+ emit(node)
103
+ end
88
104
  end
89
105
  end
90
106
  end
@@ -27,10 +27,16 @@ module RLSL
27
27
 
28
28
  def emit_terminal_statement(node)
29
29
  return emit(node, needs_return: true) if node.is_a?(IR::IfStatement)
30
+ if node.is_a?(IR::VarDecl)
31
+ return "#{emit_statement(node)}#{indent}return #{node.name};\n"
32
+ end
33
+ if node.is_a?(IR::Assignment)
34
+ return "#{emit_statement(node)}#{indent}return #{emit(node.target)};\n"
35
+ end
30
36
  return emit_statement(node) if terminal_passthrough_node?(node)
31
37
  return emit_tuple_return(node) if node.is_a?(IR::ArrayLiteral)
32
38
 
33
- "#{indent}return #{emit(node)};\n"
39
+ "#{indent}return #{emit_return_expression(node)};\n"
34
40
  end
35
41
 
36
42
  def emit_branch(node, needs_return:)