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,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # Attaches immediately preceding lossless line-comment blocks to parsed rules.
6
+ class RuleDocumentation
7
+ EMPTY_SEGMENTS = Array.new(0).freeze #: Array[Segment]
8
+ private_constant :EMPTY_SEGMENTS
9
+
10
+ # @rbs (AST::Root | AST::Fragment node, SourceDocument document) -> (AST::Root | AST::Fragment)
11
+ def self.enrich(node, document)
12
+ new(document).enrich(node)
13
+ end
14
+
15
+ # @rbs (SourceDocument document) -> void
16
+ def initialize(document)
17
+ @segments_by_line = index_segments(document) #: Hash[Integer, Array[Segment]]
18
+ @inline_lines = document.tokens.filter_map do |token|
19
+ token.location.line if token.type == :inline
20
+ end #: Array[Integer]
21
+ end
22
+
23
+ # @rbs (AST::Root | AST::Fragment node) -> (AST::Root | AST::Fragment)
24
+ def enrich(node)
25
+ rules = node.rules.map { |rule| documented_rule(rule) }
26
+ if node.is_a?(AST::Root)
27
+ AST::Root.new(
28
+ class_name: node.class_name, superclass: node.superclass, declarations: node.declarations,
29
+ rules: rules, user_code: node.user_code, loc: node.loc, extended: node.extended, cst: node.cst,
30
+ extended_loc: node.extended_loc
31
+ )
32
+ else
33
+ AST::Fragment.new(declarations: node.declarations, rules: rules, loc: node.loc)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ # @rbs (AST::Rule rule) -> AST::Rule
40
+ def documented_rule(rule)
41
+ AST::Rule.new(
42
+ lhs: rule.lhs, parameters: rule.parameters, alternatives: rule.alternatives, loc: rule.loc,
43
+ documentation: documentation_before(documentation_anchor(rule)), inline: rule.inline
44
+ )
45
+ end
46
+
47
+ # @rbs (AST::Rule rule) -> Integer
48
+ def documentation_anchor(rule)
49
+ return rule.loc.line unless rule.inline
50
+
51
+ @inline_lines.reverse_each.find { |line| line <= rule.loc.line } || rule.loc.line
52
+ end
53
+
54
+ # @rbs (Integer rule_line) -> String?
55
+ def documentation_before(rule_line)
56
+ lines = [] #: Array[String]
57
+ line = rule_line - 1
58
+ while line.positive?
59
+ content = documentation_line(line)
60
+ break unless content
61
+
62
+ lines.unshift(content)
63
+ line -= 1
64
+ end
65
+ lines.empty? ? nil : lines.join("\n").freeze
66
+ end
67
+
68
+ # @rbs (Integer line) -> String?
69
+ def documentation_line(line)
70
+ segments = @segments_by_line.fetch(line, EMPTY_SEGMENTS)
71
+ comments = segments.select { |segment| segment.kind == :line_comment }
72
+ return unless comments.length == 1
73
+
74
+ comment = comments.fetch(0)
75
+ return unless comment.text.start_with?("##")
76
+ return unless segments.all? { |segment| documentation_line_segment?(segment, comment) }
77
+
78
+ comment.text.delete_prefix("##").delete_prefix(" ")
79
+ end
80
+
81
+ # @rbs (Segment segment, Segment comment) -> bool
82
+ def documentation_line_segment?(segment, comment)
83
+ segment.equal?(comment) || %i[whitespace newline].include?(segment.kind)
84
+ end
85
+
86
+ # @rbs (SourceDocument document) -> Hash[Integer, Array[Segment]]
87
+ def index_segments(document)
88
+ indexed = Hash.new { |hash, line| hash[line] = [] } #: Hash[Integer, Array[Segment]]
89
+ document.cst.each do |segment|
90
+ covered_lines(segment).each { |line| indexed[line] << segment }
91
+ end
92
+ indexed
93
+ end
94
+
95
+ # @rbs (Segment segment) -> Array[Integer]
96
+ def covered_lines(segment)
97
+ first = segment.span.start.line
98
+ return [first] if segment.kind == :newline
99
+
100
+ (first..segment.span.finish.line).to_a
101
+ end
102
+ end
103
+ end
104
+ end
@@ -27,11 +27,12 @@ module Ibex
27
27
  :type, #: Symbol
