kumi 0.0.10 → 0.0.12

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +23 -0
  4. data/CLAUDE.md +7 -231
  5. data/README.md +5 -5
  6. data/docs/SYNTAX.md +66 -0
  7. data/docs/VECTOR_SEMANTICS.md +286 -0
  8. data/docs/features/hierarchical-broadcasting.md +67 -1
  9. data/docs/features/input-declaration-system.md +16 -0
  10. data/docs/features/s-expression-printer.md +2 -2
  11. data/lib/kumi/analyzer.rb +34 -12
  12. data/lib/kumi/compiler.rb +2 -12
  13. data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +157 -64
  14. data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +1 -1
  15. data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +47 -0
  16. data/lib/kumi/core/analyzer/passes/input_collector.rb +123 -101
  17. data/lib/kumi/core/analyzer/passes/join_reduce_planning_pass.rb +293 -0
  18. data/lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb +993 -0
  19. data/lib/kumi/core/analyzer/passes/pass_base.rb +2 -2
  20. data/lib/kumi/core/analyzer/passes/scope_resolution_pass.rb +346 -0
  21. data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +2 -1
  22. data/lib/kumi/core/analyzer/passes/toposorter.rb +9 -3
  23. data/lib/kumi/core/analyzer/passes/type_checker.rb +3 -3
  24. data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +2 -2
  25. data/lib/kumi/core/analyzer/passes/{type_inferencer.rb → type_inferencer_pass.rb} +4 -4
  26. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +2 -2
  27. data/lib/kumi/core/analyzer/plans.rb +52 -0
  28. data/lib/kumi/core/analyzer/structs/access_plan.rb +20 -0
  29. data/lib/kumi/core/analyzer/structs/input_meta.rb +29 -0
  30. data/lib/kumi/core/compiler/access_builder.rb +36 -0
  31. data/lib/kumi/core/compiler/access_planner.rb +219 -0
  32. data/lib/kumi/core/compiler/accessors/base.rb +69 -0
  33. data/lib/kumi/core/compiler/accessors/each_indexed_accessor.rb +84 -0
  34. data/lib/kumi/core/compiler/accessors/materialize_accessor.rb +55 -0
  35. data/lib/kumi/core/compiler/accessors/ravel_accessor.rb +73 -0
  36. data/lib/kumi/core/compiler/accessors/read_accessor.rb +41 -0
  37. data/lib/kumi/core/compiler_base.rb +2 -2
  38. data/lib/kumi/core/error_reporter.rb +6 -5
  39. data/lib/kumi/core/errors.rb +4 -0
  40. data/lib/kumi/core/explain.rb +157 -205
  41. data/lib/kumi/core/export/node_builders.rb +2 -2
  42. data/lib/kumi/core/export/node_serializers.rb +1 -1
  43. data/lib/kumi/core/function_registry/collection_functions.rb +21 -10
  44. data/lib/kumi/core/function_registry/conditional_functions.rb +14 -4
  45. data/lib/kumi/core/function_registry/function_builder.rb +142 -55
  46. data/lib/kumi/core/function_registry/logical_functions.rb +5 -5
  47. data/lib/kumi/core/function_registry/stat_functions.rb +2 -2
  48. data/lib/kumi/core/function_registry.rb +126 -108
  49. data/lib/kumi/core/input/validator.rb +1 -1
  50. data/lib/kumi/core/ir/execution_engine/combinators.rb +117 -0
  51. data/lib/kumi/core/ir/execution_engine/interpreter.rb +336 -0
  52. data/lib/kumi/core/ir/execution_engine/values.rb +46 -0
  53. data/lib/kumi/core/ir/execution_engine.rb +50 -0
  54. data/lib/kumi/core/ir.rb +58 -0
  55. data/lib/kumi/core/ruby_parser/build_context.rb +2 -2
  56. data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +0 -12
  57. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +36 -15
  58. data/lib/kumi/core/ruby_parser/input_builder.rb +30 -9
  59. data/lib/kumi/core/ruby_parser/parser.rb +1 -1
  60. data/lib/kumi/core/ruby_parser/schema_builder.rb +2 -2
  61. data/lib/kumi/core/ruby_parser/sugar.rb +7 -0
  62. data/lib/kumi/core/types/validator.rb +1 -1
  63. data/lib/kumi/registry.rb +14 -79
  64. data/lib/kumi/runtime/executable.rb +213 -0
  65. data/lib/kumi/schema.rb +14 -3
  66. data/lib/kumi/schema_metadata.rb +2 -2
  67. data/lib/kumi/support/ir_dump.rb +491 -0
  68. data/lib/kumi/support/s_expression_printer.rb +1 -1
  69. data/lib/kumi/syntax/location.rb +5 -0
  70. data/lib/kumi/syntax/node.rb +0 -1
  71. data/lib/kumi/syntax/root.rb +2 -2
  72. data/lib/kumi/version.rb +1 -1
  73. data/lib/kumi.rb +6 -15
  74. metadata +37 -19
  75. data/lib/kumi/core/cascade_executor_builder.rb +0 -132
  76. data/lib/kumi/core/compiled_schema.rb +0 -43
  77. data/lib/kumi/core/compiler/expression_compiler.rb +0 -146
  78. data/lib/kumi/core/compiler/function_invoker.rb +0 -55
  79. data/lib/kumi/core/compiler/path_traversal_compiler.rb +0 -158
  80. data/lib/kumi/core/compiler/reference_compiler.rb +0 -46
  81. data/lib/kumi/core/evaluation_wrapper.rb +0 -40
  82. data/lib/kumi/core/nested_structure_utils.rb +0 -78
  83. data/lib/kumi/core/schema_instance.rb +0 -115
  84. data/lib/kumi/core/vectorized_function_builder.rb +0 -88
  85. data/lib/kumi/js/compiler.rb +0 -878
  86. data/lib/kumi/js/function_registry.rb +0 -333
  87. data/migrate_to_core_iterative.rb +0 -938
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kumi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - André Muta
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2025-08-14 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: zeitwerk
@@ -23,6 +24,7 @@ dependencies:
23
24
  - - "~>"
