kumi 0.0.29 → 0.0.30

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -1
  3. data/CHANGELOG.md +12 -0
  4. data/README.md +5 -6
  5. data/docs/DEVELOPMENT.md +23 -1
  6. data/docs/GOLDEN_QUICK_START.md +141 -0
  7. data/docs/GOLDEN_TESTS.md +240 -0
  8. data/golden/array_element/expected/schema_ruby.rb +1 -1
  9. data/golden/array_index/expected/schema_ruby.rb +1 -1
  10. data/golden/array_operations/expected/schema_ruby.rb +1 -1
  11. data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
  12. data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
  13. data/golden/decimal_explicit/expected/schema_ruby.rb +1 -1
  14. data/golden/element_arrays/expected/schema_ruby.rb +1 -1
  15. data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
  16. data/golden/function_overload/expected/schema_ruby.rb +1 -1
  17. data/golden/game_of_life/expected/schema_ruby.rb +1 -1
  18. data/golden/hash_keys/expected/schema_ruby.rb +1 -1
  19. data/golden/hash_value/expected/schema_ruby.rb +1 -1
  20. data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
  21. data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
  22. data/golden/input_reference/expected/schema_ruby.rb +1 -1
  23. data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
  24. data/golden/let_inline/expected/schema_ruby.rb +1 -1
  25. data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
  26. data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
  27. data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
  28. data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
  29. data/golden/nested_hash/expected/schema_ruby.rb +1 -1
  30. data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
  31. data/golden/roll/expected/schema_ruby.rb +1 -1
  32. data/golden/shift/expected/schema_ruby.rb +1 -1
  33. data/golden/shift_2d/expected/schema_ruby.rb +1 -1
  34. data/golden/simple_math/expected/schema_ruby.rb +1 -1
  35. data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
  36. data/golden/tuples/expected/schema_ruby.rb +1 -1
  37. data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
  38. data/golden/us_tax_2024/expected/schema_ruby.rb +1 -1
  39. data/golden/with_constants/expected/schema_ruby.rb +1 -1
  40. data/lib/kumi/analyzer.rb +39 -79
  41. data/lib/kumi/core/analyzer/analysis_state.rb +2 -0
  42. data/lib/kumi/core/analyzer/constant_evaluator.rb +0 -2
  43. data/lib/kumi/core/analyzer/execution_phase.rb +24 -0
  44. data/lib/kumi/core/analyzer/execution_result.rb +38 -0
  45. data/lib/kumi/core/analyzer/pass_failure.rb +26 -0
  46. data/lib/kumi/core/analyzer/pass_manager.rb +136 -0
  47. data/lib/kumi/core/analyzer/passes/codegen/js/declaration_emitter.rb +1 -1
  48. data/lib/kumi/core/analyzer/passes/codegen/js_pass.rb +1 -1
  49. data/lib/kumi/core/analyzer/passes/codegen/ruby/declaration_emitter.rb +1 -1
  50. data/lib/kumi/core/analyzer/passes/codegen/ruby_pass.rb +1 -1
  51. data/lib/kumi/core/analyzer/passes/constant_folding_pass.rb +1 -1
  52. data/lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb +13 -31
  53. data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +1 -1
  54. data/lib/kumi/core/analyzer/passes/input_collector.rb +1 -1
  55. data/lib/kumi/core/analyzer/passes/input_form_schema_pass.rb +1 -1
  56. data/lib/kumi/core/analyzer/passes/lir/loop_invariant_code_motion_pass.rb +1 -1
  57. data/lib/kumi/core/analyzer/passes/lir/stencil_emitter.rb +0 -2
  58. data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +5 -1
  59. data/lib/kumi/core/analyzer/passes/output_schema_pass.rb +2 -1
  60. data/lib/kumi/core/analyzer/passes/snast_pass.rb +1 -1
  61. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +8 -10
  62. data/lib/kumi/core/compiler/access_planner_v2.rb +1 -2
  63. data/lib/kumi/core/functions/overload_resolver.rb +15 -19
  64. data/lib/kumi/core/functions/type_categories.rb +3 -3
  65. data/lib/kumi/core/functions/type_error_reporter.rb +1 -3
  66. data/lib/kumi/core/input/type_matcher.rb +1 -1
  67. data/lib/kumi/core/types/normalizer.rb +8 -10
  68. data/lib/kumi/core/types/validator.rb +1 -1
  69. data/lib/kumi/core/types/value_objects.rb +5 -2
  70. data/lib/kumi/core/types.rb +2 -4
  71. data/lib/kumi/dev/codegen.rb +1 -1
  72. data/lib/kumi/dev/golden/generator.rb +14 -10
  73. data/lib/kumi/dev/golden/reporter.rb +5 -5
  74. data/lib/kumi/dev/golden/representation.rb +1 -3
  75. data/lib/kumi/dev/golden/result.rb +1 -1
  76. data/lib/kumi/dev/golden/runtime_test.rb +0 -2
  77. data/lib/kumi/dev/golden/suite.rb +20 -4
  78. data/lib/kumi/dev/golden/value_normalizer.rb +1 -3
  79. data/lib/kumi/doc_generator/formatters/json.rb +11 -11
  80. data/lib/kumi/doc_generator/formatters/markdown.rb +35 -37
  81. data/lib/kumi/doc_generator/loader.rb +8 -6
  82. data/lib/kumi/doc_generator/merger.rb +12 -12
  83. data/lib/kumi/frontends/text.rb +4 -6
  84. data/lib/kumi/registry_v2/loader.rb +32 -33
  85. data/lib/kumi/schema.rb +2 -2
  86. data/lib/kumi/version.rb +1 -1
  87. metadata +13 -14
  88. data/debug_ordering.rb +0 -52
  89. data/examples/deep_schema_compilation_and_evaluation_benchmark.rb +0 -106
  90. data/examples/federal_tax_calculator_2024.rb +0 -115
  91. data/examples/game_of_life.rb +0 -95
  92. data/examples/simple_rpg_game.rb +0 -1000
  93. data/examples/static_analysis_errors.rb +0 -178
  94. data/examples/wide_schema_compilation_and_evaluation_benchmark.rb +0 -80
