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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +76 -174
  3. data/README.md +205 -52
  4. data/{documents → docs}/AST.md +29 -29
  5. data/{documents → docs}/SYNTAX.md +95 -8
  6. data/docs/features/README.md +45 -0
  7. data/docs/features/analysis-cascade-mutual-exclusion.md +89 -0
  8. data/docs/features/analysis-type-inference.md +42 -0
  9. data/docs/features/analysis-unsat-detection.md +71 -0
  10. data/docs/features/array-broadcasting.md +170 -0
  11. data/docs/features/input-declaration-system.md +42 -0
  12. data/docs/features/performance.md +16 -0
  13. data/docs/schema_metadata/broadcasts.md +53 -0
  14. data/docs/schema_metadata/cascades.md +45 -0
  15. data/docs/schema_metadata/declarations.md +54 -0
  16. data/docs/schema_metadata/dependencies.md +57 -0
  17. data/docs/schema_metadata/evaluation_order.md +29 -0
  18. data/docs/schema_metadata/examples.md +95 -0
  19. data/docs/schema_metadata/inferred_types.md +46 -0
  20. data/docs/schema_metadata/inputs.md +86 -0
  21. data/docs/schema_metadata.md +108 -0
  22. data/examples/federal_tax_calculator_2024.rb +11 -6
  23. data/lib/kumi/analyzer/constant_evaluator.rb +1 -1
  24. data/lib/kumi/analyzer/passes/broadcast_detector.rb +246 -0
  25. data/lib/kumi/analyzer/passes/{definition_validator.rb → declaration_validator.rb} +4 -4
  26. data/lib/kumi/analyzer/passes/dependency_resolver.rb +78 -38
  27. data/lib/kumi/analyzer/passes/input_collector.rb +91 -30
  28. data/lib/kumi/analyzer/passes/name_indexer.rb +2 -2
  29. data/lib/kumi/analyzer/passes/pass_base.rb +1 -1
  30. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +24 -25
  31. data/lib/kumi/analyzer/passes/toposorter.rb +44 -8
  32. data/lib/kumi/analyzer/passes/type_checker.rb +34 -14
  33. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +2 -2
  34. data/lib/kumi/analyzer/passes/type_inferencer.rb +130 -21
  35. data/lib/kumi/analyzer/passes/unsat_detector.rb +134 -56
  36. data/lib/kumi/analyzer/passes/visitor_pass.rb +2 -2
  37. data/lib/kumi/analyzer.rb +16 -17
  38. data/lib/kumi/compiler.rb +188 -16
  39. data/lib/kumi/constraint_relationship_solver.rb +6 -6
  40. data/lib/kumi/domain/validator.rb +0 -4
  41. data/lib/kumi/error_reporting.rb +1 -1
  42. data/lib/kumi/explain.rb +32 -20
  43. data/lib/kumi/export/node_registry.rb +26 -12
  44. data/lib/kumi/export/node_serializers.rb +1 -1
  45. data/lib/kumi/function_registry/collection_functions.rb +14 -9
  46. data/lib/kumi/function_registry/function_builder.rb +4 -3
  47. data/lib/kumi/function_registry.rb +8 -2
  48. data/lib/kumi/input/type_matcher.rb +3 -0
  49. data/lib/kumi/input/validator.rb +0 -3
  50. data/lib/kumi/json_schema/generator.rb +63 -0
  51. data/lib/kumi/json_schema/validator.rb +25 -0
  52. data/lib/kumi/json_schema.rb +14 -0
  53. data/lib/kumi/{parser → ruby_parser}/build_context.rb +1 -1
  54. data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +36 -0
  55. data/lib/kumi/{parser → ruby_parser}/dsl.rb +1 -1
  56. data/lib/kumi/{parser → ruby_parser}/dsl_cascade_builder.rb +5 -5
  57. data/lib/kumi/{parser → ruby_parser}/expression_converter.rb +20 -20
  58. data/lib/kumi/{parser → ruby_parser}/guard_rails.rb +1 -1
  59. data/lib/kumi/{parser → ruby_parser}/input_builder.rb +41 -10
  60. data/lib/kumi/ruby_parser/input_field_proxy.rb +46 -0
  61. data/lib/kumi/{parser → ruby_parser}/input_proxy.rb +4 -4
  62. data/lib/kumi/ruby_parser/nested_input.rb +15 -0
  63. data/lib/kumi/{parser → ruby_parser}/parser.rb +11 -10
  64. data/lib/kumi/{parser → ruby_parser}/schema_builder.rb +11 -10
  65. data/lib/kumi/{parser → ruby_parser}/sugar.rb +62 -10
  66. data/lib/kumi/ruby_parser.rb +10 -0
  67. data/lib/kumi/schema.rb +10 -4
  68. data/lib/kumi/schema_instance.rb +6 -6
  69. data/lib/kumi/schema_metadata.rb +524 -0
  70. data/lib/kumi/syntax/array_expression.rb +15 -0
  71. data/lib/kumi/syntax/call_expression.rb +11 -0
  72. data/lib/kumi/syntax/cascade_expression.rb +11 -0
  73. data/lib/kumi/syntax/case_expression.rb +11 -0
  74. data/lib/kumi/syntax/declaration_reference.rb +11 -0
  75. data/lib/kumi/syntax/hash_expression.rb +11 -0
  76. data/lib/kumi/syntax/input_declaration.rb +12 -0
  77. data/lib/kumi/syntax/input_element_reference.rb +12 -0
  78. data/lib/kumi/syntax/input_reference.rb +12 -0
  79. data/lib/kumi/syntax/literal.rb +11 -0
  80. data/lib/kumi/syntax/trait_declaration.rb +11 -0
  81. data/lib/kumi/syntax/value_declaration.rb +11 -0
  82. data/lib/kumi/vectorization_metadata.rb +108 -0
  83. data/lib/kumi/version.rb +1 -1
  84. data/lib/kumi.rb +14 -0
  85. metadata +55 -25
  86. data/lib/generators/trait_engine/templates/schema_spec.rb.erb +0 -27
  87. data/lib/kumi/domain.rb +0 -8
  88. data/lib/kumi/input.rb +0 -8
  89. data/lib/kumi/syntax/declarations.rb +0 -26
  90. data/lib/kumi/syntax/expressions.rb +0 -34
  91. data/lib/kumi/syntax/terminal_expressions.rb +0 -30
  92. data/lib/kumi/syntax.rb +0 -9
  93. /data/{documents → docs}/DSL.md +0 -0
  94. /data/{documents → docs}/FUNCTIONS.md +0 -0
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Kumi
6
+ module JsonSchema
7
+ # Converts Kumi schema metadata to JSON Schema format
8
+ class Generator
9
+ def initialize(schema_metadata)
10
+ @metadata = schema_metadata
11
+ end
12
+
13
+ def generate
14
+ {
15
+ type: "object",
16
+ properties: build_properties,
17
+ required: extract_required_fields,
18
+ "x-kumi-values": @metadata.values,
19
+ "x-kumi-traits": @metadata.traits
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def build_properties
26
+ @metadata.inputs.transform_values { |spec| convert_input_to_json_schema(spec) }
27
+ end
28
+
29
+ def extract_required_fields
30
+ @metadata.inputs.select { |_k, v| v[:required] }.keys
31
+ end
32
+
33
+ def convert_input_to_json_schema(input_spec)
34
+ base = { type: map_kumi_type_to_json_schema(input_spec[:type]) }
35
+
36
+ domain = input_spec[:domain]
37
+ return base unless domain
38
+
39
+ case domain[:type]
40
+ when :range
41
+ base[:minimum] = domain[:min]
42
+ base[:maximum] = domain[:max]
43
+ when :enum
44
+ base[:enum] = domain[:values]
45
+ end
46
+
47
+ base
48
+ end
49
+
50
+ def map_kumi_type_to_json_schema(kumi_type)
51
+ case kumi_type
52
+ when :string then "string"
53
+ when :integer then "integer"
54
+ when :float then "number"
55
+ when :boolean then "boolean"
56
+ when :array then "array"
57
+ when :hash then "object"
58
+ else "string"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module JsonSchema
5
+ # Validates data against JSON Schema (placeholder for future implementation)
6
+ class Validator
7
+ def initialize(json_schema)
8
+ @schema = json_schema
9
+ end
10
+
11
+ def validate(_data)
12
+ # Placeholder implementation
13
+ # In a real implementation, this would validate data against the JSON Schema
14
+ {
15
+ valid: true,
16
+ errors: []
17
+ }
18
+ end
19
+
20
+ def self.validate(json_schema, data)
21
+ new(json_schema).validate(data)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "json_schema/generator"
4
+ require_relative "json_schema/validator"
5
+
6
+ module Kumi
7
+ module JsonSchema
8
+ # Entry point for the JsonSchema functionality
9
+ #
10
+ # Available components:
11
+ # - Generator: Converts Kumi metadata to JSON Schema
12
+ # - Validator: Validates data against JSON Schema (placeholder)
13
+ end
14
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  class BuildContext
6
6
  attr_reader :inputs, :attributes, :traits
7
7
  attr_accessor :current_location
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module RubyParser
5
+ # DSL proxy for declaration references (traits and values)
6
+ # Handles references to declared items and field access on them
7
+ class DeclarationReferenceProxy
8
+ include Syntax
9
+
10
+ # Use shared operator methods
11
+ extend Sugar::ProxyRefinement
12
+
13
+ def initialize(name, context)
14
+ @name = name
15
+ @context = context
16
+ end
17
+
18
+ # Convert to DeclarationReference AST node
19
+ def to_ast_node
20
+ Kumi::Syntax::DeclarationReference.new(@name, loc: @context.current_location)
21
+ end
22
+
23
+ private
24
+
25
+ def method_missing(method_name, *args, &block)
26
+ # All operators are handled by ProxyRefinement methods
27
+ # Field access should use input.field.subfield syntax, not bare identifiers
28
+ super
29
+ end
30
+
31
+ def respond_to_missing?(_method_name, _include_private = false)
32
+ true
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  module Dsl
6
6
  def self.build_syntax_tree(&rule_block)
7
7
  parser = Parser.new
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  class DslCascadeBuilder
6
6
  include Syntax
7
7
 
@@ -88,8 +88,8 @@ module Kumi
88
88
  case name
89
89
  when Symbol
90
90
  create_binding(name, location)
91
- when Binding
92
- name # Already a binding from method_missing
91
+ when DeclarationReference
92
+ name # Already a binding from method_missing
93
93
  else
94
94
  raise_error("trait reference must be a symbol or bare identifier, got #{name.class}", location)
95
95
  end
@@ -97,7 +97,7 @@ module Kumi
97
97
  end
98
98
 
99
99
  def add_case(condition, result)
100
- @cases << WhenCaseExpression.new(condition, result)
100
+ @cases << Kumi::Syntax::CaseExpression.new(condition, result)
101
101
  end
102
102
 
103
103
  def ref(name)
@@ -129,7 +129,7 @@ module Kumi
129
129
  end
130
130
 
131
131
  def create_binding(name, location)
132
- Binding.new(name, loc: location)
132
+ Kumi::Syntax::DeclarationReference.new(name, loc: location)
133
133
  end
134
134
  end
135
135
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  # Converts Ruby objects and DSL expressions into AST nodes
6
6
  # This is the bridge between Ruby's native types and Kumi's syntax tree
7
7
  class ExpressionConverter
@@ -33,17 +33,17 @@ module Kumi
33
33
 
34
34
  # Create a reference to another declaration
35
35
  # @param name [Symbol] The name to reference
36
- # @return [Syntax::Binding] Reference node
36
+ # @return [Syntax::DeclarationReference] Reference node
37
37
  def ref(name)
38
38
  validate_reference_name(name)
39
- Binding.new(name, loc: current_location)
39
+ Kumi::Syntax::DeclarationReference.new(name, loc: current_location)
40
40
  end
41
41
 
42
42
  # Create a literal value node
43
43
  # @param value [Object] The literal value
44
44
  # @return [Syntax::Literal] Literal node
45
45
  def literal(value)
46
- Literal.new(value, loc: current_location)
46
+ Kumi::Syntax::Literal.new(value, loc: current_location)
47
47
  end
48
48
 
49
49
  # Create a function call expression
@@ -53,7 +53,7 @@ module Kumi
53
53
  def fn(fn_name, *args)
54
54
  validate_function_name(fn_name)
55
55
  expr_args = convert_arguments(args)
56
- CallExpression.new(fn_name, expr_args, loc: current_location)
56
+ Kumi::Syntax::CallExpression.new(fn_name, expr_args, loc: current_location)
57
57
  end
58
58
 
59
59
  # Access the input proxy for field references
@@ -72,12 +72,12 @@ module Kumi
72
72
  private
73
73
 
74
74
  def create_literal(value)
75
- Literal.new(value, loc: current_location)
75
+ Kumi::Syntax::Literal.new(value, loc: current_location)
76
76
  end
77
77
 
78
78
  def create_list(array)
79
79
  elements = array.map { |element| ensure_syntax(element) }
80
- ListExpression.new(elements, loc: current_location)
80
+ Kumi::Syntax::ArrayExpression.new(elements, loc: current_location)
81
81
  end
82
82
 
83
83
  def handle_custom_object(obj)
@@ -89,21 +89,21 @@ module Kumi
89
89
  end
90
90
 
91
91
  def validate_reference_name(name)
92
- unless name.is_a?(Symbol)
93
- raise_syntax_error(
94
- "Reference name must be a symbol, got #{name.class}",
95
- location: current_location
96
- )
97
- end
92
+ return if name.is_a?(Symbol)
93
+
94
+ raise_syntax_error(
95
+ "Reference name must be a symbol, got #{name.class}",
96
+ location: current_location
97
+ )
98
98
  end
99
99
 
100
100
  def validate_function_name(fn_name)
101
- unless fn_name.is_a?(Symbol)
102
- raise_syntax_error(
103
- "Function name must be a symbol, got #{fn_name.class}",
104
- location: current_location
105
- )
106
- end
101
+ return if fn_name.is_a?(Symbol)
102
+
103
+ raise_syntax_error(
104
+ "Function name must be a symbol, got #{fn_name.class}",
105
+ location: current_location
106
+ )
107
107
  end
108
108
 
109
109
  def convert_arguments(args)
@@ -123,4 +123,4 @@ module Kumi
123
123
  end
124
124
  end
125
125
  end
126
- end
126
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  module GuardRails
6
6
  RESERVED = %i[input trait value fn lit ref].freeze
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  class InputBuilder
6
6
  include Syntax
7
7
  include ErrorReporting
@@ -12,17 +12,20 @@ module Kumi
12
12
 
13
13
  def key(name, type: :any, domain: nil)
14
14
  normalized_type = normalize_type(type, name)
15
- @context.inputs << FieldDecl.new(name, domain, normalized_type, loc: @context.current_location)
15
+ @context.inputs << Kumi::Syntax::InputDeclaration.new(name, domain, normalized_type, [], loc: @context.current_location)
16
16
  end
17
17
 
18
- %i[integer float string boolean any].each do |type_name|
19
- define_method(type_name) do |name, domain: nil|
20
- @context.inputs << FieldDecl.new(name, domain, type_name, loc: @context.current_location)
18
+ %i[integer float string boolean any scalar].each do |type_name|
19
+ define_method(type_name) do |name, type: nil, domain: nil|
20
+ actual_type = type || (type_name == :scalar ? :any : type_name)
21
+ @context.inputs << Kumi::Syntax::InputDeclaration.new(name, domain, actual_type, [], loc: @context.current_location)
21
22
  end
22
23
  end
23
24
 
24
- def array(name_or_elem_type, **kwargs)
25
- if kwargs.any?
25
+ def array(name_or_elem_type, **kwargs, &block)
26
+ if block_given?
27
+ create_array_field_with_block(name_or_elem_type, kwargs, &block)
28
+ elsif kwargs.any?
26
29
  create_array_field(name_or_elem_type, kwargs)
27
30
  else
28
31
  Kumi::Types.array(name_or_elem_type)
@@ -36,7 +39,7 @@ module Kumi
36
39
  end
37
40
 
38
41
  def method_missing(method_name, *_args)
39
- allowed_methods = "'key', 'integer', 'float', 'string', 'boolean', 'any', 'array', and 'hash'"
42
+ allowed_methods = "'key', 'integer', 'float', 'string', 'boolean', 'any', 'scalar', 'array', and 'hash'"
40
43
  raise_syntax_error("Unknown method '#{method_name}' in input block. Only #{allowed_methods} are allowed.",
41
44
  location: @context.current_location)
42
45
  end
@@ -59,7 +62,7 @@ module Kumi
59
62
  elem_type = elem_spec.is_a?(Hash) && elem_spec[:type] ? elem_spec[:type] : :any
60
63
 
61
64
  array_type = create_array_type(field_name, elem_type)
62
- @context.inputs << FieldDecl.new(field_name, domain, array_type, loc: @context.current_location)
65
+ @context.inputs << Kumi::Syntax::InputDeclaration.new(field_name, domain, array_type, [], loc: @context.current_location)
63
66
  end
64
67
 
65
68
  def create_array_type(field_name, elem_type)
@@ -77,7 +80,7 @@ module Kumi
77
80
  val_type = extract_type(val_spec)
78
81
 
79
82
  hash_type = create_hash_type(field_name, key_type, val_type)
80
- @context.inputs << FieldDecl.new(field_name, domain, hash_type, loc: @context.current_location)
83
+ @context.inputs << Kumi::Syntax::InputDeclaration.new(field_name, domain, hash_type, [], loc: @context.current_location)
81
84
  end
82
85
 
83
86
  def extract_type(spec)
@@ -89,6 +92,34 @@ module Kumi
89
92
  rescue ArgumentError => e
90
93
  raise_syntax_error("Invalid types for hash `#{field_name}`: #{e.message}", location: @context.current_location)
91
94
  end
95
+
96
+ def create_array_field_with_block(field_name, options, &block)
97
+ domain = options[:domain]
98
+
99
+ # Collect children by creating a nested context
100
+ children = collect_array_children(&block)
101
+
102
+ # Create the InputDeclaration with children
103
+ @context.inputs << Kumi::Syntax::InputDeclaration.new(
104
+ field_name,
105
+ domain,
106
+ :array,
107
+ children,
108
+ loc: @context.current_location
109
+ )
110
+ end
111
+
112
+ def collect_array_children(&block)
113
+ # Create a temporary nested context to collect children
114
+ nested_inputs = []
115
+ nested_context = NestedInput.new(nested_inputs, @context.current_location)
116
+ nested_builder = InputBuilder.new(nested_context)
117
+
118
+ # Execute the block in the nested context
119
+ nested_builder.instance_eval(&block)
120
+
121
+ nested_inputs
122
+ end
92
123
  end
93
124
  end
94
125
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module RubyParser
5
+ # Proxy for input field access that can handle arbitrary depth nesting
6
+ # Handles input.field.subfield.subsubfield... syntax by building up path arrays
7
+ class InputFieldProxy
8
+ include Syntax
9
+
10
+ # Use shared operator methods instead of refinements
11
+ extend Sugar::ProxyRefinement
12
+
13
+ def initialize(path, context)
14
+ @path = Array(path) # Ensure it's always an array
15
+ @context = context
16
+ end
17
+
18
+ # Convert to appropriate AST node based on path length
19
+ def to_ast_node
20
+ if @path.length == 1
21
+ # Single field: input.field -> InputReference
22
+ Kumi::Syntax::InputReference.new(@path.first, loc: @context.current_location)
23
+ else
24
+ # Nested fields: input.field.subfield... -> InputElementReference
25
+ Kumi::Syntax::InputElementReference.new(@path, loc: @context.current_location)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def method_missing(method_name, *args, &block)
32
+ if args.empty? && block.nil?
33
+ # Extend the path: input.user.details -> InputFieldProxy([user, details])
34
+ InputFieldProxy.new(@path + [method_name], @context)
35
+ else
36
+ # Operators are now handled by ProxyRefinement methods
37
+ super
38
+ end
39
+ end
40
+
41
+ def respond_to_missing?(_method_name, _include_private = false)
42
+ true
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  # Proxy object for input field references (input.field_name)
6
6
  class InputProxy
7
7
  include Syntax
@@ -13,13 +13,13 @@ module Kumi
13
13
  private
14
14
 
15
15
  def method_missing(method_name, *_args)
16
- # Create a FieldRef node for the given method name
17
- FieldRef.new(method_name, loc: @context.current_location)
16
+ # Create InputFieldProxy that can handle further field access
17
+ InputFieldProxy.new(method_name, @context)
18
18
  end
19
19
 
20
20
  # This method is called when the user tries to access a field
21
21
  # on the input object, e.g. `input.field_name`.
22
- # It is used to create a FieldRef node in the AST.
22
+ # It is used to create an InputReference node in the AST.
23
23
 
24
24
  def respond_to_missing?(_method_name, _include_private = false)
25
25
  true # Allow any field name
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module RubyParser
5
+ # Simple context struct for nested input collection
6
+ class NestedInput
7
+ attr_reader :inputs, :current_location
8
+
9
+ def initialize(inputs_array, location)
10
+ @inputs = inputs_array
11
+ @current_location = location
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
+ # Main parser class for Ruby DSL
5
6
  class Parser
6
7
  include Syntax
7
8
  include ErrorReporting
@@ -33,15 +34,19 @@ module Kumi
33
34
  private
34
35
 
35
36
  def enable_refinements(rule_block)
36
- rule_block.binding.eval("using Kumi::Parser::Sugar::ExpressionRefinement")
37
- rule_block.binding.eval("using Kumi::Parser::Sugar::NumericRefinement")
38
- rule_block.binding.eval("using Kumi::Parser::Sugar::StringRefinement")
39
- rule_block.binding.eval("using Kumi::Parser::Sugar::ArrayRefinement")
40
- rule_block.binding.eval("using Kumi::Parser::Sugar::ModuleRefinement")
37
+ rule_block.binding.eval("using Kumi::RubyParser::Sugar::ExpressionRefinement")
38
+ rule_block.binding.eval("using Kumi::RubyParser::Sugar::NumericRefinement")
39
+ rule_block.binding.eval("using Kumi::RubyParser::Sugar::StringRefinement")
40
+ rule_block.binding.eval("using Kumi::RubyParser::Sugar::ArrayRefinement")
41
+ rule_block.binding.eval("using Kumi::RubyParser::Sugar::ModuleRefinement")
41
42
  rescue RuntimeError, NoMethodError
42
43
  # Refinements disabled in method scope - continue without them
43
44
  end
44
45
 
46
+ def build_syntax_tree
47
+ Root.new(@context.inputs, @context.attributes, @context.traits)
48
+ end
49
+
45
50
  def handle_parse_error(error)
46
51
  return unless literal_comparison_error?(error)
47
52
 
@@ -59,10 +64,6 @@ module Kumi
59
64
  def literal_comparison_error?(error)
60
65
  error.message =~ /comparison of Integer with Kumi::Syntax::/i
61
66
  end
62
-
63
- def build_syntax_tree
64
- Root.new(@context.inputs, @context.attributes, @context.traits)
65
- end
66
67
  end
67
68
  end
68
69
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- module Parser
4
+ module RubyParser
5
5
  class SchemaBuilder
6
6
  include GuardRails
7
7
  include Syntax
@@ -18,7 +18,7 @@ module Kumi
18
18
  validate_value_args(name, expr, blk)
19
19
 
20
20
  expression = blk ? build_cascade(&blk) : ensure_syntax(expr)
21
- @context.attributes << Attribute.new(name, expression, loc: @context.current_location)
21
+ @context.attributes << Kumi::Syntax::ValueDeclaration.new(name, expression, loc: @context.current_location)
22
22
  end
23
23
 
24
24
  def trait(*args, **kwargs)
@@ -40,24 +40,25 @@ module Kumi
40
40
 
41
41
  def ref(name)
42
42
  update_location
43
- Binding.new(name, loc: @context.current_location)
43
+ Kumi::Syntax::DeclarationReference.new(name, loc: @context.current_location)
44
44
  end
45
45
 
46
46
  def literal(value)
47
47
  update_location
48
- Literal.new(value, loc: @context.current_location)
48
+ Kumi::Syntax::Literal.new(value, loc: @context.current_location)
49
49
  end
50
50
 
51
51
  def fn(fn_name, *args)
52
52
  update_location
53
53
  expr_args = args.map { |a| ensure_syntax(a) }
54
- CallExpression.new(fn_name, expr_args, loc: @context.current_location)
54
+ Kumi::Syntax::CallExpression.new(fn_name, expr_args, loc: @context.current_location)
55
55
  end
56
56
 
57
57
  def method_missing(method_name, *args, &block)
58
58
  if args.empty? && !block_given?
59
59
  update_location
60
- Binding.new(method_name, loc: @context.current_location)
60
+ # Create proxy for declaration references (traits/values)
61
+ DeclarationReferenceProxy.new(method_name, @context)
61
62
  else
62
63
  super
63
64
  end
@@ -109,7 +110,7 @@ module Kumi
109
110
  name, expression = args
110
111
  validate_trait_name(name)
111
112
  expr = ensure_syntax(expression)
112
- @context.traits << Trait.new(name, expr, loc: @context.current_location)
113
+ @context.traits << Kumi::Syntax::TraitDeclaration.new(name, expr, loc: @context.current_location)
113
114
  else
114
115
  handle_deprecated_trait_syntax(args)
115
116
  end
@@ -137,8 +138,8 @@ module Kumi
137
138
  validate_operator(operator)
138
139
 
139
140
  rhs_exprs = rhs.map { |r| ensure_syntax(r) }
140
- expr = CallExpression.new(operator, [ensure_syntax(lhs)] + rhs_exprs, loc: @context.current_location)
141
- @context.traits << Trait.new(name, expr, loc: @context.current_location)
141
+ expr = Kumi::Syntax::CallExpression.new(operator, [ensure_syntax(lhs)] + rhs_exprs, loc: @context.current_location)
142
+ @context.traits << Kumi::Syntax::TraitDeclaration.new(name, expr, loc: @context.current_location)
142
143
  end
143
144
 
144
145
  def validate_trait_name(name)
@@ -161,7 +162,7 @@ module Kumi
161
162
  expression_converter = ExpressionConverter.new(@context)
162
163
  cascade_builder = DslCascadeBuilder.new(expression_converter, @context.current_location)
163
164
  cascade_builder.instance_eval(&blk)
164
- CascadeExpression.new(cascade_builder.cases, loc: @context.current_location)
165
+ Kumi::Syntax::CascadeExpression.new(cascade_builder.cases, loc: @context.current_location)
165
166
  end
166
167
 
167
168
  def ensure_syntax(obj)