kumi 0.0.5 → 0.0.6

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +51 -6
  3. data/README.md +173 -51
  4. data/{documents → docs}/AST.md +29 -29
  5. data/{documents → docs}/SYNTAX.md +93 -1
  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/examples/federal_tax_calculator_2024.rb +11 -6
  14. data/lib/kumi/analyzer/constant_evaluator.rb +1 -1
  15. data/lib/kumi/analyzer/passes/broadcast_detector.rb +251 -0
  16. data/lib/kumi/analyzer/passes/{definition_validator.rb → declaration_validator.rb} +4 -4
  17. data/lib/kumi/analyzer/passes/dependency_resolver.rb +72 -32
  18. data/lib/kumi/analyzer/passes/input_collector.rb +90 -29
  19. data/lib/kumi/analyzer/passes/pass_base.rb +1 -1
  20. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +9 -9
  21. data/lib/kumi/analyzer/passes/toposorter.rb +42 -6
  22. data/lib/kumi/analyzer/passes/type_checker.rb +32 -10
  23. data/lib/kumi/analyzer/passes/type_inferencer.rb +126 -17
  24. data/lib/kumi/analyzer/passes/unsat_detector.rb +133 -53
  25. data/lib/kumi/analyzer/passes/visitor_pass.rb +2 -2
  26. data/lib/kumi/analyzer.rb +11 -12
  27. data/lib/kumi/compiler.rb +194 -16
  28. data/lib/kumi/constraint_relationship_solver.rb +6 -6
  29. data/lib/kumi/domain/validator.rb +0 -4
  30. data/lib/kumi/explain.rb +20 -20
  31. data/lib/kumi/export/node_registry.rb +26 -12
  32. data/lib/kumi/export/node_serializers.rb +1 -1
  33. data/lib/kumi/function_registry/collection_functions.rb +14 -9
  34. data/lib/kumi/function_registry/function_builder.rb +4 -3
  35. data/lib/kumi/function_registry.rb +8 -2
  36. data/lib/kumi/input/type_matcher.rb +3 -0
  37. data/lib/kumi/input/validator.rb +0 -3
  38. data/lib/kumi/parser/declaration_reference_proxy.rb +36 -0
  39. data/lib/kumi/parser/dsl_cascade_builder.rb +3 -3
  40. data/lib/kumi/parser/expression_converter.rb +6 -6
  41. data/lib/kumi/parser/input_builder.rb +40 -9
  42. data/lib/kumi/parser/input_field_proxy.rb +46 -0
  43. data/lib/kumi/parser/input_proxy.rb +3 -3
  44. data/lib/kumi/parser/nested_input.rb +15 -0
  45. data/lib/kumi/parser/schema_builder.rb +10 -9
  46. data/lib/kumi/parser/sugar.rb +61 -9
  47. data/lib/kumi/syntax/array_expression.rb +15 -0
  48. data/lib/kumi/syntax/call_expression.rb +11 -0
  49. data/lib/kumi/syntax/cascade_expression.rb +11 -0
  50. data/lib/kumi/syntax/case_expression.rb +11 -0
  51. data/lib/kumi/syntax/declaration_reference.rb +11 -0
  52. data/lib/kumi/syntax/hash_expression.rb +11 -0
  53. data/lib/kumi/syntax/input_declaration.rb +12 -0
  54. data/lib/kumi/syntax/input_element_reference.rb +12 -0
  55. data/lib/kumi/syntax/input_reference.rb +12 -0
  56. data/lib/kumi/syntax/literal.rb +11 -0
  57. data/lib/kumi/syntax/trait_declaration.rb +11 -0
  58. data/lib/kumi/syntax/value_declaration.rb +11 -0
  59. data/lib/kumi/vectorization_metadata.rb +108 -0
  60. data/lib/kumi/version.rb +1 -1
  61. metadata +31 -14
  62. data/lib/kumi/domain.rb +0 -8
  63. data/lib/kumi/input.rb +0 -8
  64. data/lib/kumi/syntax/declarations.rb +0 -26
  65. data/lib/kumi/syntax/expressions.rb +0 -34
  66. data/lib/kumi/syntax/terminal_expressions.rb +0 -30
  67. data/lib/kumi/syntax.rb +0 -9
  68. /data/{documents → docs}/DSL.md +0 -0
  69. /data/{documents → docs}/FUNCTIONS.md +0 -0
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Parser
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
@@ -88,7 +88,7 @@ module Kumi
88
88
  case name
