kumi 0.0.36 → 0.1.0

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 (544) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +39 -58
  3. data/.rubocop_todo.yml +931 -0
  4. data/CHANGELOG.md +39 -2
  5. data/README.md +75 -1
  6. data/data/functions/core/arithmetic.yaml +48 -0
  7. data/data/functions/core/conversion.yaml +13 -0
  8. data/data/functions/core/select.yaml +1 -1
  9. data/data/functions/core/stencil.yaml +32 -0
  10. data/data/kernels/javascript/agg/numeric.yaml +1 -0
  11. data/data/kernels/javascript/core/arithmetic.yaml +24 -0
  12. data/data/kernels/javascript/core/coercion.yaml +18 -4
  13. data/data/kernels/ruby/agg/numeric.yaml +2 -1
  14. data/data/kernels/ruby/core/arithmetic.yaml +31 -3
  15. data/data/kernels/ruby/core/coercion.yaml +8 -0
  16. data/docs/CROSS_TARGET_SEMANTICS.md +99 -0
  17. data/docs/FORM_SCHEMA.md +13 -8
  18. data/docs/FUNCTIONS.md +120 -13
  19. data/docs/INPUTS.md +164 -0
  20. data/docs/OUTPUT_SCHEMA.md +12 -8
  21. data/docs/PASSES.md +76 -0
  22. data/docs/PASS_AUDIT.md +179 -0
  23. data/docs/PORTAL.md +39 -0
  24. data/docs/SYNTAX.md +101 -120
  25. data/docs/SYNTAX_NOTES.md +612 -0
  26. data/docs/UNSAT_DETECTION.md +2 -2
  27. data/docs/functions-reference.json +276 -84
  28. data/docs/pairwise-design.md +125 -0
  29. data/docs/superpowers/plans/2026-06-12-pass-conventions-and-dedup.md +1559 -0
  30. data/docs/superpowers/specs/2026-06-12-pass-conventions-and-dedup-design.md +136 -0
  31. data/golden/algebraic_identities/expected/ast.txt +66 -0
  32. data/golden/algebraic_identities/expected/dfir.txt +47 -0
  33. data/golden/algebraic_identities/expected/dfir_optimized.txt +59 -0
  34. data/golden/algebraic_identities/expected/input_plan.txt +7 -0
  35. data/golden/algebraic_identities/expected/loopir.txt +61 -0
  36. data/golden/algebraic_identities/expected/nast.txt +51 -0
  37. data/golden/algebraic_identities/expected/runtime.json +24 -0
  38. data/golden/algebraic_identities/expected/schema_javascript.mjs +89 -0
  39. data/golden/algebraic_identities/expected/schema_ruby.rb +84 -0
  40. data/golden/algebraic_identities/expected/snast.txt +51 -0
  41. data/golden/algebraic_identities/expected/vecir.txt +47 -0
  42. data/golden/algebraic_identities/input.json +6 -0
  43. data/golden/algebraic_identities/schema.kumi +25 -0
  44. data/golden/array_element/expected/loopir.txt +1 -2
  45. data/golden/array_element/expected/runtime.json +5 -0
  46. data/golden/array_element/expected/schema_javascript.mjs +1 -2
  47. data/golden/array_element/expected/schema_ruby.rb +2 -3
  48. data/golden/array_index/expected/runtime.json +54 -0
  49. data/golden/array_index/expected/schema_ruby.rb +1 -1
  50. data/golden/array_operations/{expected.json → expected/runtime.json} +5 -15
  51. data/golden/array_operations/expected/schema_ruby.rb +1 -1
  52. data/golden/cascade_logic/expected/runtime.json +3 -0
  53. data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
  54. data/golden/cascade_reuse_peephole/expected/ast.txt +193 -0
  55. data/golden/cascade_reuse_peephole/expected/dfir.txt +124 -0
  56. data/golden/cascade_reuse_peephole/expected/dfir_optimized.txt +320 -0
  57. data/golden/cascade_reuse_peephole/expected/input_plan.txt +2 -0
  58. data/golden/cascade_reuse_peephole/expected/loopir.txt +302 -0
  59. data/golden/cascade_reuse_peephole/expected/nast.txt +188 -0
  60. data/golden/cascade_reuse_peephole/expected/runtime.json +8 -0
  61. data/golden/cascade_reuse_peephole/expected/schema_javascript.mjs +335 -0
  62. data/golden/cascade_reuse_peephole/expected/schema_ruby.rb +337 -0
  63. data/golden/cascade_reuse_peephole/expected/snast.txt +188 -0
  64. data/golden/cascade_reuse_peephole/expected/vecir.txt +302 -0
  65. data/golden/cascade_reuse_peephole/input.json +4 -0
  66. data/golden/cascade_reuse_peephole/schema.kumi +31 -0
  67. data/golden/chained_fusion/expected/loopir.txt +5 -13
  68. data/golden/chained_fusion/expected/runtime.json +45 -0
  69. data/golden/chained_fusion/expected/schema_javascript.mjs +5 -13
  70. data/golden/chained_fusion/expected/schema_ruby.rb +6 -14
  71. data/golden/cross_import/expected/ast.txt +21 -0
  72. data/golden/cross_import/expected/dfir.txt +3 -0
  73. data/golden/cross_import/expected/dfir_optimized.txt +8 -0
  74. data/golden/cross_import/expected/input_plan.txt +5 -0
  75. data/golden/cross_import/expected/loopir.txt +14 -0
  76. data/golden/cross_import/expected/nast.txt +7 -0
  77. data/golden/cross_import/expected/runtime.json +10 -0
  78. data/golden/cross_import/expected/schema_javascript.mjs +19 -0
  79. data/golden/cross_import/expected/schema_ruby.rb +19 -0
  80. data/golden/cross_import/expected/snast.txt +7 -0
  81. data/golden/cross_import/expected/vecir.txt +8 -0
  82. data/golden/cross_import/input.json +1 -0
  83. data/golden/cross_import/schema.kumi +15 -0
  84. data/golden/cross_let/expected/ast.txt +62 -0
  85. data/golden/cross_let/expected/dfir.txt +35 -0
  86. data/golden/cross_let/expected/dfir_optimized.txt +51 -0
  87. data/golden/cross_let/expected/input_plan.txt +5 -0
  88. data/golden/cross_let/expected/loopir.txt +100 -0
  89. data/golden/cross_let/expected/nast.txt +46 -0
  90. data/golden/cross_let/expected/runtime.json +61 -0
  91. data/golden/cross_let/expected/schema_javascript.mjs +136 -0
  92. data/golden/cross_let/expected/schema_ruby.rb +123 -0
  93. data/golden/cross_let/expected/snast.txt +46 -0
  94. data/golden/cross_let/expected/vecir.txt +51 -0
  95. data/golden/cross_let/input.json +7 -0
  96. data/golden/cross_let/schema.kumi +25 -0
  97. data/golden/decimal_explicit/expected/dfir.txt +6 -6
  98. data/golden/decimal_explicit/expected/dfir_optimized.txt +6 -6
  99. data/golden/decimal_explicit/expected/loopir.txt +6 -6
  100. data/golden/decimal_explicit/expected/runtime.json +10 -0
  101. data/golden/decimal_explicit/expected/schema_ruby.rb +1 -1
  102. data/golden/decimal_explicit/expected/snast.txt +9 -9
  103. data/golden/decimal_explicit/expected/vecir.txt +6 -6
  104. data/golden/element_arrays/expected/loopir.txt +6 -13
  105. data/golden/element_arrays/expected/runtime.json +117 -0
  106. data/golden/element_arrays/expected/schema_javascript.mjs +7 -14
  107. data/golden/element_arrays/expected/schema_ruby.rb +8 -15
  108. data/golden/empty_and_null_inputs/expected/loopir.txt +4 -9
  109. data/golden/empty_and_null_inputs/expected/runtime.json +8 -0
  110. data/golden/empty_and_null_inputs/expected/schema_javascript.mjs +5 -10
  111. data/golden/empty_and_null_inputs/expected/schema_ruby.rb +6 -11
  112. data/golden/example_xpto/expected/runtime.json +4 -0
  113. data/golden/example_xpto/expected/schema_ruby.rb +1 -1
  114. data/golden/function_overload/expected/runtime.json +8 -0
  115. data/golden/function_overload/expected/schema_ruby.rb +1 -1
  116. data/golden/game_of_life/expected/loopir.txt +1 -58
  117. data/golden/game_of_life/expected/runtime.json +418 -0
  118. data/golden/game_of_life/expected/schema_javascript.mjs +1 -58
  119. data/golden/game_of_life/expected/schema_ruby.rb +2 -59
  120. data/golden/hash_keys/expected/runtime.json +20 -0
  121. data/golden/hash_keys/expected/schema_ruby.rb +1 -1
  122. data/golden/hash_value/expected/runtime.json +19 -0
  123. data/golden/hash_value/expected/schema_ruby.rb +1 -1
  124. data/golden/hierarchical_complex/expected/loopir.txt +0 -4
  125. data/golden/hierarchical_complex/expected/runtime.json +16 -0
  126. data/golden/hierarchical_complex/expected/schema_javascript.mjs +0 -4
  127. data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -5
  128. data/golden/inline_rename_scope_leak/expected/loopir.txt +1 -5
  129. data/golden/inline_rename_scope_leak/expected/runtime.json +10 -0
  130. data/golden/inline_rename_scope_leak/expected/schema_javascript.mjs +3 -7
  131. data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +4 -8
  132. data/golden/input_reference/expected/dfir.txt +2 -2
  133. data/golden/input_reference/expected/dfir_optimized.txt +2 -2
  134. data/golden/input_reference/expected/loopir.txt +2 -5
  135. data/golden/input_reference/expected/runtime.json +15 -0
  136. data/golden/input_reference/expected/schema_javascript.mjs +3 -6
  137. data/golden/input_reference/expected/schema_ruby.rb +4 -7
  138. data/golden/input_reference/expected/snast.txt +1 -1
  139. data/golden/input_reference/expected/vecir.txt +2 -2
  140. data/golden/interleaved_fusion/expected/loopir.txt +5 -10
  141. data/golden/interleaved_fusion/expected/runtime.json +37 -0
  142. data/golden/interleaved_fusion/expected/schema_javascript.mjs +5 -10
  143. data/golden/interleaved_fusion/expected/schema_ruby.rb +6 -11
  144. data/golden/let_inline/expected/runtime.json +6 -0
  145. data/golden/let_inline/expected/schema_ruby.rb +2 -2
  146. data/golden/loop_fusion/expected/loopir.txt +3 -7
  147. data/golden/loop_fusion/expected/runtime.json +29 -0
  148. data/golden/loop_fusion/expected/schema_javascript.mjs +3 -7
  149. data/golden/loop_fusion/expected/schema_ruby.rb +4 -8
  150. data/golden/min_max_empty_arrays/expected/loopir.txt +5 -12
  151. data/golden/min_max_empty_arrays/expected/runtime.json +16 -0
  152. data/golden/min_max_empty_arrays/expected/schema_javascript.mjs +7 -14
  153. data/golden/min_max_empty_arrays/expected/schema_ruby.rb +10 -17
  154. data/golden/min_reduce_scope/expected/loopir.txt +3 -7
  155. data/golden/min_reduce_scope/expected/runtime.json +11 -0
  156. data/golden/min_reduce_scope/expected/schema_javascript.mjs +4 -8
  157. data/golden/min_reduce_scope/expected/schema_ruby.rb +5 -9
  158. data/golden/mixed_dimensions/expected/loopir.txt +2 -5
  159. data/golden/mixed_dimensions/expected/runtime.json +34 -0
  160. data/golden/mixed_dimensions/expected/schema_javascript.mjs +3 -6
  161. data/golden/mixed_dimensions/expected/schema_ruby.rb +4 -7
  162. data/golden/mlp_backprop/expected/ast.txt +147 -0
  163. data/golden/mlp_backprop/expected/dfir.txt +99 -0
  164. data/golden/mlp_backprop/expected/dfir_optimized.txt +374 -0
  165. data/golden/mlp_backprop/expected/input_plan.txt +17 -0
  166. data/golden/mlp_backprop/expected/loopir.txt +440 -0
  167. data/golden/mlp_backprop/expected/nast.txt +120 -0
  168. data/golden/mlp_backprop/expected/runtime.json +43 -0
  169. data/golden/mlp_backprop/expected/schema_javascript.mjs +516 -0
  170. data/golden/mlp_backprop/expected/schema_ruby.rb +484 -0
  171. data/golden/mlp_backprop/expected/snast.txt +120 -0
  172. data/golden/mlp_backprop/expected/vecir.txt +374 -0
  173. data/golden/mlp_backprop/input.json +8 -0
  174. data/golden/mlp_backprop/schema.kumi +43 -0
  175. data/golden/multi_loop_reduction/expected/loopir.txt +2 -6
  176. data/golden/multi_loop_reduction/expected/runtime.json +5 -0
  177. data/golden/multi_loop_reduction/expected/schema_javascript.mjs +4 -8
  178. data/golden/multi_loop_reduction/expected/schema_ruby.rb +5 -9
  179. data/golden/multirank_hoisting/expected/loopir.txt +24 -42
  180. data/golden/multirank_hoisting/expected/runtime.json +47 -0
  181. data/golden/multirank_hoisting/expected/schema_javascript.mjs +24 -45
  182. data/golden/multirank_hoisting/expected/schema_ruby.rb +25 -43
  183. data/golden/nested_hash/expected/runtime.json +3 -0
  184. data/golden/nested_hash/expected/schema_ruby.rb +1 -1
  185. data/golden/outer_let/expected/ast.txt +106 -0
  186. data/golden/outer_let/expected/dfir.txt +66 -0
  187. data/golden/outer_let/expected/dfir_optimized.txt +145 -0
  188. data/golden/outer_let/expected/input_plan.txt +19 -0
  189. data/golden/outer_let/expected/loopir.txt +166 -0
  190. data/golden/outer_let/expected/nast.txt +78 -0
  191. data/golden/outer_let/expected/runtime.json +89 -0
  192. data/golden/outer_let/expected/schema_javascript.mjs +220 -0
  193. data/golden/outer_let/expected/schema_ruby.rb +201 -0
  194. data/golden/outer_let/expected/snast.txt +78 -0
  195. data/golden/outer_let/expected/vecir.txt +145 -0
  196. data/golden/outer_let/input.json +9 -0
  197. data/golden/outer_let/schema.kumi +32 -0
  198. data/golden/pairwise_cross/expected/ast.txt +62 -0
  199. data/golden/pairwise_cross/expected/dfir.txt +36 -0
  200. data/golden/pairwise_cross/expected/dfir_optimized.txt +50 -0
  201. data/golden/pairwise_cross/expected/input_plan.txt +8 -0
  202. data/golden/pairwise_cross/expected/loopir.txt +84 -0
  203. data/golden/pairwise_cross/expected/nast.txt +46 -0
  204. data/golden/pairwise_cross/expected/runtime.json +80 -0
  205. data/golden/pairwise_cross/expected/schema_javascript.mjs +113 -0
  206. data/golden/pairwise_cross/expected/schema_ruby.rb +104 -0
  207. data/golden/pairwise_cross/expected/snast.txt +46 -0
  208. data/golden/pairwise_cross/expected/vecir.txt +50 -0
  209. data/golden/pairwise_cross/input.json +8 -0
  210. data/golden/pairwise_cross/schema.kumi +18 -0
  211. data/golden/reduction_broadcast/expected/loopir.txt +4 -10
  212. data/golden/reduction_broadcast/expected/runtime.json +14 -0
  213. data/golden/reduction_broadcast/expected/schema_javascript.mjs +4 -10
  214. data/golden/reduction_broadcast/expected/schema_ruby.rb +5 -11
  215. data/golden/roll/expected/loopir.txt +4 -8
  216. data/golden/roll/expected/runtime.json +26 -0
  217. data/golden/roll/expected/schema_javascript.mjs +4 -8
  218. data/golden/roll/expected/schema_ruby.rb +5 -9
  219. data/golden/schema_imports_broadcasting_with_imports/expected/dfir.txt +3 -3
  220. data/golden/schema_imports_broadcasting_with_imports/expected/dfir_optimized.txt +3 -3
  221. data/golden/schema_imports_broadcasting_with_imports/expected/loopir.txt +3 -4
  222. data/golden/schema_imports_broadcasting_with_imports/expected/runtime.json +11 -0
  223. data/golden/schema_imports_broadcasting_with_imports/expected/schema_javascript.mjs +2 -3
  224. data/golden/schema_imports_broadcasting_with_imports/expected/schema_ruby.rb +3 -4
  225. data/golden/schema_imports_broadcasting_with_imports/expected/snast.txt +5 -5
  226. data/golden/schema_imports_broadcasting_with_imports/expected/vecir.txt +3 -3
  227. data/golden/schema_imports_complex_order_calc/expected/dfir.txt +20 -20
  228. data/golden/schema_imports_complex_order_calc/expected/dfir_optimized.txt +60 -60
  229. data/golden/schema_imports_complex_order_calc/expected/loopir.txt +34 -47
  230. data/golden/schema_imports_complex_order_calc/expected/runtime.json +33 -0
  231. data/golden/schema_imports_complex_order_calc/expected/schema_javascript.mjs +16 -29
  232. data/golden/schema_imports_complex_order_calc/expected/schema_ruby.rb +17 -30
  233. data/golden/schema_imports_complex_order_calc/expected/snast.txt +26 -26
  234. data/golden/schema_imports_complex_order_calc/expected/vecir.txt +60 -60
  235. data/golden/schema_imports_composed_order/expected/dfir.txt +3 -3
  236. data/golden/schema_imports_composed_order/expected/dfir_optimized.txt +6 -6
  237. data/golden/schema_imports_composed_order/expected/loopir.txt +6 -6
  238. data/golden/schema_imports_composed_order/expected/runtime.json +9 -0
  239. data/golden/schema_imports_composed_order/expected/schema_ruby.rb +1 -1
  240. data/golden/schema_imports_composed_order/expected/snast.txt +5 -5
  241. data/golden/schema_imports_composed_order/expected/vecir.txt +6 -6
  242. data/golden/schema_imports_discount_with_tax/expected/dfir.txt +7 -7
  243. data/golden/schema_imports_discount_with_tax/expected/dfir_optimized.txt +10 -10
  244. data/golden/schema_imports_discount_with_tax/expected/loopir.txt +10 -10
  245. data/golden/schema_imports_discount_with_tax/expected/runtime.json +10 -0
  246. data/golden/schema_imports_discount_with_tax/expected/schema_ruby.rb +1 -1
  247. data/golden/schema_imports_discount_with_tax/expected/snast.txt +11 -11
  248. data/golden/schema_imports_discount_with_tax/expected/vecir.txt +10 -10
  249. data/golden/schema_imports_line_items/expected/dfir.txt +1 -1
  250. data/golden/schema_imports_line_items/expected/dfir_optimized.txt +3 -3
  251. data/golden/schema_imports_line_items/expected/loopir.txt +3 -6
  252. data/golden/schema_imports_line_items/expected/runtime.json +8 -0
  253. data/golden/schema_imports_line_items/expected/schema_javascript.mjs +4 -7
  254. data/golden/schema_imports_line_items/expected/schema_ruby.rb +5 -8
  255. data/golden/schema_imports_line_items/expected/snast.txt +1 -1
  256. data/golden/schema_imports_line_items/expected/vecir.txt +3 -3
  257. data/golden/schema_imports_multiple/expected/dfir.txt +7 -7
  258. data/golden/schema_imports_multiple/expected/dfir_optimized.txt +13 -13
  259. data/golden/schema_imports_multiple/expected/loopir.txt +13 -13
  260. data/golden/schema_imports_multiple/expected/runtime.json +10 -0
  261. data/golden/schema_imports_multiple/expected/schema_ruby.rb +1 -1
  262. data/golden/schema_imports_multiple/expected/snast.txt +11 -11
  263. data/golden/schema_imports_multiple/expected/vecir.txt +13 -13
  264. data/golden/schema_imports_nested_expressions/expected/dfir.txt +4 -4
  265. data/golden/schema_imports_nested_expressions/expected/dfir_optimized.txt +6 -6
  266. data/golden/schema_imports_nested_expressions/expected/loopir.txt +6 -6
  267. data/golden/schema_imports_nested_expressions/expected/runtime.json +8 -0
  268. data/golden/schema_imports_nested_expressions/expected/schema_ruby.rb +1 -1
  269. data/golden/schema_imports_nested_expressions/expected/snast.txt +6 -6
  270. data/golden/schema_imports_nested_expressions/expected/vecir.txt +6 -6
  271. data/golden/schema_imports_nested_with_reductions/expected/dfir.txt +6 -6
  272. data/golden/schema_imports_nested_with_reductions/expected/dfir_optimized.txt +15 -15
  273. data/golden/schema_imports_nested_with_reductions/expected/loopir.txt +7 -14
  274. data/golden/schema_imports_nested_with_reductions/expected/runtime.json +12 -0
  275. data/golden/schema_imports_nested_with_reductions/expected/schema_javascript.mjs +8 -15
  276. data/golden/schema_imports_nested_with_reductions/expected/schema_ruby.rb +9 -16
  277. data/golden/schema_imports_nested_with_reductions/expected/snast.txt +6 -6
  278. data/golden/schema_imports_nested_with_reductions/expected/vecir.txt +15 -15
  279. data/golden/schema_imports_with_imports/expected/dfir.txt +3 -3
  280. data/golden/schema_imports_with_imports/expected/dfir_optimized.txt +3 -3
  281. data/golden/schema_imports_with_imports/expected/loopir.txt +3 -3
  282. data/golden/schema_imports_with_imports/expected/runtime.json +7 -0
  283. data/golden/schema_imports_with_imports/expected/schema_ruby.rb +1 -1
  284. data/golden/schema_imports_with_imports/expected/snast.txt +5 -5
  285. data/golden/schema_imports_with_imports/expected/vecir.txt +3 -3
  286. data/golden/shift/expected/loopir.txt +4 -8
  287. data/golden/shift/expected/runtime.json +38 -0
  288. data/golden/shift/expected/schema_javascript.mjs +4 -8
  289. data/golden/shift/expected/schema_ruby.rb +5 -9
  290. data/golden/shift_2d/expected/loopir.txt +8 -16
  291. data/golden/shift_2d/expected/runtime.json +146 -0
  292. data/golden/shift_2d/expected/schema_javascript.mjs +8 -16
  293. data/golden/shift_2d/expected/schema_ruby.rb +9 -17
  294. data/golden/simple_math/expected/runtime.json +10 -0
  295. data/golden/simple_math/expected/schema_ruby.rb +1 -1
  296. data/golden/streaming_basics/expected/loopir.txt +0 -3
  297. data/golden/streaming_basics/expected/runtime.json +25 -0
  298. data/golden/streaming_basics/expected/schema_javascript.mjs +3 -6
  299. data/golden/streaming_basics/expected/schema_ruby.rb +4 -7
  300. data/golden/transcendentals/expected/ast.txt +48 -0
  301. data/golden/transcendentals/expected/dfir.txt +31 -0
  302. data/golden/transcendentals/expected/dfir_optimized.txt +34 -0
  303. data/golden/transcendentals/expected/input_plan.txt +5 -0
  304. data/golden/transcendentals/expected/loopir.txt +46 -0
  305. data/golden/transcendentals/expected/nast.txt +34 -0
  306. data/golden/transcendentals/expected/runtime.json +27 -0
  307. data/golden/transcendentals/expected/schema_javascript.mjs +66 -0
  308. data/golden/transcendentals/expected/schema_ruby.rb +63 -0
  309. data/golden/transcendentals/expected/snast.txt +34 -0
  310. data/golden/transcendentals/expected/vecir.txt +34 -0
  311. data/golden/transcendentals/input.json +1 -0
  312. data/golden/transcendentals/schema.kumi +16 -0
  313. data/golden/tuples/expected/dfir.txt +4 -4
  314. data/golden/tuples/expected/runtime.json +12 -0
  315. data/golden/tuples/expected/schema_ruby.rb +1 -1
  316. data/golden/tuples_and_arrays/expected/dfir.txt +1 -1
  317. data/golden/tuples_and_arrays/expected/loopir.txt +0 -1
  318. data/golden/tuples_and_arrays/expected/runtime.json +13 -0
  319. data/golden/tuples_and_arrays/expected/schema_javascript.mjs +1 -2
  320. data/golden/tuples_and_arrays/expected/schema_ruby.rb +2 -3
  321. data/golden/type_promotion/expected/ast.txt +75 -0
  322. data/golden/type_promotion/expected/dfir.txt +38 -0
  323. data/golden/type_promotion/expected/dfir_optimized.txt +51 -0
  324. data/golden/type_promotion/expected/input_plan.txt +9 -0
  325. data/golden/type_promotion/expected/loopir.txt +57 -0
  326. data/golden/type_promotion/expected/nast.txt +57 -0
  327. data/golden/type_promotion/expected/runtime.json +23 -0
  328. data/golden/type_promotion/expected/schema_javascript.mjs +90 -0
  329. data/golden/type_promotion/expected/schema_ruby.rb +89 -0
  330. data/golden/type_promotion/expected/snast.txt +57 -0
  331. data/golden/type_promotion/expected/vecir.txt +51 -0
  332. data/golden/type_promotion/input.json +11 -0
  333. data/golden/type_promotion/schema.kumi +30 -0
  334. data/golden/us_tax_2024/expected/dfir.txt +6 -6
  335. data/golden/us_tax_2024/expected/loopir.txt +108 -217
  336. data/golden/us_tax_2024/expected/runtime.json +423 -0
  337. data/golden/us_tax_2024/expected/schema_javascript.mjs +108 -235
  338. data/golden/us_tax_2024/expected/schema_ruby.rb +109 -218
  339. data/golden/vector_make_object/expected/schema_ruby.rb +1 -1
  340. data/golden/with_constants/expected/schema_ruby.rb +1 -1
  341. data/lib/kumi/analyzer.rb +29 -34
  342. data/lib/kumi/configuration.rb +31 -0
  343. data/lib/kumi/core/analyzer/binder.rb +2 -2
  344. data/lib/kumi/core/analyzer/macro_expander.rb +3 -3
  345. data/lib/kumi/core/analyzer/pass_failure.rb +1 -1
  346. data/lib/kumi/core/analyzer/pass_manager.rb +211 -100
  347. data/lib/kumi/core/analyzer/passes/attach_anchors_pass.rb +44 -15
  348. data/lib/kumi/core/analyzer/passes/attach_terminal_info_pass.rb +6 -20
  349. data/lib/kumi/core/analyzer/passes/codegen/loop/js/emitter.rb +252 -14
  350. data/lib/kumi/core/analyzer/passes/codegen/loop/ruby/emitter.rb +6 -3
  351. data/lib/kumi/core/analyzer/passes/codegen/loop_js_pass.rb +5 -1
  352. data/lib/kumi/core/analyzer/passes/codegen/loop_ruby_pass.rb +3 -0
  353. data/lib/kumi/core/analyzer/passes/constant_folding_pass.rb +3 -0
  354. data/lib/kumi/core/analyzer/passes/contract_checker_pass.rb +1 -1
  355. data/lib/kumi/core/analyzer/passes/declaration_validator_pass.rb +83 -0
  356. data/lib/kumi/core/analyzer/passes/{dependency_resolver.rb → dependency_resolver_pass.rb} +8 -6
  357. data/lib/kumi/core/analyzer/passes/df_validate_pass.rb +3 -13
  358. data/lib/kumi/core/analyzer/passes/import_analysis_pass.rb +7 -2
  359. data/lib/kumi/core/analyzer/passes/input_access_planner_pass.rb +4 -28
  360. data/lib/kumi/core/analyzer/passes/{input_collector.rb → input_collector_pass.rb} +30 -3
  361. data/lib/kumi/core/analyzer/passes/input_form_schema_pass.rb +4 -1
  362. data/lib/kumi/core/analyzer/passes/ir_execution_schedule_pass.rb +4 -2
  363. data/lib/kumi/core/analyzer/passes/ir_lower_pass.rb +35 -0
  364. data/lib/kumi/core/analyzer/passes/ir_validate_pass.rb +43 -0
  365. data/lib/kumi/core/analyzer/passes/{load_input_cse.rb → load_input_cse_pass.rb} +1 -1
  366. data/lib/kumi/core/analyzer/passes/loop/lower_pass.rb +23 -10
  367. data/lib/kumi/core/analyzer/passes/loop_validate_pass.rb +2 -10
  368. data/lib/kumi/core/analyzer/passes/lower_to_dfir_pass.rb +4 -2
  369. data/lib/kumi/core/analyzer/passes/{name_indexer.rb → name_indexer_pass.rb} +3 -7
  370. data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +174 -23
  371. data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +19 -3
  372. data/lib/kumi/core/analyzer/passes/output_schema_pass.rb +3 -0
  373. data/lib/kumi/core/analyzer/passes/pass_base.rb +78 -5
  374. data/lib/kumi/core/analyzer/passes/precompute_access_paths_pass.rb +7 -1
  375. data/lib/kumi/core/analyzer/passes/{semantic_constraint_validator.rb → semantic_constraint_validator_pass.rb} +7 -14
  376. data/lib/kumi/core/analyzer/passes/snast_pass.rb +22 -48
  377. data/lib/kumi/core/analyzer/passes/{toposorter.rb → toposorter_pass.rb} +4 -5
  378. data/lib/kumi/core/analyzer/passes/{unsat_detector.rb → unsat_detector_pass.rb} +4 -15
  379. data/lib/kumi/core/analyzer/passes/vec/lower_pass.rb +6 -8
  380. data/lib/kumi/core/analyzer/passes/vec_validate_pass.rb +2 -10
  381. data/lib/kumi/core/analyzer/structs/input_meta.rb +1 -1
  382. data/lib/kumi/core/compiler/access_builder.rb +1 -1
  383. data/lib/kumi/core/compiler/access_codegen.rb +1 -1
  384. data/lib/kumi/core/compiler/access_emit/base.rb +1 -1
  385. data/lib/kumi/core/compiler/access_planner_v2.rb +2 -2
  386. data/lib/kumi/core/compiler/accessors/each_indexed_accessor.rb +1 -1
  387. data/lib/kumi/core/compiler/accessors/materialize_accessor.rb +1 -1
  388. data/lib/kumi/core/compiler/accessors/ravel_accessor.rb +1 -1
  389. data/lib/kumi/core/compiler/accessors/read_accessor.rb +1 -1
  390. data/lib/kumi/core/error_reporter.rb +6 -11
  391. data/lib/kumi/core/errors.rb +37 -11
  392. data/lib/kumi/core/expression_renderer.rb +97 -0
  393. data/lib/kumi/core/functions/overload_resolver.rb +79 -149
  394. data/lib/kumi/core/ir/execution_engine/combinators.rb +16 -10
  395. data/lib/kumi/core/nast.rb +24 -0
  396. data/lib/kumi/core/ruby_parser/build_context.rb +6 -1
  397. data/lib/kumi/core/ruby_parser/dsl.rb +12 -2
  398. data/lib/kumi/core/ruby_parser/dsl_cascade_builder.rb +14 -12
  399. data/lib/kumi/core/ruby_parser/guard_rails.rb +15 -1
  400. data/lib/kumi/core/ruby_parser/input_builder.rb +55 -48
  401. data/lib/kumi/core/ruby_parser/parser.rb +26 -18
  402. data/lib/kumi/core/ruby_parser/schema_builder.rb +60 -15
  403. data/lib/kumi/core/ruby_parser/sugar.rb +0 -86
  404. data/lib/kumi/core/types/dtype_rule.rb +76 -0
  405. data/lib/kumi/core/types/normalizer.rb +2 -2
  406. data/lib/kumi/core/types/profile.rb +82 -0
  407. data/lib/kumi/core/types/registry.rb +87 -0
  408. data/lib/kumi/core/types/system.rb +96 -0
  409. data/lib/kumi/core/types/value_objects.rb +14 -13
  410. data/lib/kumi/core/types.rb +41 -34
  411. data/lib/kumi/dev/golden_runtime.rb +163 -0
  412. data/lib/kumi/dev/golden_v2.rb +30 -5
  413. data/lib/kumi/dev/pretty_printer.rb +40 -73
  414. data/lib/kumi/frontends/ruby.rb +52 -12
  415. data/lib/kumi/frontends/source_frame.rb +71 -0
  416. data/lib/kumi/frontends/text.rb +4 -48
  417. data/lib/kumi/function_registry/loader.rb +101 -0
  418. data/lib/kumi/function_registry.rb +150 -0
  419. data/lib/kumi/ir/base/block.rb +11 -2
  420. data/lib/kumi/ir/base/builder.rb +1 -1
  421. data/lib/kumi/ir/base/function.rb +2 -4
  422. data/lib/kumi/ir/base/instruction.rb +10 -23
  423. data/lib/kumi/ir/base/module.rb +5 -4
  424. data/lib/kumi/ir/buf/lower.rb +1 -2
  425. data/lib/kumi/ir/df/access_contract.rb +3 -5
  426. data/lib/kumi/ir/df/import_inliner.rb +2 -5
  427. data/lib/kumi/ir/df/lower.rb +72 -51
  428. data/lib/kumi/ir/df/ops/axis_cross.rb +34 -0
  429. data/lib/kumi/ir/df/ops/axis_outer.rb +35 -0
  430. data/lib/kumi/ir/df/passes/broadcast_simplify.rb +1 -2
  431. data/lib/kumi/ir/df/passes/cse.rb +8 -3
  432. data/lib/kumi/ir/df/passes/decl_inlining.rb +5 -26
  433. data/lib/kumi/ir/df/passes/import_inlining.rb +42 -36
  434. data/lib/kumi/ir/df/passes/load_dedup.rb +9 -4
  435. data/lib/kumi/ir/df/passes/stencil_cse.rb +2 -5
  436. data/lib/kumi/ir/df/passes/support/instruction_cloner.rb +41 -14
  437. data/lib/kumi/ir/df/passes/tuple_fold_canonicalization.rb +8 -3
  438. data/lib/kumi/ir/df/passes/tuple_to_object.rb +1 -2
  439. data/lib/kumi/ir/df/validator.rb +3 -3
  440. data/lib/kumi/ir/df.rb +8 -0
  441. data/lib/kumi/ir/loop/lower.rb +231 -37
  442. data/lib/kumi/ir/loop/passes/array_contraction.rb +124 -0
  443. data/lib/kumi/ir/loop/passes/copy_cleanup.rb +112 -0
  444. data/lib/kumi/ir/loop/passes/loop_fusion.rb +125 -0
  445. data/lib/kumi/ir/loop/passes/support/structure.rb +100 -0
  446. data/lib/kumi/ir/loop/passes.rb +14 -0
  447. data/lib/kumi/ir/loop/pipeline.rb +9 -1
  448. data/lib/kumi/ir/loop/validator.rb +29 -4
  449. data/lib/kumi/ir/loop.rb +1 -0
  450. data/lib/kumi/ir/passes/register_generator.rb +32 -0
  451. data/lib/kumi/ir/testing/snast_factory.rb +2 -4
  452. data/lib/kumi/ir/vec/lower.rb +17 -7
  453. data/lib/kumi/ir/vec/ops/core_ops.rb +41 -0
  454. data/lib/kumi/ir/vec/passes/axis_canonicalization.rb +1 -2
  455. data/lib/kumi/ir/vec/passes/constant_propagation.rb +75 -47
  456. data/lib/kumi/ir/vec/passes/dce.rb +2 -5
  457. data/lib/kumi/ir/vec/passes/gvn.rb +8 -3
  458. data/lib/kumi/ir/vec/passes/peephole_simplify.rb +30 -13
  459. data/lib/kumi/ir/vec/passes/stencil_detection.rb +1 -2
  460. data/lib/kumi/ir/vec/passes/support/algebraic_identities.rb +70 -0
  461. data/lib/kumi/ir/vec/passes/support/instruction_cloner.rb +17 -3
  462. data/lib/kumi/ir/vec/validator.rb +26 -6
  463. data/lib/kumi/schema.rb +14 -10
  464. data/lib/kumi/schema_metadata/printer.rb +111 -0
  465. data/lib/kumi/schema_metadata.rb +289 -0
  466. data/lib/kumi/syntax/location.rb +14 -1
  467. data/lib/kumi/syntax/root.rb +35 -3
  468. data/lib/kumi/test_shared_schemas/pairwise.rb +27 -0
  469. data/lib/kumi/version.rb +1 -1
  470. data/lib/kumi.rb +3 -5
  471. data/tasks/docs_portal.rake +135 -0
  472. metadata +203 -81
  473. data/golden/array_element/expected.json +0 -5
  474. data/golden/array_index/expected.json +0 -5
  475. data/golden/cascade_logic/expected.json +0 -5
  476. data/golden/chained_fusion/expected.json +0 -45
  477. data/golden/decimal_explicit/expected.json +0 -1
  478. data/golden/element_arrays/expected.json +0 -55
  479. data/golden/empty_and_null_inputs/expected.json +0 -8
  480. data/golden/example_xpto/expected.json +0 -4
  481. data/golden/game_of_life/expected.json +0 -3
  482. data/golden/hash_keys/expected.json +0 -20
  483. data/golden/hash_value/expected.json +0 -19
  484. data/golden/hierarchical_complex/expected.json +0 -34
  485. data/golden/inline_rename_scope_leak/expected.json +0 -7
  486. data/golden/input_reference/expected.json +0 -7
  487. data/golden/interleaved_fusion/expected.json +0 -37
  488. data/golden/let_inline/expected.json +0 -1
  489. data/golden/loop_fusion/expected.json +0 -30
  490. data/golden/min_max_empty_arrays/expected.json +0 -7
  491. data/golden/min_reduce_scope/expected.json +0 -9
  492. data/golden/mixed_dimensions/expected.json +0 -6
  493. data/golden/multi_loop_reduction/expected.json +0 -5
  494. data/golden/multirank_hoisting/expected.json +0 -15
  495. data/golden/nested_hash/expected.json +0 -3
  496. data/golden/reduction_broadcast/expected.json +0 -25
  497. data/golden/roll/expected.json +0 -6
  498. data/golden/schema_imports_broadcasting_with_imports/expected.json +0 -4
  499. data/golden/schema_imports_complex_order_calc/expected.json +0 -12
  500. data/golden/schema_imports_composed_order/expected.json +0 -6
  501. data/golden/schema_imports_discount_with_tax/expected.json +0 -7
  502. data/golden/schema_imports_line_items/expected.json +0 -5
  503. data/golden/schema_imports_multiple/expected.json +0 -7
  504. data/golden/schema_imports_nested_expressions/expected.json +0 -5
  505. data/golden/schema_imports_nested_with_reductions/expected.json +0 -6
  506. data/golden/schema_imports_with_imports/expected.json +0 -4
  507. data/golden/shift/expected.json +0 -8
  508. data/golden/shift_2d/expected.json +0 -15
  509. data/golden/simple_math/expected.json +0 -1
  510. data/golden/streaming_basics/expected.json +0 -10
  511. data/golden/tuples/expected.json +0 -7
  512. data/golden/tuples_and_arrays/expected.json +0 -18
  513. data/golden/us_tax_2024/expected.json +0 -120
  514. data/lib/kumi/core/analyzer/passes/assemble_irv2_pass.rb +0 -130
  515. data/lib/kumi/core/analyzer/passes/declaration_validator.rb +0 -45
  516. data/lib/kumi/core/analyzer/passes/lower_to_irv2_pass.rb +0 -197
  517. data/lib/kumi/core/compiler/access_planner.rb +0 -258
  518. data/lib/kumi/core/functions/function_spec.rb +0 -17
  519. data/lib/kumi/core/functions/loader.rb +0 -63
  520. data/lib/kumi/core/functions/type_categories.rb +0 -44
  521. data/lib/kumi/core/functions/type_error_reporter.rb +0 -116
  522. data/lib/kumi/core/functions/type_rules.rb +0 -228
  523. data/lib/kumi/core/irv2/builder.rb +0 -48
  524. data/lib/kumi/core/irv2/declaration.rb +0 -28
  525. data/lib/kumi/core/irv2/module.rb +0 -108
  526. data/lib/kumi/core/irv2/value.rb +0 -28
  527. data/lib/kumi/core/types/validator.rb +0 -33
  528. data/lib/kumi/dev/codegen.rb +0 -194
  529. data/lib/kumi/dev/golden/generator.rb +0 -109
  530. data/lib/kumi/dev/golden/reporter.rb +0 -169
  531. data/lib/kumi/dev/golden/representation.rb +0 -40
  532. data/lib/kumi/dev/golden/result.rb +0 -106
  533. data/lib/kumi/dev/golden/runtime_test.rb +0 -131
  534. data/lib/kumi/dev/golden/suite.rb +0 -147
  535. data/lib/kumi/dev/golden/value_normalizer.rb +0 -78
  536. data/lib/kumi/dev/golden/verifier.rb +0 -76
  537. data/lib/kumi/dev/golden.rb +0 -82
  538. data/lib/kumi/dev/golden_schema_wrapper.rb +0 -116
  539. data/lib/kumi/dev/printer/irv2_formatter.rb +0 -163
  540. data/lib/kumi/kernel_registry.rb +0 -59
  541. data/lib/kumi/pack/builder.rb +0 -229
  542. data/lib/kumi/pack.rb +0 -15
  543. data/lib/kumi/registry_v2/loader.rb +0 -170
  544. data/lib/kumi/registry_v2.rb +0 -135
