rlsl 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -1
  3. data/README.md +2 -0
  4. data/lib/rlsl/base_translator/call_parser.rb +68 -0
  5. data/lib/rlsl/base_translator/code_rewriter.rb +107 -0
  6. data/lib/rlsl/base_translator/code_scanner.rb +102 -0
  7. data/lib/rlsl/base_translator.rb +159 -63
  8. data/lib/rlsl/code_generator/math_prelude.rb +79 -0
  9. data/lib/rlsl/code_generator/ruby_wrapper_generator.rb +97 -0
  10. data/lib/rlsl/code_generator/shader_function_generator.rb +19 -0
  11. data/lib/rlsl/code_generator/template_context.rb +41 -0
  12. data/lib/rlsl/code_generator/uniform_struct_generator.rb +29 -0
  13. data/lib/rlsl/code_generator.rb +26 -201
  14. data/lib/rlsl/compiled_shader.rb +4 -12
  15. data/lib/rlsl/function_context.rb +31 -14
  16. data/lib/rlsl/glsl/translator.rb +19 -38
  17. data/lib/rlsl/msl/shader.rb +8 -38
  18. data/lib/rlsl/msl/translator.rb +19 -36
  19. data/lib/rlsl/msl/uniform_buffer_packer.rb +68 -0
  20. data/lib/rlsl/prism/ast_visitor/control_flow_visiting.rb +96 -0
  21. data/lib/rlsl/prism/ast_visitor/definition_visiting.rb +79 -0
  22. data/lib/rlsl/prism/ast_visitor/expression_visiting.rb +168 -0
  23. data/lib/rlsl/prism/ast_visitor/scope_context.rb +44 -0
  24. data/lib/rlsl/prism/ast_visitor/visitor_registry.rb +21 -0
  25. data/lib/rlsl/prism/ast_visitor.rb +54 -298
  26. data/lib/rlsl/prism/builtins/function_registry.rb +114 -0
  27. data/lib/rlsl/prism/builtins/operator_rules.rb +99 -0
  28. data/lib/rlsl/prism/builtins/swizzle_rules.rb +38 -0
  29. data/lib/rlsl/prism/builtins.rb +31 -148
  30. data/lib/rlsl/prism/compilation_unit.rb +7 -0
  31. data/lib/rlsl/prism/emitters/base_emitter/control_flow_emission.rb +83 -0
  32. data/lib/rlsl/prism/emitters/base_emitter/definition_emission.rb +104 -0
  33. data/lib/rlsl/prism/emitters/base_emitter/expression_emission.rb +92 -0
  34. data/lib/rlsl/prism/emitters/base_emitter/statement_emission.rb +89 -0
  35. data/lib/rlsl/prism/emitters/base_emitter.rb +68 -423
  36. data/lib/rlsl/prism/emitters/c_emitter.rb +78 -121
  37. data/lib/rlsl/prism/emitters/glsl_emitter.rb +30 -65
  38. data/lib/rlsl/prism/emitters/msl_emitter.rb +35 -62
  39. data/lib/rlsl/prism/emitters/target_emitter.rb +77 -0
  40. data/lib/rlsl/prism/emitters/target_profile.rb +34 -0
  41. data/lib/rlsl/prism/emitters/wgsl_emitter.rb +65 -61
  42. data/lib/rlsl/prism/ir/control_flow.rb +83 -0
  43. data/lib/rlsl/prism/ir/definitions.rb +67 -0
  44. data/lib/rlsl/prism/ir/expressions.rb +197 -0
  45. data/lib/rlsl/prism/ir/node.rb +21 -0
  46. data/lib/rlsl/prism/ir/nodes.rb +4 -371
  47. data/lib/rlsl/prism/ir/traversal.rb +62 -0
  48. data/lib/rlsl/prism/source_extractor/block_locator.rb +52 -0
  49. data/lib/rlsl/prism/source_extractor.rb +14 -133
  50. data/lib/rlsl/prism/source_unit/parser.rb +78 -0
  51. data/lib/rlsl/prism/source_unit.rb +42 -0
  52. data/lib/rlsl/prism/target_capability_validator.rb +85 -0
  53. data/lib/rlsl/prism/transpiler.rb +63 -60
  54. data/lib/rlsl/prism/type_inference/call_type_resolver.rb +35 -0
  55. data/lib/rlsl/prism/type_inference/call_validator.rb +71 -0
  56. data/lib/rlsl/prism/type_inference/collection_type_resolver.rb +80 -0
  57. data/lib/rlsl/prism/type_inference/control_flow_inferer.rb +66 -0
  58. data/lib/rlsl/prism/type_inference/definition_inferer.rb +41 -0
  59. data/lib/rlsl/prism/type_inference/expression_inferer.rb +92 -0
  60. data/lib/rlsl/prism/type_inference/field_type_resolver.rb +17 -0
  61. data/lib/rlsl/prism/type_inference/inferer_registry.rb +38 -0
  62. data/lib/rlsl/prism/type_inference/scope_stack.rb +47 -0
  63. data/lib/rlsl/prism/type_inference/type_environment.rb +112 -0
  64. data/lib/rlsl/prism/type_inference/type_shapes.rb +33 -0
  65. data/lib/rlsl/prism/type_inference.rb +114 -248
  66. data/lib/rlsl/runtime_shader.rb +47 -0
  67. data/lib/rlsl/shader_builder/build_service.rb +77 -0
  68. data/lib/rlsl/shader_builder/native_extension_compiler.rb +71 -0
  69. data/lib/rlsl/shader_builder/shader_definition.rb +68 -0
  70. data/lib/rlsl/shader_builder/source_resolver.rb +91 -0
  71. data/lib/rlsl/shader_builder.rb +22 -113
  72. data/lib/rlsl/types/catalog.rb +47 -0
  73. data/lib/rlsl/types/target_resolver.rb +15 -0
  74. data/lib/rlsl/types/type_spec.rb +167 -0
  75. data/lib/rlsl/types/value_normalizer.rb +81 -0
  76. data/lib/rlsl/types.rb +11 -29
  77. data/lib/rlsl/uniform_context.rb +6 -12
  78. data/lib/rlsl/version.rb +1 -1
  79. data/lib/rlsl/wgsl/translator.rb +23 -39
  80. data/lib/rlsl.rb +14 -12
  81. metadata +55 -2
