kumi 0.0.4 → 0.0.6

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +160 -8
  3. data/README.md +278 -200
  4. data/{documents → docs}/AST.md +29 -29
  5. data/{documents → docs}/DSL.md +3 -3
  6. data/{documents → docs}/SYNTAX.md +107 -24
  7. data/docs/features/README.md +45 -0
  8. data/docs/features/analysis-cascade-mutual-exclusion.md +89 -0
  9. data/docs/features/analysis-type-inference.md +42 -0
  10. data/docs/features/analysis-unsat-detection.md +71 -0
  11. data/docs/features/array-broadcasting.md +170 -0
  12. data/docs/features/input-declaration-system.md +42 -0
  13. data/docs/features/performance.md +16 -0
  14. data/examples/federal_tax_calculator_2024.rb +43 -40
  15. data/examples/game_of_life.rb +97 -0
  16. data/examples/simple_rpg_game.rb +1000 -0
  17. data/examples/static_analysis_errors.rb +178 -0
  18. data/examples/wide_schema_compilation_and_evaluation_benchmark.rb +1 -1
  19. data/lib/kumi/analyzer/analysis_state.rb +37 -0
  20. data/lib/kumi/analyzer/constant_evaluator.rb +22 -16
  21. data/lib/kumi/analyzer/passes/broadcast_detector.rb +251 -0
  22. data/lib/kumi/analyzer/passes/{definition_validator.rb → declaration_validator.rb} +8 -7
  23. data/lib/kumi/analyzer/passes/dependency_resolver.rb +106 -26
  24. data/lib/kumi/analyzer/passes/input_collector.rb +105 -23
  25. data/lib/kumi/analyzer/passes/name_indexer.rb +2 -2
  26. data/lib/kumi/analyzer/passes/pass_base.rb +11 -28
  27. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +110 -0
  28. data/lib/kumi/analyzer/passes/toposorter.rb +45 -9
  29. data/lib/kumi/analyzer/passes/type_checker.rb +34 -11
  30. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +2 -1
  31. data/lib/kumi/analyzer/passes/type_inferencer.rb +128 -21
  32. data/lib/kumi/analyzer/passes/unsat_detector.rb +312 -13
  33. data/lib/kumi/analyzer/passes/visitor_pass.rb +4 -3
  34. data/lib/kumi/analyzer.rb +41 -24
  35. data/lib/kumi/atom_unsat_solver.rb +45 -0
  36. data/lib/kumi/cli.rb +449 -0
  37. data/lib/kumi/compiler.rb +194 -16
  38. data/lib/kumi/constraint_relationship_solver.rb +638 -0
  39. data/lib/kumi/domain/validator.rb +0 -4
  40. data/lib/kumi/error_reporter.rb +6 -6
  41. data/lib/kumi/evaluation_wrapper.rb +20 -4
  42. data/lib/kumi/explain.rb +28 -28
  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 +117 -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/parser/declaration_reference_proxy.rb +36 -0
  51. data/lib/kumi/parser/dsl_cascade_builder.rb +19 -8
  52. data/lib/kumi/parser/expression_converter.rb +80 -12
  53. data/lib/kumi/parser/input_builder.rb +40 -9
  54. data/lib/kumi/parser/input_field_proxy.rb +46 -0
  55. data/lib/kumi/parser/input_proxy.rb +3 -3
  56. data/lib/kumi/parser/nested_input.rb +15 -0
  57. data/lib/kumi/parser/parser.rb +2 -0
  58. data/lib/kumi/parser/schema_builder.rb +10 -9
  59. data/lib/kumi/parser/sugar.rb +171 -18
  60. data/lib/kumi/schema.rb +3 -1
  61. data/lib/kumi/schema_instance.rb +69 -3
  62. data/lib/kumi/syntax/array_expression.rb +15 -0
  63. data/lib/kumi/syntax/call_expression.rb +11 -0
  64. data/lib/kumi/syntax/cascade_expression.rb +11 -0
  65. data/lib/kumi/syntax/case_expression.rb +11 -0
  66. data/lib/kumi/syntax/declaration_reference.rb +11 -0
  67. data/lib/kumi/syntax/hash_expression.rb +11 -0
  68. data/lib/kumi/syntax/input_declaration.rb +12 -0
  69. data/lib/kumi/syntax/input_element_reference.rb +12 -0
  70. data/lib/kumi/syntax/input_reference.rb +12 -0
  71. data/lib/kumi/syntax/literal.rb +11 -0
  72. data/lib/kumi/syntax/root.rb +1 -0
  73. data/lib/kumi/syntax/trait_declaration.rb +11 -0
  74. data/lib/kumi/syntax/value_declaration.rb +11 -0
  75. data/lib/kumi/types/compatibility.rb +8 -0
  76. data/lib/kumi/types/validator.rb +1 -1
  77. data/lib/kumi/vectorization_metadata.rb +108 -0
  78. data/lib/kumi/version.rb +1 -1
  79. data/scripts/generate_function_docs.rb +22 -10
  80. metadata +38 -17
  81. data/CHANGELOG.md +0 -25
  82. data/lib/kumi/domain.rb +0 -8
  83. data/lib/kumi/input.rb +0 -8
  84. data/lib/kumi/syntax/declarations.rb +0 -23
  85. data/lib/kumi/syntax/expressions.rb +0 -30
  86. data/lib/kumi/syntax/terminal_expressions.rb +0 -27
  87. data/lib/kumi/syntax.rb +0 -9
  88. data/test_impossible_cascade.rb +0 -51
  89. /data/{documents → docs}/FUNCTIONS.md +0 -0
