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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +12 -0
- data/README.md +5 -6
- data/docs/DEVELOPMENT.md +23 -1
- data/docs/GOLDEN_QUICK_START.md +141 -0
- data/docs/GOLDEN_TESTS.md +240 -0
- data/golden/array_element/expected/schema_ruby.rb +1 -1
- data/golden/array_index/expected/schema_ruby.rb +1 -1
- data/golden/array_operations/expected/schema_ruby.rb +1 -1
- data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
- data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
- data/golden/decimal_explicit/expected/schema_ruby.rb +1 -1
- data/golden/element_arrays/expected/schema_ruby.rb +1 -1
- data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
- data/golden/function_overload/expected/schema_ruby.rb +1 -1
- data/golden/game_of_life/expected/schema_ruby.rb +1 -1
- data/golden/hash_keys/expected/schema_ruby.rb +1 -1
- data/golden/hash_value/expected/schema_ruby.rb +1 -1
- data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
- data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
- data/golden/input_reference/expected/schema_ruby.rb +1 -1
- data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
- data/golden/let_inline/expected/schema_ruby.rb +1 -1
- data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
- data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
- data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
- data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
- data/golden/nested_hash/expected/schema_ruby.rb +1 -1
- data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
- data/golden/roll/expected/schema_ruby.rb +1 -1
- data/golden/shift/expected/schema_ruby.rb +1 -1
- data/golden/shift_2d/expected/schema_ruby.rb +1 -1
- data/golden/simple_math/expected/schema_ruby.rb +1 -1
- data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
- data/golden/tuples/expected/schema_ruby.rb +1 -1
- data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
- data/golden/us_tax_2024/expected/schema_ruby.rb +1 -1
- data/golden/with_constants/expected/schema_ruby.rb +1 -1
- data/lib/kumi/analyzer.rb +39 -79
- data/lib/kumi/core/analyzer/analysis_state.rb +2 -0
- data/lib/kumi/core/analyzer/constant_evaluator.rb +0 -2
- data/lib/kumi/core/analyzer/execution_phase.rb +24 -0
- data/lib/kumi/core/analyzer/execution_result.rb +38 -0
- data/lib/kumi/core/analyzer/pass_failure.rb +26 -0
- data/lib/kumi/core/analyzer/pass_manager.rb +136 -0
- data/lib/kumi/core/analyzer/passes/codegen/js/declaration_emitter.rb +1 -1
- data/lib/kumi/core/analyzer/passes/codegen/js_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/codegen/ruby/declaration_emitter.rb +1 -1
- data/lib/kumi/core/analyzer/passes/codegen/ruby_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/constant_folding_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb +13 -31
- data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/input_collector.rb +1 -1
- data/lib/kumi/core/analyzer/passes/input_form_schema_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/lir/loop_invariant_code_motion_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/lir/stencil_emitter.rb +0 -2
- data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +5 -1
- data/lib/kumi/core/analyzer/passes/output_schema_pass.rb +2 -1
- data/lib/kumi/core/analyzer/passes/snast_pass.rb +1 -1
- data/lib/kumi/core/analyzer/passes/unsat_detector.rb +8 -10
- data/lib/kumi/core/compiler/access_planner_v2.rb +1 -2
- data/lib/kumi/core/functions/overload_resolver.rb +15 -19
- data/lib/kumi/core/functions/type_categories.rb +3 -3
- data/lib/kumi/core/functions/type_error_reporter.rb +1 -3
- data/lib/kumi/core/input/type_matcher.rb +1 -1
- data/lib/kumi/core/types/normalizer.rb +8 -10
- data/lib/kumi/core/types/validator.rb +1 -1
- data/lib/kumi/core/types/value_objects.rb +5 -2
- data/lib/kumi/core/types.rb +2 -4
- data/lib/kumi/dev/codegen.rb +1 -1
- data/lib/kumi/dev/golden/generator.rb +14 -10
- data/lib/kumi/dev/golden/reporter.rb +5 -5
- data/lib/kumi/dev/golden/representation.rb +1 -3
- data/lib/kumi/dev/golden/result.rb +1 -1
- data/lib/kumi/dev/golden/runtime_test.rb +0 -2
- data/lib/kumi/dev/golden/suite.rb +20 -4
- data/lib/kumi/dev/golden/value_normalizer.rb +1 -3
- data/lib/kumi/doc_generator/formatters/json.rb +11 -11
- data/lib/kumi/doc_generator/formatters/markdown.rb +35 -37
- data/lib/kumi/doc_generator/loader.rb +8 -6
- data/lib/kumi/doc_generator/merger.rb +12 -12
- data/lib/kumi/frontends/text.rb +4 -6
- data/lib/kumi/registry_v2/loader.rb +32 -33
- data/lib/kumi/schema.rb +2 -2
- data/lib/kumi/version.rb +1 -1
- metadata +13 -14
- data/debug_ordering.rb +0 -52
- data/examples/deep_schema_compilation_and_evaluation_benchmark.rb +0 -106
- data/examples/federal_tax_calculator_2024.rb +0 -115
- data/examples/game_of_life.rb +0 -95
- data/examples/simple_rpg_game.rb +0 -1000
- data/examples/static_analysis_errors.rb +0 -178
- 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[
|
|
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[
|
|
53
|
-
dtype_str = format_dtype(entry[
|
|
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[
|
|
60
|
+
if entry["params"] && !entry["params"].empty?
|
|
63
61
|
lines << "### Parameters"
|
|
64
62
|
lines << ""
|
|
65
|
-
entry[
|
|
66
|
-
lines << "- `#{param['name']}`#{
|
|
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[
|
|
69
|
+
if entry["kernels"] && !entry["kernels"].empty?
|
|
72
70
|
lines << "### Implementations"
|
|
73
71
|
lines << ""
|
|
74
|
-
entry[
|
|
75
|
-
lines.concat(format_kernel(target, kernel, entry[
|
|
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[
|
|
89
|
+
has_identity = kernel["identity"] && !kernel["identity"].empty?
|
|
92
90
|
|
|
93
|
-
if kernel[
|
|
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[
|
|
96
|
+
if kernel["impl"]
|
|
99
97
|
lines << "**Implementation:**"
|
|
100
98
|
lines << ""
|
|
101
99
|
lines << "```ruby"
|
|
102
|
-
lines << format_impl(kernel[
|
|
100
|
+
lines << format_impl(kernel["impl"])
|
|
103
101
|
lines << "```"
|
|
104
102
|
lines << ""
|
|
105
103
|
end
|
|
106
104
|
|
|
107
|
-
if kernel[
|
|
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[
|
|
112
|
+
kernel["identity"].each do |type, value|
|
|
115
113
|
lines << "- #{type}: `#{value}`"
|
|
116
114
|
end
|
|
117
115
|
lines << ""
|
|
118
|
-
elsif kernel[
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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[
|
|
146
|
-
when
|
|
143
|
+
case dtype["rule"]
|
|
144
|
+
when "same_as"
|
|
147
145
|
"same as `#{dtype['param']}`"
|
|
148
|
-
when
|
|
149
|
-
dtype[
|
|
150
|
-
when
|
|
151
|
-
params = Array(dtype[
|
|
146
|
+
when "scalar"
|
|
147
|
+
dtype["kind"] || "scalar"
|
|
148
|
+
when "promote"
|
|
149
|
+
params = Array(dtype["params"]).join("`, `")
|
|
152
150
|
"promoted from `#{params}`"
|
|
153
|
-
when
|
|
151
|
+
when "element_of"
|
|
154
152
|
"element of `#{dtype['param']}`"
|
|
155
153
|
else
|
|
156
|
-
dtype[
|
|
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[
|
|
168
|
+
entry["kind"] == "reduce"
|
|
171
169
|
end
|
|
172
170
|
end
|
|
173
171
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
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,
|
|
27
|
+
Dir.glob(File.join(dir_path, "**/*.yaml")).each do |file|
|
|
26
28
|
data = YAML.load_file(file)
|
|
27
|
-
if data && data[
|
|
28
|
-
result.concat(data[
|
|
29
|
-
elsif data && data[
|
|
30
|
-
result.concat(data[
|
|
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[
|
|
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[
|
|
30
|
-
target = extract_target(kernel[
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
data/lib/kumi/frontends/text.rb
CHANGED
|
@@ -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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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(
|
|
26
|
+
rule_type = spec.fetch("rule") { raise "dtype hash requires 'rule' key" }
|
|
27
27
|
|
|
28
28
|
case rule_type
|
|
29
|
-
when
|
|
30
|
-
param = spec.fetch(
|
|
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
|
|
34
|
-
params = spec.fetch(
|
|
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
|
|
39
|
-
param = spec.fetch(
|
|
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
|
|
43
|
-
param1 = spec.fetch(
|
|
44
|
-
param2 = spec.fetch(
|
|
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
|
|
48
|
-
param = spec.fetch(
|
|
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
|
|
52
|
-
if spec.key?(
|
|
53
|
-
element_type_spec = spec[
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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?(
|
|
63
|
-
element_type_param = spec[
|
|
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
|
|
70
|
-
if spec.key?(
|
|
71
|
-
element_types_spec = spec[
|
|
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?(
|
|
81
|
-
element_types_param = spec[
|
|
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
|
|
88
|
-
kind = spec.fetch(
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
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.
|
|
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:
|
|
13
|
+
name: mutex_m
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
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:
|
|
25
|
+
version: 0.3.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: zeitwerk
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
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:
|
|
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
|