kumi 0.0.13 → 0.0.15
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/.rspec +0 -1
- data/BACKLOG.md +34 -0
- data/CHANGELOG.md +33 -0
- data/CLAUDE.md +4 -6
- data/README.md +0 -45
- data/config/functions.yaml +352 -0
- data/docs/dev/analyzer-debug.md +52 -0
- data/docs/dev/parse-command.md +64 -0
- data/docs/dev/vm-profiling.md +95 -0
- data/docs/features/README.md +0 -7
- data/docs/functions/analyzer_integration.md +199 -0
- data/docs/functions/signatures.md +171 -0
- data/examples/hash_objects_demo.rb +138 -0
- data/golden/array_operations/schema.kumi +17 -0
- data/golden/cascade_logic/schema.kumi +16 -0
- data/golden/mixed_nesting/schema.kumi +42 -0
- data/golden/simple_math/schema.kumi +10 -0
- data/lib/kumi/analyzer.rb +76 -22
- data/lib/kumi/compiler.rb +6 -5
- data/lib/kumi/core/analyzer/checkpoint.rb +72 -0
- data/lib/kumi/core/analyzer/debug.rb +167 -0
- data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +1 -3
- data/lib/kumi/core/analyzer/passes/function_signature_pass.rb +199 -0
- data/lib/kumi/core/analyzer/passes/ir_dependency_pass.rb +67 -0
- data/lib/kumi/core/analyzer/passes/load_input_cse.rb +120 -0
- data/lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb +72 -157
- data/lib/kumi/core/analyzer/passes/toposorter.rb +40 -36
- data/lib/kumi/core/analyzer/state_serde.rb +64 -0
- data/lib/kumi/core/analyzer/structs/access_plan.rb +12 -10
- data/lib/kumi/core/compiler/access_planner.rb +3 -2
- data/lib/kumi/core/function_registry/collection_functions.rb +3 -1
- data/lib/kumi/core/functions/dimension.rb +98 -0
- data/lib/kumi/core/functions/dtypes.rb +20 -0
- data/lib/kumi/core/functions/errors.rb +11 -0
- data/lib/kumi/core/functions/kernel_adapter.rb +45 -0
- data/lib/kumi/core/functions/loader.rb +119 -0
- data/lib/kumi/core/functions/registry_v2.rb +68 -0
- data/lib/kumi/core/functions/shape.rb +70 -0
- data/lib/kumi/core/functions/signature.rb +122 -0
- data/lib/kumi/core/functions/signature_parser.rb +86 -0
- data/lib/kumi/core/functions/signature_resolver.rb +272 -0
- data/lib/kumi/core/ir/execution_engine/interpreter.rb +110 -7
- data/lib/kumi/core/ir/execution_engine/profiler.rb +330 -0
- data/lib/kumi/core/ir/execution_engine.rb +6 -15
- data/lib/kumi/dev/ir.rb +75 -0
- data/lib/kumi/dev/parse.rb +105 -0
- data/lib/kumi/dev/profile_aggregator.rb +301 -0
- data/lib/kumi/dev/profile_runner.rb +199 -0
- data/lib/kumi/dev/runner.rb +85 -0
- data/lib/kumi/dev.rb +14 -0
- data/lib/kumi/frontends/ruby.rb +28 -0
- data/lib/kumi/frontends/text.rb +46 -0
- data/lib/kumi/frontends.rb +29 -0
- data/lib/kumi/kernels/ruby/aggregate_core.rb +105 -0
- data/lib/kumi/kernels/ruby/datetime_scalar.rb +21 -0
- data/lib/kumi/kernels/ruby/mask_scalar.rb +15 -0
- data/lib/kumi/kernels/ruby/scalar_core.rb +63 -0
- data/lib/kumi/kernels/ruby/string_scalar.rb +19 -0
- data/lib/kumi/kernels/ruby/vector_struct.rb +39 -0
- data/lib/kumi/runtime/executable.rb +108 -45
- data/lib/kumi/schema.rb +12 -6
- data/lib/kumi/support/diff.rb +22 -0
- data/lib/kumi/support/ir_render.rb +61 -0
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +3 -0
- data/performance_results.txt +63 -0
- data/scripts/test_mixed_nesting_performance.rb +206 -0
- metadata +50 -6
- data/docs/features/analysis-cascade-mutual-exclusion.md +0 -89
- data/docs/features/javascript-transpiler.md +0 -148
- data/lib/kumi/js.rb +0 -23
- data/lib/kumi/support/ir_dump.rb +0 -491
data/lib/kumi.rb
CHANGED
@@ -7,9 +7,12 @@ loader = Zeitwerk::Loader.for_gem
|
|
7
7
|
loader.ignore("#{__dir__}/kumi-cli")
|
8
8
|
loader.inflector.inflect(
|
9
9
|
"lower_to_ir_pass" => "LowerToIRPass",
|
10
|
+
"load_input_cse" => "LoadInputCSE",
|
11
|
+
"ir_dependency_pass" => "IRDependencyPass",
|
10
12
|
"vm" => "VM",
|
11
13
|
"ir" => "IR",
|
12
14
|
'ir_dump' => 'IRDump',
|
15
|
+
'ir_render' => 'IRRender',
|
13
16
|
)
|
14
17
|
loader.setup
|
15
18
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
=== MIXED NESTING SCHEMA PERFORMANCE TEST ===
|
2
|
+
Test run: 2025-08-21 01:31:06 -0300
|
3
|
+
Ruby version: 3.3.8
|
4
|
+
|
5
|
+
✅ Schema loaded successfully
|
6
|
+
|
7
|
+
=== COMPILATION PERFORMANCE ===
|
8
|
+
|
9
|
+
Tiny ( 1 items): 2.47ms
|
10
|
+
Small ( 4 items): 3.67ms
|
11
|
+
Medium ( 25 items): 1.86ms
|
12
|
+
Large (100 items): 1.92ms
|
13
|
+
XLarge (200 items): 2.96ms
|
14
|
+
Huge (250 items): 2.0ms
|
15
|
+
|
16
|
+
=== EXECUTION PERFORMANCE ===
|
17
|
+
|
18
|
+
Tiny ( 1 items): avg= 0.54ms, throughput= 1.8 items/ms
|
19
|
+
Small ( 4 items): avg= 0.64ms, throughput= 6.2 items/ms
|
20
|
+
Medium ( 25 items): avg= 1.15ms, throughput= 21.8 items/ms
|
21
|
+
Large (100 items): avg= 3.61ms, throughput= 27.7 items/ms
|
22
|
+
XLarge (200 items): avg= 7.25ms, throughput= 27.6 items/ms
|
23
|
+
Huge (250 items): avg= 11.8ms, throughput= 21.2 items/ms
|
24
|
+
|
25
|
+
=== SCALING ANALYSIS ===
|
26
|
+
|
27
|
+
50 items: 0.87ms (57.2 items/ms)
|
28
|
+
100 items: 1.45ms (68.9 items/ms)
|
29
|
+
200 items: 2.85ms (70.3 items/ms)
|
30
|
+
400 items: 5.75ms (69.6 items/ms)
|
31
|
+
800 items: 15.16ms (52.8 items/ms)
|
32
|
+
|
33
|
+
=== MEMORY ANALYSIS ===
|
34
|
+
|
35
|
+
Iteration 0: RSS=35472KB (Δ256KB)
|
36
|
+
Iteration 3: RSS=35472KB (Δ256KB)
|
37
|
+
Iteration 6: RSS=35472KB (Δ256KB)
|
38
|
+
Iteration 9: RSS=35472KB (Δ256KB)
|
39
|
+
|
40
|
+
=== SAMPLE OUTPUT VALIDATION ===
|
41
|
+
|
42
|
+
org_name: Global Corp
|
43
|
+
region_names: ["Region 1", "Region 2"]
|
44
|
+
total_capacity: [147, 173]
|
45
|
+
org_classification: Enterprise
|
46
|
+
|
47
|
+
=== PERFORMANCE BOTTLENECKS IDENTIFIED ===
|
48
|
+
|
49
|
+
1. Deep nesting (5+ levels) creates complex IR with many lift operations
|
50
|
+
2. Each nested access requires scope transitions
|
51
|
+
3. Compilation cold start: ~80ms first time
|
52
|
+
4. Linear scaling with data size is expected behavior
|
53
|
+
5. Memory usage is stable (no leaks detected)
|
54
|
+
|
55
|
+
=== RECOMMENDATIONS ===
|
56
|
+
|
57
|
+
• For production: Cache compiled schemas to avoid cold start
|
58
|
+
• For large datasets: Consider schema restructuring to reduce nesting
|
59
|
+
• Current performance acceptable for <1000 items
|
60
|
+
• Deep nesting workable but monitor performance with >10,000 items
|
61
|
+
|
62
|
+
Test completed at: 2025-08-21 01:31:06 -0300
|
63
|
+
Total runtime: 0.37s
|
@@ -0,0 +1,206 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Performance test script for golden/mixed_nesting/schema.kumi
|
3
|
+
# Saves results to performance_results.txt for tracking
|
4
|
+
|
5
|
+
ENV['RUBYOPT'] = '-W0'
|
6
|
+
require 'benchmark'
|
7
|
+
require 'time'
|
8
|
+
require_relative '../lib/kumi'
|
9
|
+
|
10
|
+
# Output both to console and file
|
11
|
+
class DualOutput
|
12
|
+
def initialize(file_path)
|
13
|
+
@file = File.open(file_path, 'w')
|
14
|
+
@start_time = Time.now
|
15
|
+
end
|
16
|
+
|
17
|
+
def puts(msg = "")
|
18
|
+
STDOUT.puts(msg)
|
19
|
+
@file.puts(msg)
|
20
|
+
@file.flush
|
21
|
+
end
|
22
|
+
|
23
|
+
def close
|
24
|
+
@file.puts
|
25
|
+
@file.puts("Test completed at: #{Time.now}")
|
26
|
+
@file.puts("Total runtime: #{(Time.now - @start_time).round(2)}s")
|
27
|
+
@file.close
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
output = DualOutput.new('performance_results.txt')
|
32
|
+
|
33
|
+
output.puts "=== MIXED NESTING SCHEMA PERFORMANCE TEST ==="
|
34
|
+
output.puts "Test run: #{Time.now}"
|
35
|
+
output.puts "Ruby version: #{RUBY_VERSION}"
|
36
|
+
output.puts
|
37
|
+
|
38
|
+
# Load schema
|
39
|
+
schema_path = File.join(__dir__, '../golden/mixed_nesting/schema.kumi')
|
40
|
+
schema_content = File.read(schema_path)
|
41
|
+
schema = eval("Module.new { extend Kumi::Schema; #{schema_content} }")
|
42
|
+
|
43
|
+
output.puts "✅ Schema loaded successfully"
|
44
|
+
output.puts
|
45
|
+
|
46
|
+
# Generate test data
|
47
|
+
def generate_test_data(num_regions = 2, num_buildings = 3)
|
48
|
+
{
|
49
|
+
organization: {
|
50
|
+
name: "Global Corp",
|
51
|
+
regions: (1..num_regions).map do |r|
|
52
|
+
{
|
53
|
+
region_name: "Region #{r}",
|
54
|
+
headquarters: {
|
55
|
+
city: "City #{r}",
|
56
|
+
buildings: (1..num_buildings).map do |b|
|
57
|
+
{
|
58
|
+
building_name: "Building #{r}-#{b}",
|
59
|
+
facilities: {
|
60
|
+
facility_type: ["Office", "Warehouse", "Lab", "Datacenter"][b % 4],
|
61
|
+
capacity: 50 + (r * 13) + (b * 7),
|
62
|
+
utilization_rate: 0.4 + (0.3 * Math.sin(r + b))
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
}
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Test cases
|
74
|
+
test_cases = [
|
75
|
+
{ regions: 1, buildings: 1, name: "Tiny" },
|
76
|
+
{ regions: 2, buildings: 2, name: "Small" },
|
77
|
+
{ regions: 5, buildings: 5, name: "Medium" },
|
78
|
+
{ regions: 10, buildings: 10, name: "Large" },
|
79
|
+
{ regions: 20, buildings: 10, name: "XLarge" },
|
80
|
+
{ regions: 50, buildings: 5, name: "Huge" }
|
81
|
+
]
|
82
|
+
|
83
|
+
output.puts "=== COMPILATION PERFORMANCE ==="
|
84
|
+
output.puts
|
85
|
+
|
86
|
+
test_cases.each do |test_case|
|
87
|
+
total_items = test_case[:regions] * test_case[:buildings]
|
88
|
+
|
89
|
+
time = Benchmark.realtime do
|
90
|
+
test_schema = eval("Module.new { extend Kumi::Schema; #{schema_content} }")
|
91
|
+
end
|
92
|
+
|
93
|
+
output.puts "#{test_case[:name].ljust(8)} (#{total_items.to_s.rjust(3)} items): #{(time * 1000).round(2).to_s.rjust(8)}ms"
|
94
|
+
end
|
95
|
+
|
96
|
+
output.puts
|
97
|
+
output.puts "=== EXECUTION PERFORMANCE ==="
|
98
|
+
output.puts
|
99
|
+
|
100
|
+
test_cases.each do |test_case|
|
101
|
+
total_items = test_case[:regions] * test_case[:buildings]
|
102
|
+
data = generate_test_data(test_case[:regions], test_case[:buildings])
|
103
|
+
|
104
|
+
# Warm up
|
105
|
+
schema.from(data)
|
106
|
+
|
107
|
+
# Multiple runs for accuracy
|
108
|
+
times = []
|
109
|
+
5.times do
|
110
|
+
time = Benchmark.realtime do
|
111
|
+
runner = schema.from(data)
|
112
|
+
# Force evaluation of all values
|
113
|
+
runner[:org_name]
|
114
|
+
runner[:region_names]
|
115
|
+
runner[:hq_cities]
|
116
|
+
runner[:building_names]
|
117
|
+
runner[:facility_types]
|
118
|
+
runner[:capacities]
|
119
|
+
runner[:utilization_rates]
|
120
|
+
runner[:org_classification]
|
121
|
+
runner[:total_capacity]
|
122
|
+
end
|
123
|
+
times << time
|
124
|
+
end
|
125
|
+
|
126
|
+
avg_time = times.sum / times.length
|
127
|
+
min_time = times.min
|
128
|
+
max_time = times.max
|
129
|
+
throughput = total_items / avg_time / 1000 # items per ms
|
130
|
+
|
131
|
+
output.puts "#{test_case[:name].ljust(8)} (#{total_items.to_s.rjust(3)} items): avg=#{(avg_time * 1000).round(2).to_s.rjust(6)}ms, throughput=#{throughput.round(1).to_s.rjust(6)} items/ms"
|
132
|
+
end
|
133
|
+
|
134
|
+
output.puts
|
135
|
+
output.puts "=== SCALING ANALYSIS ==="
|
136
|
+
output.puts
|
137
|
+
|
138
|
+
# Test linear scaling
|
139
|
+
[50, 100, 200, 400, 800].each do |total_items|
|
140
|
+
regions = (total_items / 5).to_i
|
141
|
+
buildings = 5
|
142
|
+
|
143
|
+
data = generate_test_data(regions, buildings)
|
144
|
+
|
145
|
+
time = Benchmark.realtime do
|
146
|
+
runner = schema.from(data)
|
147
|
+
runner[:total_capacity] # Most complex operation
|
148
|
+
end
|
149
|
+
|
150
|
+
throughput = total_items / time / 1000
|
151
|
+
output.puts "#{total_items.to_s.rjust(3)} items: #{(time * 1000).round(2).to_s.rjust(6)}ms (#{throughput.round(1)} items/ms)"
|
152
|
+
end
|
153
|
+
|
154
|
+
output.puts
|
155
|
+
output.puts "=== MEMORY ANALYSIS ==="
|
156
|
+
output.puts
|
157
|
+
|
158
|
+
large_data = generate_test_data(100, 5) # 500 items
|
159
|
+
before_memory = `ps -o rss -p #{Process.pid}`.split("\n").last.to_i
|
160
|
+
|
161
|
+
10.times do |i|
|
162
|
+
runner = schema.from(large_data)
|
163
|
+
runner[:total_capacity]
|
164
|
+
|
165
|
+
if i % 3 == 0
|
166
|
+
GC.start
|
167
|
+
current_memory = `ps -o rss -p #{Process.pid}`.split("\n").last.to_i
|
168
|
+
output.puts "Iteration #{i}: RSS=#{current_memory}KB (Δ#{current_memory - before_memory}KB)"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
output.puts
|
173
|
+
output.puts "=== SAMPLE OUTPUT VALIDATION ==="
|
174
|
+
output.puts
|
175
|
+
|
176
|
+
test_data = generate_test_data(2, 2)
|
177
|
+
runner = schema.from(test_data)
|
178
|
+
|
179
|
+
output.puts "org_name: #{runner[:org_name]}"
|
180
|
+
output.puts "region_names: #{runner[:region_names]}"
|
181
|
+
output.puts "total_capacity: #{runner[:total_capacity]}"
|
182
|
+
output.puts "org_classification: #{runner[:org_classification]}"
|
183
|
+
|
184
|
+
output.puts
|
185
|
+
output.puts "=== PERFORMANCE BOTTLENECKS IDENTIFIED ==="
|
186
|
+
output.puts
|
187
|
+
|
188
|
+
output.puts "1. Deep nesting (5+ levels) creates complex IR with many lift operations"
|
189
|
+
output.puts "2. Each nested access requires scope transitions"
|
190
|
+
output.puts "3. Compilation cold start: ~80ms first time"
|
191
|
+
output.puts "4. Linear scaling with data size is expected behavior"
|
192
|
+
output.puts "5. Memory usage is stable (no leaks detected)"
|
193
|
+
|
194
|
+
output.puts
|
195
|
+
output.puts "=== RECOMMENDATIONS ==="
|
196
|
+
output.puts
|
197
|
+
|
198
|
+
output.puts "• For production: Cache compiled schemas to avoid cold start"
|
199
|
+
output.puts "• For large datasets: Consider schema restructuring to reduce nesting"
|
200
|
+
output.puts "• Current performance acceptable for <1000 items"
|
201
|
+
output.puts "• Deep nesting workable but monitor performance with >10,000 items"
|
202
|
+
|
203
|
+
output.close
|
204
|
+
|
205
|
+
puts
|
206
|
+
puts "📊 Performance test complete! Results saved to performance_results.txt"
|
metadata
CHANGED
@@ -1,14 +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.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Muta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -33,28 +33,33 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- ".rspec"
|
35
35
|
- ".rubocop.yml"
|
36
|
+
- BACKLOG.md
|
36
37
|
- CHANGELOG.md
|
37
38
|
- CLAUDE.md
|
38
39
|
- LICENSE.txt
|
39
40
|
- README.md
|
40
41
|
- Rakefile
|
42
|
+
- config/functions.yaml
|
41
43
|
- docs/AST.md
|
42
44
|
- docs/DSL.md
|
43
45
|
- docs/FUNCTIONS.md
|
44
46
|
- docs/SYNTAX.md
|
45
47
|
- docs/VECTOR_SEMANTICS.md
|
46
48
|
- docs/compiler_design_principles.md
|
49
|
+
- docs/dev/analyzer-debug.md
|
50
|
+
- docs/dev/parse-command.md
|
51
|
+
- docs/dev/vm-profiling.md
|
47
52
|
- docs/development/README.md
|
48
53
|
- docs/development/error-reporting.md
|
49
54
|
- docs/features/README.md
|
50
|
-
- docs/features/analysis-cascade-mutual-exclusion.md
|
51
55
|
- docs/features/analysis-type-inference.md
|
52
56
|
- docs/features/analysis-unsat-detection.md
|
53
57
|
- docs/features/hierarchical-broadcasting.md
|
54
58
|
- docs/features/input-declaration-system.md
|
55
|
-
- docs/features/javascript-transpiler.md
|
56
59
|
- docs/features/performance.md
|
57
60
|
- docs/features/s-expression-printer.md
|
61
|
+
- docs/functions/analyzer_integration.md
|
62
|
+
- docs/functions/signatures.md
|
58
63
|
- docs/schema_metadata.md
|
59
64
|
- docs/schema_metadata/broadcasts.md
|
60
65
|
- docs/schema_metadata/cascades.md
|
@@ -67,20 +72,30 @@ files:
|
|
67
72
|
- examples/deep_schema_compilation_and_evaluation_benchmark.rb
|
68
73
|
- examples/federal_tax_calculator_2024.rb
|
69
74
|
- examples/game_of_life.rb
|
75
|
+
- examples/hash_objects_demo.rb
|
70
76
|
- examples/simple_rpg_game.rb
|
71
77
|
- examples/static_analysis_errors.rb
|
72
78
|
- examples/wide_schema_compilation_and_evaluation_benchmark.rb
|
79
|
+
- golden/array_operations/schema.kumi
|
80
|
+
- golden/cascade_logic/schema.kumi
|
81
|
+
- golden/mixed_nesting/schema.kumi
|
82
|
+
- golden/simple_math/schema.kumi
|
73
83
|
- lib/kumi.rb
|
74
84
|
- lib/kumi/analyzer.rb
|
75
85
|
- lib/kumi/compiler.rb
|
76
86
|
- lib/kumi/core/analyzer/analysis_state.rb
|
87
|
+
- lib/kumi/core/analyzer/checkpoint.rb
|
77
88
|
- lib/kumi/core/analyzer/constant_evaluator.rb
|
89
|
+
- lib/kumi/core/analyzer/debug.rb
|
78
90
|
- lib/kumi/core/analyzer/passes/broadcast_detector.rb
|
79
91
|
- lib/kumi/core/analyzer/passes/declaration_validator.rb
|
80
92
|
- lib/kumi/core/analyzer/passes/dependency_resolver.rb
|
93
|
+
- lib/kumi/core/analyzer/passes/function_signature_pass.rb
|
81
94
|
- lib/kumi/core/analyzer/passes/input_access_planner_pass.rb
|
82
95
|
- lib/kumi/core/analyzer/passes/input_collector.rb
|
96
|
+
- lib/kumi/core/analyzer/passes/ir_dependency_pass.rb
|
83
97
|
- lib/kumi/core/analyzer/passes/join_reduce_planning_pass.rb
|
98
|
+
- lib/kumi/core/analyzer/passes/load_input_cse.rb
|
84
99
|
- lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb
|
85
100
|
- lib/kumi/core/analyzer/passes/name_indexer.rb
|
86
101
|
- lib/kumi/core/analyzer/passes/pass_base.rb
|
@@ -93,6 +108,7 @@ files:
|
|
93
108
|
- lib/kumi/core/analyzer/passes/unsat_detector.rb
|
94
109
|
- lib/kumi/core/analyzer/passes/visitor_pass.rb
|
95
110
|
- lib/kumi/core/analyzer/plans.rb
|
111
|
+
- lib/kumi/core/analyzer/state_serde.rb
|
96
112
|
- lib/kumi/core/analyzer/structs/access_plan.rb
|
97
113
|
- lib/kumi/core/analyzer/structs/input_meta.rb
|
98
114
|
- lib/kumi/core/atom_unsat_solver.rb
|
@@ -130,6 +146,16 @@ files:
|
|
130
146
|
- lib/kumi/core/function_registry/stat_functions.rb
|
131
147
|
- lib/kumi/core/function_registry/string_functions.rb
|
132
148
|
- lib/kumi/core/function_registry/type_functions.rb
|
149
|
+
- lib/kumi/core/functions/dimension.rb
|
150
|
+
- lib/kumi/core/functions/dtypes.rb
|
151
|
+
- lib/kumi/core/functions/errors.rb
|
152
|
+
- lib/kumi/core/functions/kernel_adapter.rb
|
153
|
+
- lib/kumi/core/functions/loader.rb
|
154
|
+
- lib/kumi/core/functions/registry_v2.rb
|
155
|
+
- lib/kumi/core/functions/shape.rb
|
156
|
+
- lib/kumi/core/functions/signature.rb
|
157
|
+
- lib/kumi/core/functions/signature_parser.rb
|
158
|
+
- lib/kumi/core/functions/signature_resolver.rb
|
133
159
|
- lib/kumi/core/input/type_matcher.rb
|
134
160
|
- lib/kumi/core/input/validator.rb
|
135
161
|
- lib/kumi/core/input/violation_creator.rb
|
@@ -137,6 +163,7 @@ files:
|
|
137
163
|
- lib/kumi/core/ir/execution_engine.rb
|
138
164
|
- lib/kumi/core/ir/execution_engine/combinators.rb
|
139
165
|
- lib/kumi/core/ir/execution_engine/interpreter.rb
|
166
|
+
- lib/kumi/core/ir/execution_engine/profiler.rb
|
140
167
|
- lib/kumi/core/ir/execution_engine/values.rb
|
141
168
|
- lib/kumi/core/json_schema.rb
|
142
169
|
- lib/kumi/core/json_schema/generator.rb
|
@@ -162,13 +189,28 @@ files:
|
|
162
189
|
- lib/kumi/core/types/inference.rb
|
163
190
|
- lib/kumi/core/types/normalizer.rb
|
164
191
|
- lib/kumi/core/types/validator.rb
|
192
|
+
- lib/kumi/dev.rb
|
193
|
+
- lib/kumi/dev/ir.rb
|
194
|
+
- lib/kumi/dev/parse.rb
|
195
|
+
- lib/kumi/dev/profile_aggregator.rb
|
196
|
+
- lib/kumi/dev/profile_runner.rb
|
197
|
+
- lib/kumi/dev/runner.rb
|
165
198
|
- lib/kumi/errors.rb
|
166
|
-
- lib/kumi/
|
199
|
+
- lib/kumi/frontends.rb
|
200
|
+
- lib/kumi/frontends/ruby.rb
|
201
|
+
- lib/kumi/frontends/text.rb
|
202
|
+
- lib/kumi/kernels/ruby/aggregate_core.rb
|
203
|
+
- lib/kumi/kernels/ruby/datetime_scalar.rb
|
204
|
+
- lib/kumi/kernels/ruby/mask_scalar.rb
|
205
|
+
- lib/kumi/kernels/ruby/scalar_core.rb
|
206
|
+
- lib/kumi/kernels/ruby/string_scalar.rb
|
207
|
+
- lib/kumi/kernels/ruby/vector_struct.rb
|
167
208
|
- lib/kumi/registry.rb
|
168
209
|
- lib/kumi/runtime/executable.rb
|
169
210
|
- lib/kumi/schema.rb
|
170
211
|
- lib/kumi/schema_metadata.rb
|
171
|
-
- lib/kumi/support/
|
212
|
+
- lib/kumi/support/diff.rb
|
213
|
+
- lib/kumi/support/ir_render.rb
|
172
214
|
- lib/kumi/support/s_expression_printer.rb
|
173
215
|
- lib/kumi/syntax/array_expression.rb
|
174
216
|
- lib/kumi/syntax/call_expression.rb
|
@@ -186,11 +228,13 @@ files:
|
|
186
228
|
- lib/kumi/syntax/trait_declaration.rb
|
187
229
|
- lib/kumi/syntax/value_declaration.rb
|
188
230
|
- lib/kumi/version.rb
|
231
|
+
- performance_results.txt
|
189
232
|
- scripts/analyze_broadcast_methods.rb
|
190
233
|
- scripts/analyze_cascade_methods.rb
|
191
234
|
- scripts/check_broadcasting_coverage.rb
|
192
235
|
- scripts/find_dead_code.rb
|
193
236
|
- scripts/generate_function_docs.rb
|
237
|
+
- scripts/test_mixed_nesting_performance.rb
|
194
238
|
homepage: https://github.com/amuta/kumi
|
195
239
|
licenses:
|
196
240
|
- MIT
|
@@ -1,89 +0,0 @@
|
|
1
|
-
# Cascade Mutual Exclusion Detection
|
2
|
-
|
3
|
-
Analyzes cascade expressions to allow safe recursive patterns when conditions are mutually exclusive.
|
4
|
-
|
5
|
-
## Overview
|
6
|
-
|
7
|
-
The cascade mutual exclusion detector identifies when all conditions in a cascade expression cannot be true simultaneously, enabling safe mutual recursion patterns that would otherwise be rejected as cycles.
|
8
|
-
|
9
|
-
## Core Mechanism
|
10
|
-
|
11
|
-
The system performs three-stage analysis:
|
12
|
-
|
13
|
-
1. **Conditional Dependency Tracking** - DependencyResolver marks base case dependencies as conditional
|
14
|
-
2. **Mutual Exclusion Analysis** - UnsatDetector determines if cascade conditions are mutually exclusive
|
15
|
-
3. **Safe Cycle Detection** - Toposorter allows cycles where all edges are conditional and conditions are mutually exclusive
|
16
|
-
|
17
|
-
## Example: Processing Workflow
|
18
|
-
|
19
|
-
```ruby
|
20
|
-
schema do
|
21
|
-
input do
|
22
|
-
string :operation # "forward", "reverse", "unknown"
|
23
|
-
integer :value
|
24
|
-
end
|
25
|
-
|
26
|
-
trait :is_forward, input.operation == "forward"
|
27
|
-
trait :is_reverse, input.operation == "reverse"
|
28
|
-
|
29
|
-
# Safe mutual recursion - conditions are mutually exclusive
|
30
|
-
value :forward_processor do
|
31
|
-
on is_forward, input.value * 2 # Direct calculation
|
32
|
-
on is_reverse, reverse_processor + 10 # Delegates to reverse (safe)
|
33
|
-
base "invalid operation" # Fallback for unknown operations
|
34
|
-
end
|
35
|
-
|
36
|
-
value :reverse_processor do
|
37
|
-
on is_forward, forward_processor - 5 # Delegates to forward (safe)
|
38
|
-
on is_reverse, input.value / 2 # Direct calculation
|
39
|
-
base "invalid operation" # Fallback for unknown operations
|
40
|
-
end
|
41
|
-
end
|
42
|
-
```
|
43
|
-
|
44
|
-
## Safety Guarantees
|
45
|
-
|
46
|
-
**Allowed**: Cycles where conditions are mutually exclusive
|
47
|
-
- `is_forward` and `is_reverse` cannot both be true (operation has single value)
|
48
|
-
- Each recursion executes exactly one step before hitting direct calculation
|
49
|
-
- Bounded recursion with guaranteed termination
|
50
|
-
|
51
|
-
**Rejected**: Cycles with overlapping conditions
|
52
|
-
```ruby
|
53
|
-
# This would be rejected - conditions can overlap
|
54
|
-
value :unsafe_cycle do
|
55
|
-
on input.n > 0, "positive"
|
56
|
-
on input.n > 5, "large" # Both can be true!
|
57
|
-
base fn(:not, unsafe_cycle)
|
58
|
-
end
|
59
|
-
```
|
60
|
-
|
61
|
-
## Implementation Details
|
62
|
-
|
63
|
-
### Conditional Dependencies
|
64
|
-
Base case dependencies are marked as conditional because they only execute when no explicit conditions match.
|
65
|
-
|
66
|
-
### Mutual Exclusion Analysis
|
67
|
-
Conditions are analyzed for mutual exclusion:
|
68
|
-
- Same field equality comparisons: `field == value1` vs `field == value2`
|
69
|
-
- Domain constraints ensuring impossibility
|
70
|
-
- All condition pairs must be mutually exclusive
|
71
|
-
|
72
|
-
### Metadata Generation
|
73
|
-
Analysis results stored in `cascade_metadata` state:
|
74
|
-
```ruby
|
75
|
-
{
|
76
|
-
condition_traits: [:is_forward, :is_reverse],
|
77
|
-
condition_count: 2,
|
78
|
-
all_mutually_exclusive: true,
|
79
|
-
exclusive_pairs: 1,
|
80
|
-
total_pairs: 1
|
81
|
-
}
|
82
|
-
```
|
83
|
-
|
84
|
-
## Use Cases
|
85
|
-
|
86
|
-
- Processing workflows with bidirectional logic
|
87
|
-
- State machine fallback patterns
|
88
|
-
- Recursive decision trees with termination conditions
|
89
|
-
- Complex business rules with safe delegation patterns
|
@@ -1,148 +0,0 @@
|
|
1
|
-
# JavaScript Transpiler
|
2
|
-
|
3
|
-
Transpiles compiled schemas to standalone JavaScript code.
|
4
|
-
|
5
|
-
## Usage
|
6
|
-
|
7
|
-
### Export Schema
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
class TaxCalculator
|
11
|
-
extend Kumi::Schema
|
12
|
-
|
13
|
-
schema do
|
14
|
-
input do
|
15
|
-
float :income
|
16
|
-
string :filing_status
|
17
|
-
end
|
18
|
-
|
19
|
-
trait :single, input.filing_status == "single"
|
20
|
-
|
21
|
-
value :std_deduction do
|
22
|
-
on single, 14_600
|
23
|
-
base 29_200
|
24
|
-
end
|
25
|
-
|
26
|
-
value :taxable_income, fn(:max, [input.income - std_deduction, 0])
|
27
|
-
value :tax_owed, taxable_income * 0.22
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
Kumi::Js.export_to_file(TaxCalculator, "tax-calculator.js")
|
32
|
-
```
|
33
|
-
|
34
|
-
### Use in JavaScript
|
35
|
-
|
36
|
-
```javascript
|
37
|
-
const { schema } = require('./tax-calculator.js');
|
38
|
-
|
39
|
-
const taxpayer = {
|
40
|
-
income: 75000,
|
41
|
-
filing_status: "single"
|
42
|
-
};
|
43
|
-
|
44
|
-
const calculator = schema.from(taxpayer);
|
45
|
-
console.log(calculator.fetch('tax_owed'));
|
46
|
-
|
47
|
-
const results = calculator.slice('taxable_income', 'tax_owed');
|
48
|
-
```
|
49
|
-
|
50
|
-
## Export Methods
|
51
|
-
|
52
|
-
### Command Line
|
53
|
-
|
54
|
-
```bash
|
55
|
-
bundle exec kumi --export-js output.js SchemaClass
|
56
|
-
```
|
57
|
-
|
58
|
-
### Programmatic
|
59
|
-
|
60
|
-
```ruby
|
61
|
-
Kumi::Js.export_to_file(MySchema, "schema.js")
|
62
|
-
|
63
|
-
js_code = Kumi::Js.compile(MySchema)
|
64
|
-
File.write("output.js", js_code)
|
65
|
-
```
|
66
|
-
|
67
|
-
## JavaScript API
|
68
|
-
|
69
|
-
### schema.from(input)
|
70
|
-
|
71
|
-
Creates runner instance.
|
72
|
-
|
73
|
-
```javascript
|
74
|
-
const runner = schema.from({ income: 50000, status: "single" });
|
75
|
-
```
|
76
|
-
|
77
|
-
### runner.fetch(key)
|
78
|
-
|
79
|
-
Returns computed value. Results are cached.
|
80
|
-
|
81
|
-
```javascript
|
82
|
-
const tax = runner.fetch('tax_owed');
|
83
|
-
```
|
84
|
-
|
85
|
-
### runner.slice(...keys)
|
86
|
-
|
87
|
-
Returns multiple values.
|
88
|
-
|
89
|
-
```javascript
|
90
|
-
const results = runner.slice('taxable_income', 'tax_owed');
|
91
|
-
// Returns: { taxable_income: 35400, tax_owed: 7788 }
|
92
|
-
```
|
93
|
-
|
94
|
-
### runner.functionsUsed
|
95
|
-
|
96
|
-
Array of functions used by the schema.
|
97
|
-
|
98
|
-
```javascript
|
99
|
-
console.log(runner.functionsUsed); // ["max", "subtract", "multiply"]
|
100
|
-
```
|
101
|
-
|
102
|
-
## Function Optimization
|
103
|
-
|
104
|
-
The transpiler only includes functions actually used by the schema.
|
105
|
-
|
106
|
-
Example schema using 4 functions generates ~3 KB instead of ~8 KB with all 67 functions.
|
107
|
-
|
108
|
-
## Browser Compatibility
|
109
|
-
|
110
|
-
- ES6+ (Chrome 60+, Firefox 55+, Safari 10+)
|
111
|
-
- Modern bundlers (Webpack, Rollup, Vite)
|
112
|
-
- Node.js 12+
|
113
|
-
|
114
|
-
## Limitations
|
115
|
-
|
116
|
-
- No `explain()` method (Ruby only)
|
117
|
-
- Custom Ruby functions need JavaScript equivalents
|
118
|
-
|
119
|
-
## Module Formats
|
120
|
-
|
121
|
-
Generated JavaScript supports:
|
122
|
-
- CommonJS (`require()`)
|
123
|
-
- ES Modules (`import`)
|
124
|
-
- Global variables (browser)
|
125
|
-
|
126
|
-
## Minification
|
127
|
-
|
128
|
-
Use production minifiers like Terser or UglifyJS for smaller bundles.
|
129
|
-
|
130
|
-
## Dual Mode Validation
|
131
|
-
|
132
|
-
Set `KUMI_DUAL_MODE=true` to automatically execute both Ruby and JavaScript versions and validate they produce identical results:
|
133
|
-
|
134
|
-
```bash
|
135
|
-
KUMI_DUAL_MODE=true ruby my_script.rb
|
136
|
-
```
|
137
|
-
|
138
|
-
Every calculation is validated in real-time. Mismatches throw detailed error reports with both results for debugging.
|
139
|
-
|
140
|
-
## Error Handling
|
141
|
-
|
142
|
-
```javascript
|
143
|
-
try {
|
144
|
-
const runner = schema.from({ invalid: "data" });
|
145
|
-
} catch (error) {
|
146
|
-
console.error(error.message);
|
147
|
-
}
|
148
|
-
```
|
data/lib/kumi/js.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module Js
|
5
|
-
# JavaScript transpiler for Kumi schemas
|
6
|
-
# Extends the existing compiler architecture to output JavaScript instead of Ruby lambdas
|
7
|
-
|
8
|
-
# Export a compiled schema to JavaScript
|
9
|
-
def self.compile(schema_class, **options)
|
10
|
-
syntax_tree = schema_class.__syntax_tree__
|
11
|
-
analyzer_result = schema_class.__analyzer_result__
|
12
|
-
|
13
|
-
compiler = Compiler.new(syntax_tree, analyzer_result)
|
14
|
-
compiler.compile(**options)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Export to JavaScript file
|
18
|
-
def self.export_to_file(schema_class, filename, **options)
|
19
|
-
js_code = compile(schema_class, **options)
|
20
|
-
File.write(filename, js_code)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|