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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +23 -0
- data/CLAUDE.md +7 -231
- data/README.md +5 -5
- data/docs/SYNTAX.md +66 -0
- data/docs/VECTOR_SEMANTICS.md +286 -0
- data/docs/features/hierarchical-broadcasting.md +67 -1
- data/docs/features/input-declaration-system.md +16 -0
- data/docs/features/s-expression-printer.md +2 -2
- 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 +123 -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/input/validator.rb +1 -1
- 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 +30 -9
- 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/core/types/validator.rb +1 -1
- 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 +37 -19
- 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,13 +1,14 @@
|
|
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.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Muta
|
8
|
+
autorequire:
|
8
9
|
bindir: bin
|
9
10
|
cert_chain: []
|
10
|
-
date:
|
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/
|
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/
|
89
|
-
- lib/kumi/core/
|
90
|
-
- lib/kumi/core/compiler/
|
91
|
-
- lib/kumi/core/compiler/
|
92
|
-
- lib/kumi/core/compiler/
|
93
|
-
- lib/kumi/core/compiler/
|
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.
|
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.
|
218
|
+
rubygems_version: 3.5.22
|
219
|
+
signing_key:
|
205
220
|
specification_version: 4
|
206
|
-
summary:
|
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
|