24
25
  - !ruby/object:Gem::Version
25
26
  version: 2.6.0
27
+ description:
26
28
  email:
27
29
  - andremuta@gmail.com
28
30
  executables: []
@@ -31,6 +33,7 @@ extra_rdoc_files: []
31
33
  files:
32
34
  - ".rspec"
33
35
  - ".rubocop.yml"
36
+ - CHANGELOG.md
34
37
  - CLAUDE.md
35
38
  - LICENSE.txt
36
39
  - README.md
@@ -39,6 +42,7 @@ files:
39
42
  - docs/DSL.md
40
43
  - docs/FUNCTIONS.md
41
44
  - docs/SYNTAX.md
45
+ - docs/VECTOR_SEMANTICS.md
42
46
  - docs/compiler_design_principles.md
43
47
  - docs/development/README.md
44
48
  - docs/development/error-reporting.md
@@ -74,23 +78,31 @@ files:
74
78
  - lib/kumi/core/analyzer/passes/broadcast_detector.rb
75
79
  - lib/kumi/core/analyzer/passes/declaration_validator.rb
76
80
  - lib/kumi/core/analyzer/passes/dependency_resolver.rb
81
+ - lib/kumi/core/analyzer/passes/input_access_planner_pass.rb
77
82
  - lib/kumi/core/analyzer/passes/input_collector.rb
83
+ - lib/kumi/core/analyzer/passes/join_reduce_planning_pass.rb
84
+ - lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb
78
85
  - lib/kumi/core/analyzer/passes/name_indexer.rb
79
86
  - lib/kumi/core/analyzer/passes/pass_base.rb
87
+ - lib/kumi/core/analyzer/passes/scope_resolution_pass.rb
80
88
  - lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb
81
89
  - lib/kumi/core/analyzer/passes/toposorter.rb
82
90
  - lib/kumi/core/analyzer/passes/type_checker.rb
83
91
  - lib/kumi/core/analyzer/passes/type_consistency_checker.rb
84
- - lib/kumi/core/analyzer/passes/type_inferencer.rb
92
+ - lib/kumi/core/analyzer/passes/type_inferencer_pass.rb
85
93
  - lib/kumi/core/analyzer/passes/unsat_detector.rb
86
94
  - lib/kumi/core/analyzer/passes/visitor_pass.rb