@@ -29,7 +29,7 @@ module Kumi
29
29
  def group_by_id(docs)
30
30
  result = {}
31
31
  docs.each do |alias_name, entry|
32
- id = entry['id']
32
+ id = entry["id"]
33
33
  result[id] ||= []
34
34
  result[id] << alias_name
35
35
  end
@@ -49,30 +49,28 @@ module Kumi
49
49
 
50
50
  lines << "- **Arity:** #{entry['arity']}"
51
51
 
52
- if entry['dtype']
53
- dtype_str = format_dtype(entry['dtype'])
52
+ if entry["dtype"]
53
+ dtype_str = format_dtype(entry["dtype"])
54
54
  lines << "- **Type:** #{dtype_str}"
55
55
  end
56
56
 
57
- if is_reducer?(entry)
58
- lines << "- **Behavior:** Reduces a dimension `[D] -> T`"
59
- end
57
+ lines << "- **Behavior:** Reduces a dimension `[D] -> T`" if is_reducer?(entry)
60
58
  lines << ""
61
59
 
62
- if entry['params'] && !entry['params'].empty?
60
+ if entry["params"] && !entry["params"].empty?
63
61
  lines << "### Parameters"
64
62
  lines << ""
65
- entry['params'].each do |param|
66
- lines << "- `#{param['name']}`#{param['description'] ? ": #{param['description']}" : ""}"
63
+ entry["params"].each do |param|
64
+ lines << "- `#{param['name']}`#{": #{param['description']}" if param['description']}"
67
65
  end
68
66
  lines << ""
69
67
  end
70
68
 
71
- if entry['kernels'] && !entry['kernels'].empty?
69
+ if entry["kernels"] && !entry["kernels"].empty?
72
70
  lines << "### Implementations"
73
71
  lines << ""
74
- entry['kernels'].each do |target, kernel|
75
- lines.concat(format_kernel(target, kernel, entry['reduction_strategy']))
72
+ entry["kernels"].each do |target, kernel|
73
+ lines.concat(format_kernel(target, kernel, entry["reduction_strategy"]))
76
74
  end
