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.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +70 -71
  4. data/data/functions/agg/boolean.yaml +6 -2
  5. data/data/functions/agg/numeric.yaml +32 -16
  6. data/data/functions/agg/string.yaml +4 -3
  7. data/data/functions/core/arithmetic.yaml +62 -14
  8. data/data/functions/core/boolean.yaml +12 -6
  9. data/data/functions/core/comparison.yaml +25 -13
  10. data/data/functions/core/constructor.yaml +16 -8
  11. data/data/functions/core/select.yaml +3 -1
  12. data/data/functions/core/stencil.yaml +14 -5
  13. data/data/functions/core/string.yaml +9 -4
  14. data/data/kernels/ruby/agg/numeric.yaml +1 -1
  15. data/docs/UNSAT_DETECTION.md +83 -0
  16. data/golden/array_element/expected/nast.txt +1 -1
  17. data/golden/array_element/expected/schema_ruby.rb +1 -1
  18. data/golden/array_index/expected/nast.txt +7 -7
  19. data/golden/array_index/expected/schema_ruby.rb +1 -1
  20. data/golden/array_operations/expected/nast.txt +2 -2
  21. data/golden/array_operations/expected/schema_ruby.rb +1 -1
  22. data/golden/array_operations/expected/snast.txt +3 -3
  23. data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
  24. data/golden/cascade_logic/expected/snast.txt +2 -2
  25. data/golden/chained_fusion/expected/nast.txt +2 -2
  26. data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
  27. data/golden/element_arrays/expected/nast.txt +2 -2
  28. data/golden/element_arrays/expected/schema_ruby.rb +1 -1
  29. data/golden/element_arrays/expected/snast.txt +1 -1
  30. data/golden/empty_and_null_inputs/expected/nast.txt +3 -3
  31. data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
  32. data/golden/function_overload/expected/ast.txt +29 -0
  33. data/golden/function_overload/expected/input_plan.txt +4 -0
  34. data/golden/function_overload/expected/lir_00_unoptimized.txt +18 -0
  35. data/golden/function_overload/expected/lir_01_hoist_scalar_references.txt +18 -0
  36. data/golden/function_overload/expected/lir_02_inlined.txt +20 -0
  37. data/golden/function_overload/expected/lir_03_cse.txt +20 -0
  38. data/golden/function_overload/expected/lir_04_1_loop_fusion.txt +20 -0
  39. data/golden/function_overload/expected/lir_04_loop_invcm.txt +20 -0
  40. data/golden/function_overload/expected/lir_06_const_prop.txt +20 -0
  41. data/golden/function_overload/expected/nast.txt +22 -0
  42. data/golden/function_overload/expected/schema_javascript.mjs +12 -0
  43. data/golden/function_overload/expected/schema_ruby.rb +39 -0
  44. data/golden/function_overload/expected/snast.txt +22 -0
  45. data/golden/function_overload/input.json +8 -0
  46. data/golden/function_overload/schema.kumi +19 -0
  47. data/golden/game_of_life/expected/lir_00_unoptimized.txt +4 -4
  48. data/golden/game_of_life/expected/lir_01_hoist_scalar_references.txt +4 -4
  49. data/golden/game_of_life/expected/lir_02_inlined.txt +16 -16
  50. data/golden/game_of_life/expected/lir_03_cse.txt +20 -16
  51. data/golden/game_of_life/expected/lir_04_1_loop_fusion.txt +20 -16
  52. data/golden/game_of_life/expected/lir_04_loop_invcm.txt +20 -16
  53. data/golden/game_of_life/expected/lir_06_const_prop.txt +20 -16
  54. data/golden/game_of_life/expected/nast.txt +4 -4
  55. data/golden/game_of_life/expected/schema_javascript.mjs +4 -2
  56. data/golden/game_of_life/expected/schema_ruby.rb +5 -3
  57. data/golden/game_of_life/expected/snast.txt +10 -10
  58. data/golden/hash_keys/expected/schema_ruby.rb +1 -1
  59. data/golden/hash_value/expected/nast.txt +1 -1
  60. data/golden/hash_value/expected/schema_ruby.rb +1 -1
  61. data/golden/hash_value/expected/snast.txt +1 -1
  62. data/golden/hierarchical_complex/expected/nast.txt +3 -3
  63. data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
  64. data/golden/hierarchical_complex/expected/snast.txt +3 -3
  65. data/golden/inline_rename_scope_leak/expected/nast.txt +3 -3
  66. data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
  67. data/golden/input_reference/expected/nast.txt +2 -2
  68. data/golden/input_reference/expected/schema_ruby.rb +1 -1
  69. data/golden/interleaved_fusion/expected/nast.txt +2 -2
  70. data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
  71. data/golden/let_inline/expected/nast.txt +4 -4
  72. data/golden/let_inline/expected/schema_ruby.rb +1 -1
  73. data/golden/loop_fusion/expected/nast.txt +1 -1
  74. data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
  75. data/golden/min_reduce_scope/expected/nast.txt +3 -3
  76. data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
  77. data/golden/min_reduce_scope/expected/snast.txt +1 -1
  78. data/golden/mixed_dimensions/expected/nast.txt +2 -2
  79. data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
  80. data/golden/multirank_hoisting/expected/nast.txt +7 -7
  81. data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
  82. data/golden/nested_hash/expected/nast.txt +1 -1
  83. data/golden/nested_hash/expected/schema_ruby.rb +1 -1
  84. data/golden/reduction_broadcast/expected/nast.txt +3 -3
  85. data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
  86. data/golden/reduction_broadcast/expected/snast.txt +1 -1
  87. data/golden/roll/expected/schema_ruby.rb +1 -1
  88. data/golden/shift/expected/schema_ruby.rb +1 -1
  89. data/golden/shift_2d/expected/schema_ruby.rb +1 -1
  90. data/golden/simple_math/expected/lir_00_unoptimized.txt +1 -1
  91. data/golden/simple_math/expected/lir_01_hoist_scalar_references.txt +1 -1
  92. data/golden/simple_math/expected/lir_02_inlined.txt +1 -1
  93. data/golden/simple_math/expected/lir_03_cse.txt +1 -1
  94. data/golden/simple_math/expected/lir_04_1_loop_fusion.txt +1 -1
  95. data/golden/simple_math/expected/lir_04_loop_invcm.txt +1 -1
  96. data/golden/simple_math/expected/lir_06_const_prop.txt +1 -1
  97. data/golden/simple_math/expected/nast.txt +5 -5
  98. data/golden/simple_math/expected/schema_ruby.rb +1 -1
  99. data/golden/simple_math/expected/snast.txt +2 -2
  100. data/golden/streaming_basics/expected/nast.txt +8 -8
  101. data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
  102. data/golden/streaming_basics/expected/snast.txt +1 -1
  103. data/golden/tuples/expected/lir_00_unoptimized.txt +5 -5
  104. data/golden/tuples/expected/lir_01_hoist_scalar_references.txt +5 -5
  105. data/golden/tuples/expected/lir_02_inlined.txt +5 -5
  106. data/golden/tuples/expected/lir_03_cse.txt +5 -5
  107. data/golden/tuples/expected/lir_04_1_loop_fusion.txt +5 -5
  108. data/golden/tuples/expected/lir_04_loop_invcm.txt +5 -5
  109. data/golden/tuples/expected/lir_06_const_prop.txt +5 -5
  110. data/golden/tuples/expected/nast.txt +4 -4
  111. data/golden/tuples/expected/schema_ruby.rb +1 -1
  112. data/golden/tuples/expected/snast.txt +6 -6
  113. data/golden/tuples_and_arrays/expected/lir_00_unoptimized.txt +1 -1
  114. data/golden/tuples_and_arrays/expected/lir_01_hoist_scalar_references.txt +1 -1
  115. data/golden/tuples_and_arrays/expected/lir_02_inlined.txt +2 -2
  116. data/golden/tuples_and_arrays/expected/lir_03_cse.txt +2 -2
  117. data/golden/tuples_and_arrays/expected/lir_04_1_loop_fusion.txt +2 -2
  118. data/golden/tuples_and_arrays/expected/lir_04_loop_invcm.txt +2 -2
  119. data/golden/tuples_and_arrays/expected/lir_06_const_prop.txt +2 -2
  120. data/golden/tuples_and_arrays/expected/nast.txt +3 -3
  121. data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
  122. data/golden/tuples_and_arrays/expected/snast.txt +2 -2
  123. data/golden/us_tax_2024/expected/ast.txt +63 -670
  124. data/golden/us_tax_2024/expected/input_plan.txt +8 -45
  125. data/golden/us_tax_2024/expected/lir_00_unoptimized.txt +253 -863
  126. data/golden/us_tax_2024/expected/lir_01_hoist_scalar_references.txt +253 -863
  127. data/golden/us_tax_2024/expected/lir_02_inlined.txt +1215 -5139
  128. data/golden/us_tax_2024/expected/lir_03_cse.txt +587 -2460
  129. data/golden/us_tax_2024/expected/lir_04_1_loop_fusion.txt +632 -2480
  130. data/golden/us_tax_2024/expected/lir_04_loop_invcm.txt +587 -2460
  131. data/golden/us_tax_2024/expected/lir_06_const_prop.txt +587 -2460
  132. data/golden/us_tax_2024/expected/nast.txt +123 -826
  133. data/golden/us_tax_2024/expected/schema_javascript.mjs +127 -581
  134. data/golden/us_tax_2024/expected/schema_ruby.rb +135 -610
  135. data/golden/us_tax_2024/expected/snast.txt +155 -858
  136. data/golden/us_tax_2024/expected.json +120 -1
  137. data/golden/us_tax_2024/input.json +18 -9
  138. data/golden/us_tax_2024/schema.kumi +48 -178
  139. data/golden/with_constants/expected/lir_00_unoptimized.txt +1 -1
  140. data/golden/with_constants/expected/lir_01_hoist_scalar_references.txt +1 -1
  141. data/golden/with_constants/expected/lir_02_inlined.txt +1 -1
  142. data/golden/with_constants/expected/lir_03_cse.txt +1 -1
  143. data/golden/with_constants/expected/lir_04_1_loop_fusion.txt +1 -1
  144. data/golden/with_constants/expected/lir_04_loop_invcm.txt +1 -1
  145. data/golden/with_constants/expected/lir_06_const_prop.txt +1 -1
  146. data/golden/with_constants/expected/nast.txt +2 -2
  147. data/golden/with_constants/expected/schema_ruby.rb +1 -1
  148. data/golden/with_constants/expected/snast.txt +2 -2
  149. data/lib/kumi/analyzer.rb +12 -12
  150. data/lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb +236 -0
  151. data/lib/kumi/core/analyzer/passes/input_collector.rb +22 -4
  152. data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +64 -18
  153. data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +9 -4
  154. data/lib/kumi/core/analyzer/passes/snast_pass.rb +3 -1
  155. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +172 -198
  156. data/lib/kumi/core/error_reporter.rb +36 -1
  157. data/lib/kumi/core/errors.rb +33 -1
  158. data/lib/kumi/core/functions/function_spec.rb +5 -4
  159. data/lib/kumi/core/functions/loader.rb +17 -1
  160. data/lib/kumi/core/functions/overload_resolver.rb +164 -0
  161. data/lib/kumi/core/functions/type_error_reporter.rb +118 -0
  162. data/lib/kumi/core/functions/type_rules.rb +155 -35
  163. data/lib/kumi/core/types/inference.rb +29 -22
  164. data/lib/kumi/core/types/normalizer.rb +29 -45
  165. data/lib/kumi/core/types/validator.rb +16 -27
  166. data/lib/kumi/core/types/value_objects.rb +116 -0
  167. data/lib/kumi/core/types.rb +45 -37
  168. data/lib/kumi/registry_v2/loader.rb +90 -0
  169. data/lib/kumi/registry_v2.rb +18 -1
  170. data/lib/kumi/version.rb +1 -1
  171. metadata +21 -7
  172. data/lib/kumi/core/analyzer/unsat_constant_evaluator.rb +0 -59
  173. data/lib/kumi/core/atom_unsat_solver.rb +0 -396
  174. data/lib/kumi/core/constraint_relationship_solver.rb +0 -641
  175. data/lib/kumi/core/types/builder.rb +0 -23
  176. data/lib/kumi/core/types/compatibility.rb +0 -96
  177. 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 forms
