kumi 0.0.6 → 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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +34 -177
  3. data/README.md +41 -7
  4. data/docs/SYNTAX.md +2 -7
  5. data/docs/features/array-broadcasting.md +1 -1
  6. data/docs/schema_metadata/broadcasts.md +53 -0
  7. data/docs/schema_metadata/cascades.md +45 -0
  8. data/docs/schema_metadata/declarations.md +54 -0
  9. data/docs/schema_metadata/dependencies.md +57 -0
  10. data/docs/schema_metadata/evaluation_order.md +29 -0
  11. data/docs/schema_metadata/examples.md +95 -0
  12. data/docs/schema_metadata/inferred_types.md +46 -0
  13. data/docs/schema_metadata/inputs.md +86 -0
  14. data/docs/schema_metadata.md +108 -0
  15. data/examples/game_of_life.rb +1 -1
  16. data/examples/static_analysis_errors.rb +7 -7
  17. data/lib/kumi/analyzer.rb +20 -20
  18. data/lib/kumi/compiler.rb +44 -50
  19. data/lib/kumi/core/analyzer/analysis_state.rb +39 -0
  20. data/lib/kumi/core/analyzer/constant_evaluator.rb +59 -0
  21. data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +248 -0
  22. data/lib/kumi/core/analyzer/passes/declaration_validator.rb +45 -0
  23. data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +153 -0
  24. data/lib/kumi/core/analyzer/passes/input_collector.rb +139 -0
  25. data/lib/kumi/core/analyzer/passes/name_indexer.rb +26 -0
  26. data/lib/kumi/core/analyzer/passes/pass_base.rb +52 -0
  27. data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +111 -0
  28. data/lib/kumi/core/analyzer/passes/toposorter.rb +110 -0
  29. data/lib/kumi/core/analyzer/passes/type_checker.rb +162 -0
  30. data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +48 -0
  31. data/lib/kumi/core/analyzer/passes/type_inferencer.rb +236 -0
  32. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +406 -0
  33. data/lib/kumi/core/analyzer/passes/visitor_pass.rb +44 -0
  34. data/lib/kumi/core/atom_unsat_solver.rb +396 -0
  35. data/lib/kumi/core/compiled_schema.rb +43 -0
  36. data/lib/kumi/core/constraint_relationship_solver.rb +641 -0
  37. data/lib/kumi/core/domain/enum_analyzer.rb +55 -0
  38. data/lib/kumi/core/domain/range_analyzer.rb +85 -0
  39. data/lib/kumi/core/domain/validator.rb +82 -0
  40. data/lib/kumi/core/domain/violation_formatter.rb +42 -0
  41. data/lib/kumi/core/error_reporter.rb +166 -0
  42. data/lib/kumi/core/error_reporting.rb +97 -0
  43. data/lib/kumi/core/errors.rb +120 -0
  44. data/lib/kumi/core/evaluation_wrapper.rb +40 -0
  45. data/lib/kumi/core/explain.rb +295 -0
  46. data/lib/kumi/core/export/deserializer.rb +41 -0
  47. data/lib/kumi/core/export/errors.rb +14 -0
  48. data/lib/kumi/core/export/node_builders.rb +142 -0
  49. data/lib/kumi/core/export/node_registry.rb +54 -0
  50. data/lib/kumi/core/export/node_serializers.rb +158 -0
  51. data/lib/kumi/core/export/serializer.rb +25 -0
  52. data/lib/kumi/core/export.rb +35 -0
  53. data/lib/kumi/core/function_registry/collection_functions.rb +202 -0
  54. data/lib/kumi/core/function_registry/comparison_functions.rb +33 -0
  55. data/lib/kumi/core/function_registry/conditional_functions.rb +38 -0
  56. data/lib/kumi/core/function_registry/function_builder.rb +95 -0
  57. data/lib/kumi/core/function_registry/logical_functions.rb +44 -0
  58. data/lib/kumi/core/function_registry/math_functions.rb +74 -0
  59. data/lib/kumi/core/function_registry/string_functions.rb +57 -0
  60. data/lib/kumi/core/function_registry/type_functions.rb +53 -0
  61. data/lib/kumi/{function_registry.rb → core/function_registry.rb} +28 -36
  62. data/lib/kumi/core/input/type_matcher.rb +97 -0
  63. data/lib/kumi/core/input/validator.rb +51 -0
  64. data/lib/kumi/core/input/violation_creator.rb +52 -0
  65. data/lib/kumi/core/json_schema/generator.rb +65 -0
  66. data/lib/kumi/core/json_schema/validator.rb +27 -0
  67. data/lib/kumi/core/json_schema.rb +16 -0
  68. data/lib/kumi/core/ruby_parser/build_context.rb +27 -0
  69. data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +38 -0
  70. data/lib/kumi/core/ruby_parser/dsl.rb +14 -0
  71. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +138 -0
  72. data/lib/kumi/core/ruby_parser/expression_converter.rb +128 -0
  73. data/lib/kumi/core/ruby_parser/guard_rails.rb +45 -0
  74. data/lib/kumi/core/ruby_parser/input_builder.rb +127 -0
  75. data/lib/kumi/core/ruby_parser/input_field_proxy.rb +48 -0
  76. data/lib/kumi/core/ruby_parser/input_proxy.rb +31 -0
  77. data/lib/kumi/core/ruby_parser/nested_input.rb +17 -0
  78. data/lib/kumi/core/ruby_parser/parser.rb +71 -0
  79. data/lib/kumi/core/ruby_parser/schema_builder.rb +175 -0
  80. data/lib/kumi/core/ruby_parser/sugar.rb +263 -0
  81. data/lib/kumi/core/ruby_parser.rb +12 -0
  82. data/lib/kumi/core/schema_instance.rb +111 -0
  83. data/lib/kumi/core/types/builder.rb +23 -0
  84. data/lib/kumi/core/types/compatibility.rb +96 -0
  85. data/lib/kumi/core/types/formatter.rb +26 -0
  86. data/lib/kumi/core/types/inference.rb +42 -0
  87. data/lib/kumi/core/types/normalizer.rb +72 -0
  88. data/lib/kumi/core/types/validator.rb +37 -0
  89. data/lib/kumi/core/types.rb +66 -0
  90. data/lib/kumi/core/vectorization_metadata.rb +110 -0
  91. data/lib/kumi/errors.rb +1 -112
  92. data/lib/kumi/registry.rb +37 -0
  93. data/lib/kumi/schema.rb +13 -7
  94. data/lib/kumi/schema_metadata.rb +524 -0
  95. data/lib/kumi/syntax/array_expression.rb +6 -6
  96. data/lib/kumi/syntax/call_expression.rb +4 -4
  97. data/lib/kumi/syntax/cascade_expression.rb +4 -4
  98. data/lib/kumi/syntax/case_expression.rb +4 -4
  99. data/lib/kumi/syntax/declaration_reference.rb +4 -4
  100. data/lib/kumi/syntax/hash_expression.rb +4 -4
  101. data/lib/kumi/syntax/input_declaration.rb +5 -5
  102. data/lib/kumi/syntax/input_element_reference.rb +5 -5
  103. data/lib/kumi/syntax/input_reference.rb +5 -5
  104. data/lib/kumi/syntax/literal.rb +4 -4
  105. data/lib/kumi/syntax/node.rb +34 -34
  106. data/lib/kumi/syntax/root.rb +6 -6
  107. data/lib/kumi/syntax/trait_declaration.rb +4 -4
  108. data/lib/kumi/syntax/value_declaration.rb +4 -4
  109. data/lib/kumi/version.rb +1 -1
  110. data/lib/kumi.rb +14 -0
  111. data/migrate_to_core_iterative.rb +938 -0
  112. data/scripts/generate_function_docs.rb +9 -9
  113. metadata +85 -69
  114. data/lib/generators/trait_engine/templates/schema_spec.rb.erb +0 -27
  115. data/lib/kumi/analyzer/analysis_state.rb +0 -37
  116. data/lib/kumi/analyzer/constant_evaluator.rb +0 -57
  117. data/lib/kumi/analyzer/passes/broadcast_detector.rb +0 -251
  118. data/lib/kumi/analyzer/passes/declaration_validator.rb +0 -43
  119. data/lib/kumi/analyzer/passes/dependency_resolver.rb +0 -151
  120. data/lib/kumi/analyzer/passes/input_collector.rb +0 -137
  121. data/lib/kumi/analyzer/passes/name_indexer.rb +0 -24
  122. data/lib/kumi/analyzer/passes/pass_base.rb +0 -50
  123. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +0 -110
  124. data/lib/kumi/analyzer/passes/toposorter.rb +0 -108
  125. data/lib/kumi/analyzer/passes/type_checker.rb +0 -162
  126. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +0 -46
  127. data/lib/kumi/analyzer/passes/type_inferencer.rb +0 -232
  128. data/lib/kumi/analyzer/passes/unsat_detector.rb +0 -406
  129. data/lib/kumi/analyzer/passes/visitor_pass.rb +0 -42
  130. data/lib/kumi/atom_unsat_solver.rb +0 -394
  131. data/lib/kumi/compiled_schema.rb +0 -41
  132. data/lib/kumi/constraint_relationship_solver.rb +0 -638
  133. data/lib/kumi/domain/enum_analyzer.rb +0 -53
  134. data/lib/kumi/domain/range_analyzer.rb +0 -83
  135. data/lib/kumi/domain/validator.rb +0 -80
  136. data/lib/kumi/domain/violation_formatter.rb +0 -40
  137. data/lib/kumi/error_reporter.rb +0 -164
  138. data/lib/kumi/error_reporting.rb +0 -95
  139. data/lib/kumi/evaluation_wrapper.rb +0 -38
  140. data/lib/kumi/explain.rb +0 -281
  141. data/lib/kumi/export/deserializer.rb +0 -39
  142. data/lib/kumi/export/errors.rb +0 -12
  143. data/lib/kumi/export/node_builders.rb +0 -140
  144. data/lib/kumi/export/node_registry.rb +0 -52
  145. data/lib/kumi/export/node_serializers.rb +0 -156
  146. data/lib/kumi/export/serializer.rb +0 -23
  147. data/lib/kumi/export.rb +0 -33
  148. data/lib/kumi/function_registry/collection_functions.rb +0 -200
  149. data/lib/kumi/function_registry/comparison_functions.rb +0 -31
  150. data/lib/kumi/function_registry/conditional_functions.rb +0 -36
  151. data/lib/kumi/function_registry/function_builder.rb +0 -93
  152. data/lib/kumi/function_registry/logical_functions.rb +0 -42
  153. data/lib/kumi/function_registry/math_functions.rb +0 -72
  154. data/lib/kumi/function_registry/string_functions.rb +0 -54
  155. data/lib/kumi/function_registry/type_functions.rb +0 -51
  156. data/lib/kumi/input/type_matcher.rb +0 -95
  157. data/lib/kumi/input/validator.rb +0 -49
  158. data/lib/kumi/input/violation_creator.rb +0 -50
  159. data/lib/kumi/parser/build_context.rb +0 -25
  160. data/lib/kumi/parser/declaration_reference_proxy.rb +0 -36
  161. data/lib/kumi/parser/dsl.rb +0 -12
  162. data/lib/kumi/parser/dsl_cascade_builder.rb +0 -136
  163. data/lib/kumi/parser/expression_converter.rb +0 -126
  164. data/lib/kumi/parser/guard_rails.rb +0 -43
  165. data/lib/kumi/parser/input_builder.rb +0 -125
  166. data/lib/kumi/parser/input_field_proxy.rb +0 -46
  167. data/lib/kumi/parser/input_proxy.rb +0 -29
  168. data/lib/kumi/parser/nested_input.rb +0 -15
  169. data/lib/kumi/parser/parser.rb +0 -68
  170. data/lib/kumi/parser/schema_builder.rb +0 -173
  171. data/lib/kumi/parser/sugar.rb +0 -261
  172. data/lib/kumi/schema_instance.rb +0 -109
  173. data/lib/kumi/types/builder.rb +0 -21
  174. data/lib/kumi/types/compatibility.rb +0 -94
  175. data/lib/kumi/types/formatter.rb +0 -24
  176. data/lib/kumi/types/inference.rb +0 -40
  177. data/lib/kumi/types/normalizer.rb +0 -70
  178. data/lib/kumi/types/validator.rb +0 -35
  179. data/lib/kumi/types.rb +0 -64
  180. data/lib/kumi/vectorization_metadata.rb +0 -108
