kumi 0.0.31 → 0.0.33
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 +21 -1
- data/README.md +31 -99
- data/data/kernels/ruby/core/arithmetic.yaml +2 -2
- data/docs/COMPOSED_SCHEMAS.md +137 -0
- data/docs/SCHEMA_IMPORTS.md +275 -0
- data/docs/SYNTAX.md +48 -0
- data/golden/array_element/expected/schema_ruby.rb +2 -27
- data/golden/array_index/expected/nast.txt +6 -6
- data/golden/array_index/expected/schema_ruby.rb +4 -31
- data/golden/array_operations/expected/lir_06_const_prop.txt +4 -8
- data/golden/array_operations/expected/schema_javascript.mjs +4 -8
- data/golden/array_operations/expected/schema_ruby.rb +10 -43
- data/golden/cascade_logic/expected/lir_06_const_prop.txt +7 -14
- data/golden/cascade_logic/expected/schema_javascript.mjs +7 -14
- data/golden/cascade_logic/expected/schema_ruby.rb +11 -45
- data/golden/chained_fusion/expected/lir_06_const_prop.txt +8 -18
- data/golden/chained_fusion/expected/schema_javascript.mjs +8 -18
- data/golden/chained_fusion/expected/schema_ruby.rb +14 -53
- data/golden/decimal_explicit/expected/schema_ruby.rb +4 -31
- data/golden/element_arrays/expected/lir_06_const_prop.txt +5 -11
- data/golden/element_arrays/expected/schema_javascript.mjs +5 -11
- data/golden/element_arrays/expected/schema_ruby.rb +13 -50
- data/golden/empty_and_null_inputs/expected/schema_ruby.rb +4 -31
- data/golden/example_xpto/expected/ast.txt +23 -0
- data/golden/example_xpto/expected/input_plan.txt +1 -0
- data/golden/example_xpto/expected/lir_00_unoptimized.txt +16 -0
- data/golden/example_xpto/expected/lir_01_hoist_scalar_references.txt +16 -0
- data/golden/example_xpto/expected/lir_02_inlined.txt +16 -0
- data/golden/example_xpto/expected/lir_03_cse.txt +16 -0
- data/golden/example_xpto/expected/lir_04_1_loop_fusion.txt +16 -0
- data/golden/example_xpto/expected/lir_04_loop_invcm.txt +16 -0
- data/golden/example_xpto/expected/lir_06_const_prop.txt +13 -0
- data/golden/example_xpto/expected/nast.txt +17 -0
- data/golden/example_xpto/expected/schema_javascript.mjs +13 -0
- data/golden/example_xpto/expected/schema_ruby.rb +13 -0
- data/golden/example_xpto/expected/snast.txt +17 -0
- data/golden/example_xpto/expected.json +4 -0
- data/golden/example_xpto/input.json +3 -0
- data/golden/example_xpto/schema.kumi +8 -0
- data/golden/function_overload/expected/schema_ruby.rb +2 -27
- data/golden/game_of_life/expected/lir_06_const_prop.txt +236 -287
- data/golden/game_of_life/expected/schema_javascript.mjs +32 -39
- data/golden/game_of_life/expected/schema_ruby.rb +34 -66
- data/golden/hash_keys/expected/lir_06_const_prop.txt +4 -10
- data/golden/hash_keys/expected/schema_javascript.mjs +6 -12
- data/golden/hash_keys/expected/schema_ruby.rb +8 -39
- data/golden/hash_value/expected/lir_06_const_prop.txt +3 -6
- data/golden/hash_value/expected/schema_javascript.mjs +3 -6
- data/golden/hash_value/expected/schema_ruby.rb +7 -37
- data/golden/hierarchical_complex/expected/lir_06_const_prop.txt +9 -18
- data/golden/hierarchical_complex/expected/schema_javascript.mjs +9 -18
- data/golden/hierarchical_complex/expected/schema_ruby.rb +14 -51
- data/golden/inline_rename_scope_leak/expected/lir_06_const_prop.txt +2 -6
- data/golden/inline_rename_scope_leak/expected/schema_javascript.mjs +2 -6
- data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +7 -39
- data/golden/input_reference/expected/schema_ruby.rb +6 -35
- data/golden/interleaved_fusion/expected/lir_06_const_prop.txt +6 -14
- data/golden/interleaved_fusion/expected/schema_javascript.mjs +6 -14
- data/golden/interleaved_fusion/expected/schema_ruby.rb +11 -47
- data/golden/let_inline/expected/lir_06_const_prop.txt +1 -2
- data/golden/let_inline/expected/schema_javascript.mjs +1 -2
- data/golden/let_inline/expected/schema_ruby.rb +3 -29
- data/golden/loop_fusion/expected/lir_06_const_prop.txt +4 -10
- data/golden/loop_fusion/expected/schema_javascript.mjs +4 -10
- data/golden/loop_fusion/expected/schema_ruby.rb +8 -41
- data/golden/min_reduce_scope/expected/lir_06_const_prop.txt +3 -6
- data/golden/min_reduce_scope/expected/schema_javascript.mjs +3 -6
- data/golden/min_reduce_scope/expected/schema_ruby.rb +8 -39
- data/golden/mixed_dimensions/expected/lir_06_const_prop.txt +1 -2
- data/golden/mixed_dimensions/expected/schema_javascript.mjs +1 -2
- data/golden/mixed_dimensions/expected/schema_ruby.rb +5 -33
- data/golden/multirank_hoisting/expected/lir_06_const_prop.txt +9 -18
- data/golden/multirank_hoisting/expected/schema_javascript.mjs +9 -18
- data/golden/multirank_hoisting/expected/schema_ruby.rb +16 -55
- data/golden/nested_hash/expected/lir_06_const_prop.txt +1 -2
- data/golden/nested_hash/expected/schema_javascript.mjs +1 -2
- data/golden/nested_hash/expected/schema_ruby.rb +3 -29
- data/golden/reduction_broadcast/expected/schema_ruby.rb +5 -33
- data/golden/roll/expected/lir_06_const_prop.txt +8 -15
- data/golden/roll/expected/schema_javascript.mjs +8 -15
- data/golden/roll/expected/schema_ruby.rb +13 -48
- data/golden/schema_imports_broadcasting_with_imports/expected/ast.txt +26 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/input_plan.txt +5 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_00_unoptimized.txt +20 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_01_hoist_scalar_references.txt +20 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_02_inlined.txt +22 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_03_cse.txt +21 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_04_1_loop_fusion.txt +21 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_04_loop_invcm.txt +21 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/lir_06_const_prop.txt +21 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/nast.txt +12 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/schema_javascript.mjs +22 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/schema_ruby.rb +24 -0
- data/golden/schema_imports_broadcasting_with_imports/expected/snast.txt +12 -0
- data/golden/schema_imports_broadcasting_with_imports/expected.json +4 -0
- data/golden/schema_imports_broadcasting_with_imports/input.json +7 -0
- data/golden/schema_imports_broadcasting_with_imports/schema.kumi +14 -0
- data/golden/schema_imports_complex_order_calc/expected/ast.txt +82 -0
- data/golden/schema_imports_complex_order_calc/expected/input_plan.txt +16 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_00_unoptimized.txt +94 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_01_hoist_scalar_references.txt +94 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_02_inlined.txt +187 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_03_cse.txt +131 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_04_1_loop_fusion.txt +131 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_04_loop_invcm.txt +131 -0
- data/golden/schema_imports_complex_order_calc/expected/lir_06_const_prop.txt +131 -0
- data/golden/schema_imports_complex_order_calc/expected/nast.txt +56 -0
- data/golden/schema_imports_complex_order_calc/expected/schema_javascript.mjs +147 -0
- data/golden/schema_imports_complex_order_calc/expected/schema_ruby.rb +149 -0
- data/golden/schema_imports_complex_order_calc/expected/snast.txt +56 -0
- data/golden/schema_imports_complex_order_calc/expected.json +12 -0
- data/golden/schema_imports_complex_order_calc/input.json +20 -0
- data/golden/schema_imports_complex_order_calc/schema.kumi +33 -0
- data/golden/schema_imports_composed_order/expected/ast.txt +33 -0
- data/golden/schema_imports_composed_order/expected/input_plan.txt +3 -0
- data/golden/schema_imports_composed_order/expected/lir_00_unoptimized.txt +25 -0
- data/golden/schema_imports_composed_order/expected/lir_01_hoist_scalar_references.txt +25 -0
- data/golden/schema_imports_composed_order/expected/lir_02_inlined.txt +33 -0
- data/golden/schema_imports_composed_order/expected/lir_03_cse.txt +33 -0
- data/golden/schema_imports_composed_order/expected/lir_04_1_loop_fusion.txt +33 -0
- data/golden/schema_imports_composed_order/expected/lir_04_loop_invcm.txt +33 -0
- data/golden/schema_imports_composed_order/expected/lir_06_const_prop.txt +33 -0
- data/golden/schema_imports_composed_order/expected/nast.txt +25 -0
- data/golden/schema_imports_composed_order/expected/schema_javascript.mjs +35 -0
- data/golden/schema_imports_composed_order/expected/schema_ruby.rb +33 -0
- data/golden/schema_imports_composed_order/expected/snast.txt +25 -0
- data/golden/schema_imports_composed_order/expected.json +6 -0
- data/golden/schema_imports_composed_order/input.json +5 -0
- data/golden/schema_imports_composed_order/schema.kumi +15 -0
- data/golden/schema_imports_discount_with_tax/expected/ast.txt +37 -0
- data/golden/schema_imports_discount_with_tax/expected/input_plan.txt +2 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_00_unoptimized.txt +30 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_01_hoist_scalar_references.txt +30 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_02_inlined.txt +37 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_03_cse.txt +34 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_04_1_loop_fusion.txt +34 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_04_loop_invcm.txt +34 -0
- data/golden/schema_imports_discount_with_tax/expected/lir_06_const_prop.txt +34 -0
- data/golden/schema_imports_discount_with_tax/expected/nast.txt +30 -0
- data/golden/schema_imports_discount_with_tax/expected/schema_javascript.mjs +37 -0
- data/golden/schema_imports_discount_with_tax/expected/schema_ruby.rb +34 -0
- data/golden/schema_imports_discount_with_tax/expected/snast.txt +30 -0
- data/golden/schema_imports_discount_with_tax/expected.json +7 -0
- data/golden/schema_imports_discount_with_tax/input.json +4 -0
- data/golden/schema_imports_discount_with_tax/schema.kumi +15 -0
- data/golden/schema_imports_line_items/expected/ast.txt +35 -0
- data/golden/schema_imports_line_items/expected/input_plan.txt +8 -0
- data/golden/schema_imports_line_items/expected/lir_00_unoptimized.txt +19 -0
- data/golden/schema_imports_line_items/expected/lir_01_hoist_scalar_references.txt +19 -0
- data/golden/schema_imports_line_items/expected/lir_02_inlined.txt +24 -0
- data/golden/schema_imports_line_items/expected/lir_03_cse.txt +22 -0
- data/golden/schema_imports_line_items/expected/lir_04_1_loop_fusion.txt +22 -0
- data/golden/schema_imports_line_items/expected/lir_04_loop_invcm.txt +22 -0
- data/golden/schema_imports_line_items/expected/lir_06_const_prop.txt +22 -0
- data/golden/schema_imports_line_items/expected/nast.txt +19 -0
- data/golden/schema_imports_line_items/expected/schema_javascript.mjs +23 -0
- data/golden/schema_imports_line_items/expected/schema_ruby.rb +22 -0
- data/golden/schema_imports_line_items/expected/snast.txt +19 -0
- data/golden/schema_imports_line_items/expected.json +5 -0
- data/golden/schema_imports_line_items/input.json +13 -0
- data/golden/schema_imports_line_items/schema.kumi +17 -0
- data/golden/schema_imports_multiple/expected/ast.txt +35 -0
- data/golden/schema_imports_multiple/expected/input_plan.txt +2 -0
- data/golden/schema_imports_multiple/expected/lir_00_unoptimized.txt +29 -0
- data/golden/schema_imports_multiple/expected/lir_01_hoist_scalar_references.txt +29 -0
- data/golden/schema_imports_multiple/expected/lir_02_inlined.txt +41 -0
- data/golden/schema_imports_multiple/expected/lir_03_cse.txt +37 -0
- data/golden/schema_imports_multiple/expected/lir_04_1_loop_fusion.txt +37 -0
- data/golden/schema_imports_multiple/expected/lir_04_loop_invcm.txt +37 -0
- data/golden/schema_imports_multiple/expected/lir_06_const_prop.txt +37 -0
- data/golden/schema_imports_multiple/expected/nast.txt +28 -0
- data/golden/schema_imports_multiple/expected/schema_javascript.mjs +40 -0
- data/golden/schema_imports_multiple/expected/schema_ruby.rb +37 -0
- data/golden/schema_imports_multiple/expected/snast.txt +28 -0
- data/golden/schema_imports_multiple/expected.json +7 -0
- data/golden/schema_imports_multiple/input.json +4 -0
- data/golden/schema_imports_multiple/schema.kumi +15 -0
- data/golden/schema_imports_nested_expressions/expected/ast.txt +31 -0
- data/golden/schema_imports_nested_expressions/expected/input_plan.txt +3 -0
- data/golden/schema_imports_nested_expressions/expected/lir_00_unoptimized.txt +22 -0
- data/golden/schema_imports_nested_expressions/expected/lir_01_hoist_scalar_references.txt +22 -0
- data/golden/schema_imports_nested_expressions/expected/lir_02_inlined.txt +32 -0
- data/golden/schema_imports_nested_expressions/expected/lir_03_cse.txt +32 -0
- data/golden/schema_imports_nested_expressions/expected/lir_04_1_loop_fusion.txt +32 -0
- data/golden/schema_imports_nested_expressions/expected/lir_04_loop_invcm.txt +32 -0
- data/golden/schema_imports_nested_expressions/expected/lir_06_const_prop.txt +28 -0
- data/golden/schema_imports_nested_expressions/expected/nast.txt +23 -0
- data/golden/schema_imports_nested_expressions/expected/schema_javascript.mjs +29 -0
- data/golden/schema_imports_nested_expressions/expected/schema_ruby.rb +28 -0
- data/golden/schema_imports_nested_expressions/expected/snast.txt +23 -0
- data/golden/schema_imports_nested_expressions/expected.json +5 -0
- data/golden/schema_imports_nested_expressions/input.json +5 -0
- data/golden/schema_imports_nested_expressions/schema.kumi +13 -0
- data/golden/schema_imports_nested_with_reductions/expected/ast.txt +47 -0
- data/golden/schema_imports_nested_with_reductions/expected/input_plan.txt +12 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_00_unoptimized.txt +31 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_01_hoist_scalar_references.txt +31 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_02_inlined.txt +58 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_03_cse.txt +49 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_04_1_loop_fusion.txt +51 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_04_loop_invcm.txt +49 -0
- data/golden/schema_imports_nested_with_reductions/expected/lir_06_const_prop.txt +49 -0
- data/golden/schema_imports_nested_with_reductions/expected/nast.txt +23 -0
- data/golden/schema_imports_nested_with_reductions/expected/schema_javascript.mjs +49 -0
- data/golden/schema_imports_nested_with_reductions/expected/schema_ruby.rb +52 -0
- data/golden/schema_imports_nested_with_reductions/expected/snast.txt +23 -0
- data/golden/schema_imports_nested_with_reductions/expected.json +6 -0
- data/golden/schema_imports_nested_with_reductions/input.json +16 -0
- data/golden/schema_imports_nested_with_reductions/schema.kumi +23 -0
- data/golden/schema_imports_with_imports/expected/ast.txt +19 -0
- data/golden/schema_imports_with_imports/expected/input_plan.txt +1 -0
- data/golden/schema_imports_with_imports/expected/lir_00_unoptimized.txt +13 -0
- data/golden/schema_imports_with_imports/expected/lir_01_hoist_scalar_references.txt +13 -0
- data/golden/schema_imports_with_imports/expected/lir_02_inlined.txt +14 -0
- data/golden/schema_imports_with_imports/expected/lir_03_cse.txt +13 -0
- data/golden/schema_imports_with_imports/expected/lir_04_1_loop_fusion.txt +13 -0
- data/golden/schema_imports_with_imports/expected/lir_04_loop_invcm.txt +13 -0
- data/golden/schema_imports_with_imports/expected/lir_06_const_prop.txt +13 -0
- data/golden/schema_imports_with_imports/expected/nast.txt +13 -0
- data/golden/schema_imports_with_imports/expected/schema_javascript.mjs +13 -0
- data/golden/schema_imports_with_imports/expected/schema_ruby.rb +13 -0
- data/golden/schema_imports_with_imports/expected/snast.txt +13 -0
- data/golden/schema_imports_with_imports/expected.json +4 -0
- data/golden/schema_imports_with_imports/input.json +3 -0
- data/golden/schema_imports_with_imports/schema.kumi +10 -0
- data/golden/shift/expected/lir_06_const_prop.txt +18 -30
- data/golden/shift/expected/schema_javascript.mjs +18 -30
- data/golden/shift/expected/schema_ruby.rb +25 -67
- data/golden/shift_2d/expected/lir_06_const_prop.txt +36 -60
- data/golden/shift_2d/expected/schema_javascript.mjs +36 -60
- data/golden/shift_2d/expected/schema_ruby.rb +49 -109
- data/golden/simple_math/expected/lir_06_const_prop.txt +3 -6
- data/golden/simple_math/expected/schema_javascript.mjs +3 -6
- data/golden/simple_math/expected/schema_ruby.rb +8 -39
- data/golden/streaming_basics/expected/lir_06_const_prop.txt +6 -12
- data/golden/streaming_basics/expected/schema_javascript.mjs +6 -12
- data/golden/streaming_basics/expected/schema_ruby.rb +14 -51
- data/golden/tuples/expected/lir_06_const_prop.txt +5 -22
- data/golden/tuples/expected/schema_javascript.mjs +5 -22
- data/golden/tuples/expected/schema_ruby.rb +11 -57
- data/golden/tuples_and_arrays/expected/lir_06_const_prop.txt +4 -8
- data/golden/tuples_and_arrays/expected/schema_javascript.mjs +4 -8
- data/golden/tuples_and_arrays/expected/schema_ruby.rb +9 -41
- data/golden/us_tax_2024/expected/lir_06_const_prop.txt +94 -171
- data/golden/us_tax_2024/expected/schema_javascript.mjs +13 -21
- data/golden/us_tax_2024/expected/schema_ruby.rb +15 -48
- data/golden/with_constants/expected/lir_06_const_prop.txt +3 -8
- data/golden/with_constants/expected/schema_javascript.mjs +3 -8
- data/golden/with_constants/expected/schema_ruby.rb +5 -35
- data/lib/kumi/analyzer.rb +8 -7
- data/lib/kumi/configuration.rb +7 -6
- data/lib/kumi/core/analyzer/passes/attach_anchors_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/attach_terminal_info_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/codegen/js/declaration_emitter.rb +20 -0
- data/lib/kumi/core/analyzer/passes/codegen/ruby/declaration_emitter.rb +16 -7
- data/lib/kumi/core/analyzer/passes/codegen/ruby/output_buffer.rb +3 -35
- data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +6 -0
- data/lib/kumi/core/analyzer/passes/import_analysis_pass.rb +90 -0
- data/lib/kumi/core/analyzer/passes/lir/constant_propagation_pass.rb +77 -36
- data/lib/kumi/core/analyzer/passes/lir/lower_pass.rb +26 -11
- data/lib/kumi/core/analyzer/passes/name_indexer.rb +20 -2
- data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +44 -0
- data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +30 -0
- data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +5 -1
- data/lib/kumi/core/analyzer/passes/snast_pass.rb +15 -0
- data/lib/kumi/core/lir/build.rb +27 -0
- data/lib/kumi/core/lir/peephole.rb +164 -0
- data/lib/kumi/core/nast.rb +16 -0
- data/lib/kumi/core/ruby_parser/build_context.rb +3 -1
- data/lib/kumi/core/ruby_parser/parser.rb +1 -1
- data/lib/kumi/core/ruby_parser/schema_builder.rb +33 -3
- data/lib/kumi/dev/golden/result.rb +9 -3
- data/lib/kumi/dev/golden/runtime_test.rb +16 -1
- data/lib/kumi/dev/golden.rb +18 -20
- data/lib/kumi/dev/golden_schema_modules.rb +8 -0
- data/lib/kumi/dev/golden_schema_wrapper.rb +116 -0
- data/lib/kumi/dev/support/kumi_runner.mjs +18 -0
- data/lib/kumi/schema.rb +44 -2
- data/lib/kumi/support/lir_printer.rb +21 -5
- data/lib/kumi/support/nast_printer.rb +11 -0
- data/lib/kumi/support/s_expression_printer.rb +9 -0
- data/lib/kumi/support/snast_printer.rb +6 -0
- data/lib/kumi/syntax/import_call.rb +11 -0
- data/lib/kumi/syntax/import_declaration.rb +11 -0
- data/lib/kumi/syntax/root.rb +2 -2
- data/lib/kumi/test_shared_schemas/compound.rb +21 -0
- data/lib/kumi/test_shared_schemas/discount.rb +19 -0
- data/lib/kumi/test_shared_schemas/price.rb +19 -0
- data/lib/kumi/test_shared_schemas/subtotal.rb +22 -0
- data/lib/kumi/test_shared_schemas/tax.rb +18 -0
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +19 -4
- metadata +176 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b98e39a72b87991ec1e2055ca1ad9ca996a1e95924ca5d4e201043f679564ed
|
|
4
|
+
data.tar.gz: ac6b469fd9cb3aa8d01bc4907c4f40ba5dbbc900e95e8309a9c68595b57d9c30
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e8b32dbcb228a886a4089992ddaa2cabbe977477226561d09be2d6a3583ebf9d3276f338e99e9612e4c1dbfe6e911801b70747aa4dbc47b820949b2425661f0
|
|
7
|
+
data.tar.gz: 6537e18270ed92b04a80f14c24758cda316a5658a2d92d7940b2f7770ef51518eabd2f421e85516484f0c49b2c699f4bc2bac63e07b08b8108f8cd500654282f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [0.0.33] – 2025-11-05
|
|
4
|
+
### Added
|
|
5
|
+
- **Peephole Optimization Helper:** `Kumi::Core::LIR::Peephole` plus specs for safer peephole transforms, inline Ruby clamp kernel, richer LIR dumps, and refreshed golden files
|
|
6
|
+
- **Import Diagnostics:** Clearer errors with available declaration hints when imports fail
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Default `Kumi.configure.compilation_mode` now `:jit`; override via config or `KUMI_COMPILATION_MODE`
|
|
10
|
+
- Improved constant propagation output formatting for Ruby/JS codegen
|
|
11
|
+
|
|
12
|
+
## [0.0.32] – 2025-10-23
|
|
13
|
+
### Added
|
|
14
|
+
- **Schema Imports:** Reuse declarations from other schemas with `import :name, from: Module` syntax
|
|
15
|
+
- **Improved Import Errors:** Clearer messages with available declaration hints when imports fail
|
|
16
|
+
- Documentation: `docs/COMPOSED_SCHEMAS.md` and `docs/SCHEMA_IMPORTS.md`
|
|
17
|
+
|
|
18
|
+
## [0.0.31] – 2025-10-22
|
|
19
|
+
### Changed
|
|
20
|
+
- Documentation for schema imports in README and SYNTAX
|
|
21
|
+
|
|
2
22
|
## [0.0.30] – 2025-10-21
|
|
3
23
|
### Changed
|
|
4
24
|
- **Analyzer Refactoring:** PassManager now centralizes pass orchestration, replacing manual orchestration in Analyzer.run_analysis_passes
|
|
@@ -174,4 +194,4 @@ Fix - Remove require of pry gem on runtime.
|
|
|
174
194
|
- Ruby >= 3.1 (Was >= 3.0)
|
|
175
195
|
|
|
176
196
|
### Notes
|
|
177
|
-
- No expected DSL changes for typical schemas; report regressions.
|
|
197
|
+
- No expected DSL changes for typical schemas; report regressions.
|
data/README.md
CHANGED
|
@@ -10,116 +10,36 @@
|
|
|
10
10
|
|
|
11
11
|
## What is Kumi?
|
|
12
12
|
|
|
13
|
-
Kumi is a **declarative DSL for building calculation systems
|
|
14
|
-
- **Typed & verifiable at compile time** (catch errors before they hit production)
|
|
15
|
-
- **Vectorized** (arrays and nested data structures work naturally)
|
|
16
|
-
- **Transparent** (inspect generated code and execution order)
|
|
17
|
-
- **Portable** (compile the same schema to Ruby or JavaScript)
|
|
13
|
+
Kumi is a **declarative DSL for building calculation systems**.
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
Schemas define:
|
|
16
|
+
- Input shape (scalars, arrays, nested structures)
|
|
17
|
+
- Declarations (computed values and boolean conditions)
|
|
18
|
+
- Dependencies between declarations
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
The compiler:
|
|
21
|
+
- Performs type checking
|
|
22
|
+
- Detects unsatisfiable constraints
|
|
23
|
+
- Determines evaluation order
|
|
24
|
+
- Generates code for Ruby or JavaScript
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
- Hard to verify (logic spread across multiple files)
|
|
25
|
-
- Fragile (changing one formula breaks hidden dependencies)
|
|
26
|
-
- Duplicated (same logic needed in backend and frontend)
|
|
27
|
-
- Opaque (hard to audit which rules applied to which data)
|
|
26
|
+
## Use Cases
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
Calculation systems appear in: tax engines, pricing models, financial projections, compliance systems, insurance underwriting, shipping rate calculators.
|
|
30
29
|
|
|
31
30
|
---
|
|
32
31
|
|
|
33
32
|
**Status**: experimental. Public API may change. Typing and some static checks are still evolving.
|
|
34
33
|
|
|
35
|
-
**Feedback**: have a use case or hit a rough edge? Open an issue or reach out.
|
|
34
|
+
**Feedback**: have a use case or hit a rough edge? Open an issue or reach out (andremuta+kumi@gmail.com).
|
|
36
35
|
|
|
37
36
|
---
|
|
38
37
|
|
|
39
|
-
##
|
|
38
|
+
## Examples
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<summary><strong>Schema</strong></summary>
|
|
45
|
-
|
|
46
|
-
```ruby
|
|
47
|
-
schema do
|
|
48
|
-
input do
|
|
49
|
-
float :income
|
|
50
|
-
float :state_rate
|
|
51
|
-
float :local_rate
|
|
52
|
-
float :retirement_contrib
|
|
53
|
-
string :filing_status
|
|
54
|
-
|
|
55
|
-
array :statuses do
|
|
56
|
-
hash :status do
|
|
57
|
-
string :name
|
|
58
|
-
float :std
|
|
59
|
-
float :addl_threshold
|
|
60
|
-
array :rates do
|
|
61
|
-
hash :bracket do
|
|
62
|
-
float :lo
|
|
63
|
-
float :hi # -1 = open-ended
|
|
64
|
-
float :rate
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# shared
|
|
72
|
-
let :big_hi, 100_000_000_000.0
|
|
73
|
-
let :state_tax, input.income * input.state_rate
|
|
74
|
-
let :local_tax, input.income * input.local_rate
|
|
75
|
-
|
|
76
|
-
# FICA constants
|
|
77
|
-
let :ss_wage_base, 168_600.0
|
|
78
|
-
let :ss_rate, 0.062
|
|
79
|
-
let :med_base_rate, 0.0145
|
|
80
|
-
let :addl_med_rate, 0.009
|
|
81
|
-
|
|
82
|
-
# per-status federal
|
|
83
|
-
let :taxable, fn(:max, [input.income - input.statuses.status.std, 0])
|
|
84
|
-
let :lo, input.statuses.status.rates.bracket.lo
|
|
85
|
-
let :hi, input.statuses.status.rates.bracket.hi
|
|
86
|
-
let :rate, input.statuses.status.rates.bracket.rate
|
|
87
|
-
let :hi_eff, select(hi == -1, big_hi, hi)
|
|
88
|
-
let :amt, fn(:clamp, taxable - lo, 0, hi_eff - lo)
|
|
89
|
-
let :fed_tax, fn(:sum, amt * rate)
|
|
90
|
-
let :in_br, (taxable >= lo) & (taxable < hi_eff)
|
|
91
|
-
let :fed_marg, fn(:sum_if, rate, in_br)
|
|
92
|
-
let :fed_eff, fed_tax / fn(:max, [input.income, 1.0])
|
|
93
|
-
|
|
94
|
-
# per-status FICA
|
|
95
|
-
let :ss_tax, fn(:min, [input.income, ss_wage_base]) * ss_rate
|
|
96
|
-
let :med_tax, input.income * med_base_rate
|
|
97
|
-
let :addl_med_tax, fn(:max, [input.income - input.statuses.status.addl_threshold, 0]) * addl_med_rate
|
|
98
|
-
let :fica_tax, ss_tax + med_tax + addl_med_tax
|
|
99
|
-
let :fica_eff, fica_tax / fn(:max, [input.income, 1.0])
|
|
100
|
-
|
|
101
|
-
# totals per status
|
|
102
|
-
let :total_tax, fed_tax + fica_tax + state_tax + local_tax
|
|
103
|
-
let :total_eff, total_tax / fn(:max, [input.income, 1.0])
|
|
104
|
-
let :after_tax, input.income - total_tax
|
|
105
|
-
let :take_home, after_tax - input.retirement_contrib
|
|
106
|
-
|
|
107
|
-
# array of result objects, one per status
|
|
108
|
-
value :summary, {
|
|
109
|
-
filing_status: input.statuses.status.name,
|
|
110
|
-
federal: { marginal: fed_marg, effective: fed_eff, tax: fed_tax },
|
|
111
|
-
fica: { effective: fica_eff, tax: fica_tax },
|
|
112
|
-
state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
|
|
113
|
-
local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
|
|
114
|
-
total: { effective: total_eff, tax: total_tax },
|
|
115
|
-
after_tax: after_tax,
|
|
116
|
-
retirement_contrib: input.retirement_contrib,
|
|
117
|
-
take_home: take_home
|
|
118
|
-
}
|
|
119
|
-
end
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
</details>
|
|
40
|
+
- **US Tax Calculator (2024)** — a single schema computes federal, state, and FICA taxes across multiple filing statuses. [Open in the demo](https://kumi-play-web.fly.dev/?example=us-federal-tax-2024).
|
|
41
|
+
- **Monte Carlo Portfolio** — demonstrates probabilistic simulations and table visualizations. [Open in the demo](https://kumi-play-web.fly.dev/?example=monte-carlo-simulation).
|
|
42
|
+
- **Conway's Game of Life** — showcases array operations powering a grid-based simulation. [Open in the demo](https://kumi-play-web.fly.dev/?example=game-of-life).
|
|
123
43
|
|
|
124
44
|
---
|
|
125
45
|
|
|
@@ -129,7 +49,7 @@ end
|
|
|
129
49
|
gem install kumi
|
|
130
50
|
```
|
|
131
51
|
|
|
132
|
-
Requires Ruby 3.1+.
|
|
52
|
+
Requires Ruby 3.1+. Runtime dependencies: `mutex_m` and `zeitwerk` (bundled via Rubygems).
|
|
133
53
|
|
|
134
54
|
## Quick Start
|
|
135
55
|
|
|
@@ -149,17 +69,29 @@ end
|
|
|
149
69
|
result = Double.from(x: 5)
|
|
150
70
|
result[:doubled] # => 10
|
|
151
71
|
|
|
72
|
+
# or just call the method directly
|
|
73
|
+
Double._doubled(x: 5) # => 10
|
|
74
|
+
|
|
152
75
|
# Export to JavaScript (same logic)
|
|
153
76
|
Double.write_source("output.mjs", platform: :javascript)
|
|
77
|
+
# ./output.mjs
|
|
78
|
+
# export function _doubled(input) {
|
|
79
|
+
# let t1 = input["x"];
|
|
80
|
+
# let t3 = t1 * 2;
|
|
81
|
+
# return t3;
|
|
82
|
+
# }
|
|
154
83
|
```
|
|
155
84
|
|
|
85
|
+
You can also override the compilation strategy without touching code by setting
|
|
86
|
+
`KUMI_COMPILATION_MODE` to `jit` or `aot` (e.g. `export KUMI_COMPILATION_MODE=aot`).
|
|
87
|
+
|
|
156
88
|
Try the [interactive demo](https://kumi-play-web.fly.dev/) (no setup required).
|
|
157
89
|
|
|
158
90
|
---
|
|
159
91
|
|
|
160
92
|
## Documentation
|
|
161
93
|
|
|
162
|
-
- **[Syntax Reference](docs/SYNTAX.md)** - DSL syntax, types, operators, and
|
|
94
|
+
- **[Syntax Reference](docs/SYNTAX.md)** - DSL syntax, types, operators, functions, and schema imports
|
|
163
95
|
- **[Functions Reference](docs/FUNCTIONS.md)** - Auto-generated docs for all functions and kernels
|
|
164
96
|
- **[functions-reference.json](docs/functions-reference.json)** - Machine-readable format for IDEs (VSCode, Monaco, etc.)
|
|
165
97
|
- **[Development Guide](docs/DEVELOPMENT.md)** - Testing, debugging, and contributing
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Composed Schemas
|
|
2
|
+
|
|
3
|
+
Multiple schemas can be imported and called within a single schema.
|
|
4
|
+
|
|
5
|
+
## Example: Order Processing with Price and Tax
|
|
6
|
+
|
|
7
|
+
### Step 1: Define Base Schemas
|
|
8
|
+
|
|
9
|
+
**Price Calculation** (`golden/_shared/price.rb`):
|
|
10
|
+
```ruby
|
|
11
|
+
module GoldenSchemas
|
|
12
|
+
module Price
|
|
13
|
+
extend Kumi::Schema
|
|
14
|
+
|
|
15
|
+
schema do
|
|
16
|
+
input do
|
|
17
|
+
decimal :base_price
|
|
18
|
+
decimal :discount_rate
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
value :discounted, input.base_price * (1.0 - input.discount_rate)
|
|
22
|
+
value :discount_amount, input.base_price * input.discount_rate
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Tax Calculation** (`golden/_shared/tax.rb`):
|
|
29
|
+
```ruby
|
|
30
|
+
module GoldenSchemas
|
|
31
|
+
module Tax
|
|
32
|
+
extend Kumi::Schema
|
|
33
|
+
|
|
34
|
+
schema do
|
|
35
|
+
input do
|
|
36
|
+
decimal :amount
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
value :tax, input.amount * 0.15
|
|
40
|
+
value :total, input.amount + tax
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Composed Order Schema
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
module GoldenSchemas
|
|
50
|
+
module ComposedOrder
|
|
51
|
+
extend Kumi::Schema
|
|
52
|
+
|
|
53
|
+
schema do
|
|
54
|
+
import :discounted, :discount_amount, from: GoldenSchemas::Price
|
|
55
|
+
import :total, from: GoldenSchemas::Tax
|
|
56
|
+
|
|
57
|
+
input do
|
|
58
|
+
decimal :item_price
|
|
59
|
+
decimal :quantity
|
|
60
|
+
decimal :discount_rate
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
value :subtotal, input.item_price * input.quantity
|
|
64
|
+
value :price_after_discount, fn(:discounted, base_price: subtotal, discount_rate: input.discount_rate)
|
|
65
|
+
value :discount_amt, fn(:discount_amount, base_price: subtotal, discount_rate: input.discount_rate)
|
|
66
|
+
value :final_total, fn(:total, amount: price_after_discount)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Test Data and Output
|
|
73
|
+
|
|
74
|
+
Input:
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"item_price": 100.0,
|
|
78
|
+
"quantity": 3,
|
|
79
|
+
"discount_rate": 0.1
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Output:
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"subtotal": 300.0,
|
|
87
|
+
"price_after_discount": 270.0,
|
|
88
|
+
"discount_amt": 30.0,
|
|
89
|
+
"final_total": 310.5
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Parameter Mapping
|
|
94
|
+
|
|
95
|
+
Imported functions are called with keyword arguments that map to the imported schema's input fields.
|
|
96
|
+
|
|
97
|
+
Price schema input fields: `base_price`, `discount_rate`
|
|
98
|
+
```kumi
|
|
99
|
+
discounted(base_price: subtotal, discount_rate: input.discount_rate)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Tax schema input fields: `amount`
|
|
103
|
+
```kumi
|
|
104
|
+
total(amount: price_after_discount)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The compiler maps keyword argument names to the imported schema's input field names and generates a runtime call that passes these values as an input hash to the imported schema's method.
|
|
108
|
+
|
|
109
|
+
## Compilation
|
|
110
|
+
|
|
111
|
+
For each imported function call:
|
|
112
|
+
1. Compiler locates the declaration in the imported schema
|
|
113
|
+
2. Maps keyword arguments to input field names
|
|
114
|
+
3. Generates a runtime call to the imported schema's generated method
|
|
115
|
+
4. Passes parameter values as an input hash to the imported function
|
|
116
|
+
5. The imported schema's compiled method executes independently at runtime
|
|
117
|
+
|
|
118
|
+
## Testing
|
|
119
|
+
|
|
120
|
+
Base schemas tested independently:
|
|
121
|
+
```bash
|
|
122
|
+
bin/kumi golden test schema_imports_discount_with_tax
|
|
123
|
+
bin/kumi golden test schema_imports_nested_with_reductions
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Composed schemas tested:
|
|
127
|
+
```bash
|
|
128
|
+
bin/kumi golden test schema_imports_composed_order
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Working examples in `golden/`:
|
|
132
|
+
- `schema_imports_with_imports` - single import
|
|
133
|
+
- `schema_imports_broadcasting_with_imports` - broadcast across arrays
|
|
134
|
+
- `schema_imports_discount_with_tax` - multiple imports
|
|
135
|
+
- `schema_imports_nested_with_reductions` - nested arrays
|
|
136
|
+
- `schema_imports_complex_order_calc` - complex multi-import
|
|
137
|
+
- `schema_imports_composed_order` - composed price and tax
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Schema Imports Feature
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Schema imports allow you to reuse declarations (values and traits) from one schema in another schema by importing them from a source module.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
### Compilation Pipeline
|
|
10
|
+
|
|
11
|
+
The schema import feature works through multiple analysis passes:
|
|
12
|
+
|
|
13
|
+
1. **NameIndexer**: Identifies all imported names in the schema
|
|
14
|
+
2. **ImportAnalysisPass**: Loads the source schemas and extracts their analyzed state
|
|
15
|
+
3. **ConvertCallToImportCall** (text parser only): Converts function calls to ImportCall nodes when the function name matches an imported declaration
|
|
16
|
+
4. **DependencyResolver**: Creates dependency edges for ImportCall nodes
|
|
17
|
+
5. **NormalizeToNASTPass**: **Substitutes** ImportCall nodes with the source expression, mapping parameters
|
|
18
|
+
|
|
19
|
+
### Key Design Principle: Runtime Function Calls
|
|
20
|
+
|
|
21
|
+
Imports are **compiled as runtime function calls** to the imported schema:
|
|
22
|
+
|
|
23
|
+
1. The compiler analyzes the imported schema and generates its module methods
|
|
24
|
+
2. When an imported function is called, the generated code invokes `ImportedModule._function_name(input_hash)`
|
|
25
|
+
3. This allows for modular code generation and schema reuse
|
|
26
|
+
|
|
27
|
+
**Behavior:**
|
|
28
|
+
- Imported functions are called at runtime via generated code
|
|
29
|
+
- Broadcasting still applies - the calling function iterates and calls the imported function for each array element
|
|
30
|
+
- Parameter mapping creates input hashes that are passed to the imported function
|
|
31
|
+
|
|
32
|
+
### Example: Tax Calculation
|
|
33
|
+
|
|
34
|
+
**Imported Schema** (`GoldenSchemas::Tax`):
|
|
35
|
+
```kumi
|
|
36
|
+
schema do
|
|
37
|
+
input do
|
|
38
|
+
decimal :amount
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
value :tax, input.amount * 0.15
|
|
42
|
+
value :total, input.amount + tax
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Calling Schema**:
|
|
47
|
+
```kumi
|
|
48
|
+
import :tax, from: GoldenSchemas::Tax
|
|
49
|
+
|
|
50
|
+
schema do
|
|
51
|
+
input do
|
|
52
|
+
decimal :amount
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
value :tax_result, tax(amount: input.amount)
|
|
56
|
+
value :total, input.amount + tax_result
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Generated Ruby Code**:
|
|
61
|
+
```ruby
|
|
62
|
+
def self._tax_result(input)
|
|
63
|
+
# The imported function is called at runtime:
|
|
64
|
+
t1 = input["amount"] || input[:amount]
|
|
65
|
+
t2 = GoldenSchemas::Tax._tax({"amount" => t1})
|
|
66
|
+
t2
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Note: The generated code uses module-level functions (`def self._name(input)`) rather than instance methods. Imported functions are invoked as module methods with parameter mapping, enabling schema composition and reuse.
|
|
71
|
+
|
|
72
|
+
## Syntax
|
|
73
|
+
|
|
74
|
+
### Import Declaration
|
|
75
|
+
|
|
76
|
+
Text parser syntax:
|
|
77
|
+
```kumi
|
|
78
|
+
import :name1, :name2, from: Module::Path
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Ruby DSL syntax:
|
|
82
|
+
```ruby
|
|
83
|
+
import :name1, :name2, from: SourceModule
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Imported Function Calls
|
|
87
|
+
|
|
88
|
+
**Text parser** (identifier syntax):
|
|
89
|
+
```kumi
|
|
90
|
+
result = tax(amount: input.price)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Ruby DSL** (function call syntax):
|
|
94
|
+
```ruby
|
|
95
|
+
result = fn(:tax, amount: input.price)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Both create an `ImportCall` node which gets substituted during normalization.
|
|
99
|
+
|
|
100
|
+
## Broadcasting with Imports
|
|
101
|
+
|
|
102
|
+
When you pass an array to an imported function, the calling schema iterates over the array and calls the imported function for each element:
|
|
103
|
+
|
|
104
|
+
```kumi
|
|
105
|
+
import :tax, from: GoldenSchemas::Tax
|
|
106
|
+
|
|
107
|
+
schema do
|
|
108
|
+
input do
|
|
109
|
+
array :items do
|
|
110
|
+
hash :item do
|
|
111
|
+
decimal :amount
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
value :item_taxes, tax(amount: input.items.item.amount)
|
|
117
|
+
value :total_tax, fn(:sum, item_taxes)
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
With input `items: [{amount: 100}, {amount: 200}, {amount: 300}]`:
|
|
122
|
+
- The generated code loops over each item and calls `GoldenSchemas::Tax._tax({"amount" => item.amount})`
|
|
123
|
+
- `item_taxes` becomes `[15, 30, 45]`
|
|
124
|
+
- `total_tax` sums to `90`
|
|
125
|
+
|
|
126
|
+
The generated code performs loop iteration and calls the imported function for each array element.
|
|
127
|
+
|
|
128
|
+
## Parameter Mapping
|
|
129
|
+
|
|
130
|
+
When you call an imported function with keyword arguments, the compiler:
|
|
131
|
+
|
|
132
|
+
1. Maps each keyword argument name to the corresponding input field in the imported schema
|
|
133
|
+
2. Constructs an input hash with the mapped parameter values
|
|
134
|
+
3. Generates a runtime call to the imported function with this hash
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
```kumi
|
|
138
|
+
# Imported schema input field: amount
|
|
139
|
+
# Call: fn(:tax, amount: input.price)
|
|
140
|
+
# Generated: GoldenSchemas::Tax._tax({"amount" => input.price})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Multiple parameters work similarly:
|
|
144
|
+
```kumi
|
|
145
|
+
# Imported schema input fields: price, rate
|
|
146
|
+
# Call: fn(:discount, price: invoice.total, rate: 0.1)
|
|
147
|
+
# Generated: GoldenSchemas::Discount._discount({"price" => invoice.total, "rate" => 0.1})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Golden Test Cases
|
|
151
|
+
|
|
152
|
+
### Basic Tests
|
|
153
|
+
1. **`schema_imports_with_imports`** - Single import, scalar parameters
|
|
154
|
+
2. **`schema_imports_broadcasting_with_imports`** - Broadcasting imported functions across arrays
|
|
155
|
+
|
|
156
|
+
### Advanced Tests
|
|
157
|
+
3. **`schema_imports_line_items`** - Importing reduction functions
|
|
158
|
+
- Imports `:subtotal` (sums quantity × price over array)
|
|
159
|
+
- Tests array aggregation through imports
|
|
160
|
+
|
|
161
|
+
4. **`schema_imports_discount_with_tax`** - Multiple imports from different schemas
|
|
162
|
+
- Imports `:tax`, `:discounted`, `:savings`
|
|
163
|
+
- Tests composing multiple imported functions in sequence
|
|
164
|
+
|
|
165
|
+
5. **`schema_imports_nested_with_reductions`** - Nested arrays with imports
|
|
166
|
+
- Imports subtotal and applies it to nested order structure
|
|
167
|
+
- Tests broadcasting imports through multiple levels
|
|
168
|
+
|
|
169
|
+
6. **`schema_imports_complex_order_calc`** - Multiple imports with nested arrays
|
|
170
|
+
- Imports tax, discount, and subtotal functions
|
|
171
|
+
- Tests order processing with taxes, discounts, and summaries across multiple orders
|
|
172
|
+
|
|
173
|
+
## Creating Reusable Schemas
|
|
174
|
+
|
|
175
|
+
Shared schemas are defined in `golden/_shared/` as Ruby DSL modules:
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
module GoldenSchemas
|
|
179
|
+
module Subtotal
|
|
180
|
+
extend Kumi::Schema
|
|
181
|
+
|
|
182
|
+
schema do
|
|
183
|
+
input do
|
|
184
|
+
array :items do
|
|
185
|
+
hash :item do
|
|
186
|
+
integer :quantity
|
|
187
|
+
integer :unit_price
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
value :subtotal, fn(:sum, input.items.item.quantity * input.items.item.unit_price)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Then import in any schema:
|
|
199
|
+
```kumi
|
|
200
|
+
import :subtotal, from: GoldenSchemas::Subtotal
|
|
201
|
+
|
|
202
|
+
schema do
|
|
203
|
+
value :order_total, subtotal(items: input.order_items)
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The shared schemas are automatically loaded and compiled in JIT mode when running golden tests.
|
|
208
|
+
|
|
209
|
+
## Implementation Details
|
|
210
|
+
|
|
211
|
+
### Parser Level (kumi-parser gem)
|
|
212
|
+
|
|
213
|
+
**Text Parser Changes**:
|
|
214
|
+
- Added `import` and `from` keywords to tokenizer
|
|
215
|
+
- Extended `parse_imports()` to handle `import :name, from: Constant::Path` syntax
|
|
216
|
+
- Added `parse_imported_function_call()` to handle identifier syntax like `tax(amount: value)`
|
|
217
|
+
- Tracks `@imported_names` during parsing to create ImportCall nodes when appropriate
|
|
218
|
+
|
|
219
|
+
**Key Methods**:
|
|
220
|
+
- `parse_imports()`: Parses import declarations
|
|
221
|
+
- `parse_constant()`: Parses scope-resolved constants like `GoldenSchemas::Tax`
|
|
222
|
+
- `parse_imported_function_call()`: Parses direct function calls with keyword arguments
|
|
223
|
+
|
|
224
|
+
### Analyzer Level (kumi gem)
|
|
225
|
+
|
|
226
|
+
**New/Modified Passes**:
|
|
227
|
+
- `ImportAnalysisPass`: Loads source schemas, extracts analyzed state, and prepares for runtime calls
|
|
228
|
+
- `SemanticConstraintValidator`: Skips validation for imported function names
|
|
229
|
+
- `CodegenPass`: Generates runtime function calls to imported schema methods
|
|
230
|
+
|
|
231
|
+
**Key Data Structures**:
|
|
232
|
+
- `ImportDeclaration`: Stores import metadata (names, source module)
|
|
233
|
+
- `ImportCall`: Represents a call to an imported function (fn_name, input_mapping)
|
|
234
|
+
- `imported_declarations`: State containing all imported names
|
|
235
|
+
- `imported_schemas`: State containing full analysis of imported schemas (used to ensure schemas are compiled before use)
|
|
236
|
+
|
|
237
|
+
## How Imports Are Compiled
|
|
238
|
+
|
|
239
|
+
**Imports are compiled as runtime function calls:**
|
|
240
|
+
|
|
241
|
+
1. The imported schema is fully analyzed and compiled as a standalone module with its own methods
|
|
242
|
+
2. When you call an imported function, the compiler generates a call to that module's method
|
|
243
|
+
3. Parameters are mapped and passed as a hash to the imported function at runtime
|
|
244
|
+
|
|
245
|
+
**Example:**
|
|
246
|
+
|
|
247
|
+
Imported schema defines: `value :tax, input.amount * 0.15`
|
|
248
|
+
|
|
249
|
+
Your schema calls: `fn(:tax, amount: input.price)`
|
|
250
|
+
|
|
251
|
+
Compiler produces:
|
|
252
|
+
```ruby
|
|
253
|
+
t1 = input["price"] || input[:price]
|
|
254
|
+
t2 = GoldenSchemas::Tax._tax({"amount" => t1})
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The imported function executes independently and returns its result to the calling schema.
|
|
258
|
+
|
|
259
|
+
## Testing Strategy
|
|
260
|
+
|
|
261
|
+
To verify imports are working correctly, test:
|
|
262
|
+
|
|
263
|
+
1. **Parser correctness**: ImportCall nodes are created for imported functions
|
|
264
|
+
2. **Parameter mapping**: Input hashes are correctly constructed from keyword arguments
|
|
265
|
+
3. **Runtime calls**: Generated code properly invokes imported schema methods with parameter hashes
|
|
266
|
+
4. **Broadcasting**: Loop iteration works correctly when passing arrays to imported functions
|
|
267
|
+
5. **Runtime evaluation**: Generated code produces correct results by calling imported functions
|
|
268
|
+
|
|
269
|
+
## Future Enhancements
|
|
270
|
+
|
|
271
|
+
1. **Trait imports**: Support importing traits for reusable conditions
|
|
272
|
+
2. **Nested imports**: Allow imported schemas to import other schemas
|
|
273
|
+
3. **Import aliasing**: Rename imports on import (`import :tax as :tax_calc`)
|
|
274
|
+
4. **Selective exports**: Mark declarations as publicly exportable
|
|
275
|
+
5. **Cross-schema optimization**: Detect and optimize repeated patterns across imports
|
data/docs/SYNTAX.md
CHANGED
|
@@ -328,6 +328,54 @@ trait :eligible, is_member | is_trial
|
|
|
328
328
|
trait :not_active, !is_active
|
|
329
329
|
```
|
|
330
330
|
|
|
331
|
+
### 3) Schema Imports
|
|
332
|
+
|
|
333
|
+
Reuse declarations from other schemas without duplicating code.
|
|
334
|
+
|
|
335
|
+
#### Basic Import
|
|
336
|
+
|
|
337
|
+
```kumi
|
|
338
|
+
import :tax, from: MySchemas::TaxCalculator
|
|
339
|
+
|
|
340
|
+
schema do
|
|
341
|
+
input do
|
|
342
|
+
decimal :amount
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Call imported declaration with input mapping
|
|
346
|
+
value :total, tax(amount: input.amount)
|
|
347
|
+
end
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Multiple Imports
|
|
351
|
+
|
|
352
|
+
```kumi
|
|
353
|
+
import :tax, :discount, from: MySchemas::Utilities
|
|
354
|
+
|
|
355
|
+
schema do
|
|
356
|
+
input do
|
|
357
|
+
decimal :price
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
value :after_tax, tax(amount: input.price)
|
|
361
|
+
value :final, discount(amount: after_tax)
|
|
362
|
+
end
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### Nested Imports
|
|
366
|
+
|
|
367
|
+
Imported schemas can themselves import from other schemas. The compiler resolves the full chain automatically.
|
|
368
|
+
|
|
369
|
+
```kumi
|
|
370
|
+
# PriceSchema imports TaxSchema internally
|
|
371
|
+
import :final_price, from: MySchemas::PriceSchema
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Key Rules:**
|
|
375
|
+
- All imported declarations must be available when the schema loads
|
|
376
|
+
- Input parameters are mapped by name (no positional arguments)
|
|
377
|
+
- Imports work with reductions, broadcasts, and cascades like normal declarations
|
|
378
|
+
|
|
331
379
|
### 4) Functions
|
|
332
380
|
|
|
333
381
|
All functions use `fn(:name, args...)` syntax.
|