77
75
  end
78
76
 
@@ -88,48 +86,48 @@ module Kumi
88
86
  lines << "`#{kernel['id']}`"
89
87
  lines << ""
90
88
 
91
- has_identity = kernel['identity'] && !kernel['identity'].empty?
89
+ has_identity = kernel["identity"] && !kernel["identity"].empty?
92
90
 
93
- if kernel['inline'] && has_identity
91
+ if kernel["inline"] && has_identity
94
92
  lines << "**Inline:** `#{escape_backticks(kernel['inline'])}` (`$0` = accumulator, `$1` = element)"
95
93
  lines << ""
96
94
  end
97
95
 
98
- if kernel['impl']
96
+ if kernel["impl"]
99
97
  lines << "**Implementation:**"
100
98
  lines << ""
101
99
  lines << "```ruby"
102
- lines << format_impl(kernel['impl'])
100
+ lines << format_impl(kernel["impl"])
103
101
  lines << "```"
104
102
  lines << ""
105
103
  end
106
104
 
107
- if kernel['fold_inline']
105
+ if kernel["fold_inline"]
108
106
  lines << "**Fold:** `#{escape_backticks(kernel['fold_inline'])}`"
109
107
  lines << ""
110
108
  end
111
109
 
112
110
  if has_identity
113
111
  lines << "**Identity:**"
114
- kernel['identity'].each do |type, value|
112
+ kernel["identity"].each do |type, value|
115
113
  lines << "- #{type}: `#{value}`"
116
114
  end
117
115
  lines << ""
118
- elsif kernel['inline']
116
+ elsif kernel["inline"]
119
117
  lines << "_Note: No identity value. First element initializes accumulator._"
120
118
  lines << ""
121
119
  end
122
120
 
123
121
  # Show reduction strategy if available
124
122
  if reduction_strategy
125
- case reduction_strategy
126
- when 'identity'
127
- lines << "**Reduction:** Monoid operation with identity element"
128
- when 'first_element'
129
- lines << "**Reduction:** First element is initial value (no identity)"
130
- else
131
- lines << "**Reduction:** #{reduction_strategy}"
132
- end
123
+ lines << case reduction_strategy
124
+ when "identity"
125
+ "**Reduction:** Monoid operation with identity element"
126
+ when "first_element"
127
+ "**Reduction:** First element is initial value (no identity)"
128
+ else
129
+ "**Reduction:** #{reduction_strategy}"
130
+ end
133
131
  lines << ""
134
132
  end
135
133
  else
@@ -142,18 +140,18 @@ module Kumi
142
140
  def format_dtype(dtype)
143
141
  return "any" if dtype.nil?
144
142
 
145
- case dtype['rule']
146
- when 'same_as'
143
+ case dtype["rule"]
144
+ when "same_as"
147
145
  "same as `#{dtype['param']}`"
148
- when 'scalar'
149
- dtype['kind'] || 'scalar'
150
- when 'promote'
151
- params = Array(dtype['params']).join('`, `')
146
+ when "scalar"
147
+ dtype["kind"] || "scalar"
148
+ when "promote"
149
+ params = Array(dtype["params"]).join("`, `")
152
150
  "promoted from `#{params}`"
153
- when 'element_of'
151
+ when "element_of"
154
152
  "element of `#{dtype['param']}`"
155
153
  else
156
- dtype['rule']
154
+ dtype["rule"]
157
155
  end
158
156
  end
159
157
 
@@ -163,11 +161,11 @@ module Kumi
163
161
  end
164
162
 
165
163
  def escape_backticks(str)
166
- str.gsub('`', '\`')
164
+ str.gsub("`", '\`')
167
165
  end
168
166
 
169
167
  def is_reducer?(entry)
170
- entry['kind'] == 'reduce'
168
+ entry["kind"] == "reduce"
171
169
  end
172
170
  end
173
171
  end
@@ -1,4 +1,4 @@
1
- require 'yaml'
1
+ require "yaml"
2
2
 
3
3
  module Kumi
4
4
  module DocGenerator
