kumi 0.0.7 → 0.0.9

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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +1 -1
  3. data/README.md +21 -5
  4. data/docs/AST.md +7 -0
  5. data/docs/features/README.md +7 -0
  6. data/docs/features/s-expression-printer.md +77 -0
  7. data/examples/game_of_life.rb +1 -1
  8. data/examples/static_analysis_errors.rb +7 -7
  9. data/lib/kumi/analyzer.rb +15 -15
  10. data/lib/kumi/compiler.rb +6 -6
  11. data/lib/kumi/core/analyzer/analysis_state.rb +39 -0
  12. data/lib/kumi/core/analyzer/constant_evaluator.rb +59 -0
  13. data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +248 -0
  14. data/lib/kumi/core/analyzer/passes/declaration_validator.rb +45 -0
  15. data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +153 -0
  16. data/lib/kumi/core/analyzer/passes/input_collector.rb +139 -0
  17. data/lib/kumi/core/analyzer/passes/name_indexer.rb +26 -0
  18. data/lib/kumi/core/analyzer/passes/pass_base.rb +52 -0
  19. data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +111 -0
  20. data/lib/kumi/core/analyzer/passes/toposorter.rb +110 -0
  21. data/lib/kumi/core/analyzer/passes/type_checker.rb +162 -0
  22. data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +48 -0
  23. data/lib/kumi/core/analyzer/passes/type_inferencer.rb +236 -0
  24. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +406 -0
  25. data/lib/kumi/core/analyzer/passes/visitor_pass.rb +44 -0
  26. data/lib/kumi/core/atom_unsat_solver.rb +396 -0
  27. data/lib/kumi/core/compiled_schema.rb +43 -0
  28. data/lib/kumi/core/constraint_relationship_solver.rb +641 -0
  29. data/lib/kumi/core/domain/enum_analyzer.rb +55 -0
  30. data/lib/kumi/core/domain/range_analyzer.rb +85 -0
  31. data/lib/kumi/core/domain/validator.rb +82 -0
  32. data/lib/kumi/core/domain/violation_formatter.rb +42 -0
  33. data/lib/kumi/core/error_reporter.rb +166 -0
  34. data/lib/kumi/core/error_reporting.rb +97 -0
  35. data/lib/kumi/core/errors.rb +120 -0
  36. data/lib/kumi/core/evaluation_wrapper.rb +40 -0
  37. data/lib/kumi/core/explain.rb +295 -0
  38. data/lib/kumi/core/export/deserializer.rb +41 -0
  39. data/lib/kumi/core/export/errors.rb +14 -0
  40. data/lib/kumi/core/export/node_builders.rb +142 -0
  41. data/lib/kumi/core/export/node_registry.rb +54 -0
  42. data/lib/kumi/core/export/node_serializers.rb +158 -0
  43. data/lib/kumi/core/export/serializer.rb +25 -0
  44. data/lib/kumi/core/export.rb +35 -0
  45. data/lib/kumi/core/function_registry/collection_functions.rb +202 -0
  46. data/lib/kumi/core/function_registry/comparison_functions.rb +33 -0
  47. data/lib/kumi/core/function_registry/conditional_functions.rb +38 -0
  48. data/lib/kumi/core/function_registry/function_builder.rb +95 -0
  49. data/lib/kumi/core/function_registry/logical_functions.rb +44 -0
  50. data/lib/kumi/core/function_registry/math_functions.rb +74 -0
  51. data/lib/kumi/core/function_registry/string_functions.rb +57 -0
  52. data/lib/kumi/core/function_registry/type_functions.rb +53 -0
  53. data/lib/kumi/{function_registry.rb → core/function_registry.rb} +28 -36
  54. data/lib/kumi/core/input/type_matcher.rb +97 -0
  55. data/lib/kumi/core/input/validator.rb +51 -0
  56. data/lib/kumi/core/input/violation_creator.rb +52 -0
  57. data/lib/kumi/core/json_schema/generator.rb +65 -0
  58. data/lib/kumi/core/json_schema/validator.rb +27 -0
  59. data/lib/kumi/core/json_schema.rb +16 -0
  60. data/lib/kumi/core/ruby_parser/build_context.rb +27 -0
  61. data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +38 -0
  62. data/lib/kumi/core/ruby_parser/dsl.rb +14 -0
  63. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +138 -0
  64. data/lib/kumi/core/ruby_parser/expression_converter.rb +128 -0
  65. data/lib/kumi/core/ruby_parser/guard_rails.rb +45 -0
  66. data/lib/kumi/core/ruby_parser/input_builder.rb +127 -0
  67. data/lib/kumi/core/ruby_parser/input_field_proxy.rb +48 -0
  68. data/lib/kumi/core/ruby_parser/input_proxy.rb +31 -0
  69. data/lib/kumi/core/ruby_parser/nested_input.rb +17 -0
  70. data/lib/kumi/core/ruby_parser/parser.rb +71 -0
  71. data/lib/kumi/core/ruby_parser/schema_builder.rb +175 -0
  72. data/lib/kumi/core/ruby_parser/sugar.rb +263 -0
  73. data/lib/kumi/core/ruby_parser.rb +12 -0
  74. data/lib/kumi/core/schema_instance.rb +111 -0
  75. data/lib/kumi/core/types/builder.rb +23 -0
  76. data/lib/kumi/core/types/compatibility.rb +96 -0
  77. data/lib/kumi/core/types/formatter.rb +26 -0
  78. data/lib/kumi/core/types/inference.rb +42 -0
  79. data/lib/kumi/core/types/normalizer.rb +72 -0
  80. data/lib/kumi/core/types/validator.rb +37 -0
  81. data/lib/kumi/core/types.rb +66 -0
  82. data/lib/kumi/core/vectorization_metadata.rb +110 -0
  83. data/lib/kumi/errors.rb +1 -112
  84. data/lib/kumi/registry.rb +37 -0
  85. data/lib/kumi/schema.rb +5 -5
  86. data/lib/kumi/schema_metadata.rb +3 -3
  87. data/lib/kumi/support/s_expression_printer.rb +161 -0
  88. data/lib/kumi/syntax/array_expression.rb +6 -6
  89. data/lib/kumi/syntax/call_expression.rb +4 -4
  90. data/lib/kumi/syntax/cascade_expression.rb +4 -4
  91. data/lib/kumi/syntax/case_expression.rb +4 -4
  92. data/lib/kumi/syntax/declaration_reference.rb +4 -4
  93. data/lib/kumi/syntax/hash_expression.rb +4 -4
  94. data/lib/kumi/syntax/input_declaration.rb +5 -5
  95. data/lib/kumi/syntax/input_element_reference.rb +5 -5
  96. data/lib/kumi/syntax/input_reference.rb +5 -5
  97. data/lib/kumi/syntax/literal.rb +4 -4
  98. data/lib/kumi/syntax/node.rb +34 -34
  99. data/lib/kumi/syntax/root.rb +6 -6
  100. data/lib/kumi/syntax/trait_declaration.rb +4 -4
  101. data/lib/kumi/syntax/value_declaration.rb +4 -4
  102. data/lib/kumi/version.rb +1 -1
  103. data/migrate_to_core_iterative.rb +938 -0
  104. data/scripts/generate_function_docs.rb +9 -9
  105. metadata +77 -72
  106. data/lib/kumi/analyzer/analysis_state.rb +0 -37
  107. data/lib/kumi/analyzer/constant_evaluator.rb +0 -57
  108. data/lib/kumi/analyzer/passes/broadcast_detector.rb +0 -246
  109. data/lib/kumi/analyzer/passes/declaration_validator.rb +0 -43
  110. data/lib/kumi/analyzer/passes/dependency_resolver.rb +0 -151
  111. data/lib/kumi/analyzer/passes/input_collector.rb +0 -137
  112. data/lib/kumi/analyzer/passes/name_indexer.rb +0 -24
  113. data/lib/kumi/analyzer/passes/pass_base.rb +0 -50
  114. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +0 -109
  115. data/lib/kumi/analyzer/passes/toposorter.rb +0 -108
  116. data/lib/kumi/analyzer/passes/type_checker.rb +0 -160
  117. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +0 -46
  118. data/lib/kumi/analyzer/passes/type_inferencer.rb +0 -232
  119. data/lib/kumi/analyzer/passes/unsat_detector.rb +0 -404
  120. data/lib/kumi/analyzer/passes/visitor_pass.rb +0 -42
  121. data/lib/kumi/atom_unsat_solver.rb +0 -394
  122. data/lib/kumi/compiled_schema.rb +0 -41
  123. data/lib/kumi/constraint_relationship_solver.rb +0 -638
  124. data/lib/kumi/domain/enum_analyzer.rb +0 -53
  125. data/lib/kumi/domain/range_analyzer.rb +0 -83
  126. data/lib/kumi/domain/validator.rb +0 -80
  127. data/lib/kumi/domain/violation_formatter.rb +0 -40
  128. data/lib/kumi/error_reporter.rb +0 -164
  129. data/lib/kumi/error_reporting.rb +0 -95
  130. data/lib/kumi/evaluation_wrapper.rb +0 -38
  131. data/lib/kumi/explain.rb +0 -293
  132. data/lib/kumi/export/deserializer.rb +0 -39
  133. data/lib/kumi/export/errors.rb +0 -12
  134. data/lib/kumi/export/node_builders.rb +0 -140
  135. data/lib/kumi/export/node_registry.rb +0 -52
  136. data/lib/kumi/export/node_serializers.rb +0 -156
  137. data/lib/kumi/export/serializer.rb +0 -23
  138. data/lib/kumi/export.rb +0 -33
  139. data/lib/kumi/function_registry/collection_functions.rb +0 -200
  140. data/lib/kumi/function_registry/comparison_functions.rb +0 -31
  141. data/lib/kumi/function_registry/conditional_functions.rb +0 -36
  142. data/lib/kumi/function_registry/function_builder.rb +0 -93
  143. data/lib/kumi/function_registry/logical_functions.rb +0 -42
  144. data/lib/kumi/function_registry/math_functions.rb +0 -72
  145. data/lib/kumi/function_registry/string_functions.rb +0 -54
  146. data/lib/kumi/function_registry/type_functions.rb +0 -51
  147. data/lib/kumi/input/type_matcher.rb +0 -95
  148. data/lib/kumi/input/validator.rb +0 -49
  149. data/lib/kumi/input/violation_creator.rb +0 -50
  150. data/lib/kumi/json_schema/generator.rb +0 -63
  151. data/lib/kumi/json_schema/validator.rb +0 -25
  152. data/lib/kumi/json_schema.rb +0 -14
  153. data/lib/kumi/ruby_parser/build_context.rb +0 -25
  154. data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +0 -36
  155. data/lib/kumi/ruby_parser/dsl.rb +0 -12
  156. data/lib/kumi/ruby_parser/dsl_cascade_builder.rb +0 -136
  157. data/lib/kumi/ruby_parser/expression_converter.rb +0 -126
  158. data/lib/kumi/ruby_parser/guard_rails.rb +0 -43
  159. data/lib/kumi/ruby_parser/input_builder.rb +0 -125
  160. data/lib/kumi/ruby_parser/input_field_proxy.rb +0 -46
  161. data/lib/kumi/ruby_parser/input_proxy.rb +0 -29
  162. data/lib/kumi/ruby_parser/nested_input.rb +0 -15
  163. data/lib/kumi/ruby_parser/parser.rb +0 -69
  164. data/lib/kumi/ruby_parser/schema_builder.rb +0 -173
  165. data/lib/kumi/ruby_parser/sugar.rb +0 -261
  166. data/lib/kumi/ruby_parser.rb +0 -10
  167. data/lib/kumi/schema_instance.rb +0 -109
  168. data/lib/kumi/types/builder.rb +0 -21
  169. data/lib/kumi/types/compatibility.rb +0 -94
  170. data/lib/kumi/types/formatter.rb +0 -24
  171. data/lib/kumi/types/inference.rb +0 -40
  172. data/lib/kumi/types/normalizer.rb +0 -70
  173. data/lib/kumi/types/validator.rb +0 -35
  174. data/lib/kumi/types.rb +0 -64
  175. data/lib/kumi/vectorization_metadata.rb +0 -108
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec2c56684edac64e9818bbf85de98e9855c3d50df80c490c8f868b3e9b701dab
4
- data.tar.gz: d5246f98e10b0365b47f6a67fed5c3a4cbc77d5ad15905d93b046251965135f5
3
+ metadata.gz: c5f9f087001a482c906f041da8cb229511966a11c56e3ced99930b59d4141543
4
+ data.tar.gz: fe5fafe03a5ca1e414b5bc3af9eccd830553aa8f44762e9d9b38e589fa342fd1
5
5
  SHA512:
6
- metadata.gz: f71ba6867b72145247b5c1ea4c3b197f831996bf078798c6a9decdb4e047f83859e062e6a6411e2311d279f8a8cc03ca7e16ac194c9a27b84f8a55b67faa3fcb
7
- data.tar.gz: 70c4dab6bf036da2d507f89c2d8e9d65a24da2685412d3e517a2b3c1696d200c294c512786c85bfccf9feca301e90e70a26999f5ed1528221331b55f77175c8d
6
+ metadata.gz: b26279c08a9387616104252d24147c920f27b0f66574cde1987024325c5fd1ba87aff876b39fdc3d4f472d9aba2c713b19a5105890b66ac3ef10a3018a0b1bca
7
+ data.tar.gz: d4c8db755880cd7088fd4b6087d2f38e86a4fdee11f867d58a480e143494ca2b85440abd9249b8c02a469a9ad21642c287d5ef8f30948bdc6d67695a895c1c13
data/CLAUDE.md CHANGED
@@ -62,7 +62,7 @@ Kumi is a Declarative logic and rules engine framework with static analysis for
62
62
  **Compiler** (`lib/kumi/compiler.rb`):
63
63
  - Transforms analyzed syntax tree into executable lambda functions
64
64
  - Maps each expression type to a compilation method
