kumi 0.0.5 → 0.0.7

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +76 -174
  3. data/README.md +205 -52
  4. data/{documents → docs}/AST.md +29 -29
  5. data/{documents → docs}/SYNTAX.md +95 -8
  6. data/docs/features/README.md +45 -0
  7. data/docs/features/analysis-cascade-mutual-exclusion.md +89 -0
  8. data/docs/features/analysis-type-inference.md +42 -0
  9. data/docs/features/analysis-unsat-detection.md +71 -0
  10. data/docs/features/array-broadcasting.md +170 -0
  11. data/docs/features/input-declaration-system.md +42 -0
  12. data/docs/features/performance.md +16 -0
  13. data/docs/schema_metadata/broadcasts.md +53 -0
  14. data/docs/schema_metadata/cascades.md +45 -0
  15. data/docs/schema_metadata/declarations.md +54 -0
  16. data/docs/schema_metadata/dependencies.md +57 -0
  17. data/docs/schema_metadata/evaluation_order.md +29 -0
  18. data/docs/schema_metadata/examples.md +95 -0
  19. data/docs/schema_metadata/inferred_types.md +46 -0
  20. data/docs/schema_metadata/inputs.md +86 -0
  21. data/docs/schema_metadata.md +108 -0
  22. data/examples/federal_tax_calculator_2024.rb +11 -6
  23. data/lib/kumi/analyzer/constant_evaluator.rb +1 -1
  24. data/lib/kumi/analyzer/passes/broadcast_detector.rb +246 -0
  25. data/lib/kumi/analyzer/passes/{definition_validator.rb → declaration_validator.rb} +4 -4
  26. data/lib/kumi/analyzer/passes/dependency_resolver.rb +78 -38
  27. data/lib/kumi/analyzer/passes/input_collector.rb +91 -30
  28. data/lib/kumi/analyzer/passes/name_indexer.rb +2 -2
  29. data/lib/kumi/analyzer/passes/pass_base.rb +1 -1
  30. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +24 -25
  31. data/lib/kumi/analyzer/passes/toposorter.rb +44 -8
  32. data/lib/kumi/analyzer/passes/type_checker.rb +34 -14
  33. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +2 -2
  34. data/lib/kumi/analyzer/passes/type_inferencer.rb +130 -21
  35. data/lib/kumi/analyzer/passes/unsat_detector.rb +134 -56
  36. data/lib/kumi/analyzer/passes/visitor_pass.rb +2 -2
  37. data/lib/kumi/analyzer.rb +16 -17
  38. data/lib/kumi/compiler.rb +188 -16
  39. data/lib/kumi/constraint_relationship_solver.rb +6 -6
  40. data/lib/kumi/domain/validator.rb +0 -4
  41. data/lib/kumi/error_reporting.rb +1 -1
  42. data/lib/kumi/explain.rb +32 -20
  43. data/lib/kumi/export/node_registry.rb +26 -12
  44. data/lib/kumi/export/node_serializers.rb +1 -1
  45. data/lib/kumi/function_registry/collection_functions.rb +14 -9
  46. data/lib/kumi/function_registry/function_builder.rb +4 -3
  47. data/lib/kumi/function_registry.rb +8 -2
  48. data/lib/kumi/input/type_matcher.rb +3 -0
  49. data/lib/kumi/input/validator.rb +0 -3
  50. data/lib/kumi/json_schema/generator.rb +63 -0
  51. data/lib/kumi/json_schema/validator.rb +25 -0
  52. data/lib/kumi/json_schema.rb +14 -0
  53. data/lib/kumi/{parser → ruby_parser}/build_context.rb +1 -1
  54. data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +36 -0
  55. data/lib/kumi/{parser → ruby_parser}/dsl.rb +1 -1
  56. data/lib/kumi/{parser → ruby_parser}/dsl_cascade_builder.rb +5 -5
  57. data/lib/kumi/{parser → ruby_parser}/expression_converter.rb +20 -20
  58. data/lib/kumi/{parser → ruby_parser}/guard_rails.rb +1 -1
  59. data/lib/kumi/{parser → ruby_parser}/input_builder.rb +41 -10
  60. data/lib/kumi/ruby_parser/input_field_proxy.rb +46 -0
  61. data/lib/kumi/{parser → ruby_parser}/input_proxy.rb +4 -4
  62. data/lib/kumi/ruby_parser/nested_input.rb +15 -0
  63. data/lib/kumi/{parser → ruby_parser}/parser.rb +11 -10
  64. data/lib/kumi/{parser → ruby_parser}/schema_builder.rb +11 -10
  65. data/lib/kumi/{parser → ruby_parser}/sugar.rb +62 -10
  66. data/lib/kumi/ruby_parser.rb +10 -0
  67. data/lib/kumi/schema.rb +10 -4
  68. data/lib/kumi/schema_instance.rb +6 -6
  69. data/lib/kumi/schema_metadata.rb +524 -0
  70. data/lib/kumi/syntax/array_expression.rb +15 -0
  71. data/lib/kumi/syntax/call_expression.rb +11 -0
  72. data/lib/kumi/syntax/cascade_expression.rb +11 -0
  73. data/lib/kumi/syntax/case_expression.rb +11 -0
  74. data/lib/kumi/syntax/declaration_reference.rb +11 -0
  75. data/lib/kumi/syntax/hash_expression.rb +11 -0
  76. data/lib/kumi/syntax/input_declaration.rb +12 -0
  77. data/lib/kumi/syntax/input_element_reference.rb +12 -0
  78. data/lib/kumi/syntax/input_reference.rb +12 -0
  79. data/lib/kumi/syntax/literal.rb +11 -0
  80. data/lib/kumi/syntax/trait_declaration.rb +11 -0
  81. data/lib/kumi/syntax/value_declaration.rb +11 -0
  82. data/lib/kumi/vectorization_metadata.rb +108 -0
  83. data/lib/kumi/version.rb +1 -1
  84. data/lib/kumi.rb +14 -0
  85. metadata +55 -25
  86. data/lib/generators/trait_engine/templates/schema_spec.rb.erb +0 -27
  87. data/lib/kumi/domain.rb +0 -8
  88. data/lib/kumi/input.rb +0 -8
  89. data/lib/kumi/syntax/declarations.rb +0 -26
  90. data/lib/kumi/syntax/expressions.rb +0 -34
  91. data/lib/kumi/syntax/terminal_expressions.rb +0 -30
  92. data/lib/kumi/syntax.rb +0 -9
  93. /data/{documents → docs}/DSL.md +0 -0
  94. /data/{documents → docs}/FUNCTIONS.md +0 -0
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  module Sugar
6
6
  include Syntax
