kumi 0.0.25 → 0.0.26
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 +9 -0
- data/README.md +70 -71
- data/data/functions/agg/boolean.yaml +6 -2
- data/data/functions/agg/numeric.yaml +32 -16
- data/data/functions/agg/string.yaml +4 -3
- data/data/functions/core/arithmetic.yaml +62 -14
- data/data/functions/core/boolean.yaml +12 -6
- data/data/functions/core/comparison.yaml +25 -13
- data/data/functions/core/constructor.yaml +16 -8
- data/data/functions/core/select.yaml +3 -1
- data/data/functions/core/stencil.yaml +14 -5
- data/data/functions/core/string.yaml +9 -4
- data/data/kernels/ruby/agg/numeric.yaml +1 -1
- data/docs/UNSAT_DETECTION.md +83 -0
- data/golden/array_element/expected/nast.txt +1 -1
- data/golden/array_element/expected/schema_ruby.rb +1 -1
- data/golden/array_index/expected/nast.txt +7 -7
- data/golden/array_index/expected/schema_ruby.rb +1 -1
- data/golden/array_operations/expected/nast.txt +2 -2
- data/golden/array_operations/expected/schema_ruby.rb +1 -1
- data/golden/array_operations/expected/snast.txt +3 -3
- data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
- data/golden/cascade_logic/expected/snast.txt +2 -2
- data/golden/chained_fusion/expected/nast.txt +2 -2
- data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
- data/golden/element_arrays/expected/nast.txt +2 -2
- data/golden/element_arrays/expected/schema_ruby.rb +1 -1
- data/golden/element_arrays/expected/snast.txt +1 -1
- data/golden/empty_and_null_inputs/expected/nast.txt +3 -3
- data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
- data/golden/function_overload/expected/ast.txt +29 -0
- data/golden/function_overload/expected/input_plan.txt +4 -0
- data/golden/function_overload/expected/lir_00_unoptimized.txt +18 -0
- data/golden/function_overload/expected/lir_01_hoist_scalar_references.txt +18 -0
- data/golden/function_overload/expected/lir_02_inlined.txt +20 -0
- data/golden/function_overload/expected/lir_03_cse.txt +20 -0
- data/golden/function_overload/expected/lir_04_1_loop_fusion.txt +20 -0
- data/golden/function_overload/expected/lir_04_loop_invcm.txt +20 -0
- data/golden/function_overload/expected/lir_06_const_prop.txt +20 -0
- data/golden/function_overload/expected/nast.txt +22 -0
- data/golden/function_overload/expected/schema_javascript.mjs +12 -0
- data/golden/function_overload/expected/schema_ruby.rb +39 -0
- data/golden/function_overload/expected/snast.txt +22 -0
- data/golden/function_overload/input.json +8 -0
- data/golden/function_overload/schema.kumi +19 -0
- data/golden/game_of_life/expected/lir_00_unoptimized.txt +4 -4
- data/golden/game_of_life/expected/lir_01_hoist_scalar_references.txt +4 -4
- data/golden/game_of_life/expected/lir_02_inlined.txt +16 -16
- data/golden/game_of_life/expected/lir_03_cse.txt +20 -16
- data/golden/game_of_life/expected/lir_04_1_loop_fusion.txt +20 -16
- data/golden/game_of_life/expected/lir_04_loop_invcm.txt +20 -16
- data/golden/game_of_life/expected/lir_06_const_prop.txt +20 -16
- data/golden/game_of_life/expected/nast.txt +4 -4
- data/golden/game_of_life/expected/schema_javascript.mjs +4 -2
- data/golden/game_of_life/expected/schema_ruby.rb +5 -3
- data/golden/game_of_life/expected/snast.txt +10 -10
- data/golden/hash_keys/expected/schema_ruby.rb +1 -1
- data/golden/hash_value/expected/nast.txt +1 -1
- data/golden/hash_value/expected/schema_ruby.rb +1 -1
- data/golden/hash_value/expected/snast.txt +1 -1
- data/golden/hierarchical_complex/expected/nast.txt +3 -3
- data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
- data/golden/hierarchical_complex/expected/snast.txt +3 -3
- data/golden/inline_rename_scope_leak/expected/nast.txt +3 -3
- data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
- data/golden/input_reference/expected/nast.txt +2 -2
- data/golden/input_reference/expected/schema_ruby.rb +1 -1
- data/golden/interleaved_fusion/expected/nast.txt +2 -2
- data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
- data/golden/let_inline/expected/nast.txt +4 -4
- data/golden/let_inline/expected/schema_ruby.rb +1 -1
- data/golden/loop_fusion/expected/nast.txt +1 -1
- data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
- data/golden/min_reduce_scope/expected/nast.txt +3 -3
- data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
- data/golden/min_reduce_scope/expected/snast.txt +1 -1
- data/golden/mixed_dimensions/expected/nast.txt +2 -2
- data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
- data/golden/multirank_hoisting/expected/nast.txt +7 -7
- data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
- data/golden/nested_hash/expected/nast.txt +1 -1
- data/golden/nested_hash/expected/schema_ruby.rb +1 -1
- data/golden/reduction_broadcast/expected/nast.txt +3 -3
- data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
- data/golden/reduction_broadcast/expected/snast.txt +1 -1
- data/golden/roll/expected/schema_ruby.rb +1 -1
- data/golden/shift/expected/schema_ruby.rb +1 -1
- data/golden/shift_2d/expected/schema_ruby.rb +1 -1
- data/golden/simple_math/expected/lir_00_unoptimized.txt +1 -1
- data/golden/simple_math/expected/lir_01_hoist_scalar_references.txt +1 -1
- data/golden/simple_math/expected/lir_02_inlined.txt +1 -1
- data/golden/simple_math/expected/lir_03_cse.txt +1 -1
- data/golden/simple_math/expected/lir_04_1_loop_fusion.txt +1 -1
- data/golden/simple_math/expected/lir_04_loop_invcm.txt +1 -1
- data/golden/simple_math/expected/lir_06_const_prop.txt +1 -1
- data/golden/simple_math/expected/nast.txt +5 -5
- data/golden/simple_math/expected/schema_ruby.rb +1 -1
- data/golden/simple_math/expected/snast.txt +2 -2
- data/golden/streaming_basics/expected/nast.txt +8 -8
- data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
- data/golden/streaming_basics/expected/snast.txt +1 -1
- data/golden/tuples/expected/lir_00_unoptimized.txt +5 -5
- data/golden/tuples/expected/lir_01_hoist_scalar_references.txt +5 -5
- data/golden/tuples/expected/lir_02_inlined.txt +5 -5
- data/golden/tuples/expected/lir_03_cse.txt +5 -5
- data/golden/tuples/expected/lir_04_1_loop_fusion.txt +5 -5
- data/golden/tuples/expected/lir_04_loop_invcm.txt +5 -5
- data/golden/tuples/expected/lir_06_const_prop.txt +5 -5
- data/golden/tuples/expected/nast.txt +4 -4
- data/golden/tuples/expected/schema_ruby.rb +1 -1
- data/golden/tuples/expected/snast.txt +6 -6
- data/golden/tuples_and_arrays/expected/lir_00_unoptimized.txt +1 -1
- data/golden/tuples_and_arrays/expected/lir_01_hoist_scalar_references.txt +1 -1
- data/golden/tuples_and_arrays/expected/lir_02_inlined.txt +2 -2
- data/golden/tuples_and_arrays/expected/lir_03_cse.txt +2 -2
- data/golden/tuples_and_arrays/expected/lir_04_1_loop_fusion.txt +2 -2
- data/golden/tuples_and_arrays/expected/lir_04_loop_invcm.txt +2 -2
- data/golden/tuples_and_arrays/expected/lir_06_const_prop.txt +2 -2
- data/golden/tuples_and_arrays/expected/nast.txt +3 -3
- data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
- data/golden/tuples_and_arrays/expected/snast.txt +2 -2
- data/golden/us_tax_2024/expected/ast.txt +63 -670
- data/golden/us_tax_2024/expected/input_plan.txt +8 -45
- data/golden/us_tax_2024/expected/lir_00_unoptimized.txt +253 -863
- data/golden/us_tax_2024/expected/lir_01_hoist_scalar_references.txt +253 -863
- data/golden/us_tax_2024/expected/lir_02_inlined.txt +1215 -5139
- data/golden/us_tax_2024/expected/lir_03_cse.txt +587 -2460
- data/golden/us_tax_2024/expected/lir_04_1_loop_fusion.txt +632 -2480
- data/golden/us_tax_2024/expected/lir_04_loop_invcm.txt +587 -2460
- data/golden/us_tax_2024/expected/lir_06_const_prop.txt +587 -2460
- data/golden/us_tax_2024/expected/nast.txt +123 -826
- data/golden/us_tax_2024/expected/schema_javascript.mjs +127 -581
- data/golden/us_tax_2024/expected/schema_ruby.rb +135 -610
- data/golden/us_tax_2024/expected/snast.txt +155 -858
- data/golden/us_tax_2024/expected.json +120 -1
- data/golden/us_tax_2024/input.json +18 -9
- data/golden/us_tax_2024/schema.kumi +48 -178
- data/golden/with_constants/expected/lir_00_unoptimized.txt +1 -1
- data/golden/with_constants/expected/lir_01_hoist_scalar_references.txt +1 -1
- data/golden/with_constants/expected/lir_02_inlined.txt +1 -1
- data/golden/with_constants/expected/lir_03_cse.txt +1 -1
- data/golden/with_constants/expected/lir_04_1_loop_fusion.txt +1 -1
- data/golden/with_constants/expected/lir_04_loop_invcm.txt +1 -1
- data/golden/with_constants/expected/lir_06_const_prop.txt +1 -1
- data/golden/with_constants/expected/nast.txt +2 -2
- data/golden/with_constants/expected/schema_ruby.rb +1 -1
- data/golden/with_constants/expected/snast.txt +2 -2
- data/lib/kumi/analyzer.rb +12 -12
- data/lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb +236 -0
- data/lib/kumi/core/analyzer/passes/input_collector.rb +22 -4
- data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +64 -18
- data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +9 -4
- data/lib/kumi/core/analyzer/passes/snast_pass.rb +3 -1
- data/lib/kumi/core/analyzer/passes/unsat_detector.rb +172 -198
- data/lib/kumi/core/error_reporter.rb +36 -1
- data/lib/kumi/core/errors.rb +33 -1
- data/lib/kumi/core/functions/function_spec.rb +5 -4
- data/lib/kumi/core/functions/loader.rb +17 -1
- data/lib/kumi/core/functions/overload_resolver.rb +164 -0
- data/lib/kumi/core/functions/type_error_reporter.rb +118 -0
- data/lib/kumi/core/functions/type_rules.rb +155 -35
- data/lib/kumi/core/types/inference.rb +29 -22
- data/lib/kumi/core/types/normalizer.rb +29 -45
- data/lib/kumi/core/types/validator.rb +16 -27
- data/lib/kumi/core/types/value_objects.rb +116 -0
- data/lib/kumi/core/types.rb +45 -37
- data/lib/kumi/registry_v2/loader.rb +90 -0
- data/lib/kumi/registry_v2.rb +18 -1
- data/lib/kumi/version.rb +1 -1
- metadata +21 -7
- data/lib/kumi/core/analyzer/unsat_constant_evaluator.rb +0 -59
- data/lib/kumi/core/atom_unsat_solver.rb +0 -396
- data/lib/kumi/core/constraint_relationship_solver.rb +0 -641
- data/lib/kumi/core/types/builder.rb +0 -23
- data/lib/kumi/core/types/compatibility.rb +0 -96
- data/lib/kumi/core/types/formatter.rb +0 -26
@@ -5,38 +5,44 @@ require "date"
|
|
5
5
|
module Kumi
|
6
6
|
module Core
|
7
7
|
module Types
|
8
|
-
# Normalizes different type inputs to canonical
|
8
|
+
# Normalizes different type inputs to canonical Type objects
|
9
9
|
class Normalizer
|
10
|
-
# Type normalization - convert various inputs to
|
10
|
+
# Type normalization - convert various inputs to Type objects
|
11
11
|
def self.normalize(type_input)
|
12
12
|
case type_input
|
13
|
+
when Type
|
14
|
+
# Already a Type object, return as-is
|
15
|
+
type_input
|
13
16
|
when Symbol
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
if Validator.valid_kind?(type_input)
|
18
|
+
Kumi::Core::Types.scalar(type_input)
|
19
|
+
else
|
20
|
+
raise ArgumentError, "Invalid type symbol: #{type_input}"
|
21
|
+
end
|
17
22
|
when String
|
18
23
|
symbol_type = type_input.to_sym
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
if Validator.valid_kind?(symbol_type)
|
25
|
+
Kumi::Core::Types.scalar(symbol_type)
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Invalid type string: #{type_input}"
|
28
|
+
end
|
22
29
|
when Hash
|
23
|
-
|
24
|
-
|
25
|
-
raise ArgumentError, "Invalid type hash: #{type_input}"
|
30
|
+
raise ArgumentError, "Hash-based types no longer supported, use Type objects instead"
|
26
31
|
when Class
|
27
32
|
# Handle Ruby class inputs
|
28
|
-
case type_input.name
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
kind = case type_input.name
|
34
|
+
when "NilClass" then :null
|
35
|
+
when "Integer" then :integer
|
36
|
+
when "String" then :string
|
37
|
+
when "Float" then :float
|
38
|
+
when "Symbol" then :symbol
|
39
|
+
when "TrueClass", "FalseClass" then :boolean
|
40
|
+
when "Array" then raise ArgumentError, "Use array(:type) helper for array types"
|
41
|
+
when "Hash" then raise ArgumentError, "Use scalar(:hash) for hash type"
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Unsupported class type: #{type_input}"
|
44
|
+
end
|
45
|
+
Kumi::Core::Types.scalar(kind)
|
40
46
|
else
|
41
47
|
case type_input
|
42
48
|
when Integer, Float, Numeric
|
@@ -46,28 +52,6 @@ module Kumi
|
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
49
|
-
|
50
|
-
# Legacy compatibility - coerce old constants to symbols
|
51
|
-
def self.coerce(type_input)
|
52
|
-
# Handle legacy constant usage
|
53
|
-
return type_input if type_input.is_a?(Symbol) && Validator.valid_type?(type_input)
|
54
|
-
|
55
|
-
# Handle legacy constant objects
|
56
|
-
case type_input
|
57
|
-
when STRING then :string
|
58
|
-
when INT then :integer
|
59
|
-
when FLOAT, NUMERIC then :float # Both FLOAT and NUMERIC map to :float
|
60
|
-
when BOOL then :boolean
|
61
|
-
when ANY then :any
|
62
|
-
when SYMBOL then :symbol
|
63
|
-
when REGEXP then :regexp
|
64
|
-
when TIME then :time
|
65
|
-
when DATE then :date
|
66
|
-
when DATETIME then :datetime
|
67
|
-
else
|
68
|
-
normalize(type_input)
|
69
|
-
end
|
70
|
-
end
|
71
55
|
end
|
72
56
|
end
|
73
57
|
end
|
@@ -7,36 +7,25 @@ module Kumi
|
|
7
7
|
class Validator
|
8
8
|
VALID_TYPES = %i[string integer float boolean any symbol regexp time date datetime array hash null].freeze
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
return true if array_type?(type)
|
14
|
-
return true if hash_type?(type)
|
15
|
-
|
16
|
-
false
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.array_type?(type)
|
20
|
-
return true if type.is_a?(Hash) && type.keys == [:array] && valid_type?(type[:array])
|
10
|
+
# Validate scalar kinds (no :array or :hash)
|
11
|
+
VALID_KINDS = %i[string integer float boolean any symbol regexp time date datetime null].freeze
|
21
12
|
|
22
|
-
|
23
|
-
|
13
|
+
def self.valid_kind?(kind)
|
14
|
+
VALID_KINDS.include?(kind)
|
24
15
|
end
|
25
16
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
def self.primitive_type?(type)
|
39
|
-
VALID_TYPES.include?(type)
|
17
|
+
def self.valid_type?(type)
|
18
|
+
# Support Type objects
|
19
|
+
case type
|
20
|
+
when ScalarType
|
21
|
+
valid_kind?(type.kind)
|
22
|
+
when ArrayType, TupleType
|
23
|
+
true # If constructed, it's valid
|
24
|
+
when Symbol
|
25
|
+
valid_kind?(type)
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
40
29
|
end
|
41
30
|
end
|
42
31
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module Core
|
5
|
+
module Types
|
6
|
+
# Base class for all type objects
|
7
|
+
class Type
|
8
|
+
def scalar?
|
9
|
+
is_a?(ScalarType)
|
10
|
+
end
|
11
|
+
|
12
|
+
def array?
|
13
|
+
is_a?(ArrayType)
|
14
|
+
end
|
15
|
+
|
16
|
+
def tuple?
|
17
|
+
is_a?(TupleType)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Represents scalar types: string, integer, float, boolean, hash
|
22
|
+
class ScalarType < Type
|
23
|
+
attr_reader :kind
|
24
|
+
|
25
|
+
def initialize(kind)
|
26
|
+
@kind = kind
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
@kind.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"#<ScalarType:#{@kind}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(other)
|
38
|
+
return false unless other.is_a?(ScalarType)
|
39
|
+
@kind == other.kind
|
40
|
+
end
|
41
|
+
|
42
|
+
def eql?(other)
|
43
|
+
self == other
|
44
|
+
end
|
45
|
+
|
46
|
+
def hash
|
47
|
+
[@kind].hash
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Represents array types with an element type
|
52
|
+
class ArrayType < Type
|
53
|
+
attr_reader :element_type
|
54
|
+
|
55
|
+
def initialize(element_type)
|
56
|
+
@element_type = element_type
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
"array<#{@element_type}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"#<ArrayType:#{to_s}>"
|
65
|
+
end
|
66
|
+
|
67
|
+
def ==(other)
|
68
|
+
return false unless other.is_a?(ArrayType)
|
69
|
+
@element_type == other.element_type
|
70
|
+
end
|
71
|
+
|
72
|
+
def eql?(other)
|
73
|
+
self == other
|
74
|
+
end
|
75
|
+
|
76
|
+
def hash
|
77
|
+
[@element_type].hash
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Represents tuple types with a list of element types
|
82
|
+
class TupleType < Type
|
83
|
+
attr_reader :element_types
|
84
|
+
|
85
|
+
def initialize(element_types)
|
86
|
+
@element_types = element_types
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s
|
90
|
+
"tuple<#{@element_types.join(', ')}>"
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"#<TupleType:#{to_s}>"
|
95
|
+
end
|
96
|
+
|
97
|
+
def ==(other)
|
98
|
+
return false unless other.is_a?(TupleType)
|
99
|
+
@element_types == other.element_types
|
100
|
+
end
|
101
|
+
|
102
|
+
def eql?(other)
|
103
|
+
self == other
|
104
|
+
end
|
105
|
+
|
106
|
+
def hash
|
107
|
+
@element_types.hash
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Namespace module for consistency with autoloader
|
112
|
+
module ValueObjects
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/kumi/core/types.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'types/value_objects'
|
4
|
+
|
3
5
|
module Kumi
|
4
6
|
module Core
|
5
7
|
module Types
|
@@ -10,64 +12,70 @@ module Kumi
|
|
10
12
|
tuple?(dtype) || array?(dtype)
|
11
13
|
end
|
12
14
|
|
13
|
-
def self.tuple?(dtype)
|
14
|
-
|
15
|
+
def self.tuple?(dtype)
|
16
|
+
dtype.is_a?(TupleType)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.array?(dtype)
|
20
|
+
dtype.is_a?(ArrayType)
|
21
|
+
end
|
15
22
|
|
16
23
|
# Validation methods
|
17
24
|
def self.valid_type?(type)
|
18
25
|
Validator.valid_type?(type)
|
19
26
|
end
|
20
27
|
|
21
|
-
# Type
|
22
|
-
def self.
|
23
|
-
|
28
|
+
# Type value object constructors
|
29
|
+
def self.scalar(kind)
|
30
|
+
ScalarType.new(kind)
|
24
31
|
end
|
25
32
|
|
26
|
-
def self.
|
27
|
-
|
33
|
+
def self.array(element_type)
|
34
|
+
elem_obj = case element_type
|
35
|
+
when Type
|
36
|
+
element_type
|
37
|
+
when :string, :integer, :float, :boolean, :hash, :any, :symbol, :regexp, :time, :date, :datetime, :null
|
38
|
+
scalar(element_type)
|
39
|
+
else
|
40
|
+
raise ArgumentError,
|
41
|
+
"array element must be Type object or scalar kind, got #{element_type.inspect}"
|
42
|
+
end
|
43
|
+
ArrayType.new(elem_obj)
|
28
44
|
end
|
29
45
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
46
|
+
def self.tuple(element_types)
|
47
|
+
unless element_types.is_a?(Array)
|
48
|
+
raise ArgumentError, "tuple expects array of Type objects, got #{element_types.class}"
|
49
|
+
end
|
34
50
|
|
35
|
-
|
36
|
-
|
51
|
+
# Convert any non-Type elements to Type objects
|
52
|
+
converted = element_types.map do |t|
|
53
|
+
case t
|
54
|
+
when Type
|
55
|
+
t
|
56
|
+
when :string, :integer, :float, :boolean, :hash, :any, :symbol, :regexp, :time, :date, :datetime, :null
|
57
|
+
scalar(t)
|
58
|
+
else
|
59
|
+
raise ArgumentError, "tuple element must be Type or scalar kind, got #{t.inspect}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
TupleType.new(converted)
|
37
64
|
end
|
38
65
|
|
39
|
-
|
40
|
-
|
41
|
-
Compatibility.compatible?(type1, type2)
|
66
|
+
def self.hash(key_type, val_type)
|
67
|
+
raise NotImplementedError, "Use scalar(:hash) instead - Kumi treats hash as scalar, not key/value pair"
|
42
68
|
end
|
43
69
|
|
44
|
-
|
45
|
-
|
70
|
+
# Normalization
|
71
|
+
def self.normalize(type_input)
|
72
|
+
Normalizer.normalize(type_input)
|
46
73
|
end
|
47
74
|
|
48
75
|
# Type inference
|
49
76
|
def self.infer_from_value(value)
|
50
77
|
Inference.infer_from_value(value)
|
51
78
|
end
|
52
|
-
|
53
|
-
# Formatting
|
54
|
-
def self.type_to_s(type)
|
55
|
-
Formatter.type_to_s(type)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Legacy compatibility constants (will be phased out)
|
59
|
-
# These should be replaced with symbols in user code over time
|
60
|
-
STRING = :string
|
61
|
-
INT = :integer # NOTE: using :integer instead of :int for clarity
|
62
|
-
FLOAT = :float
|
63
|
-
BOOL = :boolean
|
64
|
-
ANY = :any
|
65
|
-
SYMBOL = :symbol
|
66
|
-
REGEXP = :regexp
|
67
|
-
TIME = :time
|
68
|
-
DATE = :date
|
69
|
-
DATETIME = :datetime
|
70
|
-
NUMERIC = :float # Legacy: represents numeric compatibility
|
71
79
|
end
|
72
80
|
end
|
73
81
|
end
|
@@ -7,6 +7,96 @@ module Kumi
|
|
7
7
|
module Loader
|
8
8
|
module_function
|
9
9
|
|
10
|
+
# Build dtype rule from YAML specification (structured or legacy string format)
|
11
|
+
def build_dtype_rule_from_yaml(dtype_spec)
|
12
|
+
case dtype_spec
|
13
|
+
when String
|
14
|
+
# Legacy string format: "same_as(x)", "promote(a,b)", "integer", etc.
|
15
|
+
Kumi::Core::Functions::TypeRules.compile_dtype_rule(dtype_spec, [])
|
16
|
+
when Hash
|
17
|
+
# Structured format: { rule: 'same_as', param: 'x' }
|
18
|
+
build_dtype_rule_from_hash(dtype_spec)
|
19
|
+
else
|
20
|
+
raise "Invalid dtype specification: #{dtype_spec.inspect}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Build dtype rule from structured hash
|
25
|
+
def build_dtype_rule_from_hash(spec)
|
26
|
+
rule_type = spec.fetch('rule') { raise "dtype hash requires 'rule' key" }
|
27
|
+
|
28
|
+
case rule_type
|
29
|
+
when 'same_as'
|
30
|
+
param = spec.fetch('param') { raise "same_as rule requires 'param' key" }
|
31
|
+
Kumi::Core::Functions::TypeRules.build_same_as(param.to_sym)
|
32
|
+
|
33
|
+
when 'promote'
|
34
|
+
params = spec.fetch('params') { raise "promote rule requires 'params' key" }
|
35
|
+
param_syms = Array(params).map { |p| p.to_sym }
|
36
|
+
Kumi::Core::Functions::TypeRules.build_promote(*param_syms)
|
37
|
+
|
38
|
+
when 'element_of'
|
39
|
+
param = spec.fetch('param') { raise "element_of rule requires 'param' key" }
|
40
|
+
Kumi::Core::Functions::TypeRules.build_element_of(param.to_sym)
|
41
|
+
|
42
|
+
when 'unify'
|
43
|
+
param1 = spec.fetch('param1') { raise "unify rule requires 'param1' key" }
|
44
|
+
param2 = spec.fetch('param2') { raise "unify rule requires 'param2' key" }
|
45
|
+
Kumi::Core::Functions::TypeRules.build_unify(param1.to_sym, param2.to_sym)
|
46
|
+
|
47
|
+
when 'common_type'
|
48
|
+
param = spec.fetch('param') { raise "common_type rule requires 'param' key" }
|
49
|
+
Kumi::Core::Functions::TypeRules.build_common_type(param.to_sym)
|
50
|
+
|
51
|
+
when 'array'
|
52
|
+
if spec.key?('element_type')
|
53
|
+
element_type_spec = spec['element_type']
|
54
|
+
element_type = if element_type_spec.is_a?(Hash)
|
55
|
+
# Nested structured format
|
56
|
+
build_dtype_rule_from_hash(element_type_spec).call({})
|
57
|
+
else
|
58
|
+
# String or symbol
|
59
|
+
element_type_spec.to_sym
|
60
|
+
end
|
61
|
+
Kumi::Core::Functions::TypeRules.build_array(element_type)
|
62
|
+
elsif spec.key?('element_type_param')
|
63
|
+
element_type_param = spec['element_type_param'].to_sym
|
64
|
+
Kumi::Core::Functions::TypeRules.build_array(element_type_param)
|
65
|
+
else
|
66
|
+
raise "array rule requires either 'element_type' or 'element_type_param' key"
|
67
|
+
end
|
68
|
+
|
69
|
+
when 'tuple'
|
70
|
+
if spec.key?('element_types')
|
71
|
+
element_types_spec = spec['element_types']
|
72
|
+
element_types = Array(element_types_spec).map do |et|
|
73
|
+
if et.is_a?(Hash)
|
74
|
+
build_dtype_rule_from_hash(et).call({})
|
75
|
+
else
|
76
|
+
et.to_sym
|
77
|
+
end
|
78
|
+
end
|
79
|
+
Kumi::Core::Functions::TypeRules.build_tuple(*element_types)
|
80
|
+
elsif spec.key?('element_types_param')
|
81
|
+
element_types_param = spec['element_types_param'].to_sym
|
82
|
+
Kumi::Core::Functions::TypeRules.build_tuple(element_types_param)
|
83
|
+
else
|
84
|
+
raise "tuple rule requires either 'element_types' or 'element_types_param' key"
|
85
|
+
end
|
86
|
+
|
87
|
+
when 'scalar'
|
88
|
+
kind = spec.fetch('kind') { raise "scalar rule requires 'kind' key" }
|
89
|
+
kind_sym = kind.to_sym
|
90
|
+
unless Kumi::Core::Types::Validator.valid_kind?(kind_sym)
|
91
|
+
raise "scalar rule has unknown kind: #{kind}"
|
92
|
+
end
|
93
|
+
Kumi::Core::Functions::TypeRules.build_scalar(kind_sym)
|
94
|
+
|
95
|
+
else
|
96
|
+
raise "unknown dtype rule: #{rule_type}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
10
100
|
# { "core.mul" => Function(id: "core.mul", kind: :elementwise, params: [...]) }
|
11
101
|
def load_functions(dir, func_struct)
|
12
102
|
files = Dir.glob(File.join(dir, "**", "*.y{a,}ml")).sort
|
data/lib/kumi/registry_v2.rb
CHANGED
@@ -22,7 +22,7 @@ module Kumi
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def dtype_rule
|
25
|
-
@dtype_rule ||=
|
25
|
+
@dtype_rule ||= Loader.build_dtype_rule_from_yaml(dtype)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -32,6 +32,7 @@ module Kumi
|
|
32
32
|
def initialize(functions_by_id, kernels_by_key)
|
33
33
|
@functions = functions_by_id # "core.mul" => Function<...>
|
34
34
|
@alias = build_alias(@functions) # "count" => "agg.count"
|
35
|
+
@overload_resolver = Core::Functions::OverloadResolver.new(@functions)
|
35
36
|
@kernels = kernels_by_key # [fn_id, target_sym] => Kernel
|
36
37
|
@by_id = @kernels.values.to_h { |k| [k.id, k] }
|
37
38
|
end
|
@@ -50,6 +51,12 @@ module Kumi
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
54
|
+
# Type-aware function resolution for overloads
|
55
|
+
# Returns the function_id that best matches the given argument types
|
56
|
+
def resolve_function_with_types(alias_or_id, arg_types)
|
57
|
+
@overload_resolver.resolve(alias_or_id, arg_types)
|
58
|
+
end
|
59
|
+
|
53
60
|
def function_kind(id) = function(id).kind
|
54
61
|
def function_reduce?(id) = function(id).reduce?
|
55
62
|
def function_elementwise?(id) = function(id).elementwise?
|
@@ -104,6 +111,16 @@ module Kumi
|
|
104
111
|
func.aliases.each { |al| acc[al] = func.id }
|
105
112
|
end
|
106
113
|
end
|
114
|
+
|
115
|
+
def build_alias_overloads(functions)
|
116
|
+
# Maps each alias to an array of all function_ids that have that alias
|
117
|
+
functions.values.each_with_object({}) do |func, acc|
|
118
|
+
func.aliases.each do |al|
|
119
|
+
acc[al] ||= []
|
120
|
+
acc[al] << func.id
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
107
124
|
end
|
108
125
|
|
109
126
|
module_function
|
data/lib/kumi/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kumi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.26
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- André Muta
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- docs/FORM_SCHEMA.md
|
84
84
|
- docs/OUTPUT_SCHEMA.md
|
85
85
|
- docs/SYNTAX.md
|
86
|
+
- docs/UNSAT_DETECTION.md
|
86
87
|
- examples/deep_schema_compilation_and_evaluation_benchmark.rb
|
87
88
|
- examples/federal_tax_calculator_2024.rb
|
88
89
|
- examples/game_of_life.rb
|
@@ -201,6 +202,21 @@ files:
|
|
201
202
|
- golden/empty_and_null_inputs/expected/snast.txt
|
202
203
|
- golden/empty_and_null_inputs/input.json
|
203
204
|
- golden/empty_and_null_inputs/schema.kumi
|
205
|
+
- golden/function_overload/expected/ast.txt
|
206
|
+
- golden/function_overload/expected/input_plan.txt
|
207
|
+
- golden/function_overload/expected/lir_00_unoptimized.txt
|
208
|
+
- golden/function_overload/expected/lir_01_hoist_scalar_references.txt
|
209
|
+
- golden/function_overload/expected/lir_02_inlined.txt
|
210
|
+
- golden/function_overload/expected/lir_03_cse.txt
|
211
|
+
- golden/function_overload/expected/lir_04_1_loop_fusion.txt
|
212
|
+
- golden/function_overload/expected/lir_04_loop_invcm.txt
|
213
|
+
- golden/function_overload/expected/lir_06_const_prop.txt
|
214
|
+
- golden/function_overload/expected/nast.txt
|
215
|
+
- golden/function_overload/expected/schema_javascript.mjs
|
216
|
+
- golden/function_overload/expected/schema_ruby.rb
|
217
|
+
- golden/function_overload/expected/snast.txt
|
218
|
+
- golden/function_overload/input.json
|
219
|
+
- golden/function_overload/schema.kumi
|
204
220
|
- golden/game_of_life/expected.json
|
205
221
|
- golden/game_of_life/expected/ast.txt
|
206
222
|
- golden/game_of_life/expected/input_plan.txt
|
@@ -594,6 +610,7 @@ files:
|
|
594
610
|
- lib/kumi/core/analyzer/passes/contract_checker_pass.rb
|
595
611
|
- lib/kumi/core/analyzer/passes/declaration_validator.rb
|
596
612
|
- lib/kumi/core/analyzer/passes/dependency_resolver.rb
|
613
|
+
- lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb
|
597
614
|
- lib/kumi/core/analyzer/passes/input_access_planner_pass.rb
|
598
615
|
- lib/kumi/core/analyzer/passes/input_collector.rb
|
599
616
|
- lib/kumi/core/analyzer/passes/input_form_schema_pass.rb
|
@@ -629,8 +646,6 @@ files:
|
|
629
646
|
- lib/kumi/core/analyzer/state_serde.rb
|
630
647
|
- lib/kumi/core/analyzer/structs/access_plan.rb
|
631
648
|
- lib/kumi/core/analyzer/structs/input_meta.rb
|
632
|
-
- lib/kumi/core/analyzer/unsat_constant_evaluator.rb
|
633
|
-
- lib/kumi/core/atom_unsat_solver.rb
|
634
649
|
- lib/kumi/core/compiler/access_builder.rb
|
635
650
|
- lib/kumi/core/compiler/access_codegen.rb
|
636
651
|
- lib/kumi/core/compiler/access_emit/base.rb
|
@@ -645,7 +660,6 @@ files:
|
|
645
660
|
- lib/kumi/core/compiler/accessors/materialize_accessor.rb
|
646
661
|
- lib/kumi/core/compiler/accessors/ravel_accessor.rb
|
647
662
|
- lib/kumi/core/compiler/accessors/read_accessor.rb
|
648
|
-
- lib/kumi/core/constraint_relationship_solver.rb
|
649
663
|
- lib/kumi/core/domain/enum_analyzer.rb
|
650
664
|
- lib/kumi/core/domain/range_analyzer.rb
|
651
665
|
- lib/kumi/core/domain/validator.rb
|
@@ -662,6 +676,8 @@ files:
|
|
662
676
|
- lib/kumi/core/export/serializer.rb
|
663
677
|
- lib/kumi/core/functions/function_spec.rb
|
664
678
|
- lib/kumi/core/functions/loader.rb
|
679
|
+
- lib/kumi/core/functions/overload_resolver.rb
|
680
|
+
- lib/kumi/core/functions/type_error_reporter.rb
|
665
681
|
- lib/kumi/core/functions/type_rules.rb
|
666
682
|
- lib/kumi/core/input/type_matcher.rb
|
667
683
|
- lib/kumi/core/input/validator.rb
|
@@ -705,12 +721,10 @@ files:
|
|
705
721
|
- lib/kumi/core/ruby_parser/schema_builder.rb
|
706
722
|
- lib/kumi/core/ruby_parser/sugar.rb
|
707
723
|
- lib/kumi/core/types.rb
|
708
|
-
- lib/kumi/core/types/builder.rb
|
709
|
-
- lib/kumi/core/types/compatibility.rb
|
710
|
-
- lib/kumi/core/types/formatter.rb
|
711
724
|
- lib/kumi/core/types/inference.rb
|
712
725
|
- lib/kumi/core/types/normalizer.rb
|
713
726
|
- lib/kumi/core/types/validator.rb
|
727
|
+
- lib/kumi/core/types/value_objects.rb
|
714
728
|
- lib/kumi/dev.rb
|
715
729
|
- lib/kumi/dev/codegen.rb
|
716
730
|
- lib/kumi/dev/golden.rb
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module Core
|
5
|
-
module Analyzer
|
6
|
-
class UnsatConstantEvaluator
|
7
|
-
include Syntax
|
8
|
-
|
9
|
-
def initialize(definitions)
|
10
|
-
@definitions = definitions
|
11
|
-
@memo = {}
|
12
|
-
end
|
13
|
-
|
14
|
-
OPERATORS = {
|
15
|
-
add: :+,
|
16
|
-
subtract: :-,
|
17
|
-
multiply: :*,
|
18
|
-
divide: :/
|
19
|
-
}.freeze
|
20
|
-
|
21
|
-
def evaluate(node, visited = Set.new)
|
22
|
-
return :unknown unless node
|
23
|
-
return @memo[node] if @memo.key?(node)
|
24
|
-
return node.value if node.is_a?(Literal)
|
25
|
-
|
26
|
-
result = case node
|
27
|
-
when DeclarationReference then evaluate_binding(node, visited)
|
28
|
-
when CallExpression then evaluate_call_expression(node, visited)
|
29
|
-
else :unknown
|
30
|
-
end
|
31
|
-
|
32
|
-
@memo[node] = result unless result == :unknown
|
33
|
-
result
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def evaluate_binding(node, visited)
|
39
|
-
return :unknown if visited.include?(node.name)
|
40
|
-
|
41
|
-
visited << node.name
|
42
|
-
definition = @definitions[node.name]
|
43
|
-
return :unknown unless definition
|
44
|
-
|
45
|
-
evaluate(definition.expression, visited)
|
46
|
-
end
|
47
|
-
|
48
|
-
def evaluate_call_expression(node, visited)
|
49
|
-
return :unknown unless OPERATORS.key?(node.fn_name)
|
50
|
-
|
51
|
-
args = node.args.map { |arg| evaluate(arg, visited) }
|
52
|
-
return :unknown if args.any?(:unknown)
|
53
|
-
|
54
|
-
args.reduce(OPERATORS[node.fn_name])
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|