@@ -7,22 +7,22 @@
7
7
  Root = Struct.new(:inputs, :attributes, :traits)
8
8
  ```
9
9
 
10
- **FieldDecl**: Input field metadata
10
+ **InputDeclaration**: Input field metadata
11
11
  ```ruby
12
- FieldDecl = Struct.new(:name, :domain, :type)
13
- # DSL: integer :age, domain: 18..65 → FieldDecl(:age, 18..65, :integer)
12
+ InputDeclaration = Struct.new(:name, :domain, :type)
13
+ # DSL: integer :age, domain: 18..65 → InputDeclaration(:age, 18..65, :integer)
14
14
  ```
15
15
 
16
- **Trait**: Boolean predicate
16
+ **TraitDeclaration**: Boolean predicate
17
17
  ```ruby
18
- Trait = Struct.new(:name, :expression)
19
- # DSL: trait :adult, (input.age >= 18) → Trait(:adult, CallExpression(...))
18
+ TraitDeclaration = Struct.new(:name, :expression)
19
+ # DSL: trait :adult, (input.age >= 18) → TraitDeclaration(:adult, CallExpression(...))
20
20
  ```
21
21
 
22
- **Attribute**: Computed value
22
+ **ValueDeclaration**: Computed value
23
23
  ```ruby
24
- Attribute = Struct.new(:name, :expression)
25
- # DSL: value :total, fn(:add, a, b) → Attribute(:total, CallExpression(:add, [...]))
24
+ ValueDeclaration = Struct.new(:name, :expression)
25
+ # DSL: value :total, fn(:add, a, b) → ValueDeclaration(:total, CallExpression(:add, [...]))
26
26
  ```
27
27
 
28
28
  ## Expression Nodes
@@ -33,18 +33,18 @@ CallExpression = Struct.new(:fn_name, :args)
33
33
  def &(other) = CallExpression.new(:and, [self, other]) # Enable chaining
34
34
  ```
35
35
 
36
- **FieldRef**: Field access (`input.field_name`)
36
+ **InputReference**: Field access (`input.field_name`)
37
37
  ```ruby
38
- FieldRef = Struct.new(:name)
38
+ InputReference = Struct.new(:name)
39
39
  # Has operator methods: >=, <=, >, <, ==, != that create CallExpression nodes
40
40
  ```