7
7
 
@@ -13,7 +13,7 @@ module Kumi
13
13
  COMPARISON_OPS = %i[< <= > >= == !=].freeze
14
14
 
15
15
  LITERAL_TYPES = [
16
- Integer, String, Symbol, TrueClass, FalseClass, Float, Regexp
16
+ Integer, String, Symbol, TrueClass, FalseClass, Float, Regexp, NilClass
17
17
  ].freeze
18
18
 
19
19
  # Collection methods that can be applied to arrays/syntax nodes
@@ -23,11 +23,11 @@ module Kumi
23
23
  ].freeze
24
24
 
25
25
  def self.ensure_literal(obj)
26
- return Literal.new(obj) if LITERAL_TYPES.any? { |type| obj.is_a?(type) }
26
+ return Kumi::Syntax::Literal.new(obj) if LITERAL_TYPES.any? { |type| obj.is_a?(type) }
27
27
  return obj if obj.is_a?(Syntax::Node)
28
28
  return obj.to_ast_node if obj.respond_to?(:to_ast_node)
29
29
 
30
- Literal.new(obj)
30
+ Kumi::Syntax::Literal.new(obj)
31
31
  end
32
32
 
33
33
  def self.syntax_expression?(obj)
@@ -36,7 +36,7 @@ module Kumi
36
36
 
37
37
  # Create a call expression with consistent error handling
38
38
  def self.create_call_expression(fn_name, args)
39
- Syntax::CallExpression.new(fn_name, args)
39
+ Kumi::Syntax::CallExpression.new(fn_name, args)
40
40
  end
41
41
 
42
42
  module ExpressionRefinement
@@ -100,7 +100,7 @@ module Kumi
100
100
  define_method(op) do |other|
101
101
  if Sugar.syntax_expression?(other)
102
102
  other_node = Sugar.ensure_literal(other)
103
- Sugar.create_call_expression(op_name, [Syntax::Literal.new(self), other_node])
103
+ Sugar.create_call_expression(op_name, [Kumi::Syntax::Literal.new(self), other_node])
104
104
  else