data/docs/FUNCTIONS.md CHANGED
@@ -134,9 +134,12 @@ Auto-generated documentation for Kumi functions and their kernels.
134
134
 
135
135
  `agg.join:ruby:v1`
136
136
 
137
+ **Inline:** `+= $1` (`$0` = accumulator, `$1` = element)
138
+
137
139
  **Fold:** `= $0.join`
138
140
 
139
- _Note: No identity value. First element initializes accumulator._
141
+ **Identity:**
142
+ - string: ``
140
143
 
141
144
  **Reduction:** First element is initial value (no identity)
142
145
 
@@ -253,6 +256,7 @@ _Note: No identity value. First element initializes accumulator._
253
256
  **Identity:**
254
257
  - float: `0.0`
255
258
  - integer: `0`
259
+ - decimal: `0`
256
260
 
257
261
  **Reduction:** Monoid operation with identity element
258
262
 
@@ -405,7 +409,7 @@ _Note: No identity value. First element initializes accumulator._
405
409
 
406
410
  ```ruby
407
411
  (x, lo, hi)
408
- [[x, lo].max, hi].min
412
+ x.clamp(lo, hi)
409
413
  ```
410
414
 
411
415
  _Note: No identity value. First element initializes accumulator._
@@ -435,6 +439,23 @@ a.to_s + b.to_s
435
439
 