95
+ - lib/kumi/core/analyzer/plans.rb
96
+ - lib/kumi/core/analyzer/structs/access_plan.rb
97
+ - lib/kumi/core/analyzer/structs/input_meta.rb
87
98
  - lib/kumi/core/atom_unsat_solver.rb
88
- - lib/kumi/core/cascade_executor_builder.rb
89
- - lib/kumi/core/compiled_schema.rb
90
- - lib/kumi/core/compiler/expression_compiler.rb
91
- - lib/kumi/core/compiler/function_invoker.rb
92
- - lib/kumi/core/compiler/path_traversal_compiler.rb
93
- - lib/kumi/core/compiler/reference_compiler.rb
99
+ - lib/kumi/core/compiler/access_builder.rb
100
+ - lib/kumi/core/compiler/access_planner.rb
101
+ - lib/kumi/core/compiler/accessors/base.rb
102
+ - lib/kumi/core/compiler/accessors/each_indexed_accessor.rb
103
+ - lib/kumi/core/compiler/accessors/materialize_accessor.rb
104
+ - lib/kumi/core/compiler/accessors/ravel_accessor.rb
105
+ - lib/kumi/core/compiler/accessors/read_accessor.rb
94
106
  - lib/kumi/core/compiler_base.rb
95
107
  - lib/kumi/core/constraint_relationship_solver.rb
96
108
  - lib/kumi/core/domain/enum_analyzer.rb
@@ -100,7 +112,6 @@ files:
100
112
  - lib/kumi/core/error_reporter.rb
101
113
  - lib/kumi/core/error_reporting.rb
102
114
  - lib/kumi/core/errors.rb
103
- - lib/kumi/core/evaluation_wrapper.rb
104
115
  - lib/kumi/core/explain.rb
105
116
  - lib/kumi/core/export.rb
106
117
  - lib/kumi/core/export/deserializer.rb
@@ -122,10 +133,14 @@ files:
122
133
  - lib/kumi/core/input/type_matcher.rb
123
134
  - lib/kumi/core/input/validator.rb
124
135
  - lib/kumi/core/input/violation_creator.rb
136
+ - lib/kumi/core/ir.rb
137
+ - lib/kumi/core/ir/execution_engine.rb
138
+ - lib/kumi/core/ir/execution_engine/combinators.rb
139
+ - lib/kumi/core/ir/execution_engine/interpreter.rb
140
+ - lib/kumi/core/ir/execution_engine/values.rb
125
141
  - lib/kumi/core/json_schema.rb
126
142
  - lib/kumi/core/json_schema/generator.rb
127
143
  - lib/kumi/core/json_schema/validator.rb
128
- - lib/kumi/core/nested_structure_utils.rb
129
144
  - lib/kumi/core/ruby_parser.rb
130
145
  - lib/kumi/core/ruby_parser/build_context.rb
131
146
  - lib/kumi/core/ruby_parser/declaration_reference_proxy.rb
@@ -140,7 +155,6 @@ files:
140
155
  - lib/kumi/core/ruby_parser/parser.rb
141
156
  - lib/kumi/core/ruby_parser/schema_builder.rb
142
157
  - lib/kumi/core/ruby_parser/sugar.rb
143
- - lib/kumi/core/schema_instance.rb
144
158
  - lib/kumi/core/types.rb
145
159
  - lib/kumi/core/types/builder.rb
146
160
  - lib/kumi/core/types/compatibility.rb
@@ -148,14 +162,13 @@ files:
148
162
  - lib/kumi/core/types/inference.rb
149
163
  - lib/kumi/core/types/normalizer.rb
150
164
  - lib/kumi/core/types/validator.rb
151
- - lib/kumi/core/vectorized_function_builder.rb
152
165
  - lib/kumi/errors.rb
153
166
  - lib/kumi/js.rb
154
- - lib/kumi/js/compiler.rb
155
- - lib/kumi/js/function_registry.rb
156
167
  - lib/kumi/registry.rb
168
+ - lib/kumi/runtime/executable.rb
157
169
  - lib/kumi/schema.rb
158
170
  - lib/kumi/schema_metadata.rb
171
+ - lib/kumi/support/ir_dump.rb
159
172
  - lib/kumi/support/s_expression_printer.rb