@@ -0,0 +1,524 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ # Primary interface for extracting structured metadata from analyzed Kumi schemas.
5
+ #
6
+ # SchemaMetadata provides both processed semantic metadata (inputs, values, traits, functions)
7
+ # and raw analyzer state for advanced use cases. This interface is designed for building
8
+ # external tools like form generators, documentation systems, and schema analysis utilities.
9
+ #
10
+ # @example Basic usage
11
+ # class MySchema
12
+ # extend Kumi::Schema
13
+ # schema do
14
+ # input do
15
+ # integer :age, domain: 18..65
16
+ # string :name
17
+ # end
18
+ # trait :adult, (input.age >= 18)
19
+ # value :greeting, "Hello " + input.name
20
+ # end
21
+ # end
22
+ #
23
+ # metadata = MySchema.schema_metadata
24
+ # puts metadata.inputs[:age][:domain] # => { type: :range, min: 18, max: 65, ... }
25
+ # puts metadata.traits[:adult][:condition] # => ">=(input.age, 18)"
26
+ #
27
+ # @example Tool integration
28
+ # def generate_form_fields(schema_class)
29
+ # metadata = schema_class.schema_metadata
30
+ # metadata.inputs.map do |field_name, field_info|
31
+ # create_input_field(field_name, field_info[:type], field_info[:domain])
32
+ # end
33
+ # end
34
+ class SchemaMetadata
35
+ # @param state_hash [Hash] Raw analyzer state from multi-pass analysis
36
+ # @param syntax_tree [Syntax::Root] Parsed AST of the schema definition
37
+ def initialize(state_hash, syntax_tree)
38
+ @state = state_hash
39
+ @syntax_tree = syntax_tree
40
+ end
41
+
42
+ # Returns processed input field metadata with normalized types and domains.
43
+ #
44
+ # Transforms raw input metadata from the analyzer into a clean, tool-friendly format
45
+ # with consistent type representation and domain constraint normalization.
46
+ #
47
+ # @return [Hash<Symbol, Hash>] Input field metadata keyed by field name
48
+ # @example
49
+ # metadata.inputs
50
+ # # => {
51
+ # # :age => { type: :integer, domain: { type: :range, min: 18, max: 65 }, required: true },
52
+ # # :name => { type: :string, required: true },
53
+ # # :items => { type: :array, required: true }
54
+ # # }
55
+ def inputs
56
+ @inputs ||= extract_inputs
57
+ end
58
+
59
+ # Returns processed value declaration metadata with dependencies and expressions.
60
+ #
61
+ # Extracts computed value information including type inference results, dependency
62
+ # relationships, and expression representations. Cascade expressions are expanded
63
+ # into structured condition/result pairs.
64
+ #
65
+ # @return [Hash<Symbol, Hash>] Value metadata keyed by declaration name
66
+ # @example
67
+ # metadata.values
68
+ # # => {
69
+ # # :tax_amount => {
70
+ # # type: :float,
71
+ # # dependencies: [:income, :tax_rate],
72
+ # # computed: true,
73
+ # # expression: "multiply(input.income, tax_rate)"
74
+ # # },
75
+ # # :status => {
76
+ # # type: :string,
77
+ # # dependencies: [:adult, :verified],
78
+ # # computed: true,
79
+ # # cascade: { conditions: [...], base: "default" }
80
+ # # }
81
+ # # }
82
+ def values
83
+ @values ||= extract_values
84
+ end
85
+
86
+ # Returns processed trait metadata with conditions and dependencies.
87
+ #
88
+ # Extracts boolean trait information including dependency relationships and
89
+ # human-readable condition expressions for documentation and analysis.
90
+ #
91
+ # @return [Hash<Symbol, Hash>] Trait metadata keyed by trait name
92
+ # @example
93
+ # metadata.traits
94
+ # # => {
95
+ # # :adult => {
96
+ # # type: :boolean,
97
+ # # dependencies: [:age],
98
+ # # condition: ">=(input.age, 18)"
99
+ # # },
100
+ # # :eligible => {
101
+ # # type: :boolean,
102
+ # # dependencies: [:adult, :verified, :score],
103
+ # # condition: "and(adult, verified, >(input.score, 80))"
104
+ # # }
105
+ # # }
106
+ def traits
107
+ @traits ||= extract_traits
108
+ end
109
+
110
+ # Returns function registry information for functions used in the schema.
111
+ #
112
+ # Analyzes all expressions in the schema to identify function calls and extracts
113
+ # their signatures from the function registry. Useful for documentation generation
114
+ # and validation tooling.
115
+ #
116
+ # @return [Hash<Symbol, Hash>] Function metadata keyed by function name
117
+ # @example
118
+ # metadata.functions
119
+ # # => {
120
+ # # :multiply => {
121
+ # # param_types: [:float, :float],
122
+ # # return_type: :float,
123
+ # # arity: 2,
124
+ # # description: "Multiplies two numbers"
125
+ # # },
126
+ # # :sum => {
127
+ # # param_types: [{ array: :float }],
128
+ # # return_type: :float,
129
+ # # arity: 1,
130
+ # # description: "Sums array elements"
131
+ # # }
132
+ # # }
133
+ def functions
134
+ @functions ||= extract_functions
135
+ end
136
+
137
+ # Returns serializable processed metadata as a hash.
138
+ #
139
+ # Combines all processed metadata (inputs, values, traits, functions) into a single
140
+ # hash suitable for JSON serialization, API responses, and external tool integration.
141
+ # Does not include raw AST nodes or analyzer state.
142
+ #
143
+ # @return [Hash<Symbol, Hash>] Serializable metadata hash
144
+ # @example
145
+ # metadata.to_h
146
+ # # => {
147
+ # # inputs: { :age => { type: :integer, ... }, ... },
148
+ # # values: { :tax_amount => { type: :float, ... }, ... },
149
+ # # traits: { :adult => { type: :boolean, ... }, ... },
150
+ # # functions: { :multiply => { param_types: [...], ... }, ... }
151
+ # # }
152
+ def to_h
153
+ {
154
+ inputs: inputs,
155
+ values: values,
156
+ traits: traits,
157
+ functions: functions
158
+ }
159
+ end
160
+
161
+ alias to_hash to_h
162
+
163
+ # Returns raw analyzer state including AST nodes.
164
+ #
165
+ # Provides access to the complete analyzer state hash for advanced use cases
166
+ # requiring direct AST manipulation or detailed analysis. Contains non-serializable
167
+ # AST node objects and should not be used for JSON export.
168
+ #
169
+ # @return [Hash] Complete analyzer state with all keys and AST nodes
170
+ # @example
171
+ # raw_state = metadata.analyzer_state
172
+ # declarations = raw_state[:declarations] # AST nodes
173
+ # dependency_graph = raw_state[:dependencies] # Edge objects
174
+ def analyzer_state
175
+ @state.dup
176
+ end
177
+
178
+ # @deprecated Use to_h instead for processed metadata
179
+ attr_reader :state
180
+
181
+ # Returns JSON representation of processed metadata.
182
+ #
183
+ # Serializes the processed metadata (inputs, values, traits, functions) to JSON.
184
+ # Excludes raw analyzer state and AST nodes for clean serialization.
185
+ #
186
+ # @param args [Array] Arguments passed to Hash#to_json
187
+ # @return [String] JSON representation
188
+ def to_json(*args)
189
+ require "json"
190
+ to_h.to_json(*args)
191
+ end
192
+
193
+ # Returns JSON Schema representation of input fields.
194
+ #
195
+ # Generates a JSON Schema document describing the expected input structure,
196
+ # including type constraints, domain validation, and Kumi-specific extensions
197
+ # for computed values and traits.
198
+ #
199
+ # @return [Hash] JSON Schema document
200
+ # @example
201
+ # schema = metadata.to_json_schema
202
+ # # => {
203
+ # # type: "object",
204
+ # # properties: {
205
+ # # age: { type: "integer", minimum: 18, maximum: 65 },
206
+ # # name: { type: "string" }
207
+ # # },
208
+ # # required: [:age, :name],
209
+ # # "x-kumi-values": { ... },
210
+ # # "x-kumi-traits": { ... }
211
+ # # }
212
+ def to_json_schema
213
+ Core::JsonSchema::Generator.new(self).generate
214
+ end
215
+
216
+ # Returns processed declaration metadata.
217
+ #
218
+ # Transforms raw AST declaration nodes into clean, serializable metadata showing
219
+ # declaration types and basic information. For raw AST nodes, use analyzer_state.
220
+ #
221
+ # @return [Hash<Symbol, Hash>] Declaration metadata keyed by name
222
+ # @example
223
+ # metadata.declarations
224
+ # # => {
225
+ # # :adult => { type: :trait, expression: ">=(input.age, 18)" },
226
+ # # :tax_amount => { type: :value, expression: "multiply(input.income, tax_rate)" }
227
+ # # }
228
+ def declarations
229
+ @declarations ||= extract_declarations
230
+ end
231
+
232
+ # Returns processed dependency information.
233
+ #
234
+ # Transforms internal Edge objects into clean, serializable dependency data
235
+ # showing relationships between declarations. For raw Edge objects, use analyzer_state.
236
+ #
237
+ # @return [Hash<Symbol, Array<Hash>>] Dependencies with plain data
238
+ # @example
239
+ # metadata.dependencies
240
+ # # => {
241
+ # # :tax_amount => [
242
+ # # { to: :income, conditional: false },
243
+ # # { to: :tax_rate, conditional: true, cascade_owner: :status }
244
+ # # ]
245
+ # # }
246
+ def dependencies
247
+ @dependencies ||= extract_dependencies
248
+ end
249
+
250
+ # Returns reverse dependency lookup (dependents).
251
+ #
252
+ # Shows which declarations depend on each declaration. Useful for impact analysis
253
+ # and understanding how changes to input fields or computed values affect other
254
+ # parts of the schema.
255
+ #
256
+ # @return [Hash<Symbol, Array<Symbol>>] Dependent names keyed by declaration name
257
+ def dependents
258
+ @state[:dependents] || {}
259
+ end
260
+
261
+ # Returns leaf node categorization.
262
+ #
263
+ # Identifies declarations with no dependencies, categorized by type (trait/value).
264
+ # Useful for understanding schema structure and identifying independent computations.
265
+ #
266
+ # @return [Hash<Symbol, Array<Symbol>>] Leaf declarations by category
267
+ def leaves
268
+ @state[:leaves] || {}
269
+ end
270
+
271
+ # Returns topologically sorted evaluation order.
272
+ #
273
+ # Provides the dependency-safe evaluation order for all declarations. Computed by
274
+ # topological sort of the dependency graph. Critical for correct evaluation sequence
275
+ # in runners and compilers.
276
+ #
277
+ # @return [Array<Symbol>] Declaration names in evaluation order
278
+ def evaluation_order
279
+ @state[:evaluation_order] || []
280
+ end
281
+
282
+ # Returns type inference results for all declarations.
283
+ #
284
+ # Maps declaration names to their inferred types based on expression analysis.
285
+ # Includes both simple types (:boolean, :string, :float) and complex types
286
+ # for array operations and structured data.
287
+ #
288
+ # @return [Hash<Symbol, Object>] Inferred types keyed by declaration name
289
+ # @example
290
+ # types = metadata.inferred_types
291
+ # # => { :adult => :boolean, :tax_amount => :float, :totals => { array: :float } }
292
+ def inferred_types
293
+ @state[:inferred_types] || {}
294
+ end
295
+
296
+ # Returns cascade mutual exclusion analysis.
297
+ #
298
+ # Provides analysis results for cascade expressions including mutual exclusion
299
+ # detection and satisfiability analysis. Used internally for optimization and
300
+ # error detection in cascade logic.
301
+ #
302
+ # @return [Hash] Cascade analysis results
303
+ def cascades
304
+ @state[:cascades] || {}
305
+ end
306
+
307
+ # Returns array broadcasting operation metadata.
308
+ #
309
+ # Contains analysis of vectorized operations on array inputs, including element
310
+ # access paths and broadcasting behavior. Used for generating efficient compiled
311
+ # code for array operations.
312
+ #
313
+ # @return [Hash] Broadcasting operation metadata
314
+ def broadcasts
315
+ @state[:broadcasts] || {}
316
+ end
317
+
318
+ private
319
+
320
+ def extract_declarations
321
+ return {} unless @state[:declarations]
322
+
323
+ @state[:declarations].transform_values do |node|
324
+ {
325
+ type: node.is_a?(Syntax::TraitDeclaration) ? :trait : :value,
326
+ expression: expression_to_string(node.expression)
327
+ }
328
+ end
329
+ end
330
+
331
+ def extract_dependencies
332
+ return {} unless @state[:dependencies]
333
+
334
+ @state[:dependencies].transform_values do |edges|
335
+ edges.map do |edge|
336
+ result = { to: edge.to, conditional: edge.conditional }
337
+ result[:cascade_owner] = edge.cascade_owner if edge.cascade_owner
338
+ result
339
+ end
340
+ end
341
+ end
342
+
343
+ def extract_inputs
344
+ return {} unless @state[:inputs]
345
+
346
+ @state[:inputs].transform_values do |field_info|
347
+ {
348
+ type: normalize_type(field_info[:type]),
349
+ domain: normalize_domain(field_info[:domain]),
350
+ required: true
351
+ }.compact
352
+ end
353
+ end
354
+
355
+ def extract_values
356
+ return {} unless @state[:dependencies]
357
+
358
+ value_nodes = (@syntax_tree.values || []).flatten.select { |node| node.is_a?(Syntax::ValueDeclaration) }
359
+ dependency_graph = @state[:dependencies] || {}
360
+ inferred_types = @state[:inferred_types] || {}
361
+
362
+ value_nodes.each_with_object({}) do |node, result|
363
+ name = node.name
364
+ dependency_edges = dependency_graph[name] || []
365
+ dependencies = dependency_edges.map(&:to)
366
+
367
+ result[name] = {
368
+ type: inferred_types[name],
369
+ dependencies: dependencies,
370
+ computed: true
371
+ }.tap do |spec|
372
+ if node.expression.is_a?(Syntax::CascadeExpression)
373
+ spec[:cascade] = extract_cascade_info(node.expression)
374
+ else
375
+ spec[:expression] = expression_to_string(node.expression)
376
+ end
377
+ end.compact
378
+ end
379
+ end
380
+
381
+ def extract_traits
382
+ return {} unless @state[:dependencies]
383
+
384
+ trait_nodes = (@syntax_tree.traits || []).flatten.select { |node| node.is_a?(Syntax::TraitDeclaration) }
385
+ dependency_graph = @state[:dependencies] || {}
386
+
387
+ trait_nodes.each_with_object({}) do |node, result|
388
+ name = node.name
389
+ dependency_edges = dependency_graph[name] || []
390
+ dependencies = dependency_edges.map(&:to)
391
+
392
+ result[name] = {
393
+ type: :boolean,
394
+ dependencies: dependencies,
395
+ condition: expression_to_string(node.expression)
396
+ }.compact
397
+ end
398
+ end
399
+
400
+ def extract_functions
401
+ function_calls = Set.new
402
+
403
+ value_nodes = (@syntax_tree.values || []).flatten.select { |node| node.is_a?(Syntax::ValueDeclaration) }
404
+ trait_nodes = (@syntax_tree.traits || []).flatten.select { |node| node.is_a?(Syntax::TraitDeclaration) }
405
+
406
+ value_nodes.each do |node|
407
+ collect_function_calls(node.expression, function_calls)
408
+ end
409
+
410
+ trait_nodes.each do |node|
411
+ collect_function_calls(node.expression, function_calls)
412
+ end
413
+
414
+ function_calls.each_with_object({}) do |func_name, result|
415
+ next unless Kumi::Registry.supported?(func_name)
416
+
417
+ function_info = Kumi::Registry.signature(func_name)
418
+ result[func_name] = {
419
+ param_types: function_info[:param_types],
420
+ return_type: function_info[:return_type],
421
+ arity: function_info[:arity],
422
+ description: function_info[:description]
423
+ }.compact
424
+ end
425
+ end
426
+
427
+ def normalize_type(type_spec)
428
+ case type_spec
429
+ when Hash
430
+ if type_spec.key?(:hash)
431
+ :hash
432
+ elsif type_spec.key?(:array)
433
+ :array
434
+ else
435
+ type_spec
436
+ end
437
+ else
438
+ type_spec
439
+ end
440
+ end
441
+
442
+ def normalize_domain(domain_spec)
443
+ case domain_spec
444
+ when Range
445
+ {
446
+ type: :range,
447
+ min: domain_spec.begin,
448
+ max: domain_spec.end,
449
+ exclusive_end: domain_spec.exclude_end?
450
+ }
451
+ when Array
452
+ { type: :enum, values: domain_spec }
453
+ when Proc
454
+ { type: :custom, description: "custom validation function" }
455
+ when Hash
456
+ domain_spec
457
+ end
458
+ end
459
+
460
+ def extract_cascade_info(cascade_expr)
461
+ cases = cascade_expr.cases || []
462
+
463
+ conditions = []
464
+ base_case = nil
465
+
466
+ cases.each do |case_expr|
467
+ if case_expr.condition
468
+ conditions << {
469
+ when: [expression_to_string(case_expr.condition)],
470
+ then: literal_value(case_expr.result)
471
+ }
472
+ else
473
+ base_case = literal_value(case_expr.result)
474
+ end
475
+ end
476
+
477
+ result = { conditions: conditions }
478
+ result[:base] = base_case if base_case
479
+ result
480
+ end
481
+
482
+ def expression_to_string(expr)
483
+ case expr
484
+ when Syntax::Literal
485
+ expr.value.inspect
486
+ when Syntax::InputReference
487
+ "input.#{expr.name}"
488
+ when Syntax::DeclarationReference
489
+ expr.name.to_s
490
+ when Syntax::CallExpression
491
+ args = expr.args.map { |arg| expression_to_string(arg) }.join(", ")
492
+ "#{expr.fn_name}(#{args})"
493
+ when Syntax::CascadeExpression
494
+ "cascade"
495
+ else
496
+ expr.class.name.split("::").last
497
+ end
498
+ end
499
+
500
+ def literal_value(expr)
501
+ expr.is_a?(Syntax::Literal) ? expr.value : expression_to_string(expr)
502
+ end
503
+
504
+ def collect_function_calls(expr, function_calls)
505
+ case expr
506
+ when Syntax::CallExpression
507
+ function_calls << expr.fn_name
508
+ expr.args.each { |arg| collect_function_calls(arg, function_calls) }
509
+ when Syntax::CascadeExpression
510
+ expr.cases.each do |case_expr|
511
+ collect_function_calls(case_expr.condition, function_calls) if case_expr.condition
512
+ collect_function_calls(case_expr.result, function_calls)
513
+ end
514
+ when Syntax::ArrayExpression
515
+ expr.elements.each { |elem| collect_function_calls(elem, function_calls) }
516
+ when Syntax::HashExpression
517
+ expr.pairs.each do |pair|
518
+ collect_function_calls(pair.key, function_calls)
519
+ collect_function_calls(pair.value, function_calls)
520
+ end
521
+ end
522
+ end
523
+ end
524
+ end
@@ -2,14 +2,14 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- ArrayExpression = Struct.new(:elements) do
6
- include Node
5
+ ArrayExpression = Struct.new(:elements) do
6
+ include Node
7
7
 