89
89
  when Symbol
90
90
  create_binding(name, location)
91
- when Binding
91
+ when DeclarationReference
92
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)
@@ -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
@@ -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)
@@ -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 Parser
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
@@ -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 Parser
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
@@ -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)
@@ -13,7 +13,7 @@ module Kumi
13
13
  COMPARISON_OPS = %i[< <= > >= == !=].freeze
14
14
 
15
15
  LITERAL_TYPES = [
16
- Integer, String, Symbol, TrueClass, FalseClass, Float, Regexp
16
+ Integer, String, Symbol, TrueClass, FalseClass, Float, Regexp, NilClass
17
17
  ].freeze
18
18
 
19
19
  # Collection methods that can be applied to arrays/syntax nodes
@@ -23,11 +23,11 @@ module Kumi
23
23
  ].freeze
24
24
 
25
25
  def self.ensure_literal(obj)
26
- return Literal.new(obj) if LITERAL_TYPES.any? { |type| obj.is_a?(type) }
26
+ return Kumi::Syntax::Literal.new(obj) if LITERAL_TYPES.any? { |type| obj.is_a?(type) }
27
27
  return obj if obj.is_a?(Syntax::Node)
28
28
  return obj.to_ast_node if obj.respond_to?(:to_ast_node)
29
29
 
30
- Literal.new(obj)
30
+ Kumi::Syntax::Literal.new(obj)
31
31
  end
32
32
 
33
33
  def self.syntax_expression?(obj)
@@ -36,7 +36,7 @@ module Kumi
36
36
 
37
37
  # Create a call expression with consistent error handling
38
38
  def self.create_call_expression(fn_name, args)
39
- Syntax::CallExpression.new(fn_name, args)
39
+ Kumi::Syntax::CallExpression.new(fn_name, args)
40
40
  end
41
41
 
42
42
  module ExpressionRefinement
@@ -100,7 +100,7 @@ module Kumi
100
100
  define_method(op) do |other|
101
101
  if Sugar.syntax_expression?(other)
102
102
  other_node = Sugar.ensure_literal(other)
103
- Sugar.create_call_expression(op_name, [Syntax::Literal.new(self), other_node])
103
+ Sugar.create_call_expression(op_name, [Kumi::Syntax::Literal.new(self), other_node])
104
104
  else
105
105
  super(other)
106
106
  end
@@ -112,7 +112,7 @@ module Kumi
112
112
  define_method(op) do |other|
113
113
  if Sugar.syntax_expression?(other)
114
114
  other_node = Sugar.ensure_literal(other)
115
- Sugar.create_call_expression(op, [Syntax::Literal.new(self), other_node])
115
+ Sugar.create_call_expression(op, [Kumi::Syntax::Literal.new(self), other_node])
116
116
  else
117
117
  super(other)
118
118
  end
@@ -127,7 +127,7 @@ module Kumi
127
127
  def +(other)
128
128
  if Sugar.syntax_expression?(other)
129
129
  other_node = Sugar.ensure_literal(other)
130
- Sugar.create_call_expression(:concat, [Syntax::Literal.new(self), other_node])
130
+ Sugar.create_call_expression(:concat, [Kumi::Syntax::Literal.new(self), other_node])
131
131
  else
132
132
  super
133
133
  end
@@ -137,7 +137,7 @@ module Kumi
137
137
  define_method(op) do |other|
138
138
  if Sugar.syntax_expression?(other)
139
139
  other_node = Sugar.ensure_literal(other)
140
- Sugar.create_call_expression(op, [Syntax::Literal.new(self), other_node])
140
+ Sugar.create_call_expression(op, [Kumi::Syntax::Literal.new(self), other_node])
141
141
  else
142
142
  super(other)
143
143
  end
@@ -156,7 +156,7 @@ module Kumi
156
156
  # Convert array to syntax list expression with all elements as syntax nodes
157
157
  def to_syntax_list