@@ -10,11 +10,13 @@ module Kumi
10
10
 
11
11
  def load_functions
12
12
  return [] unless @functions_dir
13
+
13
14
  load_yaml_dir(@functions_dir)
14
15
  end
15
16
 
16
17
  def load_kernels
17
18
  return [] unless @kernels_dir
19
+
18
20
  load_yaml_dir(@kernels_dir)
19
21
  end
20
22
 
@@ -22,12 +24,12 @@ module Kumi
22
24
 
23
25
  def load_yaml_dir(dir_path)
24
26
  result = []
25
- Dir.glob(File.join(dir_path, '**/*.yaml')).each do |file|
27
+ Dir.glob(File.join(dir_path, "**/*.yaml")).each do |file|
26
28
  data = YAML.load_file(file)
27
- if data && data['functions']
28
- result.concat(data['functions'])
29
- elsif data && data['kernels']
30
- result.concat(data['kernels'])
29
+ if data && data["functions"]
30
+ result.concat(data["functions"])
31
+ elsif data && data["kernels"]
32
+ result.concat(data["kernels"])
31
33
  end
32
34
  end
33
35
  result
@@ -12,7 +12,7 @@ module Kumi
12
12
  result = {}
13
13
 
14
14
  functions.each do |fn|
15
- aliases = fn['aliases'] || []
15
+ aliases = fn["aliases"] || []
16
16
  aliases.each do |alias_name|
17
17
  result[alias_name] = build_doc_entry(fn, kernels)
18
18
  end
@@ -26,27 +26,27 @@ module Kumi
26
26
  def build_doc_entry(function, kernels)
27
27
  kernel_map = {}
28
28
  kernels.each do |kernel|
29
- if kernel['fn'] == function['id']
30
- target = extract_target(kernel['id'])
29
+ if kernel["fn"] == function["id"]
30
+ target = extract_target(kernel["id"])
31
31
  kernel_map[target] = kernel
32
32
  end
33
33
  end
34
34
 
35
35
  {
36
- 'id' => function['id'],
37
- 'kind' => function['kind'],
38
- 'params' => function['params'] || [],
39
- 'arity' => (function['params'] || []).length,
40
- 'kernels' => kernel_map,
41
- 'dtype' => function['dtype'],
42
- 'aliases' => function['aliases'] || [],
43
- 'reduction_strategy' => function['reduction_strategy']
36
+ "id" => function["id"],
37
+ "kind" => function["kind"],
38
+ "params" => function["params"] || [],
39
+ "arity" => (function["params"] || []).length,
40
+ "kernels" => kernel_map,
41
+ "dtype" => function["dtype"],
42
+ "aliases" => function["aliases"] || [],
43
+ "reduction_strategy" => function["reduction_strategy"]
44
44
  }
45
45
  end
46
46
 
47
47
  def extract_target(kernel_id)
48
48
  # kernel_id format: "agg.sum:ruby:v1" -> "ruby"
49
- parts = kernel_id.split(':')
49
+ parts = kernel_id.split(":")
50
50
  parts[1] if parts.length >= 2
51
51
  end
52
52
  end
@@ -30,9 +30,9 @@ module Kumi
30
30
  # Strip file:line:col prefix from e.message if it exists (from parser)
31
31
  # Also strip embedded "at FILE line=N column=M" to avoid duplication
32
32
  error_message = e.message
33
- .sub(/^\S+:\d+:\d+:\s+/, '')
34
- .gsub(/\s+at\s+\S+\s+line=\d+\s+column=\d+/, '')
35
- .strip
33
+ .sub(/^\S+:\d+:\d+:\s+/, "")
34
+ .gsub(/\s+at\s+\S+\s+line=\d+\s+column=\d+/, "")
35
+ .strip
36
36
  raise StandardError, "#{file_label}:#{line || '?'}:#{col || '?'}: #{error_message}\n#{snippet}"
37
37
  end
38
38
  end
@@ -41,9 +41,7 @@ module Kumi
41
41
  # Try to access Location object from exception
42
42
  if exception.respond_to?(:location) && exception.location
43
43
  loc = exception.location
