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
|
@@ -23,58 +23,99 @@ module Kumi
|
|
|
23
23
|
|
|
24
24
|
def run(_errors)
|
|
25
25
|
ops_by_decl = get_state(:lir_module)
|
|
26
|
-
|
|
26
|
+
out = {}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
ops_by_decl.each do |name, payload|
|
|
29
|
+
operations = Array(payload[:operations])
|
|
30
|
+
out[name] = { operations: optimize_decl(operations) }
|
|
31
|
+
end
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
out = ops_by_decl
|
|
35
|
-
state.with(:lir_06_const_prop, out).with(:lir_module, out.freeze)
|
|
33
|
+
out.freeze
|
|
34
|
+
state.with(:lir_module, out).with(:lir_06_const_prop, out)
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
private
|
|
39
38
|
|
|
40
39
|
def optimize_decl(ops)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
rewritten = ops.map(&:dup)
|
|
41
|
+
use_counts = Hash.new(0)
|
|
42
|
+
|
|
43
|
+
rewritten.each do |ins|
|
|
44
|
+
Array(ins.inputs).each do |reg|
|
|
45
|
+
use_counts[reg] += 1 if reg
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
constants = {}
|
|
50
|
+
|
|
51
|
+
LIR::Peephole.run(rewritten) do |window|
|
|
52
|
+
ins = window.current
|
|
53
|
+
break unless ins
|
|
54
|
+
|
|
55
|
+
substitution = substitute_inputs(ins, constants)
|
|
56
|
+
|
|
57
|
+
if substitution
|
|
58
|
+
new_ins, replaced_regs = substitution
|
|
59
|
+
window.replace(1, with: new_ins)
|
|
60
|
+
replaced_regs.each do |reg|
|
|
61
|
+
use_counts[reg] -= 1
|
|
57
62
|
end
|
|
58
63
|
end
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
register = window.result_register
|
|
66
|
+
constants.delete(register) if register
|
|
67
|
+
|
|
68
|
+
if register && window.const?
|
|
69
|
+
literal = window.literal
|
|
70
|
+
constants[register] = literal if literal
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
window.skip
|
|
74
|
+
end
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
rewritten.reject! do |ins|
|
|
77
|
+
reg = ins.result_register
|
|
78
|
+
ins.opcode == :Constant && reg && use_counts[reg].zero?
|
|
79
|
+
end
|
|
67
80
|
|
|
68
|
-
|
|
81
|
+
rewritten
|
|
82
|
+
end
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
def substitute_inputs(ins, constants)
|
|
85
|
+
original_inputs = Array(ins.inputs)
|
|
86
|
+
original_imms = Array(ins.immediates)
|
|
87
|
+
|
|
88
|
+
new_inputs = []
|
|
89
|
+
new_imms = []
|
|
90
|
+
changed = false
|
|
91
|
+
replaced_regs = []
|
|
92
|
+
|
|
93
|
+
original_inputs.each do |reg|
|
|
94
|
+
if (literal = constants[reg])
|
|
95
|
+
new_inputs << :__immediate_placeholder__
|
|
96
|
+
new_imms << literal
|
|
97
|
+
changed = true
|
|
98
|
+
replaced_regs << reg
|
|
99
|
+
else
|
|
100
|
+
new_inputs << reg
|
|
74
101
|
end
|
|
75
102
|
end
|
|
76
103
|
|
|
77
|
-
|
|
104
|
+
new_imms.concat(original_imms)
|
|
105
|
+
changed ||= new_imms != original_imms
|
|
106
|
+
|
|
107
|
+
return unless changed
|
|
108
|
+
|
|
109
|
+
instruction = LIR::Instruction.new(
|
|
110
|
+
opcode: ins.opcode,
|
|
111
|
+
result_register: ins.result_register,
|
|
112
|
+
stamp: ins.stamp,
|
|
113
|
+
inputs: new_inputs.empty? ? [] : new_inputs,
|
|
114
|
+
immediates: new_imms.empty? ? [] : new_imms,
|
|
115
|
+
attributes: ins.attributes,
|
|
116
|
+
location: ins.location
|
|
117
|
+
)
|
|
118
|
+
[instruction, replaced_regs]
|
|
78
119
|
end
|
|
79
120
|
end
|
|
80
121
|
end
|
|
@@ -75,16 +75,17 @@ module Kumi
|
|
|
75
75
|
|
|
76
76
|
def lower_expr(node)
|
|
77
77
|
case node
|
|
78
|
-
when NAST::Const
|
|
79
|
-
when NAST::InputRef
|
|
80
|
-
when NAST::Ref
|
|
81
|
-
when NAST::Tuple
|
|
82
|
-
when NAST::Select
|
|
83
|
-
when NAST::Fold
|
|
84
|
-
when NAST::Reduce
|
|
85
|
-
when NAST::Call
|
|
86
|
-
when NAST::Hash
|
|
87
|
-
when NAST::IndexRef
|
|
78
|
+
when NAST::Const then emit_const(node)
|
|
79
|
+
when NAST::InputRef then emit_input_ref(node)
|
|
80
|
+
when NAST::Ref then emit_ref(node)
|
|
81
|
+
when NAST::Tuple then emit_tuple(node)
|
|
82
|
+
when NAST::Select then emit_select(node)
|
|
83
|
+
when NAST::Fold then emit_fold(node)
|
|
84
|
+
when NAST::Reduce then emit_reduce(node)
|
|
85
|
+
when NAST::Call then call_emit_selection(node)
|
|
86
|
+
when NAST::Hash then emit_hash(node)
|
|
87
|
+
when NAST::IndexRef then emit_index_ref(node)
|
|
88
|
+
when NAST::ImportCall then emit_import_call(node)
|
|
88
89
|
else raise "unknown node #{node.class}"
|
|
89
90
|
end
|
|
90
91
|
end
|
|
@@ -139,6 +140,20 @@ module Kumi
|
|
|
139
140
|
ins.result_register
|
|
140
141
|
end
|
|
141
142
|
|
|
143
|
+
def emit_import_call(n)
|
|
144
|
+
regs = n.args.map { lower_expr(_1) }
|
|
145
|
+
ins = Build.import_schema_call(
|
|
146
|
+
fn_name: n.fn_name,
|
|
147
|
+
source_module: n.source_module,
|
|
148
|
+
args: regs,
|
|
149
|
+
input_mapping_keys: n.input_mapping_keys,
|
|
150
|
+
out_dtype: dtype_of(n),
|
|
151
|
+
ids: @ids
|
|
152
|
+
)
|
|
153
|
+
@ops << ins
|
|
154
|
+
ins.result_register
|
|
155
|
+
end
|
|
156
|
+
|
|
142
157
|
def emit_select(n)
|
|
143
158
|
ax = axes_of(n)
|
|
144
159
|
ensure_context_for!(ax, anchor_fqn: anchor_fqn_from_node!(n, need_prefix: ax)) unless ax.empty?
|
|
@@ -321,7 +336,7 @@ module Kumi
|
|
|
321
336
|
walk.call(x.on_false)
|
|
322
337
|
when NAST::Reduce, NAST::Fold
|
|
323
338
|
walk.call(x.arg)
|
|
324
|
-
when NAST::Call, NAST::Tuple
|
|
339
|
+
when NAST::Call, NAST::Tuple, NAST::ImportCall
|
|
325
340
|
x.args.each { walk.call(_1) }
|
|
326
341
|
when NAST::Hash
|
|
327
342
|
x.pairs.each { walk.call(_1) }
|
|
@@ -8,19 +8,37 @@ module Kumi
|
|
|
8
8
|
# DEPENDENCIES: None (first pass in pipeline)
|
|
9
9
|
# PRODUCES: :declarations - Hash mapping names to declaration nodes
|
|
10
10
|
# - annotates hints to declarations (e.g. inlining)
|
|
11
|
+
# :imported_declarations - Hash of lazy import references
|
|
11
12
|
# INTERFACE: new(schema, state).run(errors)
|
|
12
13
|
class NameIndexer < PassBase
|
|
13
14
|
def run(errors)
|
|
14
15
|
definitions = {}
|
|
16
|
+
imported_declarations = {}
|
|
15
17
|
hints = {}
|
|
16
18
|
|
|
19
|
+
# Phase 1: Register imports as lazy references
|
|
20
|
+
(schema.imports || []).each do |import_decl|
|
|
21
|
+
import_decl.names.each do |name|
|
|
22
|
+
imported_declarations[name] = {
|
|
23
|
+
type: :import,
|
|
24
|
+
from_module: import_decl.module_ref,
|
|
25
|
+
loc: import_decl.loc
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Phase 2: Index local declarations
|
|
17
31
|
each_decl do |decl|
|
|
18
|
-
|
|
32
|
+
if definitions.key?(decl.name) || imported_declarations.key?(decl.name)
|
|
33
|
+
report_error(errors, "duplicated definition `#{decl.name}`", location: decl.loc)
|
|
34
|
+
end
|
|
19
35
|
definitions[decl.name] = decl
|
|
20
36
|
hints[decl.name] = decl.hints
|
|
21
37
|
end
|
|
22
38
|
|
|
23
|
-
state.with(:declarations, definitions.freeze)
|
|
39
|
+
state.with(:declarations, definitions.freeze)
|
|
40
|
+
.with(:imported_declarations, imported_declarations.freeze)
|
|
41
|
+
.with(:hints, hints)
|
|
24
42
|
end
|
|
25
43
|
end
|
|
26
44
|
end
|
|
@@ -54,6 +54,7 @@ module Kumi
|
|
|
54
54
|
def analyze_expression(expr, errors)
|
|
55
55
|
case expr
|
|
56
56
|
when Kumi::Core::NAST::Call then analyze_call_expression(expr, errors)
|
|
57
|
+
when Kumi::Core::NAST::ImportCall then analyze_import_call(expr, errors)
|
|
57
58
|
when Kumi::Core::NAST::Tuple then analyze_tuple(expr, errors)
|
|
58
59
|
when Kumi::Core::NAST::InputRef then analyze_input_ref(expr)
|
|
59
60
|
when Kumi::Core::NAST::IndexRef then analyze_index_ref(expr, errors)
|
|
@@ -163,6 +164,49 @@ module Kumi
|
|
|
163
164
|
{ type: result_type, scope: result_scope }
|
|
164
165
|
end
|
|
165
166
|
|
|
167
|
+
def analyze_import_call(call, errors)
|
|
168
|
+
# Analyze arguments
|
|
169
|
+
arg_metadata = call.args.map { |arg| analyze_expression(arg, errors) }
|
|
170
|
+
arg_types = arg_metadata.map { |m| m[:type] }
|
|
171
|
+
arg_scopes = arg_metadata.map { |m| m[:scope] }
|
|
172
|
+
|
|
173
|
+
# Get the imported schemas from state
|
|
174
|
+
imported_schemas = get_state(:imported_schemas, required: false) || {}
|
|
175
|
+
import_meta = imported_schemas[call.fn_name]
|
|
176
|
+
|
|
177
|
+
unless import_meta
|
|
178
|
+
report_error(errors, "imported function `#{call.fn_name}` not found", location: call.loc)
|
|
179
|
+
return { type: Types.scalar(:any), scope: [] }
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Get the analyzed state of the source schema
|
|
183
|
+
analyzed_state = import_meta[:analyzed_state]
|
|
184
|
+
src_declaration_table = analyzed_state[:declaration_table] || {}
|
|
185
|
+
|
|
186
|
+
# Look up the imported declaration in the source schema
|
|
187
|
+
src_decl_meta = src_declaration_table[call.fn_name]
|
|
188
|
+
unless src_decl_meta
|
|
189
|
+
report_error(errors, "declaration `#{call.fn_name}` not found in imported schema", location: call.loc)
|
|
190
|
+
return { type: Types.scalar(:any), scope: [] }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
result_type = src_decl_meta[:result_type] || Types.scalar(:any)
|
|
194
|
+
# ImportCall broadcasts over argument scopes (like elementwise functions)
|
|
195
|
+
result_scope = lub_by_prefix(arg_scopes)
|
|
196
|
+
|
|
197
|
+
@metadata_table[node_id(call)] = {
|
|
198
|
+
kind: :import_call,
|
|
199
|
+
result_type: result_type,
|
|
200
|
+
result_scope: result_scope,
|
|
201
|
+
imported_fn: call.fn_name,
|
|
202
|
+
arg_types: arg_types,
|
|
203
|
+
arg_scopes: arg_scopes
|
|
204
|
+
}.freeze
|
|
205
|
+
|
|
206
|
+
debug " ImportCall #{call.fn_name}: -> #{result_type} in #{result_scope.inspect}"
|
|
207
|
+
{ type: result_type, scope: result_scope }
|
|
208
|
+
end
|
|
209
|
+
|
|
166
210
|
def analyze_tuple(node, errors)
|
|
167
211
|
elems = node.args.map { |e| analyze_expression(e, errors) }
|
|
168
212
|
element_types = elems.map { |m| m[:type] }
|
|
@@ -42,6 +42,9 @@ module Kumi
|
|
|
42
42
|
when Kumi::Syntax::DeclarationReference
|
|
43
43
|
NAST::Ref.new(name: node.name, loc: node.loc)
|
|
44
44
|
|
|
45
|
+
when Kumi::Syntax::ImportCall
|
|
46
|
+
normalize_import_call(node, errors)
|
|
47
|
+
|
|
45
48
|
when Kumi::Syntax::CallExpression
|
|
46
49
|
# Special handling section - very clear what's happening
|
|
47
50
|
case node.fn_name
|
|
@@ -158,6 +161,33 @@ module Kumi
|
|
|
158
161
|
NAST::Call.new(fn: :"core.and", args: [left, right], loc: loc)
|
|
159
162
|
end
|
|
160
163
|
end
|
|
164
|
+
|
|
165
|
+
def normalize_import_call(node, errors)
|
|
166
|
+
imported_schemas = get_state(:imported_schemas, required: false) || {}
|
|
167
|
+
|
|
168
|
+
import_meta = imported_schemas[node.fn_name]
|
|
169
|
+
unless import_meta
|
|
170
|
+
add_error(errors, node.loc, "imported function `#{node.fn_name}` not found in imported_schemas")
|
|
171
|
+
return NAST::Const.new(value: nil, loc: node.loc)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Don't inline the source expression. Instead, create an NAST::ImportCall node
|
|
175
|
+
# that represents a call to the compiled schema function.
|
|
176
|
+
# The compiled schema handles its own internal dependencies.
|
|
177
|
+
|
|
178
|
+
# Normalize the arguments (the values being passed)
|
|
179
|
+
args = node.input_mapping.map do |param_name, caller_expr|
|
|
180
|
+
normalize_expr(caller_expr, errors)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
NAST::ImportCall.new(
|
|
184
|
+
fn_name: node.fn_name,
|
|
185
|
+
args: args,
|
|
186
|
+
input_mapping_keys: node.input_mapping.keys,
|
|
187
|
+
source_module: import_meta[:source_module],
|
|
188
|
+
loc: node.loc
|
|
189
|
+
)
|
|
190
|
+
end
|
|
161
191
|
end
|
|
162
192
|
end
|
|
163
193
|
end
|
|
@@ -89,7 +89,11 @@ module Kumi
|
|
|
89
89
|
skip = [:cascade_and] # TODO: - hack
|
|
90
90
|
return if skip.include? fn_name
|
|
91
91
|
|
|
92
|
-
#
|
|
92
|
+
# Skip validation for imported functions - they're pure schema methods
|
|
93
|
+
imported_schemas = state[:imported_schemas] || {}
|
|
94
|
+
return if imported_schemas.key?(fn_name)
|
|
95
|
+
|
|
96
|
+
# Check if it's a built-in function in the registry
|
|
93
97
|
return if @registry.resolve_function(fn_name)
|
|
94
98
|
|
|
95
99
|
report_error(
|
|
@@ -180,6 +180,21 @@ module Kumi
|
|
|
180
180
|
stamp!(out, m[:result_scope], m[:result_type])
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
+
def visit_import_call(n)
|
|
184
|
+
args = n.args.map { _1.accept(self) }
|
|
185
|
+
m = meta_for(n)
|
|
186
|
+
out = n.class.new(
|
|
187
|
+
id: n.id,
|
|
188
|
+
fn_name: n.fn_name,
|
|
189
|
+
args: args,
|
|
190
|
+
input_mapping_keys: n.input_mapping_keys,
|
|
191
|
+
source_module: n.source_module,
|
|
192
|
+
loc: n.loc,
|
|
193
|
+
meta: n.meta.dup
|
|
194
|
+
)
|
|
195
|
+
stamp!(out, m[:result_scope], m[:result_type])
|
|
196
|
+
end
|
|
197
|
+
|
|
183
198
|
# ---------- Helpers ----------
|
|
184
199
|
|
|
185
200
|
def stamp!(node, axes, dtype)
|
data/lib/kumi/core/lir/build.rb
CHANGED
|
@@ -146,6 +146,33 @@ module Kumi
|
|
|
146
146
|
)
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
+
# ImportSchemaCall
|
|
150
|
+
# Params:
|
|
151
|
+
# fn_name: imported function name
|
|
152
|
+
# source_module: the module containing the imported schema
|
|
153
|
+
# args: argument registers (mapped parameter values)
|
|
154
|
+
# input_mapping_keys:keys of input mapping in order of args
|
|
155
|
+
# out_dtype: dtype of the result
|
|
156
|
+
# as: result register
|
|
157
|
+
# location: optional Location
|
|
158
|
+
# Result: produces
|
|
159
|
+
def import_schema_call(fn_name:, source_module:, args:, input_mapping_keys:, out_dtype:, as: nil, ids: nil, location: nil)
|
|
160
|
+
as ||= ids.generate_temp
|
|
161
|
+
Instruction.new(
|
|
162
|
+
opcode: :ImportSchemaCall,
|
|
163
|
+
result_register: as,
|
|
164
|
+
stamp: Stamp.new(dtype: out_dtype),
|
|
165
|
+
inputs: args,
|
|
166
|
+
immediates: [],
|
|
167
|
+
attributes: {
|
|
168
|
+
fn_name: fn_name.to_s,
|
|
169
|
+
source_module: source_module.to_s,
|
|
170
|
+
input_mapping_keys: Array(input_mapping_keys).map(&:to_s)
|
|
171
|
+
},
|
|
172
|
+
location:
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
149
176
|
# Fold
|
|
150
177
|
# Params:
|
|
151
178
|
# function: String function id (e.g., "core.mul")
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kumi
|
|
4
|
+
module Core
|
|
5
|
+
module LIR
|
|
6
|
+
# Peephole provides a small helper for implementing local LIR rewrites.
|
|
7
|
+
# It iterates over an instruction array and yields a Window object that
|
|
8
|
+
# exposes the current instruction alongside a handful of mutation helpers.
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
# Peephole.run(ops) do |window|
|
|
12
|
+
# next unless window.match?(:Constant, :Constant, :KernelCall)
|
|
13
|
+
#
|
|
14
|
+
# last = window.instruction(2)
|
|
15
|
+
# const = Build.constant(
|
|
16
|
+
# value: 3,
|
|
17
|
+
# dtype: last.stamp.dtype,
|
|
18
|
+
# as: last.result_register,
|
|
19
|
+
# ids: ids
|
|
20
|
+
# )
|
|
21
|
+
# window.replace(3, with: const)
|
|
22
|
+
# end
|
|
23
|
+
class Peephole
|
|
24
|
+
attr_reader :ops
|
|
25
|
+
|
|
26
|
+
def self.run(ops, &)
|
|
27
|
+
new(ops).run(&)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(ops)
|
|
31
|
+
@ops = ops
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run
|
|
35
|
+
index = 0
|
|
36
|
+
while index < @ops.length
|
|
37
|
+
window = Window.new(@ops, index)
|
|
38
|
+
yield window
|
|
39
|
+
index = window.next_index
|
|
40
|
+
end
|
|
41
|
+
@ops
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A mutable view over the instruction stream anchored at a given index.
|
|
45
|
+
class Window
|
|
46
|
+
attr_reader :index
|
|
47
|
+
|
|
48
|
+
def initialize(ops, index)
|
|
49
|
+
@ops = ops
|
|
50
|
+
@index = index
|
|
51
|
+
@next_index = index + 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def current = instruction(0)
|
|
55
|
+
|
|
56
|
+
def instruction(offset = 0) = @ops[@index + offset]
|
|
57
|
+
|
|
58
|
+
def opcode(offset = 0)
|
|
59
|
+
instruction(offset)&.opcode
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def instructions(count)
|
|
63
|
+
@ops[@index, count].compact
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def match?(*opcodes)
|
|
67
|
+
opcodes.each_with_index.all? do |opcode, off|
|
|
68
|
+
ins = instruction(off)
|
|
69
|
+
ins&.respond_to?(:opcode) && ins.opcode == opcode
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def result_register(offset = 0)
|
|
74
|
+
instruction(offset)&.result_register
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def const?(offset = 0, value: nil)
|
|
78
|
+
ins = instruction(offset)
|
|
79
|
+
return false unless ins&.opcode == :Constant
|
|
80
|
+
|
|
81
|
+
return true if value.nil?
|
|
82
|
+
|
|
83
|
+
literal_value(offset) == value
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def zero?(offset = 0)
|
|
87
|
+
const?(offset, value: 0)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def literal(offset = 0)
|
|
91
|
+
ins = instruction(offset)
|
|
92
|
+
Array(ins&.immediates).first
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def literal_value(offset = 0)
|
|
96
|
+
literal(offset)&.value
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def replace(count, with:)
|
|
100
|
+
replacements = normalize(with)
|
|
101
|
+
@ops[@index, count] = replacements
|
|
102
|
+
@next_index = @index
|
|
103
|
+
replacements
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def delete(count = 1)
|
|
107
|
+
replace(count, with: [])
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def insert_before(*new_ops)
|
|
111
|
+
new_ops = normalize(new_ops)
|
|
112
|
+
return @next_index = @index if new_ops.empty?
|
|
113
|
+
|
|
114
|
+
@ops.insert(@index, *new_ops)
|
|
115
|
+
@index += new_ops.length
|
|
116
|
+
@next_index = @index
|
|
117
|
+
new_ops
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def insert_after(*new_ops)
|
|
121
|
+
new_ops = normalize(new_ops)
|
|
122
|
+
return @next_index = @index + 1 if new_ops.empty?
|
|
123
|
+
|
|
124
|
+
@ops.insert(@index + 1, *new_ops)
|
|
125
|
+
@next_index = @index + 1
|
|
126
|
+
new_ops
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def skip(count = 1)
|
|
130
|
+
count = 1 if count.nil? || count < 1
|
|
131
|
+
@next_index = @index + count
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def rewind(count = 1)
|
|
136
|
+
count = 1 if count.nil? || count < 1
|
|
137
|
+
@next_index = [@index - count, 0].max
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def next_index
|
|
142
|
+
@next_index = 0 if @next_index.negative?
|
|
143
|
+
@next_index = @ops.length if @next_index > @ops.length
|
|
144
|
+
@next_index
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def size
|
|
148
|
+
@ops.length
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def normalize(value)
|
|
154
|
+
if value.is_a?(Array)
|
|
155
|
+
value.compact
|
|
156
|
+
else
|
|
157
|
+
value ? [value] : []
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
data/lib/kumi/core/nast.rb
CHANGED
|
@@ -101,6 +101,22 @@ module Kumi
|
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
class ImportCall < Node
|
|
105
|
+
attr_reader :fn_name, :args, :input_mapping_keys, :source_module
|
|
106
|
+
|
|
107
|
+
def initialize(fn_name:, args:, input_mapping_keys:, source_module:, **k)
|
|
108
|
+
super(**k)
|
|
109
|
+
@fn_name = fn_name.to_sym
|
|
110
|
+
@args = args
|
|
111
|
+
@input_mapping_keys = input_mapping_keys
|
|
112
|
+
@source_module = source_module
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def accept(visitor)
|
|
116
|
+
visitor.respond_to?(:visit_import_call) ? visitor.visit_import_call(self) : super
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
104
120
|
class Tuple < Node
|
|
105
121
|
attr_reader :args
|
|
106
122
|
|
|
@@ -4,13 +4,15 @@ module Kumi
|
|
|
4
4
|
module Core
|
|
5
5
|
module RubyParser
|
|
6
6
|
class BuildContext
|
|
7
|
-
attr_reader :inputs, :values, :traits
|
|
7
|
+
attr_reader :inputs, :values, :traits, :imports, :imported_names
|
|
8
8
|
attr_accessor :current_location
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
@inputs = []
|
|
12
12
|
@values = []
|
|
13
13
|
@traits = []
|
|
14
|
+
@imports = []
|
|
15
|
+
@imported_names = Set.new
|
|
14
16
|
@input_block_defined = false
|
|
15
17
|
end
|
|
16
18
|
|
|
@@ -8,7 +8,7 @@ module Kumi
|
|
|
8
8
|
include Syntax
|
|
9
9
|
include ErrorReporting
|
|
10
10
|
|
|
11
|
-
DSL_METHODS = %i[value trait input ref literal fn select shift roll].freeze
|
|
11
|
+
DSL_METHODS = %i[value trait input ref literal fn select shift roll import].freeze
|
|
12
12
|
|
|
13
13
|
def initialize(context)
|
|
14
14
|
@context = context
|
|
@@ -57,10 +57,31 @@ module Kumi
|
|
|
57
57
|
Kumi::Syntax::Literal.new(value, loc: @context.current_location)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def import(*names, from:)
|
|
61
|
+
update_location
|
|
62
|
+
|
|
63
|
+
unless from.is_a?(Module) || from.is_a?(Class)
|
|
64
|
+
raise_syntax_error(
|
|
65
|
+
"import 'from:' must reference a module or class",
|
|
66
|
+
location: @context.current_location
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
import_decl = Kumi::Syntax::ImportDeclaration.new(names, from, loc: @context.current_location)
|
|
71
|
+
@context.imports << import_decl
|
|
72
|
+
@context.imported_names.merge(names)
|
|
73
|
+
end
|
|
74
|
+
|
|
60
75
|
def fn(fn_name, *args, **kwargs)
|
|
61
76
|
update_location
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
|
|
78
|
+
if args.empty? && !kwargs.empty? && @context.imported_names.include?(fn_name)
|
|
79
|
+
mapping = build_import_mapping(kwargs)
|
|
80
|
+
Kumi::Syntax::ImportCall.new(fn_name, mapping, loc: @context.current_location)
|
|
81
|
+
else
|
|
82
|
+
expr_args = args.map { ensure_syntax(_1) }
|
|
83
|
+
Kumi::Syntax::CallExpression.new(fn_name, expr_args, kwargs, loc: @context.current_location)
|
|
84
|
+
end
|
|
64
85
|
end
|
|
65
86
|
|
|
66
87
|
def select(condition, value_when_true, value_when_false)
|
|
@@ -97,6 +118,15 @@ module Kumi
|
|
|
97
118
|
|
|
98
119
|
private
|
|
99
120
|
|
|
121
|
+
def build_import_mapping(kwargs)
|
|
122
|
+
converter = ExpressionConverter.new(@context)
|
|
123
|
+
mapping = {}
|
|
124
|
+
kwargs.each do |field_name, expr|
|
|
125
|
+
mapping[field_name] = converter.ensure_syntax(expr)
|
|
126
|
+
end
|
|
127
|
+
mapping
|
|
128
|
+
end
|
|
129
|
+
|
|
100
130
|
def update_location
|
|
101
131
|
# Use caller_locations(2, 1) to skip the DSL method and get the actual user code location
|
|
102
132
|
# Stack: [0] update_location, [1] DSL method (value/trait/etc), [2] user's DSL code
|