kumi 0.0.6 → 0.0.8
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 +34 -177
- data/README.md +41 -7
- data/docs/SYNTAX.md +2 -7
- data/docs/features/array-broadcasting.md +1 -1
- 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/game_of_life.rb +1 -1
- data/examples/static_analysis_errors.rb +7 -7
- data/lib/kumi/analyzer.rb +20 -20
- data/lib/kumi/compiler.rb +44 -50
- data/lib/kumi/core/analyzer/analysis_state.rb +39 -0
- data/lib/kumi/core/analyzer/constant_evaluator.rb +59 -0
- data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +248 -0
- data/lib/kumi/core/analyzer/passes/declaration_validator.rb +45 -0
- data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +153 -0
- data/lib/kumi/core/analyzer/passes/input_collector.rb +139 -0
- data/lib/kumi/core/analyzer/passes/name_indexer.rb +26 -0
- data/lib/kumi/core/analyzer/passes/pass_base.rb +52 -0
- data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +111 -0
- data/lib/kumi/core/analyzer/passes/toposorter.rb +110 -0
- data/lib/kumi/core/analyzer/passes/type_checker.rb +162 -0
- data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +48 -0
- data/lib/kumi/core/analyzer/passes/type_inferencer.rb +236 -0
- data/lib/kumi/core/analyzer/passes/unsat_detector.rb +406 -0
- data/lib/kumi/core/analyzer/passes/visitor_pass.rb +44 -0
- data/lib/kumi/core/atom_unsat_solver.rb +396 -0
- data/lib/kumi/core/compiled_schema.rb +43 -0
- data/lib/kumi/core/constraint_relationship_solver.rb +641 -0
- data/lib/kumi/core/domain/enum_analyzer.rb +55 -0
- data/lib/kumi/core/domain/range_analyzer.rb +85 -0
- data/lib/kumi/core/domain/validator.rb +82 -0
- data/lib/kumi/core/domain/violation_formatter.rb +42 -0
- data/lib/kumi/core/error_reporter.rb +166 -0
- data/lib/kumi/core/error_reporting.rb +97 -0
- data/lib/kumi/core/errors.rb +120 -0
- data/lib/kumi/core/evaluation_wrapper.rb +40 -0
- data/lib/kumi/core/explain.rb +295 -0
- data/lib/kumi/core/export/deserializer.rb +41 -0
- data/lib/kumi/core/export/errors.rb +14 -0
- data/lib/kumi/core/export/node_builders.rb +142 -0
- data/lib/kumi/core/export/node_registry.rb +54 -0
- data/lib/kumi/core/export/node_serializers.rb +158 -0
- data/lib/kumi/core/export/serializer.rb +25 -0
- data/lib/kumi/core/export.rb +35 -0
- data/lib/kumi/core/function_registry/collection_functions.rb +202 -0
- data/lib/kumi/core/function_registry/comparison_functions.rb +33 -0
- data/lib/kumi/core/function_registry/conditional_functions.rb +38 -0
- data/lib/kumi/core/function_registry/function_builder.rb +95 -0
- data/lib/kumi/core/function_registry/logical_functions.rb +44 -0
- data/lib/kumi/core/function_registry/math_functions.rb +74 -0
- data/lib/kumi/core/function_registry/string_functions.rb +57 -0
- data/lib/kumi/core/function_registry/type_functions.rb +53 -0
- data/lib/kumi/{function_registry.rb → core/function_registry.rb} +28 -36
- data/lib/kumi/core/input/type_matcher.rb +97 -0
- data/lib/kumi/core/input/validator.rb +51 -0
- data/lib/kumi/core/input/violation_creator.rb +52 -0
- data/lib/kumi/core/json_schema/generator.rb +65 -0
- data/lib/kumi/core/json_schema/validator.rb +27 -0
- data/lib/kumi/core/json_schema.rb +16 -0
- data/lib/kumi/core/ruby_parser/build_context.rb +27 -0
- data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +38 -0
- data/lib/kumi/core/ruby_parser/dsl.rb +14 -0
- data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +138 -0
- data/lib/kumi/core/ruby_parser/expression_converter.rb +128 -0
- data/lib/kumi/core/ruby_parser/guard_rails.rb +45 -0
- data/lib/kumi/core/ruby_parser/input_builder.rb +127 -0
- data/lib/kumi/core/ruby_parser/input_field_proxy.rb +48 -0
- data/lib/kumi/core/ruby_parser/input_proxy.rb +31 -0
- data/lib/kumi/core/ruby_parser/nested_input.rb +17 -0
- data/lib/kumi/core/ruby_parser/parser.rb +71 -0
- data/lib/kumi/core/ruby_parser/schema_builder.rb +175 -0
- data/lib/kumi/core/ruby_parser/sugar.rb +263 -0
- data/lib/kumi/core/ruby_parser.rb +12 -0
- data/lib/kumi/core/schema_instance.rb +111 -0
- data/lib/kumi/core/types/builder.rb +23 -0
- data/lib/kumi/core/types/compatibility.rb +96 -0
- data/lib/kumi/core/types/formatter.rb +26 -0
- data/lib/kumi/core/types/inference.rb +42 -0
- data/lib/kumi/core/types/normalizer.rb +72 -0
- data/lib/kumi/core/types/validator.rb +37 -0
- data/lib/kumi/core/types.rb +66 -0
- data/lib/kumi/core/vectorization_metadata.rb +110 -0
- data/lib/kumi/errors.rb +1 -112
- data/lib/kumi/registry.rb +37 -0
- data/lib/kumi/schema.rb +13 -7
- data/lib/kumi/schema_metadata.rb +524 -0
- data/lib/kumi/syntax/array_expression.rb +6 -6
- data/lib/kumi/syntax/call_expression.rb +4 -4
- data/lib/kumi/syntax/cascade_expression.rb +4 -4
- data/lib/kumi/syntax/case_expression.rb +4 -4
- data/lib/kumi/syntax/declaration_reference.rb +4 -4
- data/lib/kumi/syntax/hash_expression.rb +4 -4
- data/lib/kumi/syntax/input_declaration.rb +5 -5
- data/lib/kumi/syntax/input_element_reference.rb +5 -5
- data/lib/kumi/syntax/input_reference.rb +5 -5
- data/lib/kumi/syntax/literal.rb +4 -4
- data/lib/kumi/syntax/node.rb +34 -34
- data/lib/kumi/syntax/root.rb +6 -6
- data/lib/kumi/syntax/trait_declaration.rb +4 -4
- data/lib/kumi/syntax/value_declaration.rb +4 -4
- data/lib/kumi/version.rb +1 -1
- data/lib/kumi.rb +14 -0
- data/migrate_to_core_iterative.rb +938 -0
- data/scripts/generate_function_docs.rb +9 -9
- metadata +85 -69
- data/lib/generators/trait_engine/templates/schema_spec.rb.erb +0 -27
- data/lib/kumi/analyzer/analysis_state.rb +0 -37
- data/lib/kumi/analyzer/constant_evaluator.rb +0 -57
- data/lib/kumi/analyzer/passes/broadcast_detector.rb +0 -251
- data/lib/kumi/analyzer/passes/declaration_validator.rb +0 -43
- data/lib/kumi/analyzer/passes/dependency_resolver.rb +0 -151
- data/lib/kumi/analyzer/passes/input_collector.rb +0 -137
- data/lib/kumi/analyzer/passes/name_indexer.rb +0 -24
- data/lib/kumi/analyzer/passes/pass_base.rb +0 -50
- data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +0 -110
- data/lib/kumi/analyzer/passes/toposorter.rb +0 -108
- data/lib/kumi/analyzer/passes/type_checker.rb +0 -162
- data/lib/kumi/analyzer/passes/type_consistency_checker.rb +0 -46
- data/lib/kumi/analyzer/passes/type_inferencer.rb +0 -232
- data/lib/kumi/analyzer/passes/unsat_detector.rb +0 -406
- data/lib/kumi/analyzer/passes/visitor_pass.rb +0 -42
- data/lib/kumi/atom_unsat_solver.rb +0 -394
- data/lib/kumi/compiled_schema.rb +0 -41
- data/lib/kumi/constraint_relationship_solver.rb +0 -638
- data/lib/kumi/domain/enum_analyzer.rb +0 -53
- data/lib/kumi/domain/range_analyzer.rb +0 -83
- data/lib/kumi/domain/validator.rb +0 -80
- data/lib/kumi/domain/violation_formatter.rb +0 -40
- data/lib/kumi/error_reporter.rb +0 -164
- data/lib/kumi/error_reporting.rb +0 -95
- data/lib/kumi/evaluation_wrapper.rb +0 -38
- data/lib/kumi/explain.rb +0 -281
- data/lib/kumi/export/deserializer.rb +0 -39
- data/lib/kumi/export/errors.rb +0 -12
- data/lib/kumi/export/node_builders.rb +0 -140
- data/lib/kumi/export/node_registry.rb +0 -52
- data/lib/kumi/export/node_serializers.rb +0 -156
- data/lib/kumi/export/serializer.rb +0 -23
- data/lib/kumi/export.rb +0 -33
- data/lib/kumi/function_registry/collection_functions.rb +0 -200
- data/lib/kumi/function_registry/comparison_functions.rb +0 -31
- data/lib/kumi/function_registry/conditional_functions.rb +0 -36
- data/lib/kumi/function_registry/function_builder.rb +0 -93
- data/lib/kumi/function_registry/logical_functions.rb +0 -42
- data/lib/kumi/function_registry/math_functions.rb +0 -72
- data/lib/kumi/function_registry/string_functions.rb +0 -54
- data/lib/kumi/function_registry/type_functions.rb +0 -51
- data/lib/kumi/input/type_matcher.rb +0 -95
- data/lib/kumi/input/validator.rb +0 -49
- data/lib/kumi/input/violation_creator.rb +0 -50
- data/lib/kumi/parser/build_context.rb +0 -25
- data/lib/kumi/parser/declaration_reference_proxy.rb +0 -36
- data/lib/kumi/parser/dsl.rb +0 -12
- data/lib/kumi/parser/dsl_cascade_builder.rb +0 -136
- data/lib/kumi/parser/expression_converter.rb +0 -126
- data/lib/kumi/parser/guard_rails.rb +0 -43
- data/lib/kumi/parser/input_builder.rb +0 -125
- data/lib/kumi/parser/input_field_proxy.rb +0 -46
- data/lib/kumi/parser/input_proxy.rb +0 -29
- data/lib/kumi/parser/nested_input.rb +0 -15
- data/lib/kumi/parser/parser.rb +0 -68
- data/lib/kumi/parser/schema_builder.rb +0 -173
- data/lib/kumi/parser/sugar.rb +0 -261
- data/lib/kumi/schema_instance.rb +0 -109
- data/lib/kumi/types/builder.rb +0 -21
- data/lib/kumi/types/compatibility.rb +0 -94
- data/lib/kumi/types/formatter.rb +0 -24
- data/lib/kumi/types/inference.rb +0 -40
- data/lib/kumi/types/normalizer.rb +0 -70
- data/lib/kumi/types/validator.rb +0 -35
- data/lib/kumi/types.rb +0 -64
- data/lib/kumi/vectorization_metadata.rb +0 -108
@@ -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
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Schema Metadata Examples
|
2
|
+
|
3
|
+
For comprehensive API documentation with detailed examples, see the YARD documentation in the SchemaMetadata class.
|
4
|
+
|
5
|
+
## Basic Usage
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class TaxSchema
|
9
|
+
extend Kumi::Schema
|
10
|
+
|
11
|
+
schema do
|
12
|
+
input do
|
13
|
+
integer :income, domain: 0..1_000_000
|
14
|
+
string :filing_status, domain: %w[single married]
|
15
|
+
integer :age, domain: 18..100
|
16
|
+
end
|
17
|
+
|
18
|
+
trait :adult, (input.age >= 18)
|
19
|
+
trait :high_income, (input.income > 100_000)
|
20
|
+
|
21
|
+
value :tax_rate do
|
22
|
+
on high_income, 0.25
|
23
|
+
base 0.15
|
24
|
+
end
|
25
|
+
|
26
|
+
value :tax_amount, input.income * tax_rate
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Access schema metadata - clean object interface!
|
31
|
+
metadata = TaxSchema.schema_metadata
|
32
|
+
|
33
|
+
# Processed semantic metadata (rich, transformed from AST)
|
34
|
+
puts metadata.inputs
|
35
|
+
# => { :income => { type: :integer, domain: {...}, required: true }, ... }
|
36
|
+
|
37
|
+
puts metadata.values
|
38
|
+
# => { :tax_rate => { type: :float, cascade: {...} }, ... }
|
39
|
+
|
40
|
+
puts metadata.traits
|
41
|
+
# => { :adult => { type: :boolean, condition: "input.age >= 18" }, ... }
|
42
|
+
|
43
|
+
# Raw analyzer state (direct from analysis passes)
|
44
|
+
puts metadata.evaluation_order
|
45
|
+
# => [:adult, :high_income, :tax_rate, :tax_amount]
|
46
|
+
|
47
|
+
puts metadata.dependencies
|
48
|
+
# => { :tax_amount => [#<Edge to: :tax_rate>, #<Edge to: :income>], ... }
|
49
|
+
|
50
|
+
puts metadata.inferred_types
|
51
|
+
# => { :adult => :boolean, :tax_rate => :float, :tax_amount => :float }
|
52
|
+
|
53
|
+
# Serializable processed hash
|
54
|
+
processed_hash = metadata.to_h
|
55
|
+
puts processed_hash.keys
|
56
|
+
# => [:inputs, :values, :traits, :functions]
|
57
|
+
|
58
|
+
# Raw analyzer state (contains AST nodes)
|
59
|
+
raw_state = metadata.analyzer_state
|
60
|
+
puts raw_state.keys
|
61
|
+
# => [:declarations, :inputs, :dependencies, :dependents, :leaves, :evaluation_order, :inferred_types, :cascades, :broadcasts]
|
62
|
+
```
|
63
|
+
|
64
|
+
## Tool Integration
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# Form generator example
|
68
|
+
def generate_form_fields(schema_class)
|
69
|
+
metadata = schema_class.schema_metadata
|
70
|
+
|
71
|
+
metadata.inputs.map do |field_name, field_info|
|
72
|
+
case field_info[:type]
|
73
|
+
when :integer
|
74
|
+
create_number_input(field_name, field_info[:domain])
|
75
|
+
when :string
|
76
|
+
create_select_input(field_name, field_info[:domain])
|
77
|
+
when :boolean
|
78
|
+
create_checkbox_input(field_name)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Dependency analysis example
|
84
|
+
def analyze_field_dependencies(schema_class, field_name)
|
85
|
+
metadata = schema_class.schema_metadata
|
86
|
+
|
87
|
+
# Find what depends on this field
|
88
|
+
dependents = metadata.dependents[field_name] || []
|
89
|
+
|
90
|
+
# Find what this field depends on
|
91
|
+
dependencies = metadata.dependencies[field_name]&.map(&:to) || []
|
92
|
+
|
93
|
+
{ affects: dependents, requires: dependencies }
|
94
|
+
end
|
95
|
+
```
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Inferred Types Metadata
|
2
|
+
|
3
|
+
Type inference results for all declarations based on expression analysis.
|
4
|
+
|
5
|
+
## Access
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
metadata = MySchema.schema_metadata
|
9
|
+
types = metadata.inferred_types
|
10
|
+
```
|
11
|
+
|
12
|
+
## Structure
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Returns Hash<Symbol, Object>
|
16
|
+
{
|
17
|
+
declaration_name => type_specification
|
18
|
+
}
|
19
|
+
```
|
20
|
+
|
21
|
+
## Example
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
metadata.inferred_types
|
25
|
+
# => {
|
26
|
+
# :adult => :boolean,
|
27
|
+
# :age_group => :string,
|
28
|
+
# :tax_rate => :float,
|
29
|
+
# :count => :integer,
|
30
|
+
# :item_prices => { array: :float },
|
31
|
+
# :categories => { array: :string }
|
32
|
+
# }
|
33
|
+
```
|
34
|
+
|
35
|
+
## Type Values
|
36
|
+
|
37
|
+
- `:boolean`, `:string`, `:integer`, `:float`, `:any`
|
38
|
+
- `{ array: element_type }` for arrays
|
39
|
+
- `{ hash: { key: key_type, value: value_type } }` for hashes
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
- Type checking
|
44
|
+
- Code generation
|
45
|
+
- Editor support
|
46
|
+
- Runtime validation
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Input Metadata
|
2
|
+
|
3
|
+
Raw input field metadata extracted from `input` blocks during analysis.
|
4
|
+
|
5
|
+
## Access
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
metadata = MySchema.schema_metadata
|
9
|
+
|
10
|
+
# Processed input metadata (recommended for tools)
|
11
|
+
inputs = metadata.inputs
|
12
|
+
|
13
|
+
# Raw input metadata (advanced usage)
|
14
|
+
raw_inputs = metadata.analyzer_state[:inputs]
|
15
|
+
```
|
16
|
+
|
17
|
+
## Raw Structure
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# Raw analyzer state format
|
21
|
+
{
|
22
|
+
field_name => {
|
23
|
+
type: Symbol, # :integer, :string, :float, :boolean, :array, etc.
|
24
|
+
domain: Range|Array, # optional domain constraints
|
25
|
+
children: Hash # for array/hash types
|
26
|
+
}
|
27
|
+
}
|
28
|
+
```
|
29
|
+
|
30
|
+
## Processed Structure
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# Processed metadata format (via metadata.inputs)
|
34
|
+
{
|
35
|
+
field_name => {
|
36
|
+
type: Symbol, # normalized type
|
37
|
+
domain: Hash, # normalized domain metadata
|
38
|
+
required: Boolean # always true currently
|
39
|
+
}
|
40
|
+
}
|
41
|
+
```
|
42
|
+
|
43
|
+
## Examples
|
44
|
+
|
45
|
+
**Processed Input Metadata:**
|
46
|
+
```ruby
|
47
|
+
metadata.inputs
|
48
|
+
# => {
|
49
|
+
# :age => {
|
50
|
+
# type: :integer,
|
51
|
+
# domain: { type: :range, min: 0, max: 120, exclusive_end: false },
|
52
|
+
# required: true
|
53
|
+
# },
|
54
|
+
# :name => { type: :string, required: true },
|
55
|
+
# :active => { type: :boolean, required: true }
|
56
|
+
# }
|
57
|
+
```
|
58
|
+
|
59
|
+
**Raw Input Metadata:**
|
60
|
+
```ruby
|
61
|
+
metadata.analyzer_state[:inputs]
|
62
|
+
# => {
|
63
|
+
# :age => { type: :integer, domain: 0..120 },
|
64
|
+
# :name => { type: :string },
|
65
|
+
# :line_items => {
|
66
|
+
# type: :array,
|
67
|
+
# children: {
|
68
|
+
# :price => { type: :float, domain: 0..Float::INFINITY },
|
69
|
+
# :quantity => { type: :integer, domain: 1..100 }
|
70
|
+
# }
|
71
|
+
# }
|
72
|
+
# }
|
73
|
+
```
|
74
|
+
|
75
|
+
**Domain Types:**
|
76
|
+
- Range: `18..65`, `0..Float::INFINITY`
|
77
|
+
- Array: `%w[active inactive suspended]`
|
78
|
+
- Proc: Custom validation functions
|
79
|
+
|
80
|
+
## Usage
|
81
|
+
|
82
|
+
Form generators use this metadata to:
|
83
|
+
- Create appropriate input controls
|
84
|
+
- Set validation rules
|
85
|
+
- Build nested forms for arrays
|
86
|
+
- Generate type-safe schemas
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# Schema Metadata
|
2
|
+
|
3
|
+
Kumi's SchemaMetadata interface provides structured access to analyzed schema information for building external tools like form generators, documentation systems, and analysis utilities.
|
4
|
+
|
5
|
+
## Primary Interface
|
6
|
+
|
7
|
+
SchemaMetadata is the main interface for extracting metadata from Kumi schemas:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
metadata = MySchema.schema_metadata
|
11
|
+
```
|
12
|
+
|
13
|
+
See the comprehensive API documentation in the SchemaMetadata class for detailed method documentation, examples, and usage patterns.
|
14
|
+
|
15
|
+
## Processed Metadata (Tool-Friendly)
|
16
|
+
|
17
|
+
These methods return clean, serializable data structures:
|
18
|
+
|
19
|
+
| Method | Returns | Description |
|
20
|
+
|--------|---------|-------------|
|
21
|
+
| `inputs` | Hash | Input field metadata with normalized types and domains |
|
22
|
+
| `values` | Hash | Value declarations with dependencies and expressions |
|
23
|
+
| `traits` | Hash | Trait conditions with dependency information |
|
24
|
+
| `functions` | Hash | Function registry info for functions used in schema |
|
25
|
+
| `to_h` | Hash | Complete processed metadata (inputs, values, traits, functions) |
|
26
|
+
| `to_json` | String | JSON serialization of processed metadata |
|
27
|
+
| `to_json_schema` | Hash | JSON Schema document for input validation |
|
28
|
+
|
29
|
+
## Raw Analyzer State (Advanced)
|
30
|
+
|
31
|
+
Direct access to internal analyzer results:
|
32
|
+
|
33
|
+
| Method | Returns | Description |
|
34
|
+
|--------|---------|-------------|
|
35
|
+
| [`declarations`](schema_metadata/declarations.md) | Hash | Raw AST declaration nodes by name |
|
36
|
+
| [`dependencies`](schema_metadata/dependencies.md) | Hash | Dependency graph with Edge objects |
|
37
|
+
| `dependents` | Hash | Reverse dependency lookup |
|
38
|
+
| `leaves` | Hash | Leaf nodes (no dependencies) by type |
|
39
|
+
| [`evaluation_order`](schema_metadata/evaluation_order.md) | Array | Topologically sorted evaluation order |
|
40
|
+
| [`inferred_types`](schema_metadata/inferred_types.md) | Hash | Type inference results for declarations |
|
41
|
+
| [`cascades`](schema_metadata/cascades.md) | Hash | Cascade mutual exclusion analysis |
|
42
|
+
| [`broadcasts`](schema_metadata/broadcasts.md) | Hash | Array broadcasting operation metadata |
|
43
|
+
| `analyzer_state` | Hash | Complete raw analyzer state with AST nodes |
|
44
|
+
|
45
|
+
Note: Raw `inputs` metadata is available via `analyzer_state[:inputs]` but the processed `inputs` method is recommended for tool development.
|
46
|
+
|
47
|
+
## Usage Patterns
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Tool development - use processed metadata
|
51
|
+
metadata = MySchema.schema_metadata
|
52
|
+
form_fields = metadata.inputs.map { |name, info| create_field(name, info) }
|
53
|
+
documentation = metadata.values.map { |name, info| document_value(name, info) }
|
54
|
+
|
55
|
+
# Advanced analysis - use raw state when needed
|
56
|
+
dependency_graph = metadata.dependencies
|
57
|
+
ast_nodes = metadata.declarations
|
58
|
+
evaluation_sequence = metadata.evaluation_order
|
59
|
+
```
|
60
|
+
|
61
|
+
## Data Structure Examples
|
62
|
+
|
63
|
+
### Processed Input Metadata
|
64
|
+
```ruby
|
65
|
+
metadata.inputs
|
66
|
+
# => {
|
67
|
+
# :age => { type: :integer, domain: { type: :range, min: 18, max: 65 }, required: true },
|
68
|
+
# :name => { type: :string, required: true },
|
69
|
+
# :items => { type: :array, required: true }
|
70
|
+
# }
|
71
|
+
```
|
72
|
+
|
73
|
+
### Processed Value Metadata
|
74
|
+
```ruby
|
75
|
+
metadata.values
|
76
|
+
# => {
|
77
|
+
# :tax_amount => {
|
78
|
+
# type: :float,
|
79
|
+
# dependencies: [:income, :tax_rate],
|
80
|
+
# computed: true,
|
81
|
+
# expression: "multiply(input.income, tax_rate)"
|
82
|
+
# }
|
83
|
+
# }
|
84
|
+
```
|
85
|
+
|
86
|
+
### Clean Public Interface Examples
|
87
|
+
```ruby
|
88
|
+
# Processed dependency information (clean hashes)
|
89
|
+
metadata.dependencies
|
90
|
+
# => { :tax_amount => [{ to: :income, conditional: false }, { to: :tax_rate, conditional: false }] }
|
91
|
+
|
92
|
+
# Processed declaration metadata (clean hashes)
|
93
|
+
metadata.declarations
|
94
|
+
# => { :adult => { type: :trait, expression: ">=(input.age, 18)" }, :tax_amount => { type: :value, expression: "multiply(input.income, tax_rate)" } }
|
95
|
+
|
96
|
+
# Type inference results (clean data)
|
97
|
+
metadata.inferred_types
|
98
|
+
# => { :adult => :boolean, :tax_amount => :float, :item_totals => { array: :float } }
|
99
|
+
```
|
100
|
+
|
101
|
+
### Raw Analyzer State (Advanced Usage)
|
102
|
+
```ruby
|
103
|
+
# Complete raw state hash with internal objects (AST nodes, Edge objects)
|
104
|
+
metadata.analyzer_state
|
105
|
+
# => { declarations: {AST nodes...}, dependencies: {Edge objects...}, ... }
|
106
|
+
```
|
107
|
+
|
108
|
+
See `docs/schema_metadata/` for detailed examples.
|
data/examples/game_of_life.rb
CHANGED
@@ -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
|
@@ -52,17 +52,17 @@ module Kumi
|
|
52
52
|
|
53
53
|
def self.create_analysis_result(state)
|
54
54
|
Result.new(
|
55
|
-
definitions: state[:
|
56
|
-
dependency_graph: state[:
|
57
|
-
leaf_map: state[:
|
58
|
-
topo_order: state[:
|
59
|
-
decl_types: state[:
|
55
|
+
definitions: state[:declarations],
|
56
|
+
dependency_graph: state[:dependencies],
|
57
|
+
leaf_map: state[:leaves],
|
58
|
+
topo_order: state[:evaluation_order],
|
59
|
+
decl_types: state[:inferred_types],
|
60
60
|
state: state.to_h
|
61
61
|
)
|
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")
|