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
@@ -11,13 +11,23 @@ module Ibex
11
11
  # Hash[Integer, String] labels) -> String
12
12
  # private def self.state_sections: (IR::Automaton automaton, IR::Grammar grammar,
13
13
  # Hash[Integer, String] labels) -> String
14
+ # private def controls: (IR::Automaton automaton) -> String
15
+ # private def self.controls: (IR::Automaton automaton) -> String
16
+ # private def styles: () -> String
17
+ # private def self.styles: () -> String
18
+ # private def interaction_script: () -> String
19
+ # private def self.interaction_script: () -> String
20
+ # private def one_hop_neighbors: (IR::Automaton automaton, Integer state_id) -> Array[Integer]
21
+ # private def self.one_hop_neighbors: (IR::Automaton automaton, Integer state_id) -> Array[Integer]
14
22
  # private def item_html: (IR::AutomatonItem item, IR::Grammar grammar, Hash[Integer, String] labels) -> String
15
23
  # private def self.item_html: (IR::AutomatonItem item, IR::Grammar grammar,
16
24
  # Hash[Integer, String] labels) -> String
17
25
  # private def rule_sections: (IR::Grammar grammar, Hash[Integer, String] labels) -> String
18
26
  # private def self.rule_sections: (IR::Grammar grammar, Hash[Integer, String] labels) -> String
19
- # private def conflict_sections: (IR::Automaton automaton) -> String
20
- # private def self.conflict_sections: (IR::Automaton automaton) -> String
27
+ # private def conflict_sections: (IR::Automaton automaton, IR::Grammar grammar,
28
+ # Hash[Integer, String] labels) -> String
29
+ # private def self.conflict_sections: (IR::Automaton automaton, IR::Grammar grammar,
30
+ # Hash[Integer, String] labels) -> String
21
31
  # private def escape: (String value) -> String
22
32
  # private def self.escape: (String value) -> String
23
33
  # private def symbol_name: (Hash[Integer, String] labels, Integer id) -> String
@@ -30,12 +40,14 @@ module Ibex
30
40
  <<~HTML
31
41
  <!doctype html>
32
42
  <html lang="en"><head><meta charset="utf-8"><title>Ibex automaton</title>