28
28
  :value, #: token_value
29
29
  :location, #: Location
30
+ :span, #: SourceSpan?
30
31
  keyword_init: true
31
32
  )
32
33
 
33
34
  class Token
34
- # @rbs () -> Hash[Symbol, untyped]
35
+ # @rbs () -> Hash[Symbol, Object?]
35
36
  def to_h
36
37
  { type: type, value: value, location: location.to_h }
37
38
  end
@@ -42,31 +43,53 @@ module Ibex
42
43
  attr_reader :source #: String
43
44
  attr_reader :file #: String
44
45
  attr_reader :index #: Integer
46
+ attr_reader :byte_offset #: Integer
45
47
  attr_reader :line #: Integer
46
48
  attr_reader :column #: Integer
47
49
 
48
50
  # @rbs (String source, String file) -> void
49
51
  def initialize(source, file)
50
- @source = source
51
- @file = file
52
+ @source = SourceEncoding.validated_utf8(source, file)
53
+ @file = SourceEncoding.validated_file(file)
54
+ @ascii_only = @source.ascii_only?
55
+ @character_length = @ascii_only ? @source.bytesize : @source.length
52
56
  @index = 0
57
+ @byte_offset = 0
53
58
  @line = 1
54
59
  @column = 1
55
60
  end
56
61
 
57
62
  # @rbs () -> bool
58
63
  def eof?
59
- @index >= @source.length
64
+ @index >= @character_length
60
65
  end
61
66
 
62
67
  # @rbs (?Integer offset) -> String?
63
68
  def peek(offset = 0)
64
- @source[@index + offset]
69
+ character_index = @index + offset
70
+ if @ascii_only
71
+ return if character_index >= @character_length
72
+
73
+ return @source.byteslice(character_index, 1)
74
+ end
75
+
76
+ character_index += @character_length if character_index.negative?
77
+ return if character_index.negative? || character_index >= @character_length
78
+
79
+ start_byte = if character_index == @index
80
+ @byte_offset
81
+ elsif character_index == @index + 1
82
+ @byte_offset + utf8_character_width(@byte_offset)
83
+ else
84
+ byte_offset_for_character(character_index)
85
+ end
86
+ @source.byteslice(start_byte, utf8_character_width(start_byte))
65
87
  end
66
88
 
67
89
  # @rbs () -> String
68
90
  def rest
69
- @source[@index..] || ""
91
+ @source.byteslice(@byte_offset, @source.bytesize - @byte_offset) ||
92
+ String.new(encoding: Encoding::UTF_8)
70
93
  end
71
94
 
72
95
  # @rbs () -> Location
@@ -74,14 +97,34 @@ module Ibex
74
97
  Location.new(file: @file, line: @line, column: @column)
75
98
  end
76
99
 
100
+ # @rbs () -> SourcePosition
101
+ def position
102
+ SourcePosition.new(byte_offset: @byte_offset, line: @line, column: @column)
103
+ end
104
+
105
+ # @rbs (SourcePosition start) -> SourceSpan
106
+ def span_from(start)
107
+ SourceSpan.new(file: @file, start: start, finish: position)
108
+ end
109
+
77
110
  # @rbs (?Integer count) -> void
78
111
  def advance(count = 1)
112
+ return advance_utf8(count) unless @ascii_only
113
+
114
+ advance_ascii(count)
115
+ end
116
+
117
+ private
118
+
119
+ # @rbs (Integer count) -> void
120
+ def advance_ascii(count)
79
121
  count.times do
80
- character = @source[@index]
81
- break unless character
122
+ break if @index >= @character_length
82
123
 
124
+ newline = @source.getbyte(@index) == 10
83
125
  @index += 1
84
- if character == "\n"
126
+ @byte_offset = @index
127
+ if newline
85
128
  @line += 1
86
129
  @column = 1
87
130
  else
@@ -89,6 +132,87 @@ module Ibex
89
132
  end
90
133
  end
91
134
  end