436
440
  _Note: No identity value. First element initializes accumulator._
437
441
 
442
+ ## `core.cos`
443
+
444
+ - **Arity:** 1
445
+ - **Type:** float
446
+
447
+ ### Parameters
448
+
449
+ - `angle`
450
+
451
+ ### Implementations
452
+
453
+ #### Ruby
454
+
455
+ `cos:ruby:v1`
456
+
457
+ _Note: No identity value. First element initializes accumulator._
458
+
438
459
  ## `core.div`
439
460
 
440
461
  **Aliases:** `div`, `divide`
@@ -506,6 +527,23 @@ _Note: No identity value. First element initializes accumulator._
506
527
 
507
528
  _Note: No identity value. First element initializes accumulator._
508
529
 
530
+ ## `core.exp`
531
+
532
+ - **Arity:** 1
533
+ - **Type:** float
534
+
535
+ ### Parameters
536
+
537
+ - `number`
538
+
539
+ ### Implementations
540
+
541
+ #### Ruby
542
+
543
+ `exp:ruby:v1`
544
+
545
+ _Note: No identity value. First element initializes accumulator._
546
+
509
547
  ## `core.gt`
510
548
 
511
549
  **Aliases:** `>`, `greater_than`, `gt`
@@ -597,6 +635,23 @@ _Note: No identity value. First element initializes accumulator._
597
635
 
