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.
Files changed (175) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +1 -1
  3. data/README.md +21 -5
  4. data/docs/AST.md +7 -0
  5. data/docs/features/README.md +7 -0
  6. data/docs/features/s-expression-printer.md +77 -0
  7. data/examples/game_of_life.rb +1 -1
  8. data/examples/static_analysis_errors.rb +7 -7
  9. data/lib/kumi/analyzer.rb +15 -15
  10. data/lib/kumi/compiler.rb +6 -6
  11. data/lib/kumi/core/analyzer/analysis_state.rb +39 -0
  12. data/lib/kumi/core/analyzer/constant_evaluator.rb +59 -0
  13. data/lib/kumi/core/analyzer/passes/broadcast_detector.rb +248 -0
  14. data/lib/kumi/core/analyzer/passes/declaration_validator.rb +45 -0
  15. data/lib/kumi/core/analyzer/passes/dependency_resolver.rb +153 -0
  16. data/lib/kumi/core/analyzer/passes/input_collector.rb +139 -0
  17. data/lib/kumi/core/analyzer/passes/name_indexer.rb +26 -0
  18. data/lib/kumi/core/analyzer/passes/pass_base.rb +52 -0
  19. data/lib/kumi/core/analyzer/passes/semantic_constraint_validator.rb +111 -0
  20. data/lib/kumi/core/analyzer/passes/toposorter.rb +110 -0
  21. data/lib/kumi/core/analyzer/passes/type_checker.rb +162 -0
  22. data/lib/kumi/core/analyzer/passes/type_consistency_checker.rb +48 -0
  23. data/lib/kumi/core/analyzer/passes/type_inferencer.rb +236 -0
  24. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +406 -0
  25. data/lib/kumi/core/analyzer/passes/visitor_pass.rb +44 -0
  26. data/lib/kumi/core/atom_unsat_solver.rb +396 -0
  27. data/lib/kumi/core/compiled_schema.rb +43 -0
  28. data/lib/kumi/core/constraint_relationship_solver.rb +641 -0
  29. data/lib/kumi/core/domain/enum_analyzer.rb +55 -0
  30. data/lib/kumi/core/domain/range_analyzer.rb +85 -0
  31. data/lib/kumi/core/domain/validator.rb +82 -0
  32. data/lib/kumi/core/domain/violation_formatter.rb +42 -0
  33. data/lib/kumi/core/error_reporter.rb +166 -0
  34. data/lib/kumi/core/error_reporting.rb +97 -0
  35. data/lib/kumi/core/errors.rb +120 -0
  36. data/lib/kumi/core/evaluation_wrapper.rb +40 -0
  37. data/lib/kumi/core/explain.rb +295 -0
  38. data/lib/kumi/core/export/deserializer.rb +41 -0
  39. data/lib/kumi/core/export/errors.rb +14 -0
  40. data/lib/kumi/core/export/node_builders.rb +142 -0
  41. data/lib/kumi/core/export/node_registry.rb +54 -0
  42. data/lib/kumi/core/export/node_serializers.rb +158 -0
  43. data/lib/kumi/core/export/serializer.rb +25 -0
  44. data/lib/kumi/core/export.rb +35 -0
  45. data/lib/kumi/core/function_registry/collection_functions.rb +202 -0
  46. data/lib/kumi/core/function_registry/comparison_functions.rb +33 -0
  47. data/lib/kumi/core/function_registry/conditional_functions.rb +38 -0
  48. data/lib/kumi/core/function_registry/function_builder.rb +95 -0
  49. data/lib/kumi/core/function_registry/logical_functions.rb +44 -0
  50. data/lib/kumi/core/function_registry/math_functions.rb +74 -0
  51. data/lib/kumi/core/function_registry/string_functions.rb +57 -0
  52. data/lib/kumi/core/function_registry/type_functions.rb +53 -0
  53. data/lib/kumi/{function_registry.rb → core/function_registry.rb} +28 -36
  54. data/lib/kumi/core/input/type_matcher.rb +97 -0
  55. data/lib/kumi/core/input/validator.rb +51 -0
  56. data/lib/kumi/core/input/violation_creator.rb +52 -0
  57. data/lib/kumi/core/json_schema/generator.rb +65 -0
  58. data/lib/kumi/core/json_schema/validator.rb +27 -0
  59. data/lib/kumi/core/json_schema.rb +16 -0
  60. data/lib/kumi/core/ruby_parser/build_context.rb +27 -0
  61. data/lib/kumi/core/ruby_parser/declaration_reference_proxy.rb +38 -0
  62. data/lib/kumi/core/ruby_parser/dsl.rb +14 -0
  63. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +138 -0
  64. data/lib/kumi/core/ruby_parser/expression_converter.rb +128 -0
  65. data/lib/kumi/core/ruby_parser/guard_rails.rb +45 -0
  66. data/lib/kumi/core/ruby_parser/input_builder.rb +127 -0
  67. data/lib/kumi/core/ruby_parser/input_field_proxy.rb +48 -0
  68. data/lib/kumi/core/ruby_parser/input_proxy.rb +31 -0
  69. data/lib/kumi/core/ruby_parser/nested_input.rb +17 -0
  70. data/lib/kumi/core/ruby_parser/parser.rb +71 -0
  71. data/lib/kumi/core/ruby_parser/schema_builder.rb +175 -0
  72. data/lib/kumi/core/ruby_parser/sugar.rb +263 -0
  73. data/lib/kumi/core/ruby_parser.rb +12 -0
  74. data/lib/kumi/core/schema_instance.rb +111 -0
  75. data/lib/kumi/core/types/builder.rb +23 -0
  76. data/lib/kumi/core/types/compatibility.rb +96 -0
  77. data/lib/kumi/core/types/formatter.rb +26 -0
  78. data/lib/kumi/core/types/inference.rb +42 -0
  79. data/lib/kumi/core/types/normalizer.rb +72 -0
  80. data/lib/kumi/core/types/validator.rb +37 -0
  81. data/lib/kumi/core/types.rb +66 -0
  82. data/lib/kumi/core/vectorization_metadata.rb +110 -0
  83. data/lib/kumi/errors.rb +1 -112
  84. data/lib/kumi/registry.rb +37 -0
  85. data/lib/kumi/schema.rb +5 -5
  86. data/lib/kumi/schema_metadata.rb +3 -3
  87. data/lib/kumi/support/s_expression_printer.rb +161 -0
  88. data/lib/kumi/syntax/array_expression.rb +6 -6
  89. data/lib/kumi/syntax/call_expression.rb +4 -4
  90. data/lib/kumi/syntax/cascade_expression.rb +4 -4
  91. data/lib/kumi/syntax/case_expression.rb +4 -4
  92. data/lib/kumi/syntax/declaration_reference.rb +4 -4
  93. data/lib/kumi/syntax/hash_expression.rb +4 -4
  94. data/lib/kumi/syntax/input_declaration.rb +5 -5
  95. data/lib/kumi/syntax/input_element_reference.rb +5 -5
  96. data/lib/kumi/syntax/input_reference.rb +5 -5
  97. data/lib/kumi/syntax/literal.rb +4 -4
  98. data/lib/kumi/syntax/node.rb +34 -34
  99. data/lib/kumi/syntax/root.rb +6 -6
  100. data/lib/kumi/syntax/trait_declaration.rb +4 -4
  101. data/lib/kumi/syntax/value_declaration.rb +4 -4
  102. data/lib/kumi/version.rb +1 -1
  103. data/migrate_to_core_iterative.rb +938 -0
  104. data/scripts/generate_function_docs.rb +9 -9
  105. metadata +77 -72
  106. data/lib/kumi/analyzer/analysis_state.rb +0 -37
  107. data/lib/kumi/analyzer/constant_evaluator.rb +0 -57
  108. data/lib/kumi/analyzer/passes/broadcast_detector.rb +0 -246
  109. data/lib/kumi/analyzer/passes/declaration_validator.rb +0 -43
  110. data/lib/kumi/analyzer/passes/dependency_resolver.rb +0 -151
  111. data/lib/kumi/analyzer/passes/input_collector.rb +0 -137
  112. data/lib/kumi/analyzer/passes/name_indexer.rb +0 -24
  113. data/lib/kumi/analyzer/passes/pass_base.rb +0 -50
  114. data/lib/kumi/analyzer/passes/semantic_constraint_validator.rb +0 -109
  115. data/lib/kumi/analyzer/passes/toposorter.rb +0 -108
  116. data/lib/kumi/analyzer/passes/type_checker.rb +0 -160
  117. data/lib/kumi/analyzer/passes/type_consistency_checker.rb +0 -46
  118. data/lib/kumi/analyzer/passes/type_inferencer.rb +0 -232
  119. data/lib/kumi/analyzer/passes/unsat_detector.rb +0 -404
  120. data/lib/kumi/analyzer/passes/visitor_pass.rb +0 -42
  121. data/lib/kumi/atom_unsat_solver.rb +0 -394
  122. data/lib/kumi/compiled_schema.rb +0 -41
  123. data/lib/kumi/constraint_relationship_solver.rb +0 -638
  124. data/lib/kumi/domain/enum_analyzer.rb +0 -53
  125. data/lib/kumi/domain/range_analyzer.rb +0 -83
  126. data/lib/kumi/domain/validator.rb +0 -80
  127. data/lib/kumi/domain/violation_formatter.rb +0 -40
  128. data/lib/kumi/error_reporter.rb +0 -164
  129. data/lib/kumi/error_reporting.rb +0 -95
  130. data/lib/kumi/evaluation_wrapper.rb +0 -38
  131. data/lib/kumi/explain.rb +0 -293
  132. data/lib/kumi/export/deserializer.rb +0 -39
  133. data/lib/kumi/export/errors.rb +0 -12
  134. data/lib/kumi/export/node_builders.rb +0 -140
  135. data/lib/kumi/export/node_registry.rb +0 -52
  136. data/lib/kumi/export/node_serializers.rb +0 -156
  137. data/lib/kumi/export/serializer.rb +0 -23
  138. data/lib/kumi/export.rb +0 -33
  139. data/lib/kumi/function_registry/collection_functions.rb +0 -200
  140. data/lib/kumi/function_registry/comparison_functions.rb +0 -31
  141. data/lib/kumi/function_registry/conditional_functions.rb +0 -36
  142. data/lib/kumi/function_registry/function_builder.rb +0 -93
  143. data/lib/kumi/function_registry/logical_functions.rb +0 -42
  144. data/lib/kumi/function_registry/math_functions.rb +0 -72
  145. data/lib/kumi/function_registry/string_functions.rb +0 -54
  146. data/lib/kumi/function_registry/type_functions.rb +0 -51
  147. data/lib/kumi/input/type_matcher.rb +0 -95
  148. data/lib/kumi/input/validator.rb +0 -49
  149. data/lib/kumi/input/violation_creator.rb +0 -50
  150. data/lib/kumi/json_schema/generator.rb +0 -63
  151. data/lib/kumi/json_schema/validator.rb +0 -25
  152. data/lib/kumi/json_schema.rb +0 -14
  153. data/lib/kumi/ruby_parser/build_context.rb +0 -25
  154. data/lib/kumi/ruby_parser/declaration_reference_proxy.rb +0 -36
  155. data/lib/kumi/ruby_parser/dsl.rb +0 -12
  156. data/lib/kumi/ruby_parser/dsl_cascade_builder.rb +0 -136
  157. data/lib/kumi/ruby_parser/expression_converter.rb +0 -126
  158. data/lib/kumi/ruby_parser/guard_rails.rb +0 -43
  159. data/lib/kumi/ruby_parser/input_builder.rb +0 -125
  160. data/lib/kumi/ruby_parser/input_field_proxy.rb +0 -46
  161. data/lib/kumi/ruby_parser/input_proxy.rb +0 -29
  162. data/lib/kumi/ruby_parser/nested_input.rb +0 -15
  163. data/lib/kumi/ruby_parser/parser.rb +0 -69
  164. data/lib/kumi/ruby_parser/schema_builder.rb +0 -173
  165. data/lib/kumi/ruby_parser/sugar.rb +0 -261
  166. data/lib/kumi/ruby_parser.rb +0 -10
  167. data/lib/kumi/schema_instance.rb +0 -109
  168. data/lib/kumi/types/builder.rb +0 -21
  169. data/lib/kumi/types/compatibility.rb +0 -94
  170. data/lib/kumi/types/formatter.rb +0 -24
  171. data/lib/kumi/types/inference.rb +0 -40
  172. data/lib/kumi/types/normalizer.rb +0 -70
  173. data/lib/kumi/types/validator.rb +0 -35
  174. data/lib/kumi/types.rb +0 -64
  175. data/lib/kumi/vectorization_metadata.rb +0 -108
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module Export
6
+ module NodeSerializers
7
+ # Root node: top-level container
8
+ def serialize_root(node)
9
+ {
10
+ type: "root",
11
+ inputs: node.inputs.map { |input| serialize_node(input) },
12
+ attributes: node.attributes.map { |attr| serialize_node(attr) },
13
+ traits: node.traits.map { |trait| serialize_node(trait) }
14
+ }
15
+ end
16
+
17
+ # Field Declaration: preserves type info for analyzer
18
+ def serialize_field_declaration(node)
19
+ {
20
+ name: node.name.to_s,
21
+ name_type: node.name.class.name,
22
+ field_type: serialize_type(node.type),
23
+ domain: serialize_domain(node.domain)
24
+ }
25
+ end
26
+
27
+ # Attribute Declaration: preserves name and expression tree
28
+ def serialize_attribute_declaration(node)
29
+ {
30
+ name: node.name.to_s,
31
+ name_type: node.name.class.name,
32
+ expression: serialize_node(node.expression)
33
+ }
34
+ end
35
+
36
+ # Trait Declaration: preserves name and expression tree
37
+ def serialize_trait_declaration(node)
38
+ {
39
+ name: node.name.to_s,
40
+ name_type: node.name.class.name,
41
+ expression: serialize_node(node.expression)
42
+ }
43
+ end
44
+
45
+ # Call Expression: critical for dependency analysis
46
+ def serialize_call_expression(node)
47
+ {
48
+ function_name: node.fn_name.to_s,
49
+ function_name_type: node.fn_name.class.name,
50
+ arguments: node.args.map { |arg| serialize_node(arg) }
51
+ }
52
+ end
53
+
54
+ # Literal: preserve exact value and Ruby type
55
+ def serialize_literal(node)
56
+ {
57
+ value: node.value,
58
+ ruby_type: node.value.class.name
59
+ }
60
+ end
61
+
62
+ # Field Reference: critical for dependency resolution
63
+ def serialize_field_reference(node)
64
+ {
65
+ field_name: node.name.to_s,
66
+ name_type: node.name.class.name
67
+ }
68
+ end
69
+
70
+ # DeclarationReference Reference: critical for dependency resolution
71
+ def serialize_binding_reference(node)
72
+ {
73
+ binding_name: node.name.to_s,
74
+ name_type: node.name.class.name
75
+ }
76
+ end
77
+
78
+ # List Expression: preserve order and elements
79
+ def serialize_list_expression(node)
80
+ {
81
+ elements: node.elements.map { |element| serialize_node(element) }
82
+ }
83
+ end
84
+
85
+ # Cascade Expression: preserve condition/result pairs
86
+ def serialize_cascade_expression(node)
87
+ {
88
+ cases: node.cases.map { |case_node| serialize_node(case_node) }
89
+ }
90
+ end
91
+
92
+ # When Case Expression: individual case in cascade
93
+ def serialize_when_case_expression(node)
94
+ {
95
+ condition: serialize_node(node.condition),
96
+ result: serialize_node(node.result)
97
+ }
98
+ end
99
+
100
+ private
101
+
102
+ def serialize_type(type)
103
+ case type
104
+ when Symbol
105
+ { type: "symbol", value: type.to_s }
106
+ when Hash
107
+ if type.key?(:array)
108
+ { type: "array", element_type: serialize_type(type[:array]) }
109
+ elsif type.key?(:hash)
110
+ { type: "hash", key_type: serialize_type(type[:hash][0]), value_type: serialize_type(type[:hash][1]) }
111
+ else
112
+ { type: "hash", value: type }
113
+ end
114
+ when String, Integer, Float, TrueClass, FalseClass, NilClass
115
+ { type: "literal", value: type }
116
+ else
117
+ { type: "unknown", value: type.to_s }
118
+ end
119
+ end
120
+
121
+ def serialize_domain(domain)
122
+ return nil unless domain
123
+
124
+ case domain
125
+ when Range
126
+ { type: "range", min: domain.min, max: domain.max, exclude_end: domain.exclude_end? }
127
+ when Array
128
+ { type: "array", values: domain }
129
+ else
130
+ { type: "custom", value: domain.to_s }
131
+ end
132
+ end
133
+
134
+ def serialize_node(node)
135
+ type_name = NodeRegistry.type_name_for(node)
136
+
137
+ base_data = {
138
+ type: type_name,
139
+ **send("serialize_#{type_name}", node)
140
+ }
141
+
142
+ add_location_if_present(base_data, node) if @include_locations
143
+ base_data
144
+ end
145
+
146
+ def add_location_if_present(data, node)
147
+ return unless node.respond_to?(:loc) && node.loc
148
+
149
+ data[:location] = {
150
+ line: node.loc.line,
151
+ column: node.loc.column,
152
+ file: node.loc.file
153
+ }
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module Export
6
+ class Serializer
7
+ include NodeSerializers
8
+
9
+ def initialize(pretty: false, include_locations: false)
10
+ @pretty = pretty
11
+ @include_locations = include_locations
12
+ end
13
+
14
+ def serialize(syntax_root)
15
+ json_data = {
16
+ kumi_version: VERSION,
17
+ ast: serialize_root(syntax_root)
18
+ }
19
+
20
+ @pretty ? JSON.pretty_generate(json_data) : JSON.generate(json_data)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Kumi
6
+ module Core
7
+ module Export
8
+ # Core interface - only depends on Syntax::Root
9
+ def self.to_json(syntax_root, **options)
10
+ Serializer.new(**options).serialize(syntax_root)
11
+ end
12
+
13
+ def self.from_json(json_string, **options)
14
+ Deserializer.new(**options).deserialize(json_string)
15
+ end
16
+
17
+ # Convenience methods
18
+ def self.to_file(syntax_root, filepath, **options)
19
+ File.write(filepath, to_json(syntax_root, **options))
20
+ end
21
+
22
+ def self.from_file(filepath, **options)
23
+ from_json(File.read(filepath), **options)
24
+ end
25
+
26
+ # Validation without import
27
+ def self.valid?(json_string)
28
+ from_json(json_string)
29
+ true
30
+ rescue StandardError
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module FunctionRegistry
6
+ # Collection manipulation and query functions
7
+ module CollectionFunctions
8
+ def self.definitions
9
+ {
10
+ # Collection queries (these are reducers - they reduce arrays to scalars)
11
+ empty?: FunctionBuilder.collection_unary(:empty?, "Check if collection is empty", :empty?, reducer: true),
12
+ size: FunctionBuilder.collection_unary(:size, "Get collection size", :size, return_type: :integer, reducer: true),
13
+ length: FunctionBuilder.collection_unary(:length, "Get collection length", :length, return_type: :integer, reducer: true),
14
+
15
+ # Element access
16
+ first: FunctionBuilder::Entry.new(
17
+ fn: lambda(&:first),
18
+ arity: 1,
19
+ param_types: [Kumi::Core::Types.array(:any)],
20
+ return_type: :any,
21
+ description: "Get first element of collection",
22
+ reducer: true
23
+ ),
24
+
25
+ last: FunctionBuilder::Entry.new(
26
+ fn: lambda(&:last),
27
+ arity: 1,
28
+ param_types: [Kumi::Core::Types.array(:any)],
29
+ return_type: :any,
30
+ description: "Get last element of collection",
31
+ reducer: true
32
+ ),
33
+
34
+ # Mathematical operations on collections
35
+ sum: FunctionBuilder::Entry.new(
36
+ fn: lambda(&:sum),
37
+ arity: 1,
38
+ param_types: [Kumi::Core::Types.array(:float)],
39
+ return_type: :float,
40
+ description: "Sum all numeric elements in collection",
41
+ reducer: true
42
+ ),
43
+
44
+ min: FunctionBuilder::Entry.new(
45
+ fn: lambda(&:min),
46
+ arity: 1,
47
+ param_types: [Kumi::Core::Types.array(:float)],
48
+ return_type: :float,
49
+ description: "Find minimum value in numeric collection",
50
+ reducer: true
51
+ ),
52
+
53
+ max: FunctionBuilder::Entry.new(
54
+ fn: lambda(&:max),
55
+ arity: 1,
56
+ param_types: [Kumi::Core::Types.array(:float)],
57
+ return_type: :float,
58
+ description: "Find maximum value in numeric collection",
59
+ reducer: true
60
+ ),
61
+
62
+ # Collection operations
63
+ include?: FunctionBuilder::Entry.new(
64
+ fn: ->(collection, element) { collection.include?(element) },
65
+ arity: 2,
66
+ param_types: [Kumi::Core::Types.array(:any), :any],
67
+ return_type: :boolean,
68
+ description: "Check if collection includes element"
69
+ ),
70
+
71
+ reverse: FunctionBuilder::Entry.new(
72
+ fn: lambda(&:reverse),
73
+ arity: 1,
74
+ param_types: [Kumi::Core::Types.array(:any)],
75
+ return_type: Kumi::Core::Types.array(:any),
76
+ description: "Reverse collection order"
77
+ ),
78
+
79
+ sort: FunctionBuilder::Entry.new(
80
+ fn: lambda(&:sort),
81
+ arity: 1,
82
+ param_types: [Kumi::Core::Types.array(:any)],
83
+ return_type: Kumi::Core::Types.array(:any),
84
+ description: "Sort collection"
85
+ ),
86
+
87
+ unique: FunctionBuilder::Entry.new(
88
+ fn: lambda(&:uniq),
89
+ arity: 1,
90
+ param_types: [Kumi::Core::Types.array(:any)],
91
+ return_type: Kumi::Core::Types.array(:any),
92
+ description: "Remove duplicate elements from collection"
93
+ ),
94
+
95
+ # Array transformation functions
96
+ flatten: FunctionBuilder::Entry.new(
97
+ fn: lambda(&:flatten),
98
+ arity: 1,
99
+ param_types: [Kumi::Core::Types.array(:any)],
100
+ return_type: Kumi::Core::Types.array(:any),
101
+ description: "Flatten nested arrays into a single array"
102
+ ),
103
+
104
+ # Mathematical transformation functions
105
+ map_multiply: FunctionBuilder::Entry.new(
106
+ fn: ->(collection, factor) { collection.map { |x| x * factor } },
107
+ arity: 2,
108
+ param_types: [Kumi::Core::Types.array(:float), :float],
109
+ return_type: Kumi::Core::Types.array(:float),
110
+ description: "Multiply each element by factor"
111
+ ),
112
+
113
+ map_add: FunctionBuilder::Entry.new(
114
+ fn: ->(collection, value) { collection.map { |x| x + value } },
115
+ arity: 2,
116
+ param_types: [Kumi::Core::Types.array(:float), :float],
117
+ return_type: Kumi::Core::Types.array(:float),
118
+ description: "Add value to each element"
119
+ ),
120
+
121
+ # Conditional transformation functions
122
+ map_conditional: FunctionBuilder::Entry.new(
123
+ fn: lambda { |collection, condition_value, true_value, false_value|
124
+ collection.map { |x| x == condition_value ? true_value : false_value }
125
+ },
126
+ arity: 4,
127
+ param_types: %i[array any any any],
128
+ return_type: :array,
129
+ description: "Transform elements based on condition: if element == condition_value then true_value else false_value"
130
+ ),
131
+
132
+ # Range/index functions for grid operations
133
+ build_array: FunctionBuilder::Entry.new(
134
+ fn: lambda { |size, &generator|
135
+ (0...size).map { |i| generator ? generator.call(i) : i }
136
+ },
137
+ arity: 1,
138
+ param_types: [:integer],
139
+ return_type: Kumi::Core::Types.array(:any),
140
+ description: "Build array of given size with index values"
141
+ ),
142
+
143
+ range: FunctionBuilder::Entry.new(
144
+ fn: ->(start, finish) { (start...finish).to_a },
145
+ arity: 2,
146
+ param_types: %i[integer integer],
147
+ return_type: Kumi::Core::Types.array(:integer),
148
+ description: "Generate range of integers from start to finish (exclusive)"
149
+ ),
150
+
151
+ # Array slicing and grouping for rendering
152
+ each_slice: FunctionBuilder::Entry.new(
153
+ fn: ->(array, size) { array.each_slice(size).to_a },
154
+ arity: 2,
155
+ param_types: %i[array integer],
156
+ return_type: Kumi::Core::Types.array(:array),
157
+ description: "Group array elements into subarrays of given size"
158
+ ),
159
+
160
+ join: FunctionBuilder::Entry.new(
161
+ fn: lambda { |array, separator = ""|
162
+ array.map(&:to_s).join(separator.to_s)
163
+ },
164
+ arity: 2,
165
+ param_types: %i[array string],
166
+ return_type: :string,
167
+ description: "Join array elements into string with separator"
168
+ ),
169
+
170
+ # Transform each subarray to string and join the results
171
+ map_join_rows: FunctionBuilder::Entry.new(
172
+ fn: lambda { |array_of_arrays, row_separator = "", column_separator = "\n"|
173
+ array_of_arrays.map { |row| row.join(row_separator.to_s) }.join(column_separator.to_s)
174
+ },
175
+ arity: 3,
176
+ param_types: [Kumi::Core::Types.array(:array), :string, :string],
177
+ return_type: :string,
178
+ description: "Join 2D array into string with row and column separators"
179
+ ),
180
+
181
+ # Higher-order collection functions (limited to common patterns)
182
+ map_with_index: FunctionBuilder::Entry.new(
183
+ fn: ->(collection) { collection.map.with_index.to_a },
184
+ arity: 1,
185
+ param_types: [Kumi::Core::Types.array(:any)],
186
+ return_type: Kumi::Core::Types.array(:any),
187
+ description: "Map collection elements to [element, index] pairs"
188
+ ),
189
+
190
+ indices: FunctionBuilder::Entry.new(
191
+ fn: ->(collection) { (0...collection.size).to_a },
192
+ arity: 1,
193
+ param_types: [Kumi::Core::Types.array(:any)],
194
+ return_type: Kumi::Core::Types.array(:integer),
195
+ description: "Generate array of indices for the collection"
196
+ )
197
+ }
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module FunctionRegistry
6
+ # Comparison and equality functions
7
+ module ComparisonFunctions
8
+ def self.definitions
9
+ {
10
+ # Equality operators
11
+ :== => FunctionBuilder.equality(:==, "Equality comparison", :==),
12
+ :!= => FunctionBuilder.equality(:!=, "Inequality comparison", :!=),
13
+
14
+ # Comparison operators
15
+ :> => FunctionBuilder.comparison(:>, "Greater than comparison", :>),
16
+ :< => FunctionBuilder.comparison(:<, "Less than comparison", :<),
17
+ :>= => FunctionBuilder.comparison(:>=, "Greater than or equal comparison", :>=),
18
+ :<= => FunctionBuilder.comparison(:<=, "Less than or equal comparison", :<=),
19
+
20
+ # Range comparison
21
+ :between? => FunctionBuilder::Entry.new(
22
+ fn: ->(value, min, max) { value.between?(min, max) },
23
+ arity: 3,
24
+ param_types: %i[float float float],
25
+ return_type: :boolean,
26
+ description: "Check if value is between min and max"
27
+ )
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module FunctionRegistry
6
+ # Conditional and control flow functions
7
+ module ConditionalFunctions
8
+ def self.definitions
9
+ {
10
+ conditional: FunctionBuilder::Entry.new(
11
+ fn: ->(condition, true_value, false_value) { condition ? true_value : false_value },
12
+ arity: 3,
13
+ param_types: %i[boolean any any],
14
+ return_type: :any,
15
+ description: "Ternary conditional operator"
16
+ ),
17
+
18
+ if: FunctionBuilder::Entry.new(
19
+ fn: ->(condition, true_value, false_value = nil) { condition ? true_value : false_value },
20
+ arity: -1, # Variable arity (2 or 3)
21
+ param_types: %i[boolean any any],
22
+ return_type: :any,
23
+ description: "If-then-else conditional"
24
+ ),
25
+
26
+ coalesce: FunctionBuilder::Entry.new(
27
+ fn: ->(*values) { values.find { |v| !v.nil? } },
28
+ arity: -1, # Variable arity
29
+ param_types: [:any],
30
+ return_type: :any,
31
+ description: "Return first non-nil value"
32
+ )
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module FunctionRegistry
6
+ # Utility class to reduce repetition in function definitions
7
+ class FunctionBuilder
8
+ Entry = Struct.new(:fn, :arity, :param_types, :return_type, :description, :inverse, :reducer, keyword_init: true)
9
+
10
+ def self.comparison(_name, description, operation)
11
+ Entry.new(
12
+ fn: ->(a, b) { a.public_send(operation, b) },
13
+ arity: 2,
14
+ param_types: %i[float float],
15
+ return_type: :boolean,
16
+ description: description
17
+ )
18
+ end
19
+
20
+ def self.equality(_name, description, operation)
21
+ Entry.new(
22
+ fn: ->(a, b) { a.public_send(operation, b) },
23
+ arity: 2,
24
+ param_types: %i[any any],
25
+ return_type: :boolean,
26
+ description: description
27
+ )
28
+ end
29
+
30
+ def self.math_binary(_name, description, operation, return_type: :float)
31
+ Entry.new(
32
+ fn: lambda { |a, b|
33
+ a.public_send(operation, b)
34
+ },
35
+ arity: 2,
36
+ param_types: %i[float float],
37
+ return_type: return_type,
38
+ description: description
39
+ )
40
+ end
41
+
42
+ def self.math_unary(_name, description, operation, return_type: :float)
43
+ Entry.new(
44
+ fn: proc(&operation),
45
+ arity: 1,
46
+ param_types: [:float],
47
+ return_type: return_type,
48
+ description: description
49
+ )
50
+ end
51
+
52
+ def self.string_unary(_name, description, operation)
53
+ Entry.new(
54
+ fn: ->(str) { str.to_s.public_send(operation) },
55
+ arity: 1,
56
+ param_types: [:string],
57
+ return_type: :string,
58
+ description: description
59
+ )
60
+ end
61
+
62
+ def self.string_binary(_name, description, operation, return_type: :string)
63
+ Entry.new(
64
+ fn: ->(str, arg) { str.to_s.public_send(operation, arg.to_s) },
65
+ arity: 2,
66
+ param_types: %i[string string],
67
+ return_type: return_type,
68
+ description: description
69
+ )
70
+ end
71
+
72
+ def self.logical_variadic(_name, description, operation)
73
+ Entry.new(
74
+ fn: ->(conditions) { conditions.public_send(operation) },
75
+ arity: -1,
76
+ param_types: [:boolean],
77
+ return_type: :boolean,
78
+ description: description
79
+ )
80
+ end
81
+
82
+ def self.collection_unary(_name, description, operation, return_type: :boolean, reducer: false)
83
+ Entry.new(
84
+ fn: proc(&operation),
85
+ arity: 1,
86
+ param_types: [Kumi::Core::Types.array(:any)],
87
+ return_type: return_type,
88
+ description: description,
89
+ reducer: reducer
90
+ )
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kumi
4
+ module Core
5
+ module FunctionRegistry
6
+ # Logical operations and boolean functions
7
+ module LogicalFunctions
8
+ def self.definitions
9
+ {
10
+ # Basic logical operations
11
+ and: FunctionBuilder::Entry.new(
12
+ fn: ->(*conditions) { conditions.all? },
13
+ arity: -1,
14
+ param_types: [:boolean],
15
+ return_type: :boolean,
16
+ description: "Logical AND of multiple conditions"
17
+ ),
18
+
19
+ or: FunctionBuilder::Entry.new(
20
+ fn: ->(*conditions) { conditions.any? },
21
+ arity: -1,
22
+ param_types: [:boolean],
23
+ return_type: :boolean,
24
+ description: "Logical OR of multiple conditions"
25
+ ),
26
+
27
+ not: FunctionBuilder::Entry.new(
28
+ fn: lambda(&:!),
29
+ arity: 1,
30
+ param_types: [:boolean],
31
+ return_type: :boolean,
32
+ description: "Logical NOT"
33
+ ),
34
+
35
+ # Collection logical operations
36
+ all?: FunctionBuilder.collection_unary(:all?, "Check if all elements in collection are truthy", :all?),
37
+ any?: FunctionBuilder.collection_unary(:any?, "Check if any element in collection is truthy", :any?),
38
+ none?: FunctionBuilder.collection_unary(:none?, "Check if no elements in collection are truthy", :none?)
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end