135
+
136
+ # @rbs (Integer count) -> void
137
+ def advance_utf8(count)
138
+ count.times do
139
+ break if @index >= @character_length
140
+
141
+ leading_byte = @source.getbyte(@byte_offset)
142
+ raise Ibex::Error, "#{@file}: input must be valid UTF-8" unless leading_byte
143
+
144
+ @index += 1
145
+ @byte_offset += if leading_byte < 0x80
146
+ 1
147
+ elsif leading_byte < 0xE0
148
+ 2
149
+ elsif leading_byte < 0xF0
150
+ 3
151
+ else
152
+ 4
153
+ end
154
+ if leading_byte == 10
155
+ @line += 1
156
+ @column = 1
157
+ else
158
+ @column += 1
159
+ end
160
+ end
161
+ end
162
+
163
+ # @rbs (Integer character_index) -> Integer
164
+ def byte_offset_for_character(character_index)
165
+ start_distance = character_index
166
+ current_distance = (character_index - @index).abs
167
+ finish_distance = @character_length - character_index
168
+
169
+ if current_distance <= start_distance && current_distance <= finish_distance
170
+ walk_to_character(@byte_offset, @index, character_index)
171
+ elsif start_distance <= finish_distance
172
+ walk_to_character(0, 0, character_index)
173
+ else
174
+ walk_to_character(@source.bytesize, @character_length, character_index)
175
+ end
176
+ end
177
+
178
+ # @rbs (Integer byte_offset, Integer from_index, Integer to_index) -> Integer
179
+ def walk_to_character(byte_offset, from_index, to_index)
180
+ while from_index < to_index
181
+ byte_offset += utf8_character_width(byte_offset)
182
+ from_index += 1
183
+ end
184
+ while from_index > to_index
185
+ byte_offset = previous_character_byte_offset(byte_offset)
186
+ from_index -= 1
187
+ end
188
+ byte_offset
189
+ end
190
+
191
+ # @rbs (Integer byte_offset) -> Integer
192
+ def previous_character_byte_offset(byte_offset)
193
+ byte_offset -= 1
194
+ while (byte = @source.getbyte(byte_offset)) && continuation_byte?(byte)
195
+ byte_offset -= 1
196
+ end
197
+ byte_offset
198
+ end
199
+
200
+ # @rbs (Integer byte_offset) -> Integer
201
+ def utf8_character_width(byte_offset)
202
+ leading_byte = @source.getbyte(byte_offset)
203
+ raise Ibex::Error, "#{@file}: input must be valid UTF-8" unless leading_byte
204
+
205
+ return 1 if leading_byte < 0x80
206
+ return 2 if leading_byte < 0xE0
207
+ return 3 if leading_byte < 0xF0
208
+
209
+ 4
210
+ end
211
+
212
+ # @rbs (Integer byte) -> bool
213
+ def continuation_byte?(byte)
214
+ byte >= 0x80 && byte < 0xC0
215
+ end
92
216
  end
93
217
  end
94
218
  end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module Frontend
