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.
- checksums.yaml +4 -4
- data/CLAUDE.md +76 -174
- data/README.md +205 -52
- data/{documents → docs}/AST.md +29 -29
- data/{documents → docs}/SYNTAX.md +95 -8
- data/docs/features/README.md +45 -0
- data/docs/features/analysis-cascade-mutual-exclusion.md +89 -0
- data/docs/features/analysis-type-inference.md +42 -0
- data/docs/features/analysis-unsat-detection.md +71 -0
- data/docs/features/array-broadcasting.md +170 -0
- data/docs/features/input-declaration-system.md +42 -0
- data/docs/features/performance.md +16 -0
- data/docs/schema_metadata/broadcasts.md +53 -0
- data/docs/schema_metadata/cascades.md +45 -0
- data/docs/schema_metadata/declarations.md +54 -0
- data/docs/schema_metadata/dependencies.md +57 -0
- data/docs/schema_metadata/evaluation_order.md +29 -0
- data/docs/schema_metadata/examples.md +95 -0
- data/docs/schema_metadata/inferred_types.md +46 -0
- data/docs/schema_metadata/inputs.md +86 -0
- data/docs/schema_metadata.md +108 -0
- data/examples/federal_tax_calculator_2024.rb +11 -6
- data/lib/kumi/analyzer/constant_evaluator.rb +1 -1
- data/lib/kumi/analyzer/passes/broadcast_detector.rb +246 -0
- data/lib/kumi/analyzer/passes/{definition_validator.rb → declaration_validator.rb} +4 -4
- data/lib/kumi/analyzer/passes/dependency_resolver.rb +78 -38
- data/lib/kumi/analyzer/passes/input_collector.rb +91 -30
- data/lib/kumi/analyzer/passes/name_indexer.rb +2 -2
- data/lib/kumi/analyzer/passes/pass_base.rb +1 -1
- data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +24 -25
- data/lib/kumi/analyzer/passes/toposorter.rb +44 -8
- data/lib/kumi/analyzer/passes/type_checker.rb +34 -14
- data/lib/kumi/analyzer/passes/type_consistency_checker.rb +2 -2
- data/lib/kumi/analyzer/passes/type_inferencer.rb +130 -21
- data/lib/kumi/analyzer/passes/unsat_detector.rb +134 -56
- data/lib/kumi/analyzer/passes/visitor_pass.rb +2 -2
- data/lib/kumi/analyzer.rb +16 -17
- data/lib/kumi/compiler.rb +188 -16
- data/lib/kumi/constraint_relationship_solver.rb +6 -6
- data/lib/kumi/domain/validator.rb +0 -4
- data/lib/kumi/error_reporting.rb +1 -1
- data/lib/kumi/explain.rb +32 -20
- data/lib/kumi/export/node_registry.rb +26 -12
- data/lib/kumi/export/node_serializers.rb +1 -1
- data/lib/kumi/function_registry/collection_functions.rb +14 -9
- data/lib/kumi/function_registry/function_builder.rb +4 -3
- data/lib/kumi/function_registry.rb +8 -2
- data/lib/kumi/input/type_matcher.rb +3 -0
- data/lib/kumi/input/validator.rb +0 -3
- data/lib/kumi/json_schema/generator.rb +63 -0
- data/lib/kumi/json_schema/validator.rb +25 -0
- data/lib/kumi/json_schema.rb +14 -0
- data/lib/kumi/{parser → ruby_parser}/build_context.rb +1 -1
- data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +36 -0
- data/lib/kumi/{parser → ruby_parser}/dsl.rb +1 -1
- data/lib/kumi/{parser → ruby_parser}/dsl_cascade_builder.rb +5 -5
- data/lib/kumi/{parser → ruby_parser}/expression_converter.rb +20 -20
- data/lib/kumi/{parser → ruby_parser}/guard_rails.rb +1 -1
- data/lib/kumi/{parser → ruby_parser}/input_builder.rb +41 -10
- data/lib/kumi/ruby_parser/input_field_proxy.rb +46 -0
- data/lib/kumi/{parser → ruby_parser}/input_proxy.rb +4 -4
- data/lib/kumi/ruby_parser/nested_input.rb +15 -0
- data/lib/kumi/{parser → ruby_parser}/parser.rb +11 -10
- data/lib/kumi/{parser → ruby_parser}/schema_builder.rb +11 -10
- data/lib/kumi/{parser → ruby_parser}/sugar.rb +62 -10
- data/lib/kumi/ruby_parser.rb +10 -0
- data/lib/kumi/schema.rb +10 -4
- data/lib/kumi/schema_instance.rb +6 -6
- data/lib/kumi/schema_metadata.rb +524 -0
- data/lib/kumi/syntax/array_expression.rb +15 -0
- data/lib/kumi/syntax/call_expression.rb +11 -0
- data/lib/kumi/syntax/cascade_expression.rb +11 -0
- data/lib/kumi/syntax/case_expression.rb +11 -0
- data/lib/kumi/syntax/declaration_reference.rb +11 -0
- data/lib/kumi/syntax/hash_expression.rb +11 -0
- data/lib/kumi/syntax/input_declaration.rb +12 -0
- data/lib/kumi/syntax/input_element_reference.rb +12 -0
- data/lib/kumi/syntax/input_reference.rb +12 -0
- data/lib/kumi/syntax/literal.rb +11 -0
- data/lib/kumi/syntax/trait_declaration.rb +11 -0
- data/lib/kumi/syntax/value_declaration.rb +11 -0
- data/lib/kumi/vectorization_metadata.rb +108 -0
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +14 -0
- metadata +55 -25
- data/lib/generators/trait_engine/templates/schema_spec.rb.erb +0 -27
- data/lib/kumi/domain.rb +0 -8
- data/lib/kumi/input.rb +0 -8
- data/lib/kumi/syntax/declarations.rb +0 -26
- data/lib/kumi/syntax/expressions.rb +0 -34
- data/lib/kumi/syntax/terminal_expressions.rb +0 -30
- data/lib/kumi/syntax.rb +0 -9
- /data/{documents → docs}/DSL.md +0 -0
- /data/{documents → docs}/FUNCTIONS.md +0 -0
@@ -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
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# Array Broadcasting
|
2
|
+
|
3
|
+
Automatic vectorization of operations over array fields with element-wise computation and aggregation.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The array broadcasting system enables natural field access syntax on array inputs (`input.items.price`) that automatically applies operations element-wise across the array, with intelligent detection of map vs reduce operations.
|
8
|
+
|
9
|
+
## Core Mechanism
|
10
|
+
|
11
|
+
The system uses a three-stage pipeline:
|
12
|
+
|
13
|
+
1. **Parser** - Creates InputElementReference AST nodes for nested field access
|
14
|
+
2. **BroadcastDetector** - Identifies which operations should be vectorized vs scalar
|
15
|
+
3. **Compiler** - Generates appropriate map/reduce functions based on usage context
|
16
|
+
|
17
|
+
## Basic Broadcasting
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
schema do
|
21
|
+
input do
|
22
|
+
array :line_items do
|
23
|
+
float :price
|
24
|
+
integer :quantity
|
25
|
+
string :category
|
26
|
+
end
|
27
|
+
float :tax_rate, type: :float
|
28
|
+
end
|
29
|
+
|
30
|
+
# Element-wise computation - broadcasts over each item
|
31
|
+
value :subtotals, input.line_items.price * input.line_items.quantity
|
32
|
+
|
33
|
+
# Element-wise traits - applied to each item
|
34
|
+
trait :is_taxable, (input.line_items.category != "digital")
|
35
|
+
|
36
|
+
# Conditional logic - element-wise evaluation
|
37
|
+
value :taxes, fn(:if, is_taxable, subtotals * input.tax_rate, 0.0)
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
## Aggregation Operations
|
42
|
+
|
43
|
+
Operations that consume arrays to produce scalars are automatically detected:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
schema do
|
47
|
+
# These aggregate the vectorized results
|
48
|
+
value :total_subtotal, fn(:sum, subtotals)
|
49
|
+
value :total_tax, fn(:sum, taxes)
|
50
|
+
value :grand_total, total_subtotal + total_tax
|
51
|
+
|
52
|
+
# Statistics over arrays
|
53
|
+
value :avg_price, fn(:avg, input.line_items.price)
|
54
|
+
value :max_quantity, fn(:max, input.line_items.quantity)
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
## Field Access Nesting
|
59
|
+
|
60
|
+
Supports arbitrary depth field access with path building:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
schema do
|
64
|
+
input do
|
65
|
+
array :orders do
|
66
|
+
array :items do
|
67
|
+
hash :product do
|
68
|
+
string :name
|
69
|
+
float :base_price
|
70
|
+
end
|
71
|
+
integer :quantity
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Deep field access - automatically broadcasts over nested arrays
|
77
|
+
value :all_product_names, input.orders.items.product.name
|
78
|
+
value :total_values, input.orders.items.product.base_price * input.orders.items.quantity
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
## Type Inference
|
83
|
+
|
84
|
+
The type system automatically infers appropriate types for broadcasted operations:
|
85
|
+
|
86
|
+
- `input.items.price` (float array) → inferred as `:float` per element
|
87
|
+
- `input.items.price * input.items.quantity` → element-wise `:float` result
|
88
|
+
- `fn(:sum, input.items.price)` → scalar `:float` result
|
89
|
+
|
90
|
+
## Implementation Details
|
91
|
+
|
92
|
+
### Parser Layer
|
93
|
+
- **InputFieldProxy** - Handles `input.field.subfield...` with path building
|
94
|
+
- **InputElementReference** - AST node representing array field access paths
|
95
|
+
|
96
|
+
### Analysis Layer
|
97
|
+
- **BroadcastDetector** - Identifies vectorized vs scalar operations
|
98
|
+
- **TypeInferencer** - Infers types for array element access patterns
|
99
|
+
|
100
|
+
### Compilation Layer
|
101
|
+
- **Automatic Dispatch** - Maps element-wise operations to array map functions
|
102
|
+
- **Reduction Detection** - Converts aggregation functions to array reduce operations
|
103
|
+
|
104
|
+
## Usage Patterns
|
105
|
+
|
106
|
+
### Element-wise Operations
|
107
|
+
```ruby
|
108
|
+
# All of these broadcast element-wise
|
109
|
+
value :discounted_prices, input.items.price * 0.9
|
110
|
+
trait :expensive, (input.items.price > 100.0)
|
111
|
+
value :categories, input.items.category
|
112
|
+
```
|
113
|
+
|
114
|
+
### Aggregation Operations
|
115
|
+
```ruby
|
116
|
+
# These consume arrays to produce scalars
|
117
|
+
value :item_count, fn(:size, input.items)
|
118
|
+
value :total_price, fn(:sum, input.items.price)
|
119
|
+
value :has_expensive, fn(:any?, expensive)
|
120
|
+
```
|
121
|
+
|
122
|
+
### Mixed Operations
|
123
|
+
```ruby
|
124
|
+
# Element-wise computation followed by aggregation
|
125
|
+
value :line_totals, input.items.price * input.items.quantity
|
126
|
+
value :order_total, fn(:sum, line_totals)
|
127
|
+
value :avg_line_total, fn(:avg, line_totals)
|
128
|
+
```
|
129
|
+
|
130
|
+
## Error Handling
|
131
|
+
|
132
|
+
### Dimension Mismatch Detection
|
133
|
+
|
134
|
+
Array broadcasting operations are only valid within the same array source. Attempting to broadcast across different arrays generates detailed error messages:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
schema do
|
138
|
+
input do
|
139
|
+
array :items do
|
140
|
+
string :name
|
141
|
+
end
|
142
|
+
array :logs do
|
143
|
+
string :user_name
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# This will generate a dimension mismatch error
|
148
|
+
trait :same_name, input.items.name == input.logs.user_name
|
149
|
+
end
|
150
|
+
|
151
|
+
# Error:
|
152
|
+
# Cannot broadcast operation across arrays from different sources: items, logs.
|
153
|
+
# Problem: Multiple operands are arrays from different sources:
|
154
|
+
# - Operand 1 resolves to array(string) from array 'items'
|
155
|
+
# - Operand 2 resolves to array(string) from array 'logs'
|
156
|
+
# Direct operations on arrays from different sources is ambiguous and not supported.
|
157
|
+
# Vectorized operations can only work on fields from the same array input.
|
158
|
+
```
|
159
|
+
|
160
|
+
The error messages provide:
|
161
|
+
- **Quick Summary**: Identifies the conflicting array sources
|
162
|
+
- **Type Information**: Shows the resolved types of each operand
|
163
|
+
- **Clear Explanation**: Why the operation is ambiguous and not supported
|
164
|
+
|
165
|
+
## Performance Characteristics
|
166
|
+
|
167
|
+
- **Single Pass** - Each array is traversed once per computation chain
|
168
|
+
- **Lazy Evaluation** - Operations are composed into efficient pipelines
|
169
|
+
- **Memory Efficient** - No intermediate array allocations for simple operations
|
170
|
+
- **Type Safe** - Full compile-time type checking for array element operations
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Input Declarations
|
2
|
+
|
3
|
+
Declares expected inputs with types and domain constraints, separating input metadata from business logic.
|
4
|
+
|
5
|
+
## Declaration Syntax
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
schema do
|
9
|
+
input do
|
10
|
+
string :customer_name
|
11
|
+
integer :age, domain: 18..120
|
12
|
+
float :balance, domain: 0.0..Float::INFINITY
|
13
|
+
boolean :verified
|
14
|
+
array :tags, elem: { type: :string }
|
15
|
+
hash :metadata, key: { type: :string }, val: { type: :any }
|
16
|
+
any :flexible
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :adult, (input.age >= 18)
|
20
|
+
value :status, input.verified ? "verified" : "pending"
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
## Domain Constraints
|
25
|
+
|
26
|
+
**Validation occurs at runtime:**
|
27
|
+
```ruby
|
28
|
+
schema.from(credit_score: 900) # Domain: 300..850
|
29
|
+
# => InputValidationError: Field :credit_score value 900 is outside domain 300..850
|
30
|
+
```
|
31
|
+
|
32
|
+
**Constraint types:**
|
33
|
+
- Range domains: `domain: 18..120`
|
34
|
+
- Array domains: `domain: %w[active inactive]`
|
35
|
+
- Regex domains: `domain: /^[a-zA-Z]+$/`
|
36
|
+
|
37
|
+
## Validation Process
|
38
|
+
|
39
|
+
- Input data validated against declared field metadata
|
40
|
+
- Type validation checks value matches declared type
|
41
|
+
- Domain validation checks value satisfies constraints
|
42
|
+
- Detailed error messages for violations
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Performance
|
2
|
+
|
3
|
+
TODO: Add benchmark data
|
4
|
+
|
5
|
+
Processes large schemas with optimized algorithms for analysis, compilation, and execution.
|
6
|
+
|
7
|
+
## Execution Model
|
8
|
+
|
9
|
+
**Compilation:**
|
10
|
+
- Each expression compiled to executable lambda
|
11
|
+
- Direct function calls for operations
|
12
|
+
|
13
|
+
**Runtime:**
|
14
|
+
- Result caching to avoid recomputation
|
15
|
+
- Selective evaluation: only requested keys computed
|
16
|
+
- Direct lambda invocation
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Broadcasts Metadata
|
2
|
+
|
3
|
+
Array broadcasting operation analysis for vectorized computations.
|
4
|
+
|
5
|
+
## Structure
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
state[:broadcasts] = {
|
9
|
+
array_fields: Hash,
|
10
|
+
vectorized_operations: Hash,
|
11
|
+
reduction_operations: Hash
|
12
|
+
}
|
13
|
+
```
|
14
|
+
|
15
|
+
## Array Fields
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
array_fields: {
|
19
|
+
:line_items => {
|
20
|
+
element_fields: [:price, :quantity, :name],
|
21
|
+
element_types: { price: :float, quantity: :integer, name: :string }
|
22
|
+
}
|
23
|
+
}
|
24
|
+
```
|
25
|
+
|
26
|
+
## Vectorized Operations
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
vectorized_operations: {
|
30
|
+
:item_totals => {
|
31
|
+
operation: :multiply,
|
32
|
+
vectorized_args: { 0 => true, 1 => true }
|
33
|
+
}
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
## Reduction Operations
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
reduction_operations: {
|
41
|
+
:total_amount => {
|
42
|
+
function: :sum,
|
43
|
+
source: :array_field
|
44
|
+
}
|
45
|
+
}
|
46
|
+
```
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
- Compiler optimizations
|
51
|
+
- Parallel execution
|
52
|
+
- Type inference
|
53
|
+
- Performance analysis
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Cascades Metadata
|
2
|
+
|
3
|
+
Cascade mutual exclusion analysis for safe conditional cycles.
|
4
|
+
|
5
|
+
## Structure
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
state[:cascades] = {
|
9
|
+
cascade_name => {
|
10
|
+
condition_traits: Array,
|
11
|
+
condition_count: Integer,
|
12
|
+
all_mutually_exclusive: Boolean,
|
13
|
+
exclusive_pairs: Integer,
|
14
|
+
total_pairs: Integer
|
15
|
+
}
|
16
|
+
}
|
17
|
+
```
|
18
|
+
|
19
|
+
## Example
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
{
|
23
|
+
:tax_rate => {
|
24
|
+
condition_traits: [:single, :married],
|
25
|
+
condition_count: 2,
|
26
|
+
all_mutually_exclusive: true,
|
27
|
+
exclusive_pairs: 1,
|
28
|
+
total_pairs: 1
|
29
|
+
}
|
30
|
+
}
|
31
|
+
```
|
32
|
+
|
33
|
+
## Fields
|
34
|
+
|
35
|
+
- `condition_traits`: Trait names used in cascade conditions
|
36
|
+
- `all_mutually_exclusive`: Whether all condition pairs are exclusive
|
37
|
+
- `exclusive_pairs`: Count of mutually exclusive pairs
|
38
|
+
- `total_pairs`: Total possible pairs
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
- Cycle safety analysis
|
43
|
+
- Topological sorting
|
44
|
+
- Optimization detection
|
45
|
+
- Dependency validation
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Declarations Metadata
|
2
|
+
|
3
|
+
Processed declaration metadata for all schema declarations (traits and values) with clean, serializable information.
|
4
|
+
|
5
|
+
## Access
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
metadata = MySchema.schema_metadata
|
9
|
+
declarations = metadata.declarations
|
10
|
+
```
|
11
|
+
|
12
|
+
## Structure
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Returns Hash<Symbol, Hash>
|
16
|
+
{
|
17
|
+
declaration_name => {
|
18
|
+
type: :trait | :value, # Declaration type
|
19
|
+
expression: String # Human-readable expression
|
20
|
+
}
|
21
|
+
}
|
22
|
+
```
|
23
|
+
|
24
|
+
## Example
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
metadata.declarations
|
28
|
+
# => {
|
29
|
+
# :adult => { type: :trait, expression: ">=(input.age, 18)" },
|
30
|
+
# :tax_amount => { type: :value, expression: "multiply(input.income, tax_rate)" },
|
31
|
+
# :status => { type: :value, expression: "cascade" }
|
32
|
+
# }
|
33
|
+
```
|
34
|
+
|
35
|
+
## Raw AST Access
|
36
|
+
|
37
|
+
For advanced use cases requiring direct AST manipulation:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
raw_declarations = metadata.analyzer_state[:declarations]
|
41
|
+
# => { :adult => #<TraitDeclaration...>, :tax_amount => #<ValueDeclaration...> }
|
42
|
+
```
|
43
|
+
|
44
|
+
## AST Node Types
|
45
|
+
|
46
|
+
- **TraitDeclaration**: Boolean conditions
|
47
|
+
- **ValueDeclaration**: Computed values or cascades
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
- Dependency analysis
|
52
|
+
- Code generation
|
53
|
+
- AST traversal
|
54
|
+
- Type inference
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Dependencies Metadata
|
2
|
+
|
3
|
+
Processed dependency information showing relationships between declarations with clean, serializable data.
|
4
|
+
|
5
|
+
## Access
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
metadata = MySchema.schema_metadata
|
9
|
+
dependencies = metadata.dependencies
|
10
|
+
```
|
11
|
+
|
12
|
+
## Structure
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Returns Hash<Symbol, Array<Hash>>
|
16
|
+
{
|
17
|
+
declaration_name => [
|
18
|
+
{
|
19
|
+
to: Symbol, # Target declaration name
|
20
|
+
conditional: Boolean, # True if dependency is conditional (cascade branch)
|
21
|
+
cascade_owner: Symbol # Optional: cascade that owns this conditional edge
|
22
|
+
}
|
23
|
+
]
|
24
|
+
}
|
25
|
+
```
|
26
|
+
|
27
|
+
## Example
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
metadata.dependencies
|
31
|
+
# => {
|
32
|
+
# :tax_amount => [
|
33
|
+
# { to: :income, conditional: false },
|
34
|
+
# { to: :deductions, conditional: false }
|
35
|
+
# ],
|
36
|
+
# :status => [
|
37
|
+
# { to: :adult, conditional: true, cascade_owner: :status },
|
38
|
+
# { to: :verified, conditional: true, cascade_owner: :status }
|
39
|
+
# ]
|
40
|
+
# }
|
41
|
+
```
|
42
|
+
|
43
|
+
## Raw Edge Objects
|
44
|
+
|
45
|
+
For advanced use cases requiring direct Edge object access:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
raw_dependencies = metadata.analyzer_state[:dependencies]
|
49
|
+
# => { :tax_amount => [#<Edge to: :income>, #<Edge to: :deductions>] }
|
50
|
+
```
|
51
|
+
|
52
|
+
## Usage
|
53
|
+
|
54
|
+
- Topological sorting
|
55
|
+
- Cycle detection
|
56
|
+
- Evaluation planning
|
57
|
+
- Dependency visualization
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Evaluation Order Metadata
|
2
|
+
|
3
|
+
Topologically sorted order for safe declaration evaluation.
|
4
|
+
|
5
|
+
## Structure
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
state[:evaluation_order] = [:name1, :name2, :name3, ...]
|
9
|
+
```
|
10
|
+
|
11
|
+
## Example
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
[:income, :deductions, :taxable_income, :tax_rate, :tax_amount, :adult, :status]
|
15
|
+
```
|
16
|
+
|
17
|
+
## Properties
|
18
|
+
|
19
|
+
- Dependencies appear before dependents
|
20
|
+
- Handles conditional cycles in cascades
|
21
|
+
- Deterministic ordering
|
22
|
+
- Leaf nodes typically appear first
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
- Compilation order
|
27
|
+
- Evaluation sequencing
|
28
|
+
- Optimization planning
|
29
|
+
- Parallel execution grouping
|