kumi 0.0.7 → 0.0.9
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 +21 -5
- data/docs/AST.md +7 -0
- data/docs/features/README.md +7 -0
- data/docs/features/s-expression-printer.md +77 -0
- 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/support/s_expression_printer.rb +161 -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/migrate_to_core_iterative.rb +938 -0
- data/scripts/generate_function_docs.rb +9 -9
- metadata +77 -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
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module Core
|
5
|
+
module Types
|
6
|
+
# Formats types for display and debugging
|
7
|
+
class Formatter
|
8
|
+
# Convert types to string representation
|
9
|
+
def self.type_to_s(type)
|
10
|
+
case type
|
11
|
+
when Hash
|
12
|
+
if type[:array]
|
13
|
+
"array(#{type_to_s(type[:array])})"
|
14
|
+
elsif type[:hash]
|
15
|
+
"hash(#{type_to_s(type[:hash][0])}, #{type_to_s(type[:hash][1])})"
|
16
|
+
else
|
17
|
+
type.to_s
|
18
|
+
end
|
19
|
+
else
|
20
|
+
type.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module Kumi
|
6
|
+
module Core
|
7
|
+
module Types
|
8
|
+
# Infers types from Ruby values
|
9
|
+
class Inference
|
10
|
+
def self.infer_from_value(value)
|
11
|
+
case value
|
12
|
+
when String then :string
|
13
|
+
when Integer then :integer
|
14
|
+
when Float then :float
|
15
|
+
when TrueClass, FalseClass then :boolean
|
16
|
+
when Symbol then :symbol
|
17
|
+
when Regexp then :regexp
|
18
|
+
when Time then :time
|
19
|
+
when DateTime then :datetime
|
20
|
+
when Date then :date
|
21
|
+
when Array
|
22
|
+
return Kumi::Core::Types.array(:any) if value.empty?
|
23
|
+
|
24
|
+
# Infer element type from first element (simple heuristic)
|
25
|
+
first_elem_type = infer_from_value(value.first)
|
26
|
+
Kumi::Core::Types.array(first_elem_type)
|
27
|
+
when Hash
|
28
|
+
return Kumi::Core::Types.hash(:any, :any) if value.empty?
|
29
|
+
|
30
|
+
# Infer key/value types from first pair (simple heuristic)
|
31
|
+
first_key, first_value = value.first
|
32
|
+
key_type = infer_from_value(first_key)
|
33
|
+
value_type = infer_from_value(first_value)
|
34
|
+
Kumi::Core::Types.hash(key_type, value_type)
|
35
|
+
else
|
36
|
+
:any
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module Kumi
|
6
|
+
module Core
|
7
|
+
module Types
|
8
|
+
# Normalizes different type inputs to canonical forms
|
9
|
+
class Normalizer
|
10
|
+
# Type normalization - convert various inputs to canonical type symbols
|
11
|
+
def self.normalize(type_input)
|
12
|
+
case type_input
|
13
|
+
when Symbol
|
14
|
+
return type_input if Validator.valid_type?(type_input)
|
15
|
+
|
16
|
+
raise ArgumentError, "Invalid type symbol: #{type_input}"
|
17
|
+
when String
|
18
|
+
symbol_type = type_input.to_sym
|
19
|
+
return symbol_type if Validator.valid_type?(symbol_type)
|
20
|
+
|
21
|
+
raise ArgumentError, "Invalid type string: #{type_input}"
|
22
|
+
when Hash
|
23
|
+
return type_input if Validator.valid_type?(type_input)
|
24
|
+
|
25
|
+
raise ArgumentError, "Invalid type hash: #{type_input}"
|
26
|
+
when Class
|
27
|
+
# Handle Ruby class inputs
|
28
|
+
case type_input.name
|
29
|
+
when "Integer" then :integer
|
30
|
+
when "String" then :string
|
31
|
+
when "Float" then :float
|
32
|
+
when "TrueClass", "FalseClass" then :boolean
|
33
|
+
when "Array" then raise ArgumentError, "Use array(:type) helper for array types"
|
34
|
+
when "Hash" then raise ArgumentError, "Use hash(:key_type, :value_type) helper for hash types"
|
35
|
+
else
|
36
|
+
raise ArgumentError, "Unsupported class type: #{type_input}"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
case type_input
|
40
|
+
when Integer, Float, Numeric
|
41
|
+
raise ArgumentError, "Type must be a symbol, got #{type_input} (#{type_input.class})"
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Invalid type input: #{type_input} (#{type_input.class})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Legacy compatibility - coerce old constants to symbols
|
49
|
+
def self.coerce(type_input)
|
50
|
+
# Handle legacy constant usage
|
51
|
+
return type_input if type_input.is_a?(Symbol) && Validator.valid_type?(type_input)
|
52
|
+
|
53
|
+
# Handle legacy constant objects
|
54
|
+
case type_input
|
55
|
+
when STRING then :string
|
56
|
+
when INT then :integer
|
57
|
+
when FLOAT, NUMERIC then :float # Both FLOAT and NUMERIC map to :float
|
58
|
+
when BOOL then :boolean
|
59
|
+
when ANY then :any
|
60
|
+
when SYMBOL then :symbol
|
61
|
+
when REGEXP then :regexp
|
62
|
+
when TIME then :time
|
63
|
+
when DATE then :date
|
64
|
+
when DATETIME then :datetime
|
65
|
+
else
|
66
|
+
normalize(type_input)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module Core
|
5
|
+
module Types
|
6
|
+
# Validates type definitions and structures
|
7
|
+
class Validator
|
8
|
+
VALID_TYPES = %i[string integer float boolean any symbol regexp time date datetime array].freeze
|
9
|
+
|
10
|
+
def self.valid_type?(type)
|
11
|
+
return true if VALID_TYPES.include?(type)
|
12
|
+
return true if array_type?(type)
|
13
|
+
return true if hash_type?(type)
|
14
|
+
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.array_type?(type)
|
19
|
+
type.is_a?(Hash) && type.keys == [:array] && valid_type?(type[:array])
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.hash_type?(type)
|
23
|
+
type.is_a?(Hash) &&
|
24
|
+
type.keys.sort == [:hash] &&
|
25
|
+
type[:hash].is_a?(Array) &&
|
26
|
+
type[:hash].size == 2 &&
|
27
|
+
valid_type?(type[:hash][0]) &&
|
28
|
+
valid_type?(type[:hash][1])
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.primitive_type?(type)
|
32
|
+
VALID_TYPES.include?(type)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module Core
|
5
|
+
module Types
|
6
|
+
# Re-export constants for compatibility
|
7
|
+
VALID_TYPES = Validator::VALID_TYPES
|
8
|
+
|
9
|
+
# Validation methods
|
10
|
+
def self.valid_type?(type)
|
11
|
+
Validator.valid_type?(type)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Type builders
|
15
|
+
def self.array(elem_type)
|
16
|
+
Builder.array(elem_type)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.hash(key_type, val_type)
|
20
|
+
Builder.hash(key_type, val_type)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Normalization
|
24
|
+
def self.normalize(type_input)
|
25
|
+
Normalizer.normalize(type_input)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.coerce(type_input)
|
29
|
+
Normalizer.coerce(type_input)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Compatibility and unification
|
33
|
+
def self.compatible?(type1, type2)
|
34
|
+
Compatibility.compatible?(type1, type2)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.unify(type1, type2)
|
38
|
+
Compatibility.unify(type1, type2)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Type inference
|
42
|
+
def self.infer_from_value(value)
|
43
|
+
Inference.infer_from_value(value)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Formatting
|
47
|
+
def self.type_to_s(type)
|
48
|
+
Formatter.type_to_s(type)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Legacy compatibility constants (will be phased out)
|
52
|
+
# These should be replaced with symbols in user code over time
|
53
|
+
STRING = :string
|
54
|
+
INT = :integer # NOTE: using :integer instead of :int for clarity
|
55
|
+
FLOAT = :float
|
56
|
+
BOOL = :boolean
|
57
|
+
ANY = :any
|
58
|
+
SYMBOL = :symbol
|
59
|
+
REGEXP = :regexp
|
60
|
+
TIME = :time
|
61
|
+
DATE = :date
|
62
|
+
DATETIME = :datetime
|
63
|
+
NUMERIC = :float # Legacy: represents numeric compatibility
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module Core
|
5
|
+
# Metadata system for vectorization detection and handling
|
6
|
+
module VectorizationMetadata
|
7
|
+
# Tracks which declarations are arrays with children (vectorizable)
|
8
|
+
class ArrayDeclarationTracker
|
9
|
+
def initialize
|
10
|
+
@array_declarations = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def register_array(name, children)
|
14
|
+
@array_declarations[name] = children.map(&:name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def array_declaration?(name)
|
18
|
+
@array_declarations.key?(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def array_children(name)
|
22
|
+
@array_declarations[name] || []
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_arrays
|
26
|
+
@array_declarations.keys
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Detects vectorized operations in expressions
|
31
|
+
class VectorizationDetector
|
32
|
+
def initialize(array_tracker)
|
33
|
+
@array_tracker = array_tracker
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check if an expression should be vectorized
|
37
|
+
def vectorized_expression?(expression)
|
38
|
+
case expression
|
39
|
+
when Kumi::Syntax::CallExpression
|
40
|
+
vectorized_call?(expression)
|
41
|
+
when Kumi::Syntax::InputElementReference
|
42
|
+
vectorized_element_reference?(expression)
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check if a function call should be treated as a reducer
|
49
|
+
def reducer_function?(fn_name, args)
|
50
|
+
REDUCER_FUNCTIONS.include?(fn_name) &&
|
51
|
+
args.any? { |arg| vectorized_expression?(arg) }
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
REDUCER_FUNCTIONS = %i[sum min max size length first last].freeze
|
57
|
+
|
58
|
+
def vectorized_call?(call_expr)
|
59
|
+
# Arithmetic operations between array elements are vectorized
|
60
|
+
ARITHMETIC_OPERATIONS.include?(call_expr.fn_name) &&
|
61
|
+
call_expr.args.any? { |arg| vectorized_expression?(arg) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def vectorized_element_reference?(elem_ref)
|
65
|
+
return false unless elem_ref.path.size >= 2
|
66
|
+
|
67
|
+
array_name, _field_name = elem_ref.path
|
68
|
+
@array_tracker.array_declaration?(array_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
ARITHMETIC_OPERATIONS = %i[add subtract multiply divide modulo power].freeze
|
72
|
+
end
|
73
|
+
|
74
|
+
# Metadata about how values should be computed
|
75
|
+
class ComputationMetadata
|
76
|
+
attr_reader :vectorized_values, :reducer_values, :scalar_values
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
@vectorized_values = Set.new
|
80
|
+
@reducer_values = Set.new
|
81
|
+
@scalar_values = Set.new
|
82
|
+
end
|
83
|
+
|
84
|
+
def mark_vectorized(name)
|
85
|
+
@vectorized_values.add(name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def mark_reducer(name)
|
89
|
+
@reducer_values.add(name)
|
90
|
+
end
|
91
|
+
|
92
|
+
def mark_scalar(name)
|
93
|
+
@scalar_values.add(name)
|
94
|
+
end
|
95
|
+
|
96
|
+
def vectorized?(name)
|
97
|
+
@vectorized_values.include?(name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def reducer?(name)
|
101
|
+
@reducer_values.include?(name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def scalar?(name)
|
105
|
+
@scalar_values.include?(name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/kumi/errors.rb
CHANGED
@@ -1,116 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Kumi
|
4
2
|
module Errors
|
5
|
-
|
6
|
-
|
7
|
-
class LocatedError < Error
|
8
|
-
attr_reader :location
|
9
|
-
|
10
|
-
def initialize(message, location = nil)
|
11
|
-
super(message)
|
12
|
-
@location = location
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_s
|
16
|
-
if @location
|
17
|
-
"#{super} at #{@location.file}:#{@location.line}:#{@location.column}"
|
18
|
-
else
|
19
|
-
super
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class SemanticError < LocatedError; end
|
25
|
-
|
26
|
-
class TypeError < SemanticError; end
|
27
|
-
|
28
|
-
class FieldMetadataError < SemanticError; end
|
29
|
-
|
30
|
-
class SyntaxError < LocatedError; end
|
31
|
-
|
32
|
-
class RuntimeError < Error; end
|
33
|
-
|
34
|
-
class DomainViolationError < Error
|
35
|
-
attr_reader :violations
|
36
|
-
|
37
|
-
def initialize(violations)
|
38
|
-
@violations = violations
|
39
|
-
super(format_message)
|
40
|
-
end
|
41
|
-
|
42
|
-
def single_violation?
|
43
|
-
violations.size == 1
|
44
|
-
end
|
45
|
-
|
46
|
-
def multiple_violations?
|
47
|
-
violations.size > 1
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def format_message
|
53
|
-
if single_violation?
|
54
|
-
violations.first[:message]
|
55
|
-
else
|
56
|
-
"Multiple domain violations:\n#{violations.map { |v| " - #{v[:message]}" }.join("\n")}"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class InputValidationError < Error
|
62
|
-
attr_reader :violations
|
63
|
-
|
64
|
-
def initialize(violations)
|
65
|
-
@violations = violations
|
66
|
-
super(format_message)
|
67
|
-
end
|
68
|
-
|
69
|
-
def single_violation?
|
70
|
-
violations.size == 1
|
71
|
-
end
|
72
|
-
|
73
|
-
def multiple_violations?
|
74
|
-
violations.size > 1
|
75
|
-
end
|
76
|
-
|
77
|
-
def type_violations
|
78
|
-
violations.select { |v| v[:type] == :type_violation }
|
79
|
-
end
|
80
|
-
|
81
|
-
def domain_violations
|
82
|
-
violations.select { |v| v[:type] == :domain_violation }
|
83
|
-
end
|
84
|
-
|
85
|
-
def type_violations?
|
86
|
-
type_violations.any?
|
87
|
-
end
|
88
|
-
|
89
|
-
def domain_violations?
|
90
|
-
domain_violations.any?
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def format_message
|
96
|
-
if single_violation?
|
97
|
-
violations.first[:message]
|
98
|
-
else
|
99
|
-
message_parts = []
|
100
|
-
|
101
|
-
if type_violations?
|
102
|
-
message_parts << "Type violations:"
|
103
|
-
type_violations.each { |v| message_parts << " - #{v[:message]}" }
|
104
|
-
end
|
105
|
-
|
106
|
-
if domain_violations?
|
107
|
-
message_parts << "Domain violations:"
|
108
|
-
domain_violations.each { |v| message_parts << " - #{v[:message]}" }
|
109
|
-
end
|
110
|
-
|
111
|
-
message_parts.join("\n")
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
3
|
+
include Core::Errors
|
115
4
|
end
|
116
5
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Kumi
|
2
|
+
module Registry
|
3
|
+
extend Core::FunctionRegistry
|
4
|
+
Entry = Core::FunctionRegistry::FunctionBuilder::Entry
|
5
|
+
|
6
|
+
@functions = Core::FunctionRegistry::CORE_FUNCTIONS.transform_values(&:dup)
|
7
|
+
@frozen = false
|
8
|
+
@lock = Mutex.new
|
9
|
+
|
10
|
+
class FrozenError < RuntimeError; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def reset!
|
14
|
+
@lock.synchronize do
|
15
|
+
@functions = Core::FunctionRegistry::CORE_FUNCTIONS.transform_values(&:dup)
|
16
|
+
@frozen = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def register(name, &block)
|
21
|
+
@lock.synchronize do
|
22
|
+
raise FrozenError, "registry is frozen" if @frozen
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def freeze!
|
29
|
+
@lock.synchronize do
|
30
|
+
@functions.each_value(&:freeze)
|
31
|
+
@functions.freeze
|
32
|
+
@frozen = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/kumi/schema.rb
CHANGED
@@ -17,11 +17,11 @@ module Kumi
|
|
17
17
|
|
18
18
|
# Validate input types and domain constraints
|
19
19
|
input_meta = @__analyzer_result__.state[:inputs] || {}
|
20
|
-
violations = Input::Validator.validate_context(context, input_meta)
|
20
|
+
violations = Core::Input::Validator.validate_context(context, input_meta)
|
21
21
|
|
22
22
|
raise Errors::InputValidationError, violations unless violations.empty?
|
23
23
|
|
24
|
-
SchemaInstance.new(@__compiled_schema__, @__analyzer_result__.state, context)
|
24
|
+
Core::SchemaInstance.new(@__compiled_schema__, @__analyzer_result__.state, context)
|
25
25
|
end
|
26
26
|
|
27
27
|
def explain(context, *keys)
|
@@ -29,19 +29,19 @@ module Kumi
|
|
29
29
|
|
30
30
|
# Validate input types and domain constraints
|
31
31
|
input_meta = @__analyzer_result__.state[:inputs] || {}
|
32
|
-
violations = Input::Validator.validate_context(context, input_meta)
|
32
|
+
violations = Core::Input::Validator.validate_context(context, input_meta)
|
33
33
|
|
34
34
|
raise Errors::InputValidationError, violations unless violations.empty?
|
35
35
|
|
36
36
|
keys.each do |key|
|
37
|
-
puts
|
37
|
+
puts Core::Explain.call(self, key, inputs: context)
|
38
38
|
end
|
39
39
|
|
40
40
|
nil
|
41
41
|
end
|
42
42
|
|
43
43
|
def schema(&block)
|
44
|
-
@__syntax_tree__ =
|
44
|
+
@__syntax_tree__ = Core::RubyParser::Dsl.build_syntax_tree(&block).freeze
|
45
45
|
@__analyzer_result__ = Analyzer.analyze!(@__syntax_tree__).freeze
|
46
46
|
@__compiled_schema__ = Compiler.compile(@__syntax_tree__, analyzer: @__analyzer_result__).freeze
|
47
47
|
|
data/lib/kumi/schema_metadata.rb
CHANGED
@@ -210,7 +210,7 @@ module Kumi
|
|
210
210
|
# # "x-kumi-traits": { ... }
|
211
211
|
# # }
|
212
212
|
def to_json_schema
|
213
|
-
JsonSchema::Generator.new(self).generate
|
213
|
+
Core::JsonSchema::Generator.new(self).generate
|
214
214
|
end
|
215
215
|
|
216
216
|
# Returns processed declaration metadata.
|
@@ -412,9 +412,9 @@ module Kumi
|
|
412
412
|
end
|
413
413
|
|
414
414
|
function_calls.each_with_object({}) do |func_name, result|
|
415
|
-
next unless Kumi::
|
415
|
+
next unless Kumi::Registry.supported?(func_name)
|
416
416
|
|
417
|
-
function_info = Kumi::
|
417
|
+
function_info = Kumi::Registry.signature(func_name)
|
418
418
|
result[func_name] = {
|
419
419
|
param_types: function_info[:param_types],
|
420
420
|
return_type: function_info[:return_type],
|