65
- - Handles function calls via `FunctionRegistry`
65
+ - Handles function calls via `Kumi::Registry`
66
66
  - Produces `CompiledSchema` with executable bindings
67
67
 
68
68
  **Function Registry** (`lib/kumi/function_registry.rb`):
data/README.md CHANGED
@@ -8,7 +8,6 @@ Kumi is a Declarative logic and rules engine framework with static analysis for
8
8
  It is well-suited for scenarios with complex, interdependent calculations, enforcing validation and consistency across your business rules while maintaining performance.
9
9
 
10
10
 
11
-
12
11
  ## What can you build?
13
12
 
14
13
  Calculate U.S. federal taxes:
@@ -118,16 +117,14 @@ value :monthly_payment, fn(:pmt, rate: 0.05/12, nper: 36, pv: -loan_amount)
118
117
  ```
119
118
  Note: You can find a list all core functions in [docs/FUNCTIONS.md](docs/FUNCTIONS.md)
120
119
 
121
- These primitives are statically analyzed during schema definition to catch logical errors before runtime.
122
-
123
120
  </details>
124
121
 
125
122
  <details>
126
- <summary><strong>🔍 Static Analysis</strong> - Catch business logic errors at definition time</summary>
123
+ <summary><strong>🔍 Static Analysis</strong> - Catch errors at definition time and provides rich metadata</summary>
127
124
 
128
125
  ### Static Analysis
129
126
 
130
- Kumi catches business logic errors that cause runtime failures or silent bugs:
127
+ Kumi catches many types of business logic errors that cause runtime failures or silent bugs:
131
128
 
132
129
  ```ruby