44
- if loc.respond_to?(:line) && loc.respond_to?(:column)
45
- return [loc.line, loc.column]
46
- end
44
+ return [loc.line, loc.column] if loc.respond_to?(:line) && loc.respond_to?(:column)
47
45
  end
48
46
 
49
47
  # Fall back to parsing error message if no Location object
@@ -23,52 +23,52 @@ module Kumi
23
23
 
24
24
  # Build dtype rule from structured hash
25
25
  def build_dtype_rule_from_hash(spec)
26
- rule_type = spec.fetch('rule') { raise "dtype hash requires 'rule' key" }
26
+ rule_type = spec.fetch("rule") { raise "dtype hash requires 'rule' key" }
27
27
 
28
28
  case rule_type
29
- when 'same_as'
30
- param = spec.fetch('param') { raise "same_as rule requires 'param' key" }
29
+ when "same_as"
30
+ param = spec.fetch("param") { raise "same_as rule requires 'param' key" }
31
31
  Kumi::Core::Functions::TypeRules.build_same_as(param.to_sym)
32
32
 
33
- when 'promote'
34
- params = spec.fetch('params') { raise "promote rule requires 'params' key" }
33
+ when "promote"
34
+ params = spec.fetch("params") { raise "promote rule requires 'params' key" }
35
35
  param_syms = Array(params).map { |p| p.to_sym }
36
36
  Kumi::Core::Functions::TypeRules.build_promote(*param_syms)
37
37
 
38
- when 'element_of'
39
- param = spec.fetch('param') { raise "element_of rule requires 'param' key" }
38
+ when "element_of"
39
+ param = spec.fetch("param") { raise "element_of rule requires 'param' key" }
40
40
  Kumi::Core::Functions::TypeRules.build_element_of(param.to_sym)
41
41
 
42
- when 'unify'
43
- param1 = spec.fetch('param1') { raise "unify rule requires 'param1' key" }
44
- param2 = spec.fetch('param2') { raise "unify rule requires 'param2' key" }
42
+ when "unify"
43
+ param1 = spec.fetch("param1") { raise "unify rule requires 'param1' key" }
44
+ param2 = spec.fetch("param2") { raise "unify rule requires 'param2' key" }
45
45
  Kumi::Core::Functions::TypeRules.build_unify(param1.to_sym, param2.to_sym)
46
46
 
47
- when 'common_type'
48
- param = spec.fetch('param') { raise "common_type rule requires 'param' key" }
47
+ when "common_type"
48
+ param = spec.fetch("param") { raise "common_type rule requires 'param' key" }
49
49
  Kumi::Core::Functions::TypeRules.build_common_type(param.to_sym)
50
50
 
51
- when 'array'
52
- if spec.key?('element_type')
53
- element_type_spec = spec['element_type']
51
+ when "array"
52
+ if spec.key?("element_type")
53
+ element_type_spec = spec["element_type"]
54
54
  element_type = if element_type_spec.is_a?(Hash)
55
- # Nested structured format
56
- build_dtype_rule_from_hash(element_type_spec).call({})
57
- else
58
- # String or symbol
59
- element_type_spec.to_sym
60
- end
55
+ # Nested structured format
56
+ build_dtype_rule_from_hash(element_type_spec).call({})
57
+ else
58
+ # String or symbol
59
+ element_type_spec.to_sym
60
+ end
61
61
  Kumi::Core::Functions::TypeRules.build_array(element_type)
62
- elsif spec.key?('element_type_param')
63
- element_type_param = spec['element_type_param'].to_sym
62
+ elsif spec.key?("element_type_param")
63
+ element_type_param = spec["element_type_param"].to_sym
64
64
  Kumi::Core::Functions::TypeRules.build_array(element_type_param)
65
65
  else
66
66
  raise "array rule requires either 'element_type' or 'element_type_param' key"
67
67
  end
68
68
 
69
- when 'tuple'
70
- if spec.key?('element_types')
71
- element_types_spec = spec['element_types']
69
+ when "tuple"
70
+ if spec.key?("element_types")
71
+ element_types_spec = spec["element_types"]
72
72
  element_types = Array(element_types_spec).map do |et|
