kumi 0.0.7 → 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.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +1 -1
  3. data/README.md +8 -5
  4. data/examples/game_of_life.rb +1 -1
  5. data/examples/static_analysis_errors.rb +7 -7
  6. data/lib/kumi/analyzer.rb +15 -15
  7. data/lib/kumi/compiler.rb +6 -6
  8. data/lib/kumi/core/analyzer/analysis_state.rb +39 -0
  9. data/lib/kumi/core/analyzer/constant_evaluator.rb +59 -0
  10. data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +248 -0
  11. data/lib/kumi/core/analyzer/passes/declaration_validator.rb +45 -0
  12. data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +153 -0
  13. data/lib/kumi/core/analyzer/passes/input_collector.rb +139 -0
  14. data/lib/kumi/core/analyzer/passes/name_indexer.rb +26 -0
  15. data/lib/kumi/core/analyzer/passes/pass_base.rb +52 -0
  16. data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +111 -0
  17. data/lib/kumi/core/analyzer/passes/toposorter.rb +110 -0
  18. data/lib/kumi/core/analyzer/passes/type_checker.rb +162 -0
  19. data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +48 -0
  20. data/lib/kumi/core/analyzer/passes/type_inferencer.rb +236 -0
  21. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +406 -0
  22. data/lib/kumi/core/analyzer/passes/visitor_pass.rb +44 -0
  23. data/lib/kumi/core/atom_unsat_solver.rb +396 -0
  24. data/lib/kumi/core/compiled_schema.rb +43 -0
  25. data/lib/kumi/core/constraint_relationship_solver.rb +641 -0
  26. data/lib/kumi/core/domain/enum_analyzer.rb +55 -0
  27. data/lib/kumi/core/domain/range_analyzer.rb +85 -0
  28. data/lib/kumi/core/domain/validator.rb +82 -0
  29. data/lib/kumi/core/domain/violation_formatter.rb +42 -0
  30. data/lib/kumi/core/error_reporter.rb +166 -0
  31. data/lib/kumi/core/error_reporting.rb +97 -0
  32. data/lib/kumi/core/errors.rb +120 -0
  33. data/lib/kumi/core/evaluation_wrapper.rb +40 -0
  34. data/lib/kumi/core/explain.rb +295 -0
  35. data/lib/kumi/core/export/deserializer.rb +41 -0
  36. data/lib/kumi/core/export/errors.rb +14 -0
  37. data/lib/kumi/core/export/node_builders.rb +142 -0
  38. data/lib/kumi/core/export/node_registry.rb +54 -0
  39. data/lib/kumi/core/export/node_serializers.rb +158 -0
  40. data/lib/kumi/core/export/serializer.rb +25 -0
  41. data/lib/kumi/core/export.rb +35 -0
  42. data/lib/kumi/core/function_registry/collection_functions.rb +202 -0
  43. data/lib/kumi/core/function_registry/comparison_functions.rb +33 -0
  44. data/lib/kumi/core/function_registry/conditional_functions.rb +38 -0
  45. data/lib/kumi/core/function_registry/function_builder.rb +95 -0
  46. data/lib/kumi/core/function_registry/logical_functions.rb +44 -0
  47. data/lib/kumi/core/function_registry/math_functions.rb +74 -0
  48. data/lib/kumi/core/function_registry/string_functions.rb +57 -0
  49. data/lib/kumi/core/function_registry/type_functions.rb +53 -0
  50. data/lib/kumi/{function_registry.rb → core/function_registry.rb} +28 -36
  51. data/lib/kumi/core/input/type_matcher.rb +97 -0
  52. data/lib/kumi/core/input/validator.rb +51 -0
  53. data/lib/kumi/core/input/violation_creator.rb +52 -0
  54. data/lib/kumi/core/json_schema/generator.rb +65 -0
  55. data/lib/kumi/core/json_schema/validator.rb +27 -0
  56. data/lib/kumi/core/json_schema.rb +16 -0
  57. data/lib/kumi/core/ruby_parser/build_context.rb +27 -0
  58. data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +38 -0
  59. data/lib/kumi/core/ruby_parser/dsl.rb +14 -0
  60. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +138 -0
  61. data/lib/kumi/core/ruby_parser/expression_converter.rb +128 -0
  62. data/lib/kumi/core/ruby_parser/guard_rails.rb +45 -0
  63. data/lib/kumi/core/ruby_parser/input_builder.rb +127 -0
  64. data/lib/kumi/core/ruby_parser/input_field_proxy.rb +48 -0
  65. data/lib/kumi/core/ruby_parser/input_proxy.rb +31 -0
  66. data/lib/kumi/core/ruby_parser/nested_input.rb +17 -0
  67. data/lib/kumi/core/ruby_parser/parser.rb +71 -0
  68. data/lib/kumi/core/ruby_parser/schema_builder.rb +175 -0
  69. data/lib/kumi/core/ruby_parser/sugar.rb +263 -0
  70. data/lib/kumi/core/ruby_parser.rb +12 -0
  71. data/lib/kumi/core/schema_instance.rb +111 -0
  72. data/lib/kumi/core/types/builder.rb +23 -0
  73. data/lib/kumi/core/types/compatibility.rb +96 -0
  74. data/lib/kumi/core/types/formatter.rb +26 -0
  75. data/lib/kumi/core/types/inference.rb +42 -0
  76. data/lib/kumi/core/types/normalizer.rb +72 -0
  77. data/lib/kumi/core/types/validator.rb +37 -0
  78. data/lib/kumi/core/types.rb +66 -0
  79. data/lib/kumi/core/vectorization_metadata.rb +110 -0
  80. data/lib/kumi/errors.rb +1 -112
  81. data/lib/kumi/registry.rb +37 -0
  82. data/lib/kumi/schema.rb +5 -5
  83. data/lib/kumi/schema_metadata.rb +3 -3
  84. data/lib/kumi/syntax/array_expression.rb +6 -6
  85. data/lib/kumi/syntax/call_expression.rb +4 -4
  86. data/lib/kumi/syntax/cascade_expression.rb +4 -4
  87. data/lib/kumi/syntax/case_expression.rb +4 -4
  88. data/lib/kumi/syntax/declaration_reference.rb +4 -4
  89. data/lib/kumi/syntax/hash_expression.rb +4 -4
  90. data/lib/kumi/syntax/input_declaration.rb +5 -5
  91. data/lib/kumi/syntax/input_element_reference.rb +5 -5
  92. data/lib/kumi/syntax/input_reference.rb +5 -5
  93. data/lib/kumi/syntax/literal.rb +4 -4
  94. data/lib/kumi/syntax/node.rb +34 -34
  95. data/lib/kumi/syntax/root.rb +6 -6
  96. data/lib/kumi/syntax/trait_declaration.rb +4 -4
  97. data/lib/kumi/syntax/value_declaration.rb +4 -4
  98. data/lib/kumi/version.rb +1 -1
  99. data/migrate_to_core_iterative.rb +938 -0
  100. data/scripts/generate_function_docs.rb +9 -9
  101. metadata +75 -72
  102. data/lib/kumi/analyzer/analysis_state.rb +0 -37
  103. data/lib/kumi/analyzer/constant_evaluator.rb +0 -57
  104. data/lib/kumi/analyzer/passes/broadcast_detector.rb +0 -246
  105. data/lib/kumi/analyzer/passes/declaration_validator.rb +0 -43
  106. data/lib/kumi/analyzer/passes/dependency_resolver.rb +0 -151
  107. data/lib/kumi/analyzer/passes/input_collector.rb +0 -137
  108. data/lib/kumi/analyzer/passes/name_indexer.rb +0 -24
  109. data/lib/kumi/analyzer/passes/pass_base.rb +0 -50
  110. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +0 -109
  111. data/lib/kumi/analyzer/passes/toposorter.rb +0 -108
  112. data/lib/kumi/analyzer/passes/type_checker.rb +0 -160
  113. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +0 -46
  114. data/lib/kumi/analyzer/passes/type_inferencer.rb +0 -232
  115. data/lib/kumi/analyzer/passes/unsat_detector.rb +0 -404
  116. data/lib/kumi/analyzer/passes/visitor_pass.rb +0 -42
  117. data/lib/kumi/atom_unsat_solver.rb +0 -394
  118. data/lib/kumi/compiled_schema.rb +0 -41
  119. data/lib/kumi/constraint_relationship_solver.rb +0 -638
  120. data/lib/kumi/domain/enum_analyzer.rb +0 -53
  121. data/lib/kumi/domain/range_analyzer.rb +0 -83
  122. data/lib/kumi/domain/validator.rb +0 -80
  123. data/lib/kumi/domain/violation_formatter.rb +0 -40
  124. data/lib/kumi/error_reporter.rb +0 -164
  125. data/lib/kumi/error_reporting.rb +0 -95
  126. data/lib/kumi/evaluation_wrapper.rb +0 -38
  127. data/lib/kumi/explain.rb +0 -293
  128. data/lib/kumi/export/deserializer.rb +0 -39
  129. data/lib/kumi/export/errors.rb +0 -12
  130. data/lib/kumi/export/node_builders.rb +0 -140
  131. data/lib/kumi/export/node_registry.rb +0 -52
  132. data/lib/kumi/export/node_serializers.rb +0 -156
  133. data/lib/kumi/export/serializer.rb +0 -23
  134. data/lib/kumi/export.rb +0 -33
  135. data/lib/kumi/function_registry/collection_functions.rb +0 -200
  136. data/lib/kumi/function_registry/comparison_functions.rb +0 -31
  137. data/lib/kumi/function_registry/conditional_functions.rb +0 -36
  138. data/lib/kumi/function_registry/function_builder.rb +0 -93
  139. data/lib/kumi/function_registry/logical_functions.rb +0 -42
  140. data/lib/kumi/function_registry/math_functions.rb +0 -72
  141. data/lib/kumi/function_registry/string_functions.rb +0 -54
  142. data/lib/kumi/function_registry/type_functions.rb +0 -51
  143. data/lib/kumi/input/type_matcher.rb +0 -95
  144. data/lib/kumi/input/validator.rb +0 -49
  145. data/lib/kumi/input/violation_creator.rb +0 -50
  146. data/lib/kumi/json_schema/generator.rb +0 -63
  147. data/lib/kumi/json_schema/validator.rb +0 -25
  148. data/lib/kumi/json_schema.rb +0 -14
  149. data/lib/kumi/ruby_parser/build_context.rb +0 -25
  150. data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +0 -36
  151. data/lib/kumi/ruby_parser/dsl.rb +0 -12
  152. data/lib/kumi/ruby_parser/dsl_cascade_builder.rb +0 -136
  153. data/lib/kumi/ruby_parser/expression_converter.rb +0 -126
  154. data/lib/kumi/ruby_parser/guard_rails.rb +0 -43
  155. data/lib/kumi/ruby_parser/input_builder.rb +0 -125
  156. data/lib/kumi/ruby_parser/input_field_proxy.rb +0 -46
  157. data/lib/kumi/ruby_parser/input_proxy.rb +0 -29
  158. data/lib/kumi/ruby_parser/nested_input.rb +0 -15
  159. data/lib/kumi/ruby_parser/parser.rb +0 -69
  160. data/lib/kumi/ruby_parser/schema_builder.rb +0 -173
  161. data/lib/kumi/ruby_parser/sugar.rb +0 -261
  162. data/lib/kumi/ruby_parser.rb +0 -10
  163. data/lib/kumi/schema_instance.rb +0 -109
  164. data/lib/kumi/types/builder.rb +0 -21
  165. data/lib/kumi/types/compatibility.rb +0 -94
  166. data/lib/kumi/types/formatter.rb +0 -24
  167. data/lib/kumi/types/inference.rb +0 -40
  168. data/lib/kumi/types/normalizer.rb +0 -70
  169. data/lib/kumi/types/validator.rb +0 -35
  170. data/lib/kumi/types.rb +0 -64
  171. data/lib/kumi/vectorization_metadata.rb +0 -108
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module Input
5
- class TypeMatcher
6
- def self.matches?(value, declared_type)
7
- case declared_type
8
- when :integer
9
- value.is_a?(Integer)
10
- when :float
11
- value.is_a?(Float) || value.is_a?(Integer) # Allow integer for float
12
- when :string
13
- value.is_a?(String)
14
- when :boolean
15
- value.is_a?(TrueClass) || value.is_a?(FalseClass)
16
- when :symbol
17
- value.is_a?(Symbol)
18
- when :array
19
- # Simple :array type - just check if it's an Array
20
- value.is_a?(Array)
21
- when :any
22
- true
23
- else
24
- # Handle complex types (arrays, hashes)
25
- handle_complex_type(value, declared_type)
26
- end
27
- end
28
-
29
- def self.infer_type(value)
30
- case value
31
- when Integer then :integer
32
- when Float then :float
33
- when String then :string
34
- when TrueClass, FalseClass then :boolean
35
- when Symbol then :symbol
36
- when Array then { array: :mixed }
37
- when Hash then { hash: %i[mixed mixed] }
38
- else :unknown
39
- end
40
- end
41
-
42
- def self.format_type(type)
43
- case type
44
- when Symbol
45
- type.to_s
46
- when Hash
47
- format_complex_type(type)
48
- else
49
- type.inspect
50
- end
51
- end
52
-
53
- private_class_method def self.handle_complex_type(value, declared_type)
54
- return false unless declared_type.is_a?(Hash)
55
-
56
- if declared_type.key?(:array)
57
- handle_array_type(value, declared_type[:array])
58
- elsif declared_type.key?(:hash)
59
- handle_hash_type(value, declared_type[:hash])
60
- else
61
- false
62
- end
63
- end
64
-
65
- private_class_method def self.handle_array_type(value, element_type)
66
- return false unless value.is_a?(Array)
67
- return true if element_type == :any
68
-
69
- value.all? { |elem| matches?(elem, element_type) }
70
- end
71
-
72
- private_class_method def self.handle_hash_type(value, hash_spec)
73
- return false unless value.is_a?(Hash)
74
-
75
- key_type, value_type = hash_spec
76
- return true if key_type == :any && value_type == :any
77
-
78
- value.all? do |k, v|
79
- matches?(k, key_type) && matches?(v, value_type)
80
- end
81
- end
82
-
83
- private_class_method def self.format_complex_type(type)
84
- if type.key?(:array)
85
- "array(#{format_type(type[:array])})"
86
- elsif type.key?(:hash)
87
- key_type, value_type = type[:hash]
88
- "hash(#{format_type(key_type)}, #{format_type(value_type)})"
89
- else
90
- type.inspect
91
- end
92
- end
93
- end
94
- end
95
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module Input
5
- class Validator
6
- def self.validate_context(context, input_meta)
7
- violations = []
8
-
9
- context.each do |field, value|
10
- meta = input_meta[field]
11
- next unless meta
12
-
13
- # Type validation first
14
- if should_validate_type?(meta) && !TypeMatcher.matches?(value, meta[:type])
15
- violations << ViolationCreator.create_type_violation(field, value, meta[:type])
16
- next # Skip domain validation if type is wrong
17
- end
18
-
19
- # Domain validation second (only if type is correct)
20
- if should_validate_domain?(meta) && !Domain::Validator.validate_field(field, value, meta[:domain])
21
- violations << ViolationCreator.create_domain_violation(field, value, meta[:domain])
22
- end
23
- end
24
-
25
- violations
26
- end
27
-
28
- def self.type_matches?(value, declared_type)
29
- TypeMatcher.matches?(value, declared_type)
30
- end
31
-
32
- def self.infer_type(value)
33
- TypeMatcher.infer_type(value)
34
- end
35
-
36
- def self.format_type(type)
37
- TypeMatcher.format_type(type)
38
- end
39
-
40
- private_class_method def self.should_validate_type?(meta)
41
- meta[:type] && meta[:type] != :any
42
- end
43
-
44
- private_class_method def self.should_validate_domain?(meta)
45
- meta[:domain]
46
- end
47
- end
48
- end
49
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module Input
5
- class ViolationCreator
6
- def self.create_type_violation(field, value, expected_type)
7
- {
8
- type: :type_violation,
9
- field: field,
10
- value: value,
11
- expected_type: expected_type,
12
- actual_type: TypeMatcher.infer_type(value),
13
- message: format_type_violation_message(field, value, expected_type)
14
- }
15
- end
16
-
17
- def self.create_domain_violation(field, value, domain)
18
- {
19
- type: :domain_violation,
20
- field: field,
21
- value: value,
22
- domain: domain,
23
- message: Kumi::Domain::ViolationFormatter.format_message(field, value, domain)
24
- }
25
- end
26
-
27
- def self.create_missing_field_violation(field, expected_type)
28
- {
29
- type: :missing_field_violation,
30
- field: field,
31
- expected_type: expected_type,
32
- message: format_missing_field_message(field, expected_type)
33
- }
34
- end
35
-
36
- private_class_method def self.format_type_violation_message(field, value, expected_type)
37
- actual_type = TypeMatcher.infer_type(value)
38
- expected_formatted = TypeMatcher.format_type(expected_type)
39
- actual_formatted = TypeMatcher.format_type(actual_type)
40
-
41
- "Field :#{field} expected #{expected_formatted}, got #{value.inspect} of type #{actual_formatted}"
42
- end
43
-
44
- private_class_method def self.format_missing_field_message(field, expected_type)
45
- expected_formatted = TypeMatcher.format_type(expected_type)
46
- "Missing required field :#{field} of type #{expected_formatted}"
47
- end
48
- end
49
- end
50
- end
@@ -1,63 +0,0 @@
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
@@ -1,25 +0,0 @@
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
@@ -1,14 +0,0 @@
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,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module RubyParser
5
- class BuildContext
6
- attr_reader :inputs, :attributes, :traits
7
- attr_accessor :current_location
8
-
9
- def initialize
10
- @inputs = []
11
- @attributes = []
12
- @traits = []
13
- @input_block_defined = false
14
- end
15
-
16
- def input_block_defined?
17
- @input_block_defined
18
- end
19
-
20
- def mark_input_block_defined!
21
- @input_block_defined = true
22
- end
23
- end
24
- end
25
- end
@@ -1,36 +0,0 @@
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,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module RubyParser
5
- module Dsl
6
- def self.build_syntax_tree(&rule_block)
7
- parser = Parser.new
8
- parser.parse(&rule_block)
9
- end
10
- end
11
- end
12
- end
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module RubyParser
5
- class DslCascadeBuilder
6
- include Syntax
7
-
8
- attr_reader :cases
9
-
10
- def initialize(context, loc)
11
- @context = context
12
- @cases = []
13
- @loc = loc
14
- end
15
-
16
- def on(*args)
17
- on_loc = current_location
18
- validate_on_args(args, "on", on_loc)
19
-
20
- trait_names = args[0..-2]
21
- expr = args.last
22
-
23
- trait_bindings = convert_trait_names_to_bindings(trait_names, on_loc)
24
- condition = create_fn(:all?, trait_bindings)
25
- result = ensure_syntax(expr)
26
- add_case(condition, result)
27
- end
28
-
29
- def on_any(*args)
30
- on_loc = current_location
31
- validate_on_args(args, "on_any", on_loc)
32
-
33
- trait_names = args[0..-2]
34
- expr = args.last
35
-
36
- trait_bindings = convert_trait_names_to_bindings(trait_names, on_loc)
37
- condition = create_fn(:any?, trait_bindings)
38
- result = ensure_syntax(expr)
39
- add_case(condition, result)
40
- end
41
-
42
- def on_none(*args)
43
- on_loc = current_location
44
- validate_on_args(args, "on_none", on_loc)
45
-
46
- trait_names = args[0..-2]
47
- expr = args.last
48
-
49
- trait_bindings = convert_trait_names_to_bindings(trait_names, on_loc)
50
- condition = create_fn(:none?, trait_bindings)
51
- result = ensure_syntax(expr)
52
- add_case(condition, result)
53
- end
54
-
55
- def base(expr)
56
- result = ensure_syntax(expr)
57
- add_case(create_literal(true), result)
58
- end
59
-
60
- def method_missing(method_name, *args, &block)
61
- return super if !args.empty? || block_given?
62
-
63
- # Allow direct trait references in cascade conditions
64
- create_binding(method_name, @loc)
65
- end
66
-
67
- def respond_to_missing?(_method_name, _include_private = false)
68
- true
69
- end
70
-
71
- private
72
-
73
- def current_location
74
- caller_info = caller_locations(1, 1).first
75
- Location.new(file: caller_info.path, line: caller_info.lineno, column: 0)
76
- end
77
-
78
- def validate_on_args(args, method_name, location)
79
- raise_error("cascade '#{method_name}' requires at least one trait name", location) if args.empty?
80
-
81
- return unless args.size == 1
82
-
83
- raise_error("cascade '#{method_name}' requires an expression as the last argument", location)
84
- end
85
-
86
- def convert_trait_names_to_bindings(trait_names, location)
87
- trait_names.map do |name|
88
- case name
89
- when Symbol
90
- create_binding(name, location)
91
- when DeclarationReference
92
- name # Already a binding from method_missing
93
- else
94
- raise_error("trait reference must be a symbol or bare identifier, got #{name.class}", location)
95
- end
96
- end
97
- end
98
-
99
- def add_case(condition, result)
100
- @cases << Kumi::Syntax::CaseExpression.new(condition, result)
101
- end
102
-
103
- def ref(name)
104
- @context.ref(name)
105
- end
106
-
107
- def fn(name, *args)
108
- @context.fn(name, *args)
109
- end
110
-
111
- def create_literal(value)
112
- @context.literal(value)
113
- end
114
-
115
- def create_fn(name, args)
116
- @context.fn(name, args)
117
- end
118
-
119
- def input
120
- @context.input
121
- end
122
-
123
- def ensure_syntax(expr)
124
- @context.ensure_syntax(expr)
125
- end
126
-
127
- def raise_error(message, location)
128
- @context.raise_error(message, location)
129
- end
130
-
131
- def create_binding(name, location)
132
- Kumi::Syntax::DeclarationReference.new(name, loc: location)
133
- end
134
- end
135
- end
136
- end
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module RubyParser
5
- # Converts Ruby objects and DSL expressions into AST nodes
6
- # This is the bridge between Ruby's native types and Kumi's syntax tree
7
- class ExpressionConverter
8
- include Syntax
9
- include ErrorReporting
10
-
11
- # Use the same literal types as Sugar module to avoid duplication
12
- LITERAL_TYPES = Sugar::LITERAL_TYPES
13
-
14
- def initialize(context)
15
- @context = context
16
- end
17
-
18
- # Convert any Ruby object into a syntax node
19
- # @param obj [Object] The object to convert
20
- # @return [Syntax::Node] The corresponding AST node
21
- def ensure_syntax(obj)
22
- case obj
23
- when *LITERAL_TYPES
24
- create_literal(obj)
25
- when Array
26
- create_list(obj)
27
- when Syntax::Node
28
- obj
29
- else
30
- handle_custom_object(obj)
31
- end
32
- end
33
-
34
- # Create a reference to another declaration
35
- # @param name [Symbol] The name to reference
36
- # @return [Syntax::DeclarationReference] Reference node
37
- def ref(name)
38
- validate_reference_name(name)
39
- Kumi::Syntax::DeclarationReference.new(name, loc: current_location)
40
- end
41
-
42
- # Create a literal value node
43
- # @param value [Object] The literal value
44
- # @return [Syntax::Literal] Literal node
45
- def literal(value)
46
- Kumi::Syntax::Literal.new(value, loc: current_location)
47
- end
48
-
49
- # Create a function call expression
50
- # @param fn_name [Symbol] The function name
51
- # @param args [Array] The function arguments
52
- # @return [Syntax::CallExpression] Function call node
53
- def fn(fn_name, *args)
54
- validate_function_name(fn_name)
55
- expr_args = convert_arguments(args)
56
- Kumi::Syntax::CallExpression.new(fn_name, expr_args, loc: current_location)
57
- end
58
-
59
- # Access the input proxy for field references
60
- # @return [InputProxy] Proxy for input field access
61
- def input
62
- InputProxy.new(@context)
63
- end
64
-
65
- # Raise a syntax error with location information
66
- # @param message [String] Error message
67
- # @param location [Location] Error location
68
- def raise_error(message, location)
69
- raise_syntax_error(message, location: location)
70
- end
71
-
72
- private
73
-
74
- def create_literal(value)
75
- Kumi::Syntax::Literal.new(value, loc: current_location)
76
- end
77
-
78
- def create_list(array)
79
- elements = array.map { |element| ensure_syntax(element) }
80
- Kumi::Syntax::ArrayExpression.new(elements, loc: current_location)
81
- end
82
-
83
- def handle_custom_object(obj)
84
- if obj.respond_to?(:to_ast_node)
85
- obj.to_ast_node
86
- else
87
- raise_invalid_expression_error(obj)
88
- end
89
- end
90
-
91
- def validate_reference_name(name)
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
- end
99
-
100
- def validate_function_name(fn_name)
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
- end
108
-
109
- def convert_arguments(args)
110
- args.map { |arg| ensure_syntax(arg) }
111
- end
112
-
113
- def raise_invalid_expression_error(obj)
114
- raise_syntax_error(
115
- "Cannot convert #{obj.class} to AST node. " \
116
- "Value: #{obj.inspect}",
117
- location: current_location
118
- )
119
- end
120
-
121
- def current_location
122
- @context.current_location
123
- end
124
- end
125
- end
126
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kumi
4
- module RubyParser
5
- module GuardRails
6
- RESERVED = %i[input trait value fn lit ref].freeze
7
-
8
- def self.included(base)
9
- base.singleton_class.prepend(ClassMethods)
10
- end
11
-
12
- module ClassMethods
13
- # prevent accidental addition of new DSL keywords
14
- def method_added(name)
15
- if GuardRails::RESERVED.include?(name)
16
- # Check if this is a redefinition by looking at the call stack
17
- # We want to allow the original definition but prevent redefinition
18
- calling_location = caller_locations(1, 1).first
19
-
20
- # Allow the original definition from schema_builder.rb
21
- if calling_location&.path&.include?("schema_builder.rb")
22
- super
23
- return
24
- end
25
-
26
- # This is a redefinition attempt, prevent it
27
- raise Kumi::Errors::SemanticError,
28
- "DSL keyword `#{name}` is reserved; " \
29
- "do not redefine it inside SchemaBuilder"
30
- end
31
- super
32
- end
33
- end
34
-
35
- # catch any stray method call inside DSL block
36
- def method_missing(name, *_args)
37
- raise NoMethodError, "unknown DSL keyword `#{name}`"
38
- end
39
-
40
- def respond_to_missing?(*) = false
41
- end
42
- end
43
- end