133
130
  module InsurancePolicyPricer
@@ -328,6 +325,19 @@ Kumi::Explain.call(FederalTax2024, :fed_tax, inputs: {income: 100_000, filing_st
328
325
  # = 15,099.50
329
326
  ```
330
327
 
328
+ **Debug AST Structure**: Visualize the parsed schema as S-expressions:
329
+
330
+ ```ruby
331
+ puts Kumi::Support::SExpressionPrinter.print(FederalTax2024.__syntax_tree__)
332
+ # => (Root
333
+ # inputs: [
334
+ # (InputDeclaration :income :float)
335
+ # (InputDeclaration :filing_status :string domain: ["single", "married_joint"])
336
+ # ]
337
+ # traits: [...]
338
+ # attributes: [...])
339
+ ```
340
+
331
341
  </details>
332
342
 
333
343
  <details>
@@ -361,6 +371,12 @@ The SchemaMetadata interface provides both processed metadata for tool developme
361
371
 
362
372
  </details>
363
373
 
374
+ ## Beyond Rules: What the Metadata Unlocks
375
+ * **Auto-generated forms** – compile schema → field spec → React form
376
+ * **Scenario explorer** – derive all trait combinations, Monte Carlo outcomes
377
+ * **Coverage dashboard** – flag branches never hit in prod
378
+ * **Schema diff** – highlight behaviour changes across versions
379
+
364
380
  ## Usage
365
381
 
366
382
  **Suitable for:**
data/docs/AST.md CHANGED
@@ -39,6 +39,13 @@ InputReference = Struct.new(:name)
39
39
  # Has operator methods: >=, <=, >, <, ==, != that create CallExpression nodes
40
40
  ```
41
41
 
42
+ **InputElementReference**: Access of nested input fields (`input.field_name.element.subelement.subsubelement`)
43
+ ```ruby
44
+ InputElementReference = Struct.new(:path)
45
+ # Represents nested input access
46
+ # DSL: input.address.street → InputElementReference([:address, :street])
47
+ ```
48
+
42
49
  **DeclarationReference**: References to other declarations
43
50
  ```ruby
44
51
  DeclarationReference = Struct.new(:name)
@@ -37,6 +37,13 @@ Processes large schemas with optimized algorithms.
37
37
  - Result caching
38
38
  - Selective evaluation
39
39
 
40
+ ### [S-Expression Printer](s-expression-printer.md)
41
+ Debug and inspect AST structures with readable S-expression notation output.
42
+
43
+ - Visitor pattern implementation for all node types
44
+ - Proper indentation and hierarchical structure
45
+ - Useful for debugging schema parsing and AST analysis
46
+
40
47
  ## Integration
41
48
 
42
49
  - Type inference uses input declarations
@@ -0,0 +1,77 @@
1
+ # S-Expression Printer
2
+
3
+ Debug and inspect Kumi AST structures with readable S-expression notation output.
4
+
5
+ ## Overview
6
+
7
+ The S-Expression Printer provides a clean, structured way to visualize Kumi's Abstract Syntax Tree (AST) nodes in traditional Lisp-style S-expression format. This is particularly useful for debugging schema parsing, understanding AST structure, and analyzing complex expressions.
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ require 'kumi/support/s_expression_printer'
13
+
14
+ # Print any AST node
15
+ Kumi::Support::SExpressionPrinter.print(node)
16
+
17
+ # Print a complete schema AST
18
+ module MySchema
19
+ extend Kumi::Schema
20
+
21
+ schema do
22
+ input do
23
+ integer :age
24
+ string :name
25
+ end
26
+
27
+ trait :adult, (input.age >= 18)
28
+ value :greeting, fn(:concat, "Hello ", input.name)
29
+ end
30
+ end
31
+
32
+ puts Kumi::Support::SExpressionPrinter.print(MySchema.__syntax_tree__)
33
+ ```
34
+
35
+ ## Output Format
36
+
37
+ The printer produces indented S-expressions that clearly show the hierarchical structure:
38
+
39
+ ```lisp
40
+ (Root
41
+ inputs: [
42
+ (InputDeclaration :age :integer)
43
+ (InputDeclaration :name :string)
44
+ ]
45
+ attributes: [
46
+ (ValueDeclaration :greeting
47
+ (CallExpression :concat
48
+ (Literal "Hello ")
49
+ (InputReference :name)
50
+ )
51
+ )
52
+ ]
53
+ traits: [
54
+ (TraitDeclaration :adult
55
+ (CallExpression :>=
56
+ (InputReference :age)
57
+ (Literal 18)
58
+ )
59
+ )
60
+ ]
61
+ )
62
+ ```
63
+
64
+ ## Supported Node Types
65
+
66
+ The printer handles all Kumi AST node types:
67
+
68
+ - **Root** - Schema container with inputs, attributes, and traits
69
+ - **Declarations** - InputDeclaration, ValueDeclaration, TraitDeclaration
70
+ - **Expressions** - CallExpression, ArrayExpression, CascadeExpression, CaseExpression
71
+ - **References** - InputReference, InputElementReference, DeclarationReference
72
+ - **Literals** - Literal values (strings, numbers, booleans)
73
+ - **Collections** - Arrays and HashExpression nodes
74
+
75
+ ## Implementation
76
+
77
+ Built as a visitor pattern class that traverses AST nodes recursively, with each node type having its own specialized formatting method. The printer preserves proper indentation and handles nested structures gracefully.
@@ -13,7 +13,7 @@ begin
13
13
  cells[neighbor_index]
14
14
  end.sum
15
15
  end
16
- Kumi::FunctionRegistry.register_with_metadata(:neighbor_cells_sum, method(:neighbor_cells_sum_method),
16
+ Kumi::Core::FunctionRegistry.register_with_metadata(:neighbor_cells_sum, method(:neighbor_cells_sum_method),
17
17
  return_type: :integer, arity: 5,
18
18
  param_types: %i[array integer integer integer integer],
19
19
  description: "Get neighbor cells for Conway's Game of Life")
@@ -22,7 +22,7 @@ begin
22
22
  value :yearly_rate, monthly_rate * 12
23
23
  end
24
24
  end
25
- rescue Kumi::Errors::SemanticError => e
25
+ rescue Kumi::Core::Errors::SemanticError => e
26
26
  puts " → #{e.message}"
27
27
  end
28
28
 
@@ -48,7 +48,7 @@ begin
48
48
  end
49
49
  end
50
50
  end
51
- rescue Kumi::Errors::SemanticError => e
51
+ rescue Kumi::Core::Errors::SemanticError => e
52
52
  puts " → #{e.message}"
53
53
  end
54
54
 
@@ -71,7 +71,7 @@ begin
71
71
  value :invalid_sum, input.name + input.age
72
72
  end
73
73
  end
74
- rescue Kumi::Errors::TypeError => e
74
+ rescue Kumi::Core::Errors::TypeError => e
75
75
  puts " → #{e.message}"
76
76
  end
77
77
 
@@ -94,7 +94,7 @@ begin
94
94
  trait :impossible_score, input.score == 150
95
95
  end
96
96
  end
97
- rescue Kumi::Errors::SemanticError => e
97
+ rescue Kumi::Core::Errors::SemanticError => e
98
98
  puts " → #{e.message}"
99
99
  end
100
100
 
@@ -114,7 +114,7 @@ begin
114
114
  value :result, ref(:nonexistent_trait) ? 100 : 0
115
115
  end
116
116
  end
117
- rescue Kumi::Errors::SemanticError => e
117
+ rescue Kumi::Core::Errors::SemanticError => e
118
118
  puts " → #{e.message}"
119
119
  end
120
120
 
@@ -134,7 +134,7 @@ begin
134
134
  value :result, fn(:nonexistent_function, input.text)
135
135
  end
136
136
  end
137
- rescue Kumi::Errors::TypeError => e
137
+ rescue Kumi::Core::Errors::TypeError => e
138
138
  puts " → #{e.message}"
139
139
  end
140
140
 
@@ -162,7 +162,7 @@ begin
162
162
  value :result, ref(:undefined_declaration)
163
163
  end
164
164
  end
165
- rescue Kumi::Errors::SemanticError => e
165
+ rescue Kumi::Core::Errors::SemanticError => e
166
166
  puts " → " + e.message.split("\n").join("\n → ")
167
167
  end
168
168
 
data/lib/kumi/analyzer.rb CHANGED
@@ -7,21 +7,21 @@ module Kumi
7
7
  module_function
8
8
 
9
9
  DEFAULT_PASSES = [
10
- Passes::NameIndexer, # 1. Finds all names and checks for duplicates.
11
- Passes::InputCollector, # 2. Collects field metadata from input declarations.
12
- Passes::DeclarationValidator, # 3. Checks the basic structure of each rule.
13
- Passes::SemanticConstraintValidator, # 4. Validates DSL semantic constraints at AST level.
14
- Passes::DependencyResolver, # 5. Builds the dependency graph with conditional dependencies.
15
- Passes::UnsatDetector, # 6. Detects unsatisfiable constraints and analyzes cascade mutual exclusion.
16
- Passes::Toposorter, # 7. Creates the final evaluation order, allowing safe cycles.
17
- Passes::BroadcastDetector, # 8. Detects which operations should be broadcast over arrays (must run before type inference).
18
- Passes::TypeInferencer, # 9. Infers types for all declarations (uses vectorization metadata).
19
- Passes::TypeConsistencyChecker, # 10. Validates declared vs inferred type consistency.
20
- Passes::TypeChecker # 11. Validates types using inferred information.
10
+ Core::Analyzer::Passes::NameIndexer, # 1. Finds all names and checks for duplicates.
11
+ Core::Analyzer::Passes::InputCollector, # 2. Collects field metadata from input declarations.
12
+ Core::Analyzer::Passes::DeclarationValidator, # 3. Checks the basic structure of each rule.
13
+ Core::Analyzer::Passes::SemanticConstraintValidator, # 4. Validates DSL semantic constraints at AST level.
14
+ Core::Analyzer::Passes::DependencyResolver, # 5. Builds the dependency graph with conditional dependencies.
15
+ Core::Analyzer::Passes::UnsatDetector, # 6. Detects unsatisfiable constraints and analyzes cascade mutual exclusion.
16
+ Core::Analyzer::Passes::Toposorter, # 7. Creates the final evaluation order, allowing safe cycles.
17
+ Core::Analyzer::Passes::BroadcastDetector, # 8. Detects which operations should be broadcast over arrays (must run before type inference).
18
+ Core::Analyzer::Passes::TypeInferencer, # 9. Infers types for all declarations (uses vectorization metadata).
19
+ Core::Analyzer::Passes::TypeConsistencyChecker, # 10. Validates declared vs inferred type consistency.
20
+ Core::Analyzer::Passes::TypeChecker # 11. Validates types using inferred information.
21
21
  ].freeze
22
22
 
23
- def analyze!(schema, passes: DEFAULT_PASSES, **opts)
24
- state = AnalysisState.new(opts)
23
+ def self.analyze!(schema, passes: DEFAULT_PASSES, **opts)
24
+ state = Core::Analyzer::AnalysisState.new(opts)
25
25
  errors = []
26
26
 
27
27
  state = run_analysis_passes(schema, passes, state, errors)
@@ -35,7 +35,7 @@ module Kumi
35
35
  begin
36
36
  state = pass_instance.run(errors)
37
37
  rescue StandardError => e
38
- errors << ErrorReporter.create_error(e.message, location: nil, type: :semantic)
38
+ errors << Core::ErrorReporter.create_error(e.message, location: nil, type: :semantic)
39
39
  end
40
40
  end
41
41
  state
@@ -62,7 +62,7 @@ module Kumi
62
62
  end
63
63
 
64
64
  # Handle both old and new error formats for backward compatibility
65
- def format_errors(errors)
65
+ def self.format_errors(errors)
66
66
  return "" if errors.empty?
67
67
 
68
68
  errors.map(&:to_s).join("\n")
data/lib/kumi/compiler.rb CHANGED
@@ -158,7 +158,7 @@ module Kumi
158
158
  compile_declaration(decl)
159
159
  end
160
160
 
161
- CompiledSchema.new(@bindings.freeze)
161
+ Core::CompiledSchema.new(@bindings.freeze)
162
162
  end
163
163
 
164
164
  private
@@ -218,7 +218,7 @@ module Kumi
218
218
  return false unless broadcast_meta
219
219
 
220
220
  # Reduction functions are NOT vectorized operations - they consume arrays
221
- return false if FunctionRegistry.reducer?(expr.fn_name)
221
+ return false if Kumi::Registry.reducer?(expr.fn_name)
222
222
 
223
223
  expr.args.any? do |arg|
224
224
  case arg
@@ -244,7 +244,7 @@ module Kumi
244
244
  vectorized_function_call(name, values)
245
245
  else
246
246
  # All arguments are scalars - regular function call
247
- fn = FunctionRegistry.fetch(name)
247
+ fn = Kumi::Registry.fetch(name)
248
248
  fn.call(*values)
249
249
  end
250
250
  rescue StandardError => e
@@ -257,7 +257,7 @@ module Kumi
257
257
 
258
258
  def vectorized_function_call(fn_name, values)
259
259
  # Get the function from registry
260
- fn = FunctionRegistry.fetch(fn_name)
260
+ fn = Kumi::Registry.fetch(fn_name)
261
261
 
262
262
  # Find array dimensions for broadcasting
263
263
  array_values = values.select { |v| v.is_a?(Array) }
@@ -276,14 +276,14 @@ module Kumi
276
276
  end
277
277
 
278
278
  def invoke_function(name, arg_fns, ctx, loc)
279
- fn = FunctionRegistry.fetch(name)
279
+ fn = Kumi::Registry.fetch(name)
280
280
  values = arg_fns.map { |fn| fn.call(ctx) }
281
281
  fn.call(*values)
282
282
  rescue StandardError => e
283
283
  # Preserve original error class and backtrace while adding context
284
284
  enhanced_message = "Error calling fn(:#{name}) at #{loc}: #{e.message}"
285
285
 
286
- if e.is_a?(Kumi::Errors::Error)
286
+ if e.is_a?(Kumi::Core::Errors::Error)
287
287
  # Re-raise Kumi errors with enhanced message but preserve type
288
288
  e.define_singleton_method(:message) { enhanced_message }
289
289
  raise e
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module Analyzer
6
+ # Simple immutable state wrapper to prevent accidental mutations between passes
7
+ class AnalysisState
8
+ def initialize(data = {})
9
+ @data = data.dup.freeze
10
+ end
11
+
12
+ # Get a value (same as hash access)
13
+ def [](key)
14
+ @data[key]
15
+ end
16
+
17
+ # Check if key exists (same as hash)
18
+ def key?(key)
19
+ @data.key?(key)
20
+ end
21
+
22
+ # Get all keys (same as hash)
23
+ def keys
24
+ @data.keys
25
+ end
26
+
27
+ # Create new state with additional data (simple and clean)
28
+ def with(key, value)
29
+ AnalysisState.new(@data.merge(key => value))
30
+ end
31
+
32
+ # Convert back to hash for final result
33
+ def to_h
34
+ @data.dup
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module Analyzer
6
+ class ConstantEvaluator
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