598
636
  _Note: No identity value. First element initializes accumulator._
599
637
 
638
+ ## `core.log`
639
+
640
+ - **Arity:** 1
641
+ - **Type:** float
642
+
643
+ ### Parameters
644
+
645
+ - `number`
646
+
647
+ ### Implementations
648
+
649
+ #### Ruby
650
+
651
+ `log:ruby:v1`
652
+
653
+ _Note: No identity value. First element initializes accumulator._
654
+
600
655
  ## `core.lt`
601
656
 
602
657
  **Aliases:** `<`, `less_than`, `lt`
@@ -671,29 +726,29 @@ _Note: No identity value. First element initializes accumulator._
671
726
 
672
727
  _Note: No identity value. First element initializes accumulator._
673
728
 
674
- ## `core.mul`
729
+ ## `core.mul:string_repeat`
675
730
 
676
731
  **Aliases:** `mul`, `multiply`
677
732
 
678
733
  - **Arity:** 2
679
- - **Type:** promoted from `left_operand`, `right_operand`
734
+ - **Type:** string
680
735
 
681
736
  ### Parameters
682
737
 
683
- - `left_operand`
684
- - `right_operand`
738
+ - `string_value`
739
+ - `repeat_count`
685
740
 
686
741
  ### Implementations
687
742
 