@@ -3,139 +3,88 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class CEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "float",
9
- int: "int",
10
- bool: "int",
11
- vec2: "vec2",
12
- vec3: "vec3",
13
- vec4: "vec4",
14
- mat2: "mat2",
15
- mat3: "mat3",
16
- mat4: "mat4",
17
- sampler2D: "sampler2D"
18
- }.freeze
19
-
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "vec2_new",
22
- vec3: "vec3_new",
23
- vec4: "vec4_new"
24
- }.freeze
25
-
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "mat2_new",
28
- mat3: "mat3_new",
29
- mat4: "mat4_new"
30
- }.freeze
31
-
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "texture_sample",
34
- texture: "texture_sample",
35
- textureLod: "texture_sample_lod"
36
- }.freeze
37
-
38
- VECTOR_OPS = {
39
- "+" => "add",
40
- "-" => "sub",
41
- "*" => "mul",
42
- "/" => "div"
43
- }.freeze
44
-
45
- MATH_FUNCTIONS = {
46
- sin: "sinf",
47
- cos: "cosf",
48
- tan: "tanf",
49
- asin: "asinf",
50
- acos: "acosf",
51
- atan: "atanf",
52
- atan2: "atan2f",
53
- sqrt: "sqrtf",
54
- pow: "powf",
55
- exp: "expf",
56
- log: "logf",
57
- abs: "fabsf",
58
- floor: "floorf",
59
- ceil: "ceilf",
60
- min: "fminf",
61
- max: "fmaxf",
62
- fract: "fract",
63
- mod: "fmodf",
64
- clamp: "clamp_f",
65
- mix: "mix_f",
66
- smoothstep: "smoothstep",
67
- length: "vec_length",
68
- normalize: "vec_normalize",
69
- dot: "vec_dot"
70
- }.freeze
6
+ class CEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "float",
10
+ int: "int",
11
+ bool: "int",
12
+ vec2: "vec2",
13
+ vec3: "vec3",
14
+ vec4: "vec4",
15
+ mat2: "mat2",
16
+ mat3: "mat3",
17
+ mat4: "mat4",
18
+ sampler2D: "sampler2D"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "vec2_new",
22
+ vec3: "vec3_new",
23
+ vec4: "vec4_new"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "mat2_new",
27
+ mat3: "mat3_new",
28
+ mat4: "mat4_new"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "texture_sample",
32
+ texture: "texture_sample",
33
+ textureLod: "texture_sample_lod"
34
+ },
35
+ math_functions: {
36
+ sin: "sinf",
37
+ cos: "cosf",
38
+ tan: "tanf",
39
+ asin: "asinf",
40
+ acos: "acosf",
41
+ atan: "atanf",
42
+ atan2: "atan2f",
43
+ sqrt: "sqrtf",
44
+ pow: "powf",
45
+ exp: "expf",
46
+ log: "logf",
47
+ abs: "fabsf",
48
+ floor: "floorf",
49
+ ceil: "ceilf",
50
+ min: "fminf",
51
+ max: "fmaxf",
52
+ fract: "fract",
53
+ mod: "fmodf",
54
+ clamp: "clamp_f",
55
+ mix: "mix_f",
56
+ smoothstep: "smoothstep",
57
+ length: "vec_length",
58
+ normalize: "vec_normalize",
59
+ dot: "vec_dot"
60
+ },
61
+ vector_ops: {
62
+ "+" => "add",
63
+ "-" => "sub",
64
+ "*" => "mul",
65
+ "/" => "div"
66
+ },
67
+ call_resolver: :emit_profile_func_call,
68
+ binary_op_resolver: :emit_profile_binary_op
69
+ ).freeze
71
70
 
