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,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module GLSL
|
|
5
|
+
class Translator < BaseTranslator
|
|
6
|
+
TYPE_MAP = {
|
|
7
|
+
"vec2" => "vec2",
|
|
8
|
+
"vec3" => "vec3",
|
|
9
|
+
"vec4" => "vec4"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
FUNC_REPLACEMENTS = BaseTranslator.common_func_replacements(
|
|
13
|
+
target_vec2: "vec2",
|
|
14
|
+
target_vec3: "vec3",
|
|
15
|
+
target_vec4: "vec4"
|
|
16
|
+
).freeze
|
|
17
|
+
|
|
18
|
+
def initialize(uniforms, helpers_code, fragment_code, version: "450")
|
|
19
|
+
super(uniforms, helpers_code, fragment_code)
|
|
20
|
+
@version = version
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
def translate_code(c_code)
|
|
26
|
+
result = super(c_code)
|
|
27
|
+
return result if result.empty?
|
|
28
|
+
|
|
29
|
+
result.gsub!(/\bstatic\s+/, "")
|
|
30
|
+
result.gsub!(/\binline\s+/, "")
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_shader(helpers, fragment)
|
|
35
|
+
<<~GLSL
|
|
36
|
+
#version #{@version}
|
|
37
|
+
|
|
38
|
+
// Uniforms
|
|
39
|
+
#{generate_uniform_declarations}
|
|
40
|
+
|
|
41
|
+
// Output
|
|
42
|
+
layout(rgba8, binding = 0) uniform writeonly image2D outputImage;
|
|
43
|
+
|
|
44
|
+
#{helpers}
|
|
45
|
+
|
|
46
|
+
vec3 shader_fragment(vec2 frag_coord, vec2 resolution) {
|
|
47
|
+
vec2 uv = frag_coord / resolution.y;
|
|
48
|
+
#{fragment}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
layout(local_size_x = 8, local_size_y = 8) in;
|
|
52
|
+
void main() {
|
|
53
|
+
ivec2 texSize = imageSize(outputImage);
|
|
54
|
+
vec2 resolution = vec2(float(texSize.x), float(texSize.y));
|
|
55
|
+
|
|
56
|
+
if (gl_GlobalInvocationID.x >= uint(resolution.x) ||
|
|
57
|
+
gl_GlobalInvocationID.y >= uint(resolution.y)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
vec2 frag_coord = vec2(float(gl_GlobalInvocationID.x),
|
|
62
|
+
resolution.y - 1.0 - float(gl_GlobalInvocationID.y));
|
|
63
|
+
vec3 color = shader_fragment(frag_coord, resolution);
|
|
64
|
+
|
|
65
|
+
imageStore(outputImage, ivec2(gl_GlobalInvocationID.xy),
|
|
66
|
+
vec4(clamp(color, 0.0, 1.0), 1.0));
|
|
67
|
+
}
|
|
68
|
+
GLSL
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def generate_uniform_declarations
|
|
74
|
+
declarations = ["layout(binding = 1) uniform ShaderUniforms {",
|
|
75
|
+
" vec2 resolution;"]
|
|
76
|
+
@uniforms.each do |name, type|
|
|
77
|
+
glsl_type = uniform_type_to_target(type)
|
|
78
|
+
declarations << " #{glsl_type} #{name};"
|
|
79
|
+
end
|
|
80
|
+
declarations << "} u;"
|
|
81
|
+
declarations.join("\n")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def target_vec2_type
|
|
85
|
+
"vec2"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def target_vec3_type
|
|
89
|
+
"vec3"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def target_vec4_type
|
|
93
|
+
"vec4"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "metaco"
|
|
5
|
+
METACO_AVAILABLE = true
|
|
6
|
+
rescue LoadError
|
|
7
|
+
METACO_AVAILABLE = false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module RLSL
|
|
11
|
+
module MSL
|
|
12
|
+
class Shader
|
|
13
|
+
attr_reader :name, :msl_source
|
|
14
|
+
|
|
15
|
+
def initialize(name, uniforms, msl_source)
|
|
16
|
+
@name = name
|
|
17
|
+
@uniform_types = uniforms
|
|
18
|
+
@uniform_names = uniforms.keys
|
|
19
|
+
@msl_source = msl_source
|
|
20
|
+
@compiled_handles = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def metal?
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render_metal(handle, width, height, uniforms = {})
|
|
28
|
+
unless METACO_AVAILABLE
|
|
29
|
+
raise LoadError, "metaco gem is required for Metal rendering. Install it with: gem install metaco"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
unless @compiled_handles[handle]
|
|
33
|
+
Metaco.compile_compute_shader(handle, @msl_source)
|
|
34
|
+
@compiled_handles[handle] = true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
uniform_data = pack_uniforms(uniforms, width, height)
|
|
38
|
+
|
|
39
|
+
Metaco.dispatch_compute(handle, uniform_data)
|
|
40
|
+
Metaco.present_compute(handle)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def pack_uniforms(uniforms, width, height)
|
|
46
|
+
data = [width.to_f, height.to_f].pack("ff")
|
|
47
|
+
current_offset = 8
|
|
48
|
+
|
|
49
|
+
@uniform_names.each do |name|
|
|
50
|
+
value = uniforms[name]
|
|
51
|
+
type = @uniform_types[name]
|
|
52
|
+
|
|
53
|
+
alignment = case type
|
|
54
|
+
when :float then 4
|
|
55
|
+
when :vec2 then 8
|
|
56
|
+
when :vec3, :vec4 then 16
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
padding_needed = (alignment - (current_offset % alignment)) % alignment
|
|
60
|
+
data += "\x00" * padding_needed
|
|
61
|
+
current_offset += padding_needed
|
|
62
|
+
|
|
63
|
+
case type
|
|
64
|
+
when :float
|
|
65
|
+
data += [value.to_f].pack("f")
|
|
66
|
+
current_offset += 4
|
|
67
|
+
when :vec2
|
|
68
|
+
data += value.pack("ff")
|
|
69
|
+
current_offset += 8
|
|
70
|
+
when :vec3
|
|
71
|
+
data += (value + [0.0]).pack("ffff")
|
|
72
|
+
current_offset += 16
|
|
73
|
+
when :vec4
|
|
74
|
+
data += value.pack("ffff")
|
|
75
|
+
current_offset += 16
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
data.ljust(256, "\x00")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module MSL
|
|
5
|
+
class Translator < BaseTranslator
|
|
6
|
+
TYPE_MAP = {
|
|
7
|
+
"vec2" => "float2",
|
|
8
|
+
"vec3" => "float3",
|
|
9
|
+
"vec4" => "float4"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
FUNC_REPLACEMENTS = BaseTranslator.common_func_replacements(
|
|
13
|
+
target_vec2: "float2",
|
|
14
|
+
target_vec3: "float3",
|
|
15
|
+
target_vec4: "float4"
|
|
16
|
+
).freeze
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
def translate_code(c_code)
|
|
21
|
+
result = super(c_code)
|
|
22
|
+
return result if result.empty?
|
|
23
|
+
|
|
24
|
+
result.gsub!(/\bstatic\s+/, "")
|
|
25
|
+
result.gsub!(/\binline\s+/, "")
|
|
26
|
+
result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def generate_shader(helpers, fragment)
|
|
30
|
+
<<~MSL
|
|
31
|
+
#include <metal_stdlib>
|
|
32
|
+
using namespace metal;
|
|
33
|
+
|
|
34
|
+
// Uniform buffer structure
|
|
35
|
+
struct Uniforms {
|
|
36
|
+
#{generate_uniform_struct}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Helper functions
|
|
40
|
+
#{helpers}
|
|
41
|
+
|
|
42
|
+
// Fragment shader function
|
|
43
|
+
float3 shader_fragment(float2 frag_coord, float2 resolution, constant Uniforms& u) {
|
|
44
|
+
float2 uv = frag_coord / resolution.y;
|
|
45
|
+
#{fragment}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Compute kernel entry point
|
|
49
|
+
kernel void compute_shader(
|
|
50
|
+
texture2d<float, access::write> output [[texture(0)]],
|
|
51
|
+
constant Uniforms& u [[buffer(0)]],
|
|
52
|
+
uint2 gid [[thread_position_in_grid]]
|
|
53
|
+
) {
|
|
54
|
+
float2 resolution = float2(output.get_width(), output.get_height());
|
|
55
|
+
if (gid.x >= uint(resolution.x) || gid.y >= uint(resolution.y)) return;
|
|
56
|
+
|
|
57
|
+
float2 frag_coord = float2(gid.x, resolution.y - 1.0 - float(gid.y));
|
|
58
|
+
float3 color = shader_fragment(frag_coord, resolution, u);
|
|
59
|
+
|
|
60
|
+
output.write(float4(clamp(color, 0.0, 1.0), 1.0), gid);
|
|
61
|
+
}
|
|
62
|
+
MSL
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def generate_uniform_struct
|
|
68
|
+
fields = ["float2 resolution;"]
|
|
69
|
+
@uniforms.each do |name, type|
|
|
70
|
+
msl_type = uniform_type_to_target(type)
|
|
71
|
+
fields << "#{msl_type} #{name};"
|
|
72
|
+
end
|
|
73
|
+
fields.join("\n ")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def target_vec2_type
|
|
77
|
+
"float2"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def target_vec3_type
|
|
81
|
+
"float3"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def target_vec4_type
|
|
85
|
+
"float4"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require "set"
|
|
5
|
+
|
|
6
|
+
module RLSL
|
|
7
|
+
module Prism
|
|
8
|
+
class ASTVisitor
|
|
9
|
+
BINARY_OPERATORS = %w[+ - * / % == != < > <= >= && ||].freeze
|
|
10
|
+
UNARY_OPERATORS = %w[- !].freeze
|
|
11
|
+
|
|
12
|
+
def initialize(context = {})
|
|
13
|
+
@context = context
|
|
14
|
+
@uniforms = context[:uniforms] || {}
|
|
15
|
+
@params = Set.new(context[:params] || [])
|
|
16
|
+
@declared_vars = Set.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def parse(source)
|
|
20
|
+
result = ::Prism.parse(source)
|
|
21
|
+
|
|
22
|
+
unless result.success?
|
|
23
|
+
errors = result.errors.map(&:message).join(", ")
|
|
24
|
+
raise "Parse error: #{errors}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
program = result.value
|
|
28
|
+
visit(program)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def visit(node)
|
|
32
|
+
return nil if node.nil?
|
|
33
|
+
|
|
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
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
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
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def visit_default(node)
|
|
53
|
+
children = []
|
|
54
|
+
node.child_nodes.compact.each do |child|
|
|
55
|
+
result = visit(child)
|
|
56
|
+
children << result if result
|
|
57
|
+
end
|
|
58
|
+
children.length == 1 ? children.first : children
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def visit_program(node)
|
|
62
|
+
visit(node.statements)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def visit_statements(node)
|
|
66
|
+
statements = node.body.map { |stmt| visit(stmt) }.compact.flatten
|
|
67
|
+
IR::Block.new(statements)
|
|
68
|
+
end
|
|
69
|
+
|
|
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)
|
|
120
|
+
end
|
|
121
|
+
|
|
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)
|
|
271
|
+
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
|
|
316
|
+
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
|
+
end
|
|
327
|
+
|
|
328
|
+
def visit_global_variable_write(node)
|
|
329
|
+
name = node.name.to_s.sub(/^\$/, "").to_sym
|
|
330
|
+
value = visit(node.value)
|
|
331
|
+
|
|
332
|
+
IR::GlobalDecl.new(name, value, is_static: true)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def visit_constant_write(node)
|
|
336
|
+
name = node.name.to_sym
|
|
337
|
+
value = visit(node.value)
|
|
338
|
+
|
|
339
|
+
IR::GlobalDecl.new(name, value, is_const: true, is_static: true)
|
|
340
|
+
end
|
|
341
|
+
|
|
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)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def visit_local_variable_target(node)
|
|
354
|
+
name = node.name.to_sym
|
|
355
|
+
@declared_vars.add(name)
|
|
356
|
+
IR::VarRef.new(name)
|
|
357
|
+
end
|
|
358
|
+
|
|
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
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|