688
- #### Ruby
743
+ #### String_repeat
689
744
 
690
- `mul:ruby:v1`
745
+ `mul:string_repeat:ruby:v1`
691
746
 
692
747
  **Implementation:**
693
748
 
694
749
  ```ruby
695
- (a, b)
696
- a * b
750
+ (str, count)
751
+ str * count
697
752
  ```
698
753
 
699
754
  _Note: No identity value. First element initializes accumulator._
@@ -800,11 +855,46 @@ _Note: No identity value. First element initializes accumulator._
800
855
 
801
856
  ```ruby
802
857
  (a, b)
803
- a ** b
858
+ r = a ** b
859
+ r.is_a?(Complex) ? Float::NAN : r
804
860
  ```
805
861
 
806
862
  _Note: No identity value. First element initializes accumulator._
807
863
 
864
+ ## `core.sin`
865
+
866
+ - **Arity:** 1
867
+ - **Type:** float
868
+
869
+ ### Parameters
870
+
871
+ - `angle`
872
+
873
+ ### Implementations
874
+
875
+ #### Ruby
876
+
877
+ `sin:ruby:v1`
878
+
879
+ _Note: No identity value. First element initializes accumulator._
880
+
881
+ ## `core.sqrt`
882
+
883
+ - **Arity:** 1
884
+ - **Type:** float
885
+
886
+ ### Parameters
887
+
888
+ - `number`
889
+
890
+ ### Implementations
891
+
892
+ #### Ruby
893
+
894
+ `sqrt:ruby:v1`
895
+
896
+ _Note: No identity value. First element initializes accumulator._
897
+
808
898
  ## `core.sub`