105
105
  super(other)
106
106
  end
@@ -112,7 +112,7 @@ module Kumi
112
112
  define_method(op) do |other|
113
113
  if Sugar.syntax_expression?(other)
114
114
  other_node = Sugar.ensure_literal(other)
115
- Sugar.create_call_expression(op, [Syntax::Literal.new(self), other_node])
115
+ Sugar.create_call_expression(op, [Kumi::Syntax::Literal.new(self), other_node])
116
116
  else
117
117
  super(other)
118
118
  end
@@ -127,7 +127,7 @@ module Kumi
127
127
  def +(other)
128
128
  if Sugar.syntax_expression?(other)
129
129
  other_node = Sugar.ensure_literal(other)
130
- Sugar.create_call_expression(:concat, [Syntax::Literal.new(self), other_node])
130
+ Sugar.create_call_expression(:concat, [Kumi::Syntax::Literal.new(self), other_node])
131
131
  else
132
132
  super
133
133
  end
@@ -137,7 +137,7 @@ module Kumi
137
137
  define_method(op) do |other|
138
138
  if Sugar.syntax_expression?(other)
139
139
  other_node = Sugar.ensure_literal(other)
140
- Sugar.create_call_expression(op, [Syntax::Literal.new(self), other_node])
140
+ Sugar.create_call_expression(op, [Kumi::Syntax::Literal.new(self), other_node])
141
141
  else
142
142
  super(other)
143
143
  end
@@ -156,7 +156,7 @@ module Kumi
156
156
  # Convert array to syntax list expression with all elements as syntax nodes
157
157
  def to_syntax_list
158
158
  syntax_elements = map { |item| Sugar.ensure_literal(item) }
159
- Syntax::ListExpression.new(syntax_elements)
159
+ Kumi::Syntax::ArrayExpression.new(syntax_elements)
160
160
  end
161
161
 
162
162
  # Create array method that works with syntax expressions
@@ -204,6 +204,58 @@ module Kumi
204
204
  end
205
205
  end
206
206
  end
207
+
208
+ # Shared refinement for proxy objects that need to handle operators
209
+ # Both DeclarationReferenceProxy and InputFieldProxy can use this
210
+ module ProxyRefinement
211
+ def self.extended(proxy_class)
212
+ # Add operator methods directly to the proxy class
213
+ proxy_class.class_eval do
214
+ # Arithmetic operations
215
+ ARITHMETIC_OPS.each do |op, op_name|
216
+ define_method(op) do |other|
217
+ ast_node = to_ast_node
218
+ other_node = Sugar.ensure_literal(other)
219
+ Sugar.create_call_expression(op_name, [ast_node, other_node])
220
+ end
221
+ end
222
+
223
+ # Comparison operations (including == and != that don't work with refinements)
224
+ COMPARISON_OPS.each do |op|
225
+ define_method(op) do |other|
226
+ ast_node = to_ast_node
227
+ other_node = Sugar.ensure_literal(other)
228
+ Sugar.create_call_expression(op, [ast_node, other_node])
229
+ end
230
+ end
231
+
232
+ # Logical operations
233
+ define_method(:&) do |other|
234
+ ast_node = to_ast_node
235
+ other_node = Sugar.ensure_literal(other)
236
+ Sugar.create_call_expression(:and, [ast_node, other_node])
237
+ end
238
+
239
+ define_method(:|) do |other|
240
+ ast_node = to_ast_node
241
+ other_node = Sugar.ensure_literal(other)
242
+ Sugar.create_call_expression(:or, [ast_node, other_node])
243
+ end
244
+
245
+ # Array access
246
+ define_method(:[]) do |index|
247
+ ast_node = to_ast_node
248
+ Sugar.create_call_expression(:at, [ast_node, Sugar.ensure_literal(index)])
249
+ end
250
+
251
+ # Unary minus
252
+ define_method(:-@) do
253
+ ast_node = to_ast_node
254
+ Sugar.create_call_expression(:subtract, [Sugar.ensure_literal(0), ast_node])
255
+ end
256
+ end
257
+ end
258
+ end
207
259
  end
208
260
  end