8
+ # Normalizes different type inputs to canonical Type objects
9
9
  class Normalizer
10
- # Type normalization - convert various inputs to canonical type symbols
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
- return type_input if Validator.valid_type?(type_input)
15
-
16
- raise ArgumentError, "Invalid type symbol: #{type_input}"
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
- return symbol_type if Validator.valid_type?(symbol_type)
20
-
21
- raise ArgumentError, "Invalid type string: #{type_input}"
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
- return type_input if Validator.valid_type?(type_input)
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
- when "NilClass" then :null
30
- when "Integer" then :integer
31
- when "String" then :string
32
- when "Float" then :float
33
- when "Symbol" then :symbol
34
- when "TrueClass", "FalseClass" then :boolean
35
- when "Array" then raise ArgumentError, "Use array(:type) helper for array types"
36
- when "Hash" then raise ArgumentError, "Use hash(:key_type, :value_type) helper for hash types"
37
- else
38
- raise ArgumentError, "Unsupported class type: #{type_input}"
39
- end
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
- def self.valid_type?(type)
11
- return true if !type.is_a?(Hash) && VALID_TYPES.include?(type.to_s.to_sym)
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
- type = type.to_s
23
- type.is_a?(String) && type.match?(/^array<(.+)>$/) && type.scan(/(\w+)/)[1..-1].flatten.all? { |t| valid_type?(t) }
13
+ def self.valid_kind?(kind)
14
+ VALID_KINDS.include?(kind)
24
15
  end