809
899
 
810
900
  **Aliases:** `sub`, `subtract`
@@ -832,6 +922,23 @@ _Note: No identity value. First element initializes accumulator._
832
922
 
833
923
  _Note: No identity value. First element initializes accumulator._
834
924
 
925
+ ## `core.tanh`
926
+
927
+ - **Arity:** 1
928
+ - **Type:** float
929
+
930
+ ### Parameters
931
+
932
+ - `number`
933
+
934
+ ### Implementations
935
+
936
+ #### Ruby
937
+
938
+ `tanh:ruby:v1`
939
+
940
+ _Note: No identity value. First element initializes accumulator._
941
+
835
942
  ## `core.to_decimal`
836
943
 
837
944
  - **Arity:** 1
@@ -911,7 +1018,7 @@ _Note: No identity value. First element initializes accumulator._
911
1018
 
912
1019
  _Note: No identity value. First element initializes accumulator._
913
1020
 
914
- ## `core.to_string`
1021
+ ## `core.to_string:float`
915
1022
 
916
1023
  **Aliases:** `to_str`, `to_string`
917
1024
 
@@ -926,7 +1033,7 @@ _Note: No identity value. First element initializes accumulator._
926
1033
 
927
1034
  #### Ruby
928
1035
 
929
- `to_string:ruby:v1`
1036
+ `to_string_float:ruby:v1`
930
1037
 
931
1038
  **Implementation:**
932
1039
 