72
71
  protected
73
72
 
74
- def type_name(type)
75
- TYPE_MAP[type&.to_sym] || "float"
76
- end
77
-
78
73
  def format_number(value)
79
74
  formatted = super(value)
80
75
  "#{formatted}f"
81
76
  end
82
77
 
83
- def emit_func_call(node)
78
+ def emit_profile_func_call(node)
84
79
  name = node.name.to_sym
85
-
86
- if VECTOR_CONSTRUCTORS.key?(name)
87
- args = node.args.map { |arg| emit(arg) }.join(", ")
88
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
89
- end
90
-
91
- if MATRIX_CONSTRUCTORS.key?(name)
92
- args = node.args.map { |arg| emit(arg) }.join(", ")
93
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
94
- end
95
-
96
- if TEXTURE_FUNCTIONS.key?(name)
97
- args = node.args.map { |arg| emit(arg) }.join(", ")
98
- return "#{TEXTURE_FUNCTIONS[name]}(#{args})"
99
- end
100
-
101
- if %i[length normalize dot].include?(name) && node.args.first&.type
102
- vec_type = node.args.first.type
103
- if %i[vec2 vec3 vec4].include?(vec_type)
104
- func_name = "#{vec_type}_#{name}"
105
- args = node.args.map { |arg| emit(arg) }.join(", ")
106
- return "#{func_name}(#{args})"
107
- end
108
- end
109
-
110
- if MATH_FUNCTIONS.key?(name)
111
- func_name = MATH_FUNCTIONS[name]
112
-
113
- if name == :mix && node.args.first&.type
114
- first_type = node.args.first.type
115
- if %i[vec2 vec3 vec4].include?(first_type)
116
- func_name = "mix_v3"
117
- end
118
- end
119
-
120
- args = node.args.map { |arg| emit(arg) }.join(", ")
121
- return "#{func_name}(#{args})"
122
- end
123
-
124
- super
80
+ return emit_named_call("#{node.args.first.type}_#{name}", node.args) if vector_math_call?(name, node)
81
+ return emit_named_call("mix_v3", node.args) if vector_mix_call?(name, node)
125
82
  end
126
83
 
127
- def emit_binary_op(node)
128
- left_type = node.left.type
129
- op = node.operator
84
+ def emit_profile_binary_op(node)
85
+ return unless vector_type?(node.left.type) && profile.vector_ops.key?(node.operator)
130
86
 
131
- if vector_type?(left_type) && VECTOR_OPS.key?(op)
132
- vec_func = "#{left_type}_#{VECTOR_OPS[op]}"
133
- left = emit(node.left)
134
- right = emit(node.right)
135
- return "#{vec_func}(#{left}, #{right})"
136
- end
137
-
138
- super
87
+ emit_named_call("#{node.left.type}_#{profile.vector_ops[node.operator]}", [node.left, node.right])
139
88
  end
140
89
 
141
90
  def emit_bool_literal(node)
@@ -144,6 +93,14 @@ module RLSL
144
93
 
145
94
  private
146
95
 
