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.
- checksums.yaml +4 -4
- data/CLAUDE.md +1 -1
- data/README.md +8 -5
- data/examples/game_of_life.rb +1 -1
- data/examples/static_analysis_errors.rb +7 -7
- data/lib/kumi/analyzer.rb +15 -15
- data/lib/kumi/compiler.rb +6 -6
- 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 +5 -5
- data/lib/kumi/schema_metadata.rb +3 -3
- 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/migrate_to_core_iterative.rb +938 -0
- data/scripts/generate_function_docs.rb +9 -9
- metadata +75 -72
- 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 -246
- 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 -109
- data/lib/kumi/analyzer/passes/toposorter.rb +0 -108
- data/lib/kumi/analyzer/passes/type_checker.rb +0 -160
- 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 -404
- 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 -293
- 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/json_schema/generator.rb +0 -63
- data/lib/kumi/json_schema/validator.rb +0 -25
- data/lib/kumi/json_schema.rb +0 -14
- data/lib/kumi/ruby_parser/build_context.rb +0 -25
- data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +0 -36
- data/lib/kumi/ruby_parser/dsl.rb +0 -12
- data/lib/kumi/ruby_parser/dsl_cascade_builder.rb +0 -136
- data/lib/kumi/ruby_parser/expression_converter.rb +0 -126
- data/lib/kumi/ruby_parser/guard_rails.rb +0 -43
- data/lib/kumi/ruby_parser/input_builder.rb +0 -125
- data/lib/kumi/ruby_parser/input_field_proxy.rb +0 -46
- data/lib/kumi/ruby_parser/input_proxy.rb +0 -29
- data/lib/kumi/ruby_parser/nested_input.rb +0 -15
- data/lib/kumi/ruby_parser/parser.rb +0 -69
- data/lib/kumi/ruby_parser/schema_builder.rb +0 -173
- data/lib/kumi/ruby_parser/sugar.rb +0 -261
- data/lib/kumi/ruby_parser.rb +0 -10
- 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
@@ -1,125 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module RubyParser
|
5
|
-
class InputBuilder
|
6
|
-
include Syntax
|
7
|
-
include ErrorReporting
|
8
|
-
|
9
|
-
def initialize(context)
|
10
|
-
@context = context
|
11
|
-
end
|
12
|
-
|
13
|
-
def key(name, type: :any, domain: nil)
|
14
|
-
normalized_type = normalize_type(type, name)
|
15
|
-
@context.inputs << Kumi::Syntax::InputDeclaration.new(name, domain, normalized_type, [], loc: @context.current_location)
|
16
|
-
end
|
17
|
-
|
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)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
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?
|
29
|
-
create_array_field(name_or_elem_type, kwargs)
|
30
|
-
else
|
31
|
-
Kumi::Types.array(name_or_elem_type)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def hash(name_or_key_type, val_type = nil, **kwargs)
|
36
|
-
return Kumi::Types.hash(name_or_key_type, val_type) unless val_type.nil?
|
37
|
-
|
38
|
-
create_hash_field(name_or_key_type, kwargs)
|
39
|
-
end
|
40
|
-
|
41
|
-
def method_missing(method_name, *_args)
|
42
|
-
allowed_methods = "'key', 'integer', 'float', 'string', 'boolean', 'any', 'scalar', 'array', and 'hash'"
|
43
|
-
raise_syntax_error("Unknown method '#{method_name}' in input block. Only #{allowed_methods} are allowed.",
|
44
|
-
location: @context.current_location)
|
45
|
-
end
|
46
|
-
|
47
|
-
def respond_to_missing?(_method_name, _include_private = false)
|
48
|
-
false
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def normalize_type(type, name)
|
54
|
-
Kumi::Types.normalize(type)
|
55
|
-
rescue ArgumentError => e
|
56
|
-
raise_syntax_error("Invalid type for input `#{name}`: #{e.message}", location: @context.current_location)
|
57
|
-
end
|
58
|
-
|
59
|
-
def create_array_field(field_name, options)
|
60
|
-
elem_spec = options[:elem]
|
61
|
-
domain = options[:domain]
|
62
|
-
elem_type = elem_spec.is_a?(Hash) && elem_spec[:type] ? elem_spec[:type] : :any
|
63
|
-
|
64
|
-
array_type = create_array_type(field_name, elem_type)
|
65
|
-
@context.inputs << Kumi::Syntax::InputDeclaration.new(field_name, domain, array_type, [], loc: @context.current_location)
|
66
|
-
end
|
67
|
-
|
68
|
-
def create_array_type(field_name, elem_type)
|
69
|
-
Kumi::Types.array(elem_type)
|
70
|
-
rescue ArgumentError => e
|
71
|
-
raise_syntax_error("Invalid element type for array `#{field_name}`: #{e.message}", location: @context.current_location)
|
72
|
-
end
|
73
|
-
|
74
|
-
def create_hash_field(field_name, options)
|
75
|
-
key_spec = options[:key]
|
76
|
-
val_spec = options[:val] || options[:value]
|
77
|
-
domain = options[:domain]
|
78
|
-
|
79
|
-
key_type = extract_type(key_spec)
|
80
|
-
val_type = extract_type(val_spec)
|
81
|
-
|
82
|
-
hash_type = create_hash_type(field_name, key_type, val_type)
|
83
|
-
@context.inputs << Kumi::Syntax::InputDeclaration.new(field_name, domain, hash_type, [], loc: @context.current_location)
|
84
|
-
end
|
85
|
-
|
86
|
-
def extract_type(spec)
|
87
|
-
spec.is_a?(Hash) && spec[:type] ? spec[:type] : :any
|
88
|
-
end
|
89
|
-
|
90
|
-
def create_hash_type(field_name, key_type, val_type)
|
91
|
-
Kumi::Types.hash(key_type, val_type)
|
92
|
-
rescue ArgumentError => e
|
93
|
-
raise_syntax_error("Invalid types for hash `#{field_name}`: #{e.message}", location: @context.current_location)
|
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
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
@@ -1,46 +0,0 @@
|
|
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,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module RubyParser
|
5
|
-
# Proxy object for input field references (input.field_name)
|
6
|
-
class InputProxy
|
7
|
-
include Syntax
|
8
|
-
|
9
|
-
def initialize(context)
|
10
|
-
@context = context
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def method_missing(method_name, *_args)
|
16
|
-
# Create InputFieldProxy that can handle further field access
|
17
|
-
InputFieldProxy.new(method_name, @context)
|
18
|
-
end
|
19
|
-
|
20
|
-
# This method is called when the user tries to access a field
|
21
|
-
# on the input object, e.g. `input.field_name`.
|
22
|
-
# It is used to create an InputReference node in the AST.
|
23
|
-
|
24
|
-
def respond_to_missing?(_method_name, _include_private = false)
|
25
|
-
true # Allow any field name
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,15 +0,0 @@
|
|
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,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module RubyParser
|
5
|
-
# Main parser class for Ruby DSL
|
6
|
-
class Parser
|
7
|
-
include Syntax
|
8
|
-
include ErrorReporting
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
@context = BuildContext.new
|
12
|
-
@interface = SchemaBuilder.new(@context)
|
13
|
-
end
|
14
|
-
|
15
|
-
def parse(&rule_block)
|
16
|
-
enable_refinements(rule_block)
|
17
|
-
|
18
|
-
before_consts = ::Object.constants
|
19
|
-
@interface.freeze # stop singleton hacks
|
20
|
-
@interface.instance_eval(&rule_block)
|
21
|
-
added = ::Object.constants - before_consts
|
22
|
-
|
23
|
-
unless added.empty?
|
24
|
-
raise Kumi::Errors::SemanticError,
|
25
|
-
"DSL cannot define global constants: #{added.join(', ')}"
|
26
|
-
end
|
27
|
-
|
28
|
-
build_syntax_tree
|
29
|
-
rescue ArgumentError => e
|
30
|
-
handle_parse_error(e)
|
31
|
-
raise
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def enable_refinements(rule_block)
|
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")
|
42
|
-
rescue RuntimeError, NoMethodError
|
43
|
-
# Refinements disabled in method scope - continue without them
|
44
|
-
end
|
45
|
-
|
46
|
-
def build_syntax_tree
|
47
|
-
Root.new(@context.inputs, @context.attributes, @context.traits)
|
48
|
-
end
|
49
|
-
|
50
|
-
def handle_parse_error(error)
|
51
|
-
return unless literal_comparison_error?(error)
|
52
|
-
|
53
|
-
warn <<~HINT
|
54
|
-
#{error.backtrace.first.split(':', 2).join(':')}: \
|
55
|
-
Literal‑left comparison failed because the schema block is \
|
56
|
-
defined inside a method (Ruby disallows refinements there).
|
57
|
-
|
58
|
-
• Move the `schema do … end` block to the top level of a class or module, OR
|
59
|
-
• Write the comparison as `input.age >= 80` (preferred), OR
|
60
|
-
• Wrap the literal: `lit(80) <= input.age`.
|
61
|
-
HINT
|
62
|
-
end
|
63
|
-
|
64
|
-
def literal_comparison_error?(error)
|
65
|
-
error.message =~ /comparison of Integer with Kumi::Syntax::/i
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,173 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kumi
|
4
|
-
module RubyParser
|
5
|
-
class SchemaBuilder
|
6
|
-
include GuardRails
|
7
|
-
include Syntax
|
8
|
-
include ErrorReporting
|
9
|
-
|
10
|
-
DSL_METHODS = %i[value trait input ref literal fn].freeze
|
11
|
-
|
12
|
-
def initialize(context)
|
13
|
-
@context = context
|
14
|
-
end
|
15
|
-
|
16
|
-
def value(name = nil, expr = nil, &blk)
|
17
|
-
update_location
|
18
|
-
validate_value_args(name, expr, blk)
|
19
|
-
|
20
|
-
expression = blk ? build_cascade(&blk) : ensure_syntax(expr)
|
21
|
-
@context.attributes << Kumi::Syntax::ValueDeclaration.new(name, expression, loc: @context.current_location)
|
22
|
-
end
|
23
|
-
|
24
|
-
def trait(*args, **kwargs)
|
25
|
-
update_location
|
26
|
-
raise_syntax_error("keyword trait syntax not supported", location: @context.current_location) unless kwargs.empty?
|
27
|
-
build_positional_trait(args)
|
28
|
-
end
|
29
|
-
|
30
|
-
def input(&blk)
|
31
|
-
return InputProxy.new(@context) unless block_given?
|
32
|
-
|
33
|
-
raise_syntax_error("input block already defined", location: @context.current_location) if @context.input_block_defined?
|
34
|
-
@context.mark_input_block_defined!
|
35
|
-
|
36
|
-
update_location
|
37
|
-
input_builder = InputBuilder.new(@context)
|
38
|
-
input_builder.instance_eval(&blk)
|
39
|
-
end
|
40
|
-
|
41
|
-
def ref(name)
|
42
|
-
update_location
|
43
|
-
Kumi::Syntax::DeclarationReference.new(name, loc: @context.current_location)
|
44
|
-
end
|
45
|
-
|
46
|
-
def literal(value)
|
47
|
-
update_location
|
48
|
-
Kumi::Syntax::Literal.new(value, loc: @context.current_location)
|
49
|
-
end
|
50
|
-
|
51
|
-
def fn(fn_name, *args)
|
52
|
-
update_location
|
53
|
-
expr_args = args.map { |a| ensure_syntax(a) }
|
54
|
-
Kumi::Syntax::CallExpression.new(fn_name, expr_args, loc: @context.current_location)
|
55
|
-
end
|
56
|
-
|
57
|
-
def method_missing(method_name, *args, &block)
|
58
|
-
if args.empty? && !block_given?
|
59
|
-
update_location
|
60
|
-
# Create proxy for declaration references (traits/values)
|
61
|
-
DeclarationReferenceProxy.new(method_name, @context)
|
62
|
-
else
|
63
|
-
super
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def respond_to_missing?(_method_name, _include_private = false)
|
68
|
-
true
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
def update_location
|
74
|
-
# Use caller_locations(2, 1) to skip the DSL method and get the actual user code location
|
75
|
-
# Stack: [0] update_location, [1] DSL method (value/trait/etc), [2] user's DSL code
|
76
|
-
caller_location = caller_locations(2, 1).first
|
77
|
-
|
78
|
-
@context.current_location = Location.new(
|
79
|
-
file: caller_location.path,
|
80
|
-
line: caller_location.lineno,
|
81
|
-
column: 0
|
82
|
-
)
|
83
|
-
end
|
84
|
-
|
85
|
-
def validate_value_args(name, expr, blk)
|
86
|
-
raise_syntax_error("value requires a name as first argument", location: @context.current_location) if name.nil?
|
87
|
-
unless name.is_a?(Symbol)
|
88
|
-
raise_syntax_error("The name for 'value' must be a Symbol, got #{name.class}",
|
89
|
-
location: @context.current_location)
|
90
|
-
end
|
91
|
-
|
92
|
-
has_expr = !expr.nil?
|
93
|
-
has_block = blk
|
94
|
-
|
95
|
-
if has_expr && has_block
|
96
|
-
raise_syntax_error("value '#{name}' cannot be called with both an expression and a block", location: @context.current_location)
|
97
|
-
elsif !has_expr && !has_block
|
98
|
-
raise_syntax_error("value '#{name}' requires an expression or a block", location: @context.current_location)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def build_positional_trait(args)
|
103
|
-
case args.size
|
104
|
-
when 0
|
105
|
-
raise_syntax_error("trait requires a name and expression", location: @context.current_location)
|
106
|
-
when 1
|
107
|
-
name = args.first
|
108
|
-
raise_syntax_error("trait '#{name}' requires an expression", location: @context.current_location)
|
109
|
-
when 2
|
110
|
-
name, expression = args
|
111
|
-
validate_trait_name(name)
|
112
|
-
expr = ensure_syntax(expression)
|
113
|
-
@context.traits << Kumi::Syntax::TraitDeclaration.new(name, expr, loc: @context.current_location)
|
114
|
-
else
|
115
|
-
handle_deprecated_trait_syntax(args)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def handle_deprecated_trait_syntax(args)
|
120
|
-
if args.size == 3
|
121
|
-
name, = args
|
122
|
-
raise_syntax_error("trait '#{name}' requires exactly 3 arguments: lhs, operator, and rhs", location: @context.current_location)
|
123
|
-
end
|
124
|
-
|
125
|
-
# warn "DEPRECATION: trait(:name, lhs, operator, rhs) syntax is deprecated. Use: trait :name, (lhs operator rhs)"
|
126
|
-
|
127
|
-
if args.size == 4
|
128
|
-
name, lhs, operator, rhs = args
|
129
|
-
build_deprecated_trait(name, lhs, operator, [rhs])
|
130
|
-
else
|
131
|
-
name, lhs, operator, *rhs = args
|
132
|
-
build_deprecated_trait(name, lhs, operator, rhs)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def build_deprecated_trait(name, lhs, operator, rhs)
|
137
|
-
validate_trait_name(name)
|
138
|
-
validate_operator(operator)
|
139
|
-
|
140
|
-
rhs_exprs = rhs.map { |r| ensure_syntax(r) }
|
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)
|
143
|
-
end
|
144
|
-
|
145
|
-
def validate_trait_name(name)
|
146
|
-
return if name.is_a?(Symbol)
|
147
|
-
|
148
|
-
raise_syntax_error("The name for 'trait' must be a Symbol, got #{name.class}", location: @context.current_location)
|
149
|
-
end
|
150
|
-
|
151
|
-
def validate_operator(operator)
|
152
|
-
unless operator.is_a?(Symbol)
|
153
|
-
raise_syntax_error("expects a symbol for an operator, got #{operator.class}", location: @context.current_location)
|
154
|
-
end
|
155
|
-
|
156
|
-
return if FunctionRegistry.operator?(operator)
|
157
|
-
|
158
|
-
raise_syntax_error("unsupported operator `#{operator}`", location: @context.current_location)
|
159
|
-
end
|
160
|
-
|
161
|
-
def build_cascade(&blk)
|
162
|
-
expression_converter = ExpressionConverter.new(@context)
|
163
|
-
cascade_builder = DslCascadeBuilder.new(expression_converter, @context.current_location)
|
164
|
-
cascade_builder.instance_eval(&blk)
|
165
|
-
Kumi::Syntax::CascadeExpression.new(cascade_builder.cases, loc: @context.current_location)
|
166
|
-
end
|
167
|
-
|
168
|
-
def ensure_syntax(obj)
|
169
|
-
ExpressionConverter.new(@context).ensure_syntax(obj)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|