8
- def children = elements
8
+ def children = elements
9
9
 
10
- def size
11
- elements.size
10
+ def size
11
+ elements.size
12
+ end
12
13
  end
13
- end
14
14
  end
15
15
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- CallExpression = Struct.new(:fn_name, :args) do
6
- include Node
5
+ CallExpression = Struct.new(:fn_name, :args) do
6
+ include Node
7
7
 
8
- def children = args
9
- end
8
+ def children = args
9
+ end
10
10
  end
11
11
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- CascadeExpression = Struct.new(:cases) do
6
- include Node
5
+ CascadeExpression = Struct.new(:cases) do
6
+ include Node
7
7
 
8
- def children = cases
9
- end
8
+ def children = cases
9
+ end
10
10
  end
11
11
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- CaseExpression = Struct.new(:condition, :result) do
6
- include Node
5
+ CaseExpression = Struct.new(:condition, :result) do
6
+ include Node
7
7
 
8
- def children = [condition, result]
9
- end
8
+ def children = [condition, result]
9
+ end
10
10
  end
11
11
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- DeclarationReference = Struct.new(:name) do
6
- include Node
5
+ DeclarationReference = Struct.new(:name) do
6
+ include Node
7
7
 
8
- def children = []
9
- end
8
+ def children = []
9
+ end
10
10
  end