73
73
  if et.is_a?(Hash)
74
74
  build_dtype_rule_from_hash(et).call({})
@@ -77,19 +77,18 @@ module Kumi
77
77
  end
78
78
  end
79
79
  Kumi::Core::Functions::TypeRules.build_tuple(*element_types)
80
- elsif spec.key?('element_types_param')
81
- element_types_param = spec['element_types_param'].to_sym
80
+ elsif spec.key?("element_types_param")
81
+ element_types_param = spec["element_types_param"].to_sym
82
82
  Kumi::Core::Functions::TypeRules.build_tuple(element_types_param)
83
83
  else
84
84
  raise "tuple rule requires either 'element_types' or 'element_types_param' key"
85
85
  end
86
86
 
87
- when 'scalar'
88
- kind = spec.fetch('kind') { raise "scalar rule requires 'kind' key" }
87
+ when "scalar"
88
+ kind = spec.fetch("kind") { raise "scalar rule requires 'kind' key" }
89
89
  kind_sym = kind.to_sym
90
- unless Kumi::Core::Types::Validator.valid_kind?(kind_sym)
91
- raise "scalar rule has unknown kind: #{kind}"
92
- end
90
+ raise "scalar rule has unknown kind: #{kind}" unless Kumi::Core::Types::Validator.valid_kind?(kind_sym)
91
+
93
92
  Kumi::Core::Functions::TypeRules.build_scalar(kind_sym)
94
93
 
95
94
  else
data/lib/kumi/schema.rb CHANGED
@@ -11,7 +11,7 @@ module Kumi
11
11
  module Schema
12
12
  # The `__syntax_tree__` is available on the class for introspection.
13
13
  attr_reader :__kumi_syntax_tree__, :__kumi_compiled_module__
14
- alias_method :__syntax_tree__, :__kumi_syntax_tree__
14
+ alias __syntax_tree__ __kumi_syntax_tree__
15
15
 
16
16
  def build_syntax_tree(&)
17
17
  @__kumi_syntax_tree__ = Kumi::Core::RubyParser::Dsl.build_syntax_tree(&)
@@ -98,7 +98,7 @@ module Kumi
98
98
  end
99
99
  end
100
100
 
101
- def compile_and_write_cache(cache_path, digest)
101
+ def compile_and_write_cache(cache_path, _digest)
102
102
  # 1. Extract the generated code from the final state object.
103
103
  compiler_output = @__kumi_analyzer_result__.state[:ruby_codegen_files]["codegen.rb"]
104
104
  raise "Compiler did not produce ruby_codegen_files" unless compiler_output
data/lib/kumi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kumi
4
- VERSION = "0.0.29"
4
+ VERSION = "0.0.30"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kumi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.29
4
+ version: 0.0.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - André Muta
@@ -10,33 +10,33 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: zeitwerk
13
+ name: mutex_m
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 2.6.0
18
+ version: 0.3.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 2.6.0
25
+ version: 0.3.0
26
26
  - !ruby/object:Gem::Dependency
27
- name: mutex_m
27
+ name: zeitwerk
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.3.0
32
+ version: 2.6.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.3.0
39
+ version: 2.6.0
40
40
  email:
41
41
  - andremuta@gmail.com
42
42
  executables: []
@@ -82,22 +82,17 @@ files:
82
82
  - data/kernels/ruby/core/constructor.yaml
83
83
  - data/kernels/ruby/core/select.yaml
84
84
  - data/kernels/ruby/core/string.yaml
85
- - debug_ordering.rb
86
85
  - docs/ARCHITECTURE.md
87
86
  - docs/DEVELOPMENT.md
88
87
  - docs/FORM_SCHEMA.md
89
88
  - docs/FUNCTIONS.md
89
+ - docs/GOLDEN_QUICK_START.md
90
+ - docs/GOLDEN_TESTS.md
90
91
  - docs/OUTPUT_SCHEMA.md
91
92
  - docs/SYNTAX.md
92
93
  - docs/UNSAT_DETECTION.md
93
94
  - docs/VSCODE_EXTENSION.md
94
95
  - docs/functions-reference.json