96
+ def vector_math_call?(name, node)
97
+ %i[length normalize dot].include?(name) && vector_type?(node.args.first&.type)
98
+ end
99
+
100
+ def vector_mix_call?(name, node)
101
+ name == :mix && vector_type?(node.args.first&.type)
102
+ end
103
+
147
104
  def vector_type?(type)
148
105
  %i[vec2 vec3 vec4].include?(type)
149
106
  end
@@ -3,71 +3,36 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class GLSLEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "float",
9
- int: "int",
10
- bool: "bool",
11
- vec2: "vec2",
12
- vec3: "vec3",
13
- vec4: "vec4",
14
- mat2: "mat2",
15
- mat3: "mat3",
16
- mat4: "mat4",
17
- sampler2D: "sampler2D"
18
- }.freeze
19
-
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "vec2",
22
- vec3: "vec3",
23
- vec4: "vec4"
24
- }.freeze
25
-
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "mat2",
28
- mat3: "mat3",
29
- mat4: "mat4"
30
- }.freeze
31
-
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "texture2D",
34
- texture: "texture",
35
- textureLod: "textureLod"
36
- }.freeze
37
-
38
- protected
39
-
40
- def type_name(type)
41
- TYPE_MAP[type&.to_sym] || "float"
42
- end
43
-
44
- def emit_func_call(node)
45
- name = node.name.to_sym
46
-
47
- if VECTOR_CONSTRUCTORS.key?(name)
48
- args = node.args.map { |arg| emit(arg) }.join(", ")
49
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
50
- end
51
-
52
- if MATRIX_CONSTRUCTORS.key?(name)
53
- args = node.args.map { |arg| emit(arg) }.join(", ")
54
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
55
- end
56
-
57
- if TEXTURE_FUNCTIONS.key?(name)
58
- args = node.args.map { |arg| emit(arg) }.join(", ")
59
- return "#{TEXTURE_FUNCTIONS[name]}(#{args})"
60
- end
61
-
62
- args = node.args.map { |arg| emit(arg) }.join(", ")
63
- "#{name}(#{args})"
64
- end
65
-
66
- def emit_binary_op(node)
67
- left = emit_with_precedence(node.left, node.operator)
68
- right = emit_with_precedence(node.right, node.operator)
69
- "#{left} #{node.operator} #{right}"
70
- end
6
+ class GLSLEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "float",
10
+ int: "int",
11
+ bool: "bool",
12
+ vec2: "vec2",
13
+ vec3: "vec3",
14
+ vec4: "vec4",
15
+ mat2: "mat2",
16
+ mat3: "mat3",
17
+ mat4: "mat4",
18
+ sampler2D: "sampler2D"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "vec2",
22
+ vec3: "vec3",
23
+ vec4: "vec4"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "mat2",
27
+ mat3: "mat3",
28
+ mat4: "mat4"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "texture2D",
32
+ texture: "texture",
33
+ textureLod: "textureLod"
34
+ }
35
+ ).freeze
71
36
  end
72
37
  end
73
38
  end
@@ -3,72 +3,45 @@
3
3
  module RLSL
4
4
  module Prism
5
5
  module Emitters
6
- class MSLEmitter < BaseEmitter
7
- TYPE_MAP = {
8
- float: "float",
9
- int: "int",
10
- bool: "bool",
11
- vec2: "float2",
12
- vec3: "float3",
13
- vec4: "float4",
14
- mat2: "float2x2",
15
- mat3: "float3x3",
16
- mat4: "float4x4",
17
- sampler2D: "texture2d<float>"
18
- }.freeze
19
-
20
- VECTOR_CONSTRUCTORS = {
21
- vec2: "float2",
22
- vec3: "float3",
23
- vec4: "float4"
24
- }.freeze
25
-
26
- MATRIX_CONSTRUCTORS = {
27
- mat2: "float2x2",
28
- mat3: "float3x3",
29
- mat4: "float4x4"
30
- }.freeze
31
-
32
- TEXTURE_FUNCTIONS = {
33
- texture2D: "sample",
34
- texture: "sample",
35
- textureLod: "sample"
36
- }.freeze
6
+ class MSLEmitter < TargetEmitter
7
+ PROFILE = TargetProfile.new(
8
+ type_map: {
9
+ float: "float",
10
+ int: "int",
11
+ bool: "bool",
12
+ vec2: "float2",
13
+ vec3: "float3",
14
+ vec4: "float4",
15
+ mat2: "float2x2",
16
+ mat3: "float3x3",
17
+ mat4: "float4x4",
18
+ sampler2D: "texture2d<float>"
19
+ },
20
+ vector_constructors: {
21
+ vec2: "float2",
22
+ vec3: "float3",
23
+ vec4: "float4"
24
+ },
25
+ matrix_constructors: {
26
+ mat2: "float2x2",
27
+ mat3: "float3x3",
28
+ mat4: "float4x4"
29
+ },
30
+ texture_functions: {
31
+ texture2D: "sample",
32
+ texture: "sample",
33
+ textureLod: "sample"
34
+ }
35
+ ).freeze
37
36
 
