kumi 0.0.0 → 0.0.4
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/.rubocop.yml +113 -3
- data/CHANGELOG.md +21 -1
- data/CLAUDE.md +387 -0
- data/README.md +270 -20
- data/docs/development/README.md +120 -0
- data/docs/development/error-reporting.md +361 -0
- data/documents/AST.md +126 -0
- data/documents/DSL.md +154 -0
- data/documents/FUNCTIONS.md +132 -0
- data/documents/SYNTAX.md +367 -0
- data/examples/deep_schema_compilation_and_evaluation_benchmark.rb +106 -0
- data/examples/federal_tax_calculator_2024.rb +112 -0
- data/examples/wide_schema_compilation_and_evaluation_benchmark.rb +80 -0
- data/lib/generators/trait_engine/templates/schema_spec.rb.erb +27 -0
- data/lib/kumi/analyzer/constant_evaluator.rb +51 -0
- data/lib/kumi/analyzer/passes/definition_validator.rb +42 -0
- data/lib/kumi/analyzer/passes/dependency_resolver.rb +71 -0
- data/lib/kumi/analyzer/passes/input_collector.rb +55 -0
- data/lib/kumi/analyzer/passes/name_indexer.rb +24 -0
- data/lib/kumi/analyzer/passes/pass_base.rb +67 -0
- data/lib/kumi/analyzer/passes/toposorter.rb +72 -0
- data/lib/kumi/analyzer/passes/type_checker.rb +139 -0
- data/lib/kumi/analyzer/passes/type_consistency_checker.rb +45 -0
- data/lib/kumi/analyzer/passes/type_inferencer.rb +125 -0
- data/lib/kumi/analyzer/passes/unsat_detector.rb +107 -0
- data/lib/kumi/analyzer/passes/visitor_pass.rb +41 -0
- data/lib/kumi/analyzer.rb +54 -0
- data/lib/kumi/atom_unsat_solver.rb +349 -0
- data/lib/kumi/compiled_schema.rb +41 -0
- data/lib/kumi/compiler.rb +127 -0
- data/lib/kumi/domain/enum_analyzer.rb +53 -0
- data/lib/kumi/domain/range_analyzer.rb +83 -0
- data/lib/kumi/domain/validator.rb +84 -0
- data/lib/kumi/domain/violation_formatter.rb +40 -0
- data/lib/kumi/domain.rb +8 -0
- data/lib/kumi/error_reporter.rb +164 -0
- data/lib/kumi/error_reporting.rb +95 -0
- data/lib/kumi/errors.rb +116 -0
- data/lib/kumi/evaluation_wrapper.rb +22 -0
- data/lib/kumi/explain.rb +281 -0
- data/lib/kumi/export/deserializer.rb +39 -0
- data/lib/kumi/export/errors.rb +12 -0
- data/lib/kumi/export/node_builders.rb +140 -0
- data/lib/kumi/export/node_registry.rb +38 -0
- data/lib/kumi/export/node_serializers.rb +156 -0
- data/lib/kumi/export/serializer.rb +23 -0
- data/lib/kumi/export.rb +33 -0
- data/lib/kumi/function_registry/collection_functions.rb +92 -0
- data/lib/kumi/function_registry/comparison_functions.rb +31 -0
- data/lib/kumi/function_registry/conditional_functions.rb +36 -0
- data/lib/kumi/function_registry/function_builder.rb +92 -0
- data/lib/kumi/function_registry/logical_functions.rb +42 -0
- data/lib/kumi/function_registry/math_functions.rb +72 -0
- data/lib/kumi/function_registry/string_functions.rb +54 -0
- data/lib/kumi/function_registry/type_functions.rb +51 -0
- data/lib/kumi/function_registry.rb +138 -0
- data/lib/kumi/input/type_matcher.rb +92 -0
- data/lib/kumi/input/validator.rb +52 -0
- data/lib/kumi/input/violation_creator.rb +50 -0
- data/lib/kumi/input.rb +8 -0
- data/lib/kumi/parser/build_context.rb +25 -0
- data/lib/kumi/parser/dsl.rb +12 -0
- data/lib/kumi/parser/dsl_cascade_builder.rb +125 -0
- data/lib/kumi/parser/expression_converter.rb +58 -0
- data/lib/kumi/parser/guard_rails.rb +43 -0
- data/lib/kumi/parser/input_builder.rb +94 -0
- data/lib/kumi/parser/input_proxy.rb +29 -0
- data/lib/kumi/parser/parser.rb +66 -0
- data/lib/kumi/parser/schema_builder.rb +172 -0
- data/lib/kumi/parser/sugar.rb +108 -0
- data/lib/kumi/schema.rb +49 -0
- data/lib/kumi/schema_instance.rb +43 -0
- data/lib/kumi/syntax/declarations.rb +23 -0
- data/lib/kumi/syntax/expressions.rb +30 -0
- data/lib/kumi/syntax/node.rb +46 -0
- data/lib/kumi/syntax/root.rb +12 -0
- data/lib/kumi/syntax/terminal_expressions.rb +27 -0
- data/lib/kumi/syntax.rb +9 -0
- data/lib/kumi/types/builder.rb +21 -0
- data/lib/kumi/types/compatibility.rb +86 -0
- data/lib/kumi/types/formatter.rb +24 -0
- data/lib/kumi/types/inference.rb +40 -0
- data/lib/kumi/types/normalizer.rb +70 -0
- data/lib/kumi/types/validator.rb +35 -0
- data/lib/kumi/types.rb +64 -0
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +7 -3
- data/scripts/generate_function_docs.rb +59 -0
- data/test_impossible_cascade.rb +51 -0
- metadata +93 -10
- data/sig/kumi.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 243e6f2e2f7f65919f41fae1eddf877796e6221674e41dc5d32a36c708efab03
|
4
|
+
data.tar.gz: 8649def0a1299dd416d00480daa9fb0dfe3b6e418c4ea6c1ad869c873ad3eedf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e677d95d1ef34f8144b1f829259ad716e8f3822ab216957cf34a0058b4f7651843232202dc485a62e4c44077761a91b4db419963b649fae4a429042bf49be410
|
7
|
+
data.tar.gz: 3e7795238333d362f35c71b9478b01f4ff396110dc517a320dc304288a1ab8deac0a3af8b99c3d8f87cfd47a9a91f1bbd46509eb0055db4faa042b9b6ca14bd9
|
data/.rubocop.yml
CHANGED
@@ -1,8 +1,118 @@
|
|
1
|
+
plugins:
|
2
|
+
- rubocop-performance
|
3
|
+
- rubocop-rspec
|
4
|
+
|
1
5
|
AllCops:
|
2
|
-
|
6
|
+
NewCops: enable
|
7
|
+
TargetRubyVersion: 3.0
|
8
|
+
SuggestExtensions: false
|
9
|
+
Exclude:
|
10
|
+
- 'bin/*'
|
11
|
+
- 'spec/fixtures/**/*'
|
12
|
+
- 'examples/**/*' # Examples may use deprecated syntax
|
13
|
+
- '*.txt'
|
14
|
+
- 'test_*.rb' # Temporary test files
|
15
|
+
- 'spec/integration/performance_spec.rb'
|
3
16
|
|
17
|
+
# Common stylistic choices
|
4
18
|
Style/StringLiterals:
|
5
19
|
EnforcedStyle: double_quotes
|
6
20
|
|
7
|
-
Style/
|
8
|
-
|
21
|
+
Style/Documentation:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/OpenStructUse:
|
25
|
+
Enabled: false # Used in AST nodes and test fixtures
|
26
|
+
|
27
|
+
# Naming conventions - allow underscores in symbols for tax codes, etc.
|
28
|
+
Naming/VariableNumber:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
# Layout
|
32
|
+
Layout/LineLength:
|
33
|
+
Max: 140
|
34
|
+
|
35
|
+
# Metrics - Reasonable limits for a DSL/compiler project
|
36
|
+
Metrics/MethodLength:
|
37
|
+
Max: 20
|
38
|
+
Exclude:
|
39
|
+
- 'lib/kumi/function_registry.rb' # Large function registry
|
40
|
+
- 'lib/kumi/function_registry/**/*' # Function registry modules with data definitions
|
41
|
+
- 'lib/kumi/analyzer/passes/**/*' # Analyzer passes often need longer methods
|
42
|
+
- 'lib/kumi/types.rb' # Type system operations
|
43
|
+
- 'lib/kumi/types/**/*' # Type system modules
|
44
|
+
- 'lib/kumi/runner.rb' # Complex runner methods
|
45
|
+
- 'spec/**/*' # Test files often need longer methods
|
46
|
+
|
47
|
+
Metrics/AbcSize:
|
48
|
+
Max: 20
|
49
|
+
Exclude:
|
50
|
+
- 'lib/kumi/function_registry.rb' # Large function registry
|
51
|
+
- 'lib/kumi/function_registry/**/*' # Function registry modules with data definitions
|
52
|
+
- 'lib/kumi/analyzer/passes/**/*' # Complex analyzer logic
|
53
|
+
- 'lib/kumi/types.rb' # Type system operations
|
54
|
+
- 'lib/kumi/types/**/*' # Type system modules
|
55
|
+
- 'lib/kumi/runner.rb' # Complex runner methods
|
56
|
+
- 'spec/**/*' # Test files often have higher ABC
|
57
|
+
|
58
|
+
Metrics/CyclomaticComplexity:
|
59
|
+
Max: 10
|
60
|
+
Exclude:
|
61
|
+
- 'lib/kumi/analyzer/passes/**/*' # Complex analyzer logic
|
62
|
+
- 'lib/kumi/types.rb' # Type system operations
|
63
|
+
- 'lib/kumi/types/**/*' # Type system modules
|
64
|
+
- 'lib/kumi/runner.rb' # Complex runner methods
|
65
|
+
|
66
|
+
Metrics/PerceivedComplexity:
|
67
|
+
Max: 10
|
68
|
+
Exclude:
|
69
|
+
- 'lib/kumi/analyzer/passes/**/*' # Complex analyzer logic
|
70
|
+
- 'lib/kumi/types.rb' # Type system operations
|
71
|
+
- 'lib/kumi/types/**/*' # Type system modules
|
72
|
+
|
73
|
+
Metrics/ParameterLists:
|
74
|
+
Max: 6
|
75
|
+
Exclude:
|
76
|
+
- 'lib/kumi/analyzer/passes/dependency_resolver.rb' # Complex dependency analysis
|
77
|
+
- 'lib/kumi/function_registry.rb' # Metadata registration
|
78
|
+
|
79
|
+
Metrics/ModuleLength:
|
80
|
+
Max: 150
|
81
|
+
Exclude:
|
82
|
+
- 'lib/kumi/function_registry.rb' # Large function registry
|
83
|
+
|
84
|
+
# Allow missing super in base classes that are designed for inheritance
|
85
|
+
Lint/MissingSuper:
|
86
|
+
Exclude:
|
87
|
+
- 'lib/kumi/analyzer/passes/pass_base.rb' # Base class for analyzer passes
|
88
|
+
- 'lib/kumi/types.rb' # Type system value objects
|
89
|
+
|
90
|
+
# RSpec-specific configuration
|
91
|
+
RSpec:
|
92
|
+
Enabled: true
|
93
|
+
|
94
|
+
RSpec/MultipleExpectations:
|
95
|
+
Enabled: false
|
96
|
+
|
97
|
+
RSpec/ExampleLength:
|
98
|
+
Max: 30
|
99
|
+
Exclude:
|
100
|
+
- 'spec/kumi/export/comprehensive_integration_spec.rb' # Comprehensive integration tests need longer examples
|
101
|
+
|
102
|
+
RSpec/MultipleMemoizedHelpers:
|
103
|
+
Max: 10
|
104
|
+
|
105
|
+
RSpec/VerifiedDoubleReference:
|
106
|
+
Enabled: false # May require significant test refactoring
|
107
|
+
|
108
|
+
RSpec/DescribeClass:
|
109
|
+
Enabled: false # Integration tests don't always test specific classes
|
110
|
+
|
111
|
+
RSpec/ContextWording:
|
112
|
+
Enabled: false # Allow flexible context naming
|
113
|
+
|
114
|
+
RSpec/ExpectActual:
|
115
|
+
Enabled: true # Keep this - it's a good practice
|
116
|
+
|
117
|
+
RSpec/IdenticalEqualityAssertion:
|
118
|
+
Enabled: false # Sometimes needed for testing type systems
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
|
3
|
+
### Changed
|
4
|
+
- **BREAKING**: Replaced `StrictCycleChecker` with `AtomUnsatSolver` for stack-safe UNSAT detection
|
5
|
+
- Refactored cycle detection to use iterative Kahn's topological sort algorithm instead of recursive DFS
|
6
|
+
- Added support for always-false comparison detection (e.g., `100 < 100`)
|
7
|
+
|
8
|
+
### Added
|
9
|
+
- **Evaluation memoization**: `EvaluationWrapper` struct with `@__schema_cache__` provides automatic caching of computed bindings
|
10
|
+
- **Massive performance improvements**: 369x-1,379x speedup on deep dependency chains through intelligent memoization
|
11
|
+
- Depth-safe UNSAT detection handles 30k+ node graphs without stack overflow
|
12
|
+
- Comprehensive test suite for large graph scenarios (acyclic ladders, cycles, mixed constraints)
|
13
|
+
- Enhanced documentation with YARD comments for `AtomUnsatSolver` module
|
14
|
+
- Extracted `StrictInequalitySolver` module for clear separation of cycle detection logic
|
15
|
+
|
16
|
+
### Performance
|
17
|
+
- **Stack-safe UNSAT detection**: Eliminates `SystemStackError` in constraint analysis for 30k+ node graphs
|
18
|
+
- **Fixed gather_atoms recursion**: Made AST traversal iterative to handle deep dependency chains
|
19
|
+
- Sub-millisecond performance on 10k-20k node constraint graphs with cycle detection
|
20
|
+
- Maintained identical UNSAT detection correctness for all existing scenarios
|
21
|
+
- **Note**: Deep schemas (2500+ dependencies) may still hit Ruby stack limits in compilation/evaluation phases
|
22
|
+
|
23
|
+
## [0.1.0] - 2025-07-01
|
4
24
|
|
5
25
|
- Initial release
|
data/CLAUDE.md
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
# CLAUDE.md
|
2
|
+
|
3
|
+
!! Remember, this gem is not on production yet, so no backward compatilibity is necessary. But do not change the public interfaces (e.g. DSL, Schema) without explicitly requested or demanded.
|
4
|
+
!! We are using zeitwerk, i.e.: no requires
|
5
|
+
!! Do not care about linter or coverage unless asked to do so.
|
6
|
+
|
7
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
8
|
+
|
9
|
+
## Project Overview
|
10
|
+
|
11
|
+
Kumi is a declarative decision-modeling compiler for Ruby that transforms complex business rules into executable dependency graphs. It features a multi-pass analyzer that validates rule interdependencies, detects cycles, infers types, and generates optimized evaluation functions. The system separates input field declarations from business logic through an explicit input block syntax.
|
12
|
+
|
13
|
+
## Development Commands
|
14
|
+
|
15
|
+
### Testing
|
16
|
+
- `bundle exec rspec` - Run all tests
|
17
|
+
- `bundle exec rspec spec/path/to/specific_spec.rb` - Run specific test file
|
18
|
+
- `bundle exec rspec spec/path/to/specific_spec.rb:123` - Run specific test at line
|
19
|
+
|
20
|
+
### Linting & Code Quality
|
21
|
+
- `bundle exec rubocop` - Run RuboCop linter
|
22
|
+
- `bundle exec rubocop -a` - Auto-fix RuboCop issues where possible
|
23
|
+
- `rake` - Run default task (includes rspec and rubocop)
|
24
|
+
|
25
|
+
### Gem Management
|
26
|
+
- `bundle install` - Install dependencies
|
27
|
+
- `gem build kumi.gemspec` - Build the gem
|
28
|
+
- `gem install ./kumi-*.gem` - Install locally built gem
|
29
|
+
|
30
|
+
## Architecture Overview
|
31
|
+
|
32
|
+
### Core Components
|
33
|
+
|
34
|
+
**Schema System** (`lib/kumi/schema.rb`):
|
35
|
+
- Entry point that ties together parsing, analysis, and compilation
|
36
|
+
- Provides the `schema(&block)` DSL method that builds the syntax tree, runs analysis, and compiles to executable form
|
37
|
+
- Generates a `Runner` instance for executing queries against input data
|
38
|
+
|
39
|
+
**Parser** (`lib/kumi/parser/`):
|
40
|
+
- `dsl.rb` - Main DSL parser that converts Ruby block syntax into AST nodes
|
41
|
+
- `dsl_builder_context.rb` - Context for building DSL elements with input/value/trait methods
|
42
|
+
- `dsl_cascade_builder.rb` - Specialized builder for cascade expressions
|
43
|
+
- `dsl_proxy.rb` - Proxy object for method delegation during parsing
|
44
|
+
- `input_dsl_proxy.rb` - Proxy for input block DSL supporting both `key` method and type-specific DSL methods
|
45
|
+
- `input_proxy.rb` - Proxy for `input.field_name` references in expressions
|
46
|
+
|
47
|
+
**Syntax Tree** (`lib/kumi/syntax/`):
|
48
|
+
- `node.rb` - Base node class with location tracking
|
49
|
+
- `root.rb` - Root schema node containing inputs, attributes, and traits
|
50
|
+
- `declarations.rb` - Attribute and trait declaration nodes
|
51
|
+
- `expressions.rb` - Expression nodes (calls, lists, cascades)
|
52
|
+
- `terminal_expressions.rb` - Terminal nodes (literals, field references, bindings, field declarations)
|
53
|
+
|
54
|
+
**Analyzer** (`lib/kumi/analyzer.rb`):
|
55
|
+
- Multi-pass analysis system that validates schemas and builds dependency graphs
|
56
|
+
- **Pass 1**: `name_indexer.rb` - Find all names, check for duplicates
|
57
|
+
- **Pass 2**: `input_collector.rb` - Collect field metadata, validate conflicts
|
58
|
+
- **Pass 3**: `definition_validator.rb` - Validate basic structure
|
59
|
+
- **Pass 4**: `dependency_resolver.rb` - Build dependency graph
|
60
|
+
- **Pass 5**: `cycle_detector.rb` - Find circular dependencies
|
61
|
+
- **Pass 6**: `toposorter.rb` - Create evaluation order
|
62
|
+
- **Pass 7**: `type_inferencer.rb` - Infer types for all declarations
|
63
|
+
- **Pass 8**: `type_checker.rb` - Validate function types and compatibility using inferred types
|
64
|
+
|
65
|
+
**Compiler** (`lib/kumi/compiler.rb`):
|
66
|
+
- Transforms analyzed syntax tree into executable lambda functions
|
67
|
+
- Maps each expression type to a compilation method
|
68
|
+
- Handles function calls via `FunctionRegistry`
|
69
|
+
- Produces `CompiledSchema` with executable bindings
|
70
|
+
|
71
|
+
**Function Registry** (`lib/kumi/function_registry.rb`):
|
72
|
+
- Registry of available functions (operators, math, string, logical, collection operations)
|
73
|
+
- Supports custom function registration with comprehensive type metadata
|
74
|
+
- Each function includes param_types, return_type, arity, and description
|
75
|
+
- Core functions include: `==`, `>`, `<`, `add`, `multiply`, `and`, `or`, `clamp`, etc.
|
76
|
+
- Maintains backward compatibility with legacy type checking system
|
77
|
+
- Function documents are generated by the script ./scripts/generate_function_docs.rb
|
78
|
+
|
79
|
+
**Runner** (`lib/kumi/runner.rb`):
|
80
|
+
- Executes compiled schemas against input data
|
81
|
+
- Provides `fetch(key)` for individual value retrieval with caching
|
82
|
+
- Provides `slice(*keys)` for batch evaluation
|
83
|
+
- Provides `explain(key)` for detailed execution tracing
|
84
|
+
|
85
|
+
**Input Validation System** (`lib/kumi/input/` and `lib/kumi/domain/`):
|
86
|
+
- `input/validator.rb` - Main validation coordinator for type and domain checking
|
87
|
+
- `input/type_matcher.rb` - Type validation logic for primitive and complex types
|
88
|
+
- `input/violation_creator.rb` - Creates standardized violation objects with detailed messages
|
89
|
+
- `domain/validator.rb` - Domain constraint validation (ranges, arrays, procs)
|
90
|
+
- `domain/range_analyzer.rb` - Range domain analysis and validation
|
91
|
+
- `domain/enum_analyzer.rb` - Enumeration domain analysis and validation
|
92
|
+
- `domain/violation_formatter.rb` - Formats domain violation error messages
|
93
|
+
|
94
|
+
### Key Patterns
|
95
|
+
|
96
|
+
**DSL Structure**:
|
97
|
+
```ruby
|
98
|
+
schema do
|
99
|
+
input do
|
100
|
+
# Recommended type-specific DSL methods
|
101
|
+
string :field_name
|
102
|
+
integer :number_field, domain: 0..100
|
103
|
+
array :scores, elem: { type: :float }
|
104
|
+
hash :metadata, key: { type: :string }, val: { type: :any }
|
105
|
+
|
106
|
+
# Fields with no declared type
|
107
|
+
any :misc_field
|
108
|
+
end
|
109
|
+
|
110
|
+
trait :name, (expression) # Boolean conditions with new syntax
|
111
|
+
value :name, expression # Computed values
|
112
|
+
value :name do # Conditional logic
|
113
|
+
on condition, result
|
114
|
+
base default_result
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
**IMPORTANT CASCADE CONDITION SYNTAX:**
|
120
|
+
In cascade expressions (`value :name do ... end`), trait references use **symbols**, not bare identifiers:
|
121
|
+
```ruby
|
122
|
+
value :status do
|
123
|
+
on :adult, "Adult Status" # ✅ Correct - use :trait_name symbol
|
124
|
+
on :verified, "Verified User"
|
125
|
+
base "Unverified"
|
126
|
+
end
|
127
|
+
|
128
|
+
# NOT this:
|
129
|
+
value :status do
|
130
|
+
on adult, "Adult Status" # ❌ Wrong - don't use bare identifier in cascade
|
131
|
+
on verified, "Verified User" # ❌ Wrong
|
132
|
+
base "Unverified"
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
**Input Block System**:
|
137
|
+
- **Required**: All schemas must have an `input` block declaring expected fields
|
138
|
+
- **Type Declarations**: Preferred via type-specific methods (e.g. `integer :field`, `string :name`, `any :field` for untyped fields)
|
139
|
+
- **Complex Types**: Use helper functions: `array(:element_type)` and `hash(:key_type, :value_type)`
|
140
|
+
- **Domain Constraints**: Fields can have domains: `integer :age, domain: 18..65` (validated at runtime)
|
141
|
+
- **Field Access**: Use `input.field_name` to reference input fields in expressions
|
142
|
+
- **Separation**: Input metadata (types, domains) is separate from business logic
|
143
|
+
|
144
|
+
**Expression Types**:
|
145
|
+
- `input.field_name` - Access input data with operator methods (>=, <=, >, <, ==, !=)
|
146
|
+
- `ref(:name)` - Reference other declarations
|
147
|
+
- `fn(:name, args...)` - Function calls
|
148
|
+
- `(expr1) & (expr2)` - Logical AND chaining
|
149
|
+
- `[element1, element2]` - Lists
|
150
|
+
- Literals (numbers, strings, booleans)
|
151
|
+
|
152
|
+
**Analysis Flow**:
|
153
|
+
1. Parse DSL → Syntax Tree
|
154
|
+
2. Analyze Syntax Tree → Analysis Result (dependency graph, type information, topo order)
|
155
|
+
3. Compile → Executable Schema
|
156
|
+
4. Execute with Runner
|
157
|
+
|
158
|
+
**Type System** (`lib/kumi/types.rb`):
|
159
|
+
- Simple symbol-based type system for clean and intuitive declaration
|
160
|
+
- **Dual Type System**: Declared types (from input blocks) and inferred types (from expressions)
|
161
|
+
- Automatic type inference for all declarations based on expression analysis
|
162
|
+
- Type primitives: `:string`, `:integer`, `:float`, `:boolean`, `:any`, `:symbol`, `:regexp`, `:time`, `:date`, `:datetime`
|
163
|
+
- Collection types: `array(:element_type)` and `hash(:key_type, :value_type)` helper functions
|
164
|
+
- Type compatibility checking and unification algorithms for numeric types
|
165
|
+
- Enhanced error messages showing type provenance (declared vs inferred)
|
166
|
+
- Legacy compatibility constants maintained for backward compatibility
|
167
|
+
|
168
|
+
### Examples Directory
|
169
|
+
|
170
|
+
The `examples/` directory contains comprehensive examples showing Kumi usage patterns:
|
171
|
+
- `cascade_demonstration.rb` - Demonstrates cascade logic with UnsatDetector fixes (working)
|
172
|
+
- `working_comprehensive_schema.rb` - Complete feature showcase (current best practices, working)
|
173
|
+
- `federal_tax_calculator_2024.rb` - Real-world tax calculation example (working)
|
174
|
+
- `tax_2024.rb` - Simple tax example with explain functionality (working)
|
175
|
+
- `wide_schema_compilation_and_evaluation_benchmark.rb` - Performance benchmark for wide schemas (compilation and evaluation scaling)
|
176
|
+
- `deep_schema_compilation_and_evaluation_benchmark.rb` - Performance benchmark for deep dependency chains (stack-safe evaluation)
|
177
|
+
- `comprehensive_god_schema.rb` - Complex example (currently has UnsatDetector semantic errors)
|
178
|
+
|
179
|
+
*Note: Some examples may use deprecated syntax and should be updated to use the new input block system.*
|
180
|
+
|
181
|
+
## Test Structure
|
182
|
+
|
183
|
+
- `spec/kumi/` - Unit tests for core components
|
184
|
+
- `spec/integration/` - Integration tests for full workflows
|
185
|
+
- `spec/fixtures/` - Test fixtures and sample schemas
|
186
|
+
- `spec/support/` - Test helpers (`ast_factory.rb`, `schema_generator.rb`)
|
187
|
+
|
188
|
+
## Key Files for Understanding
|
189
|
+
|
190
|
+
1. `lib/kumi/schema.rb` - Start here to understand the main API
|
191
|
+
2. `examples/input_block_typing_showcase.rb` - Comprehensive example of current features
|
192
|
+
3. `lib/kumi/analyzer.rb` - Core analysis pipeline with multi-pass system
|
193
|
+
4. `lib/kumi/types.rb` - Static type system implementation
|
194
|
+
5. `lib/kumi/function_registry.rb` - Available functions and extension patterns
|
195
|
+
6. `lib/kumi/analyzer/passes/type_inferencer.rb` - Type inference algorithm
|
196
|
+
7. `lib/kumi/analyzer/passes/type_checker.rb` - Type validation with enhanced error messages
|
197
|
+
8. `spec/kumi/input_block_spec.rb` - Input block syntax and behavior
|
198
|
+
9. `spec/integration/compiler_integration_spec.rb` - End-to-end test examples
|
199
|
+
10. `documents/DSL.md` - Concise DSL syntax reference
|
200
|
+
11. `documents/AST.md` - AST node types and structure reference
|
201
|
+
12. `documents/SYNTAX.md` - Comprehensive sugar vs sugar-free syntax comparison with examples
|
202
|
+
|
203
|
+
## Input Block System Details
|
204
|
+
|
205
|
+
### Required Input Blocks
|
206
|
+
- **All schemas must have an input block** - This is now mandatory
|
207
|
+
- Input blocks declare expected fields with optional type and domain constraints
|
208
|
+
- **Empty input blocks are allowed** - Use `input {}` for schemas that don't require external data
|
209
|
+
- Fields are accessed via `input.field_name` syntax (replaces deprecated `key(:field)`)
|
210
|
+
|
211
|
+
### Type System Integration
|
212
|
+
- **Declared Types**: Explicit type declarations in input blocks (e.g. `integer :field`, `string :name`, `any :field`)
|
213
|
+
- **Inferred Types**: Types automatically inferred from expression analysis
|
214
|
+
- **Type Checking**: Validates compatibility between declared and inferred types
|
215
|
+
- **Enhanced Errors**: Error messages show type provenance (declared vs inferred)
|
216
|
+
- **Helper Functions**: Use `array(:type)` and `hash(:key_type, :value_type)` for complex types
|
217
|
+
|
218
|
+
### Parser Components
|
219
|
+
- `input_dsl_proxy.rb` - Supports type-specific DSL methods (`integer`, `float`, `string`, `boolean`, `array`, `hash`, `any`)
|
220
|
+
- `input_proxy.rb` - Handles `input.field_name` references in expressions
|
221
|
+
- `input_collector.rb` - Collects and validates field metadata consistency
|
222
|
+
|
223
|
+
### Domain Constraints
|
224
|
+
- Can be declared: `integer :age, domain: 18..65`
|
225
|
+
- **Implemented**: Domain validation is active and enforced at runtime
|
226
|
+
- Supports Range domains (`18..65`), Array domains (`%w[active inactive]`), and Proc domains for custom validation
|
227
|
+
- Field metadata includes domain information and runtime validation occurs during `Schema.from()`
|
228
|
+
|
229
|
+
### Type Examples
|
230
|
+
```ruby
|
231
|
+
input do
|
232
|
+
# New type-specific DSL methods (recommended)
|
233
|
+
string :name
|
234
|
+
integer :age, domain: 18..65
|
235
|
+
hash :metadata, key: { type: :string }, val: { type: :any }
|
236
|
+
|
237
|
+
# For untyped/any fields
|
238
|
+
any :misc
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
### Trait Syntax Evolution
|
243
|
+
|
244
|
+
**Current Syntax** (recommended):
|
245
|
+
```ruby
|
246
|
+
trait :adult, (input.age >= 18)
|
247
|
+
trait :qualified, (input.age >= 21) & (input.score > 80) & (input.verified == true)
|
248
|
+
```
|
249
|
+
|
250
|
+
**Composite Trait Syntax** (NEW - bare identifier references):
|
251
|
+
```ruby
|
252
|
+
# Base traits
|
253
|
+
trait :adult, (input.age >= 18)
|
254
|
+
trait :verified, (input.verified == true)
|
255
|
+
trait :high_score, (input.score > 80)
|
256
|
+
|
257
|
+
# Composite traits using bare identifier syntax
|
258
|
+
trait :eligible, adult & verified & high_score
|
259
|
+
trait :mixed, adult & (input.income > 50_000) & verified
|
260
|
+
|
261
|
+
# Backward compatibility - both syntaxes work together
|
262
|
+
trait :legacy_mix, adult & ref(:verified) & (input.score > 90)
|
263
|
+
```
|
264
|
+
|
265
|
+
**Deprecated Syntax** (with warnings):
|
266
|
+
```ruby
|
267
|
+
trait :adult, input.age, :>=, 18 # OLD - shows deprecation warning
|
268
|
+
trait :qualified, input.age, :>=, 21, input.score # OLD - shows deprecation warning
|
269
|
+
```
|
270
|
+
|
271
|
+
**Key Changes**:
|
272
|
+
- **NEW**: Bare identifier syntax allows direct trait reference: `adult` instead of `ref(:adult)`
|
273
|
+
- New syntax uses parenthesized expressions: `trait :name, (expression)`
|
274
|
+
- FieldRef nodes have operator methods that create CallExpression nodes
|
275
|
+
- Logical AND chaining via `&` operator (Ruby limitation prevents `&&`)
|
276
|
+
- Only AND operations supported to maintain constraint satisfaction system
|
277
|
+
- **Backward Compatible**: Both `trait_name` and `ref(:trait_name)` work together
|
278
|
+
- Old syntax maintained with deprecation warnings for backward compatibility
|
279
|
+
|
280
|
+
## Common Development Tasks
|
281
|
+
|
282
|
+
### Adding New Analyzer Passes
|
283
|
+
1. Create pass class inheriting from `PassBase` in `lib/kumi/analyzer/passes/`
|
284
|
+
2. Implement `run(errors)` method that calls `set_state(key, value)` to store results
|
285
|
+
3. Add pass to `PASSES` array in `lib/kumi/analyzer.rb` in correct order
|
286
|
+
4. Consider dependencies on other passes (e.g., TypeChecker needs TypeInferencer)
|
287
|
+
|
288
|
+
### Working with AST Nodes
|
289
|
+
- All nodes include `Node` module for location tracking
|
290
|
+
- Use `spec/support/ast_factory.rb` helpers in tests
|
291
|
+
- Field declarations use `FieldDecl` nodes with name, domain, and type
|
292
|
+
- Field references use `FieldRef` nodes (from `input.field_name`) with operator methods
|
293
|
+
- FieldRef operator methods (>=, <=, >, <, ==, !=) create CallExpression nodes
|
294
|
+
- CallExpression `&` method enables logical AND chaining
|
295
|
+
|
296
|
+
### Testing Input Block Features
|
297
|
+
- See `spec/kumi/input_block_spec.rb` for comprehensive input block tests
|
298
|
+
- Use `schema_generator.rb` helper for creating test schemas
|
299
|
+
- All integration tests now require input blocks
|
300
|
+
|
301
|
+
## Architecture Design Principles
|
302
|
+
|
303
|
+
- **Multi-pass Analysis**: Each analysis pass has a single responsibility and builds on previous passes
|
304
|
+
- **Immutable Syntax Tree**: AST nodes are immutable; analysis results stored separately in analyzer state
|
305
|
+
- **Dependency-driven Evaluation**: All computation follows dependency graph to ensure correct order
|
306
|
+
- **Type Safety**: Optional but comprehensive type checking without breaking existing schemas
|
307
|
+
- **Backward Compatibility**: New features maintain compatibility with existing DSL and APIs
|
308
|
+
- **Ruby Integration**: Leverages Ruby's metaprogramming while providing structured analysis
|
309
|
+
- **Separation of Concerns**: Input metadata (types, domains) separated from business logic
|
310
|
+
- **Class Decomposition**: Large classes split into focused, single-responsibility components following RuboCop guidelines
|
311
|
+
- **Delegation Pattern**: Complex operations delegated to specialized analyzer and formatter classes
|
312
|
+
- **Unified Error Reporting**: Consistent, localized error messages throughout the system with clear interface patterns
|
313
|
+
|
314
|
+
## Code Organization Patterns
|
315
|
+
|
316
|
+
### Modular Validation Architecture
|
317
|
+
- **Coordinator Classes**: Main classes like `Input::Validator` and `Domain::Validator` coordinate but delegate complex logic
|
318
|
+
- **Specialized Analyzers**: Domain-specific classes like `RangeAnalyzer` and `EnumAnalyzer` handle specific constraint types
|
319
|
+
- **Formatter Classes**: Dedicated classes like `ViolationFormatter` handle message formatting with consistent patterns
|
320
|
+
- **Creator Classes**: Classes like `ViolationCreator` centralize object creation with standardized structure
|
321
|
+
|
322
|
+
### Testing Best Practices
|
323
|
+
- **Spec Organization**: Tests organized by component with clear separation between unit and integration tests
|
324
|
+
- **Error Variable Extraction**: RSpec patterns avoid multiline block chains by extracting error variables for assertion
|
325
|
+
- **Shared Contexts**: Use `schema_generator` and other shared contexts for consistent test setup
|
326
|
+
|
327
|
+
### RuboCop Compliance
|
328
|
+
- **Method Length**: Keep methods under 10 lines through extraction and delegation
|
329
|
+
- **Class Length**: Break classes over 100 lines into focused components
|
330
|
+
- **Complexity Metrics**: Reduce cyclomatic and ABC complexity through single-responsibility design
|
331
|
+
- **Style Consistency**: Follow Ruby style guidelines for readability and maintainability
|
332
|
+
|
333
|
+
### Error Reporting Architecture
|
334
|
+
- **Unified Interface**: Use `ErrorReporter` module and `ErrorReporting` mixin for consistent error handling
|
335
|
+
- **Location Information**: All errors must include proper file:line:column location data
|
336
|
+
- **Precise Location Tracking**: Error objects preserve location data in `.location` attribute for programmatic access
|
337
|
+
- **User Code Location**: Errors point to actual user DSL code, not internal library files
|
338
|
+
- **Backward Compatibility**: Support both legacy `[location, message]` and new `ErrorEntry` formats
|
339
|
+
- **Type Categorization**: Errors categorized as `:syntax`, `:semantic`, `:type`, `:runtime`
|
340
|
+
- **Enhanced Messaging**: Support for error suggestions, context, and similar name detection
|
341
|
+
|
342
|
+
## Development Guides and Standards
|
343
|
+
|
344
|
+
### Error Reporting Standards
|
345
|
+
**For Parser Classes**:
|
346
|
+
```ruby
|
347
|
+
class MyParser
|
348
|
+
include ErrorReporting
|
349
|
+
|
350
|
+
def parse_something
|
351
|
+
# Immediate error raising
|
352
|
+
raise_syntax_error("Invalid syntax", location: current_location)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
```
|
356
|
+
|
357
|
+
**For Analyzer Passes**:
|
358
|
+
```ruby
|
359
|
+
class MyAnalyzerPass < PassBase
|
360
|
+
def run(errors)
|
361
|
+
# Error accumulation with enhanced location
|
362
|
+
report_error(errors, "semantic error", location: node.loc, type: :semantic)
|
363
|
+
|
364
|
+
# Backward compatible method
|
365
|
+
add_error(errors, node.loc, "legacy format error")
|
366
|
+
end
|
367
|
+
end
|
368
|
+
```
|
369
|
+
### Testing Error Scenarios
|
370
|
+
- Use `spec/integration/dsl_breakage_spec.rb` patterns for comprehensive error testing
|
371
|
+
- Use `spec/integration/potential_breakage_spec.rb` for edge cases break
|
372
|
+
- Use `spec/fixtures/location_tracking_test_schema.rb` fixture for testing different syntax error types
|
373
|
+
- Test backward compatibility with existing analyzer pass specs
|
374
|
+
|
375
|
+
### Key Development Files
|
376
|
+
12. `lib/kumi/error_reporter.rb` - Central error reporting functionality
|
377
|
+
13. `lib/kumi/error_reporting.rb` - Mixin for consistent error interfaces
|
378
|
+
14. `spec/integration/location_tracking_spec.rb` - Comprehensive tests for error location accuracy
|
379
|
+
15. `spec/fixtures/location_tracking_test_schema.rb` - Fixture with intentional syntax errors for location testing
|
380
|
+
16. `ERROR_REPORTING_INTERFACE.md` - Detailed error reporting implementation guide
|
381
|
+
17. `ON_ERRORS.md` - Comprehensive analysis of DSL breakage scenarios and error quality
|
382
|
+
18. `spec/integration/dsl_breakage_spec.rb` - Integration tests for all DSL breakage scenarios
|
383
|
+
19. `spec/integration/potential_breakage_spec.rb` - Edge cases that should break but might not
|
384
|
+
20. `docs/development/README.md` - Development guides directory index
|
385
|
+
21. `docs/development/error-reporting.md` - Comprehensive error reporting standards and patterns
|
386
|
+
|
387
|
+
#
|