158
158
  syntax_elements = map { |item| Sugar.ensure_literal(item) }
159
- Syntax::ListExpression.new(syntax_elements)
159
+ Kumi::Syntax::ArrayExpression.new(syntax_elements)
160
160
  end
161
161
 
162
162
  # Create array method that works with syntax expressions
@@ -204,6 +204,58 @@ module Kumi
204
204
  end
205
205
  end
206
206
  end
207
+
208
+ # Shared refinement for proxy objects that need to handle operators
209
+ # Both DeclarationReferenceProxy and InputFieldProxy can use this
210
+ module ProxyRefinement
211
+ def self.extended(proxy_class)
212
+ # Add operator methods directly to the proxy class
213
+ proxy_class.class_eval do
214
+ # Arithmetic operations
215
+ ARITHMETIC_OPS.each do |op, op_name|
216
+ define_method(op) do |other|
217
+ ast_node = to_ast_node
218
+ other_node = Sugar.ensure_literal(other)
219
+ Sugar.create_call_expression(op_name, [ast_node, other_node])
220
+ end
221
+ end
222
+
223
+ # Comparison operations (including == and != that don't work with refinements)
224
+ COMPARISON_OPS.each do |op|
225
+ define_method(op) do |other|
226
+ ast_node = to_ast_node
227
+ other_node = Sugar.ensure_literal(other)
228
+ Sugar.create_call_expression(op, [ast_node, other_node])
229
+ end
230
+ end
231
+
232
+ # Logical operations
233
+ define_method(:&) do |other|
234
+ ast_node = to_ast_node
235
+ other_node = Sugar.ensure_literal(other)
236
+ Sugar.create_call_expression(:and, [ast_node, other_node])
237
+ end
238
+
239
+ define_method(:|) do |other|
240
+ ast_node = to_ast_node
241
+ other_node = Sugar.ensure_literal(other)
242
+ Sugar.create_call_expression(:or, [ast_node, other_node])
243
+ end
244
+
245
+ # Array access
246
+ define_method(:[]) do |index|
247
+ ast_node = to_ast_node
248
+ Sugar.create_call_expression(:at, [ast_node, Sugar.ensure_literal(index)])
249
+ end
250
+
251
+ # Unary minus
252
+ define_method(:-@) do
253
+ ast_node = to_ast_node
254
+ Sugar.create_call_expression(:subtract, [Sugar.ensure_literal(0), ast_node])
255
+ end
256
+ end
257
+ end
258
+ end
207
259
  end
208
260
  end
209
261
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ ArrayExpression = Struct.new(:elements) do
6
+ include Node
7
+
8
+ def children = elements
9
+
10
+ def size
11
+ elements.size
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ CallExpression = Struct.new(:fn_name, :args) do
6
+ include Node
7
+
8
+ def children = args
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ CascadeExpression = Struct.new(:cases) do
6
+ include Node
7
+
8
+ def children = cases
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ CaseExpression = Struct.new(:condition, :result) do
6
+ include Node
7
+
8
+ def children = [condition, result]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ DeclarationReference = Struct.new(:name) do
6
+ include Node
7
+
8
+ def children = []
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ HashExpression = Struct.new(:pairs) do
6
+ include Node
7
+
8
+ def children = pairs.flatten
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ # For field metadata declarations inside input blocks
6
+ InputDeclaration = Struct.new(:name, :domain, :type, :children) do
7
+ include Node
8
+
9
+ def children = self[:children] || []
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ # For field usage/reference in expressions (input.field_name)
6
+ InputElementReference = Struct.new(:path) do
7
+ include Node
8
+
9
+ def children = []
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ # For field usage/reference in expressions (input.field_name)
6
+ InputReference = Struct.new(:name) do
7
+ include Node
8
+
9
+ def children = []
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ Literal = Struct.new(:value) do
6
+ include Node
7
+
8
+ def children = []
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ TraitDeclaration = Struct.new(:name, :expression) do
6
+ include Node
7
+
8
+ def children = [expression]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Syntax
5
+ ValueDeclaration = Struct.new(:name, :expression) do
6
+ include Node
7
+
8
+ def children = [expression]
9
+ end
10
+ end
11
+ end