95
- - examples/deep_schema_compilation_and_evaluation_benchmark.rb
96
- - examples/federal_tax_calculator_2024.rb
97
- - examples/game_of_life.rb
98
- - examples/simple_rpg_game.rb
99
- - examples/static_analysis_errors.rb
100
- - examples/wide_schema_compilation_and_evaluation_benchmark.rb
101
96
  - golden/array_element/expected.json
102
97
  - golden/array_element/expected/ast.txt
103
98
  - golden/array_element/expected/input_plan.txt
@@ -616,9 +611,13 @@ files:
616
611
  - lib/kumi/core/analyzer/constant_evaluator.rb
617
612
  - lib/kumi/core/analyzer/constant_folding_helpers.rb
618
613
  - lib/kumi/core/analyzer/debug.rb
614
+ - lib/kumi/core/analyzer/execution_phase.rb
615
+ - lib/kumi/core/analyzer/execution_result.rb
619
616
  - lib/kumi/core/analyzer/fn_aliases.rb
620
617
  - lib/kumi/core/analyzer/folder.rb
621
618
  - lib/kumi/core/analyzer/macro_expander.rb
619
+ - lib/kumi/core/analyzer/pass_failure.rb
620
+ - lib/kumi/core/analyzer/pass_manager.rb
622
621
  - lib/kumi/core/analyzer/passes/assemble_irv2_pass.rb
623
622
  - lib/kumi/core/analyzer/passes/attach_anchors_pass.rb
624
623
  - lib/kumi/core/analyzer/passes/attach_terminal_info_pass.rb