41
41
 
42
- **Binding**: References to other declarations
42
+ **DeclarationReference**: References to other declarations
43
43
  ```ruby
44
- Binding = Struct.new(:name)
44
+ DeclarationReference = Struct.new(:name)
45
45
  # Created by: ref(:name) OR bare identifier (trait_name) in composite traits
46
- # DSL: ref(:adult) → Binding(:adult)
47
- # DSL: adult & verified → CallExpression(:and, [Binding(:adult), Binding(:verified)])
46
+ # DSL: ref(:adult) → DeclarationReference(:adult)
47
+ # DSL: adult & verified → CallExpression(:and, [DeclarationReference(:adult), DeclarationReference(:verified)])
48
48
  ```
49
49
 
50
50
  **Literal**: Constants (`18`, `"text"`, `true`)
@@ -52,9 +52,9 @@ Binding = Struct.new(:name)
52
52
  Literal = Struct.new(:value)
53
53
  ```
54
54
 
55
- **ListExpression**: Arrays (`[1, 2, 3]`)
55
+ **ArrayExpression**: Arrays (`[1, 2, 3]`)
56
56
  ```ruby
57
- ListExpression = Struct.new(:elements)
57
+ ArrayExpression = Struct.new(:elements)
58
58
  ```
59
59
 
60
60
  ## Cascade Expressions (Conditional Values)
@@ -64,9 +64,9 @@ ListExpression = Struct.new(:elements)
64
64
  CascadeExpression = Struct.new(:cases)
65
65
  ```
66
66
 
67
- **WhenCaseExpression**: Individual conditions
67
+ **CaseExpression**: Individual conditions
68
68
  ```ruby
69
- WhenCaseExpression = Struct.new(:condition, :result)
69
+ CaseExpression = Struct.new(:condition, :result)
70
70
  ```
71
71
 
72
72
  **Case type mappings**:
@@ -76,7 +76,7 @@ WhenCaseExpression = Struct.new(:condition, :result)
76
76
 
77
77
  ## Key Nuances
78
78
 
79
- **Operator methods on FieldRef**: Enable `input.age >= 18` syntax by defining operators that create `CallExpression` nodes
79
+ **Operator methods on InputReference**: Enable `input.age >= 18` syntax by defining operators that create `CallExpression` nodes
80
80
 
81
81
  **CallExpression `&` method**: Enables expression chaining like `(expr1) & (expr2)`
82
82
 
@@ -92,14 +92,14 @@ WhenCaseExpression = Struct.new(:condition, :result)
92
92
 
93
93
  **Simple**: `(input.age >= 18)`
94
94
  ```
95
- CallExpression(:>=, [FieldRef(:age), Literal(18)])
95
+ CallExpression(:>=, [InputReference(:age), Literal(18)])
96
96
  ```
97
97
 
98
98
  **Chained AND**: `(input.age >= 21) & (input.verified == true)`
99
99
  ```
100
100
  CallExpression(:and, [
101
- CallExpression(:>=, [FieldRef(:age), Literal(21)]),
102
- CallExpression(:==, [FieldRef(:verified), Literal(true)])
101
+ CallExpression(:>=, [InputReference(:age), Literal(21)]),
102
+ CallExpression(:==, [InputReference(:verified), Literal(true)])
103
103
  ])
104
104
  ```
105
105
 