209
261
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ # Ruby DSL parser for Kumi schemas
5
+ # Converts Ruby block syntax into AST nodes
6
+ module RubyParser
7
+ # This module contains all Ruby DSL parsing functionality
8
+ # The main entry point is through Dsl.build_syntax_tree
9
+ end
10
+ end
data/lib/kumi/schema.rb CHANGED
@@ -16,19 +16,19 @@ module Kumi
16
16
  raise("No schema defined") unless @__compiled_schema__
17
17
 
18
18
  # Validate input types and domain constraints
19
- input_meta = @__analyzer_result__.state[:input_meta] || {}
19
+ input_meta = @__analyzer_result__.state[:inputs] || {}
20
20
  violations = Input::Validator.validate_context(context, input_meta)
21
21
 
22
22
  raise Errors::InputValidationError, violations unless violations.empty?
23
23
 
24
- SchemaInstance.new(@__compiled_schema__, @__analyzer_result__, context)
24
+ SchemaInstance.new(@__compiled_schema__, @__analyzer_result__.state, context)
25
25
  end
26
26
 
27
27
  def explain(context, *keys)
28
28
  raise("No schema defined") unless @__compiled_schema__
29
29
 
30
30
  # Validate input types and domain constraints
31
- input_meta = @__analyzer_result__.state[:input_meta] || {}
31
+ input_meta = @__analyzer_result__.state[:inputs] || {}
32
32
  violations = Input::Validator.validate_context(context, input_meta)
33
33
 
34
34
  raise Errors::InputValidationError, violations unless violations.empty?
@@ -41,11 +41,17 @@ module Kumi
41
41
  end
42
42
 
43
43
  def schema(&block)
44
- @__syntax_tree__ = Kumi::Parser::Dsl.build_syntax_tree(&block).freeze
44
+ @__syntax_tree__ = Kumi::RubyParser::Dsl.build_syntax_tree(&block).freeze
45
45
  @__analyzer_result__ = Analyzer.analyze!(@__syntax_tree__).freeze
46
46
  @__compiled_schema__ = Compiler.compile(@__syntax_tree__, analyzer: @__analyzer_result__).freeze
47
47
 
48
48
  Inspector.new(@__syntax_tree__, @__analyzer_result__, @__compiled_schema__)
49
49
  end
50
+
51
+ def schema_metadata
52
+ raise("No schema defined") unless @__analyzer_result__
53
+
54
+ @schema_metadata ||= SchemaMetadata.new(@__analyzer_result__.state, @__syntax_tree__)
55
+ end
50
56
  end
51
57
  end
@@ -11,11 +11,11 @@ module Kumi
11
11
  # instance.input # original context (read‑only)
12
12
 
13
13
  class SchemaInstance
14
- attr_reader :compiled_schema, :analysis, :context
14
+ attr_reader :compiled_schema, :metadata, :context
15
15
 
16
- def initialize(compiled_schema, analysis, context)
16
+ def initialize(compiled_schema, metadata, context)
17
17
  @compiled_schema = compiled_schema # Kumi::CompiledSchema
18
- @analysis = analysis # Analyzer result (for deps)
18
+ @metadata = metadata # Frozen state hash
19
19
  @context = context.is_a?(EvaluationWrapper) ? context : EvaluationWrapper.new(context)
20
20
  end
21
21
 
@@ -68,12 +68,12 @@ module Kumi
68
68
 
69
69
  def input_field_exists?(field)
70
70
  # Check if field is declared in input block
71
- input_meta = @analysis&.state&.dig(:input_meta) || {}
71
+ input_meta = @metadata[:inputs] || {}
72
72
  input_meta.key?(field) || @context.key?(field)
73
73
  end
74
74
 
75
75
  def validate_domain_constraint(field, value)
76
- input_meta = @analysis&.state&.dig(:input_meta) || {}
76
+ input_meta = @metadata[:inputs] || {}
77
77
  field_meta = input_meta[field]
78
78
  return unless field_meta&.dig(:domain)
79
79
 
@@ -99,7 +99,7 @@ module Kumi
99
99
 
100
100
  def find_dependent_declarations_optimized(field)
101
101
  # Use precomputed transitive closure for true O(1) lookup!
102
- transitive_dependents = @analysis&.state&.dig(:transitive_dependents)
102
+ transitive_dependents = @metadata[:dependents]
103
103
  return [] unless transitive_dependents
104
104
 
105
105
  # This is truly O(1) - just array lookup, no traversal needed