kumi 0.0.37 → 0.1.0
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 +39 -58
- data/.rubocop_todo.yml +931 -0
- data/CHANGELOG.md +37 -0
- data/README.md +3 -0
- data/data/functions/core/arithmetic.yaml +48 -0
- data/data/functions/core/conversion.yaml +13 -0
- data/data/functions/core/select.yaml +1 -1
- data/data/functions/core/stencil.yaml +32 -0
- data/data/kernels/javascript/agg/numeric.yaml +1 -0
- data/data/kernels/javascript/core/arithmetic.yaml +24 -0
- data/data/kernels/javascript/core/coercion.yaml +18 -4
- data/data/kernels/ruby/agg/numeric.yaml +2 -1
- data/data/kernels/ruby/core/arithmetic.yaml +31 -3
- data/data/kernels/ruby/core/coercion.yaml +8 -0
- data/docs/CROSS_TARGET_SEMANTICS.md +99 -0
- data/docs/FORM_SCHEMA.md +13 -8
- data/docs/FUNCTIONS.md +120 -13
- data/docs/INPUTS.md +164 -0
- data/docs/OUTPUT_SCHEMA.md +12 -8
- data/docs/PASSES.md +76 -0
- data/docs/PASS_AUDIT.md +179 -0
- data/docs/PORTAL.md +39 -0
- data/docs/SYNTAX.md +84 -121
- data/docs/SYNTAX_NOTES.md +612 -0
- data/docs/UNSAT_DETECTION.md +2 -2
- data/docs/functions-reference.json +276 -84
- data/docs/pairwise-design.md +125 -0
- data/docs/superpowers/plans/2026-06-12-pass-conventions-and-dedup.md +1559 -0
- data/docs/superpowers/specs/2026-06-12-pass-conventions-and-dedup-design.md +136 -0
- data/golden/algebraic_identities/expected/ast.txt +66 -0
- data/golden/algebraic_identities/expected/dfir.txt +47 -0
- data/golden/algebraic_identities/expected/dfir_optimized.txt +59 -0
- data/golden/algebraic_identities/expected/input_plan.txt +7 -0
- data/golden/algebraic_identities/expected/loopir.txt +61 -0
- data/golden/algebraic_identities/expected/nast.txt +51 -0
- data/golden/algebraic_identities/expected/runtime.json +24 -0
- data/golden/algebraic_identities/expected/schema_javascript.mjs +89 -0
- data/golden/algebraic_identities/expected/schema_ruby.rb +84 -0
- data/golden/algebraic_identities/expected/snast.txt +51 -0
- data/golden/algebraic_identities/expected/vecir.txt +47 -0
- data/golden/algebraic_identities/input.json +6 -0
- data/golden/algebraic_identities/schema.kumi +25 -0
- data/golden/array_element/expected/loopir.txt +1 -2
- data/golden/array_element/expected/runtime.json +5 -0
- data/golden/array_element/expected/schema_javascript.mjs +1 -2
- data/golden/array_element/expected/schema_ruby.rb +2 -3
- data/golden/array_index/expected/runtime.json +54 -0
- data/golden/array_index/expected/schema_ruby.rb +1 -1
- data/golden/array_operations/{expected.json → expected/runtime.json} +5 -15
- data/golden/array_operations/expected/schema_ruby.rb +1 -1
- data/golden/cascade_logic/expected/runtime.json +3 -0
- data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
- data/golden/cascade_reuse_peephole/expected/ast.txt +193 -0
- data/golden/cascade_reuse_peephole/expected/dfir.txt +124 -0
- data/golden/cascade_reuse_peephole/expected/dfir_optimized.txt +320 -0
- data/golden/cascade_reuse_peephole/expected/input_plan.txt +2 -0
- data/golden/cascade_reuse_peephole/expected/loopir.txt +302 -0
- data/golden/cascade_reuse_peephole/expected/nast.txt +188 -0
- data/golden/cascade_reuse_peephole/expected/runtime.json +8 -0
- data/golden/cascade_reuse_peephole/expected/schema_javascript.mjs +335 -0
- data/golden/cascade_reuse_peephole/expected/schema_ruby.rb +337 -0
- data/golden/cascade_reuse_peephole/expected/snast.txt +188 -0
- data/golden/cascade_reuse_peephole/expected/vecir.txt +302 -0
- data/golden/cascade_reuse_peephole/input.json +4 -0
- data/golden/cascade_reuse_peephole/schema.kumi +31 -0
- data/golden/chained_fusion/expected/loopir.txt +5 -13
- data/golden/chained_fusion/expected/runtime.json +45 -0
- data/golden/chained_fusion/expected/schema_javascript.mjs +5 -13
- data/golden/chained_fusion/expected/schema_ruby.rb +6 -14
- data/golden/cross_import/expected/ast.txt +21 -0
- data/golden/cross_import/expected/dfir.txt +3 -0
- data/golden/cross_import/expected/dfir_optimized.txt +8 -0
- data/golden/cross_import/expected/input_plan.txt +5 -0
- data/golden/cross_import/expected/loopir.txt +14 -0
- data/golden/cross_import/expected/nast.txt +7 -0
- data/golden/cross_import/expected/runtime.json +10 -0
- data/golden/cross_import/expected/schema_javascript.mjs +19 -0
- data/golden/cross_import/expected/schema_ruby.rb +19 -0
- data/golden/cross_import/expected/snast.txt +7 -0
- data/golden/cross_import/expected/vecir.txt +8 -0
- data/golden/cross_import/input.json +1 -0
- data/golden/cross_import/schema.kumi +15 -0
- data/golden/cross_let/expected/ast.txt +62 -0
- data/golden/cross_let/expected/dfir.txt +35 -0
- data/golden/cross_let/expected/dfir_optimized.txt +51 -0
- data/golden/cross_let/expected/input_plan.txt +5 -0
- data/golden/cross_let/expected/loopir.txt +100 -0
- data/golden/cross_let/expected/nast.txt +46 -0
- data/golden/cross_let/expected/runtime.json +61 -0
- data/golden/cross_let/expected/schema_javascript.mjs +136 -0
- data/golden/cross_let/expected/schema_ruby.rb +123 -0
- data/golden/cross_let/expected/snast.txt +46 -0
- data/golden/cross_let/expected/vecir.txt +51 -0
- data/golden/cross_let/input.json +7 -0
- data/golden/cross_let/schema.kumi +25 -0
- data/golden/decimal_explicit/expected/dfir.txt +6 -6
- data/golden/decimal_explicit/expected/dfir_optimized.txt +6 -6
- data/golden/decimal_explicit/expected/loopir.txt +6 -6
- data/golden/decimal_explicit/expected/runtime.json +10 -0
- data/golden/decimal_explicit/expected/schema_ruby.rb +1 -1
- data/golden/decimal_explicit/expected/snast.txt +9 -9
- data/golden/decimal_explicit/expected/vecir.txt +6 -6
- data/golden/element_arrays/expected/loopir.txt +6 -13
- data/golden/element_arrays/expected/runtime.json +117 -0
- data/golden/element_arrays/expected/schema_javascript.mjs +7 -14
- data/golden/element_arrays/expected/schema_ruby.rb +8 -15
- data/golden/empty_and_null_inputs/expected/loopir.txt +4 -9
- data/golden/empty_and_null_inputs/expected/runtime.json +8 -0
- data/golden/empty_and_null_inputs/expected/schema_javascript.mjs +5 -10
- data/golden/empty_and_null_inputs/expected/schema_ruby.rb +6 -11
- data/golden/example_xpto/expected/runtime.json +4 -0
- data/golden/example_xpto/expected/schema_ruby.rb +1 -1
- data/golden/function_overload/expected/runtime.json +8 -0
- data/golden/function_overload/expected/schema_ruby.rb +1 -1
- data/golden/game_of_life/expected/loopir.txt +1 -58
- data/golden/game_of_life/expected/runtime.json +418 -0
- data/golden/game_of_life/expected/schema_javascript.mjs +1 -58
- data/golden/game_of_life/expected/schema_ruby.rb +2 -59
- data/golden/hash_keys/expected/runtime.json +20 -0
- data/golden/hash_keys/expected/schema_ruby.rb +1 -1
- data/golden/hash_value/expected/runtime.json +19 -0
- data/golden/hash_value/expected/schema_ruby.rb +1 -1
- data/golden/hierarchical_complex/expected/loopir.txt +0 -4
- data/golden/hierarchical_complex/expected/runtime.json +16 -0
- data/golden/hierarchical_complex/expected/schema_javascript.mjs +0 -4
- data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -5
- data/golden/inline_rename_scope_leak/expected/loopir.txt +1 -5
- data/golden/inline_rename_scope_leak/expected/runtime.json +10 -0
- data/golden/inline_rename_scope_leak/expected/schema_javascript.mjs +3 -7
- data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +4 -8
- data/golden/input_reference/expected/dfir.txt +2 -2
- data/golden/input_reference/expected/dfir_optimized.txt +2 -2
- data/golden/input_reference/expected/loopir.txt +2 -5
- data/golden/input_reference/expected/runtime.json +15 -0
- data/golden/input_reference/expected/schema_javascript.mjs +3 -6
- data/golden/input_reference/expected/schema_ruby.rb +4 -7
- data/golden/input_reference/expected/snast.txt +1 -1
- data/golden/input_reference/expected/vecir.txt +2 -2
- data/golden/interleaved_fusion/expected/loopir.txt +5 -10
- data/golden/interleaved_fusion/expected/runtime.json +37 -0
- data/golden/interleaved_fusion/expected/schema_javascript.mjs +5 -10
- data/golden/interleaved_fusion/expected/schema_ruby.rb +6 -11
- data/golden/let_inline/expected/runtime.json +6 -0
- data/golden/let_inline/expected/schema_ruby.rb +2 -2
- data/golden/loop_fusion/expected/loopir.txt +3 -7
- data/golden/loop_fusion/expected/runtime.json +29 -0
- data/golden/loop_fusion/expected/schema_javascript.mjs +3 -7
- data/golden/loop_fusion/expected/schema_ruby.rb +4 -8
- data/golden/min_max_empty_arrays/expected/loopir.txt +5 -12
- data/golden/min_max_empty_arrays/expected/runtime.json +16 -0
- data/golden/min_max_empty_arrays/expected/schema_javascript.mjs +7 -14
- data/golden/min_max_empty_arrays/expected/schema_ruby.rb +10 -17
- data/golden/min_reduce_scope/expected/loopir.txt +3 -7
- data/golden/min_reduce_scope/expected/runtime.json +11 -0
- data/golden/min_reduce_scope/expected/schema_javascript.mjs +4 -8
- data/golden/min_reduce_scope/expected/schema_ruby.rb +5 -9
- data/golden/mixed_dimensions/expected/loopir.txt +2 -5
- data/golden/mixed_dimensions/expected/runtime.json +34 -0
- data/golden/mixed_dimensions/expected/schema_javascript.mjs +3 -6
- data/golden/mixed_dimensions/expected/schema_ruby.rb +4 -7
- data/golden/mlp_backprop/expected/ast.txt +147 -0
- data/golden/mlp_backprop/expected/dfir.txt +99 -0
- data/golden/mlp_backprop/expected/dfir_optimized.txt +374 -0
- data/golden/mlp_backprop/expected/input_plan.txt +17 -0
- data/golden/mlp_backprop/expected/loopir.txt +440 -0
- data/golden/mlp_backprop/expected/nast.txt +120 -0
- data/golden/mlp_backprop/expected/runtime.json +43 -0
- data/golden/mlp_backprop/expected/schema_javascript.mjs +516 -0
- data/golden/mlp_backprop/expected/schema_ruby.rb +484 -0
- data/golden/mlp_backprop/expected/snast.txt +120 -0
- data/golden/mlp_backprop/expected/vecir.txt +374 -0
- data/golden/mlp_backprop/input.json +8 -0
- data/golden/mlp_backprop/schema.kumi +43 -0
- data/golden/multi_loop_reduction/expected/loopir.txt +2 -6
- data/golden/multi_loop_reduction/expected/runtime.json +5 -0
- data/golden/multi_loop_reduction/expected/schema_javascript.mjs +4 -8
- data/golden/multi_loop_reduction/expected/schema_ruby.rb +5 -9
- data/golden/multirank_hoisting/expected/loopir.txt +24 -42
- data/golden/multirank_hoisting/expected/runtime.json +47 -0
- data/golden/multirank_hoisting/expected/schema_javascript.mjs +24 -45
- data/golden/multirank_hoisting/expected/schema_ruby.rb +25 -43
- data/golden/nested_hash/expected/runtime.json +3 -0
- data/golden/nested_hash/expected/schema_ruby.rb +1 -1
- data/golden/outer_let/expected/ast.txt +106 -0
- data/golden/outer_let/expected/dfir.txt +66 -0
- data/golden/outer_let/expected/dfir_optimized.txt +145 -0
- data/golden/outer_let/expected/input_plan.txt +19 -0
- data/golden/outer_let/expected/loopir.txt +166 -0
- data/golden/outer_let/expected/nast.txt +78 -0
- data/golden/outer_let/expected/runtime.json +89 -0
- data/golden/outer_let/expected/schema_javascript.mjs +220 -0
- data/golden/outer_let/expected/schema_ruby.rb +201 -0
- data/golden/outer_let/expected/snast.txt +78 -0
- data/golden/outer_let/expected/vecir.txt +145 -0
- data/golden/outer_let/input.json +9 -0
- data/golden/outer_let/schema.kumi +32 -0
- data/golden/pairwise_cross/expected/ast.txt +62 -0
- data/golden/pairwise_cross/expected/dfir.txt +36 -0
- data/golden/pairwise_cross/expected/dfir_optimized.txt +50 -0
- data/golden/pairwise_cross/expected/input_plan.txt +8 -0
- data/golden/pairwise_cross/expected/loopir.txt +84 -0
- data/golden/pairwise_cross/expected/nast.txt +46 -0
- data/golden/pairwise_cross/expected/runtime.json +80 -0
- data/golden/pairwise_cross/expected/schema_javascript.mjs +113 -0
- data/golden/pairwise_cross/expected/schema_ruby.rb +104 -0
- data/golden/pairwise_cross/expected/snast.txt +46 -0
- data/golden/pairwise_cross/expected/vecir.txt +50 -0
- data/golden/pairwise_cross/input.json +8 -0
- data/golden/pairwise_cross/schema.kumi +18 -0
- data/golden/reduction_broadcast/expected/loopir.txt +4 -10
- data/golden/reduction_broadcast/expected/runtime.json +14 -0
- data/golden/reduction_broadcast/expected/schema_javascript.mjs +4 -10
- data/golden/reduction_broadcast/expected/schema_ruby.rb +5 -11
- data/golden/roll/expected/loopir.txt +4 -8
- data/golden/roll/expected/runtime.json +26 -0
- data/golden/roll/expected/schema_javascript.mjs +4 -8
- data/golden/roll/expected/schema_ruby.rb +5 -9
- data/golden/schema_imports_broadcasting_with_imports/expected/dfir.txt +3 -3
- data/golden/schema_imports_broadcasting_with_imports/expected/dfir_optimized.txt +3 -3
- data/golden/schema_imports_broadcasting_with_imports/expected/loopir.txt +3 -4
- data/golden/schema_imports_broadcasting_with_imports/expected/runtime.json +11 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/schema_javascript.mjs +2 -3
- data/golden/schema_imports_broadcasting_with_imports/expected/schema_ruby.rb +3 -4
- data/golden/schema_imports_broadcasting_with_imports/expected/snast.txt +5 -5
- data/golden/schema_imports_broadcasting_with_imports/expected/vecir.txt +3 -3
- data/golden/schema_imports_complex_order_calc/expected/dfir.txt +20 -20
- data/golden/schema_imports_complex_order_calc/expected/dfir_optimized.txt +60 -60
- data/golden/schema_imports_complex_order_calc/expected/loopir.txt +34 -47
- data/golden/schema_imports_complex_order_calc/expected/runtime.json +33 -0
- data/golden/schema_imports_complex_order_calc/expected/schema_javascript.mjs +16 -29
- data/golden/schema_imports_complex_order_calc/expected/schema_ruby.rb +17 -30
- data/golden/schema_imports_complex_order_calc/expected/snast.txt +26 -26
- data/golden/schema_imports_complex_order_calc/expected/vecir.txt +60 -60
- data/golden/schema_imports_composed_order/expected/dfir.txt +3 -3
- data/golden/schema_imports_composed_order/expected/dfir_optimized.txt +6 -6
- data/golden/schema_imports_composed_order/expected/loopir.txt +6 -6
- data/golden/schema_imports_composed_order/expected/runtime.json +9 -0
- data/golden/schema_imports_composed_order/expected/schema_ruby.rb +1 -1
- data/golden/schema_imports_composed_order/expected/snast.txt +5 -5
- data/golden/schema_imports_composed_order/expected/vecir.txt +6 -6
- data/golden/schema_imports_discount_with_tax/expected/dfir.txt +7 -7
- data/golden/schema_imports_discount_with_tax/expected/dfir_optimized.txt +10 -10
- data/golden/schema_imports_discount_with_tax/expected/loopir.txt +10 -10
- data/golden/schema_imports_discount_with_tax/expected/runtime.json +10 -0
- data/golden/schema_imports_discount_with_tax/expected/schema_ruby.rb +1 -1
- data/golden/schema_imports_discount_with_tax/expected/snast.txt +11 -11
- data/golden/schema_imports_discount_with_tax/expected/vecir.txt +10 -10
- data/golden/schema_imports_line_items/expected/dfir.txt +1 -1
- data/golden/schema_imports_line_items/expected/dfir_optimized.txt +3 -3
- data/golden/schema_imports_line_items/expected/loopir.txt +3 -6
- data/golden/schema_imports_line_items/expected/runtime.json +8 -0
- data/golden/schema_imports_line_items/expected/schema_javascript.mjs +4 -7
- data/golden/schema_imports_line_items/expected/schema_ruby.rb +5 -8
- data/golden/schema_imports_line_items/expected/snast.txt +1 -1
- data/golden/schema_imports_line_items/expected/vecir.txt +3 -3
- data/golden/schema_imports_multiple/expected/dfir.txt +7 -7
- data/golden/schema_imports_multiple/expected/dfir_optimized.txt +13 -13
- data/golden/schema_imports_multiple/expected/loopir.txt +13 -13
- data/golden/schema_imports_multiple/expected/runtime.json +10 -0
- data/golden/schema_imports_multiple/expected/schema_ruby.rb +1 -1
- data/golden/schema_imports_multiple/expected/snast.txt +11 -11
- data/golden/schema_imports_multiple/expected/vecir.txt +13 -13
- data/golden/schema_imports_nested_expressions/expected/dfir.txt +4 -4
- data/golden/schema_imports_nested_expressions/expected/dfir_optimized.txt +6 -6
- data/golden/schema_imports_nested_expressions/expected/loopir.txt +6 -6
- data/golden/schema_imports_nested_expressions/expected/runtime.json +8 -0
- data/golden/schema_imports_nested_expressions/expected/schema_ruby.rb +1 -1
- data/golden/schema_imports_nested_expressions/expected/snast.txt +6 -6
- data/golden/schema_imports_nested_expressions/expected/vecir.txt +6 -6
- data/golden/schema_imports_nested_with_reductions/expected/dfir.txt +6 -6
- data/golden/schema_imports_nested_with_reductions/expected/dfir_optimized.txt +15 -15
- data/golden/schema_imports_nested_with_reductions/expected/loopir.txt +7 -14
- data/golden/schema_imports_nested_with_reductions/expected/runtime.json +12 -0
- data/golden/schema_imports_nested_with_reductions/expected/schema_javascript.mjs +8 -15
- data/golden/schema_imports_nested_with_reductions/expected/schema_ruby.rb +9 -16
- data/golden/schema_imports_nested_with_reductions/expected/snast.txt +6 -6
- data/golden/schema_imports_nested_with_reductions/expected/vecir.txt +15 -15
- data/golden/schema_imports_with_imports/expected/dfir.txt +3 -3
- data/golden/schema_imports_with_imports/expected/dfir_optimized.txt +3 -3
- data/golden/schema_imports_with_imports/expected/loopir.txt +3 -3
- data/golden/schema_imports_with_imports/expected/runtime.json +7 -0
- data/golden/schema_imports_with_imports/expected/schema_ruby.rb +1 -1
- data/golden/schema_imports_with_imports/expected/snast.txt +5 -5
- data/golden/schema_imports_with_imports/expected/vecir.txt +3 -3
- data/golden/shift/expected/loopir.txt +4 -8
- data/golden/shift/expected/runtime.json +38 -0
- data/golden/shift/expected/schema_javascript.mjs +4 -8
- data/golden/shift/expected/schema_ruby.rb +5 -9
- data/golden/shift_2d/expected/loopir.txt +8 -16
- data/golden/shift_2d/expected/runtime.json +146 -0
- data/golden/shift_2d/expected/schema_javascript.mjs +8 -16
- data/golden/shift_2d/expected/schema_ruby.rb +9 -17
- data/golden/simple_math/expected/runtime.json +10 -0
- data/golden/simple_math/expected/schema_ruby.rb +1 -1
- data/golden/streaming_basics/expected/loopir.txt +0 -3
- data/golden/streaming_basics/expected/runtime.json +25 -0
- data/golden/streaming_basics/expected/schema_javascript.mjs +3 -6
- data/golden/streaming_basics/expected/schema_ruby.rb +4 -7
- data/golden/transcendentals/expected/ast.txt +48 -0
- data/golden/transcendentals/expected/dfir.txt +31 -0
- data/golden/transcendentals/expected/dfir_optimized.txt +34 -0
- data/golden/transcendentals/expected/input_plan.txt +5 -0
- data/golden/transcendentals/expected/loopir.txt +46 -0
- data/golden/transcendentals/expected/nast.txt +34 -0
- data/golden/transcendentals/expected/runtime.json +27 -0
- data/golden/transcendentals/expected/schema_javascript.mjs +66 -0
- data/golden/transcendentals/expected/schema_ruby.rb +63 -0
- data/golden/transcendentals/expected/snast.txt +34 -0
- data/golden/transcendentals/expected/vecir.txt +34 -0
- data/golden/transcendentals/input.json +1 -0
- data/golden/transcendentals/schema.kumi +16 -0
- data/golden/tuples/expected/dfir.txt +4 -4
- data/golden/tuples/expected/runtime.json +12 -0
- data/golden/tuples/expected/schema_ruby.rb +1 -1
- data/golden/tuples_and_arrays/expected/dfir.txt +1 -1
- data/golden/tuples_and_arrays/expected/loopir.txt +0 -1
- data/golden/tuples_and_arrays/expected/runtime.json +13 -0
- data/golden/tuples_and_arrays/expected/schema_javascript.mjs +1 -2
- data/golden/tuples_and_arrays/expected/schema_ruby.rb +2 -3
- data/golden/type_promotion/expected/ast.txt +75 -0
- data/golden/type_promotion/expected/dfir.txt +38 -0
- data/golden/type_promotion/expected/dfir_optimized.txt +51 -0
- data/golden/type_promotion/expected/input_plan.txt +9 -0
- data/golden/type_promotion/expected/loopir.txt +57 -0
- data/golden/type_promotion/expected/nast.txt +57 -0
- data/golden/type_promotion/expected/runtime.json +23 -0
- data/golden/type_promotion/expected/schema_javascript.mjs +90 -0
- data/golden/type_promotion/expected/schema_ruby.rb +89 -0
- data/golden/type_promotion/expected/snast.txt +57 -0
- data/golden/type_promotion/expected/vecir.txt +51 -0
- data/golden/type_promotion/input.json +11 -0
- data/golden/type_promotion/schema.kumi +30 -0
- data/golden/us_tax_2024/expected/dfir.txt +6 -6
- data/golden/us_tax_2024/expected/loopir.txt +108 -217
- data/golden/us_tax_2024/expected/runtime.json +423 -0
- data/golden/us_tax_2024/expected/schema_javascript.mjs +108 -235
- data/golden/us_tax_2024/expected/schema_ruby.rb +109 -218
- data/golden/vector_make_object/expected/schema_ruby.rb +1 -1
- data/golden/with_constants/expected/schema_ruby.rb +1 -1
- data/lib/kumi/analyzer.rb +29 -34
- data/lib/kumi/configuration.rb +31 -0
- data/lib/kumi/core/analyzer/binder.rb +2 -2
- data/lib/kumi/core/analyzer/macro_expander.rb +3 -3
- data/lib/kumi/core/analyzer/pass_failure.rb +1 -1
- data/lib/kumi/core/analyzer/pass_manager.rb +211 -100
- data/lib/kumi/core/analyzer/passes/attach_anchors_pass.rb +44 -15
- data/lib/kumi/core/analyzer/passes/attach_terminal_info_pass.rb +6 -20
- data/lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb +235 -35
- data/lib/kumi/core/analyzer/passes/codegen/loop/ruby/emitter.rb +6 -3
- data/lib/kumi/core/analyzer/passes/codegen/loop_js_pass.rb +3 -0
- data/lib/kumi/core/analyzer/passes/codegen/loop_ruby_pass.rb +3 -0
- data/lib/kumi/core/analyzer/passes/constant_folding_pass.rb +3 -0
- data/lib/kumi/core/analyzer/passes/contract_checker_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/declaration_validator_pass.rb +83 -0
- data/lib/kumi/core/analyzer/passes/{dependency_resolver.rb → dependency_resolver_pass.rb} +8 -6
- data/lib/kumi/core/analyzer/passes/df_validate_pass.rb +3 -13
- data/lib/kumi/core/analyzer/passes/import_analysis_pass.rb +7 -2
- data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +4 -28
- data/lib/kumi/core/analyzer/passes/{input_collector.rb → input_collector_pass.rb} +30 -3
- data/lib/kumi/core/analyzer/passes/input_form_schema_pass.rb +4 -1
- data/lib/kumi/core/analyzer/passes/ir_execution_schedule_pass.rb +4 -2
- data/lib/kumi/core/analyzer/passes/ir_lower_pass.rb +35 -0
- data/lib/kumi/core/analyzer/passes/ir_validate_pass.rb +43 -0
- data/lib/kumi/core/analyzer/passes/{load_input_cse.rb → load_input_cse_pass.rb} +1 -1
- data/lib/kumi/core/analyzer/passes/loop/lower_pass.rb +23 -10
- data/lib/kumi/core/analyzer/passes/loop_validate_pass.rb +2 -10
- data/lib/kumi/core/analyzer/passes/lower_to_dfir_pass.rb +4 -2
- data/lib/kumi/core/analyzer/passes/{name_indexer.rb → name_indexer_pass.rb} +3 -7
- data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +174 -23
- data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +19 -3
- data/lib/kumi/core/analyzer/passes/output_schema_pass.rb +3 -0
- data/lib/kumi/core/analyzer/passes/pass_base.rb +78 -5
- data/lib/kumi/core/analyzer/passes/precompute_access_paths_pass.rb +7 -1
- data/lib/kumi/core/analyzer/passes/{semantic_constraint_validator.rb → semantic_constraint_validator_pass.rb} +7 -14
- data/lib/kumi/core/analyzer/passes/snast_pass.rb +22 -48
- data/lib/kumi/core/analyzer/passes/{toposorter.rb → toposorter_pass.rb} +4 -5
- data/lib/kumi/core/analyzer/passes/{unsat_detector.rb → unsat_detector_pass.rb} +4 -15
- data/lib/kumi/core/analyzer/passes/vec/lower_pass.rb +6 -8
- data/lib/kumi/core/analyzer/passes/vec_validate_pass.rb +2 -10
- data/lib/kumi/core/analyzer/structs/input_meta.rb +1 -1
- data/lib/kumi/core/compiler/access_builder.rb +1 -1
- data/lib/kumi/core/compiler/access_codegen.rb +1 -1
- data/lib/kumi/core/compiler/access_emit/base.rb +1 -1
- data/lib/kumi/core/compiler/access_planner_v2.rb +2 -2
- data/lib/kumi/core/compiler/accessors/each_indexed_accessor.rb +1 -1
- data/lib/kumi/core/compiler/accessors/materialize_accessor.rb +1 -1
- data/lib/kumi/core/compiler/accessors/ravel_accessor.rb +1 -1
- data/lib/kumi/core/compiler/accessors/read_accessor.rb +1 -1
- data/lib/kumi/core/error_reporter.rb +6 -11
- data/lib/kumi/core/errors.rb +37 -11
- data/lib/kumi/core/expression_renderer.rb +97 -0
- data/lib/kumi/core/functions/overload_resolver.rb +79 -149
- data/lib/kumi/core/ir/execution_engine/combinators.rb +16 -10
- data/lib/kumi/core/nast.rb +24 -0
- data/lib/kumi/core/ruby_parser/dsl.rb +12 -2
- data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +14 -12
- data/lib/kumi/core/ruby_parser/guard_rails.rb +15 -1
- data/lib/kumi/core/ruby_parser/input_builder.rb +55 -48
- data/lib/kumi/core/ruby_parser/parser.rb +25 -17
- data/lib/kumi/core/ruby_parser/schema_builder.rb +43 -15
- data/lib/kumi/core/ruby_parser/sugar.rb +0 -86
- data/lib/kumi/core/types/dtype_rule.rb +76 -0
- data/lib/kumi/core/types/normalizer.rb +2 -2
- data/lib/kumi/core/types/profile.rb +82 -0
- data/lib/kumi/core/types/registry.rb +87 -0
- data/lib/kumi/core/types/system.rb +96 -0
- data/lib/kumi/core/types/value_objects.rb +14 -13
- data/lib/kumi/core/types.rb +41 -34
- data/lib/kumi/dev/golden_runtime.rb +163 -0
- data/lib/kumi/dev/golden_v2.rb +30 -5
- data/lib/kumi/dev/pretty_printer.rb +40 -73
- data/lib/kumi/frontends/ruby.rb +52 -12
- data/lib/kumi/frontends/source_frame.rb +71 -0
- data/lib/kumi/frontends/text.rb +4 -48
- data/lib/kumi/function_registry/loader.rb +101 -0
- data/lib/kumi/function_registry.rb +150 -0
- data/lib/kumi/ir/base/block.rb +11 -2
- data/lib/kumi/ir/base/builder.rb +1 -1
- data/lib/kumi/ir/base/function.rb +2 -4
- data/lib/kumi/ir/base/instruction.rb +10 -23
- data/lib/kumi/ir/base/module.rb +5 -4
- data/lib/kumi/ir/buf/lower.rb +1 -2
- data/lib/kumi/ir/df/access_contract.rb +3 -5
- data/lib/kumi/ir/df/import_inliner.rb +2 -5
- data/lib/kumi/ir/df/lower.rb +72 -51
- data/lib/kumi/ir/df/ops/axis_cross.rb +34 -0
- data/lib/kumi/ir/df/ops/axis_outer.rb +35 -0
- data/lib/kumi/ir/df/passes/broadcast_simplify.rb +1 -2
- data/lib/kumi/ir/df/passes/cse.rb +8 -3
- data/lib/kumi/ir/df/passes/decl_inlining.rb +5 -26
- data/lib/kumi/ir/df/passes/import_inlining.rb +42 -36
- data/lib/kumi/ir/df/passes/load_dedup.rb +9 -4
- data/lib/kumi/ir/df/passes/stencil_cse.rb +2 -5
- data/lib/kumi/ir/df/passes/support/instruction_cloner.rb +41 -14
- data/lib/kumi/ir/df/passes/tuple_fold_canonicalization.rb +8 -3
- data/lib/kumi/ir/df/passes/tuple_to_object.rb +1 -2
- data/lib/kumi/ir/df/validator.rb +3 -3
- data/lib/kumi/ir/df.rb +8 -0
- data/lib/kumi/ir/loop/lower.rb +231 -37
- data/lib/kumi/ir/loop/passes/array_contraction.rb +124 -0
- data/lib/kumi/ir/loop/passes/copy_cleanup.rb +112 -0
- data/lib/kumi/ir/loop/passes/loop_fusion.rb +125 -0
- data/lib/kumi/ir/loop/passes/support/structure.rb +100 -0
- data/lib/kumi/ir/loop/passes.rb +14 -0
- data/lib/kumi/ir/loop/pipeline.rb +9 -1
- data/lib/kumi/ir/loop/validator.rb +29 -4
- data/lib/kumi/ir/loop.rb +1 -0
- data/lib/kumi/ir/passes/register_generator.rb +32 -0
- data/lib/kumi/ir/testing/snast_factory.rb +2 -4
- data/lib/kumi/ir/vec/lower.rb +17 -7
- data/lib/kumi/ir/vec/ops/core_ops.rb +41 -0
- data/lib/kumi/ir/vec/passes/axis_canonicalization.rb +1 -2
- data/lib/kumi/ir/vec/passes/constant_propagation.rb +75 -47
- data/lib/kumi/ir/vec/passes/dce.rb +2 -5
- data/lib/kumi/ir/vec/passes/gvn.rb +8 -3
- data/lib/kumi/ir/vec/passes/peephole_simplify.rb +30 -13
- data/lib/kumi/ir/vec/passes/stencil_detection.rb +1 -2
- data/lib/kumi/ir/vec/passes/support/algebraic_identities.rb +70 -0
- data/lib/kumi/ir/vec/passes/support/instruction_cloner.rb +17 -3
- data/lib/kumi/ir/vec/validator.rb +26 -6
- data/lib/kumi/schema.rb +14 -10
- data/lib/kumi/schema_metadata/printer.rb +111 -0
- data/lib/kumi/schema_metadata.rb +289 -0
- data/lib/kumi/syntax/location.rb +14 -1
- data/lib/kumi/syntax/root.rb +35 -3
- data/lib/kumi/test_shared_schemas/pairwise.rb +27 -0
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +3 -5
- data/tasks/docs_portal.rake +135 -0
- metadata +203 -81
- data/golden/array_element/expected.json +0 -5
- data/golden/array_index/expected.json +0 -5
- data/golden/cascade_logic/expected.json +0 -5
- data/golden/chained_fusion/expected.json +0 -45
- data/golden/decimal_explicit/expected.json +0 -1
- data/golden/element_arrays/expected.json +0 -55
- data/golden/empty_and_null_inputs/expected.json +0 -8
- data/golden/example_xpto/expected.json +0 -4
- data/golden/game_of_life/expected.json +0 -3
- data/golden/hash_keys/expected.json +0 -20
- data/golden/hash_value/expected.json +0 -19
- data/golden/hierarchical_complex/expected.json +0 -34
- data/golden/inline_rename_scope_leak/expected.json +0 -7
- data/golden/input_reference/expected.json +0 -7
- data/golden/interleaved_fusion/expected.json +0 -37
- data/golden/let_inline/expected.json +0 -1
- data/golden/loop_fusion/expected.json +0 -30
- data/golden/min_max_empty_arrays/expected.json +0 -7
- data/golden/min_reduce_scope/expected.json +0 -9
- data/golden/mixed_dimensions/expected.json +0 -6
- data/golden/multi_loop_reduction/expected.json +0 -5
- data/golden/multirank_hoisting/expected.json +0 -15
- data/golden/nested_hash/expected.json +0 -3
- data/golden/reduction_broadcast/expected.json +0 -25
- data/golden/roll/expected.json +0 -6
- data/golden/schema_imports_broadcasting_with_imports/expected.json +0 -4
- data/golden/schema_imports_complex_order_calc/expected.json +0 -12
- data/golden/schema_imports_composed_order/expected.json +0 -6
- data/golden/schema_imports_discount_with_tax/expected.json +0 -7
- data/golden/schema_imports_line_items/expected.json +0 -5
- data/golden/schema_imports_multiple/expected.json +0 -7
- data/golden/schema_imports_nested_expressions/expected.json +0 -5
- data/golden/schema_imports_nested_with_reductions/expected.json +0 -6
- data/golden/schema_imports_with_imports/expected.json +0 -4
- data/golden/shift/expected.json +0 -8
- data/golden/shift_2d/expected.json +0 -15
- data/golden/simple_math/expected.json +0 -1
- data/golden/streaming_basics/expected.json +0 -10
- data/golden/tuples/expected.json +0 -7
- data/golden/tuples_and_arrays/expected.json +0 -18
- data/golden/us_tax_2024/expected.json +0 -120
- data/lib/kumi/core/analyzer/passes/assemble_irv2_pass.rb +0 -130
- data/lib/kumi/core/analyzer/passes/declaration_validator.rb +0 -45
- data/lib/kumi/core/analyzer/passes/lower_to_irv2_pass.rb +0 -197
- data/lib/kumi/core/compiler/access_planner.rb +0 -258
- data/lib/kumi/core/functions/function_spec.rb +0 -17
- data/lib/kumi/core/functions/loader.rb +0 -63
- data/lib/kumi/core/functions/type_categories.rb +0 -44
- data/lib/kumi/core/functions/type_error_reporter.rb +0 -116
- data/lib/kumi/core/functions/type_rules.rb +0 -228
- data/lib/kumi/core/irv2/builder.rb +0 -48
- data/lib/kumi/core/irv2/declaration.rb +0 -28
- data/lib/kumi/core/irv2/module.rb +0 -108
- data/lib/kumi/core/irv2/value.rb +0 -28
- data/lib/kumi/core/types/validator.rb +0 -33
- data/lib/kumi/dev/codegen.rb +0 -194
- data/lib/kumi/dev/golden/generator.rb +0 -109
- data/lib/kumi/dev/golden/reporter.rb +0 -169
- data/lib/kumi/dev/golden/representation.rb +0 -40
- data/lib/kumi/dev/golden/result.rb +0 -106
- data/lib/kumi/dev/golden/runtime_test.rb +0 -131
- data/lib/kumi/dev/golden/suite.rb +0 -147
- data/lib/kumi/dev/golden/value_normalizer.rb +0 -78
- data/lib/kumi/dev/golden/verifier.rb +0 -76
- data/lib/kumi/dev/golden.rb +0 -82
- data/lib/kumi/dev/golden_schema_wrapper.rb +0 -116
- data/lib/kumi/dev/printer/irv2_formatter.rb +0 -163
- data/lib/kumi/kernel_registry.rb +0 -59
- data/lib/kumi/pack/builder.rb +0 -229
- data/lib/kumi/pack.rb +0 -15
- data/lib/kumi/registry_v2/loader.rb +0 -170
- data/lib/kumi/registry_v2.rb +0 -135
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.1.0] – 2026-06-20
|
|
4
|
+
### Added
|
|
5
|
+
- **Parser rewrite integration** (kumi-parser ≥ 0.1.0): new lexer/parser/grammar with located, framed parse errors. Semantic and parse errors now render on one `file:line:col` format; no more `?:?`.
|
|
6
|
+
- **Cross-target semantics doc + parity tests** (`docs/CROSS_TARGET_SEMANTICS.md`): pinned Ruby/JS contracts for `to_string(float)`, `to_integer`/`to_float` of strings, and `pow` with a negative base and fractional exponent, each backed by a spec running Ruby against real node JS.
|
|
7
|
+
- **Cross-references** beyond plain input fields: cross-axis (`A × A'`) iteration in Loop lowering, usable through `let`.
|
|
8
|
+
- **Optimization passes**: algebraic identity folding (`x*1`, `x/1`, `x-0`, integer `x+0`/`x*0`) in Vec constant propagation and a Loop-layer copy-cleanup pass.
|
|
9
|
+
- `SchemaMetadata`: the schema's algebra exposed as read-only data.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **Type system consolidated** under one `Core::Types` namespace (Registry/Profile/System/DtypeRule) with a per-target `Profile` seam; decimal > float > integer promotion lattice.
|
|
13
|
+
- Compiler invariants, backend capability limits, and registry/schema/IR faults are now typed errors that break loudly instead of silently degrading.
|
|
14
|
+
- `__select__` condition is typed `boolean`; the JS Loop emitter supports `impl`-only kernels (emitted once as a module-level helper), mirroring Ruby.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `to_string(float)`, string `to_integer`/`to_float`, and `pow` of a negative base produced divergent results between Ruby and JS; kernels rewritten so both targets agree.
|
|
18
|
+
- Typo'd input paths (`input.nope`) report a clean located user error instead of a compiler bug deep in dimensional analysis.
|
|
19
|
+
- `PeepholeSimplify` could drop a def while keeping a stale use.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- Vestigial `return_stamp` IR field, dead IRv2/pack tooling, and duplicated `RegisterGenerator`/terminal-instruction lookups across IR passes.
|
|
23
|
+
|
|
24
|
+
## [0.0.40] – 2026-06-15
|
|
25
|
+
### Added
|
|
26
|
+
- LoopIR optimization pipeline (`Kumi::IR::Loop::Pipeline`), previously empty, now runs two passes on every compile:
|
|
27
|
+
- **LoopFusion** — merges sibling loops over the same axis and source into one traversal, hoisting independent scalar barriers above the loop. Stencil consumers (shifts, lengths, re-iteration of a partially built array) and accumulator reads correctly block fusion.
|
|
28
|
+
- **ArrayContraction** — replaces intermediate arrays that are filled and read back at the same index within one loop with the scalar that fills them, eliminating the materialization entirely.
|
|
29
|
+
- `outer(...)` usable from text schemas (requires kumi-parser ≥ 0.0.33, now the pinned version). Golden `outer_let` covers the text path with Ruby/JS parity.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- Streaming exports throw a `TypeError` when the target aliases an input array (silent zero-length corruption before; feedback loops must double-buffer) and when a typed-array target is passed for record outputs (silently produced an empty buffer before).
|
|
33
|
+
- Array input arity errors now explain the element rule (Kumi maps over arrays by default, so the element must be named) and show the correct single-child form, instead of a cryptic type error.
|
|
34
|
+
- The schema digest (compiled module name + compile-cache key) no longer folds in `RUBY_VERSION`. Generated code is plain Ruby with identical semantics across supported Rubies, so the digest is now Ruby-version-independent; this also lets the codegen goldens verify across the full CI Ruby matrix.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Ruby DSL `let` raised `ArgumentError: struct size differs` since 0.0.37 (`inline: true` leaked into the `ValueDeclaration` struct instead of the `hints:` channel; the text frontend was unaffected).
|
|
38
|
+
- `outer`/`cross` value used through a `let` that lives purely on the inner pairing axis (no dependence on the outer-consuming axis) read `nil` past the inner array's length (`nil can't be coerced into Float`). The materialized read indexed by positional loop depth instead of by the value's own axis; it now matches by axis. Affected both Ruby and JS codegen.
|
|
39
|
+
|
|
3
40
|
## [0.0.37] – 2026-06-11
|
|
4
41
|
### Added
|
|
5
42
|
- Streaming hints for JS codegen
|
data/README.md
CHANGED
|
@@ -175,7 +175,10 @@ The playground's example picker groups these by theme (language tour, business l
|
|
|
175
175
|
## Documentation
|
|
176
176
|
|
|
177
177
|
- **[Syntax Reference](docs/SYNTAX.md)** — DSL syntax, types, operators, functions
|
|
178
|
+
- **[Syntax Notes](docs/SYNTAX_NOTES.md)** — parser differences, nested input recipes, expression literals, and post-parse errors
|
|
179
|
+
- **[Input Shapes](docs/INPUTS.md)** — declaring scalars, arrays, hashes, and the element rule
|
|
178
180
|
- **[Functions Reference](docs/FUNCTIONS.md)** — auto-generated docs for all functions and kernels ([machine-readable JSON](docs/functions-reference.json))
|
|
181
|
+
- **[Cross-Target Semantics](docs/CROSS_TARGET_SEMANTICS.md)** — where Ruby and JS would diverge (float `to_string`, string conversions, `pow`) and how the kernels keep them identical
|
|
179
182
|
- **[Schema Imports](docs/SCHEMA_IMPORTS.md)** — composing and reusing schemas
|
|
180
183
|
- **[Architecture](docs/ARCHITECTURE.md)** — the compiler pipeline and IR stack
|
|
181
184
|
- **[Golden Tests](docs/GOLDEN_TESTS.md)** — the end-to-end test harness
|
|
@@ -7,6 +7,54 @@ functions:
|
|
|
7
7
|
param: number
|
|
8
8
|
aliases: ["abs"]
|
|
9
9
|
|
|
10
|
+
- id: core.sqrt
|
|
11
|
+
kind: elementwise
|
|
12
|
+
params: [{ name: number, dtype: numeric }]
|
|
13
|
+
dtype:
|
|
14
|
+
rule: scalar
|
|
15
|
+
kind: float
|
|
16
|
+
aliases: ["sqrt"]
|
|
17
|
+
|
|
18
|
+
- id: core.sin
|
|
19
|
+
kind: elementwise
|
|
20
|
+
params: [{ name: angle, dtype: numeric }]
|
|
21
|
+
dtype:
|
|
22
|
+
rule: scalar
|
|
23
|
+
kind: float
|
|
24
|
+
aliases: ["sin"]
|
|
25
|
+
|
|
26
|
+
- id: core.cos
|
|
27
|
+
kind: elementwise
|
|
28
|
+
params: [{ name: angle, dtype: numeric }]
|
|
29
|
+
dtype:
|
|
30
|
+
rule: scalar
|
|
31
|
+
kind: float
|
|
32
|
+
aliases: ["cos"]
|
|
33
|
+
|
|
34
|
+
- id: core.exp
|
|
35
|
+
kind: elementwise
|
|
36
|
+
params: [{ name: number, dtype: numeric }]
|
|
37
|
+
dtype:
|
|
38
|
+
rule: scalar
|
|
39
|
+
kind: float
|
|
40
|
+
aliases: ["exp"]
|
|
41
|
+
|
|
42
|
+
- id: core.log
|
|
43
|
+
kind: elementwise
|
|
44
|
+
params: [{ name: number, dtype: numeric }]
|
|
45
|
+
dtype:
|
|
46
|
+
rule: scalar
|
|
47
|
+
kind: float
|
|
48
|
+
aliases: ["log"]
|
|
49
|
+
|
|
50
|
+
- id: core.tanh
|
|
51
|
+
kind: elementwise
|
|
52
|
+
params: [{ name: number, dtype: numeric }]
|
|
53
|
+
dtype:
|
|
54
|
+
rule: scalar
|
|
55
|
+
kind: float
|
|
56
|
+
aliases: ["tanh"]
|
|
57
|
+
|
|
10
58
|
- id: core.max
|
|
11
59
|
kind: elementwise
|
|
12
60
|
params:
|
|
@@ -30,3 +30,16 @@ functions:
|
|
|
30
30
|
rule: scalar
|
|
31
31
|
kind: string
|
|
32
32
|
aliases: ["to_string", "to_str"]
|
|
33
|
+
|
|
34
|
+
# Float overload: a whole-valued float must stringify as "3.0", not "3", and
|
|
35
|
+
# both targets must agree. Ruby's Float#to_s already does this; the JS kernel
|
|
36
|
+
# reproduces Ruby's exact float formatting (see data/kernels/.../coercion.yaml
|
|
37
|
+
# and docs/CROSS_TARGET_SEMANTICS.md). Picked over the generic overload for
|
|
38
|
+
# float arguments by dtype-based overload resolution.
|
|
39
|
+
- id: core.to_string:float
|
|
40
|
+
kind: elementwise
|
|
41
|
+
params: [{ name: value, dtype: float }]
|
|
42
|
+
dtype:
|
|
43
|
+
rule: scalar
|
|
44
|
+
kind: string
|
|
45
|
+
aliases: ["to_string", "to_str"]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
functions:
|
|
2
2
|
- id: __select__
|
|
3
3
|
kind: elementwise
|
|
4
|
-
params: [{ name: condition_mask }, { name: value_when_true }, { name: value_when_false }]
|
|
4
|
+
params: [{ name: condition_mask, dtype: boolean }, { name: value_when_true }, { name: value_when_false }]
|
|
5
5
|
dtype:
|
|
6
6
|
rule: same_as
|
|
7
7
|
param: value_when_true
|
|
@@ -9,6 +9,38 @@ functions:
|
|
|
9
9
|
policy: wrap # wrap|clamp
|
|
10
10
|
axis_offset: 0
|
|
11
11
|
|
|
12
|
+
# Re-exposes the source array under a fresh, independent axis (the
|
|
13
|
+
# broadcast-dual of a reduction). Lowered specially: it introduces a new
|
|
14
|
+
# innermost loop over the same carrier so an array can be combined with
|
|
15
|
+
# itself (e.g. all-pairs / N-body). Axis stamping lives in the dimensional
|
|
16
|
+
# analyzer; this entry exists so resolution/validation treat cross as a
|
|
17
|
+
# known unary call.
|
|
18
|
+
- id: cross
|
|
19
|
+
kind: elementwise
|
|
20
|
+
params: [{ name: source }]
|
|
21
|
+
dtype:
|
|
22
|
+
rule: same_as
|
|
23
|
+
param: source
|
|
24
|
+
constraint_semantics:
|
|
25
|
+
domain_effect: NONE
|
|
26
|
+
pure_combiner: false
|
|
27
|
+
|
|
28
|
+
# Re-exposes a value from a DIFFERENT array as a fresh inner axis, so two
|
|
29
|
+
# distinct arrays can be paired all-pairs (A x B). Where `cross` self-joins one
|
|
30
|
+
# array, `outer` joins two. Lowered specially: it opens a new innermost loop
|
|
31
|
+
# over the *other* array's carrier. Axis stamping lives in the dimensional
|
|
32
|
+
# analyzer; this entry exists so resolution/validation treat outer as a known
|
|
33
|
+
# unary call.
|
|
34
|
+
- id: outer
|
|
35
|
+
kind: elementwise
|
|
36
|
+
params: [{ name: source }]
|
|
37
|
+
dtype:
|
|
38
|
+
rule: same_as
|
|
39
|
+
param: source
|
|
40
|
+
constraint_semantics:
|
|
41
|
+
domain_effect: NONE
|
|
42
|
+
pure_combiner: false
|
|
43
|
+
|
|
12
44
|
- id: shift
|
|
13
45
|
kind: elementwise
|
|
14
46
|
params: [{ name: source }, { name: offset }]
|
|
@@ -3,6 +3,30 @@ kernels:
|
|
|
3
3
|
fn: core.abs
|
|
4
4
|
inline: "= Math.abs($0)"
|
|
5
5
|
|
|
6
|
+
- id: sqrt:javascript:v1
|
|
7
|
+
fn: core.sqrt
|
|
8
|
+
inline: "= Math.sqrt($0)"
|
|
9
|
+
|
|
10
|
+
- id: sin:javascript:v1
|
|
11
|
+
fn: core.sin
|
|
12
|
+
inline: "= Math.sin($0)"
|
|
13
|
+
|
|
14
|
+
- id: cos:javascript:v1
|
|
15
|
+
fn: core.cos
|
|
16
|
+
inline: "= Math.cos($0)"
|
|
17
|
+
|
|
18
|
+
- id: exp:javascript:v1
|
|
19
|
+
fn: core.exp
|
|
20
|
+
inline: "= Math.exp($0)"
|
|
21
|
+
|
|
22
|
+
- id: log:javascript:v1
|
|
23
|
+
fn: core.log
|
|
24
|
+
inline: "= Math.log($0)"
|
|
25
|
+
|
|
26
|
+
- id: tanh:javascript:v1
|
|
27
|
+
fn: core.tanh
|
|
28
|
+
inline: "= Math.tanh($0)"
|
|
29
|
+
|
|
6
30
|
- id: mod:javascript:v1
|
|
7
31
|
fn: core.mod
|
|
8
32
|
inline: "= (($0 % $1) + $1) % $1"
|
|
@@ -4,17 +4,31 @@ kernels:
|
|
|
4
4
|
inline: "= typeof $0 === 'string' ? parseFloat($0) : Number($0)"
|
|
5
5
|
impl: "(value) {\n if (typeof value === 'string') return parseFloat(value);\n return Number(value);\n}"
|
|
6
6
|
|
|
7
|
+
# Match Ruby String#to_i / Numeric#to_i (and to_f): a non-numeric string is 0
|
|
8
|
+
# (not NaN), parsing is base-10 (so "0x1f" is 0, not 31), and a numeric value
|
|
9
|
+
# truncates toward zero without scientific-notation surprises (Math.trunc, not
|
|
10
|
+
# parseInt — parseInt(1e-7) is 1). Emitted as module-level helpers (no inline).
|
|
11
|
+
# Contract documented in docs/CROSS_TARGET_SEMANTICS.md.
|
|
7
12
|
- id: to_integer:javascript:v1
|
|
8
13
|
fn: core.to_integer
|
|
9
|
-
|
|
10
|
-
impl: "(value) { return parseInt(value); }"
|
|
14
|
+
impl: "(value) {\n if (typeof value === 'number') return Math.trunc(value) || 0;\n const m = String(value).trim().match(/^[+-]?\\d+/);\n return m ? parseInt(m[0], 10) : 0;\n}"
|
|
11
15
|
|
|
12
16
|
- id: to_float:javascript:v1
|
|
13
17
|
fn: core.to_float
|
|
14
|
-
|
|
15
|
-
impl: "(value) { return parseFloat(value); }"
|
|
18
|
+
impl: "(value) {\n if (typeof value === 'number') return value;\n const f = parseFloat(value);\n return Number.isNaN(f) ? 0.0 : f;\n}"
|
|
16
19
|
|
|
17
20
|
- id: to_string:javascript:v1
|
|
18
21
|
fn: core.to_string
|
|
19
22
|
inline: "= String($0)"
|
|
20
23
|
impl: "(value) { return String(value); }"
|
|
24
|
+
|
|
25
|
+
# Reproduce Ruby's Float#to_s so to_string(float) matches across targets:
|
|
26
|
+
# - whole floats keep ".0" (3.0 -> "3.0", not JS's "3")
|
|
27
|
+
# - negative zero keeps its sign (-0.0 -> "-0.0")
|
|
28
|
+
# - exponent form is "1.0e+21" / "1.0e-07" (mantissa gets ".0", exponent
|
|
29
|
+
# is signed and zero-padded to >= 2 digits)
|
|
30
|
+
# No `inline`: this is emitted once as a module-level helper function. The
|
|
31
|
+
# contract is documented in docs/CROSS_TARGET_SEMANTICS.md.
|
|
32
|
+
- id: to_string_float:javascript:v1
|
|
33
|
+
fn: core.to_string:float
|
|
34
|
+
impl: "(value) {\n if (Number.isNaN(value)) return 'NaN';\n if (value === Infinity) return 'Infinity';\n if (value === -Infinity) return '-Infinity';\n if (Object.is(value, -0)) return '-0.0';\n let s = String(value);\n const e = s.indexOf('e');\n if (e !== -1) {\n let mant = s.slice(0, e);\n let exp = s.slice(e + 1);\n if (!mant.includes('.')) mant += '.0';\n const sign = exp[0] === '-' ? '-' : '+';\n let digits = exp.replace(/^[+-]/, '');\n if (digits.length < 2) digits = '0' + digits;\n return mant + 'e' + sign + digits;\n }\n return s.includes('.') ? s : s + '.0';\n}"
|
|
@@ -2,7 +2,31 @@ kernels:
|
|
|
2
2
|
- id: abs:ruby:v1
|
|
3
3
|
fn: core.abs
|
|
4
4
|
inline: "= $0.abs"
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
- id: sqrt:ruby:v1
|
|
7
|
+
fn: core.sqrt
|
|
8
|
+
inline: "= Math.sqrt($0)"
|
|
9
|
+
|
|
10
|
+
- id: sin:ruby:v1
|
|
11
|
+
fn: core.sin
|
|
12
|
+
inline: "= Math.sin($0)"
|
|
13
|
+
|
|
14
|
+
- id: cos:ruby:v1
|
|
15
|
+
fn: core.cos
|
|
16
|
+
inline: "= Math.cos($0)"
|
|
17
|
+
|
|
18
|
+
- id: exp:ruby:v1
|
|
19
|
+
fn: core.exp
|
|
20
|
+
inline: "= Math.exp($0)"
|
|
21
|
+
|
|
22
|
+
- id: log:ruby:v1
|
|
23
|
+
fn: core.log
|
|
24
|
+
inline: "= Math.log($0)"
|
|
25
|
+
|
|
26
|
+
- id: tanh:ruby:v1
|
|
27
|
+
fn: core.tanh
|
|
28
|
+
inline: "= Math.tanh($0)"
|
|
29
|
+
|
|
6
30
|
- id: mod.ruby:v1
|
|
7
31
|
fn: core.mod
|
|
8
32
|
inline: "= $0 % $1"
|
|
@@ -37,10 +61,14 @@ kernels:
|
|
|
37
61
|
inline: "= $0 * $1"
|
|
38
62
|
impl: "(str, count)\n str * count"
|
|
39
63
|
|
|
64
|
+
# A negative base with a fractional exponent makes Ruby return a Complex,
|
|
65
|
+
# whereas JS Math.pow returns NaN. A numeric DSL should not silently produce
|
|
66
|
+
# complex numbers, so collapse the Complex case to NaN to match JS.
|
|
67
|
+
# Contract documented in docs/CROSS_TARGET_SEMANTICS.md.
|
|
40
68
|
- id: pow:ruby:v1
|
|
41
69
|
fn: core.pow
|
|
42
|
-
inline: "= $0 ** $1"
|
|
43
|
-
impl: "(a, b)\n a ** b"
|
|
70
|
+
inline: "= (($0) ** ($1)).then { |r| r.is_a?(Complex) ? Float::NAN : r }"
|
|
71
|
+
impl: "(a, b)\n r = a ** b\n r.is_a?(Complex) ? Float::NAN : r"
|
|
44
72
|
|
|
45
73
|
- id: div:ruby:v1
|
|
46
74
|
fn: core.div
|
|
@@ -18,3 +18,11 @@ kernels:
|
|
|
18
18
|
fn: core.to_string
|
|
19
19
|
inline: "= $0.to_s"
|
|
20
20
|
impl: "(value)\n value.to_s"
|
|
21
|
+
|
|
22
|
+
# Float#to_s already renders Ruby's canonical float form ("3.0", "1.0e+21"),
|
|
23
|
+
# which IS the cross-target contract; the JS kernel reproduces it exactly.
|
|
24
|
+
# See docs/CROSS_TARGET_SEMANTICS.md.
|
|
25
|
+
- id: to_string_float:ruby:v1
|
|
26
|
+
fn: core.to_string:float
|
|
27
|
+
inline: "= $0.to_s"
|
|
28
|
+
impl: "(value)\n value.to_s"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Cross-Target Semantics
|
|
2
|
+
|
|
3
|
+
Kumi compiles one schema to **both Ruby and JavaScript**, and the central promise
|
|
4
|
+
is that the two targets compute **identical results**. Most operations are
|
|
5
|
+
identical for free (arithmetic, comparisons, array iteration). A handful are not,
|
|
6
|
+
because the two host languages disagree on edge-case behavior. This document is
|
|
7
|
+
the single place that records those cases and where each is pinned.
|
|
8
|
+
|
|
9
|
+
## Where the contract lives
|
|
10
|
+
|
|
11
|
+
Per-operation semantics live in **kernels**, one YAML entry per `(function,
|
|
12
|
+
target)`:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
data/kernels/ruby/**/*.yaml # Ruby implementation of each function
|
|
16
|
+
data/kernels/javascript/**/*.yaml # JavaScript implementation of each function
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A kernel has an `inline` form (expanded at the call site) and/or an `impl` form
|
|
20
|
+
(a named helper function emitted once per module). When Ruby and JS would
|
|
21
|
+
otherwise diverge, the kernels are written so **both produce the agreed result**,
|
|
22
|
+
and the YAML carries a comment pointing back here.
|
|
23
|
+
|
|
24
|
+
This is the configurable seam: changing a cross-target rule means editing the two
|
|
25
|
+
kernels (and the golden snapshots), not the compiler. Nothing in the analyzer or
|
|
26
|
+
emitters hard-codes these semantics.
|
|
27
|
+
|
|
28
|
+
> The Ruby and JS Loop emitters both support `impl`-only kernels (a kernel with
|
|
29
|
+
> no `inline` is emitted once as a module-level helper, `__<fn_id>(...)`), so a
|
|
30
|
+
> non-trivial rule can be a clean named function on both targets instead of an
|
|
31
|
+
> inline blob. See `codegen/loop/{ruby,js}/emitter.rb`.
|
|
32
|
+
|
|
33
|
+
## The pinned cases
|
|
34
|
+
|
|
35
|
+
### `to_string` of a float — keep the `.0`
|
|
36
|
+
|
|
37
|
+
A whole-valued float must stringify the same way on both targets.
|
|
38
|
+
|
|
39
|
+
| value | Ruby `to_s` | JS `String()` | Kumi (both) |
|
|
40
|
+
| --------- | ----------- | ------------- | ----------- |
|
|
41
|
+
| `3.0` | `"3.0"` | `"3"` | `"3.0"` |
|
|
42
|
+
| `100.0` | `"100.0"` | `"100"` | `"100.0"` |
|
|
43
|
+
| `-0.0` | `"-0.0"` | `"0"` | `"-0.0"` |
|
|
44
|
+
| `1e21` | `"1.0e+21"` | `"1e+21"` | `"1.0e+21"` |
|
|
45
|
+
| `1e-7` | `"1.0e-07"` | `"1e-7"` | `"1.0e-07"` |
|
|
46
|
+
|
|
47
|
+
The contract is **Ruby's `Float#to_s`** (the float dtype stays visible in the
|
|
48
|
+
string). JS cannot distinguish `3` (integer) from `3.0` (float) at runtime, so
|
|
49
|
+
this is a **dtype-dispatched overload**: `core.to_string:float` (param
|
|
50
|
+
`dtype: float`) is selected for float arguments by overload resolution, and its
|
|
51
|
+
JS kernel reproduces Ruby's formatting (mantissa `.0`, signed zero-padded
|
|
52
|
+
exponent, `-0.0` sign). Non-float `to_string` keeps the plain `String($0)`.
|
|
53
|
+
|
|
54
|
+
Kernels: `to_string_float:ruby:v1`, `to_string_float:javascript:v1`.
|
|
55
|
+
|
|
56
|
+
### `to_integer` / `to_float` of a string — Ruby parsing, not JS parsing
|
|
57
|
+
|
|
58
|
+
`String#to_i` / `String#to_f` and JS `parseInt` / `parseFloat` disagree:
|
|
59
|
+
|
|
60
|
+
| input | Ruby `to_i` | JS `parseInt` | Kumi (both) |
|
|
61
|
+
| ---------- | ----------- | ------------- | ----------- |
|
|
62
|
+
| `"abc"` | `0` | `NaN` | `0` |
|
|
63
|
+
| `""` | `0` | `NaN` | `0` |
|
|
64
|
+
| `"0x1f"` | `0` | `31` (hex) | `0` |
|
|
65
|
+
| `"12abc"` | `12` | `12` | `12` |
|
|
66
|
+
|
|
67
|
+
The contract is **Ruby's** behavior: a non-numeric string is the type's zero
|
|
68
|
+
(`0` / `0.0`), parsing is base-10 (no `0x` hex), and a numeric value truncates
|
|
69
|
+
toward zero with `Math.trunc` (JS `parseInt(1e-7)` is the wrong `1`). The JS
|
|
70
|
+
kernels are written to match.
|
|
71
|
+
|
|
72
|
+
Kernels: `to_integer:javascript:v1`, `to_float:javascript:v1`.
|
|
73
|
+
|
|
74
|
+
### `pow` of a negative base with a fractional exponent — NaN, not Complex
|
|
75
|
+
|
|
76
|
+
`(-8.0) ** (1.0/3)` is mathematically complex. Ruby returns a `Complex`; JS
|
|
77
|
+
`Math.pow` returns `NaN`. A numeric DSL should not silently produce complex
|
|
78
|
+
numbers, so the **contract is `NaN`** (matching JS). The Ruby kernel collapses a
|
|
79
|
+
`Complex` result to `Float::NAN`.
|
|
80
|
+
|
|
81
|
+
Kernel: `pow:ruby:v1`.
|
|
82
|
+
|
|
83
|
+
## Not divergences (documented to prevent false alarms)
|
|
84
|
+
|
|
85
|
+
- **`mean` / `sum` / `min` / `max` of an empty array.** `mean([])` is `NaN` on
|
|
86
|
+
*both* targets (`0.0 / 0`). When probing via JSON, note `JSON.stringify(NaN)`
|
|
87
|
+
is `"null"`, so a Ruby `NaN` looks like a JS `null` through a JSON round-trip —
|
|
88
|
+
that is a serialization artifact, not a semantic difference.
|
|
89
|
+
|
|
90
|
+
## Adding or changing a rule
|
|
91
|
+
|
|
92
|
+
1. Decide the agreed result for both targets.
|
|
93
|
+
2. Edit the Ruby kernel and the JS kernel so each produces it (add a `# see
|
|
94
|
+
docs/CROSS_TARGET_SEMANTICS.md` comment and a row in this file).
|
|
95
|
+
3. Regenerate the golden snapshots: `bin/kumi golden_v2 update`. The
|
|
96
|
+
`runtime.json` of any affected golden should change only where the old
|
|
97
|
+
behavior was actually wrong.
|
|
98
|
+
4. Add a parity case (Ruby vs generated-JS over edge inputs) so the contract is
|
|
99
|
+
tested, not just documented.
|
data/docs/FORM_SCHEMA.md
CHANGED
|
@@ -52,13 +52,18 @@ bin/kumi analyze schema.kumi --dump input_form_schema
|
|
|
52
52
|
|
|
53
53
|
**Schema:**
|
|
54
54
|
```kumi
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
schema do
|
|
56
|
+
input do
|
|
57
|
+
array :items do
|
|
58
|
+
hash :item do
|
|
59
|
+
string :name
|
|
60
|
+
float :price
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
float :discount
|
|
65
|
+
end
|
|
66
|
+
end
|
|
62
67
|
```
|
|
63
68
|
|
|
64
69
|
**Generated Form Schema:**
|
|
@@ -82,4 +87,4 @@ input discount: float
|
|
|
82
87
|
|
|
83
88
|
## Implementation
|
|
84
89
|
|
|
85
|
-
The `InputFormSchemaPass` runs after `
|
|
90
|
+
The `InputFormSchemaPass` runs after `InputCollectorPass` in the analyzer pipeline and produces a clean schema containing only type information needed for form generation, excluding internal metadata like access modes and navigation steps.
|