@@ -107,10 +107,10 @@ CallExpression(:and, [
107
107
  ```
108
108
  CallExpression(:and, [
109
109
  CallExpression(:and, [
110
- Binding(:adult),
111
- Binding(:verified)
110
+ DeclarationReference(:adult),
111
+ DeclarationReference(:verified)
112
112
  ]),
113
- Binding(:high_income)
113
+ DeclarationReference(:high_income)
114
114
  ])
115
115
  ```
116
116
 
@@ -118,9 +118,9 @@ CallExpression(:and, [
118
118
  ```
119
119
  CallExpression(:and, [
120
120
  CallExpression(:and, [
121
- Binding(:adult),
122
- CallExpression(:>, [FieldRef(:score), Literal(80)])
121
+ DeclarationReference(:adult),
122
+ CallExpression(:>, [InputReference(:score), Literal(80)])
123
123
  ]),
124
- Binding(:verified)
124
+ DeclarationReference(:verified)
125
125
  ])
126
126
  ```
@@ -109,13 +109,13 @@ For conditional logic, a `value` takes a block to create a **cascade**. Cascades
109
109
  ```ruby
110
110
  value :access_level do
111
111
  # `on` implies AND: user must be :premium AND :verified.
112
- on :premium, :verified, "Full Access"
112
+ on premium,verified, "Full Access"
113
113
 
114
114
  # `on_any` implies OR: user can be :staff OR :admin.
115
- on_any :staff, :admin, "Elevated Access"
115
+ on_any staff,admin, "Elevated Access"
116
116
 
117
117
  # `on_none` implies NOT (A OR B): user is neither :blocked NOR :suspended.
118
- on_none :blocked, :suspended, "Limited Access"
118
+ on_none blocked,suspended, "Limited Access"
119
119
 
120
120
  # `base` is the default if no other conditions match.
121
121
  base "No Access"
@@ -10,6 +10,7 @@ This document provides a comprehensive comparison of Kumi's DSL syntax showing b
10
10
  - [Trait Declarations](#trait-declarations)
11
11
  - [Expressions](#expressions)
12
12
  - [Functions](#functions)
13
+ - [Array Broadcasting](#array-broadcasting)
13
14
  - [Cascade Logic](#cascade-logic)
14
15
  - [References](#references)
15
16
 
@@ -187,11 +188,13 @@ value :sorted_scores, fn(:sort, input.score_array)
187
188
 
188
189
  ### Built-in Functions Available
189
190
 
191
+ See [FUNCTIONS.md](FUNCTIONS.md)
192
+
190
193
  | Category | Sugar | Sugar-Free |
191
194
  |----------|-------|------------|
192
195
  | **Arithmetic** | `+`, `-`, `*`, `/`, `**` | `fn(:add, a, b)`, `fn(:subtract, a, b)`, etc. |
193
196
  | **Comparison** | `>`, `<`, `>=`, `<=`, `==`, `!=` | `fn(:>, a, b)`, `fn(:<, a, b)`, etc. |
194
- | **Logical** | `&` (AND only) | `fn(:and, a, b)`, `fn(:or, a, b)`, `fn(:not, a)` |
197
+ | **Logical** | `&` `|` | `fn(:and, a, b)`, `fn(:or, a, b)`, `fn(:not, a)` |
195
198
  | **Math** | `abs`, `round`, `ceil`, `floor` | `fn(:abs, x)`, `fn(:round, x)`, etc. |
196
199
  | **String** | `.length`, `.upcase`, `.downcase` | `fn(:string_length, s)`, `fn(:upcase, s)`, etc. |
197
200
  | **Collection** | `.sum`, `.size`, `.max`, `.min` | `fn(:sum, arr)`, `fn(:size, arr)`, etc. |
@@ -208,31 +211,110 @@ value :clamped_score, fn(:clamp, input.raw_score, 0, 1600)
208
211
  value :formatted_name, fn(:add, fn(:add, input.first_name, " "), input.last_name)
209
212
  ```
210
213
 
211
- ## Cascade Logic
214
+ ## Array Broadcasting
215
+
216
+ Array broadcasting enables element-wise operations on array fields with automatic vectorization.
212
217
 
213
- Cascade syntax is the same in both approaches, but conditions use different syntax:
218
+ ### Array Input Declarations
214
219
 
215
220
  ```ruby
216
- # With Sugar
217
- value :grade_letter do
218
- on :excellent_student, "A+"
219
- on :high_scorer, "A"
220
- on :above_average, "B"
221
- on :needs_improvement, "C"
222
- base "F"
221
+ input do
222
+ array :line_items do
223
+ float :price
224
+ integer :quantity
225
+ string :category
226
+ end
227
+
228
+ array :orders do
229
+ array :items do
230
+ hash :product do
231
+ string :name
232
+ float :base_price
233
+ end
234
+ integer :quantity
235
+ end
236
+ end
223
237
  end
238
+ ```
239
+
240
+ ### Element-wise Operations
241
+
242
+ ```ruby
243
+ # With Sugar - Automatic Broadcasting
244
+ value :subtotals, input.line_items.price * input.line_items.quantity
245
+ trait :is_taxable, (input.line_items.category != "digital")
246
+ value :discounted_prices, input.line_items.price * 0.9
247
+
248
+ # Sugar-Free - Explicit Broadcasting
249
+ value :subtotals, fn(:multiply, input.line_items.price, input.line_items.quantity)
250
+ trait :is_taxable, fn(:!=, input.line_items.category, "digital")
251
+ value :discounted_prices, fn(:multiply, input.line_items.price, 0.9)
252
+ ```
253
+
254
+ ### Aggregation Operations
224
255
 
225
- # Sugar-Free
256
+ ```ruby
257
+ # With Sugar - Automatic Aggregation Detection
258
+ value :total_subtotal, fn(:sum, subtotals)
259
+ value :avg_price, fn(:avg, input.line_items.price)
260
+ value :max_quantity, fn(:max, input.line_items.quantity)
261
+ value :item_count, fn(:size, input.line_items)
262
+
263
+ # Sugar-Free - Same Syntax
264
+ value :total_subtotal, fn(:sum, subtotals)
265
+ value :avg_price, fn(:avg, input.line_items.price)
266
+ value :max_quantity, fn(:max, input.line_items.quantity)
267
+ value :item_count, fn(:size, input.line_items)
268
+ ```
269
+
270
+ ### Nested Array Access
271
+
272
+ ```ruby
273
+ # With Sugar - Deep Field Access
274
+ value :all_product_names, input.orders.items.product.name
275
+ value :total_values, input.orders.items.product.base_price * input.orders.items.quantity
276
+
277
+ # Sugar-Free - Same Deep Access
278
+ value :all_product_names, input.orders.items.product.name
279
+ value :total_values, fn(:multiply, input.orders.items.product.base_price, input.orders.items.quantity)
280
+ ```
281
+
282
+ ### Mixed Operations
283
+
284
+ ```ruby
285
+ # With Sugar - Element-wise then Aggregation
286
+ value :line_totals, input.items.price * input.items.quantity
287
+ value :order_total, fn(:sum, line_totals)
288
+ value :avg_line_total, fn(:avg, line_totals)
289
+ trait :has_expensive, fn(:any?, expensive_items)
290
+
291
+ # Sugar-Free - Same Pattern
292
+ value :line_totals, fn(:multiply, input.items.price, input.items.quantity)
293
+ value :order_total, fn(:sum, line_totals)
294
+ value :avg_line_total, fn(:avg, line_totals)
295
+ trait :has_expensive, fn(:any?, expensive_items)
296
+ ```
297
+
298
+ ### Broadcasting Type Inference
299
+
300
+ The type system automatically infers appropriate types:
301
+ - `input.items.price` (float array) → inferred as `:float` per element
302
+ - `input.items.price * input.items.quantity` → element-wise `:float` result
303
+ - `fn(:sum, input.items.price)` → scalar `:float` result
304
+
305
+ ## Cascade Logic
306
+
307
+ Cascades are similar to Ruby when case, where each case is one of more trait reference and finally the value if that branch is true.
308
+ ```ruby
226
309
  value :grade_letter do
227
- on :excellent_student, "A+"
228
- on :high_scorer, "A"
229
- on :above_average, "B"
230
- on :needs_improvement, "C"
310
+ on excellent_student, "A+"
311
+ on high_scorer, "A"
312
+ on above_average, "B"
313
+ on needs_improvement, "C"
231
314
  base "F"
232
315
  end
233
316
  ```
234
317
 
235
- The difference is in how the traits referenced in cascade conditions are defined (see Trait Declarations above).
236
318
 
237
319
  ## References
238
320
 
@@ -287,9 +369,9 @@ module StudentEvaluation
287
369
 
288
370
  # Cascade with sugar-defined traits
289
371
  value :scholarship_amount do
290
- on :scholarship_candidate, 10000
291
- on :high_performer, 5000
292
- on :math_excellence, 2500
372
+ on scholarship_candidate, 10000
373
+ on high_performer, 5000
374
+ on math_excellence, 2500
293
375
  base 0
294
376
  end
295
377
  end
@@ -324,9 +406,9 @@ module StudentEvaluation
324
406
 
325
407
  # Cascade with sugar-free defined traits
326
408
  value :scholarship_amount do
327
- on :scholarship_candidate, 10000
328
- on :high_performer, 5000
329
- on :math_excellence, 2500
409
+ on scholarship_candidate, 10000
410
+ on high_performer, 5000
411
+ on math_excellence, 2500
330
412
  base 0
331
413
  end
332
414
  end
@@ -351,10 +433,11 @@ end
351
433
  ## Syntax Limitations
352
434
 
353
435
  ### Sugar Syntax Limitations:
354
- - Only supports `&` for logical AND (no `&&` due to Ruby precedence)
355
- - No logical OR sugar syntax (must use `fn(:or, a, b)`)
436
+ - Supports `&` for logical AND (no `&&` due to Ruby precedence)
437
+ - Supports `|` for logical OR
356
438
  - Limited operator precedence control
357
439
  - Some Ruby methods not available as sugar
440
+ - **Refinement scope**: Array methods like `.max` on syntax expressions may not work in certain contexts (e.g., test helpers, Ruby < 3.0). Use `fn(:max, array)` instead.
358
441
 
359
442
  ### Sugar-Free Advantages:
360
443
  - Full access to all registered functions
@@ -0,0 +1,45 @@
1
+ # Features
2
+
3
+ ## Core Features
4
+
5
+ ### [Unsatisfiability Detection](analysis-unsat-detection.md)
6
+ Analyzes rule combinations to detect logical impossibilities across dependency chains.
7
+
8
+ - Detects impossible combinations at compile-time
9
+ - Validates domain constraints
10
+ - Reports multiple errors
11
+
12
+ ### [Cascade Mutual Exclusion](analysis-cascade-mutual-exclusion.md)
13
+ Enables safe mutual recursion when cascade conditions are mutually exclusive.
14
+
15
+ - Allows mathematically sound recursive patterns
16
+ - Detects mutually exclusive conditions
17
+ - Prevents unsafe cycles while enabling safe ones
18
+
19
+ ### [Type Inference](analysis-type-inference.md)
20
+ Determines types from expressions and propagates them through dependencies.
21
+
22
+ - Infers types from literals and function calls
23
+ - Propagates types through dependency graph
24
+ - Validates function arguments
25
+
26
+ ### [Input Declarations](input-declaration-system.md)
27
+ Defines expected inputs with types and constraints.
28
+
29
+ - Type-specific declaration methods
30
+ - Domain validation at runtime
31
+ - Separates input metadata from business logic
32
+
33
+ ### [Performance](performance.md)
34
+ TODO: Add benchmark data
35
+ Processes large schemas with optimized algorithms.
36
+
37
+ - Result caching
38
+ - Selective evaluation
39
+
40
+ ## Integration
41
+
42
+ - Type inference uses input declarations
43
+ - Unsatisfiability detection uses type information for validation
44
+ - Cascade mutual exclusion integrates with dependency analysis and cycle detection
45
+ - Performance optimizations apply to all analysis passes
@@ -0,0 +1,89 @@
1
+ # Cascade Mutual Exclusion Detection
2
+
3
+ Analyzes cascade expressions to allow safe recursive patterns when conditions are mutually exclusive.
4
+
5
+ ## Overview
6
+
7
+ The cascade mutual exclusion detector identifies when all conditions in a cascade expression cannot be true simultaneously, enabling safe mutual recursion patterns that would otherwise be rejected as cycles.
8
+
9
+ ## Core Mechanism
10
+
11
+ The system performs three-stage analysis:
12
+
13
+ 1. **Conditional Dependency Tracking** - DependencyResolver marks base case dependencies as conditional
14
+ 2. **Mutual Exclusion Analysis** - UnsatDetector determines if cascade conditions are mutually exclusive
15
+ 3. **Safe Cycle Detection** - Toposorter allows cycles where all edges are conditional and conditions are mutually exclusive
16
+
17
+ ## Example: Processing Workflow
18
+
19
+ ```ruby
20
+ schema do
21
+ input do
22
+ string :operation # "forward", "reverse", "unknown"
23
+ integer :value
24
+ end
25
+
26
+ trait :is_forward, input.operation == "forward"
27
+ trait :is_reverse, input.operation == "reverse"
28
+
29
+ # Safe mutual recursion - conditions are mutually exclusive
30
+ value :forward_processor do
31
+ on is_forward, input.value * 2 # Direct calculation
32
+ on is_reverse, reverse_processor + 10 # Delegates to reverse (safe)
33
+ base "invalid operation" # Fallback for unknown operations
34
+ end
35
+
36
+ value :reverse_processor do
37
+ on is_forward, forward_processor - 5 # Delegates to forward (safe)
38
+ on is_reverse, input.value / 2 # Direct calculation
39
+ base "invalid operation" # Fallback for unknown operations
40
+ end
41
+ end
42
+ ```
43
+
44
+ ## Safety Guarantees
45
+
46
+ **Allowed**: Cycles where conditions are mutually exclusive
47
+ - `is_forward` and `is_reverse` cannot both be true (operation has single value)
48
+ - Each recursion executes exactly one step before hitting direct calculation
49
+ - Bounded recursion with guaranteed termination
50
+
51
+ **Rejected**: Cycles with overlapping conditions
52
+ ```ruby
53
+ # This would be rejected - conditions can overlap
54
+ value :unsafe_cycle do
55
+ on input.n > 0, "positive"
56
+ on input.n > 5, "large" # Both can be true!
57
+ base fn(:not, unsafe_cycle)
58
+ end
59
+ ```
60
+
61
+ ## Implementation Details
62
+
63
+ ### Conditional Dependencies
64
+ Base case dependencies are marked as conditional because they only execute when no explicit conditions match.
65
+
66
+ ### Mutual Exclusion Analysis
67
+ Conditions are analyzed for mutual exclusion:
68
+ - Same field equality comparisons: `field == value1` vs `field == value2`
69
+ - Domain constraints ensuring impossibility
70
+ - All condition pairs must be mutually exclusive
71
+
72
+ ### Metadata Generation
73
+ Analysis results stored in `cascade_metadata` state:
74
+ ```ruby
75
+ {
76
+ condition_traits: [:is_forward, :is_reverse],
77
+ condition_count: 2,
78
+ all_mutually_exclusive: true,
79
+ exclusive_pairs: 1,
80
+ total_pairs: 1
81
+ }
82
+ ```
83
+
84
+ ## Use Cases
85
+
86
+ - Processing workflows with bidirectional logic
87
+ - State machine fallback patterns
88
+ - Recursive decision trees with termination conditions
89
+ - Complex business rules with safe delegation patterns
@@ -0,0 +1,42 @@
1
+ # Type Inference
2
+
3
+ Infers types from expressions and propagates them through dependency chains for compile-time type checking.
4
+
5
+ ## Type Inference Rules
6
+
7
+ **Literals:**
8
+ - `42` → `:integer`
9
+ - `3.14` → `:float`
10
+ - `"hello"` → `:string`
11
+ - `true` → `:boolean`
12
+ - `[1, 2, 3]` → `{ array: :integer }`
13
+
14
+ **Operations:**
15
+ - Integer arithmetic → `:integer`
16
+ - Mixed numeric operations → `:numeric`
17
+ - Comparisons → `:boolean`
18
+
19
+ **Functions:**
20
+ - Return types defined in function registry
21
+ - Arguments validated against parameter types
22
+
23
+ ## Error Detection
24
+
25
+ **Type mismatches:**
26
+ ```
27
+ TypeError: argument 2 of addition expects numeric, got input field `age` of declared type integer,
28
+ but argument 1 is input field `customer_name` of declared type string
29
+ ```
30
+
31
+ **Function validation:**
32
+ - Arity validation (correct number of arguments)
33
+ - Type compatibility validation
34
+ - Unknown function detection
35
+
36
+ ## Inference Process
37
+
38
+ - Processes declarations in topological order to resolve dependencies
39
+ - Literal types determined from values
40
+ - Function return types from function registry
41
+ - Array types unified from element types
42
+ - Cascade types inferred from result expressions
@@ -0,0 +1,71 @@
1
+ # Unsatisfiability Detection
2
+
3
+ Analyzes logical relationships across dependency chains to detect impossible rule combinations at compile time.
4
+
5
+ ## Example: Credit Card Approval System
6
+
7
+ This schema contains impossible rule combinations:
8
+
9
+ ```ruby
10
+ module CreditCardApproval
11
+ extend Kumi::Schema
12
+
13
+ schema do
14
+ input do
15
+ integer :annual_income
16
+ integer :monthly_debt
17
+ integer :credit_score, domain: 300..850
18
+ string :employment_type, domain: %w[full_time part_time contract self_employed]
19
+ end
20
+
21
+ # Financial calculations
22
+ value :monthly_income, input.annual_income / 12
23
+ value :available_monthly, monthly_income - input.monthly_debt
24
+ value :credit_limit_base, input.annual_income * 0.3
25
+ value :score_multiplier, (input.credit_score - 600) * 0.01
26
+ value :final_credit_limit, credit_limit_base * score_multiplier
27
+ value :employment_stability_factor,
28
+ input.employment_type == "full_time" ? 1.0 :
29
+ input.employment_type == "part_time" ? 0.7 :
30
+ input.employment_type == "contract" ? 0.5 : 0.3
31
+
32
+ # Business rules
33
+ trait :stable_income, (input.annual_income >= 60_000)
34
+ trait :good_credit, (input.credit_score >= 700)
35
+ trait :high_available_income, (available_monthly >= 4_000)
36
+ trait :premium_limit_qualified, (final_credit_limit >= 50_000)
37
+ trait :stable_employment, (input.employment_type == "full_time")
38
+ trait :high_stability_factor, (employment_stability_factor >= 0.8)
39
+ trait :excellent_credit, (input.credit_score == 900)
40
+
41
+ # Approval tiers - which combinations are impossible?
42
+ value :approval_tier do
43
+ on stable_income,good_credit,premium_limit_qualified, "platinum_tier"
44
+ on stable_employment,high_stability_factor, "executive_tier"
45
+ on excellent_credit,good_credit, "perfect_score_tier"
46
+ on stable_income,good_credit, "standard_tier"
47
+ base "manual_review"
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+ **Detected errors:**
54
+
55
+ ```
56
+ SemanticError:
57
+ conjunction `excellent_credit AND good_credit` is impossible
58
+ conjunction `excellent_credit` is impossible
59
+ ```
60
+
61
+ **Root cause:**
62
+ - `excellent_credit` requires `credit_score == 900`
63
+ - Input domain constrains `credit_score` to `300..850`
64
+ - Any cascade condition using `excellent_credit` becomes impossible
65
+
66
+ ## Detection Mechanisms
67
+
68
+ - Domain constraint violations: `field == value` where value is outside declared domain
69
+ - Cascade condition analysis: each `on` condition checked independently
70
+ - OR expression handling: impossible only if both sides are impossible
71
+ - Cross-variable mathematical constraint analysis