11
11
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- HashExpression = Struct.new(:pairs) do
6
- include Node
5
+ HashExpression = Struct.new(:pairs) do
6
+ include Node
7
7
 
8
- def children = pairs.flatten
9
- end
8
+ def children = pairs.flatten
9
+ end
10
10
  end
11
11
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- # For field metadata declarations inside input blocks
6
- InputDeclaration = Struct.new(:name, :domain, :type, :children) do
7
- include Node
5
+ # For field metadata declarations inside input blocks
6
+ InputDeclaration = Struct.new(:name, :domain, :type, :children) do
7
+ include Node
8
8
 
9
- def children = self[:children] || []
10
- end
9
+ def children = self[:children] || []
10
+ end
11
11
  end
12
12
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- # For field usage/reference in expressions (input.field_name)
6
- InputElementReference = Struct.new(:path) do
7
- include Node
5
+ # For field usage/reference in expressions (input.field_name)
6
+ InputElementReference = Struct.new(:path) do
7
+ include Node
8
8
 
9
- def children = []
10
- end
9
+ def children = []
10
+ end
11
11
  end
12
12
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- # For field usage/reference in expressions (input.field_name)
6
- InputReference = Struct.new(:name) do
7
- include Node
5
+ # For field usage/reference in expressions (input.field_name)
6
+ InputReference = Struct.new(:name) do
7
+ include Node
8
8
 
9
- def children = []
10
- end
9
+ def children = []
10
+ end
11
11
  end
12
12
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Kumi
4
4
  module Syntax
5
- Literal = Struct.new(:value) do
6
- include Node
5
+ Literal = Struct.new(:value) do
6
+ include Node
7
7
 
8
- def children = []
9
- end
8
+ def children = []
9
+ end
10
10
  end
11
11
  end