kumi 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +23 -0
  4. data/CLAUDE.md +7 -231
  5. data/README.md +5 -5
  6. data/docs/SYNTAX.md +66 -0
  7. data/docs/VECTOR_SEMANTICS.md +286 -0
  8. data/docs/features/hierarchical-broadcasting.md +67 -1
  9. data/docs/features/input-declaration-system.md +16 -0
  10. data/docs/features/s-expression-printer.md +2 -2
  11. data/lib/kumi/analyzer.rb +34 -12
  12. data/lib/kumi/compiler.rb +2 -12
  13. data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +157 -64
  14. data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +1 -1
  15. data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +47 -0
  16. data/lib/kumi/core/analyzer/passes/input_collector.rb +123 -101
  17. data/lib/kumi/core/analyzer/passes/join_reduce_planning_pass.rb +293 -0
  18. data/lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb +993 -0
  19. data/lib/kumi/core/analyzer/passes/pass_base.rb +2 -2
  20. data/lib/kumi/core/analyzer/passes/scope_resolution_pass.rb +346 -0
  21. data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +2 -1
  22. data/lib/kumi/core/analyzer/passes/toposorter.rb +9 -3
  23. data/lib/kumi/core/analyzer/passes/type_checker.rb +3 -3
  24. data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +2 -2
  25. data/lib/kumi/core/analyzer/passes/{type_inferencer.rb → type_inferencer_pass.rb} +4 -4
  26. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +2 -2
  27. data/lib/kumi/core/analyzer/plans.rb +52 -0
  28. data/lib/kumi/core/analyzer/structs/access_plan.rb +20 -0
  29. data/lib/kumi/core/analyzer/structs/input_meta.rb +29 -0
  30. data/lib/kumi/core/compiler/access_builder.rb +36 -0
  31. data/lib/kumi/core/compiler/access_planner.rb +219 -0
  32. data/lib/kumi/core/compiler/accessors/base.rb +69 -0
  33. data/lib/kumi/core/compiler/accessors/each_indexed_accessor.rb +84 -0
  34. data/lib/kumi/core/compiler/accessors/materialize_accessor.rb +55 -0
  35. data/lib/kumi/core/compiler/accessors/ravel_accessor.rb +73 -0
  36. data/lib/kumi/core/compiler/accessors/read_accessor.rb +41 -0
  37. data/lib/kumi/core/compiler_base.rb +2 -2
  38. data/lib/kumi/core/error_reporter.rb +6 -5
  39. data/lib/kumi/core/errors.rb +4 -0
  40. data/lib/kumi/core/explain.rb +157 -205
  41. data/lib/kumi/core/export/node_builders.rb +2 -2
  42. data/lib/kumi/core/export/node_serializers.rb +1 -1
  43. data/lib/kumi/core/function_registry/collection_functions.rb +21 -10
  44. data/lib/kumi/core/function_registry/conditional_functions.rb +14 -4
  45. data/lib/kumi/core/function_registry/function_builder.rb +142 -55
  46. data/lib/kumi/core/function_registry/logical_functions.rb +5 -5
  47. data/lib/kumi/core/function_registry/stat_functions.rb +2 -2
  48. data/lib/kumi/core/function_registry.rb +126 -108
  49. data/lib/kumi/core/input/validator.rb +1 -1
  50. data/lib/kumi/core/ir/execution_engine/combinators.rb +117 -0
  51. data/lib/kumi/core/ir/execution_engine/interpreter.rb +336 -0
  52. data/lib/kumi/core/ir/execution_engine/values.rb +46 -0
  53. data/lib/kumi/core/ir/execution_engine.rb +50 -0
  54. data/lib/kumi/core/ir.rb +58 -0
  55. data/lib/kumi/core/ruby_parser/build_context.rb +2 -2
  56. data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +0 -12
  57. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +36 -15
  58. data/lib/kumi/core/ruby_parser/input_builder.rb +30 -9
  59. data/lib/kumi/core/ruby_parser/parser.rb +1 -1
  60. data/lib/kumi/core/ruby_parser/schema_builder.rb +2 -2
  61. data/lib/kumi/core/ruby_parser/sugar.rb +7 -0
  62. data/lib/kumi/core/types/validator.rb +1 -1
  63. data/lib/kumi/registry.rb +14 -79
  64. data/lib/kumi/runtime/executable.rb +213 -0
  65. data/lib/kumi/schema.rb +14 -3
  66. data/lib/kumi/schema_metadata.rb +2 -2
  67. data/lib/kumi/support/ir_dump.rb +491 -0
  68. data/lib/kumi/support/s_expression_printer.rb +1 -1
  69. data/lib/kumi/syntax/location.rb +5 -0
  70. data/lib/kumi/syntax/node.rb +0 -1
  71. data/lib/kumi/syntax/root.rb +2 -2
  72. data/lib/kumi/version.rb +1 -1
  73. data/lib/kumi.rb +6 -15
  74. metadata +37 -19
  75. data/lib/kumi/core/cascade_executor_builder.rb +0 -132
  76. data/lib/kumi/core/compiled_schema.rb +0 -43
  77. data/lib/kumi/core/compiler/expression_compiler.rb +0 -146
  78. data/lib/kumi/core/compiler/function_invoker.rb +0 -55
  79. data/lib/kumi/core/compiler/path_traversal_compiler.rb +0 -158
  80. data/lib/kumi/core/compiler/reference_compiler.rb +0 -46
  81. data/lib/kumi/core/evaluation_wrapper.rb +0 -40
  82. data/lib/kumi/core/nested_structure_utils.rb +0 -78
  83. data/lib/kumi/core/schema_instance.rb +0 -115
  84. data/lib/kumi/core/vectorized_function_builder.rb +0 -88
  85. data/lib/kumi/js/compiler.rb +0 -878
  86. data/lib/kumi/js/function_registry.rb +0 -333
  87. data/migrate_to_core_iterative.rb +0 -938
@@ -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