ibex 0.1.0 → 0.3.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 (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -123
  3. data/docs/architecture.md +477 -31
  4. data/docs/bison-import.md +103 -0
  5. data/docs/comparison-policy.md +127 -0
  6. data/docs/configuration-model.md +59 -0
  7. data/docs/configuration-report.md +28 -0
  8. data/docs/conflict-explanation-reviews/v1/records/README.md +28 -0
  9. data/docs/conflict-explanation-study.md +62 -0
  10. data/docs/construction-profiling.md +157 -0
  11. data/docs/cst-migration.md +106 -0
  12. data/docs/cst.md +237 -0
  13. data/docs/decisions/0000-template.md +24 -0
  14. data/docs/decisions/0001-separate-ir-pipeline.md +34 -0
  15. data/docs/decisions/0002-opaque-user-code-boundary.md +32 -0
  16. data/docs/decisions/0003-self-hosted-grammar-frontend.md +32 -0
  17. data/docs/decisions/0004-shared-semantic-and-lossless-source-model.md +33 -0
  18. data/docs/decisions/0005-contained-grammar-composition.md +34 -0
  19. data/docs/decisions/0006-bounded-structural-grammar-lowering.md +32 -0
  20. data/docs/decisions/0007-shared-parser-construction-pipeline.md +33 -0
  21. data/docs/decisions/0008-versioned-runtime-package-boundary.md +36 -0
  22. data/docs/decisions/0009-isolated-parser-sessions.md +34 -0
  23. data/docs/decisions/0010-committed-runtime-observation.md +34 -0
  24. data/docs/decisions/0011-versioned-semantic-action-boundary.md +37 -0
  25. data/docs/decisions/0012-bounded-nonexecuting-analysis.md +37 -0
  26. data/docs/decisions/0013-transactional-generation-publication.md +37 -0
  27. data/docs/decisions/0014-versioned-generated-lexer.md +35 -0
  28. data/docs/decisions/0015-worker-isolated-browser-analysis.md +32 -0
  29. data/docs/decisions/0016-red-green-concrete-syntax.md +34 -0
  30. data/docs/decisions/0017-persistent-syntax-artifacts.md +35 -0
  31. data/docs/decisions/0018-conservative-incremental-syntax-reuse.md +38 -0
  32. data/docs/decisions/0019-runtime-syntax-session-boundary.md +51 -0
  33. data/docs/decisions/0020-grammar-ir-parser-contract.md +65 -0
  34. data/docs/decisions/0021-data-only-parser-table-sidecar.md +50 -0
  35. data/docs/decisions/0022-manifest-bound-verification-report.md +77 -0
  36. data/docs/decisions/0023-syntax-only-repair-results.md +48 -0
  37. data/docs/decisions/0024-direct-ielr-construction.md +42 -0
  38. data/docs/decisions/README.md +74 -0
  39. data/docs/declarative-configuration.md +262 -0
  40. data/docs/development.md +306 -0
  41. data/docs/direct-ielr-decision.md +120 -0
  42. data/docs/direct-multi-entry-decision.md +82 -0
  43. data/docs/editor-setup.md +26 -0
  44. data/docs/error-ux-review-rubric-v1.md +117 -0
  45. data/docs/error-ux-reviews/v1/records/README.md +22 -0
  46. data/docs/error-ux-round2-review-status-v1.json +27 -0
  47. data/docs/error-ux-round2-reviews/v1/records/README.md +30 -0
  48. data/docs/error-ux-round2-v1.json +1581 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +114 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +629 -18
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/ja/bison-import.md +51 -0
  56. data/docs/ja/stable-api.md +53 -0
  57. data/docs/lexer-construction-profile.md +109 -0
  58. data/docs/lexer-migration.md +56 -0
  59. data/docs/maturity.md +128 -0
  60. data/docs/project-site-strategy.md +54 -0
  61. data/docs/racc-migration-evidence.md +45 -0
  62. data/docs/racc-migration.md +45 -6
  63. data/docs/release-readiness.md +221 -0
  64. data/docs/repair-semantics.md +123 -0
  65. data/docs/runtime-abi-evolution.md +307 -0
  66. data/docs/stability.md +234 -0
  67. data/docs/status.md +43 -0
  68. data/docs/syntax-sessions.md +183 -0
  69. data/docs/table-artifact.md +125 -0
  70. data/docs/test-interactions.md +219 -0
  71. data/docs/verification-report.md +137 -0
  72. data/docs/verifier-trust-boundary.md +199 -0
  73. data/docs/workloads.md +132 -0
  74. data/examples/README.md +60 -0
  75. data/examples/calculator.y +44 -0
  76. data/examples/csv.y +32 -0
  77. data/examples/ini.y +70 -0
  78. data/examples/json.y +48 -0
  79. data/examples/tiny_language.y +75 -0
  80. data/lib/ibex/analysis/digraph.rb +128 -0
  81. data/lib/ibex/analysis/sets.rb +5 -4
  82. data/lib/ibex/analysis.rb +1 -0
  83. data/lib/ibex/artifact_set.rb +63 -0
  84. data/lib/ibex/bison_import/importer.rb +516 -0
  85. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  86. data/lib/ibex/bison_import.rb +200 -0
  87. data/lib/ibex/bounded_subprocess.rb +171 -0
  88. data/lib/ibex/cli/ambiguity.rb +75 -0
  89. data/lib/ibex/cli/analysis.rb +134 -0
  90. data/lib/ibex/cli/bison_import.rb +121 -0
  91. data/lib/ibex/cli/config.rb +128 -0
  92. data/lib/ibex/cli/counterexample_options.rb +6 -2
  93. data/lib/ibex/cli/coverage.rb +173 -0
  94. data/lib/ibex/cli/debug.rb +106 -0
  95. data/lib/ibex/cli/diagnostics.rb +136 -0
  96. data/lib/ibex/cli/documentation.rb +70 -0
  97. data/lib/ibex/cli/equiv.rb +169 -0
  98. data/lib/ibex/cli/error_messages.rb +184 -0
  99. data/lib/ibex/cli/explain.rb +85 -0
  100. data/lib/ibex/cli/fix.rb +203 -0
  101. data/lib/ibex/cli/formatting.rb +387 -0
  102. data/lib/ibex/cli/fuzz.rb +200 -0
  103. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  104. data/lib/ibex/cli/generation_artifacts.rb +189 -0
  105. data/lib/ibex/cli/generation_error_messages.rb +39 -0
  106. data/lib/ibex/cli/grammar_tests.rb +140 -0
  107. data/lib/ibex/cli/ir_tools.rb +128 -0
  108. data/lib/ibex/cli/lsp.rb +33 -0
  109. data/lib/ibex/cli/outputs.rb +133 -23
  110. data/lib/ibex/cli/racc_migration.rb +87 -0
  111. data/lib/ibex/cli/reduce.rb +202 -0
  112. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  113. data/lib/ibex/cli/samples.rb +118 -0
  114. data/lib/ibex/cli/verify.rb +116 -0
  115. data/lib/ibex/cli/watch.rb +104 -0
  116. data/lib/ibex/cli.rb +825 -55
  117. data/lib/ibex/codegen/action_locations.rb +103 -0
  118. data/lib/ibex/codegen/action_method_source.rb +209 -0
  119. data/lib/ibex/codegen/action_source.rb +119 -0
  120. data/lib/ibex/codegen/ambiguity.rb +174 -0
  121. data/lib/ibex/codegen/cst_metadata.rb +171 -0
  122. data/lib/ibex/codegen/documentation.rb +170 -0
  123. data/lib/ibex/codegen/explain.rb +380 -0
  124. data/lib/ibex/codegen/generated_action_abi.rb +293 -0
  125. data/lib/ibex/codegen/html.rb +94 -10
  126. data/lib/ibex/codegen/mermaid.rb +43 -0
  127. data/lib/ibex/codegen/railroad.rb +197 -0
  128. data/lib/ibex/codegen/railroad_documentation.rb +59 -0
  129. data/lib/ibex/codegen/rbs.rb +331 -2
  130. data/lib/ibex/codegen/report.rb +67 -15
  131. data/lib/ibex/codegen/ruby.rb +305 -66
  132. data/lib/ibex/codegen/ruby_actions.rb +146 -0
  133. data/lib/ibex/codegen/ruby_ast.rb +121 -0
  134. data/lib/ibex/codegen/ruby_error_messages.rb +28 -0
  135. data/lib/ibex/codegen/ruby_lexer.rb +83 -0
  136. data/lib/ibex/codegen/ruby_syntax.rb +96 -0
  137. data/lib/ibex/codegen/ruby_table_metadata.rb +57 -0
  138. data/lib/ibex/codegen/ruby_value_printers.rb +56 -0
  139. data/lib/ibex/codegen/symbol_labels.rb +1 -1
  140. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  141. data/lib/ibex/configuration/explanation.rb +421 -0
  142. data/lib/ibex/configuration/inspector.rb +232 -0
  143. data/lib/ibex/configuration.rb +580 -0
  144. data/lib/ibex/coverage/collector.rb +198 -0
  145. data/lib/ibex/coverage/event_stream.rb +103 -0
  146. data/lib/ibex/coverage/report.rb +267 -0
  147. data/lib/ibex/coverage/runtime_event_validator.rb +169 -0
  148. data/lib/ibex/coverage.rb +13 -0
  149. data/lib/ibex/delta_reducer.rb +99 -0
  150. data/lib/ibex/diff.rb +140 -0
  151. data/lib/ibex/equiv/machine.rb +135 -0
  152. data/lib/ibex/equiv.rb +373 -0
  153. data/lib/ibex/error_messages/parser.rb +159 -0
  154. data/lib/ibex/error_messages/parser_v2.rb +198 -0
  155. data/lib/ibex/error_messages/renderer.rb +65 -0
  156. data/lib/ibex/error_messages/sentence_search.rb +196 -0
  157. data/lib/ibex/error_messages/update.rb +169 -0
  158. data/lib/ibex/error_messages.rb +165 -0
  159. data/lib/ibex/fix.rb +557 -0
  160. data/lib/ibex/frontend/ast.rb +166 -7
  161. data/lib/ibex/frontend/bootstrap_parser.rb +40 -4
  162. data/lib/ibex/frontend/diagnostic.rb +81 -0
  163. data/lib/ibex/frontend/diagnostic_recovery.rb +268 -0
  164. data/lib/ibex/frontend/dsl.rb +66 -7
  165. data/lib/ibex/frontend/formatter.rb +453 -0
  166. data/lib/ibex/frontend/generated_parser.rb +644 -190
  167. data/lib/ibex/frontend/generated_parser_base.rb +170 -32
  168. data/lib/ibex/frontend/generated_parser_includes.rb +51 -0
  169. data/lib/ibex/frontend/generated_parser_metadata.rb +61 -0
  170. data/lib/ibex/frontend/generated_parser_parameters.rb +60 -0
  171. data/lib/ibex/frontend/generation.rb +33 -0
  172. data/lib/ibex/frontend/lexer.rb +162 -10
  173. data/lib/ibex/frontend/lexer_recovery.rb +84 -0
  174. data/lib/ibex/frontend/parser/declarations.rb +262 -11
  175. data/lib/ibex/frontend/parser/parameters.rb +82 -0
  176. data/lib/ibex/frontend/parser/rules.rb +43 -6
  177. data/lib/ibex/frontend/parser.rb +182 -4
  178. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  179. data/lib/ibex/frontend/regenerator.rb +27 -1
  180. data/lib/ibex/frontend/resolution.rb +70 -0
  181. data/lib/ibex/frontend/resolver.rb +217 -0
  182. data/lib/ibex/frontend/rule_documentation.rb +104 -0
  183. data/lib/ibex/frontend/source_cursor.rb +133 -9
  184. data/lib/ibex/frontend/source_document.rb +229 -0
  185. data/lib/ibex/frontend/source_loader.rb +150 -0
  186. data/lib/ibex/frontend/source_span.rb +89 -0
  187. data/lib/ibex/frontend/token_adapter/declaration_document_state.rb +47 -0
  188. data/lib/ibex/frontend/token_adapter/declaration_lexer_state.rb +83 -0
  189. data/lib/ibex/frontend/token_adapter/declaration_state.rb +244 -22
  190. data/lib/ibex/frontend/token_adapter/delimiter_tracker.rb +8 -2
  191. data/lib/ibex/frontend/token_adapter/rule_state.rb +60 -2
  192. data/lib/ibex/frontend/token_adapter.rb +18 -3
  193. data/lib/ibex/frontend.rb +14 -2
  194. data/lib/ibex/fuzz.rb +219 -0
  195. data/lib/ibex/generation_input.rb +57 -0
  196. data/lib/ibex/generation_manifest.rb +211 -0
  197. data/lib/ibex/generation_transaction.rb +261 -0
  198. data/lib/ibex/generation_transaction_recovery.rb +109 -0
  199. data/lib/ibex/generation_transaction_validation.rb +196 -0
  200. data/lib/ibex/grammar_tests.rb +206 -0
  201. data/lib/ibex/ir/automaton_ir.rb +86 -11
  202. data/lib/ibex/ir/grammar_ir.rb +170 -26
  203. data/lib/ibex/ir/lexer_ir.rb +76 -0
  204. data/lib/ibex/ir/parser_contract.rb +135 -0
  205. data/lib/ibex/ir/serialize.rb +268 -50
  206. data/lib/ibex/ir/validator/automaton.rb +375 -0
  207. data/lib/ibex/ir/validator/base.rb +147 -0
  208. data/lib/ibex/ir/validator/grammar.rb +634 -0
  209. data/lib/ibex/ir/validator/lexer.rb +113 -0
  210. data/lib/ibex/ir/validator.rb +63 -0
  211. data/lib/ibex/ir.rb +71 -5
  212. data/lib/ibex/lalr/build_metrics.rb +71 -0
  213. data/lib/ibex/lalr/builder.rb +514 -52
  214. data/lib/ibex/lalr/conflict.rb +1 -0
  215. data/lib/ibex/lalr/conflict_search.rb +26 -9
  216. data/lib/ibex/lalr/counterexample.rb +38 -18
  217. data/lib/ibex/lalr/direct_lookaheads.rb +289 -0
  218. data/lib/ibex/lalr/goto_follows.rb +229 -0
  219. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  220. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  221. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  222. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  223. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  224. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  225. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  226. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  227. data/lib/ibex/lalr/ielr_partition.rb +166 -0
  228. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  229. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  230. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  231. data/lib/ibex/lalr/on_error_reductions.rb +74 -0
  232. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  233. data/lib/ibex/lalr.rb +59 -1
  234. data/lib/ibex/location.rb +129 -0
  235. data/lib/ibex/lsp/document_handlers.rb +68 -0
  236. data/lib/ibex/lsp/document_store.rb +264 -0
  237. data/lib/ibex/lsp/document_store_diagnostics.rb +44 -0
  238. data/lib/ibex/lsp/document_store_validation.rb +61 -0
  239. data/lib/ibex/lsp/initialization_handlers.rb +80 -0
  240. data/lib/ibex/lsp/navigation_handlers.rb +70 -0
  241. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  242. data/lib/ibex/lsp/position_codec.rb +113 -0
  243. data/lib/ibex/lsp/protocol_error.rb +33 -0
  244. data/lib/ibex/lsp/request_handlers.rb +46 -0
  245. data/lib/ibex/lsp/request_support.rb +94 -0
  246. data/lib/ibex/lsp/server.rb +148 -0
  247. data/lib/ibex/lsp/symbol_index.rb +241 -0
  248. data/lib/ibex/lsp/symbol_index_builder.rb +277 -0
  249. data/lib/ibex/lsp/symbol_index_precedence_references.rb +44 -0
  250. data/lib/ibex/lsp/symbol_index_source_queries.rb +61 -0
  251. data/lib/ibex/lsp/symbol_occurrence.rb +23 -0
  252. data/lib/ibex/lsp/transport.rb +128 -0
  253. data/lib/ibex/lsp/workspace.rb +128 -0
  254. data/lib/ibex/lsp/workspace_analyzer.rb +199 -0
  255. data/lib/ibex/lsp.rb +33 -0
  256. data/lib/ibex/messages/en.yml +28 -0
  257. data/lib/ibex/messages/ja.yml +28 -0
  258. data/lib/ibex/messages.rb +68 -0
  259. data/lib/ibex/metrics.rb +175 -0
  260. data/lib/ibex/normalize/declarations.rb +142 -7
  261. data/lib/ibex/normalize/diagnostics.rb +94 -24
  262. data/lib/ibex/normalize/expander.rb +64 -59
  263. data/lib/ibex/normalize/expression.rb +63 -40
  264. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  265. data/lib/ibex/normalize/inline_expansion.rb +465 -0
  266. data/lib/ibex/normalize/inline_validation.rb +174 -0
  267. data/lib/ibex/normalize/lexer.rb +131 -0
  268. data/lib/ibex/normalize/named_references.rb +60 -0
  269. data/lib/ibex/normalize/nodes.rb +46 -0
  270. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +70 -0
  271. data/lib/ibex/normalize/parameter_lowering.rb +143 -0
  272. data/lib/ibex/normalize/parameter_substitution.rb +125 -0
  273. data/lib/ibex/normalize/parameter_validation.rb +140 -0
  274. data/lib/ibex/normalize/parameters.rb +226 -0
  275. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  276. data/lib/ibex/normalize/recovery_declarations.rb +84 -0
  277. data/lib/ibex/normalize.rb +172 -17
  278. data/lib/ibex/racc_migration/checker.rb +122 -0
  279. data/lib/ibex/racc_migration/harness.rb +177 -0
  280. data/lib/ibex/racc_migration/report.rb +97 -0
  281. data/lib/ibex/racc_migration.rb +12 -0
  282. data/lib/ibex/rake_task.rb +116 -0
  283. data/lib/ibex/samples.rb +216 -0
  284. data/lib/ibex/table_artifact/builder.rb +266 -0
  285. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  286. data/lib/ibex/table_artifact/document.rb +39 -0
  287. data/lib/ibex/table_artifact/executor.rb +187 -0
  288. data/lib/ibex/table_artifact/serializer.rb +57 -0
  289. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  290. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  291. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  292. data/lib/ibex/table_artifact/validator.rb +253 -0
  293. data/lib/ibex/table_artifact.rb +88 -0
  294. data/lib/ibex/table_simulation/result.rb +55 -0
  295. data/lib/ibex/table_simulation/simulator.rb +253 -0
  296. data/lib/ibex/table_simulation/step.rb +62 -0
  297. data/lib/ibex/table_simulation/text.rb +31 -0
  298. data/lib/ibex/table_simulation.rb +13 -0
  299. data/lib/ibex/tables.rb +22 -69
  300. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  301. data/lib/ibex/verification_report/builder.rb +159 -0
  302. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  303. data/lib/ibex/verification_report/logical_path.rb +68 -0
  304. data/lib/ibex/verification_report/validator.rb +329 -0
  305. data/lib/ibex/verification_report.rb +69 -0
  306. data/lib/ibex/verify/action_correspondence.rb +138 -0
  307. data/lib/ibex/verify/language_witness.rb +295 -0
  308. data/lib/ibex/verify/reference_collection.rb +190 -0
  309. data/lib/ibex/verify/result.rb +73 -0
  310. data/lib/ibex/verify/verifier.rb +598 -0
  311. data/lib/ibex/verify.rb +15 -0
  312. data/lib/ibex/version.rb +1 -1
  313. data/lib/ibex/watch/runner.rb +181 -0
  314. data/lib/ibex/watch/source_snapshot.rb +98 -0
  315. data/lib/ibex/watch.rb +11 -0
  316. data/lib/ibex.rb +35 -1
  317. data/schema/automaton-ir-definitions.schema.json +401 -0
  318. data/schema/automaton-ir.schema.json +65 -0
  319. data/schema/benchmark-v1.schema.json +212 -0
  320. data/schema/benchmark-v2.schema.json +61 -0
  321. data/schema/bison-import-v1.schema.json +106 -0
  322. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  323. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  324. data/schema/construction-profile-v1.schema.json +779 -0
  325. data/schema/cst-v1.json +170 -0
  326. data/schema/diff-v1.schema.json +41 -0
  327. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  328. data/schema/equiv-v1.schema.json +64 -0
  329. data/schema/error-ux-review-v1.schema.json +609 -0
  330. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  331. data/schema/error-ux-round2-v1.schema.json +519 -0
  332. data/schema/error-ux-v1.schema.json +258 -0
  333. data/schema/explain-v1.schema.json +495 -0
  334. data/schema/fix-v1.schema.json +64 -0
  335. data/schema/fix-v2.schema.json +66 -0
  336. data/schema/fix-v3.schema.json +101 -0
  337. data/schema/frontend-diagnostics-v1.schema.json +154 -0
  338. data/schema/fuzz-regression-v1.schema.json +79 -0
  339. data/schema/fuzz-v1.schema.json +114 -0
  340. data/schema/generation-manifest-v1.schema.json +115 -0
  341. data/schema/grammar-ir-extensions.schema.json +734 -0
  342. data/schema/grammar-ir-foundation.schema.json +426 -0
  343. data/schema/grammar-ir.schema.json +271 -0
  344. data/schema/ielr-benchmark-v1.schema.json +85 -0
  345. data/schema/lexer-ir-v1.schema.json +215 -0
  346. data/schema/lexer-profile-v1.schema.json +491 -0
  347. data/schema/metrics-v1.schema.json +60 -0
  348. data/schema/migration-check-v1.schema.json +60 -0
  349. data/schema/performance-comparison-v1.schema.json +395 -0
  350. data/schema/public-performance-comparison-v1.schema.json +506 -0
  351. data/schema/public-performance-profile-v1.schema.json +360 -0
  352. data/schema/reduce-v1.schema.json +22 -0
  353. data/schema/reduce-v2.schema.json +63 -0
  354. data/schema/runtime-coverage-v1.schema.json +86 -0
  355. data/schema/runtime-event-v1.schema.json +308 -0
  356. data/schema/table-artifact-v1.schema.json +377 -0
  357. data/schema/table-simulation-v1.schema.json +70 -0
  358. data/schema/verification-report-v1.schema.json +222 -0
  359. data/schema/verify-v1.schema.json +42 -0
  360. data/sig/ibex/analysis/digraph.rbs +19 -0
  361. data/sig/ibex/artifact_set.rbs +37 -0
  362. data/sig/ibex/bison_import/importer.rbs +140 -0
  363. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  364. data/sig/ibex/bison_import.rbs +95 -0
  365. data/sig/ibex/bounded_subprocess.rbs +76 -0
  366. data/sig/ibex/cli/ambiguity.rbs +28 -0
  367. data/sig/ibex/cli/analysis.rbs +43 -0
  368. data/sig/ibex/cli/bison_import.rbs +32 -0
  369. data/sig/ibex/cli/config.rbs +35 -0
  370. data/sig/ibex/cli/counterexample_options.rbs +2 -0
  371. data/sig/ibex/cli/coverage.rbs +53 -0
  372. data/sig/ibex/cli/debug.rbs +28 -0
  373. data/sig/ibex/cli/diagnostics.rbs +51 -0
  374. data/sig/ibex/cli/documentation.rbs +29 -0
  375. data/sig/ibex/cli/equiv.rbs +49 -0
  376. data/sig/ibex/cli/error_messages.rbs +63 -0
  377. data/sig/ibex/cli/explain.rbs +31 -0
  378. data/sig/ibex/cli/fix.rbs +53 -0
  379. data/sig/ibex/cli/formatting.rbs +107 -0
  380. data/sig/ibex/cli/fuzz.rbs +47 -0
  381. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  382. data/sig/ibex/cli/generation_artifacts.rbs +69 -0
  383. data/sig/ibex/cli/generation_error_messages.rbs +19 -0
  384. data/sig/ibex/cli/grammar_tests.rbs +48 -0
  385. data/sig/ibex/cli/ir_tools.rbs +39 -0
  386. data/sig/ibex/cli/lsp.rbs +14 -0
  387. data/sig/ibex/cli/outputs.rbs +26 -3
  388. data/sig/ibex/cli/racc_migration.rbs +30 -0
  389. data/sig/ibex/cli/reduce.rbs +60 -0
  390. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  391. data/sig/ibex/cli/samples.rbs +38 -0
  392. data/sig/ibex/cli/verify.rbs +28 -0
  393. data/sig/ibex/cli/watch.rbs +43 -0
  394. data/sig/ibex/cli.rbs +182 -9
  395. data/sig/ibex/codegen/action_locations.rbs +45 -0
  396. data/sig/ibex/codegen/action_method_source.rbs +65 -0
  397. data/sig/ibex/codegen/action_source.rbs +50 -0
  398. data/sig/ibex/codegen/ambiguity.rbs +60 -0
  399. data/sig/ibex/codegen/cst_metadata.rbs +59 -0
  400. data/sig/ibex/codegen/documentation.rbs +50 -0
  401. data/sig/ibex/codegen/explain.rbs +105 -0
  402. data/sig/ibex/codegen/generated_action_abi.rbs +105 -0
  403. data/sig/ibex/codegen/html.rbs +18 -2
  404. data/sig/ibex/codegen/mermaid.rbs +16 -0
  405. data/sig/ibex/codegen/railroad.rbs +82 -0
  406. data/sig/ibex/codegen/railroad_documentation.rbs +31 -0
  407. data/sig/ibex/codegen/rbs.rbs +86 -4
  408. data/sig/ibex/codegen/report.rbs +16 -8
  409. data/sig/ibex/codegen/ruby.rbs +105 -28
  410. data/sig/ibex/codegen/ruby_actions.rbs +54 -0
  411. data/sig/ibex/codegen/ruby_ast.rbs +34 -0
  412. data/sig/ibex/codegen/ruby_error_messages.rbs +16 -0
  413. data/sig/ibex/codegen/ruby_lexer.rbs +25 -0
  414. data/sig/ibex/codegen/ruby_syntax.rbs +27 -0
  415. data/sig/ibex/codegen/ruby_table_metadata.rbs +26 -0
  416. data/sig/ibex/codegen/ruby_value_printers.rbs +28 -0
  417. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  418. data/sig/ibex/configuration/explanation.rbs +195 -0
  419. data/sig/ibex/configuration/inspector.rbs +53 -0
  420. data/sig/ibex/configuration.rbs +242 -0
  421. data/sig/ibex/coverage/collector.rbs +78 -0
  422. data/sig/ibex/coverage/event_stream.rbs +44 -0
  423. data/sig/ibex/coverage/report.rbs +102 -0
  424. data/sig/ibex/coverage/runtime_event_validator.rbs +70 -0
  425. data/sig/ibex/coverage.rbs +7 -0
  426. data/sig/ibex/delta_reducer.rbs +38 -0
  427. data/sig/ibex/diff.rbs +47 -0
  428. data/sig/ibex/equiv/machine.rbs +54 -0
  429. data/sig/ibex/equiv.rbs +129 -0
  430. data/sig/ibex/error_messages/parser.rbs +58 -0
  431. data/sig/ibex/error_messages/parser_v2.rbs +67 -0
  432. data/sig/ibex/error_messages/renderer.rbs +23 -0
  433. data/sig/ibex/error_messages/sentence_search.rbs +80 -0
  434. data/sig/ibex/error_messages/update.rbs +43 -0
  435. data/sig/ibex/error_messages.rbs +85 -0
  436. data/sig/ibex/fix.rbs +138 -0
  437. data/sig/ibex/frontend/ast.rbs +237 -22
  438. data/sig/ibex/frontend/bootstrap_parser.rbs +13 -0
  439. data/sig/ibex/frontend/diagnostic.rbs +53 -0
  440. data/sig/ibex/frontend/diagnostic_recovery.rbs +98 -0
  441. data/sig/ibex/frontend/dsl.rbs +33 -4
  442. data/sig/ibex/frontend/formatter.rbs +143 -0
  443. data/sig/ibex/frontend/generated_parser.rbs +228 -68
  444. data/sig/ibex/frontend/generated_parser_base.rbs +65 -12
  445. data/sig/ibex/frontend/generated_parser_includes.rbs +23 -0
  446. data/sig/ibex/frontend/generated_parser_metadata.rbs +23 -0
  447. data/sig/ibex/frontend/generated_parser_parameters.rbs +24 -0
  448. data/sig/ibex/frontend/generation.rbs +6 -0
  449. data/sig/ibex/frontend/lexer.rbs +49 -2
  450. data/sig/ibex/frontend/lexer_recovery.rbs +25 -0
  451. data/sig/ibex/frontend/parser/declarations.rbs +57 -0
  452. data/sig/ibex/frontend/parser/parameters.rbs +28 -0
  453. data/sig/ibex/frontend/parser/rules.rbs +3 -0
  454. data/sig/ibex/frontend/parser.rbs +66 -0
  455. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  456. data/sig/ibex/frontend/regenerator.rbs +11 -0
  457. data/sig/ibex/frontend/resolution.rbs +34 -0
  458. data/sig/ibex/frontend/resolver.rbs +91 -0
  459. data/sig/ibex/frontend/rule_documentation.rbs +42 -0
  460. data/sig/ibex/frontend/source_cursor.rbs +38 -5
  461. data/sig/ibex/frontend/source_document.rbs +119 -0
  462. data/sig/ibex/frontend/source_loader.rbs +66 -0
  463. data/sig/ibex/frontend/source_span.rbs +56 -0
  464. data/sig/ibex/frontend/token_adapter/declaration_document_state.rbs +21 -0
  465. data/sig/ibex/frontend/token_adapter/declaration_lexer_state.rbs +27 -0
  466. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +83 -3
  467. data/sig/ibex/frontend/token_adapter/rule_state.rbs +20 -0
  468. data/sig/ibex/frontend/token_adapter.rbs +11 -2
  469. data/sig/ibex/frontend.rbs +1 -1
  470. data/sig/ibex/fuzz.rbs +71 -0
  471. data/sig/ibex/generation_input.rbs +37 -0
  472. data/sig/ibex/generation_manifest.rbs +70 -0
  473. data/sig/ibex/generation_transaction.rbs +82 -0
  474. data/sig/ibex/generation_transaction_recovery.rbs +36 -0
  475. data/sig/ibex/generation_transaction_validation.rbs +65 -0
  476. data/sig/ibex/grammar_tests.rbs +93 -0
  477. data/sig/ibex/ir/automaton_ir.rbs +35 -10
  478. data/sig/ibex/ir/grammar_ir.rbs +96 -22
  479. data/sig/ibex/ir/lexer_ir.rbs +57 -0
  480. data/sig/ibex/ir/parser_contract.rbs +69 -0
  481. data/sig/ibex/ir/serialize.rbs +75 -23
  482. data/sig/ibex/ir/validator/automaton.rbs +112 -0
  483. data/sig/ibex/ir/validator/base.rbs +69 -0
  484. data/sig/ibex/ir/validator/grammar.rbs +195 -0
  485. data/sig/ibex/ir/validator/lexer.rbs +37 -0
  486. data/sig/ibex/ir/validator.rbs +16 -0
  487. data/sig/ibex/ir.rbs +45 -5
  488. data/sig/ibex/lalr/build_metrics.rbs +60 -0
  489. data/sig/ibex/lalr/builder.rbs +150 -19
  490. data/sig/ibex/lalr/conflict_search.rbs +12 -5
  491. data/sig/ibex/lalr/counterexample.rbs +20 -14
  492. data/sig/ibex/lalr/direct_lookaheads.rbs +93 -0
  493. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  494. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  495. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  496. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  497. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  498. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  499. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  500. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  501. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  502. data/sig/ibex/lalr/ielr_partition.rbs +71 -0
  503. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  504. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  505. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  506. data/sig/ibex/lalr/on_error_reductions.rbs +22 -0
  507. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  508. data/sig/ibex/lalr.rbs +17 -1
  509. data/sig/ibex/location.rbs +67 -0
  510. data/sig/ibex/lsp/document_handlers.rbs +24 -0
  511. data/sig/ibex/lsp/document_store.rbs +94 -0
  512. data/sig/ibex/lsp/document_store_diagnostics.rbs +20 -0
  513. data/sig/ibex/lsp/document_store_validation.rbs +26 -0
  514. data/sig/ibex/lsp/initialization_handlers.rbs +30 -0
  515. data/sig/ibex/lsp/navigation_handlers.rbs +33 -0
  516. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  517. data/sig/ibex/lsp/position_codec.rbs +44 -0
  518. data/sig/ibex/lsp/protocol_error.rbs +32 -0
  519. data/sig/ibex/lsp/request_handlers.rbs +26 -0
  520. data/sig/ibex/lsp/request_support.rbs +40 -0
  521. data/sig/ibex/lsp/server.rbs +55 -0
  522. data/sig/ibex/lsp/symbol_index.rbs +77 -0
  523. data/sig/ibex/lsp/symbol_index_builder.rbs +88 -0
  524. data/sig/ibex/lsp/symbol_index_precedence_references.rbs +18 -0
  525. data/sig/ibex/lsp/symbol_index_source_queries.rbs +26 -0
  526. data/sig/ibex/lsp/symbol_occurrence.rbs +31 -0
  527. data/sig/ibex/lsp/transport.rbs +47 -0
  528. data/sig/ibex/lsp/workspace.rbs +42 -0
  529. data/sig/ibex/lsp/workspace_analyzer.rbs +69 -0
  530. data/sig/ibex/lsp.rbs +7 -0
  531. data/sig/ibex/messages.rbs +26 -0
  532. data/sig/ibex/metrics.rbs +50 -0
  533. data/sig/ibex/normalize/declarations.rbs +31 -0
  534. data/sig/ibex/normalize/diagnostics.rbs +13 -4
  535. data/sig/ibex/normalize/expander.rbs +22 -16
  536. data/sig/ibex/normalize/expression.rbs +11 -9
  537. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  538. data/sig/ibex/normalize/inline_expansion.rbs +129 -0
  539. data/sig/ibex/normalize/inline_validation.rbs +40 -0
  540. data/sig/ibex/normalize/lexer.rbs +37 -0
  541. data/sig/ibex/normalize/named_references.rbs +20 -0
  542. data/sig/ibex/normalize/nodes.rbs +14 -0
  543. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +33 -0
  544. data/sig/ibex/normalize/parameter_lowering.rbs +35 -0
  545. data/sig/ibex/normalize/parameter_substitution.rbs +42 -0
  546. data/sig/ibex/normalize/parameter_validation.rbs +44 -0
  547. data/sig/ibex/normalize/parameters.rbs +51 -0
  548. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  549. data/sig/ibex/normalize/recovery_declarations.rbs +24 -0
  550. data/sig/ibex/normalize.rbs +129 -18
  551. data/sig/ibex/racc_migration/checker.rbs +36 -0
  552. data/sig/ibex/racc_migration/harness.rbs +16 -0
  553. data/sig/ibex/racc_migration/report.rbs +55 -0
  554. data/sig/ibex/racc_migration.rbs +8 -0
  555. data/sig/ibex/rake_task.rbs +51 -0
  556. data/sig/ibex/samples.rbs +60 -0
  557. data/sig/ibex/table_artifact/builder.rbs +93 -0
  558. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  559. data/sig/ibex/table_artifact/document.rbs +27 -0
  560. data/sig/ibex/table_artifact/executor.rbs +66 -0
  561. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  562. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  563. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  564. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  565. data/sig/ibex/table_artifact/validator.rbs +66 -0
  566. data/sig/ibex/table_artifact.rbs +32 -0
  567. data/sig/ibex/table_simulation/result.rbs +36 -0
  568. data/sig/ibex/table_simulation/simulator.rbs +98 -0
  569. data/sig/ibex/table_simulation/step.rbs +42 -0
  570. data/sig/ibex/table_simulation/text.rbs +14 -0
  571. data/sig/ibex/table_simulation.rbs +7 -0
  572. data/sig/ibex/tables.rbs +10 -35
  573. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  574. data/sig/ibex/verification_report/builder.rbs +53 -0
  575. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  576. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  577. data/sig/ibex/verification_report/validator.rbs +114 -0
  578. data/sig/ibex/verification_report.rbs +44 -0
  579. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  580. data/sig/ibex/verify/language_witness.rbs +112 -0
  581. data/sig/ibex/verify/reference_collection.rbs +63 -0
  582. data/sig/ibex/verify/result.rbs +52 -0
  583. data/sig/ibex/verify/verifier.rbs +170 -0
  584. data/sig/ibex/verify.rbs +6 -0
  585. data/sig/ibex/watch/runner.rbs +58 -0
  586. data/sig/ibex/watch/source_snapshot.rbs +46 -0
  587. data/sig/ibex/watch.rbs +7 -0
  588. data/sig/ibex.rbs +2 -0
  589. metadata +524 -19
  590. data/.rubocop.yml +0 -43
  591. data/CHANGELOG.md +0 -30
  592. data/Rakefile +0 -25
  593. data/Steepfile +0 -10
  594. data/docs/compat-notes.md +0 -37
  595. data/docs/lexer-coverage.md +0 -14
  596. data/docs/phase10-extensions.md +0 -27
  597. data/gemfiles/Gemfile +0 -7
  598. data/gemfiles/Gemfile.lock +0 -98
  599. data/lib/ibex/frontend/grammar.y +0 -156
  600. data/lib/ibex/runtime/parser.rb +0 -360
  601. data/lib/ibex/runtime.rb +0 -8
  602. data/sig/ibex/runtime/parser.rbs +0 -167
  603. data/sig/ibex/runtime.rbs +0 -6
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module Coverage
6
+ # Strictly turns one or more complete parse sessions into a coverage report.
7
+ class Collector
8
+ # @rbs!
9
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
10
+
11
+ # @rbs @grammar_digest: String?
12
+ # @rbs @table_format_version: Integer?
13
+ # @rbs @state_count: Integer?
14
+ # @rbs @production_count: Integer?
15
+ # @rbs @sessions: Integer
16
+ # @rbs @event_count: Integer
17
+ # @rbs @state_hits: Hash[Integer, Integer]
18
+ # @rbs @production_hits: Hash[Integer, Integer]
19
+ # @rbs @in_session: bool
20
+ # @rbs @expected_sequence: Integer
21
+
22
+ # @rbs () -> void
23
+ def initialize
24
+ @grammar_digest = nil
25
+ @table_format_version = nil
26
+ @state_count = nil
27
+ @production_count = nil
28
+ @sessions = 0
29
+ @event_count = 0
30
+ @state_hits = Hash.new(0)
31
+ @production_hits = Hash.new(0)
32
+ @in_session = false
33
+ @expected_sequence = 1
34
+ end
35
+
36
+ # @rbs (String path) -> Report
37
+ def self.collect_file(path)
38
+ collector = new
39
+ EventStream.each_file(path) do |document, line|
40
+ collector.consume(document, source: path, line: line)
41
+ end
42
+ collector.finish(source: path)
43
+ end
44
+
45
+ # @rbs (Hash[String, json_value] document, source: String, line: Integer) -> void
46
+ def consume(document, source:, line:)
47
+ event = document.fetch("event")
48
+ sequence = document.fetch("sequence")
49
+ data = document.fetch("data")
50
+ unless event.is_a?(String) && data.is_a?(Hash) && data.keys.all?(String)
51
+ invalid(source, line, "event document contains invalid session data")
52
+ end
53
+ event_data = data #: Hash[String, json_value]
54
+ if event == "start"
55
+ start_session(event_data, sequence, source, line)
56
+ else
57
+ consume_session_event(event, event_data, sequence, source, line)
58
+ end
59
+ @event_count += 1
60
+ end
61
+
62
+ # @rbs (source: String) -> Report
63
+ def finish(source:)
64
+ invalid(source, 1, "event stream ended before accept or reject") if @in_session
65
+ invalid(source, 1, "event stream does not contain a parse session") if @sessions.zero?
66
+ Report.new(
67
+ grammar_digest: required_metadata(@grammar_digest),
68
+ table_format_version: required_metadata(@table_format_version),
69
+ state_count: required_metadata(@state_count),
70
+ production_count: required_metadata(@production_count),
71
+ sessions: @sessions,
72
+ event_count: @event_count,
73
+ state_hits: @state_hits,
74
+ production_hits: @production_hits
75
+ )
76
+ rescue ArgumentError => e
77
+ raise Ibex::Error, "#{source}:1:1: #{e.message}"
78
+ end
79
+
80
+ private
81
+
82
+ # @rbs (Hash[String, json_value] data, json_value sequence, String source, Integer line) -> void
83
+ def start_session(data, sequence, source, line)
84
+ invalid(source, line, "new start event before prior session ended") if @in_session
85
+ invalid(source, line, "start event sequence must be 1") unless sequence == 1
86
+
87
+ metadata, initial_state = session_metadata(data, source, line)
88
+ establish_metadata(metadata, source, line)
89
+ validate_metadata!(source, line)
90
+ hit_state(initial_state, source, line)
91
+ @sessions += 1
92
+ @in_session = true
93
+ @expected_sequence = 2
94
+ end
95
+
96
+ # @rbs (String event, Hash[String, json_value] data, json_value sequence, String source, Integer line) -> void
97
+ def consume_session_event(event, data, sequence, source, line)
98
+ invalid(source, line, "event appears outside a parse session") unless @in_session
99
+ unless sequence == @expected_sequence
100
+ invalid(source, line, "expected event sequence #{@expected_sequence}, got #{sequence.inspect}")
101
+ end
102
+
103
+ case event
104
+ when "shift", "recover" then hit_state(data["state"], source, line)
105
+ when "reduce"
106
+ hit_production(data["production_id"], source, line)
107
+ hit_state(data["goto_state"], source, line)
108
+ end
109
+ if %w[accept reject].include?(event)
110
+ @in_session = false
111
+ @expected_sequence = 1
112
+ else
113
+ @expected_sequence += 1
114
+ end
115
+ end
116
+
117
+ # @rbs (Hash[String, json_value] data, String source, Integer line)
118
+ # -> [[String, Integer, Integer, Integer], Integer]
119
+ def session_metadata(data, source, line)
120
+ digest = data["grammar_digest"]
121
+ format = data["table_format_version"]
122
+ states = data["state_count"]
123
+ productions = data["production_count"]
124
+ initial_state = data["initial_state"]
125
+ unless digest.is_a?(String) && format.is_a?(Integer) && states.is_a?(Integer) &&
126
+ productions.is_a?(Integer) && initial_state.is_a?(Integer)
127
+ invalid(source, line, "start event lacks generated parser coverage metadata")
128
+ end
129
+ [[digest, format, states, productions], initial_state]
130
+ end
131
+
132
+ # @rbs ([String, Integer, Integer, Integer] metadata, String source, Integer line) -> void
133
+ def establish_metadata(metadata, source, line)
134
+ unless @grammar_digest
135
+ @grammar_digest, @table_format_version, @state_count, @production_count = metadata
136
+ return
137
+ end
138
+
139
+ expected = [@grammar_digest, @table_format_version, @state_count, @production_count]
140
+ invalid(source, line, "parse sessions use different parser metadata") unless metadata == expected
141
+ end
142
+
143
+ # @rbs (String source, Integer line) -> void
144
+ def validate_metadata!(source, line)
145
+ Report.new(
146
+ grammar_digest: required_metadata(@grammar_digest),
147
+ table_format_version: required_metadata(@table_format_version),
148
+ state_count: required_metadata(@state_count),
149
+ production_count: required_metadata(@production_count),
150
+ sessions: 1,
151
+ event_count: 1,
152
+ state_hits: {},
153
+ production_hits: {}
154
+ )
155
+ rescue ArgumentError => e
156
+ invalid(source, line, e.message)
157
+ end
158
+
159
+ # @rbs (json_value id, String source, Integer line) -> void
160
+ def hit_state(id, source, line)
161
+ total = required_metadata(@state_count)
162
+ invalid(source, line, "state id #{id.inspect} is outside 0...#{total}") unless valid_id?(id, total)
163
+ state_id = id #: Integer
164
+ increment(@state_hits, state_id, "state", source, line)
165
+ end
166
+
167
+ # @rbs (json_value id, String source, Integer line) -> void
168
+ def hit_production(id, source, line)
169
+ total = required_metadata(@production_count)
170
+ invalid(source, line, "production id #{id.inspect} is outside 0...#{total}") unless valid_id?(id, total)
171
+ production_id = id #: Integer
172
+ increment(@production_hits, production_id, "production", source, line)
173
+ end
174
+
175
+ # @rbs (json_value id, Integer total) -> bool
176
+ def valid_id?(id, total)
177
+ id.is_a?(Integer) && id >= 0 && id < total
178
+ end
179
+
180
+ # @rbs (Hash[Integer, Integer] hits, Integer id, String kind, String source, Integer line) -> void
181
+ def increment(hits, id, kind, source, line)
182
+ count = hits[id] + 1
183
+ invalid(source, line, "#{kind} hit count exceeds the supported count") if count > Report::MAX_COUNT
184
+ hits[id] = count
185
+ end
186
+
187
+ # @rbs [T] (T? value) -> T
188
+ def required_metadata(value)
189
+ value || raise(ArgumentError, "coverage metadata is unavailable")
190
+ end
191
+
192
+ # @rbs (String source, Integer line, String message) -> bot
193
+ def invalid(source, line, message)
194
+ raise Ibex::Error, "#{source}:#{line}:1: #{message}"
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+ require_relative "../error"
6
+ require_relative "../runtime/event"
7
+ require_relative "runtime_event_validator"
8
+
9
+ module Ibex
10
+ module Coverage
11
+ # Bounded reader for the public runtime-event JSON Lines protocol.
12
+ module EventStream
13
+ # @rbs!
14
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
15
+
16
+ # @rbs!
17
+ # interface _CoverageEventInput
18
+ # def gets: (Integer) -> String?
19
+ # end
20
+
21
+ MAX_LINE_BYTES = 1_048_576 #: Integer
22
+ MAX_NESTING = 32 #: Integer
23
+ ROOT_KEYS = %w[data event ibex_runtime_event schema_version sequence].freeze #: Array[String]
24
+ EVENT_TYPES = %w[start shift reduce error recover discard accept reject].freeze #: Array[String]
25
+
26
+ class << self
27
+ # @rbs (String path) { (Hash[String, json_value], Integer) -> void } -> void
28
+ def each_file(path, &block)
29
+ File.open(path, "rb") { |input| each_io(input, source: path, &block) }
30
+ end
31
+
32
+ # @rbs (_CoverageEventInput input, source: String)
33
+ # { (Hash[String, json_value], Integer) -> void } -> void
34
+ def each_io(input, source:, &block)
35
+ line_number = 0
36
+ while (line = input.gets(MAX_LINE_BYTES + 1))
37
+ line_number += 1
38
+ reject_oversized_line(line, source, line_number)
39
+ document = parse_line(line, source, line_number)
40
+ validate_envelope(document, source, line_number)
41
+ block.call(document, line_number)
42
+ end
43
+ raise_invalid(source, 1, "event stream is empty") if line_number.zero?
44
+ end
45
+
46
+ private
47
+
48
+ # @rbs (String line, String source, Integer line_number) -> void
49
+ def reject_oversized_line(line, source, line_number)
50
+ return if line.bytesize <= MAX_LINE_BYTES
51
+
52
+ raise_invalid(source, line_number, "event line exceeds #{MAX_LINE_BYTES} bytes")
53
+ end
54
+
55
+ # @rbs (String line, String source, Integer line_number) -> Hash[String, json_value]
56
+ def parse_line(line, source, line_number)
57
+ text = line.dup.force_encoding(Encoding::UTF_8)
58
+ raise_invalid(source, line_number, "event line is not valid UTF-8") unless text.valid_encoding?
59
+
60
+ value = JSON.parse(text, max_nesting: MAX_NESTING, allow_nan: false) #: json_value
61
+ if value.is_a?(Hash) && value.keys.all?(String)
62
+ document = value #: Hash[String, json_value]
63
+ return document
64
+ end
65
+
66
+ raise_invalid(source, line_number, "event line must be a JSON object")
67
+ rescue JSON::ParserError => e
68
+ raise_invalid(source, line_number, "invalid event JSON: #{e.message}")
69
+ end
70
+
71
+ # @rbs (Hash[String, json_value] document, String source, Integer line_number) -> void
72
+ def validate_envelope(document, source, line_number)
73
+ unless document.keys.sort == ROOT_KEYS
74
+ raise_invalid(source, line_number, "event object has unknown or missing fields")
75
+ end
76
+ unless document["ibex_runtime_event"] == Runtime::Event::IDENTIFIER &&
77
+ document["schema_version"] == Runtime::Event::SCHEMA_VERSION
78
+ raise_invalid(source, line_number, "unsupported runtime event schema")
79
+ end
80
+ sequence = document["sequence"]
81
+ raise_invalid(source, line_number, "event sequence must be positive") unless positive_integer?(sequence)
82
+ unless EVENT_TYPES.include?(document["event"])
83
+ raise_invalid(source, line_number, "unknown runtime event type")
84
+ end
85
+ raise_invalid(source, line_number, "event data must be an object") unless document["data"].is_a?(Hash)
86
+ return if RuntimeEventValidator.valid?(document)
87
+
88
+ raise_invalid(source, line_number, "event data does not match the runtime-event v1 schema")
89
+ end
90
+
91
+ # @rbs (json_value value) -> bool
92
+ def positive_integer?(value)
93
+ value.is_a?(Integer) && value.positive?
94
+ end
95
+
96
+ # @rbs (String source, Integer line_number, String message) -> bot
97
+ def raise_invalid(source, line_number, message)
98
+ raise Ibex::Error, "#{source}:#{line_number}:1: #{message}"
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "json"
5
+ require_relative "../error"
6
+
7
+ module Ibex
8
+ module Coverage
9
+ # Versioned, mergeable runtime coverage result.
10
+ class Report
11
+ # @rbs!
12
+ # type json_value = String | Integer | Float | bool | nil | Array[json_value] | Hash[String, json_value]
13
+
14
+ IDENTIFIER = "runtime-coverage" #: String
15
+ SCHEMA_VERSION = 1 #: Integer
16
+ MAX_DOCUMENT_BYTES = 16_777_216 #: Integer
17
+ MAX_TOTAL = 1_000_000 #: Integer
18
+ MAX_COUNT = 9_223_372_036_854_775_807 #: Integer
19
+ ROOT_KEYS = %w[events grammar_digest ibex_coverage production_hits schema_version sessions state_hits
20
+ table_format_version totals].freeze #: Array[String]
21
+ TOTAL_KEYS = %w[productions states].freeze #: Array[String]
22
+ HIT_KEYS = %w[count id].freeze #: Array[String]
23
+
24
+ attr_reader :grammar_digest #: String
25
+ attr_reader :table_format_version #: Integer
26
+ attr_reader :state_count #: Integer
27
+ attr_reader :production_count #: Integer
28
+ attr_reader :sessions #: Integer
29
+ attr_reader :event_count #: Integer
30
+ attr_reader :state_hits #: Hash[Integer, Integer]
31
+ attr_reader :production_hits #: Hash[Integer, Integer]
32
+
33
+ # rubocop:disable Layout/LineLength
34
+ # @rbs (grammar_digest: String, table_format_version: Integer, state_count: Integer, production_count: Integer, sessions: Integer, event_count: Integer, state_hits: Hash[Integer, Integer], production_hits: Hash[Integer, Integer]) -> void
35
+ def initialize(grammar_digest:, table_format_version:, state_count:, production_count:, sessions:, event_count:,
36
+ state_hits:, production_hits:)
37
+ @grammar_digest = validate_digest(grammar_digest)
38
+ @table_format_version = positive_integer(table_format_version, "table_format_version")
39
+ @state_count = total_integer(state_count, "state total", minimum: 1)
40
+ @production_count = total_integer(production_count, "production total", minimum: 0)
41
+ @sessions = positive_integer(sessions, "sessions")
42
+ @event_count = positive_integer(event_count, "events")
43
+ raise ArgumentError, "events must be at least sessions" if @event_count < @sessions
44
+
45
+ @state_hits = validate_hits(state_hits, @state_count, "state").freeze
46
+ @production_hits = validate_hits(production_hits, @production_count, "production").freeze
47
+ validate_event_bounds
48
+ freeze
49
+ end
50
+ # rubocop:enable Layout/LineLength
51
+
52
+ # @rbs () -> Hash[String, json_value]
53
+ def to_h
54
+ {
55
+ "ibex_coverage" => IDENTIFIER,
56
+ "schema_version" => SCHEMA_VERSION,
57
+ "grammar_digest" => @grammar_digest,
58
+ "table_format_version" => @table_format_version,
59
+ "totals" => { "states" => @state_count, "productions" => @production_count },
60
+ "sessions" => @sessions,
61
+ "events" => @event_count,
62
+ "state_hits" => hit_documents(@state_hits),
63
+ "production_hits" => hit_documents(@production_hits)
64
+ }
65
+ end
66
+
67
+ # @rbs (*Object) -> String
68
+ def to_json(*)
69
+ "#{JSON.pretty_generate(to_h)}\n"
70
+ end
71
+
72
+ # @rbs (String path) -> Report
73
+ def self.load_file(path)
74
+ size = File.size(path)
75
+ if size > MAX_DOCUMENT_BYTES
76
+ raise Ibex::Error, "#{path}:1:1: coverage document exceeds #{MAX_DOCUMENT_BYTES} bytes"
77
+ end
78
+
79
+ source = File.binread(path)
80
+ if source.bytesize > MAX_DOCUMENT_BYTES
81
+ raise Ibex::Error, "#{path}:1:1: coverage document exceeds #{MAX_DOCUMENT_BYTES} bytes"
82
+ end
83
+
84
+ source.force_encoding(Encoding::UTF_8)
85
+ raise Ibex::Error, "#{path}:1:1: coverage document is not valid UTF-8" unless source.valid_encoding?
86
+
87
+ value = JSON.parse(source, max_nesting: 16, allow_nan: false)
88
+ from_h(value, source: path)
89
+ rescue JSON::ParserError => e
90
+ raise Ibex::Error, "#{path}:1:1: invalid coverage JSON: #{e.message}"
91
+ end
92
+
93
+ # @rbs (json_value value, source: String) -> Report
94
+ def self.from_h(value, source:)
95
+ document = string_hash(value, source, "coverage document")
96
+ invalid(source, "coverage object has unknown or missing fields") unless document.keys.sort == ROOT_KEYS
97
+ valid_schema = document["ibex_coverage"] == IDENTIFIER &&
98
+ document["schema_version"] == SCHEMA_VERSION
99
+ invalid(source, "unsupported coverage schema") unless valid_schema
100
+ totals = string_hash(document["totals"], source, "coverage totals")
101
+ invalid(source, "coverage totals have unknown or missing fields") unless totals.keys.sort == TOTAL_KEYS
102
+
103
+ grammar_digest = document["grammar_digest"] #: String
104
+ table_format_version = document["table_format_version"] #: Integer
105
+ state_count = totals["states"] #: Integer
106
+ production_count = totals["productions"] #: Integer
107
+ sessions = document["sessions"] #: Integer
108
+ event_count = document["events"] #: Integer
109
+ new(
110
+ grammar_digest: grammar_digest,
111
+ table_format_version: table_format_version,
112
+ state_count: state_count,
113
+ production_count: production_count,
114
+ sessions: sessions,
115
+ event_count: event_count,
116
+ state_hits: parse_hits(document["state_hits"], state_count, source, "state"),
117
+ production_hits: parse_hits(document["production_hits"], production_count, source, "production")
118
+ )
119
+ rescue ArgumentError => e
120
+ raise Ibex::Error, "#{source}:1:1: #{e.message}"
121
+ end
122
+
123
+ # @rbs (Array[Report] reports) -> Report
124
+ def self.merge(reports)
125
+ raise ArgumentError, "at least one coverage report is required" if reports.empty?
126
+
127
+ first = reports.first
128
+ reports.drop(1).each { |report| ensure_compatible(first, report) }
129
+ new(
130
+ grammar_digest: first.grammar_digest,
131
+ table_format_version: first.table_format_version,
132
+ state_count: first.state_count,
133
+ production_count: first.production_count,
134
+ sessions: checked_sum(reports.map(&:sessions), "sessions"),
135
+ event_count: checked_sum(reports.map(&:event_count), "events"),
136
+ state_hits: merge_hits(reports.map(&:state_hits), "state"),
137
+ production_hits: merge_hits(reports.map(&:production_hits), "production")
138
+ )
139
+ end
140
+
141
+ private
142
+
143
+ # @rbs (json_value input) -> String
144
+ def validate_digest(input)
145
+ return input.dup.freeze if input.is_a?(String) && input.match?(/\Asha256:[0-9a-f]{64}\z/)
146
+
147
+ raise ArgumentError, "grammar_digest must be a full lowercase SHA-256 digest"
148
+ end
149
+
150
+ # @rbs (json_value input, String name) -> Integer
151
+ def positive_integer(input, name)
152
+ return input if input.is_a?(Integer) && input.positive? && input <= MAX_COUNT
153
+
154
+ raise ArgumentError, "#{name} must be a bounded positive integer"
155
+ end
156
+
157
+ # @rbs (json_value input, String name, minimum: Integer) -> Integer
158
+ def total_integer(input, name, minimum:)
159
+ return input if input.is_a?(Integer) && input >= minimum && input <= MAX_TOTAL
160
+
161
+ raise ArgumentError, "#{name} must be between #{minimum} and #{MAX_TOTAL}"
162
+ end
163
+
164
+ # @rbs (Hash[Integer, Integer] input, Integer total, String kind) -> Hash[Integer, Integer]
165
+ def validate_hits(input, total, kind)
166
+ input.to_h do |id, count|
167
+ unless id.is_a?(Integer) && id >= 0 && id < total
168
+ raise ArgumentError, "#{kind} hit id #{id.inspect} is outside 0...#{total}"
169
+ end
170
+
171
+ [id, positive_integer(count, "#{kind} hit count")]
172
+ end.sort.to_h
173
+ end
174
+
175
+ # @rbs (Hash[Integer, Integer] hits) -> Array[Hash[String, Integer]]
176
+ def hit_documents(hits)
177
+ hits.map { |id, count| { "id" => id, "count" => count } }
178
+ end
179
+
180
+ # @rbs () -> void
181
+ def validate_event_bounds
182
+ raise ArgumentError, "state hit counts exceed events" if @state_hits.values.sum > @event_count
183
+ return unless @production_hits.values.sum > @event_count
184
+
185
+ raise ArgumentError, "production hit counts exceed events"
186
+ end
187
+
188
+ class << self
189
+ private
190
+
191
+ # @rbs (json_value value, String source, String name) -> Hash[String, json_value]
192
+ def string_hash(value, source, name)
193
+ if value.is_a?(Hash) && value.keys.all?(String)
194
+ return value #: Hash[String, json_value]
195
+ end
196
+
197
+ invalid(source, "#{name} must be an object")
198
+ end
199
+
200
+ # @rbs (json_value value, json_value total, String source, String kind) -> Hash[Integer, Integer]
201
+ def parse_hits(value, total, source, kind)
202
+ invalid(source, "#{kind}_hits must be an array") unless value.is_a?(Array)
203
+ hits = {} #: Hash[Integer, Integer]
204
+ previous = -1
205
+ value.each do |entry|
206
+ id, count = parse_hit(entry, total, previous, source, kind)
207
+ hits[id] = count
208
+ previous = id
209
+ end
210
+ hits
211
+ end
212
+
213
+ # @rbs (json_value entry, json_value total, Integer previous, String source, String kind) -> [Integer, Integer]
214
+ def parse_hit(entry, total, previous, source, kind)
215
+ hit = string_hash(entry, source, "#{kind} hit")
216
+ invalid(source, "#{kind} hit has unknown or missing fields") unless hit.keys.sort == HIT_KEYS
217
+ id = hit["id"]
218
+ count = hit["count"]
219
+ unless id.is_a?(Integer) && id > previous
220
+ invalid(source, "#{kind} hits must have unique, ascending integer ids")
221
+ end
222
+ unless total.is_a?(Integer) && id >= 0 && id < total
223
+ invalid(source, "#{kind} hit id is outside the declared total")
224
+ end
225
+ unless count.is_a?(Integer) && count.positive? && count <= MAX_COUNT
226
+ invalid(source, "#{kind} hit count must be positive")
227
+ end
228
+ [id, count]
229
+ end
230
+
231
+ # @rbs (Report expected, Report actual) -> void
232
+ def ensure_compatible(expected, actual)
233
+ fields = %i[grammar_digest table_format_version state_count production_count]
234
+ mismatch = fields.find { |field| expected.public_send(field) != actual.public_send(field) }
235
+ return unless mismatch
236
+
237
+ raise ArgumentError, "coverage reports have different #{mismatch}"
238
+ end
239
+
240
+ # @rbs (Array[Integer] values, String name) -> Integer
241
+ def checked_sum(values, name)
242
+ total = values.sum
243
+ raise ArgumentError, "merged #{name} exceed the supported count" if total > MAX_COUNT
244
+
245
+ total
246
+ end
247
+
248
+ # @rbs (Array[Hash[Integer, Integer]] collections, String kind) -> Hash[Integer, Integer]
249
+ def merge_hits(collections, kind)
250
+ result = Hash.new(0) #: Hash[Integer, Integer]
251
+ collections.each do |hits|
252
+ hits.each do |id, count|
253
+ result[id] += count
254
+ raise ArgumentError, "merged #{kind} hit count exceeds the supported count" if result[id] > MAX_COUNT
255
+ end
256
+ end
257
+ result
258
+ end
259
+
260
+ # @rbs (String source, String message) -> bot
261
+ def invalid(source, message)
262
+ raise Ibex::Error, "#{source}:1:1: #{message}"
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end