data/debug_ordering.rb DELETED
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require_relative 'spec/spec_helper'
6
-
7
- include PackTestHelper
8
-
9
- schema = <<~KUMI
10
- schema do
11
- input do
12
- array :numbers do
13
- integer :value
14
- end
15
- end
16
-
17
- value :total, fn(:sum, input.numbers.value)
18
- end
19
- KUMI
20
-
21
- pack = pack_for(schema)
22
- generator = Kumi::Codegen::RubyV3::Generator.new(pack, module_name: "OrderingTest")
23
-
24
- generated_code = generator.render
25
- puts "=== GENERATED CODE ==="
26
- puts generated_code
27
- puts "======================"
28
-
29
- # Analyze the structure
30
- lines = generated_code.split("\n")
31
- while_line_idx = lines.find_index { |line| line.include?("while i0 < arr0.length") }
32
- end_line_idx = lines.find_index(while_line_idx) { |line| line.strip == "end" }
33
-
34
- puts "\n=== ANALYSIS ==="
35
- puts "While line at index: #{while_line_idx}"
36
- puts "End line at index: #{end_line_idx}"
37
-
38
- if while_line_idx && end_line_idx
39
- loop_body_lines = lines[(while_line_idx + 1)...end_line_idx]
40
- puts "Loop body lines:"
41
- loop_body_lines.each_with_index do |line, i|
42
- puts " #{while_line_idx + 1 + i}: #{line}"
43
- end
44
-
45
- value_access_line = loop_body_lines.find { |line| line.include?('a0["value"]') }
46
- acc_add_line = loop_body_lines.find { |line| line.include?("acc_") && line.include?("+=") }
47
-
48
- puts "\nValue access line: #{value_access_line.inspect}"
49
- puts "Acc add line: #{acc_add_line.inspect}"
50
- else
51
- puts "Could not find while loop structure"
52
- end
@@ -1,106 +0,0 @@
1
- # Deep Schema Compilation and Evaluation Benchmark
2
- #
3
- # This benchmark measures Kumi's performance with increasingly deep dependency chains
4
- # to understand how compilation and evaluation times scale with schema depth.
5
- #
6
- # What it tests:
7
- # - Compilation time for schemas with deep dependency chains (50, 100, 150 levels)
8
- # - Evaluation performance for computing results through long dependency paths
9
- # - Stack-safe evaluation through iterative dependency resolution
10
- #
11
- # Schema structure:
12
- # - input: single integer seed
13
- # - values: chain of dependencies v0 = seed, v1 = v0 + 1, v2 = v1 + 2, ..., v_n = v_(n-1) + n
14
- # - traits: conditional checks at each level (value > threshold)
15
- # - cascade: final_result depends on first trait that evaluates to true
16
- #
17
- # Depth limits: Ruby stack overflow occurs around 200-300 levels depending on system,
18
- # so we test with conservative depths (50, 100, 150) to ensure reliability.
19
- #
20
- # Usage: bundle exec ruby examples/deep_schema_compilation_and_evaluation_benchmark.rb
21
- require "benchmark"
22
- require "benchmark/ips"
23
- require_relative "../lib/kumi"
24
-
25
- # ------------------------------------------------------------------
26
- # 1. Helper that builds a deep dependency chain schema
27
- # ------------------------------------------------------------------
28
- def build_deep_schema(depth)
29
- Class.new do
30
- extend Kumi::Schema
31
-
32
- schema do
33
- input { integer :seed }
34
-
35
- # Build dependency chain: v0 = seed, v1 = v0 + 1, v2 = v1 + 2, etc.
36
- value :v0, input.seed
37
-
38
- (1...depth).each do |i|
39
- value :"v#{i}", fn(:add, ref(:"v#{i-1}"), i)
40
- end
41
-
42
- # Build traits that check if values exceed thresholds
43
- # Use varying thresholds to create realistic evaluation scenarios
44
- (0...depth).each do |i|
45
- threshold = i * (i + 1) / 2 + 1000 # Quadratic growth to ensure variety
46
- trait :"threshold_#{i}", fn(:>, ref(:"v#{i}"), threshold)
47
- end
48
-
49
- # Final cascade that finds first trait that's true
50
- value :final_result do
51
- (0...depth).each do |i|
52
- on :"threshold_#{i}", fn(:multiply, ref(:"v#{i}"), 2)
53
- end
54
- base ref(:"v#{depth-1}") # Default to final value
55
- end
56
- end
57
- end
58
- end
59
-
60
- # Conservative depths to avoid Ruby stack overflow
61
- # Ruby stack depth limit is around 1000-2000 frames depending on the system
62
- # Keep depths well below this limit for reliable operation
63
- DEPTHS = [50, 100, 150, 200]
64
-
65
- # ------------------------------------------------------------------
66
- # 2. Measure compilation once per depth
67
- # ------------------------------------------------------------------
68
- compile_times = {}
69
- schemas = {}
70
-
71
- DEPTHS.each do |d|
72
- puts "\n--- Building #{d}-deep schema ---"
73
- t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
74
- schemas[d] = build_deep_schema(d)
75
- compile_times[d] = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
76
- end
77
-
78
- puts "=== compilation times ==="
79
- compile_times.each do |d, t|
80
- puts format("compile %3d-deep: %6.1f ms", d, t * 1_000)
81
- end
82
- puts
83
-
84
- # ------------------------------------------------------------------
85
- # 3. Pure evaluation benchmark – no compilation inside the loop
86
- # ------------------------------------------------------------------
87
- Benchmark.ips do |x|
88
- schemas.each do |d, schema|
89
- runner = schema.from(seed: 0) # memoised runner
90
- x.report("eval #{d}-deep") { runner[:final_result] }
91
- end
92
- x.compare!
93
- end
94
- # Warming up --------------------------------------
95
- # eval 50-deep 222.000 i/100ms
96
- # eval 100-deep 57.000 i/100ms
97
- # eval 150-deep 26.000 i/100ms
98
- # Calculating -------------------------------------
99
- # eval 50-deep 2.166k (± 1.9%) i/s (461.70 μs/i) - 10.878k in 5.024320s
100
- # eval 100-deep 561.698 (± 1.4%) i/s (1.78 ms/i) - 2.850k in 5.075057s
101
- # eval 150-deep 253.732 (± 0.8%) i/s (3.94 ms/i) - 1.274k in 5.021499s
102
-
103
- # Comparison:
104
- # eval 50-deep: 2165.9 i/s
105
- # eval 100-deep: 561.7 i/s - 3.86x slower
106
- # eval 150-deep: 253.7 i/s - 8.54x slower