data/docs/INPUTS.md ADDED
@@ -0,0 +1,164 @@
1
+ # Kumi Input Shapes
2
+
3
+ The `input do … end` block declares the shape of the data a schema runs on:
4
+ scalars, arrays, hashes, and any nesting of them. Every value referenced in the
5
+ body (`input.foo.bar`) must trace back to a declaration here.
6
+
7
+ This is the reference for declaring inputs. For the rest of the language
8
+ (declarations, operators, functions, control flow) see [SYNTAX.md](SYNTAX.md).
9
+
10
+ ## The element rule: always name the element
11
+
12
+ Kumi **maps over arrays by default** (unless you `zip`). The single child inside
13
+ an `array` block names the element that the map binds and iterates over — so the
14
+ element must always be named. Every `array` declares **exactly one child**:
15
+
16
+ - a primitive (`integer :value`) for a **scalar array**,
17
+ - a `hash` (`hash :item do … end`) when each element has **several fields**,
18
+ - a nested `array` for an **array of arrays**.
19
+
20
+ There is no childless/nameless array — without a name there is nothing for the
21
+ map to bind. Omitting the child is an error that tells you exactly this.
22
+
23
+ `index: :i` is **not** the element — it names the **axis/position** so you can
24
+ read the index value with `index(:i)`. You do *not* write `input.cells.i` to
25
+ reach the element; the element is the named child (`input.cells.value`) or the
26
+ broadcast array itself (`input.cells`).
27
+
28
+ ## Scalars
29
+
30
+ Scalar inputs represent single values:
31
+
32
+ ```kumi
33
+ input do
34
+ integer :x
35
+ float :rate
36
+ decimal :price # Precise decimal for money calculations
37
+ string :name
38
+ end
39
+ ```
40
+
41
+ **Example:**
42
+ ```kumi
43
+ schema do
44
+ input do
45
+ integer :x
46
+ integer :y
47
+ end
48
+
49
+ value :sum, input.x + input.y
50
+ value :product, input.x * input.y
51
+ end
52
+ ```
53
+
54
+ ## Arrays
55
+
56
+ Arrays represent sequences. Navigate using dot notation through each level.
57
+ Remember the [element rule](#the-element-rule-always-name-the-element): each
58
+ `array` declares exactly one named child.
59
+
60
+ **1D Array (scalar elements):**
61
+ ```kumi
62
+ input do
63
+ array :cells do
64
+ integer :value # `value` names the element
65
+ # Access: input.cells.value (maps elementwise)
66
+ # input.cells (whole array, e.g. fn(:size, …))
67
+ end
68
+ end
69
+ ```
70
+
71
+ **2D Array (grid):**
72
+ ```kumi
73
+ input do
74
+ array :rows do
75
+ array :col do
76
+ integer :v # Access: input.rows.col.v
77
+ end
78
+ end
79
+ end
80
+ ```
81
+
82
+ **3D Array (cube):**
83
+ ```kumi
84
+ input do
85
+ array :cube do
86
+ array :layer do
87
+ array :row do
88
+ integer :cell # Access: input.cube.layer.row.cell
89
+ end
90
+ end
91
+ end
92
+ end
93
+ ```
94
+
95
+ ## Arrays of Hashes
96
+
97
+ Common pattern for structured collections — use a `hash` child when each element
98
+ has several fields:
99
+
100
+ ```kumi
101
+ input do
102
+ array :items do
103
+ hash :item do
104
+ float :price
105
+ integer :quantity
106
+ string :category
107
+ end
108
+ end
109
+ end
110
+
111
+ # Access: input.items.item.price
112
+ # input.items.item.quantity
113
+ ```
114
+
115
+ ## Hashes
116
+
117
+ Hashes represent structured data with named fields:
118
+
119
+ ```kumi
120
+ input do
121
+ hash :config do
122
+ string :app_name
123
+ array :servers do
124
+ hash :server do
125
+ string :hostname
126
+ integer :port
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # Access scalar: input.config.app_name
133
+ # Access nested: input.config.servers.server.hostname
134
+ ```
135
+
136
+ ## Arrays with Named Indices
137
+
138
+ `index:` names an axis so you can read its **position** with `index(:name)`. It
139
+ is independent of the element — you still name the element child (here the
140
+ placeholder `integer :_`, since only the positions are used). `index(:i)` reads
141
+ the position; it is never an element accessor (`input.x.i` is not a thing).
142
+
143
+ ```kumi
144
+ input do
145
+ array :x, index: :i do
146
+ array :y, index: :j do
147
+ integer :_ # element child (value unused, only positions matter)
148
+ end
149
+ end
150
+ end
151
+
152
+ # Use in expressions
153
+ let :W, fn(:array_size, input.x.y)
154
+ value :row_major, (index(:i) * W) + index(:j)
155
+ value :col_major, (index(:j) * fn(:array_size, input.x)) + index(:i)
156
+ ```
157
+
158
+ ## See also
159
+
160
+ - [SYNTAX.md](SYNTAX.md) — declarations, operators, functions, control flow.
161
+ - [SYNTAX_NOTES.md](SYNTAX_NOTES.md) — parser differences, mixed nested
162
+ array/hash recipes, expression literals, and post-parse analyzer errors.
163
+ - All-pairs over arrays: `cross` (one array, A × A') and `outer` (two arrays,
164
+ A × B) in [SYNTAX.md](SYNTAX.md#all-pairs-cross).
@@ -31,15 +31,19 @@ bin/kumi analyze schema.kumi --dump output_schema --side-tables
31
31
 
32
32
  **Schema:**
33
33
  ```kumi
34
- input items: array {
35
- item: hash {
36
- price: float
37
- quantity: integer
38
- }
39
- }
34
+ schema do
35
+ input do
36
+ array :items do
37
+ hash :item do
38
+ float :price
39
+ integer :quantity
40
+ end
41
+ end
42
+ end
40
43
 
41
- trait expensive: items.item.price > 100.0
42
- value total: items.item.price * items.item.quantity
44
+ trait :expensive, input.items.item.price > 100.0
45
+ value :total, input.items.item.price * input.items.item.quantity
46
+ end
43
47
  ```
44
48
 
45
49
  **Generated Output Schema:**
data/docs/PASSES.md ADDED
@@ -0,0 +1,76 @@
1
+ # Compiler Pass Conventions
2
+
3
+ These rules are enforced by `spec/kumi/analyzer_pipeline_contract_spec.rb`,
4
+ `PassManager` contract checks, and RuboCop. Change the enforcement when you
5
+ change a rule.
6
+
7
+ ## Naming
8
+
9
+ - Every pass class name ends in `Pass`; its file name ends in `_pass.rb`.
10
+ - New acronyms in class names require a zeitwerk inflector entry in
11
+ `lib/kumi.rb` and are discouraged — prefer plain words.
12
+
13
+ ## Two pass shapes, never mixed
14
+
15
+ - **Analyzer passes** (`lib/kumi/core/analyzer/passes/`): subclass
16
+ `Passes::PassBase`. `run(errors)` takes an error accumulator and returns an
17
+ `AnalysisState`. State is immutable; produce new state with `state.with`.
18
+ - **IR passes** (`lib/kumi/ir/*/passes/`): subclass `Kumi::IR::Passes::Base`.
19
+ `run(graph:, context:)` returns a graph; compose with `IR::Passes::Pipeline`.
20
+ - Bridging happens only through the boundary adapters `IRValidatePass` and
21
+ `IRLowerPass` — never call an IR pipeline ad hoc from an analyzer pass.
22
+
23
+ ## State contracts
24
+
25
+ Every analyzer pass declares what it touches, at the top of the class body:
26
+
27
+ class SNASTPass < PassBase
28
+ reads :nast_module, :metadata_table, :registry
29
+ writes :snast_module
30
+ end
31
+
32
+ - `reads` — required keys; fails fast in `PassManager` if absent. Also defines
33
+ a reader method per key.
34
+ - `optional_reads` — keys that may be absent; reader returns `nil`.
35
+ - `writes` — every key the pass adds or replaces. A pass that produces no
36
+ state declares bare `writes` (no arguments) so the contract is still
37
+ explicit. `PassManager` rejects any undeclared write.
38
+ - `# In:` / `# Out:` comments are banned — the DSL is the single source of
39
+ truth, and `spec/kumi/analyzer_pipeline_contract_spec.rb` checks that every
40
+ pipeline pass declares a contract and that pass ordering satisfies all reads.
41
+ - Passes that annotate IR/NAST nodes in place (e.g. `AttachTerminalInfoPass`)
42
+ declare bare `writes`; keep such in-place mutation limited to node `meta` /
43
+ annotation fields, never structure.
44
+
45
+ ## Error reporting
46
+
47
+ One channel per logical error — never report AND raise the same problem (that
48
+ surfaces it twice, once via the accumulator and once via PassManager's exception
49
+ capture, which also leaks internal file paths).
50
+
51
+ - **User-facing errors** (the schema is wrong): record them in the `errors`
52
+ accumulator passed to `run`, with a real `location:` (pass the node's `loc`,
53
+ never interpolate it into the message string). Use `report(errors, msg, node:)`
54
+ to keep going and collect more, or `halt_pass!(errors, msg, node:)` to record
55
+ one and stop the pass cleanly. A halted/erroring pass fails because `errors`
56
+ is non-empty — no exception needed.
57
+ - **Internal invariants** ("can't happen" — a violated assumption, not bad user
58
+ input): `raise Kumi::Core::Errors::CompilerBug, "..."`. It is framed as a bug
59
+ to report and is never presented to users as if they wrote bad input.
60
+ - Do not raise `SemanticError`/`TypeError` directly from a pass for user errors;
61
+ route them through the accumulator so they are located and deduplicated.
62
+
63
+ ## Loading
64
+
65
+ - zeitwerk owns everything under `lib/kumi/`. Inside `lib/`, only require
66
+ stdlib (`require "json"` etc.) — never `require "kumi/..."` for autoloadable
67
+ constants. Exceptions are the files explicitly ignored in `lib/kumi.rb`.
68
+
69
+ ## Debug and checkpoint env vars
70
+
71
+ - `DEBUG_<SHORT_NAME>=1` enables per-pass debug output; the short name is the
72
+ class name minus the `Pass` suffix, underscored (`SNASTPass` → `DEBUG_SNAST`).
73
+ - `KUMI_RESUME_AT` / `KUMI_STOP_AFTER` take the pass short class name
74
+ (e.g. `SNASTPass`).
75
+ - `KUMI_DEBUG_REQUIRE_FROZEN=1` makes `PassManager` assert state values are
76
+ frozen after each pass (debug mode only).
@@ -0,0 +1,179 @@
1
+ # Analyzer Pass Audit — Findings & Plan
2
+
3
+ Audit of `lib/kumi/core/analyzer/` (≈45 passes + machinery). Baseline: 959 examples,
4
+ 0 failures, 50/50 goldens. Each finding is evidence-backed; severity is impact × reach.
5
+
6
+ The framework itself (`PassManager`, `AnalysisState`, contract DSL in `PassBase`,
7
+ `docs/PASSES.md`, the contract spec) is **good** — immutable state, declared reads/writes,
8
+ ordering enforcement, per-pass budget, checkpoint/resume. The problems are in how passes
9
+ *report errors* and a few localized hygiene issues. Most value is in error reporting.
10
+
11
+ ---
12
+
13
+ > **STATUS (2026-06-18, later): widened beyond passes.** Fixed a SECOND user-facing leak
14
+ > (unknown function `fn(:nope,...)` leaked an internal pass/file path — the validator's
15
+ > existence check called a method that raised instead of returning false; added a non-raising
16
+ > `RegistryV2#function?`). Typed the registry kernel-lookup invariants, IR execution-engine
17
+ > combinator invariants, and schema.rb operational errors: added `Errors::ConfigurationError`
18
+ > (host-app misuse), put the unused `Errors::RuntimeError` to work for align_to's :error policy.
19
+ > ~70 bare `raise "string"` remain in lower-traffic IR-pipeline/dev paths. 971 specs, 50/50 goldens.
20
+ >
21
+ > **STATUS (2026-06-18): F1, F2, F3, F4, F5 DONE + location rendering unified.**
22
+ > Added `Errors::UnsupportedFeature` (a valid construct the backend can't emit) alongside
23
+ > `CompilerBug`. Converted the codegen "does not support opcode/shift-policy/inline" raises
24
+ > (Ruby + JS emitters) to `UnsupportedFeature`; converted the compiler accessor invariants
25
+ > ("unknown accessor mode/operation") and the binder/macro_expander/pass_manager contract
26
+ > invariants to `CompilerBug`. Unified all source-location rendering on one `file:line:col`
27
+ > format (Location#to_s is the single authority; ErrorEntry/LocatedError/PassFailure/SourceFrame
28
+ > all delegate; killed the double-location). Gated by error_reporting_spec + error_handling_spec.
29
+ > DISCOVERY: `compiler/access_planner.rb` (v1, 258 LOC) is DEAD — superseded by
30
+ > `AccessPlannerV2`, zero live refs, no specs; its 7 ArgumentError invariants were left
31
+ > untouched pending deletion (separate cleanup). Remaining: **F6**, **F7**, +delete dead v1.
32
+ > 969 specs, 50/50 goldens.
33
+ >
34
+ > **STATUS (2026-06-18): F1, F2, F4, F5 DONE.** A type mismatch now surfaces as exactly
35
+ > one located error with no internal leaks (was 3). Added `Errors::CompilerBug`; `PassBase`
36
+ > gained `report`/`halt_pass!` (via a `catch(HALT)` in PassManager) and lost the broken
37
+ > `error` footgun; removed the duplicate re-append in `analyzer.rb`; swept
38
+ > `nast_dimensional_analyzer_pass` + `snast_pass` + `ir_execution_schedule_pass` and the
39
+ > invariant lookups in `attach_anchors`/`attach_terminal_info`/`input_collector`/
40
+ > `precompute_access_paths` to CompilerBug. Rule documented in `PASSES.md`. Gated by
41
+ > `spec/kumi/core/analyzer/error_reporting_spec.rb`. Remaining: **F3** (codegen "does not
42
+ > support" capability raises — 7 sites), **F6**, **F7**. 965 specs, 50/50 goldens.
43
+
44
+ ## F1 — Errors triple-report; located error buried under internal leaks ★★★ (highest impact)
45
+
46
+ **Evidence:** a single `add(string, string)` type mismatch surfaces THREE times to the user:
47
+
48
+ ```
49
+ at s.kumi line=5 column=18: add(string, string) - type mismatch <- good, located
50
+ Error in Analysis Pass(NASTDimensionalAnalyzerPass) at .../nast_dimensional_analyzer_pass.rb:119: ... type mismatch
51
+ Error in Analysis Pass(NASTDimensionalAnalyzerPass) at .../nast_dimensional_analyzer_pass.rb:119: ... type mismatch
52
+ ```
53
+
54
+ **Root cause:** `NASTDimensionalAnalyzerPass` (and others) do BOTH
55
+ `report_type_error(errors, ...)` (accumulate, located) AND immediately `raise TypeError`
56
+ (`nast_dimensional_analyzer_pass.rb:108-128`, `:151`). `PassManager#capture_exception`
57
+ (`pass_manager.rb:148`) catches the raise and adds a SECOND error built from the backtrace
58
+ head (`Error in Analysis Pass(X) at <file:line>: …`) — leaking compiler internals + file
59
+ paths to the user. The duplication-to-three comes from the raise being captured and the
60
+ located error also flowing through.
61
+
62
+ **Fix:** pick ONE reporting channel per logical error. For user-facing errors: accumulate
63
+ via `report_*` and `return state` (or `throw`/sentinel to stop the pass) — do NOT also raise.
64
+ `capture_exception` should be reserved for genuine bugs (unexpected exceptions), and its
65
+ message should not be presented as a user error alongside located ones. Dedup errors before
66
+ `handle_analysis_errors` formats them.
67
+
68
+ ## F2 — Location passed in the message string, not the structured field ★★★
69
+
70
+ **Evidence:** `snast_pass.rb:94` —
71
+ `raise SemanticError, "select mask axes ... at: #{n.loc}"`. The location is interpolated
72
+ into the text instead of `SemanticError.new(msg, n.loc)`, so `SourceFrame`/`PassFailure`
73
+ can't render a code frame and the coordinate can't be deduped.
74
+
75
+ **Also:** 14 sites do `raise Kumi::Core::Errors::SemanticError, "msg"` / `TypeError, "msg"`
76
+ with NO location arg at all (`snast_pass.rb:102,133,140,196,209`,
77
+ `nast_dimensional_analyzer_pass.rb:128,151,179,183,211,215,396`, `ir_execution_schedule_pass.rb:23,54`).
78
+ Each user-reachable one should pass the node's `loc`.
79
+
80
+ **Fix:** every `LocatedError` raised from a user-reachable condition passes `node.loc` as the
81
+ second arg; never interpolate location into the message.
82
+
83
+ ## F3 — Bare `raise "string"` (RuntimeError) for both invariants and user errors ★★
84
+
85
+ **Evidence (14 sites):** `input_collector_pass.rb:120`, `attach_anchors_pass.rb:60,70,90`,
86
+ `attach_terminal_info_pass.rb:36`, `snast_pass.rb:102`, `nast_dimensional_analyzer_pass.rb:69,344`,
87
+ `precompute_access_paths_pass.rb:76` (`raise ArgumentError, "order"` — opaque), the three
88
+ codegen emitters (`"… codegen does not support …"`).
89
+
90
+ Two distinct intents are conflated:
91
+ - **Internal invariants** ("unknown parent container", "no anchor for axes") — "can't happen"
92
+ bugs. These SHOULD raise, but as a dedicated `Kumi::Core::Errors::CompilerBug` (new) so
93
+ they're never mistaken for user errors and carry a "please report" framing.
94
+ - **Capability limits** (codegen "does not support opcode X") — these are real, expected
95
+ conditions; should be a typed error with the offending opcode/policy, not a bare string.
96
+
97
+ **Fix:** introduce `CompilerBug < Error` for invariants; convert capability raises to a typed
98
+ `Errors::UnsupportedFeature` (or reuse `CompilationError`) with structured context.
99
+
100
+ ## F4 — `PassBase#error` helper is a footgun (reads unset `@errors`) ★★
101
+
102
+ **Evidence:** `pass_base.rb:113-116` — `def error(...) add_error(@errors, ...)`. `@errors`
103
+ is never assigned by `PassBase`; `run(errors)` passes the accumulator as an ARGUMENT. Only
104
+ `snast_pass.rb:17` sets `@errors` manually (then ignores it and raises anyway). No other pass
105
+ calls `error`. So the base helper silently pushes to `nil` for any pass that uses it without
106
+ the undocumented `@errors =` ritual.
107
+
108
+ **Fix:** either (a) remove the broken `error`/`add_error` instance helpers from `PassBase` and
109
+ standardize on the `ErrorReporting` mixin's `report_*(errors, …)` (explicit accumulator), or
110
+ (b) make `PassBase#run` store `@errors = errors` via a `super`/template method so `error`
111
+ works. Prefer (a) — explicit accumulator threading matches the rest of the codebase.
112
+
113
+ ## F5 — Two reporting idioms split the pipeline in half ★★
114
+
115
+ **Evidence:** ~15 passes take `run(errors)` and accumulate; ~14 take `run(_errors)` and only
116
+ `raise`. The accumulator path enables multi-error reporting + located `PassFailure`; the raise
117
+ path gives single-error, degraded `capture_exception` output. The split is not by intent
118
+ (validation vs lowering) — it's incidental.
119
+
120
+ **Fix:** document the rule in `PASSES.md` ("user-reachable errors accumulate; invariants
121
+ raise `CompilerBug`"), then bring the raise-only validators in line. Lowering passes that only
122
+ hit invariants legitimately keep `_errors`.
123
+
124
+ ## F6 — `FormalConstraintPropagator` misfiled in `passes/`, breaks naming convention ★
125
+
126
+ **Evidence:** `passes/formal_constraint_propagator.rb` — class `FormalConstraintPropagator`,
127
+ no `Pass` suffix, not a `PassBase`, not in any pipeline list. It's a COLLABORATOR used by
128
+ `UnsatDetectorPass:19`. It also carries the banned `# RESPONSIBILITY:/DEPENDENCIES:/INTERFACE:`
129
+ header comment (cousin of the `# In:/# Out:` ban in PASSES.md).
130
+
131
+ **Fix:** move it to `lib/kumi/core/analyzer/` (alongside `binder.rb`, `folder.rb`,
132
+ `constant_evaluator.rb`) so `passes/` contains only passes; drop the header comment.
133
+
134
+ ## F7 — Codegen emitters duplicate structure (JS 410 / Ruby 212 LOC) ★ (lower priority, higher risk)
135
+
136
+ **Evidence:** `codegen/loop/js/emitter.rb` and `codegen/loop/ruby/emitter.rb` share a parallel
137
+ method skeleton (`emit_instruction`, `emit_function`, `emit_acc_step`, `emit_shift_read`,
138
+ `apply_inline`, `kernel_expr`, `format_literal`, `format_object`, `tuple_keys?`, `reg`) and the
139
+ same opcode-dispatch + "does not support" raise pattern, but emit different target syntax.
140
+
141
+ **Assessment:** real duplication, but target languages legitimately diverge; over-abstraction
142
+ here is a known trap. Recommend a SMALL shared base (opcode dispatch table + the unsupported-
143
+ opcode raise via F3's typed error + register/scratch naming), NOT a unified emitter. Defer
144
+ until F1–F5 land; treat as opportunistic.
145
+
146
+ ## F8 — Two error modules with overlapping names ★ (note, not necessarily a change)
147
+
148
+ `Core::ErrorReporting` (instance mixin: `report_*`, `raise_*`) vs `Core::ErrorReporter`
149
+ (module: `create_error`, `add_error`, `raise_error`, `ErrorEntry`). The split is defensible
150
+ (mixin vs factory) but the near-identical names invite confusion. Consider renaming the mixin
151
+ to `ErrorReportable` OR documenting the boundary in one place. Low priority.
152
+
153
+ ---
154
+
155
+ ## Plan (ordered by impact, each independently shippable & test-gated)
156
+
157
+ 1. **F1+F2+F4+F5 together (the error-model fix)** — the highest-value, coherent change:
158
+ - Add `CompilerBug` error class.
159
+ - Establish the rule: user-reachable → accumulate located error + stop pass cleanly;
160
+ invariant → raise `CompilerBug`. Document in `PASSES.md`.
161
+ - Remove/fix the `PassBase#error` footgun.
162
+ - Sweep `nast_dimensional_analyzer_pass` and `snast_pass` first (they cause the visible
163
+ triple-report), then the rest of the raise-only validators.
164
+ - Dedup errors in `analyzer.rb` before formatting.
165
+ - **Net user-visible win:** one clean, located error instead of three with leaked internals.
166
+ - Gate: new spec asserting the type-mismatch case yields exactly ONE located error with no
167
+ `Error in Analysis Pass(...)` / no internal file paths.
168
+
169
+ 2. **F3** — typed errors for invariants (`CompilerBug`) and capability limits
170
+ (`UnsupportedFeature`/`CompilationError`) across passes + codegen emitters.
171
+
172
+ 3. **F6** — relocate `FormalConstraintPropagator` out of `passes/`.
173
+
174
+ 4. **F7** — opportunistic small shared codegen base (only after 1–3, only if low-risk).
175
+
176
+ 5. **F8** — naming/doc clarification (optional).
177
+
178
+ Non-goals: rewriting the pass framework (it's sound), reordering the pipeline, touching IR
179
+ pass internals beyond error reporting.