5
+ # An immutable lexical unit or trivia slice in a concrete syntax tree.
6
+ class Segment
7
+ TRIVIA_KINDS = %i[whitespace newline line_comment block_comment].freeze #: Array[Symbol]
8
+ OPAQUE_KINDS = %i[action user_code_body].freeze #: Array[Symbol]
9
+
10
+ attr_reader :kind #: Symbol
11
+ attr_reader :span #: SourceSpan
12
+ attr_reader :text #: String
13
+ attr_reader :token_type #: Symbol?
14
+ attr_reader :token_index #: Integer?
15
+
16
+ # @rbs (kind: Symbol, span: SourceSpan, text: String, ?token_type: Symbol?,
17
+ # ?token_index: Integer?) -> void
18
+ def initialize(kind:, span:, text:, token_type: nil, token_index: nil)
19
+ raise ArgumentError, "segment text does not match its span" unless text.bytesize == span.length
20
+
21
+ @kind = kind
22
+ @span = span
23
+ @text = text.dup.freeze
24
+ @token_type = token_type
25
+ @token_index = token_index
26
+ freeze
27
+ end
28
+
29
+ # @rbs () -> bool
30
+ def trivia?
31
+ TRIVIA_KINDS.include?(kind)
32
+ end
33
+
34
+ # @rbs () -> bool
35
+ def opaque?
36
+ OPAQUE_KINDS.include?(kind)
37
+ end
38
+
39
+ # @rbs () -> bool
40
+ def token?
41
+ !token_index.nil?
42
+ end
43
+
44
+ # @rbs () -> String
45
+ def render
46
+ text
47
+ end
48
+ end
49
+
50
+ module CST
51
+ # The immutable lexical root of a grammar source document.
52
+ class Document
53
+ # @rbs! include Enumerable[Segment]
54
+ # @rbs skip
55
+ include Enumerable
56
+
57
+ attr_reader :segments #: Array[Segment]
58
+
59
+ # @rbs (Array[Segment] segments) -> void
60
+ def initialize(segments)
61
+ validate_contiguous(segments)
62
+ @segments = segments.dup.freeze
63
+ freeze
64
+ end
65
+
66
+ # @rbs!
67
+ # def each: () -> Enumerator[Segment, Document]
68
+ # | () { (Segment) -> void } -> Document
69
+ # @rbs skip
70
+ def each(&block)
71
+ return enum_for(:each) unless block
72
+
73
+ segments.each(&block)
74
+ self
75
+ end
76
+
77
+ # @rbs () -> String
78
+ def render
79
+ segments.map(&:render).join
80
+ end
81
+
82
+ private
83
+
84
+ # @rbs (Array[Segment] segments) -> void
85
+ def validate_contiguous(segments)
86
+ segments.each_cons(2) do |left, right|
87
+ next unless left && right
88
+ next if left.span.end_byte == right.span.start_byte
89
+
90
+ raise ArgumentError, "concrete syntax tree segments must be contiguous"
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # Lossless grammar source plus its semantic parse and lexical concrete syntax tree.
97
+ class SourceDocument
98
+ attr_reader :source #: String
99
+ attr_reader :file #: String
100
+ attr_reader :tokens #: Array[Token]
101
+ attr_reader :cst #: CST::Document
102
+ attr_reader :ast #: AST::Root | AST::Fragment | nil
103
+
104
+ # @rbs (source: String, file: String, tokens: Array[Token], cst: CST::Document,
105
+ # ?ast: AST::Root | AST::Fragment | nil) -> void
106
+ def initialize(source:, file:, tokens:, cst:, ast: nil)
107
+ @source = SourceEncoding.validated_utf8(source, file)
108
+ @file = file.dup.freeze
109
+ @tokens = tokens.dup.freeze
110
+ @cst = cst
111
+ @ast = ast
112
+ @line_starts = build_line_starts.freeze #: Array[Integer]
113
+ validate_render
114
+ freeze
115
+ end
116
+
117
+ # @rbs () -> String
118
+ def render
119
+ cst.render
120
+ end
121
+
122
+ # @rbs (SourceSpan span) -> String
123
+ def slice(span)
124
+ validate_span(span)
125
+ source.byteslice(span.start_byte, span.length) || ""
126
+ end
127
+
128
+ # @rbs (Integer byte_offset) -> SourcePosition
129
+ def position_at(byte_offset)
130
+ validate_byte_offset(byte_offset)
131
+ prefix = source.byteslice(0, byte_offset) || ""
132
+ raise ArgumentError, "byte offset must be on a UTF-8 character boundary" unless prefix.valid_encoding?
133
+
134
+ line_index = (@line_starts.bsearch_index { |start| start > byte_offset } || @line_starts.length) - 1
135
+ line_start = @line_starts.fetch(line_index)
136
+ line_prefix = source.byteslice(line_start, byte_offset - line_start) || ""
137
+ SourcePosition.new(byte_offset: byte_offset, line: line_index + 1, column: line_prefix.length + 1)
138
+ end
139
+
140
+ # @rbs (Integer line, Integer column) -> Integer
141
+ def byte_offset_at(line, column)
142
+ raise ArgumentError, "line and column must be positive" unless line.positive? && column.positive?
143
+
144
+ line_start = @line_starts[line - 1]
145
+ raise ArgumentError, "line is outside the source" unless line_start
146
+
147
+ line_end = @line_starts[line] || source.bytesize
148
+ line_text = source.byteslice(line_start, line_end - line_start) || ""
149
+ line_text = line_text.delete_suffix("\n")
150
+ character_count = column - 1
151
+ raise ArgumentError, "column is outside the source line" if character_count > line_text.length
152
+
153
+ line_start + line_text.each_char.take(character_count).join.bytesize
154
+ end
155
+
156
+ # @rbs (Integer start_byte, Integer end_byte) -> SourceSpan
157
+ def span(start_byte, end_byte)
158
+ SourceSpan.new(file: file, start: position_at(start_byte), finish: position_at(end_byte))
159
+ end
160
+
161
+ # @rbs (Segment segment) -> Token?
162
+ def token_for(segment)
163
+ index = segment.token_index
164
+ index && tokens[index]
165
+ end
166
+
167
+ # @rbs (AST::Root | AST::Fragment ast) -> SourceDocument
168
+ def with_ast(ast)
169
+ self.class.new(source: source, file: file, tokens: tokens, cst: cst, ast: ast)
170
+ end
171
+
172
+ private
173
+
174
+ # @rbs () -> Array[Integer]
175
+ def build_line_starts
176
+ starts = [0]
177
+ offset = 0
178
+ binary_source = source.b
179
+ while (newline = binary_source.index("\n", offset))
180
+ offset = newline + 1
181
+ starts << offset
182
+ end
183
+ starts
184
+ end
185
+
186
+ # @rbs () -> void
187
+ def validate_render
188
+ return if full_source_coverage? && segment_slices_match? && render == source
189
+
190
+ raise ArgumentError, "concrete syntax tree does not reproduce its source"
191
+ end
192
+
193
+ # @rbs () -> bool
194
+ def full_source_coverage?
195
+ segments = cst.segments
196
+ return source.empty? if segments.empty?
197
+
198
+ first = segments.first
199
+ last = segments.last
200
+ return false unless first && last
201
+
202
+ first.span.start_byte.zero? && last.span.end_byte == source.bytesize
203
+ end
204
+
205
+ # @rbs () -> bool
206
+ def segment_slices_match?
207
+ cst.segments.all? do |segment|
208
+ segment.span.file == file &&
209
+ source.byteslice(segment.span.start_byte, segment.span.length) == segment.text
210
+ end
211
+ end
212
+
213
+ # @rbs (Integer byte_offset) -> void
214
+ def validate_byte_offset(byte_offset)
215
+ return if byte_offset.between?(0, source.bytesize)
216
+
217
+ raise ArgumentError, "byte offset is outside the source"
218
+ end
219
+
220
+ # @rbs (SourceSpan span) -> void
221
+ def validate_span(span)
222
+ raise ArgumentError, "span belongs to another source" unless span.file == file
223
+
224
+ validate_byte_offset(span.start_byte)
225
+ validate_byte_offset(span.end_byte)
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../generation_input"
4
+
5
+ module Ibex
6
+ module Frontend
7
+ # Resolves canonical grammar paths and reads either open-buffer overlays or disk.
8
+ class SourceLoader
9
+ # @rbs @overlays: Hash[String, String]
10
+ # @rbs @aliases: Hash[String, String]
11
+ # @rbs @read_records: Hash[String, GenerationInput]
12
+ # @rbs @record_reads: bool
13
+ # @rbs @access_paths: Hash[String, Array[String]]
14
+
15
+ # @rbs () -> Array[GenerationInput]
16
+ def read_records
17
+ @read_records.values
18
+ end
19
+
20
+ # Associate the lexical path used by a resolver with its canonical source.
21
+ # @rbs (String canonical, String access_path) -> void
22
+ def record_access(canonical, access_path)
23
+ paths = (@access_paths[canonical] ||= [])
24
+ expanded = File.expand_path(access_path)
25
+ paths << expanded unless paths.include?(expanded)
26
+ @read_records[canonical]&.add_access_path(expanded)
27
+ end
28
+
29
+ # @rbs (?overlays: Hash[String, String], ?record_reads: bool) -> void
30
+ def initialize(overlays: {}, record_reads: false)
31
+ @overlays = {} #: Hash[String, String]
32
+ @aliases = {} #: Hash[String, String]
33
+ @read_records = {} #: Hash[String, GenerationInput]
34
+ @record_reads = record_reads
35
+ @access_paths = {} #: Hash[String, Array[String]]
36
+ overlays.each { |path, source| set_overlay(path, source) }
37
+ end
38
+
39
+ # Canonicalize an existing file, or a missing path explicitly allowed by an open overlay.
40
+ # @rbs (String path, ?base: String, ?allow_missing: bool) -> String
41
+ def canonical_path(path, base: Dir.pwd, allow_missing: false)
42
+ expanded = File.expand_path(path, base)
43
+ aliased = @aliases[expanded]
44
+ return aliased if aliased
45
+
46
+ begin
47
+ File.realpath(expanded)
48
+ rescue Errno::ENOENT
49
+ canonical = canonical_missing_path(expanded)
50
+ return canonical if allow_missing || @overlays.key?(canonical)
51
+
52
+ raise
53
+ end
54
+ end
55
+
56
+ # @rbs (String path) -> String
57
+ def read(path)
58
+ canonical = canonical_path(path, allow_missing: true)
59
+ overlay = @overlays[canonical]
60
+ source = overlay || File.binread(canonical)
61
+ if @record_reads
62
+ record = GenerationInput.new(canonical, source)
63
+ (@access_paths[canonical] || []).each { |access| record.add_access_path(access) }
64
+ @read_records[canonical] = record
65
+ end
66
+
67
+ source
68
+ end
69
+
70
+ # @rbs (String path) -> bool
71
+ def file?(path)
72
+ canonical = canonical_path(path, allow_missing: true)
73
+ @overlays.key?(canonical) || File.file?(canonical)
74
+ rescue SystemCallError
75
+ false
76
+ end
77
+
78
+ # @rbs (String path) -> bool
79
+ def overlay?(path)
80
+ canonical = canonical_path(path, allow_missing: true)
81
+ @overlays.key?(canonical)
82
+ rescue SystemCallError
83
+ false
84
+ end
85
+
86
+ # Install or replace an editor buffer without writing it to disk.
87
+ # @rbs (String path, String source) -> String
88
+ def set_overlay(path, source)
89
+ expanded = File.expand_path(path)
90
+ canonical = canonical_path(expanded, allow_missing: true)
91
+ raise ArgumentError, "overlay path must not be a directory" if File.directory?(canonical)
92
+
93
+ @aliases[expanded] = canonical
94
+ @overlays[canonical] = source.dup.freeze
95
+ canonical
96
+ end
97
+
98
+ # Remove an editor buffer and return its canonical path.
99
+ # @rbs (String path) -> String
100
+ def delete_overlay(path)
101
+ expanded = File.expand_path(path)
102
+ canonical = @aliases.delete(expanded) || canonical_path(expanded, allow_missing: true)
103
+ @aliases.delete_if { |_alias_path, target| target == canonical }
104
+ @overlays.delete(canonical)
105
+ canonical
106
+ end
107
+
108
+ # Read disk while deliberately bypassing an open overlay.
109
+ # @rbs (String path) -> String
110
+ def disk_source(path)
111
+ File.binread(disk_path(path))
112
+ end
113
+
114
+ # @rbs (String path) -> bool
115
+ def disk_file?(path)
116
+ File.file?(disk_path(path))
117
+ rescue SystemCallError
118
+ false
119
+ end
120
+
121
+ # Resolve the current disk target without consulting overlay aliases.
122
+ # @rbs (String path) -> String
123
+ def disk_path(path)
124
+ File.realpath(File.expand_path(path))
125
+ end
126
+
127
+ private
128
+
129
+ # Resolve the nearest existing ancestor so symlink escapes remain visible for new files.
130
+ # @rbs (String expanded) -> String
131
+ def canonical_missing_path(expanded)
132
+ raise Errno::ENOENT, expanded if File.symlink?(expanded)
133
+
134
+ cursor = expanded
135
+ suffix = [] #: Array[String]
136
+ loop do
137
+ parent = File.dirname(cursor)
138
+ raise Errno::ENOENT, expanded if parent == cursor
139
+
140
+ suffix.unshift(File.basename(cursor))
141
+ cursor = parent
142
+ break if File.exist?(cursor) || File.symlink?(cursor)
143
+ end
144
+ raise Errno::ENOTDIR, cursor unless File.directory?(File.realpath(cursor))
145
+
146
+ File.join(File.realpath(cursor), *suffix)
147
+ end
148
+ end
149
+ end
150
+ end