25
16
 
26
- def item_types(type)
27
- end
28
-
29
- def self.hash_type?(type)
30
- type.is_a?(Hash) &&
31
- type.keys.sort == [:hash] &&
32
- type[:hash].is_a?(Array) &&
33
- type[:hash].size == 2 &&
34
- valid_type?(type[:hash][0]) &&
35
- valid_type?(type[:hash][1])
36
- end
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
@@ -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) = dtype == :tuple || dtype.match?(/^tuple</)
14
- def self.array?(dtype) = dtype == :array || dtype.match?(/^array</)
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 builders
22
- def self.array(elem_type)
23
- Builder.array(elem_type)
28
+ # Type value object constructors
29
+ def self.scalar(kind)
30
+ ScalarType.new(kind)
24
31
  end
25
32
 
26
- def self.hash(key_type, val_type)
27
- Builder.hash(key_type, val_type)
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
- # Normalization
31
- def self.normalize(type_input)
32
- Normalizer.normalize(type_input)
33
- end
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
- def self.coerce(type_input)
36
- Normalizer.coerce(type_input)
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
- # Compatibility and unification
40
- def self.compatible?(type1, type2)
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
- def self.unify(type1, type2)
45
- Compatibility.unify(type1, type2)
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
@@ -22,7 +22,7 @@ module Kumi
22
22
  end
23
23
 
24
24
  def dtype_rule
25
- @dtype_rule ||= Core::Functions::TypeRules.compile_dtype_rule(dtype, param_names)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- VERSION = "0.0.25"
4
+ VERSION = "0.0.26"
5
5
  end
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.25
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