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
@@ -1,333 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module Js
|
5
|
-
# Maps Ruby function registry to JavaScript implementations
|
6
|
-
# Each function maintains the same signature and behavior as the Ruby version
|
7
|
-
module FunctionRegistry
|
8
|
-
# Generate complete JavaScript function registry
|
9
|
-
def self.generate_js_registry
|
10
|
-
{
|
11
|
-
# Mathematical functions
|
12
|
-
**math_functions,
|
13
|
-
|
14
|
-
# Comparison functions
|
15
|
-
**comparison_functions,
|
16
|
-
|
17
|
-
# Logical functions
|
18
|
-
**logical_functions,
|
19
|
-
|
20
|
-
# String functions
|
21
|
-
**string_functions,
|
22
|
-
|
23
|
-
# Collection functions
|
24
|
-
**collection_functions,
|
25
|
-
|
26
|
-
# Conditional functions
|
27
|
-
**conditional_functions,
|
28
|
-
|
29
|
-
# Type functions
|
30
|
-
**type_functions,
|
31
|
-
|
32
|
-
# Statistical functions
|
33
|
-
**stat_functions
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
# Generate JavaScript code for the function registry
|
38
|
-
def self.generate_js_code(functions_required: nil)
|
39
|
-
registry = generate_js_registry
|
40
|
-
|
41
|
-
# Filter registry to only include required functions if specified
|
42
|
-
registry = registry.slice(*functions_required) if functions_required && !functions_required.empty?
|
43
|
-
|
44
|
-
functions_js = registry.map do |name, js_code|
|
45
|
-
# Handle symbol names that need quoting in JS
|
46
|
-
js_name = name.to_s.match?(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/) ? name : "\"#{name}\""
|
47
|
-
" #{js_name}: #{js_code}"
|
48
|
-
end.join(",\n")
|
49
|
-
|
50
|
-
<<~JAVASCRIPT
|
51
|
-
const kumiRegistry = {
|
52
|
-
#{functions_js}
|
53
|
-
};
|
54
|
-
JAVASCRIPT
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.math_functions
|
58
|
-
{
|
59
|
-
# Basic arithmetic
|
60
|
-
add: "(a, b) => a + b",
|
61
|
-
subtract: "(a, b) => a - b",
|
62
|
-
multiply: "(a, b) => a * b",
|
63
|
-
divide: "(a, b) => a / b",
|
64
|
-
modulo: "(a, b) => a % b",
|
65
|
-
power: "(a, b) => Math.pow(a, b)",
|
66
|
-
|
67
|
-
# Unary operations
|
68
|
-
abs: "(a) => Math.abs(a)",
|
69
|
-
floor: "(a) => Math.floor(a)",
|
70
|
-
ceil: "(a) => Math.ceil(a)",
|
71
|
-
|
72
|
-
# Special operations
|
73
|
-
round: "(a, precision = 0) => Number(a.toFixed(precision))",
|
74
|
-
clamp: "(value, min, max) => Math.min(max, Math.max(min, value))",
|
75
|
-
|
76
|
-
# Complex mathematical operations
|
77
|
-
piecewise_sum: <<~JS.strip
|
78
|
-
(value, breaks, rates) => {
|
79
|
-
if (breaks.length !== rates.length) {
|
80
|
-
throw new Error('breaks & rates size mismatch');
|
81
|
-
}
|
82
|
-
#{' '}
|
83
|
-
let acc = 0.0;
|
84
|
-
let previous = 0.0;
|
85
|
-
let marginal = rates[rates.length - 1];
|
86
|
-
#{' '}
|
87
|
-
for (let i = 0; i < breaks.length; i++) {
|
88
|
-
const upper = breaks[i];
|
89
|
-
const rate = rates[i];
|
90
|
-
#{' '}
|
91
|
-
if (value <= upper) {
|
92
|
-
marginal = rate;
|
93
|
-
acc += (value - previous) * rate;
|
94
|
-
break;
|
95
|
-
} else {
|
96
|
-
acc += (upper - previous) * rate;
|
97
|
-
previous = upper;
|
98
|
-
}
|
99
|
-
}
|
100
|
-
#{' '}
|
101
|
-
return [acc, marginal];
|
102
|
-
}
|
103
|
-
JS
|
104
|
-
}
|
105
|
-
end
|
106
|
-
|
107
|
-
def self.comparison_functions
|
108
|
-
{
|
109
|
-
# Equality operators (using strict equality)
|
110
|
-
"==": "(a, b) => a === b",
|
111
|
-
"!=": "(a, b) => a !== b",
|
112
|
-
|
113
|
-
# Comparison operators
|
114
|
-
">": "(a, b) => a > b",
|
115
|
-
"<": "(a, b) => a < b",
|
116
|
-
">=": "(a, b) => a >= b",
|
117
|
-
"<=": "(a, b) => a <= b",
|
118
|
-
|
119
|
-
# Range comparison
|
120
|
-
between?: "(value, min, max) => value >= min && value <= max"
|
121
|
-
}
|
122
|
-
end
|
123
|
-
|
124
|
-
def self.logical_functions
|
125
|
-
{
|
126
|
-
# Basic logical operations
|
127
|
-
and: "(...conditions) => conditions.every(x => x)",
|
128
|
-
or: "(...conditions) => conditions.some(x => x)",
|
129
|
-
not: "(a) => !a",
|
130
|
-
|
131
|
-
# Collection logical operations
|
132
|
-
all?: "(collection) => collection.every(x => x)",
|
133
|
-
any?: "(collection) => collection.some(x => x)",
|
134
|
-
none?: "(collection) => !collection.some(x => x)",
|
135
|
-
|
136
|
-
# Element-wise AND for cascades - works on arrays with hierarchical broadcasting
|
137
|
-
cascade_and: <<~JS.strip
|
138
|
-
(...conditions) => {
|
139
|
-
if (conditions.length === 0) return false;
|
140
|
-
if (conditions.length === 1) return conditions[0];
|
141
|
-
#{' '}
|
142
|
-
// Start with first condition
|
143
|
-
let result = conditions[0];
|
144
|
-
#{' '}
|
145
|
-
// Apply element-wise AND with remaining conditions using hierarchical broadcasting
|
146
|
-
for (let i = 1; i < conditions.length; i++) {
|
147
|
-
result = kumiRuntime.elementWiseAnd(result, conditions[i]);
|
148
|
-
}
|
149
|
-
#{' '}
|
150
|
-
return result;
|
151
|
-
}
|
152
|
-
JS
|
153
|
-
}
|
154
|
-
end
|
155
|
-
|
156
|
-
def self.string_functions
|
157
|
-
{
|
158
|
-
# String transformations
|
159
|
-
upcase: "(str) => str.toString().toUpperCase()",
|
160
|
-
downcase: "(str) => str.toString().toLowerCase()",
|
161
|
-
capitalize: "(str) => { const s = str.toString(); return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(); }",
|
162
|
-
strip: "(str) => str.toString().trim()",
|
163
|
-
|
164
|
-
# String queries
|
165
|
-
string_length: "(str) => str.toString().length",
|
166
|
-
length: "(str) => str.toString().length",
|
167
|
-
|
168
|
-
# String inclusion checks
|
169
|
-
string_include?: "(str, substr) => str.toString().includes(substr.toString())",
|
170
|
-
includes?: "(str, substr) => str.toString().includes(substr.toString())",
|
171
|
-
contains?: "(str, substr) => str.toString().includes(substr.toString())",
|
172
|
-
start_with?: "(str, prefix) => str.toString().startsWith(prefix.toString())",
|
173
|
-
end_with?: "(str, suffix) => str.toString().endsWith(suffix.toString())",
|
174
|
-
|
175
|
-
# String building
|
176
|
-
concat: "(...strings) => strings.map(s => s.toString()).join('')"
|
177
|
-
}
|
178
|
-
end
|
179
|
-
|
180
|
-
def self.collection_functions
|
181
|
-
{
|
182
|
-
# Collection queries (reducers)
|
183
|
-
empty?: "(collection) => collection.length === 0",
|
184
|
-
size: "(collection) => collection.length",
|
185
|
-
|
186
|
-
# Element access
|
187
|
-
first: "(collection) => collection[0]",
|
188
|
-
last: "(collection) => collection[collection.length - 1]",
|
189
|
-
|
190
|
-
# Mathematical operations on collections
|
191
|
-
sum: "(collection) => collection.reduce((a, b) => a + b, 0)",
|
192
|
-
min: "(collection) => Math.min(...collection)",
|
193
|
-
max: "(collection) => Math.max(...collection)",
|
194
|
-
|
195
|
-
# Collection operations
|
196
|
-
include?: "(collection, element) => collection.includes(element)",
|
197
|
-
reverse: "(collection) => [...collection].reverse()",
|
198
|
-
sort: "(collection) => [...collection].sort()",
|
199
|
-
unique: "(collection) => [...new Set(collection)]",
|
200
|
-
flatten: "(collection) => collection.flat(Infinity)",
|
201
|
-
flatten_one: "(collection) => collection.flat(1)",
|
202
|
-
flatten_deep: "(collection) => collection.flat(Infinity)",
|
203
|
-
|
204
|
-
# Array transformation functions
|
205
|
-
map_multiply: "(collection, factor) => collection.map(x => x * factor)",
|
206
|
-
map_add: "(collection, value) => collection.map(x => x + value)",
|
207
|
-
map_conditional: "(collection, condition_value, true_value, false_value) => collection.map(x => x === condition_value ? true_value : false_value)",
|
208
|
-
|
209
|
-
# Range/index functions
|
210
|
-
build_array: "(size) => Array.from({length: size}, (_, i) => i)",
|
211
|
-
range: "(start, finish) => Array.from({length: finish - start}, (_, i) => start + i)",
|
212
|
-
|
213
|
-
# Array slicing and grouping
|
214
|
-
each_slice: <<~JS.strip,
|
215
|
-
(array, size) => {
|
216
|
-
const result = [];
|
217
|
-
for (let i = 0; i < array.length; i += size) {
|
218
|
-
result.push(array.slice(i, i + size));
|
219
|
-
}
|
220
|
-
return result;
|
221
|
-
}
|
222
|
-
JS
|
223
|
-
|
224
|
-
join: "(array, separator = '') => array.map(x => x.toString()).join(separator.toString())",
|
225
|
-
|
226
|
-
map_join_rows: "(array_of_arrays, row_separator = '', column_separator = '\\n') => array_of_arrays.map(row => row.join(row_separator.toString())).join(column_separator.toString())",
|
227
|
-
|
228
|
-
# Higher-order collection functions
|
229
|
-
map_with_index: "(collection) => collection.map((item, index) => [item, index])",
|
230
|
-
indices: "(collection) => Array.from({length: collection.length}, (_, i) => i)",
|
231
|
-
|
232
|
-
# Conditional aggregation functions
|
233
|
-
count_if: "(condition_array) => condition_array.filter(x => x === true).length",
|
234
|
-
sum_if: "(value_array, condition_array) => value_array.reduce((sum, value, i) => sum + (condition_array[i] ? value : 0), 0)",
|
235
|
-
avg_if: <<~JS.strip,
|
236
|
-
(value_array, condition_array) => {
|
237
|
-
const pairs = value_array.map((value, i) => [value, condition_array[i]]);
|
238
|
-
const true_values = pairs.filter(([_, condition]) => condition).map(([value, _]) => value);
|
239
|
-
return true_values.length === 0 ? 0.0 : true_values.reduce((a, b) => a + b, 0) / true_values.length;
|
240
|
-
}
|
241
|
-
JS
|
242
|
-
|
243
|
-
# Flattening utilities for hierarchical data
|
244
|
-
any_across: "(nested_array) => nested_array.flat(Infinity).some(x => x)",
|
245
|
-
all_across: "(nested_array) => nested_array.flat(Infinity).every(x => x)",
|
246
|
-
count_across: "(nested_array) => nested_array.flat(Infinity).length"
|
247
|
-
}
|
248
|
-
end
|
249
|
-
|
250
|
-
def self.conditional_functions
|
251
|
-
{
|
252
|
-
conditional: "(condition, true_value, false_value) => condition ? true_value : false_value",
|
253
|
-
if: "(condition, true_value, false_value = null) => condition ? true_value : false_value",
|
254
|
-
coalesce: "(...values) => values.find(v => v != null)"
|
255
|
-
}
|
256
|
-
end
|
257
|
-
|
258
|
-
def self.type_functions
|
259
|
-
{
|
260
|
-
# Hash/object operations - assuming TypeFunctions exists
|
261
|
-
fetch: "(hash, key, default_value = null) => hash.hasOwnProperty(key) ? hash[key] : default_value",
|
262
|
-
has_key?: "(hash, key) => hash.hasOwnProperty(key)",
|
263
|
-
keys: "(hash) => Object.keys(hash)",
|
264
|
-
values: "(hash) => Object.values(hash)",
|
265
|
-
at: "(collection, index) => collection[index]"
|
266
|
-
}
|
267
|
-
end
|
268
|
-
|
269
|
-
def self.stat_functions
|
270
|
-
{
|
271
|
-
# Statistical functions - mirror Ruby StatFunctions behavior
|
272
|
-
avg: "(array) => array.length === 0 ? 0 : array.reduce((sum, val) => sum + val, 0) / array.length",
|
273
|
-
mean: "(array) => array.length === 0 ? 0 : array.reduce((sum, val) => sum + val, 0) / array.length",
|
274
|
-
median: <<~JS.strip,
|
275
|
-
(array) => {
|
276
|
-
if (array.length === 0) return 0;
|
277
|
-
const sorted = [...array].sort((a, b) => a - b);
|
278
|
-
const mid = Math.floor(sorted.length / 2);
|
279
|
-
return sorted.length % 2 === 0#{' '}
|
280
|
-
? (sorted[mid - 1] + sorted[mid]) / 2
|
281
|
-
: sorted[mid];
|
282
|
-
}
|
283
|
-
JS
|
284
|
-
variance: <<~JS.strip,
|
285
|
-
(array) => {
|
286
|
-
if (array.length === 0) return 0;
|
287
|
-
const mean = array.reduce((sum, val) => sum + val, 0) / array.length;
|
288
|
-
const squaredDiffs = array.map(val => Math.pow(val - mean, 2));
|
289
|
-
return squaredDiffs.reduce((sum, val) => sum + val, 0) / array.length;
|
290
|
-
}
|
291
|
-
JS
|
292
|
-
stdev: <<~JS.strip,
|
293
|
-
(array) => {
|
294
|
-
if (array.length === 0) return 0;
|
295
|
-
const mean = array.reduce((sum, val) => sum + val, 0) / array.length;
|
296
|
-
const squaredDiffs = array.map(val => Math.pow(val - mean, 2));
|
297
|
-
const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / array.length;
|
298
|
-
return Math.sqrt(variance);
|
299
|
-
}
|
300
|
-
JS
|
301
|
-
sample_variance: <<~JS.strip,
|
302
|
-
(array) => {
|
303
|
-
if (array.length <= 1) return 0;
|
304
|
-
const mean = array.reduce((sum, val) => sum + val, 0) / array.length;
|
305
|
-
const squaredDiffs = array.map(val => Math.pow(val - mean, 2));
|
306
|
-
return squaredDiffs.reduce((sum, val) => sum + val, 0) / (array.length - 1);
|
307
|
-
}
|
308
|
-
JS
|
309
|
-
sample_stdev: <<~JS.strip,
|
310
|
-
(array) => {
|
311
|
-
if (array.length <= 1) return 0;
|
312
|
-
const mean = array.reduce((sum, val) => sum + val, 0) / array.length;
|
313
|
-
const squaredDiffs = array.map(val => Math.pow(val - mean, 2));
|
314
|
-
const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / (array.length - 1);
|
315
|
-
return Math.sqrt(variance);
|
316
|
-
}
|
317
|
-
JS
|
318
|
-
# Flattened statistics functions
|
319
|
-
flat_size: "(nestedArray) => nestedArray.flat(Infinity).length",
|
320
|
-
flat_sum: "(nestedArray) => nestedArray.flat(Infinity).reduce((sum, val) => sum + val, 0)",
|
321
|
-
flat_avg: <<~JS.strip,
|
322
|
-
(nestedArray) => {
|
323
|
-
const flattened = nestedArray.flat(Infinity);
|
324
|
-
return flattened.length === 0 ? 0 : flattened.reduce((sum, val) => sum + val, 0) / flattened.length;
|
325
|
-
}
|
326
|
-
JS
|
327
|
-
flat_max: "(nestedArray) => Math.max(...nestedArray.flat(Infinity))",
|
328
|
-
flat_min: "(nestedArray) => Math.min(...nestedArray.flat(Infinity))"
|
329
|
-
}
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
333
|
-
end
|