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,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # @rbs!
6
+ # type lsp_value = String | Integer | Float | bool | nil | Array[lsp_value] | Hash[String, lsp_value]
7
+ # type lsp_object = Hash[String, lsp_value]
8
+
9
+ # Converts between frontend byte offsets and zero-based LSP UTF-16 positions.
10
+ class PositionCodec
11
+ # @rbs (String source) -> void
12
+ def initialize(source)
13
+ @source = Frontend::SourceEncoding.validated_utf8(source, "(lsp)")
14
+ @line_starts = build_line_starts.freeze #: Array[Integer]
15
+ end
16
+
17
+ # @rbs (Integer byte_offset) -> Hash[String, Integer]
18
+ def position(byte_offset)
19
+ validate_byte_offset(byte_offset)
20
+ prefix = @source.byteslice(0, byte_offset) || ""
21
+ raise ArgumentError, "byte offset is not on a UTF-8 character boundary" unless prefix.valid_encoding?
22
+
23
+ line = (@line_starts.bsearch_index { |start| start > byte_offset } || @line_starts.length) - 1
24
+ line_start = @line_starts.fetch(line)
25
+ line_end = content_end(line)
26
+ raise ArgumentError, "byte offset points inside a line ending" if byte_offset > line_end
27
+
28
+ text = @source.byteslice(line_start, byte_offset - line_start) || ""
29
+ { "line" => line, "character" => utf16_length(text) }
30
+ end
31
+
32
+ # @rbs (lsp_object position) -> Integer
33
+ def byte_offset(position)
34
+ line = integer_member(position, "line")
35
+ character = integer_member(position, "character")
36
+ raise ArgumentError, "line and character must be non-negative" if line.negative? || character.negative?
37
+
38
+ line_start = @line_starts[line]
39
+ raise ArgumentError, "line is outside the document" unless line_start
40
+
41
+ text = @source.byteslice(line_start, content_end(line) - line_start) || ""
42
+ line_start + byte_length_at_utf16(text, character)
43
+ end
44
+
45
+ # @rbs (Frontend::SourceSpan span) -> Hash[String, Hash[String, Integer]]
46
+ def range(span)
47
+ { "start" => position(span.start_byte), "end" => position(span.end_byte) }
48
+ end
49
+
50
+ private
51
+
52
+ # @rbs () -> Array[Integer]
53
+ def build_line_starts
54
+ starts = [0]
55
+ offset = 0
56
+ @source.each_char do |character|
57
+ offset += character.bytesize
58
+ starts << offset if character == "\n"
59
+ end
60
+ starts
61
+ end
62
+
63
+ # @rbs (Integer line) -> Integer
64
+ def content_end(line)
65
+ boundary = @line_starts[line + 1] || @source.bytesize
66
+ return boundary unless @line_starts[line + 1]
67
+
68
+ before_newline = boundary - 1
69
+ @source.getbyte(before_newline - 1) == 13 ? before_newline - 1 : before_newline
70
+ end
71
+
72
+ # @rbs (String text) -> Integer
73
+ def utf16_length(text)
74
+ text.each_codepoint.sum { |codepoint| codepoint > 0xFFFF ? 2 : 1 }
75
+ end
76
+
77
+ # @rbs (String text, Integer units) -> Integer
78
+ def byte_length_at_utf16(text, units)
79
+ consumed_units = 0
80
+ consumed_bytes = 0
81
+ text.each_char do |character|
82
+ return consumed_bytes if consumed_units == units
83
+
84
+ width = character.ord > 0xFFFF ? 2 : 1
85
+ if consumed_units + width > units
86
+ raise ArgumentError, "character points into the middle of a UTF-16 surrogate pair"
87
+ end
88
+
89
+ consumed_units += width
90
+ consumed_bytes += character.bytesize
91
+ end
92
+ return consumed_bytes if consumed_units == units
93
+
94
+ raise ArgumentError, "character is outside the document line"
95
+ end
96
+
97
+ # @rbs (lsp_object position, String name) -> Integer
98
+ def integer_member(position, name)
99
+ value = position[name]
100
+ raise ArgumentError, "#{name} must be an integer" unless value.is_a?(Integer)
101
+
102
+ value
103
+ end
104
+
105
+ # @rbs (Integer byte_offset) -> void
106
+ def validate_byte_offset(byte_offset)
107
+ return if byte_offset.is_a?(Integer) && byte_offset.between?(0, @source.bytesize)
108
+
109
+ raise ArgumentError, "byte offset is outside the document"
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # Resource bounds shared by transport and open-document validation.
6
+ module Limits
7
+ MAX_HEADER_BYTES = 16 * 1024
8
+ MAX_MESSAGE_BYTES = 1024 * 1024
9
+ MAX_DOCUMENT_BYTES = 2 * 1024 * 1024
10
+ MAX_OUTPUT_BYTES = 1024 * 1024
11
+ MAX_ERROR_MESSAGE_CHARACTERS = 1024
12
+ MAX_REQUEST_ID_BYTES = 256
13
+ end
14
+
15
+ # Bounded protocol and workspace failures with their JSON-RPC error code.
16
+ class ProtocolError < Ibex::Error
17
+ attr_reader :code #: Integer
18
+ attr_reader :request_id #: String | Integer | nil
19
+ attr_reader :fatal #: bool
20
+
21
+ # @rbs (String message, ?code: Integer, ?request_id: String | Integer | nil, ?fatal: bool) -> void
22
+ def initialize(message, code: -32_600, request_id: nil, fatal: false)
23
+ normalized = message.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
24
+ bounded = normalized[0, Limits::MAX_ERROR_MESSAGE_CHARACTERS] || ""
25
+ bounded += "…" if bounded.length < normalized.length
26
+ super("(lsp):1:1: #{bounded}")
27
+ @code = code
28
+ @request_id = request_id
29
+ @fatal = fatal
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # Dispatches the finite LSP surface to focused lifecycle, document, and navigation handlers.
6
+ module RequestHandlers
7
+ include InitializationHandlers
8
+ include DocumentHandlers
9
+ include NavigationHandlers
10
+ include RequestSupport
11
+
12
+ HANDLERS = {
13
+ "initialize" => :initialize_workspace,
14
+ "initialized" => :initialized_notification,
15
+ "shutdown" => :shutdown_request,
16
+ "exit" => :exit_notification,
17
+ "textDocument/didOpen" => :did_open,
18
+ "textDocument/didChange" => :did_change,
19
+ "textDocument/didSave" => :did_save,
20
+ "textDocument/didClose" => :did_close,
21
+ "textDocument/definition" => :definition,
22
+ "textDocument/references" => :references,
23
+ "textDocument/prepareRename" => :prepare_rename,
24
+ "textDocument/rename" => :rename,
25
+ "textDocument/hover" => :hover,
26
+ "textDocument/completion" => :completion,
27
+ "$/cancelRequest" => :cancel_request
28
+ }.freeze #: Hash[String, Symbol]
29
+
30
+ private
31
+
32
+ # @rbs (String method, lsp_value? params) -> lsp_value?
33
+ def dispatch(method, params)
34
+ handler = HANDLERS[method]
35
+ raise ProtocolError.new("method not found: #{method}", code: -32_601) unless handler
36
+
37
+ send(handler, params)
38
+ end
39
+
40
+ # @rbs (lsp_value? params) -> nil
41
+ def cancel_request(_params)
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # Shared publication, state, and parameter validation for request handlers.
6
+ module RequestSupport
7
+ private
8
+
9
+ # @rbs (Array[DocumentStore::publication] publications) -> void
10
+ def publish(publications)
11
+ publications.each do |publication|
12
+ version = publication[:version]
13
+ next if version && stale_publication?(publication.fetch(:uri), version)
14
+
15
+ params = {
16
+ "uri" => publication.fetch(:uri),
17
+ "diagnostics" => publication.fetch(:diagnostics)
18
+ } #: lsp_object
19
+ params["version"] = version if version
20
+ @transport.write_message(
21
+ "jsonrpc" => "2.0", "method" => "textDocument/publishDiagnostics", "params" => params
22
+ )
23
+ end
24
+ end
25
+
26
+ # @rbs (String uri, Integer version) -> bool
27
+ def stale_publication?(uri, version)
28
+ path = workspace.path(uri)
29
+ store.snapshot_for(path)&.fetch(:version) != version
30
+ end
31
+
32
+ # @rbs (lsp_value? value) -> lsp_object
33
+ def params_hash(value)
34
+ return {} if value.nil?
35
+
36
+ if value.is_a?(Hash)
37
+ result = value #: lsp_object
38
+ return result
39
+ end
40
+
41
+ raise ProtocolError.new("params must be an object", code: -32_602)
42
+ end
43
+
44
+ # @rbs (lsp_object value, String name) -> lsp_object
45
+ def hash_member(value, name)
46
+ member = value[name]
47
+ if member.is_a?(Hash)
48
+ result = member #: lsp_object
49
+ return result
50
+ end
51
+
52
+ raise ProtocolError.new("#{name} must be an object", code: -32_602)
53
+ end
54
+
55
+ # @rbs (lsp_object value, String name) -> String
56
+ def string_member(value, name)
57
+ member = value[name]
58
+ return member if member.is_a?(String)
59
+
60
+ raise ProtocolError.new("#{name} must be a string", code: -32_602)
61
+ end
62
+
63
+ # @rbs (lsp_object value, String name) -> Integer
64
+ def integer_member(value, name)
65
+ member = value[name]
66
+ return member if member.is_a?(Integer)
67
+
68
+ raise ProtocolError.new("#{name} must be an integer", code: -32_602)
69
+ end
70
+
71
+ # @rbs (Symbol state) -> void
72
+ def require_state!(state)
73
+ return if @state == state
74
+
75
+ raise ProtocolError.new("request requires #{state} server state", code: -32_600)
76
+ end
77
+
78
+ # @rbs () -> void
79
+ def require_running!
80
+ require_state!(:running)
81
+ end
82
+
83
+ # @rbs () -> DocumentStore
84
+ def store
85
+ @store || raise(ProtocolError.new("server is not initialized", code: -32_002))
86
+ end
87
+
88
+ # @rbs () -> Workspace
89
+ def workspace
90
+ @workspace || raise(ProtocolError.new("server is not initialized", code: -32_002))
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # Coordinates JSON-RPC lifecycle, request responses, and protocol-safe logging.
6
+ class Server
7
+ include RequestHandlers
8
+
9
+ REQUEST_METHODS = %w[
10
+ initialize shutdown textDocument/definition textDocument/references textDocument/prepareRename
11
+ textDocument/rename textDocument/hover textDocument/completion
12
+ ].freeze #: Array[String]
13
+ NOTIFICATION_METHODS = %w[
14
+ initialized exit textDocument/didOpen textDocument/didChange textDocument/didSave textDocument/didClose
15
+ $/cancelRequest
16
+ ].freeze #: Array[String]
17
+
18
+ # @rbs (stdin: Transport::_Input, stdout: Transport::_Output, stderr: Object?) -> void
19
+ def initialize(stdin:, stdout:, stderr:)
20
+ @transport = Transport.new(stdin, stdout)
21
+ @stderr = stderr
22
+ @state = :uninitialized
23
+ @exit_requested = false
24
+ @exit_status = nil #: Integer?
25
+ end
26
+
27
+ # @rbs () -> Integer
28
+ def run
29
+ until @exit_requested
30
+ message = read_message
31
+ break unless message
32
+
33
+ process_message(message)
34
+ end
35
+ @exit_status || 1
36
+ end
37
+
38
+ private
39
+
40
+ # @rbs () -> lsp_object?
41
+ def read_message
42
+ @transport.read_message
43
+ rescue ProtocolError => e
44
+ send_error(nil, e)
45
+ @exit_requested = true if e.fatal
46
+ retry unless e.fatal
47
+ nil
48
+ end
49
+
50
+ # @rbs (lsp_object message) -> void
51
+ def process_message(message)
52
+ request_id = safe_request_id(message)
53
+ notification = notification_envelope?(message)
54
+ request_id = validate_envelope(message)
55
+ method = message.fetch("method") #: String
56
+ validate_message_kind!(method, message.key?("id"))
57
+ return if request_id.nil? && method.start_with?("$/")
58
+
59
+ validate_lifecycle!(method)
60
+ result = dispatch(method, message["params"])
61
+ send_result(request_id, result) if request_id
62
+ rescue ProtocolError => e
63
+ notification ? log(e.message) : send_error(request_id, e)
64
+ rescue StandardError => e
65
+ error = ProtocolError.new("internal error: #{e.message}", code: -32_603)
66
+ notification ? log(error.message) : send_error(request_id, error)
67
+ end
68
+
69
+ # @rbs (lsp_object message) -> (String | Integer)?
70
+ def safe_request_id(message)
71
+ value = message["id"]
72
+ return unless value.is_a?(String) || value.is_a?(Integer)
73
+
74
+ value if valid_request_id?(value)
75
+ end
76
+
77
+ # @rbs (lsp_object message) -> bool
78
+ def notification_envelope?(message)
79
+ !message.key?("id") && message["jsonrpc"] == "2.0" && message["method"].is_a?(String)
80
+ end
81
+
82
+ # @rbs (String method, bool request) -> void
83
+ def validate_message_kind!(method, request)
84
+ if REQUEST_METHODS.include?(method) && !request
85
+ raise ProtocolError.new("#{method} must be a request", code: -32_600)
86
+ end
87
+ return unless NOTIFICATION_METHODS.include?(method) && request
88
+
89
+ raise ProtocolError.new("#{method} must be a notification", code: -32_600)
90
+ end
91
+
92
+ # @rbs (lsp_object message) -> (String | Integer)?
93
+ def validate_envelope(message)
94
+ unless message["jsonrpc"] == "2.0" && message["method"].is_a?(String)
95
+ raise ProtocolError.new("invalid JSON-RPC request", code: -32_600)
96
+ end
97
+ return nil unless message.key?("id")
98
+
99
+ request_id = message["id"]
100
+ unless (request_id.is_a?(String) || request_id.is_a?(Integer)) && valid_request_id?(request_id)
101
+ raise ProtocolError.new("request id must be a bounded string or integer", code: -32_600)
102
+ end
103
+
104
+ request_id
105
+ end
106
+
107
+ # @rbs (Object? value) -> bool
108
+ def valid_request_id?(value)
109
+ return false unless value.is_a?(String) || value.is_a?(Integer)
110
+
111
+ value.to_s.bytesize <= Limits::MAX_REQUEST_ID_BYTES
112
+ end
113
+
114
+ # @rbs (String method) -> void
115
+ def validate_lifecycle!(method)
116
+ return if method == "exit"
117
+ return if @state == :uninitialized && method == "initialize"
118
+ return if @state == :initialized && %w[initialized shutdown].include?(method)
119
+ return if @state == :running
120
+
121
+ code = @state == :uninitialized ? -32_002 : -32_600
122
+ raise ProtocolError.new("request is invalid in #{@state} server state", code: code)
123
+ end
124
+
125
+ # @rbs (String | Integer request_id, lsp_value result) -> void
126
+ def send_result(request_id, result)
127
+ @transport.write_message("jsonrpc" => "2.0", "id" => request_id, "result" => result)
128
+ rescue ProtocolError => e
129
+ send_error(request_id, e)
130
+ end
131
+
132
+ # @rbs (String | Integer | nil request_id, ProtocolError error) -> void
133
+ def send_error(request_id, error)
134
+ @transport.write_message(
135
+ "jsonrpc" => "2.0", "id" => request_id,
136
+ "error" => { "code" => error.code, "message" => error.message }
137
+ )
138
+ rescue ProtocolError => e
139
+ log(e.message)
140
+ end
141
+
142
+ # @rbs (String message) -> void
143
+ def log(message)
144
+ @stderr.puts(message)
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ibex
4
+ module LSP
5
+ # Answers source navigation and guarded workspace rename operations.
6
+ class SymbolIndex
7
+ IDENTIFIER = /\A[A-Za-z_][A-Za-z0-9_]*\z/ #: Regexp
8
+ RESERVED = %w[
9
+ class fragment include token prechigh preclow left right nonassoc options expect start
10
+ recover sync on_error_reduce convert display type pragma extended rule end
11
+ separated_list separated_nonempty_list
12
+ ].freeze #: Array[String]
13
+
14
+ # @rbs (DocumentStore store, String path) -> void
15
+ def initialize(store, path)
16
+ @store = store
17
+ @path = path
18
+ files = store.files_for(path)
19
+ @occurrences, @documents = SymbolIndexBuilder.new(store, files).build
20
+ @snapshot_state = files.to_h do |file|
21
+ snapshot = store.snapshot_for(file)
22
+ [file, snapshot && [snapshot.fetch(:version), snapshot.fetch(:source)]]
23
+ end #: Hash[String, [Integer?, String]?]
24
+ end
25
+
26
+ # @rbs (String path, lsp_object position) -> Array[lsp_object]
27
+ def definition(path, position)
28
+ occurrence = occurrence_at(path, position)
29
+ return [] unless occurrence
30
+ return [include_target_location(occurrence)] if occurrence.kind == :include
31
+
32
+ matching(occurrence.key, role: :definition).map { |entry| location(entry) }
33
+ end
34
+
35
+ # @rbs (String path, lsp_object position, ?include_declaration: bool) -> Array[lsp_object]
36
+ def references(path, position, include_declaration: false)
37
+ occurrence = occurrence_at(path, position)
38
+ return [] unless occurrence
39
+
40
+ entries = matching(occurrence.key)
41
+ entries = entries.reject { |entry| entry.role == :definition } unless include_declaration
42
+ entries.map { |entry| location(entry) }
43
+ end
44
+
45
+ # @rbs (String path, lsp_object position) -> lsp_object?
46
+ def prepare_rename(path, position)
47
+ occurrence = renameable_occurrence(path, position)
48
+ { "range" => range(occurrence), "placeholder" => occurrence.name }
49
+ end
50
+
51
+ # @rbs (String path, lsp_object position, String new_name) -> lsp_object
52
+ def rename(path, position, new_name)
53
+ ensure_fresh!
54
+ occurrence = renameable_occurrence(path, position)
55
+ validate_new_name(occurrence, new_name)
56
+ entries = matching(occurrence.key)
57
+ validate_unshared(entries)
58
+ validate_nonoverlapping(entries)
59
+ changes = entries.group_by(&:path).transform_values do |path_entries|
60
+ path_entries.sort_by { |entry| entry.span.start_byte }.map do |entry|
61
+ { "range" => range(entry), "newText" => new_name }
62
+ end
63
+ end
64
+ replacements = changed_sources(entries, new_name)
65
+ unless @store.valid_replacements?(replacements)
66
+ raise ProtocolError.new("rename would make the affected grammar closure invalid", code: -32_602)
67
+ end
68
+
69
+ document_changes = changes.sort.map do |changed_path, edits|
70
+ snapshot = @store.snapshot_for(changed_path)
71
+ version = snapshot&.fetch(:open) ? snapshot.fetch(:version) : nil
72
+ {
73
+ "textDocument" => { "uri" => @store.uri_for(changed_path), "version" => version },
74
+ "edits" => edits
75
+ }
76
+ end
77
+ { "documentChanges" => document_changes }
78
+ end
79
+
80
+ # @rbs (String path, lsp_object position) -> lsp_object?
81
+ def hover(path, position)
82
+ occurrence = occurrence_at(path, position)
83
+ return unless occurrence
84
+
85
+ value = hover_value(occurrence)
86
+ return unless value
87
+
88
+ {
89
+ "contents" => { "kind" => "markdown", "value" => value },
90
+ "range" => range(occurrence)
91
+ }
92
+ end
93
+
94
+ private
95
+
96
+ # @rbs (String path, lsp_object position) -> SymbolOccurrence?
97
+ def occurrence_at(path, position)
98
+ document = @documents[path]
99
+ return unless document
100
+
101
+ offset = PositionCodec.new(document.source).byte_offset(position)
102
+ candidates = @occurrences.select do |entry|
103
+ entry.path == path && entry.span.start_byte <= offset && offset < entry.span.end_byte
104
+ end
105
+ candidates.min_by { |entry| entry.span.length }
106
+ end
107
+
108
+ # @rbs (symbol_key key, ?role: Symbol?) -> Array[SymbolOccurrence]
109
+ def matching(key, role: nil)
110
+ @occurrences.select { |entry| entry.key == key && (!role || entry.role == role) }
111
+ end
112
+
113
+ # @rbs (SymbolOccurrence occurrence) -> lsp_object
114
+ def location(occurrence)
115
+ { "uri" => @store.uri_for(occurrence.path), "range" => range(occurrence) }
116
+ end
117
+
118
+ # @rbs (SymbolOccurrence occurrence) -> lsp_object
119
+ def include_target_location(occurrence)
120
+ target = occurrence.data.fetch(:target)
121
+ point = { "line" => 0, "character" => 0 }
122
+ { "uri" => @store.uri_for(target), "range" => { "start" => point, "end" => point.dup } }
123
+ end
124
+
125
+ # @rbs (SymbolOccurrence occurrence) -> Hash[String, Hash[String, Integer]]
126
+ def range(occurrence)
127
+ document = @documents.fetch(occurrence.path)
128
+ PositionCodec.new(document.source).range(occurrence.span)
129
+ end
130
+
131
+ # @rbs (String path, lsp_object position) -> SymbolOccurrence
132
+ def renameable_occurrence(path, position)
133
+ occurrence = occurrence_at(path, position)
134
+ unless occurrence && %i[rule terminal parameter].include?(occurrence.kind) &&
135
+ occurrence.name.match?(IDENTIFIER) && !RESERVED.include?(occurrence.name)
136
+ raise ProtocolError.new("symbol at position cannot be renamed", code: -32_602)
137
+ end
138
+ unless matching(occurrence.key, role: :definition).any?
139
+ raise ProtocolError.new("unresolved symbol cannot be renamed", code: -32_602)
140
+ end
141
+
142
+ occurrence
143
+ end
144
+
145
+ # @rbs (SymbolOccurrence occurrence, String new_name) -> void
146
+ def validate_new_name(occurrence, new_name)
147
+ unless new_name.is_a?(String) && new_name.match?(IDENTIFIER) && !RESERVED.include?(new_name)
148
+ raise ProtocolError.new("new name must be a non-reserved grammar identifier", code: -32_602)
149
+ end
150
+
151
+ collision = @occurrences.any? do |entry|
152
+ entry.role == :definition && entry.name == new_name && entry.key != occurrence.key &&
153
+ collision_scope?(entry, occurrence)
154
+ end
155
+ raise ProtocolError.new("rename collides with existing symbol #{new_name}", code: -32_602) if collision
156
+ end
157
+
158
+ # @rbs (SymbolOccurrence candidate, SymbolOccurrence occurrence) -> bool
159
+ def collision_scope?(candidate, occurrence)
160
+ return candidate.key.take(3) == occurrence.key.take(3) if occurrence.kind == :parameter
161
+
162
+ candidate.kind != :parameter
163
+ end
164
+
165
+ # @rbs (Array[SymbolOccurrence] entries) -> void
166
+ def validate_nonoverlapping(entries)
167
+ entries.group_by(&:path).each_value do |path_entries|
168
+ ordered = path_entries.sort_by { |entry| [entry.span.start_byte, entry.span.end_byte] }
169
+ overlap = (1...ordered.length).any? do |index|
170
+ ordered.fetch(index - 1).span.end_byte > ordered.fetch(index).span.start_byte
171
+ end
172
+ raise ProtocolError.new("rename contains overlapping source edits", code: -32_603) if overlap
173
+ end
174
+ end
175
+
176
+ # @rbs (Array[SymbolOccurrence] entries) -> void
177
+ def validate_unshared(entries)
178
+ return unless entries.any? { |entry| @store.roots_for(entry.path).length > 1 }
179
+
180
+ raise ProtocolError.new("rename affecting a fragment shared by multiple roots is ambiguous", code: -32_602)
181
+ end
182
+
183
+ # @rbs () -> void
184
+ def ensure_fresh!
185
+ stale = @snapshot_state.any? do |path, expected|
186
+ snapshot = @store.snapshot_for(path)
187
+ actual = snapshot && [snapshot.fetch(:version), snapshot.fetch(:source)]
188
+ actual != expected
189
+ end
190
+ raise ProtocolError.new("symbol snapshot is stale; retry the request", code: -32_602) if stale
191
+ end
192
+
193
+ # @rbs (Array[SymbolOccurrence] entries, String new_name) -> Hash[String, String]
194
+ def changed_sources(entries, new_name)
195
+ entries.group_by(&:path).to_h do |path, path_entries|
196
+ document = @documents.fetch(path)
197
+ source = document.source.dup
198
+ path_entries.sort_by { |entry| -entry.span.start_byte }.each do |entry|
199
+ prefix = source.byteslice(0, entry.span.start_byte) || ""
200
+ suffix = source.byteslice(entry.span.end_byte, source.bytesize - entry.span.end_byte) || ""
201
+ source = prefix + new_name + suffix
202
+ end
203
+ [path, source]
204
+ end
205
+ end
206
+
207
+ # @rbs (SymbolOccurrence occurrence) -> String?
208
+ def hover_value(occurrence)
209
+ case occurrence.kind
210
+ when :rule then rule_hover(occurrence)
211
+ when :terminal then terminal_hover(occurrence)
212
+ when :parameter then "`#{occurrence.name}` — formal parameter of `#{occurrence.data[:rule]}`"
213
+ when :include then "Includes `#{occurrence.data[:target]}`"
214
+ end
215
+ end
216
+
217
+ # @rbs (SymbolOccurrence occurrence) -> String
218
+ def rule_hover(occurrence)
219
+ definition = matching(occurrence.key, role: :definition).first
220
+ data = definition&.data || occurrence.data
221
+ lines = ["```ibex", data[:signature] || occurrence.name, "```"]
222
+ lines << data[:documentation] if data[:documentation]
223
+ lines.join("\n")
224
+ end
225
+
226
+ # @rbs (SymbolOccurrence occurrence) -> String
227
+ def terminal_hover(occurrence)
228
+ definition = matching(occurrence.key, role: :definition).first
229
+ data = definition&.data || occurrence.data
230
+ lines = ["`#{occurrence.name}` — terminal"]
231
+ lines << "Display: #{data[:display]}" if data[:display]
232
+ lines << "Type: `#{data[:type]}`" if data[:type]
233
+ if (precedence = data[:precedence])
234
+ precedence_data = precedence #: Hash[Symbol, symbol_data_value]
235
+ lines << "Precedence: #{precedence_data[:associativity]} level #{precedence_data[:level]}"
236
+ end
237
+ lines.join("\n\n")
238
+ end
239
+ end
240
+ end
241
+ end