38
37
  protected
39
38
 
40
- def type_name(type)
41
- TYPE_MAP[type&.to_sym] || "float"
42
- end
43
-
44
- def emit_func_call(node)
45
- name = node.name.to_sym
46
-
47
- if VECTOR_CONSTRUCTORS.key?(name)
48
- args = node.args.map { |arg| emit(arg) }.join(", ")
49
- return "#{VECTOR_CONSTRUCTORS[name]}(#{args})"
50
- end
51
-
52
- if MATRIX_CONSTRUCTORS.key?(name)
53
- args = node.args.map { |arg| emit(arg) }.join(", ")
54
- return "#{MATRIX_CONSTRUCTORS[name]}(#{args})"
55
- end
56
-
57
- # MSL texture sampling: texture.sample(sampler, uv)
58
- if TEXTURE_FUNCTIONS.key?(name) && node.args.length >= 2
59
- texture = emit(node.args[0])
60
- uv = emit(node.args[1])
61
- return "#{texture}.sample(textureSampler, #{uv})"
62
- end
63
-
64
- args = node.args.map { |arg| emit(arg) }.join(", ")
65
- "#{name}(#{args})"
66
- end
39
+ def emit_texture_call(name, node)
40
+ return unless profile.texture_functions.key?(name) && node.args.length >= 2
67
41
 
68
- def emit_binary_op(node)
69
- left = emit_with_precedence(node.left, node.operator)
70
- right = emit_with_precedence(node.right, node.operator)
71
- "#{left} #{node.operator} #{right}"
42
+ texture = emit(node.args[0])
43
+ uv = emit(node.args[1])
44
+ "#{texture}.sample(textureSampler, #{uv})"
72
45
  end
73
46
  end
74
47
  end
@@ -0,0 +1,77 @@
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
+ return emit_named_call(constructor_name, node.args) if constructor_name
23
+
24
+ texture_call = emit_texture_call(name, node)
25
+ return texture_call if texture_call
26
+
27
+ math_function = profile.math_functions[name]
28
+ return emit_named_call(math_function, node.args) if math_function
29
+
30
+ emit_named_call(name, node.args, receiver: node.receiver)
31
+ end
32
+
33
+ def emit_binary_op(node)
34
+ resolved_binary_op = emit_profile_binary_op(node)
35
+ return resolved_binary_op if resolved_binary_op
36
+
37
+ left = emit_with_precedence(node.left, node.operator)
38
+ right = emit_with_precedence(node.right, node.operator)
39
+ "#{left} #{node.operator} #{right}"
40
+ end
41
+
42
+ def default_type_name
43
+ profile.default_type_name
44
+ end
45
+
46
+ def emit_texture_call(name, node)
47
+ return unless profile.texture_functions.key?(name)
48
+
49
+ emit_named_call(profile.texture_functions[name], node.args)
50
+ end
51
+
52
+ def emit_named_call(name, args, receiver: nil)
53
+ rendered_args = []
54
+ rendered_args << emit(receiver) if receiver
55
+ rendered_args.concat(args.map { |arg| emit(arg) })
56
+ "#{name}(#{rendered_args.join(', ')})"
57
+ end
58
+
59
+ def emit_profile_call(node)
60
+ return unless profile.call_resolver
61
+
62
+ send(profile.call_resolver, node)
63
+ end
64
+
65
+ def emit_profile_binary_op(node)
66
+ return unless profile.binary_op_resolver
67
+
68
+ send(profile.binary_op_resolver, node)
69
+ end
70
+
71
+ def profile
72
+ self.class::PROFILE
73
+ end
74
+ end
75
+ end
76
+ end
77
+ 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