160
173
  - lib/kumi/syntax/array_expression.rb
161
174
  - lib/kumi/syntax/call_expression.rb
@@ -167,12 +180,12 @@ files:
167
180
  - lib/kumi/syntax/input_element_reference.rb
168
181
  - lib/kumi/syntax/input_reference.rb
169
182
  - lib/kumi/syntax/literal.rb
183
+ - lib/kumi/syntax/location.rb
170
184
  - lib/kumi/syntax/node.rb
171
185
  - lib/kumi/syntax/root.rb
172
186
  - lib/kumi/syntax/trait_declaration.rb
173
187
  - lib/kumi/syntax/value_declaration.rb
174
188
  - lib/kumi/version.rb
175
- - migrate_to_core_iterative.rb
176
189
  - scripts/analyze_broadcast_methods.rb
177
190
  - scripts/analyze_cascade_methods.rb
178
191
  - scripts/check_broadcasting_coverage.rb
@@ -187,6 +200,7 @@ metadata:
187
200
  source_code_uri: https://github.com/amuta/kumi
188
201
  changelog_uri: https://github.com/amuta/kumi/blob/main/CHANGELOG.md
189
202
  rubygems_mfa_required: 'true'
203
+ post_install_message:
190
204
  rdoc_options: []
191
205
  require_paths:
192
206
  - lib
@@ -194,14 +208,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
208
  requirements:
195
209
  - - ">="
196
210
  - !ruby/object:Gem::Version
197
- version: 3.0.0
211
+ version: 3.1.0
198
212
  required_rubygems_version: !ruby/object:Gem::Requirement
199
213
  requirements:
200
214
  - - ">="
201
215
  - !ruby/object:Gem::Version
202
216
  version: '0'
203
217
  requirements: []
204
- rubygems_version: 3.7.1
218
+ rubygems_version: 3.5.22
219
+ signing_key:
205
220
  specification_version: 4