33
- <style>body{font:14px system-ui;margin:2rem;max-width:90rem}code{white-space:pre-wrap}.state{border:1px solid #bbb;padding:1rem;margin:1rem 0}.conflict{color:#a00}a{color:#075985}</style>
43
+ #{styles}
34
44
  </head><body><h1>Ibex #{escape(automaton.algorithm)} automaton</h1>
35
45
  <nav><a href="#rules">Rules</a> · <a href="#conflicts">Conflicts</a></nav>
36
- #{state_sections(automaton, grammar, labels)}
46
+ #{controls(automaton)}
47
+ <main id="states">#{state_sections(automaton, grammar, labels)}</main>
37
48
  <h2 id="rules">Rules</h2>#{rule_sections(grammar, labels)}
38
- <h2 id="conflicts">Conflicts</h2>#{conflict_sections(automaton)}
49
+ <h2 id="conflicts">Conflicts</h2>#{conflict_sections(automaton, grammar, labels)}
50
+ #{interaction_script}
39
51
  </body></html>
40
52
  HTML
41
53
  end
@@ -44,6 +56,62 @@ module Ibex
44
56
  # @rbs skip
45
57
  private
46
58
 
59
+ # @rbs skip
60
+ def styles
61
+ <<~HTML
62
+ <style>
63
+ body{font:14px system-ui;margin:2rem;max-width:90rem}code{white-space:pre-wrap}
64
+ .controls{align-items:center;display:flex;flex-wrap:wrap;gap:1rem;margin:1rem 0}
65
+ .state{border:1px solid #bbb;padding:1rem;margin:1rem 0}.state[hidden]{display:none}
66
+ .conflict-state{background:#fff7f7;border:2px solid #b91c1c}.conflict{color:#a00}a{color:#075985}
67
+ </style>
68
+ HTML
69
+ end
70
+
71
+ # @rbs skip
72
+ def interaction_script
73
+ <<~HTML
74
+ <script>
75
+ (() => {
76
+ const search = document.getElementById("state-search");
77
+ const conflictOnly = document.getElementById("conflict-only");
78
+ const neighborhood = document.getElementById("conflict-neighborhood");
79
+ const states = Array.from(document.querySelectorAll(".state"));
80
+ const update = () => {
81
+ const query = search.value.trim().toLowerCase();
82
+ const focus = neighborhood.value;
83
+ states.forEach((state) => {
84
+ const matchesText = !query || state.textContent.toLowerCase().includes(query);
85
+ const matchesConflict = !conflictOnly.checked || state.classList.contains("conflict-state");
86
+ const nearby = !focus || state.dataset.neighbors.split(" ").includes(focus);
87
+ state.hidden = !(matchesText && matchesConflict && nearby);
88
+ });
89
+ };
90
+ search.addEventListener("input", update);
91
+ conflictOnly.addEventListener("change", update);
92
+ neighborhood.addEventListener("change", update);
93
+ })();
94
+ </script>
95
+ HTML
96
+ end
97
+
98
+ # @rbs skip
99
+ def controls(automaton)
100
+ options = automaton.states.filter_map do |state|
101
+ next if state.conflicts.empty?
102
+
103
+ %(<option value="#{state.id}">State #{state.id}</option>)
104
+ end.join
105
+ <<~HTML
106
+ <div class="controls">
107
+ <label>Search states <input id="state-search" type="search" aria-controls="states"></label>
108
+ <label><input id="conflict-only" type="checkbox"> Conflicts only</label>
109
+ <label>Conflict neighborhood
110
+ <select id="conflict-neighborhood"><option value="">All states</option>#{options}</select></label>
111
+ </div>
112
+ HTML
113
+ end
114
+
47
115
  # @rbs skip
48
116
  def state_sections(automaton, grammar, labels)
49
117
  automaton.states.map do |state|
@@ -52,13 +120,25 @@ module Ibex
52
120
  "<li>#{symbol} → <a href=\"#state-#{target}\">state #{target}</a></li>"
53
121
  end.join
54
122
  items = state.items.map { |item| item_html(item, grammar, labels) }.join
123
+ class_name = state.conflicts.empty? ? "state" : "state conflict-state"
124
+ neighbors = one_hop_neighbors(automaton, state.id).join(" ")
55
125
  <<~HTML
56
- <section class="state" id="state-#{state.id}"><h2>State #{state.id}</h2>
126
+ <section class="#{class_name}" id="state-#{state.id}" data-state-id="#{state.id}" data-neighbors="#{neighbors}">
127
+ <h2>State #{state.id}</h2>
57
128
  <ul>#{transitions}</ul><ul>#{items}</ul></section>
58
129
  HTML
59
130
  end.join
60
131
  end
61
132
 
133
+ # @rbs skip
134
+ def one_hop_neighbors(automaton, state_id)
135
+ outgoing = automaton.states.fetch(state_id).transitions.values
136
+ incoming = automaton.states.filter_map do |state|
137
+ state.id if state.transitions.value?(state_id)
138
+ end
139
+ ([state_id] + outgoing + incoming).uniq.sort
140
+ end
141
+
62
142
  # @rbs skip
63
143
  def item_html(item, grammar, labels)
64
144
  return "<li><code>$accept</code></li>" if item.production.negative?
@@ -81,11 +161,13 @@ module Ibex
81
161
  end
82
162
 
83
163
  # @rbs skip
84
- def conflict_sections(automaton)
164
+ def conflict_sections(automaton, grammar, labels)
85
165
  conflicts = automaton.states.flat_map do |state|
86
166
  state.conflicts.map do |conflict|
87
167
  link = "<a href=\"#state-#{state.id}\">state #{state.id}</a>"
88
- "<li class=\"conflict\">#{link}: #{escape(conflict.inspect)}</li>"
168
+ symbol = grammar.symbol(conflict[:symbol])
169
+ displayed = symbol ? conflict.merge(symbol: symbol_name(labels, symbol.id)) : conflict
170
+ "<li class=\"conflict\">#{link}: #{escape(displayed.inspect)}</li>"
89
171
  end
90
172
  end
91
173
  conflicts.empty? ? "<p>None</p>" : "<ul>#{conflicts.join}</ul>"
@@ -100,10 +182,12 @@ module Ibex
100
182
  def symbol_name(labels, id)
101
183
  labels.fetch(id) { raise Ibex::Error, "missing grammar symbol id #{id}" }
102
184
  end
103
- module_function :state_sections, :item_html, :rule_sections, :conflict_sections, :escape, :symbol_name
185
+ module_function :styles, :interaction_script, :controls, :state_sections, :one_hop_neighbors
186
+ module_function :item_html, :rule_sections, :conflict_sections, :escape, :symbol_name
104
187
 
105
188
  class << self
106
- private :state_sections, :item_html, :rule_sections, :conflict_sections, :escape, :symbol_name
189
+ private :styles, :interaction_script, :controls, :state_sections, :one_hop_neighbors
190
+ private :item_html, :rule_sections, :conflict_sections, :escape, :symbol_name
107
191
  end
108
192
  end
109
193
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "symbol_labels"
4
+
5
+ module Ibex
6
+ module Codegen
7
+ # Renders an Automaton IR graph as a GitHub-compatible Mermaid flowchart.
8
+ module Mermaid
9
+ # @rbs (IR::Automaton automaton) -> String
10
+ def render(automaton)
11
+ labels = SymbolLabels.build(automaton.grammar)
12
+ lines = ["flowchart LR"]
13
+ automaton.states.each do |state|
14
+ lines << %( state_#{state.id}["State #{state.id}"])
15
+ state.transitions.each do |symbol_id, target|
16
+ label = escape(labels.fetch(symbol_id) { raise Ibex::Error, "missing grammar symbol id #{symbol_id}" })
17
+ lines << " state_#{state.id} -->|#{label}| state_#{target}"
18
+ end
19
+ end
20
+ conflicting = automaton.states.reject { |state| state.conflicts.empty? }
21
+ unless conflicting.empty?
22
+ lines << " classDef conflict fill:#fee2e2,stroke:#b91c1c,stroke-width:2px"
23
+ conflicting.each { |state| lines << " class state_#{state.id} conflict;" }
24
+ end
25
+ "#{lines.join("\n")}\n"
26
+ end
27
+ module_function :render
28
+
29
+ private
30
+
31
+ # @rbs (String value) -> String
32
+ def escape(value)
33
+ value.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;").gsub("|", "&#124;")
34
+ .gsub('"', "&quot;").gsub(/\s+/, " ")
35
+ end
36
+ module_function :escape
37
+
38
+ class << self
39
+ private :escape
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "symbol_labels"
4
+ require_relative "railroad_documentation"
5
+
6
+ module Ibex
7
+ module Codegen
8
+ # Renders normalized Grammar IR as a self-contained SVG railroad diagram.
9
+ module Railroad
10
+ CHAR_WIDTH = 8 #: Integer
11
+ BOX_HEIGHT = 28 #: Integer
12
+ BOX_PADDING = 20 #: Integer
13
+ TRACK_GAP = 18 #: Integer
14
+ ROW_HEIGHT = 46 #: Integer
15
+ SECTION_HEADER = 30 #: Integer
16
+ SECTION_GAP = 18 #: Integer
17
+ PAGE_PADDING = 20 #: Integer
18
+ TITLE_HEIGHT = 48 #: Integer
19
+ EPSILON_WIDTH = 34 #: Integer
20
+ START_RADIUS = 4 #: Integer
21
+ END_RADIUS = 6 #: Integer
22
+ MIN_RAIL_X = 180 #: Integer
23
+ # @rbs STYLE: String
24
+ STYLE = <<~CSS
25
+ text { fill: #172033; font: 14px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
26
+ .diagram-title { font-size: 18px; font-weight: 700; }
27
+ .rule-name { font-weight: 700; }
28
+ .rule-documentation { fill: #4b5565; font: 12px system-ui, sans-serif; }
29
+ .production-id { fill: #697386; font-size: 11px; text-anchor: end; }
30
+ .track { fill: none; stroke: #697386; stroke-width: 2; }
31
+ .start { fill: #172033; }
32
+ .end { fill: #fff; stroke: #172033; stroke-width: 2; }
33
+ .end-dot { fill: #172033; }
34
+ .symbol { stroke: #172033; stroke-width: 1.5; }
35
+ .terminal { fill: #fff3df; }
36
+ .nonterminal { fill: #e8f1ff; }
37
+ .symbol-label { text-anchor: middle; }
38
+ .epsilon { fill: #697386; font-style: italic; text-anchor: middle; }
39
+ CSS
40
+
41
+ extend RailroadDocumentation
42
+
43
+ class << self
44
+ # @rbs (IR::Grammar grammar) -> String
45
+ def render(grammar)
46
+ labels = SymbolLabels.build(grammar)
47
+ groups = production_groups(grammar)
48
+ rail_x = [MIN_RAIL_X, groups.map { |symbol, _| label_width(labels.fetch(symbol.id)) }.max.to_i + 44].max
49
+ width = document_width(grammar, groups, labels, rail_x)
50
+ height = document_height(groups)
51
+ lines = document_start(grammar, width, height)
52
+ append_sections(lines, grammar, groups, labels, rail_x)
53
+ lines << "</svg>"
54
+ "#{lines.join("\n")}\n"
55
+ end
56
+
57
+ private
58
+
59
+ # @rbs (IR::Grammar grammar) -> Array[[IR::GrammarSymbol, Array[IR::Production]]]
60
+ def production_groups(grammar)
61
+ grouped = grammar.productions.group_by(&:lhs)
62
+ grammar.nonterminals.map { |symbol| [symbol, grouped.fetch(symbol.id, [])] }
63
+ end
64
+
65
+ # @rbs (IR::Grammar grammar, Array[[IR::GrammarSymbol, Array[IR::Production]]] groups,
66
+ # Hash[Integer, String] labels, Integer rail_x) -> Integer
67
+ def document_width(grammar, groups, labels, rail_x)
68
+ rows = groups.flat_map(&:last)
69
+ widest = rows.map { |production| row_end_x(grammar, production, labels, rail_x) }.max
70
+ documentation_width = groups.flat_map { |symbol, _| documentation_lines(symbol.documentation) }
71
+ .map { |line| label_width(line) + (PAGE_PADDING * 2) }.max
72
+ [widest.to_i + PAGE_PADDING, documentation_width.to_i,
73
+ label_width("#{grammar.class_name} grammar") + (PAGE_PADDING * 2), 360].max
74
+ end
75
+
76
+ # @rbs (Array[[IR::GrammarSymbol, Array[IR::Production]]] groups) -> Integer
77
+ def document_height(groups)
78
+ sections = groups.sum do |symbol, productions|
79
+ section_header_height(symbol) + ([productions.length, 1].max * ROW_HEIGHT)
80
+ end
81
+ TITLE_HEIGHT + sections + (SECTION_GAP * [groups.length - 1, 0].max) + PAGE_PADDING
82
+ end
83
+
84
+ # @rbs (IR::Grammar grammar, Integer width, Integer height) -> Array[String]
85
+ def document_start(grammar, width, height)
86
+ title = escape(grammar.class_name)
87
+ [
88
+ %(<?xml version="1.0" encoding="UTF-8"?>),
89
+ %(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 #{width} #{height}" width="#{width}" ),
90
+ %( height="#{height}" role="img" aria-labelledby="railroad-title">),
91
+ " <title id=\"railroad-title\">Railroad diagram for #{title}</title>",
92
+ " <style>",
93
+ STYLE.lines.map { |line| " #{line.rstrip}" }.join("\n"),
94
+ " </style>",
95
+ " <text class=\"diagram-title\" x=\"#{PAGE_PADDING}\" y=\"30\">#{title} grammar</text>"
96
+ ]
97
+ end
98
+
99
+ # @rbs (Array[String] lines, IR::Grammar grammar,
100
+ # Array[[IR::GrammarSymbol, Array[IR::Production]]] groups,
101
+ # Hash[Integer, String] labels, Integer rail_x) -> void
102
+ def append_sections(lines, grammar, groups, labels, rail_x)
103
+ y = TITLE_HEIGHT
104
+ groups.each do |symbol, productions|
105
+ lines << %( <g id="nonterminal-#{escape(symbol.id)}" class="rule" transform="translate(0 #{y})">)
106
+ lines << %( <text class="rule-name" x="#{PAGE_PADDING}" y="20">#{escape(labels.fetch(symbol.id))}</text>)
107
+ append_rule_documentation(lines, symbol)
108
+ header_height = section_header_height(symbol)
109
+ append_productions(lines, grammar, productions, labels, rail_x, header_height)
110
+ lines << " </g>"
111
+ y += header_height + ([productions.length, 1].max * ROW_HEIGHT) + SECTION_GAP
112
+ end
113
+ end
114
+
115
+ # @rbs (Array[String] lines, IR::Grammar grammar, Array[IR::Production] productions,
116
+ # Hash[Integer, String] labels, Integer rail_x, Integer header_height) -> void
117
+ def append_productions(lines, grammar, productions, labels, rail_x, header_height)
118
+ if productions.empty?
119
+ lines << %( <text class="epsilon" x="#{rail_x}" y="#{header_height + 18}">∅</text>)
120
+ return
121
+ end
122
+ productions.each_with_index do |production, index|
123
+ y = header_height + (index * ROW_HEIGHT) + (BOX_HEIGHT / 2)
124
+ lines.concat(production_lines(grammar, production, labels, rail_x, y))
125
+ end
126
+ end
127
+
128
+ # @rbs (IR::Grammar grammar, IR::Production production, Hash[Integer, String] labels,
129
+ # Integer rail_x, Integer offset_y) -> Array[String]
130
+ def production_lines(grammar, production, labels, rail_x, offset_y)
131
+ lines = [
132
+ %(<g class="production" data-production="#{escape(production.id)}" transform="translate(0 #{offset_y})">)
133
+ ]
134
+ lines << %(<text class="production-id" x="#{rail_x - 14}" y="4">p#{escape(production.id)}</text>)
135
+ lines << %(<circle class="start" cx="#{rail_x}" cy="0" r="#{START_RADIUS}"/>)
136
+ cursor = rail_x + START_RADIUS
137
+ lines << track(cursor, cursor + TRACK_GAP)
138
+ cursor += TRACK_GAP
139
+ cursor = append_rhs(lines, grammar, production, labels, cursor)
140
+ end_x = cursor + TRACK_GAP + END_RADIUS
141
+ lines << track(cursor, end_x - END_RADIUS)
142
+ lines << %(<circle class="end" cx="#{end_x}" cy="0" r="#{END_RADIUS}"/>)
143
+ lines << %(<circle class="end-dot" cx="#{end_x}" cy="0" r="2"/>)
144
+ lines << "</g>"
145
+ lines.map { |line| " #{line}" }
146
+ end
147
+
148
+ # @rbs (Array[String] lines, IR::Grammar grammar, IR::Production production,
149
+ # Hash[Integer, String] labels, Integer cursor) -> Integer
150
+ def append_rhs(lines, grammar, production, labels, cursor)
151
+ return append_epsilon(lines, cursor) if production.rhs.empty?
152
+
153
+ production.rhs.each_with_index do |symbol_id, index|
154
+ symbol = grammar.symbol_by_id(symbol_id) || raise(Ibex::Error, "missing grammar symbol id #{symbol_id}")
155
+ width = label_width(labels.fetch(symbol_id))
156
+ kind = symbol.terminal? ? "terminal" : "nonterminal"
157
+ lines << "<rect class=\"symbol #{kind}\" x=\"#{cursor}\" y=\"#{-(BOX_HEIGHT / 2)}\" " \
158
+ "width=\"#{width}\" height=\"#{BOX_HEIGHT}\" rx=\"5\"/>"
159
+ lines << "<text class=\"symbol-label\" x=\"#{cursor + (width / 2)}\" y=\"5\">" \
160
+ "#{escape(labels.fetch(symbol_id))}</text>"
161
+ cursor += width
162
+ next if index == production.rhs.length - 1
163
+
164
+ lines << track(cursor, cursor + TRACK_GAP)
165
+ cursor += TRACK_GAP
166
+ end
167
+ cursor
168
+ end
169
+
170
+ # @rbs (Array[String] lines, Integer cursor) -> Integer
171
+ def append_epsilon(lines, cursor)
172
+ lines << track(cursor, cursor + EPSILON_WIDTH)
173
+ lines << %(<text class="epsilon" x="#{cursor + (EPSILON_WIDTH / 2)}" y="-6">ε</text>)
174
+ cursor + EPSILON_WIDTH
175
+ end
176
+
177
+ # @rbs (IR::Grammar grammar, IR::Production production, Hash[Integer, String] labels,
178
+ # Integer rail_x) -> Integer
179
+ def row_end_x(grammar, production, labels, rail_x)
180
+ widths = production.rhs.map do |symbol_id|
181
+ raise Ibex::Error, "missing grammar symbol id #{symbol_id}" unless grammar.symbol_by_id(symbol_id)
182
+
183
+ label_width(labels.fetch(symbol_id))
184
+ end
185
+ content = widths.empty? ? EPSILON_WIDTH : widths.sum + (TRACK_GAP * [widths.length - 1, 0].max)
186
+ rail_x + START_RADIUS + TRACK_GAP + content + TRACK_GAP + (END_RADIUS * 2)
187
+ end
188
+
189
+ # @rbs (String value) -> Integer
190
+ def label_width(value)
191
+ glyph_width = value.each_char.sum { |character| character.ascii_only? ? CHAR_WIDTH : CHAR_WIDTH * 2 }
192
+ [glyph_width + BOX_PADDING, 38].max
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Codegen
5
+ # Documentation layout helpers mixed into the railroad renderer singleton.
6
+ module RailroadDocumentation
7
+ DOCUMENTATION_COLUMNS = 72 #: Integer
8
+ DOCUMENTATION_LINE_HEIGHT = 18 #: Integer
9
+ DOCUMENTATION_GAP = 8 #: Integer
10
+
11
+ private
12
+
13
+ # @rbs (Array[String] lines, IR::GrammarSymbol symbol) -> void
14
+ def append_rule_documentation(lines, symbol)
15
+ # @type self: singleton(Railroad)
16
+ documentation = symbol.documentation
17
+ return unless documentation
18
+
19
+ lines << " <desc>#{escape(documentation)}</desc>"
20
+ documentation_lines(documentation).each_with_index do |line, index|
21
+ y = Railroad::SECTION_HEADER + DOCUMENTATION_GAP + (index * DOCUMENTATION_LINE_HEIGHT)
22
+ lines << %( <text class="rule-documentation" x="#{Railroad::PAGE_PADDING}" y="#{y}">#{escape(line)}</text>)
23
+ end
24
+ end
25
+
26
+ # @rbs (IR::GrammarSymbol symbol) -> Integer
27
+ def section_header_height(symbol)
28
+ # @type self: singleton(Railroad)
29
+ lines = documentation_lines(symbol.documentation)
30
+ return Railroad::SECTION_HEADER if lines.empty?
31
+
32
+ Railroad::SECTION_HEADER + DOCUMENTATION_GAP + (lines.length * DOCUMENTATION_LINE_HEIGHT)
33
+ end
34
+
35
+ # @rbs (String? documentation) -> Array[String]
36
+ def documentation_lines(documentation)
37
+ return [] unless documentation
38
+
39
+ documentation.split("\n", -1).flat_map do |line|
40
+ characters = line.each_char.to_a
41
+ characters.empty? ? [""] : characters.each_slice(DOCUMENTATION_COLUMNS).map(&:join)
42
+ end
43
+ end
44
+
45
+ # @rbs (String | Integer value) -> String
46
+ def escape(value)
47
+ xml = value.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
48
+ .gsub(/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u, "\uFFFD")
49
+ xml.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
50
+ .gsub('"', "&quot;").gsub("'", "&apos;")
51
+ end
52
+
53
+ # @rbs (Integer from, Integer to) -> String
54
+ def track(from, to)
55
+ %(<line class="track" x1="#{from}" y1="0" x2="#{to}" y2="0"/>)
56
+ end
57
+ end
58
+ end
59
+ end