kumi 0.0.10 → 0.0.11
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 +18 -0
- data/CLAUDE.md +7 -231
- data/README.md +1 -1
- data/docs/VECTOR_SEMANTICS.md +286 -0
- data/docs/features/hierarchical-broadcasting.md +1 -1
- data/docs/features/s-expression-printer.md +2 -2
- data/examples/deep_schema_compilation_and_evaluation_benchmark.rb +21 -15
- data/lib/kumi/analyzer.rb +34 -12
- data/lib/kumi/compiler.rb +2 -12
- data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +157 -64
- data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +1 -1
- data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +47 -0
- data/lib/kumi/core/analyzer/passes/input_collector.rb +118 -101
- data/lib/kumi/core/analyzer/passes/join_reduce_planning_pass.rb +293 -0
- data/lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb +993 -0
- data/lib/kumi/core/analyzer/passes/pass_base.rb +2 -2
- data/lib/kumi/core/analyzer/passes/scope_resolution_pass.rb +346 -0
- data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +2 -1
- data/lib/kumi/core/analyzer/passes/toposorter.rb +9 -3
- data/lib/kumi/core/analyzer/passes/type_checker.rb +3 -3
- data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +2 -2
- data/lib/kumi/core/analyzer/passes/{type_inferencer.rb → type_inferencer_pass.rb} +4 -4
- data/lib/kumi/core/analyzer/passes/unsat_detector.rb +2 -2
- data/lib/kumi/core/analyzer/plans.rb +52 -0
- data/lib/kumi/core/analyzer/structs/access_plan.rb +20 -0
- data/lib/kumi/core/analyzer/structs/input_meta.rb +29 -0
- data/lib/kumi/core/compiler/access_builder.rb +36 -0
- data/lib/kumi/core/compiler/access_planner.rb +219 -0
- data/lib/kumi/core/compiler/accessors/base.rb +69 -0
- data/lib/kumi/core/compiler/accessors/each_indexed_accessor.rb +84 -0
- data/lib/kumi/core/compiler/accessors/materialize_accessor.rb +55 -0
- data/lib/kumi/core/compiler/accessors/ravel_accessor.rb +73 -0
- data/lib/kumi/core/compiler/accessors/read_accessor.rb +41 -0
- data/lib/kumi/core/compiler_base.rb +2 -2
- data/lib/kumi/core/error_reporter.rb +6 -5
- data/lib/kumi/core/errors.rb +4 -0
- data/lib/kumi/core/explain.rb +157 -205
- data/lib/kumi/core/export/node_builders.rb +2 -2
- data/lib/kumi/core/export/node_serializers.rb +1 -1
- data/lib/kumi/core/function_registry/collection_functions.rb +21 -10
- data/lib/kumi/core/function_registry/conditional_functions.rb +14 -4
- data/lib/kumi/core/function_registry/function_builder.rb +142 -55
- data/lib/kumi/core/function_registry/logical_functions.rb +5 -5
- data/lib/kumi/core/function_registry/stat_functions.rb +2 -2
- data/lib/kumi/core/function_registry.rb +126 -108
- data/lib/kumi/core/ir/execution_engine/combinators.rb +117 -0
- data/lib/kumi/core/ir/execution_engine/interpreter.rb +336 -0
- data/lib/kumi/core/ir/execution_engine/values.rb +46 -0
- data/lib/kumi/core/ir/execution_engine.rb +50 -0
- data/lib/kumi/core/ir.rb +58 -0
- data/lib/kumi/core/ruby_parser/build_context.rb +2 -2
- data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +0 -12
- data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +36 -15
- data/lib/kumi/core/ruby_parser/input_builder.rb +5 -5
- data/lib/kumi/core/ruby_parser/parser.rb +1 -1
- data/lib/kumi/core/ruby_parser/schema_builder.rb +2 -2
- data/lib/kumi/core/ruby_parser/sugar.rb +7 -0
- data/lib/kumi/registry.rb +14 -79
- data/lib/kumi/runtime/executable.rb +213 -0
- data/lib/kumi/schema.rb +14 -3
- data/lib/kumi/schema_metadata.rb +2 -2
- data/lib/kumi/support/ir_dump.rb +491 -0
- data/lib/kumi/support/s_expression_printer.rb +1 -1
- data/lib/kumi/syntax/location.rb +5 -0
- data/lib/kumi/syntax/node.rb +0 -1
- data/lib/kumi/syntax/root.rb +2 -2
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +6 -15
- metadata +26 -15
- data/lib/kumi/core/cascade_executor_builder.rb +0 -132
- data/lib/kumi/core/compiled_schema.rb +0 -43
- data/lib/kumi/core/compiler/expression_compiler.rb +0 -146
- data/lib/kumi/core/compiler/function_invoker.rb +0 -55
- data/lib/kumi/core/compiler/path_traversal_compiler.rb +0 -158
- data/lib/kumi/core/compiler/reference_compiler.rb +0 -46
- data/lib/kumi/core/evaluation_wrapper.rb +0 -40
- data/lib/kumi/core/nested_structure_utils.rb +0 -78
- data/lib/kumi/core/schema_instance.rb +0 -115
- data/lib/kumi/core/vectorized_function_builder.rb +0 -88
- data/lib/kumi/js/compiler.rb +0 -878
- data/lib/kumi/js/function_registry.rb +0 -333
- data/migrate_to_core_iterative.rb +0 -938
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kumi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Muta
|
@@ -31,6 +31,7 @@ extra_rdoc_files: []
|
|
31
31
|
files:
|
32
32
|
- ".rspec"
|
33
33
|
- ".rubocop.yml"
|
34
|
+
- CHANGELOG.md
|
34
35
|
- CLAUDE.md
|
35
36
|
- LICENSE.txt
|
36
37
|
- README.md
|
@@ -39,6 +40,7 @@ files:
|
|
39
40
|
- docs/DSL.md
|
40
41
|
- docs/FUNCTIONS.md
|
41
42
|
- docs/SYNTAX.md
|
43
|
+
- docs/VECTOR_SEMANTICS.md
|
42
44
|
- docs/compiler_design_principles.md
|
43
45
|
- docs/development/README.md
|
44
46
|
- docs/development/error-reporting.md
|
@@ -74,23 +76,31 @@ files:
|
|
74
76
|
- lib/kumi/core/analyzer/passes/broadcast_detector.rb
|
75
77
|
- lib/kumi/core/analyzer/passes/declaration_validator.rb
|
76
78
|
- lib/kumi/core/analyzer/passes/dependency_resolver.rb
|
79
|
+
- lib/kumi/core/analyzer/passes/input_access_planner_pass.rb
|
77
80
|
- lib/kumi/core/analyzer/passes/input_collector.rb
|
81
|
+
- lib/kumi/core/analyzer/passes/join_reduce_planning_pass.rb
|
82
|
+
- lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb
|
78
83
|
- lib/kumi/core/analyzer/passes/name_indexer.rb
|
79
84
|
- lib/kumi/core/analyzer/passes/pass_base.rb
|
85
|
+
- lib/kumi/core/analyzer/passes/scope_resolution_pass.rb
|
80
86
|
- lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb
|
81
87
|
- lib/kumi/core/analyzer/passes/toposorter.rb
|
82
88
|
- lib/kumi/core/analyzer/passes/type_checker.rb
|
83
89
|
- lib/kumi/core/analyzer/passes/type_consistency_checker.rb
|
84
|
-
- lib/kumi/core/analyzer/passes/
|
90
|
+
- lib/kumi/core/analyzer/passes/type_inferencer_pass.rb
|
85
91
|
- lib/kumi/core/analyzer/passes/unsat_detector.rb
|
86
92
|
- lib/kumi/core/analyzer/passes/visitor_pass.rb
|
93
|
+
- lib/kumi/core/analyzer/plans.rb
|
94
|
+
- lib/kumi/core/analyzer/structs/access_plan.rb
|
95
|
+
- lib/kumi/core/analyzer/structs/input_meta.rb
|
87
96
|
- lib/kumi/core/atom_unsat_solver.rb
|
88
|
-
- lib/kumi/core/
|
89
|
-
- lib/kumi/core/
|
90
|
-
- lib/kumi/core/compiler/
|
91
|
-
- lib/kumi/core/compiler/
|
92
|
-
- lib/kumi/core/compiler/
|
93
|
-
- lib/kumi/core/compiler/
|
97
|
+
- lib/kumi/core/compiler/access_builder.rb
|
98
|
+
- lib/kumi/core/compiler/access_planner.rb
|
99
|
+
- lib/kumi/core/compiler/accessors/base.rb
|
100
|
+
- lib/kumi/core/compiler/accessors/each_indexed_accessor.rb
|
101
|
+
- lib/kumi/core/compiler/accessors/materialize_accessor.rb
|
102
|
+
- lib/kumi/core/compiler/accessors/ravel_accessor.rb
|
103
|
+
- lib/kumi/core/compiler/accessors/read_accessor.rb
|
94
104
|
- lib/kumi/core/compiler_base.rb
|
95
105
|
- lib/kumi/core/constraint_relationship_solver.rb
|
96
106
|
- lib/kumi/core/domain/enum_analyzer.rb
|
@@ -100,7 +110,6 @@ files:
|
|
100
110
|
- lib/kumi/core/error_reporter.rb
|
101
111
|
- lib/kumi/core/error_reporting.rb
|
102
112
|
- lib/kumi/core/errors.rb
|
103
|
-
- lib/kumi/core/evaluation_wrapper.rb
|
104
113
|
- lib/kumi/core/explain.rb
|
105
114
|
- lib/kumi/core/export.rb
|
106
115
|
- lib/kumi/core/export/deserializer.rb
|
@@ -122,10 +131,14 @@ files:
|
|
122
131
|
- lib/kumi/core/input/type_matcher.rb
|
123
132
|
- lib/kumi/core/input/validator.rb
|
124
133
|
- lib/kumi/core/input/violation_creator.rb
|
134
|
+
- lib/kumi/core/ir.rb
|
135
|
+
- lib/kumi/core/ir/execution_engine.rb
|
136
|
+
- lib/kumi/core/ir/execution_engine/combinators.rb
|
137
|
+
- lib/kumi/core/ir/execution_engine/interpreter.rb
|
138
|
+
- lib/kumi/core/ir/execution_engine/values.rb
|
125
139
|
- lib/kumi/core/json_schema.rb
|
126
140
|
- lib/kumi/core/json_schema/generator.rb
|
127
141
|
- lib/kumi/core/json_schema/validator.rb
|
128
|
-
- lib/kumi/core/nested_structure_utils.rb
|
129
142
|
- lib/kumi/core/ruby_parser.rb
|
130
143
|
- lib/kumi/core/ruby_parser/build_context.rb
|
131
144
|
- lib/kumi/core/ruby_parser/declaration_reference_proxy.rb
|
@@ -140,7 +153,6 @@ files:
|
|
140
153
|
- lib/kumi/core/ruby_parser/parser.rb
|
141
154
|
- lib/kumi/core/ruby_parser/schema_builder.rb
|
142
155
|
- lib/kumi/core/ruby_parser/sugar.rb
|
143
|
-
- lib/kumi/core/schema_instance.rb
|
144
156
|
- lib/kumi/core/types.rb
|
145
157
|
- lib/kumi/core/types/builder.rb
|
146
158
|
- lib/kumi/core/types/compatibility.rb
|
@@ -148,14 +160,13 @@ files:
|
|
148
160
|
- lib/kumi/core/types/inference.rb
|
149
161
|
- lib/kumi/core/types/normalizer.rb
|
150
162
|
- lib/kumi/core/types/validator.rb
|
151
|
-
- lib/kumi/core/vectorized_function_builder.rb
|
152
163
|
- lib/kumi/errors.rb
|
153
164
|
- lib/kumi/js.rb
|
154
|
-
- lib/kumi/js/compiler.rb
|
155
|
-
- lib/kumi/js/function_registry.rb
|
156
165
|
- lib/kumi/registry.rb
|
166
|
+
- lib/kumi/runtime/executable.rb
|
157
167
|
- lib/kumi/schema.rb
|
158
168
|
- lib/kumi/schema_metadata.rb
|
169
|
+
- lib/kumi/support/ir_dump.rb
|
159
170
|
- lib/kumi/support/s_expression_printer.rb
|
160
171
|
- lib/kumi/syntax/array_expression.rb
|
161
172
|
- lib/kumi/syntax/call_expression.rb
|
@@ -167,12 +178,12 @@ files:
|
|
167
178
|
- lib/kumi/syntax/input_element_reference.rb
|
168
179
|
- lib/kumi/syntax/input_reference.rb
|
169
180
|
- lib/kumi/syntax/literal.rb
|
181
|
+
- lib/kumi/syntax/location.rb
|
170
182
|
- lib/kumi/syntax/node.rb
|
171
183
|
- lib/kumi/syntax/root.rb
|
172
184
|
- lib/kumi/syntax/trait_declaration.rb
|
173
185
|
- lib/kumi/syntax/value_declaration.rb
|
174
186
|
- lib/kumi/version.rb
|
175
|
-
- migrate_to_core_iterative.rb
|
176
187
|
- scripts/analyze_broadcast_methods.rb
|
177
188
|
- scripts/analyze_cascade_methods.rb
|
178
189
|
- scripts/check_broadcasting_coverage.rb
|
@@ -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
|
@@ -1,158 +0,0 @@
|
|
1
|
-
module Kumi
|
2
|
-
module Core
|
3
|
-
module Compiler
|
4
|
-
module PathTraversalCompiler
|
5
|
-
private
|
6
|
-
|
7
|
-
def compile_element_field_reference(expr)
|
8
|
-
path = expr.path
|
9
|
-
|
10
|
-
# Check if we have nested paths metadata for this path
|
11
|
-
nested_paths = @analysis.state[:broadcasts]&.dig(:nested_paths)
|
12
|
-
unless nested_paths && nested_paths[path]
|
13
|
-
raise Errors::CompilationError, "Missing nested path metadata for #{path.inspect}. This indicates an analyzer bug."
|
14
|
-
end
|
15
|
-
|
16
|
-
# Determine operation mode based on context
|
17
|
-
operation_mode = determine_operation_mode_for_path(path)
|
18
|
-
path_metadata = nested_paths[path]
|
19
|
-
lambda do |ctx|
|
20
|
-
traverse_nested_path(ctx, path, operation_mode, path_metadata)
|
21
|
-
end
|
22
|
-
|
23
|
-
# ERROR: All nested paths should have metadata from the analyzer
|
24
|
-
# If we reach here, it means the BroadcastDetector didn't process this path
|
25
|
-
end
|
26
|
-
|
27
|
-
# Metadata-driven nested array traversal using the traversal algorithm from our design
|
28
|
-
def traverse_nested_path(data, path, operation_mode, path_metadata = nil)
|
29
|
-
access_mode = path_metadata&.dig(:access_mode) || :object
|
30
|
-
|
31
|
-
# Use specialized traversal for element access mode
|
32
|
-
result = if access_mode == :element
|
33
|
-
traverse_element_path(data, path, operation_mode)
|
34
|
-
else
|
35
|
-
traverse_path_recursive(data, path, operation_mode, access_mode)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Post-process result based on operation mode
|
39
|
-
case operation_mode
|
40
|
-
when :flatten
|
41
|
-
# Completely flatten nested arrays for aggregation
|
42
|
-
flatten_completely(result)
|
43
|
-
else
|
44
|
-
result
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Specialized traversal for element access mode
|
49
|
-
# In element access, we need to extract the specific field from EvaluationWrapper
|
50
|
-
# then apply progressive traversal based on path depth
|
51
|
-
def traverse_element_path(data, path, _operation_mode)
|
52
|
-
# Handle EvaluationWrapper by extracting the specific field
|
53
|
-
if data.is_a?(Core::EvaluationWrapper)
|
54
|
-
field_name = path.first
|
55
|
-
array_data = data[field_name]
|
56
|
-
|
57
|
-
# Always apply progressive traversal based on path depth
|
58
|
-
# This gives us the structure at the correct nesting level for both
|
59
|
-
# broadcast operations and structure operations
|
60
|
-
if array_data.is_a?(Array) && path.length > 1
|
61
|
-
# Flatten exactly (path_depth - 1) levels to get the desired nesting level
|
62
|
-
array_data.flatten(path.length - 1)
|
63
|
-
else
|
64
|
-
array_data
|
65
|
-
end
|
66
|
-
else
|
67
|
-
data
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def traverse_path_recursive(data, path, operation_mode, access_mode = :object, original_path_length = nil)
|
72
|
-
# Track original path length to determine traversal depth
|
73
|
-
original_path_length ||= path.length
|
74
|
-
current_depth = original_path_length - path.length
|
75
|
-
|
76
|
-
return data if path.empty?
|
77
|
-
|
78
|
-
field = path.first
|
79
|
-
remaining_path = path[1..]
|
80
|
-
|
81
|
-
if remaining_path.empty?
|
82
|
-
# Final field - extract based on operation mode
|
83
|
-
case operation_mode
|
84
|
-
when :broadcast, :flatten
|
85
|
-
# Extract field preserving array structure
|
86
|
-
extract_field_preserving_structure(data, field, access_mode, current_depth)
|
87
|
-
else
|
88
|
-
# Simple field access
|
89
|
-
if data.is_a?(Array)
|
90
|
-
data.map do |item|
|
91
|
-
access_field(item, field, access_mode, current_depth)
|
92
|
-
end
|
93
|
-
else
|
94
|
-
access_field(data, field, access_mode, current_depth)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
elsif data.is_a?(Array)
|
98
|
-
# Intermediate step - traverse deeper
|
99
|
-
# Array of items - traverse each item
|
100
|
-
data.map do |item|
|
101
|
-
traverse_path_recursive(access_field(item, field, access_mode, current_depth), remaining_path, operation_mode, access_mode,
|
102
|
-
original_path_length)
|
103
|
-
end
|
104
|
-
else
|
105
|
-
# Single item - traverse directly
|
106
|
-
traverse_path_recursive(access_field(data, field, access_mode, current_depth), remaining_path, operation_mode, access_mode,
|
107
|
-
original_path_length)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def extract_field_preserving_structure(data, field, access_mode = :object, depth = 0)
|
112
|
-
if data.is_a?(Array)
|
113
|
-
data.map { |item| extract_field_preserving_structure(item, field, access_mode, depth) }
|
114
|
-
else
|
115
|
-
access_field(data, field, access_mode, depth)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def access_field(data, field, access_mode, _depth = 0)
|
120
|
-
case access_mode
|
121
|
-
when :element
|
122
|
-
# Element access mode - for nested arrays, we need to traverse one level deeper
|
123
|
-
# This enables progressive path traversal like input.cube.layer.row.value
|
124
|
-
if data.is_a?(Core::EvaluationWrapper)
|
125
|
-
data[field]
|
126
|
-
elsif data.is_a?(Array)
|
127
|
-
# For element access, flatten one level to traverse deeper into nested structure
|
128
|
-
data.flatten(1)
|
129
|
-
else
|
130
|
-
# If not an array, return as-is (leaf level)
|
131
|
-
data
|
132
|
-
end
|
133
|
-
when :object
|
134
|
-
# Object access mode - normal hash/object field access
|
135
|
-
data[field]
|
136
|
-
else
|
137
|
-
# Default to object access
|
138
|
-
data[field]
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def flatten_completely(data)
|
143
|
-
result = []
|
144
|
-
flatten_recursive(data, result)
|
145
|
-
result
|
146
|
-
end
|
147
|
-
|
148
|
-
def flatten_recursive(data, result)
|
149
|
-
if data.is_a?(Array)
|
150
|
-
data.each { |item| flatten_recursive(item, result) }
|
151
|
-
else
|
152
|
-
result << data
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|