206
- summary: A Declarative logic framework with static analysis for Ruby.
221
+ summary: Kumi is a declarative rules-and-calculation DSL for Ruby that compiles your
222
+ business logic into a typed, analyzable dependency graph with
223
+ vector semantics over nested data. It does static checks at definition time, lowers
224
+ to a small IR, and runs with consistent, predictable behavior.
207
225
  test_files: []
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module Core
5
- # Builds cascade execution lambdas from analysis metadata
6
- class CascadeExecutorBuilder
7
- include NestedStructureUtils
8
-
9
- def self.build_executor(strategy, analysis_state)
10
- new(strategy, analysis_state).build
11
- end
12
-
13
- def initialize(strategy, analysis_state)
14
- @strategy = strategy
15
- @analysis_state = analysis_state
16
- end
17
-
18
- def build
19
- case @strategy[:mode]
20
- when :hierarchical
21
- build_hierarchical_executor
22
- when :nested_array, :deep_nested_array
23
- build_nested_array_executor
24
- when :simple_array
25
- build_simple_array_executor
26
- else
27
- build_scalar_executor
28
- end
29
- end
30
-
31
- private
32
-
33
- def build_hierarchical_executor
34
- lambda do |cond_results, res_results, base_result, pairs|
35
- # Find the result structure to use as template (deepest structure)
36
- all_values = (res_results + cond_results + [base_result].compact).select { |v| v.is_a?(Array) }
37
- result_template = all_values.max_by { |v| calculate_array_depth(v) }
38
-
39
- return execute_scalar_cascade(cond_results, res_results, base_result, pairs) unless result_template
40
-
41
- # Apply hierarchical cascade logic using the result structure as template
42
- map_nested_structure(result_template) do |*indices|
43
- result = nil
44
-
45
- # Check conditional cases first with hierarchical broadcasting for conditions
46
- pairs.each_with_index do |(_cond, _res), pair_idx|
47
- cond_val = navigate_with_hierarchical_broadcasting(cond_results[pair_idx], indices, result_template)
48
- next unless cond_val
49
-
50
- res_val = navigate_nested_indices(res_results[pair_idx], indices)
51
- result = res_val
52
- break
53
- end
54
-
55
- # If no conditional case matched, use base case
56
- result = navigate_nested_indices(base_result, indices) if result.nil? && base_result
57
-
58
- result
59
- end
60
- end
61
- end
62
-
63
- def build_nested_array_executor
64
- lambda do |cond_results, res_results, base_result, pairs|
65
- # For nested arrays, we need to find the structure template
66
- structure_template = find_structure_template(cond_results + res_results + [base_result].compact)
67
- return execute_scalar_cascade(cond_results, res_results, base_result, pairs) unless structure_template
68
-
69
- # Apply cascade logic recursively through the nested structure
70
- map_nested_structure(structure_template) do |*indices|
71
- result = nil
72
-
73
- # Check conditional cases first
74
- pairs.each_with_index do |(_cond, _res), pair_idx|
75
- cond_val = navigate_nested_indices(cond_results[pair_idx], indices)
76
- next unless cond_val
77
-
78
- res_val = navigate_nested_indices(res_results[pair_idx], indices)
79
- result = res_val
80
- break
81
- end
82
-
83
- # If no conditional case matched, use base case
84
- result = navigate_nested_indices(base_result, indices) if result.nil? && base_result
85
-
86
- result
87
- end
88
- end
89
- end
90
-
91
- def build_simple_array_executor
92
- lambda do |cond_results, res_results, base_result, pairs|
93
- array_length = determine_array_length(cond_results + res_results + [base_result].compact)
94
-
95
- (0...array_length).map do |i|
96
- result = nil
97
- # Check conditional cases first
98
- pairs.each_with_index do |(_cond, _res), pair_idx|
99
- cond_val = extract_element_at_index(cond_results[pair_idx], i)
100
- next unless cond_val
101
-
102
- res_val = extract_element_at_index(res_results[pair_idx], i)
103
- result = res_val
104
- break
105
- end
106
-
107
- # If no conditional case matched, use base case
108
- result = extract_element_at_index(base_result, i) if result.nil? && base_result
109
-
110
- result
111
- end
112
- end
113
- end
114
-
115
- def build_scalar_executor
116
- lambda do |cond_results, res_results, base_result, pairs|
117
- pairs.each_with_index do |(_cond, _res), pair_idx|
118
- return res_results[pair_idx] if cond_results[pair_idx]
119
- end
120
- base_result
121
- end
122
- end
123
-
124
- def execute_scalar_cascade(cond_results, res_results, base_result, pairs)
125
- pairs.each_with_index do |(_cond, _res), pair_idx|
126
- return res_results[pair_idx] if cond_results[pair_idx]
127
- end
128
- base_result
129
- end
130
- end
131
- end
132
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module Core
5
- class CompiledSchema
6
- attr_reader :bindings
7
-
8
- def initialize(bindings)
9
- @bindings = bindings.freeze
10
- end
11
-
12
- def evaluate(ctx, *key_names)
13
- target_keys = key_names.empty? ? @bindings.keys : validate_keys(key_names)
14
-
15
- target_keys.each_with_object({}) do |key, result|
16
- result[key] = evaluate_binding(key, ctx)
17
- end
18
- end
19
-
20
- def evaluate_binding(key, ctx)
21
- memo = ctx.instance_variable_get(:@__schema_cache__)
22
- return memo[key] if memo&.key?(key)
23
-
24
- value = @bindings[key][1].call(ctx)
25
- memo[key] = value if memo
26
- value
27
- end
28
-
29
- private
30
-
31
- def hash_like?(obj)
32
- obj.respond_to?(:key?) && obj.respond_to?(:[])
33
- end
34
-
35
- def validate_keys(keys)
36
- unknown_keys = keys - @bindings.keys
37
- return keys if unknown_keys.empty?
38
-
39
- raise Kumi::Errors::RuntimeError, "No binding named #{unknown_keys.first}"
40
- end
41
- end
42
- end
43
- end
@@ -1,146 +0,0 @@
1
- module Kumi
2
- module Core
3
- module Compiler
4
- module ExpressionCompiler
5
- private
6
-
7
- def compile_list(expr)
8
- fns = expr.elements.map { |e| compile_expr(e) }
9
- ->(ctx) { fns.map { |fn| fn.call(ctx) } }
10
- end
11
-
12
- def compile_call(expr)
13
- fn_name = expr.fn_name
14
- arg_fns = expr.args.map { |a| compile_expr(a) }
15
-
16
- # Get compilation metadata once
17
- compilation_meta = @analysis.state[:broadcasts]&.dig(:compilation_metadata, @current_declaration)
18
-
19
- # Check if this is a vectorized operation
20
- if vectorized_operation?(expr)
21
- # Build vectorized executor at COMPILATION time
22
- executor = Core::VectorizedFunctionBuilder.build_executor(fn_name, compilation_meta, @analysis.state)
23
-
24
- lambda do |ctx|
25
- # Evaluate arguments and use pre-built executor at RUNTIME
26
- values = arg_fns.map { |fn| fn.call(ctx) }
27
- executor.call(values, expr.loc)
28
- end
29
- else
30
- # Use pre-computed function call strategy
31
- function_strategy = compilation_meta&.dig(:function_call_strategy) || {}
32
-
33
- if function_strategy[:flattening_required]
34
- flattening_info = @analysis.state[:broadcasts][:flattening_declarations][@current_declaration]
35
- ->(ctx) { invoke_function_with_flattening(fn_name, arg_fns, ctx, expr.loc, expr.args, flattening_info) }
36
- else
37
- ->(ctx) { invoke_function(fn_name, arg_fns, ctx, expr.loc) }
38
- end
39
- end
40
- end
41
-
42
- def compile_cascade(expr)
43
- # Use metadata to determine if this cascade is vectorized
44
- broadcast_meta = @analysis.state[:broadcasts]
45
- cascade_info = @current_declaration && broadcast_meta&.dig(:vectorized_operations, @current_declaration)
46
- is_vectorized = cascade_info && cascade_info[:source] == :cascade_with_vectorized_conditions_or_results
47
-
48
- # Separate conditional cases from base case
49
- conditional_cases = expr.cases.select(&:condition)
50
- base_case = expr.cases.find { |c| c.condition.nil? }
51
-
52
- # Compile conditional pairs
53
- pairs = conditional_cases.map do |c|
54
- condition_fn = if is_vectorized
55
- transform_vectorized_condition(c.condition)
56
- else
57
- compile_expr(c.condition)
58
- end
59
- result_fn = compile_expr(c.result)
60
- [condition_fn, result_fn]
61
- end
62
-
63
- # Compile base case
64
- base_fn = base_case ? compile_expr(base_case.result) : nil
65
-
66
- if is_vectorized
67
- # Capture the current declaration name in the closure
68
- current_decl_name = @current_declaration
69
-
70
- # Get pre-computed cascade strategy
71
- compilation_meta = @analysis.state[:broadcasts]&.dig(:compilation_metadata, current_decl_name)
72
- cascade_info = compilation_meta&.dig(:cascade_info) || {}
73
-
74
- # Build executor at COMPILATION time (outside the lambda)
75
- strategy = @analysis.state[:broadcasts][:cascade_strategies][current_decl_name]
76
- executor = strategy ? Core::CascadeExecutorBuilder.build_executor(strategy, @analysis.state) : nil
77
-
78
- # Metadata-driven vectorized cascade evaluation
79
- lambda do |ctx|
80
- # Evaluate all conditions and results
81
- cond_results = pairs.map { |cond, _res| cond.call(ctx) }
82
- res_results = pairs.map { |_cond, res| res.call(ctx) }
83
- base_result = base_fn&.call(ctx)
84
-
85
- if ENV["DEBUG_CASCADE"]
86
- puts "DEBUG: Vectorized cascade evaluation for #{current_decl_name}:"
87
- cond_results.each_with_index { |cr, i| puts " cond_results[#{i}]: #{cr.inspect}" }
88
- res_results.each_with_index { |rr, i| puts " res_results[#{i}]: #{rr.inspect}" }
89
- puts " base_result: #{base_result.inspect}"
90
- puts " Pre-computed cascade_info: #{cascade_info.inspect}"
91
- end
92
-
93
- # Use pre-built executor at RUNTIME
94
- if executor
95
- executor.call(cond_results, res_results, base_result, pairs)
96
- else
97
- # Fallback for cases without strategy
98
- pairs.each_with_index do |(_cond, _res), pair_idx|
99
- return res_results[pair_idx] if cond_results[pair_idx]
100
- end
101
- base_result
102
- end
103
- end
104
- else
105
- # Non-vectorized cascade - standard evaluation
106
- lambda do |ctx|
107
- pairs.each { |cond, res| return res.call(ctx) if cond.call(ctx) }
108
- # If no conditional case matched, return base case
109
- base_fn&.call(ctx)
110
- end
111
- end
112
- end
113
-
114
- def transform_vectorized_condition(condition_expr)
115
- if condition_expr.is_a?(Kumi::Syntax::CallExpression) &&
116
- condition_expr.fn_name == :cascade_and
117
-
118
- puts " transform_vectorized_condition: handling cascade_and with #{condition_expr.args.length} args" if ENV["DEBUG_CASCADE"]
119
-
120
- # For cascade_and in vectorized contexts, we need to compile it as a structure-level operation
121
- # rather than element-wise operation
122
- return compile_cascade_and_for_hierarchical_broadcasting(condition_expr)
123
- end
124
-
125
- # Otherwise compile normally
126
- compile_expr(condition_expr)
127
- end
128
-
129
- def compile_cascade_and_for_hierarchical_broadcasting(condition_expr)
130
- # Compile individual trait references
131
- trait_fns = condition_expr.args.map { |arg| compile_expr(arg) }
132
-
133
- lambda do |ctx|
134
- # Evaluate all traits to get their array structures
135
- trait_values = trait_fns.map { |fn| fn.call(ctx) }
136
-
137
- fn = Kumi::Registry.fetch(:cascade_and)
138
- result = fn.call(*trait_values)
139
-
140
- result
141
- end
142
- end
143
- end
144
- end
145
- end
146
- end
@@ -1,55 +0,0 @@
1
- module Kumi
2
- module Core
3
- module Compiler
4
- module FunctionInvoker
5
- private
6
-
7
- def invoke_function(name, arg_fns, ctx, loc)
8
- fn = Kumi::Registry.fetch(name)
9
- values = arg_fns.map { |fn| fn.call(ctx) }
10
-
11
- # REMOVED AUTO-FLATTENING: Let operations work on the structure they receive
12
- # If flattening is needed, it should be handled by explicit operation modes
13
- # in the InputElementReference compilation, not here.
14
- fn.call(*values)
15
- rescue StandardError => e
16
- # Preserve original error class and backtrace while adding context
17
- enhanced_message = "Error calling fn(:#{name}) at #{loc}: #{e.message}"
18
-
19
- if e.is_a?(Kumi::Core::Errors::Error)
20
- # Re-raise Kumi errors with enhanced message but preserve type
21
- e.define_singleton_method(:message) { enhanced_message }
22
- raise e
23
- else
24
- # For non-Kumi errors, wrap in RuntimeError but preserve original error info
25
- runtime_error = Errors::RuntimeError.new(enhanced_message)
26
- runtime_error.set_backtrace(e.backtrace)
27
- runtime_error.define_singleton_method(:cause) { e }
28
- raise runtime_error
29
- end
30
- end
31
-
32
- def invoke_function_with_flattening(name, arg_fns, ctx, loc, _original_args, _flattening_info)
33
- fn = Kumi::Registry.fetch(name)
34
-
35
- # Use pre-computed flattening indices from analysis
36
- compilation_meta = @analysis.state[:broadcasts]&.dig(:compilation_metadata, @current_declaration)
37
- flatten_indices = compilation_meta&.dig(:function_call_strategy, :flatten_argument_indices) || []
38
-
39
- values = arg_fns.map.with_index do |arg_fn, index|
40
- value = arg_fn.call(ctx)
41
- flatten_indices.include?(index) ? flatten_completely(value) : value
42
- end
43
-
44
- fn.call(*values)
45
- rescue StandardError => e
46
- enhanced_message = "Error calling fn(:#{name}) at #{loc}: #{e.message}"
47
- runtime_error = Errors::RuntimeError.new(enhanced_message)
48
- runtime_error.set_backtrace(e.backtrace)
49
- runtime_error.define_singleton_method(:cause) { e }
50
- raise runtime_error
51
- end
52
- end
53
- end
54
- end
55
- end