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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +18 -1
- data/lib/rlsl/base_translator/call_parser.rb +68 -0
- data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
- data/lib/rlsl/base_translator/code_scanner.rb +102 -0
- data/lib/rlsl/base_translator.rb +159 -63
- data/lib/rlsl/code_generator/math_prelude.rb +79 -0
- data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +97 -0
- data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
- data/lib/rlsl/code_generator/template_context.rb +41 -0
- data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
- data/lib/rlsl/code_generator.rb +26 -201
- data/lib/rlsl/compiled_shader.rb +4 -12
- data/lib/rlsl/function_context.rb +31 -14
- data/lib/rlsl/glsl/translator.rb +19 -38
- data/lib/rlsl/msl/shader.rb +8 -38
- data/lib/rlsl/msl/translator.rb +19 -36
- data/lib/rlsl/msl/uniform_buffer_packer.rb +68 -0
- data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +96 -0
- data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +79 -0
- data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +168 -0
- data/lib/rlsl/prism/ast_visitor/scope_context.rb +44 -0
- data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
- data/lib/rlsl/prism/ast_visitor.rb +54 -298
- data/lib/rlsl/prism/builtins/function_registry.rb +114 -0
- data/lib/rlsl/prism/builtins/operator_rules.rb +99 -0
- data/lib/rlsl/prism/builtins/swizzle_rules.rb +38 -0
- data/lib/rlsl/prism/builtins.rb +31 -148
- data/lib/rlsl/prism/compilation_unit.rb +7 -0
- data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +83 -0
- data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +104 -0
- data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +92 -0
- data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +89 -0
- data/lib/rlsl/prism/emitters/base_emitter.rb +68 -423
- data/lib/rlsl/prism/emitters/c_emitter.rb +78 -121
- data/lib/rlsl/prism/emitters/glsl_emitter.rb +30 -65
- data/lib/rlsl/prism/emitters/msl_emitter.rb +35 -62
- data/lib/rlsl/prism/emitters/target_emitter.rb +77 -0
- data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
- data/lib/rlsl/prism/emitters/wgsl_emitter.rb +65 -61
- data/lib/rlsl/prism/ir/control_flow.rb +83 -0
- data/lib/rlsl/prism/ir/definitions.rb +67 -0
- data/lib/rlsl/prism/ir/expressions.rb +197 -0
- data/lib/rlsl/prism/ir/node.rb +21 -0
- data/lib/rlsl/prism/ir/nodes.rb +4 -371
- data/lib/rlsl/prism/ir/traversal.rb +62 -0
- data/lib/rlsl/prism/source_extractor/block_locator.rb +52 -0
- data/lib/rlsl/prism/source_extractor.rb +14 -133
- data/lib/rlsl/prism/source_unit/parser.rb +78 -0
- data/lib/rlsl/prism/source_unit.rb +42 -0
- data/lib/rlsl/prism/target_capability_validator.rb +85 -0
- data/lib/rlsl/prism/transpiler.rb +63 -60
- data/lib/rlsl/prism/type_inference/call_type_resolver.rb +35 -0
- data/lib/rlsl/prism/type_inference/call_validator.rb +71 -0
- data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +80 -0
- data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +66 -0
- data/lib/rlsl/prism/type_inference/definition_inferer.rb +41 -0
- data/lib/rlsl/prism/type_inference/expression_inferer.rb +92 -0
- data/lib/rlsl/prism/type_inference/field_type_resolver.rb +17 -0
- data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
- data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
- data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
- data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
- data/lib/rlsl/prism/type_inference.rb +114 -248
- data/lib/rlsl/runtime_shader.rb +47 -0
- data/lib/rlsl/shader_builder/build_service.rb +77 -0
- data/lib/rlsl/shader_builder/native_extension_compiler.rb +71 -0
- data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
- data/lib/rlsl/shader_builder/source_resolver.rb +91 -0
- data/lib/rlsl/shader_builder.rb +22 -113
- data/lib/rlsl/types/catalog.rb +47 -0
- data/lib/rlsl/types/target_resolver.rb +15 -0
- data/lib/rlsl/types/type_spec.rb +167 -0
- data/lib/rlsl/types/value_normalizer.rb +81 -0
- data/lib/rlsl/types.rb +11 -29
- data/lib/rlsl/uniform_context.rb +6 -12
- data/lib/rlsl/version.rb +1 -1
- data/lib/rlsl/wgsl/translator.rb +23 -39
- data/lib/rlsl.rb +14 -12
- metadata +55 -16
data/lib/rlsl/msl/translator.rb
CHANGED
|
@@ -3,29 +3,22 @@
|
|
|
3
3
|
module RLSL
|
|
4
4
|
module MSL
|
|
5
5
|
class Translator < BaseTranslator
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
PROFILE = BaseTranslator.build_profile(
|
|
7
|
+
uniform_target: :msl,
|
|
8
|
+
identifier_replacements: {
|
|
9
|
+
"vec2" => "float2",
|
|
10
|
+
"vec3" => "float3",
|
|
11
|
+
"vec4" => "float4"
|
|
12
|
+
},
|
|
13
|
+
call_rewrites: BaseTranslator.common_call_rewrites(
|
|
14
|
+
target_vec2: "float2",
|
|
15
|
+
target_vec3: "float3",
|
|
16
|
+
target_vec4: "float4"
|
|
17
|
+
)
|
|
18
|
+
)
|
|
17
19
|
|
|
18
20
|
protected
|
|
19
21
|
|
|
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
22
|
def generate_shader(helpers, fragment)
|
|
30
23
|
<<~MSL
|
|
31
24
|
#include <metal_stdlib>
|
|
@@ -64,26 +57,16 @@ module RLSL
|
|
|
64
57
|
|
|
65
58
|
private
|
|
66
59
|
|
|
60
|
+
def profile
|
|
61
|
+
PROFILE
|
|
62
|
+
end
|
|
63
|
+
|
|
67
64
|
def generate_uniform_struct
|
|
68
|
-
fields =
|
|
69
|
-
|
|
70
|
-
msl_type = uniform_type_to_target(type)
|
|
71
|
-
fields << "#{msl_type} #{name};"
|
|
65
|
+
fields = uniform_lines(resolution_line: "#{target_vec2_type} resolution;") do |name, msl_type|
|
|
66
|
+
"#{msl_type} #{name};"
|
|
72
67
|
end
|
|
73
68
|
fields.join("\n ")
|
|
74
69
|
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
70
|
end
|
|
88
71
|
end
|
|
89
72
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module MSL
|
|
5
|
+
class UniformBufferPacker
|
|
6
|
+
def initialize(shader_name, uniform_types, uniform_names)
|
|
7
|
+
@shader_name = shader_name
|
|
8
|
+
@uniform_types = uniform_types
|
|
9
|
+
@uniform_names = uniform_names
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def pack(width, height, uniforms)
|
|
13
|
+
normalized_uniforms = UniformTypes.normalize_values(@uniform_types, uniforms, shader_name: @shader_name)
|
|
14
|
+
data = [width.to_f, height.to_f].pack("ff")
|
|
15
|
+
current_offset = 8
|
|
16
|
+
|
|
17
|
+
@uniform_names.each do |name|
|
|
18
|
+
value = normalized_uniforms[name]
|
|
19
|
+
spec = UniformTypes.metal_spec(@uniform_types[name])
|
|
20
|
+
current_offset, data = append_padding(data, current_offset, spec.metal_alignment)
|
|
21
|
+
data << pack_uniform_value(spec, value)
|
|
22
|
+
current_offset += spec.metal_size
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
data.ljust(256, "\x00")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def append_padding(data, current_offset, alignment)
|
|
31
|
+
padding_needed = (alignment - (current_offset % alignment)) % alignment
|
|
32
|
+
return [current_offset, data] if padding_needed.zero?
|
|
33
|
+
|
|
34
|
+
[current_offset + padding_needed, data + ("\x00" * padding_needed)]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pack_uniform_value(spec, value)
|
|
38
|
+
case spec.wrapper_kind
|
|
39
|
+
when :float
|
|
40
|
+
[value.to_f].pack("f")
|
|
41
|
+
when :int
|
|
42
|
+
[value.to_i].pack("l")
|
|
43
|
+
when :bool
|
|
44
|
+
[value ? 1 : 0].pack("l")
|
|
45
|
+
when :vector
|
|
46
|
+
pack_vector_uniform(spec.vector_size, value)
|
|
47
|
+
else
|
|
48
|
+
raise ArgumentError, "Unsupported Metal uniform type: #{spec.c_type}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def pack_vector_uniform(vector_size, value)
|
|
53
|
+
components = Array(value).map(&:to_f)
|
|
54
|
+
|
|
55
|
+
case vector_size
|
|
56
|
+
when 2
|
|
57
|
+
components.pack("ff")
|
|
58
|
+
when 3
|
|
59
|
+
(components + [0.0]).pack("ffff")
|
|
60
|
+
when 4
|
|
61
|
+
components.pack("ffff")
|
|
62
|
+
else
|
|
63
|
+
raise ArgumentError, "Unsupported vector size: #{vector_size}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class ASTVisitor
|
|
6
|
+
module ControlFlowVisiting
|
|
7
|
+
VISITORS = {}.tap do |visitors|
|
|
8
|
+
visitors[::Prism::BlockNode] = :visit_block if defined?(::Prism::BlockNode)
|
|
9
|
+
visitors[::Prism::LambdaNode] = :visit_lambda if defined?(::Prism::LambdaNode)
|
|
10
|
+
visitors[::Prism::IfNode] = :visit_if if defined?(::Prism::IfNode)
|
|
11
|
+
visitors[::Prism::ElseNode] = :visit_else if defined?(::Prism::ElseNode)
|
|
12
|
+
visitors[::Prism::UnlessNode] = :visit_unless if defined?(::Prism::UnlessNode)
|
|
13
|
+
visitors[::Prism::ReturnNode] = :visit_return if defined?(::Prism::ReturnNode)
|
|
14
|
+
visitors[::Prism::RangeNode] = :visit_range if defined?(::Prism::RangeNode)
|
|
15
|
+
visitors[::Prism::ForNode] = :visit_for if defined?(::Prism::ForNode)
|
|
16
|
+
visitors[::Prism::WhileNode] = :visit_while if defined?(::Prism::WhileNode)
|
|
17
|
+
visitors[::Prism::BreakNode] = :visit_break if defined?(::Prism::BreakNode)
|
|
18
|
+
end.freeze
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def visit_block(node)
|
|
23
|
+
visit_with_scoped_vars(node.body, params: extract_block_params(node))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def visit_lambda(node)
|
|
27
|
+
visit_block(node)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def visit_if(node)
|
|
31
|
+
condition = visit(node.predicate)
|
|
32
|
+
then_branch = visit_with_scoped_vars(node.statements)
|
|
33
|
+
else_branch = node.subsequent ? visit_with_scoped_vars(node.subsequent) : nil
|
|
34
|
+
|
|
35
|
+
IR::IfStatement.new(condition, then_branch, else_branch)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def visit_else(node)
|
|
39
|
+
visit(node.statements)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def visit_elsif(node)
|
|
43
|
+
visit_if(node)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def visit_if_node(node)
|
|
47
|
+
visit_if(node)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def visit_unless(node)
|
|
51
|
+
condition = IR::UnaryOp.new("!", visit(node.predicate))
|
|
52
|
+
then_branch = visit_with_scoped_vars(node.statements)
|
|
53
|
+
else_branch = node.else_clause ? visit_with_scoped_vars(node.else_clause) : nil
|
|
54
|
+
|
|
55
|
+
IR::IfStatement.new(condition, then_branch, else_branch)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def visit_return(node)
|
|
59
|
+
expr = node.arguments ? normalize_expression(visit(node.arguments.arguments.first)) : nil
|
|
60
|
+
IR::Return.new(expr)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def visit_range(node)
|
|
64
|
+
[visit(node.left), visit(node.right)]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def visit_for(node)
|
|
68
|
+
range = visit(node.collection)
|
|
69
|
+
body = visit(node.statements)
|
|
70
|
+
IR::ForLoop.new(node.index.name.to_sym, range[0], range[1], body)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def visit_call_with_block(node)
|
|
74
|
+
return visit_plain_call(node) unless times_loop?(node)
|
|
75
|
+
|
|
76
|
+
count = visit(node.receiver)
|
|
77
|
+
block = visit(node.block)
|
|
78
|
+
var_name = extract_block_params(node.block).first || :i
|
|
79
|
+
IR::ForLoop.new(var_name, IR::Literal.new(0, :int), count, block)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def visit_while(node)
|
|
83
|
+
IR::WhileLoop.new(visit(node.predicate), visit(node.statements))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def visit_break(_node)
|
|
87
|
+
IR::Break.new
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def times_loop?(node)
|
|
91
|
+
node.name.to_s == "times" && node.receiver
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class ASTVisitor
|
|
6
|
+
module DefinitionVisiting
|
|
7
|
+
VISITORS = {}.tap do |visitors|
|
|
8
|
+
visitors[::Prism::LocalVariableWriteNode] = :visit_local_variable_write if defined?(::Prism::LocalVariableWriteNode)
|
|
9
|
+
visitors[::Prism::LocalVariableOperatorWriteNode] = :visit_local_variable_operator_write if defined?(::Prism::LocalVariableOperatorWriteNode)
|
|
10
|
+
visitors[::Prism::LocalVariableReadNode] = :visit_local_variable_read if defined?(::Prism::LocalVariableReadNode)
|
|
11
|
+
visitors[::Prism::DefNode] = :visit_def if defined?(::Prism::DefNode)
|
|
12
|
+
visitors[::Prism::GlobalVariableWriteNode] = :visit_global_variable_write if defined?(::Prism::GlobalVariableWriteNode)
|
|
13
|
+
visitors[::Prism::ConstantWriteNode] = :visit_constant_write if defined?(::Prism::ConstantWriteNode)
|
|
14
|
+
visitors[::Prism::MultiWriteNode] = :visit_multi_write if defined?(::Prism::MultiWriteNode)
|
|
15
|
+
visitors[::Prism::LocalVariableTargetNode] = :visit_local_variable_target if defined?(::Prism::LocalVariableTargetNode)
|
|
16
|
+
end.freeze
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def visit_local_variable_write(node)
|
|
21
|
+
name = node.name.to_sym
|
|
22
|
+
value = normalize_expression(visit(node.value))
|
|
23
|
+
|
|
24
|
+
if known_variable?(name)
|
|
25
|
+
IR::Assignment.new(IR::VarRef.new(name), value)
|
|
26
|
+
else
|
|
27
|
+
declare_variable(name)
|
|
28
|
+
IR::VarDecl.new(name, value)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def visit_local_variable_operator_write(node)
|
|
33
|
+
name = node.name.to_sym
|
|
34
|
+
operator = node.operator.to_s.delete_suffix("=")
|
|
35
|
+
value = normalize_expression(visit(node.value))
|
|
36
|
+
|
|
37
|
+
declare_variable(name)
|
|
38
|
+
target = IR::VarRef.new(name)
|
|
39
|
+
expr = IR::BinaryOp.new(operator, IR::VarRef.new(name), value)
|
|
40
|
+
IR::Assignment.new(target, expr)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def visit_local_variable_read(node)
|
|
44
|
+
IR::VarRef.new(node.name.to_sym, infer_param_type(node.name.to_sym))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def visit_def(node)
|
|
48
|
+
params = extract_required_params(node.parameters)
|
|
49
|
+
body = visit_with_scoped_vars(node.body, params: params)
|
|
50
|
+
IR::FunctionDefinition.new(node.name.to_sym, params, body)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def visit_global_variable_write(node)
|
|
54
|
+
IR::GlobalDecl.new(node.name.to_s.sub(/^\$/, "").to_sym, visit(node.value), is_static: true)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def visit_constant_write(node)
|
|
58
|
+
IR::GlobalDecl.new(node.name.to_sym, visit(node.value), is_const: true, is_static: true)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def visit_multi_write(node)
|
|
62
|
+
targets = node.lefts.map do |target|
|
|
63
|
+
name = target.name.to_sym
|
|
64
|
+
declare_variable(name)
|
|
65
|
+
IR::VarRef.new(name)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
IR::MultipleAssignment.new(targets, visit(node.value))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def visit_local_variable_target(node)
|
|
72
|
+
name = node.name.to_sym
|
|
73
|
+
declare_variable(name)
|
|
74
|
+
IR::VarRef.new(name)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class ASTVisitor
|
|
6
|
+
module ExpressionVisiting
|
|
7
|
+
VISITORS = {}.tap do |visitors|
|
|
8
|
+
visitors[::Prism::IntegerNode] = :visit_integer if defined?(::Prism::IntegerNode)
|
|
9
|
+
visitors[::Prism::FloatNode] = :visit_float if defined?(::Prism::FloatNode)
|
|
10
|
+
visitors[::Prism::RationalNode] = :visit_rational if defined?(::Prism::RationalNode)
|
|
11
|
+
visitors[::Prism::TrueNode] = :visit_true if defined?(::Prism::TrueNode)
|
|
12
|
+
visitors[::Prism::FalseNode] = :visit_false if defined?(::Prism::FalseNode)
|
|
13
|
+
visitors[::Prism::ParenthesesNode] = :visit_parentheses if defined?(::Prism::ParenthesesNode)
|
|
14
|
+
visitors[::Prism::CallNode] = :visit_call if defined?(::Prism::CallNode)
|
|
15
|
+
visitors[::Prism::AndNode] = :visit_and if defined?(::Prism::AndNode)
|
|
16
|
+
visitors[::Prism::OrNode] = :visit_or if defined?(::Prism::OrNode)
|
|
17
|
+
visitors[::Prism::NotNode] = :visit_not if defined?(::Prism::NotNode)
|
|
18
|
+
visitors[::Prism::ArrayNode] = :visit_array if defined?(::Prism::ArrayNode)
|
|
19
|
+
visitors[::Prism::ConstantReadNode] = :visit_constant_read if defined?(::Prism::ConstantReadNode)
|
|
20
|
+
visitors[::Prism::ConstantPathNode] = :visit_constant_path if defined?(::Prism::ConstantPathNode)
|
|
21
|
+
visitors[::Prism::GlobalVariableReadNode] = :visit_global_variable_read if defined?(::Prism::GlobalVariableReadNode)
|
|
22
|
+
end.freeze
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def visit_integer(node)
|
|
27
|
+
IR::Literal.new(node.value, :int)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def visit_float(node)
|
|
31
|
+
IR::Literal.new(node.value, :float)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def visit_rational(node)
|
|
35
|
+
IR::Literal.new(node.value.to_f, :float)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def visit_true(_node)
|
|
39
|
+
IR::BoolLiteral.new(true)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def visit_false(_node)
|
|
43
|
+
IR::BoolLiteral.new(false)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def visit_parentheses(node)
|
|
47
|
+
inner = normalize_expression(visit(node.body))
|
|
48
|
+
inner = inner.statements.first if single_statement_block?(inner)
|
|
49
|
+
IR::Parenthesized.new(inner)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def visit_call(node)
|
|
53
|
+
return visit_call_with_block(node) if node.block
|
|
54
|
+
|
|
55
|
+
visit_plain_call(node)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def visit_plain_call(node)
|
|
59
|
+
method_name = node.name.to_s
|
|
60
|
+
receiver = normalize_expression(visit(node.receiver)) if node.receiver
|
|
61
|
+
args = node.arguments&.arguments&.map { |arg| normalize_expression(visit(arg)) } || []
|
|
62
|
+
|
|
63
|
+
return IR::VarRef.new(method_name.to_sym, infer_param_type(method_name.to_sym)) if parameter_reference_call?(method_name, receiver, args)
|
|
64
|
+
return visit_receiver_call(method_name, receiver) if receiver_without_arguments?(node, receiver, args)
|
|
65
|
+
return IR::BinaryOp.new(method_name, receiver, args.first) if binary_operator_call?(method_name, receiver, args)
|
|
66
|
+
return IR::UnaryOp.new("-", receiver) if method_name == "-@" && receiver
|
|
67
|
+
return IR::UnaryOp.new("!", args.first) if method_name == "!" && args.length == 1
|
|
68
|
+
return IR::ArrayIndex.new(receiver, args.first) if method_name == "[]" && receiver && args.length == 1
|
|
69
|
+
|
|
70
|
+
IR::FuncCall.new(method_name.to_sym, args, receiver)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def visit_and(node)
|
|
74
|
+
left = normalize_expression(visit(node.left))
|
|
75
|
+
right = normalize_expression(visit(node.right))
|
|
76
|
+
IR::BinaryOp.new("&&", left, right, :bool)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def visit_or(node)
|
|
80
|
+
left = normalize_expression(visit(node.left))
|
|
81
|
+
right = normalize_expression(visit(node.right))
|
|
82
|
+
IR::BinaryOp.new("||", left, right, :bool)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def visit_not(node)
|
|
86
|
+
operand = normalize_expression(visit(node.expression))
|
|
87
|
+
IR::UnaryOp.new("!", operand, :bool)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def visit_array(node)
|
|
91
|
+
elements = node.elements.map { |elem| normalize_expression(visit(elem)) }
|
|
92
|
+
IR::ArrayLiteral.new(elements)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def visit_index(node)
|
|
96
|
+
array = normalize_expression(visit(node.receiver))
|
|
97
|
+
index = normalize_expression(visit(node.arguments.arguments.first))
|
|
98
|
+
IR::ArrayIndex.new(array, index)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def visit_constant_read(node)
|
|
102
|
+
name = node.name.to_s
|
|
103
|
+
return IR::Constant.new(name.to_sym, :float) if %w[PI TAU].include?(name)
|
|
104
|
+
|
|
105
|
+
IR::VarRef.new(name.to_sym)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def visit_constant_path(node)
|
|
109
|
+
path_parts = []
|
|
110
|
+
current = node
|
|
111
|
+
while current.is_a?(::Prism::ConstantPathNode)
|
|
112
|
+
path_parts.unshift(current.name.to_s)
|
|
113
|
+
current = current.parent
|
|
114
|
+
end
|
|
115
|
+
path_parts.unshift(current.name.to_s) if current.respond_to?(:name)
|
|
116
|
+
|
|
117
|
+
IR::VarRef.new(path_parts.join("_").to_sym)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def visit_global_variable_read(node)
|
|
121
|
+
IR::VarRef.new(node.name.to_s.sub(/^\$/, "").to_sym)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def normalize_expression(node)
|
|
125
|
+
return node unless node.is_a?(IR::IfStatement)
|
|
126
|
+
|
|
127
|
+
then_expr = extract_if_branch_expr(node.then_branch)
|
|
128
|
+
else_expr = extract_if_branch_expr(node.else_branch)
|
|
129
|
+
raise "Unsupported if-expression: branches must be single expressions" unless then_expr && else_expr
|
|
130
|
+
|
|
131
|
+
IR::Ternary.new(node.condition, then_expr, else_expr)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def extract_if_branch_expr(branch)
|
|
135
|
+
return nil unless branch
|
|
136
|
+
return branch unless branch.is_a?(IR::Block)
|
|
137
|
+
return nil if branch.statements.empty? || branch.statements.length != 1
|
|
138
|
+
|
|
139
|
+
branch.statements.first
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def single_statement_block?(node)
|
|
143
|
+
node.is_a?(IR::Block) && node.statements.length == 1
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def parameter_reference_call?(method_name, receiver, args)
|
|
147
|
+
!receiver && args.empty? && parameter_reference?(method_name.to_sym)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def receiver_without_arguments?(node, receiver, args)
|
|
151
|
+
receiver && args.empty? && !node.arguments
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def visit_receiver_call(method_name, receiver)
|
|
155
|
+
return receiver if method_name == "freeze"
|
|
156
|
+
return IR::FieldAccess.new(receiver, method_name, :float) if Builtins.single_component_field?(method_name)
|
|
157
|
+
return IR::Swizzle.new(receiver, method_name, Builtins.swizzle_type(method_name)) if Builtins.swizzle?(method_name)
|
|
158
|
+
|
|
159
|
+
IR::FieldAccess.new(receiver, method_name)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def binary_operator_call?(method_name, receiver, args)
|
|
163
|
+
BINARY_OPERATORS.include?(method_name) && receiver && args.length == 1
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class ASTVisitor
|
|
6
|
+
class ScopeContext
|
|
7
|
+
Scope = Struct.new(:params, :declared_vars)
|
|
8
|
+
|
|
9
|
+
def initialize(params: [])
|
|
10
|
+
@scopes = [build_scope(params)]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def with_scope(params: [])
|
|
14
|
+
@scopes << build_scope(params)
|
|
15
|
+
yield
|
|
16
|
+
ensure
|
|
17
|
+
@scopes.pop
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parameter?(name)
|
|
21
|
+
@scopes.reverse_each.any? { |scope| scope.params.include?(name.to_sym) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def declared?(name)
|
|
25
|
+
@scopes.reverse_each.any? { |scope| scope.declared_vars.include?(name.to_sym) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def known_variable?(name)
|
|
29
|
+
parameter?(name) || declared?(name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def declare(name)
|
|
33
|
+
@scopes.last.declared_vars.add(name.to_sym)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def build_scope(params)
|
|
39
|
+
Scope.new(Set.new(Array(params).map(&:to_sym)), Set.new)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RLSL
|
|
4
|
+
module Prism
|
|
5
|
+
class ASTVisitor
|
|
6
|
+
class VisitorRegistry
|
|
7
|
+
TRANSPARENT_NODES = [
|
|
8
|
+
(::Prism::ArgumentsNode if defined?(::Prism::ArgumentsNode)),
|
|
9
|
+
(::Prism::BlockParametersNode if defined?(::Prism::BlockParametersNode)),
|
|
10
|
+
(::Prism::ParametersNode if defined?(::Prism::ParametersNode))
|
|
11
|
+
].compact.freeze
|
|
12
|
+
|
|
13
|
+
def self.build(*visitor_maps)
|
|
14
|
+
visitor_maps.each_with_object({}) do |visitor_map, merged|
|
|
15
|
+
merged.merge!(visitor_map)
|
|
16
|
+
end.freeze
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|