ibex 0.2.0 → 0.4.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 (461) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -272
  3. data/docs/architecture.md +187 -35
  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 +3 -4
  12. data/docs/cst.md +24 -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 +274 -0
  40. data/docs/development.md +196 -2
  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 +2 -1
  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 +1617 -0
  49. data/docs/error-ux-round2.md +111 -0
  50. data/docs/error-ux.md +54 -0
  51. data/docs/getting-started.md +80 -0
  52. data/docs/grammar-reference.md +184 -13
  53. data/docs/ielr-design.md +43 -0
  54. data/docs/ielr.md +92 -0
  55. data/docs/impact.md +101 -0
  56. data/docs/ja/bison-import.md +51 -0
  57. data/docs/ja/stable-api.md +53 -0
  58. data/docs/lexer-construction-profile.md +109 -0
  59. data/docs/lexer-migration.md +5 -0
  60. data/docs/maturity.md +129 -0
  61. data/docs/project-site-strategy.md +54 -0
  62. data/docs/racc-migration-evidence.md +45 -0
  63. data/docs/racc-migration.md +11 -0
  64. data/docs/release-readiness.md +126 -68
  65. data/docs/repair-semantics.md +123 -0
  66. data/docs/runtime-abi-evolution.md +310 -0
  67. data/docs/stability.md +145 -34
  68. data/docs/status.md +43 -0
  69. data/docs/syntax-sessions.md +183 -0
  70. data/docs/table-artifact.md +125 -0
  71. data/docs/test-interactions.md +219 -0
  72. data/docs/verification-report.md +137 -0
  73. data/docs/verifier-trust-boundary.md +199 -0
  74. data/docs/workloads.md +132 -0
  75. data/lib/ibex/analysis/digraph.rb +128 -0
  76. data/lib/ibex/analysis/sets.rb +14 -0
  77. data/lib/ibex/analysis.rb +1 -0
  78. data/lib/ibex/bison_import/importer.rb +516 -0
  79. data/lib/ibex/bison_import/tokenizer.rb +260 -0
  80. data/lib/ibex/bison_import.rb +200 -0
  81. data/lib/ibex/bounded_subprocess.rb +171 -0
  82. data/lib/ibex/cli/ambiguity.rb +14 -5
  83. data/lib/ibex/cli/analysis.rb +134 -0
  84. data/lib/ibex/cli/bison_import.rb +121 -0
  85. data/lib/ibex/cli/config.rb +128 -0
  86. data/lib/ibex/cli/diagnostics.rb +31 -11
  87. data/lib/ibex/cli/documentation.rb +11 -8
  88. data/lib/ibex/cli/equiv.rb +169 -0
  89. data/lib/ibex/cli/error_messages.rb +11 -4
  90. data/lib/ibex/cli/explain.rb +14 -5
  91. data/lib/ibex/cli/fix.rb +203 -0
  92. data/lib/ibex/cli/formatting.rb +11 -10
  93. data/lib/ibex/cli/fuzz.rb +200 -0
  94. data/lib/ibex/cli/fuzz_regressions.rb +145 -0
  95. data/lib/ibex/cli/generation_artifacts.rb +53 -2
  96. data/lib/ibex/cli/generation_error_messages.rb +1 -1
  97. data/lib/ibex/cli/grammar_tests.rb +32 -14
  98. data/lib/ibex/cli/impact.rb +393 -0
  99. data/lib/ibex/cli/ir_tools.rb +5 -61
  100. data/lib/ibex/cli/outputs.rb +60 -33
  101. data/lib/ibex/cli/reduce.rb +202 -0
  102. data/lib/ibex/cli/reduce_reporting.rb +71 -0
  103. data/lib/ibex/cli/samples.rb +49 -23
  104. data/lib/ibex/cli/verify.rb +116 -0
  105. data/lib/ibex/cli/watch.rb +6 -5
  106. data/lib/ibex/cli.rb +362 -56
  107. data/lib/ibex/codegen/action_locations.rb +1 -1
  108. data/lib/ibex/codegen/action_method_source.rb +5 -6
  109. data/lib/ibex/codegen/action_source.rb +3 -2
  110. data/lib/ibex/codegen/ambiguity.rb +5 -4
  111. data/lib/ibex/codegen/explain.rb +123 -55
  112. data/lib/ibex/codegen/generated_action_abi.rb +10 -5
  113. data/lib/ibex/codegen/rbs.rb +15 -7
  114. data/lib/ibex/codegen/report.rb +25 -13
  115. data/lib/ibex/codegen/ruby.rb +28 -64
  116. data/lib/ibex/codegen/ruby_actions.rb +9 -6
  117. data/lib/ibex/codegen/ruby_syntax.rb +15 -9
  118. data/lib/ibex/configuration/analysis_grammar.rb +35 -0
  119. data/lib/ibex/configuration/explanation.rb +421 -0
  120. data/lib/ibex/configuration/inspector.rb +232 -0
  121. data/lib/ibex/configuration.rb +580 -0
  122. data/lib/ibex/coverage/collector.rb +21 -11
  123. data/lib/ibex/coverage/event_stream.rb +13 -7
  124. data/lib/ibex/coverage/report.rb +26 -18
  125. data/lib/ibex/coverage/runtime_event_validator.rb +30 -21
  126. data/lib/ibex/delta_reducer.rb +99 -0
  127. data/lib/ibex/diff.rb +141 -0
  128. data/lib/ibex/equiv/machine.rb +135 -0
  129. data/lib/ibex/equiv.rb +373 -0
  130. data/lib/ibex/fix.rb +557 -0
  131. data/lib/ibex/frontend/ast.rb +22 -2
  132. data/lib/ibex/frontend/bootstrap_parser.rb +6 -13
  133. data/lib/ibex/frontend/diagnostic.rb +1 -1
  134. data/lib/ibex/frontend/formatter.rb +72 -26
  135. data/lib/ibex/frontend/generated_parser.rb +147 -117
  136. data/lib/ibex/frontend/generated_parser_base.rb +7 -3
  137. data/lib/ibex/frontend/generated_parser_includes.rb +1 -11
  138. data/lib/ibex/frontend/generation.rb +1 -1
  139. data/lib/ibex/frontend/parser/declarations.rb +17 -2
  140. data/lib/ibex/frontend/parser_configuration_support.rb +56 -0
  141. data/lib/ibex/frontend/regenerator.rb +1 -0
  142. data/lib/ibex/frontend/resolution.rb +2 -1
  143. data/lib/ibex/frontend/resolver.rb +1 -1
  144. data/lib/ibex/frontend/rule_documentation.rb +2 -1
  145. data/lib/ibex/frontend/source_cursor.rb +2 -2
  146. data/lib/ibex/frontend/source_span.rb +9 -1
  147. data/lib/ibex/frontend/token_adapter/declaration_state.rb +38 -6
  148. data/lib/ibex/frontend/token_adapter.rb +10 -0
  149. data/lib/ibex/frontend.rb +1 -0
  150. data/lib/ibex/fuzz.rb +219 -0
  151. data/lib/ibex/generation_input.rb +1 -1
  152. data/lib/ibex/generation_manifest.rb +36 -25
  153. data/lib/ibex/generation_transaction.rb +1 -1
  154. data/lib/ibex/generation_transaction_recovery.rb +4 -4
  155. data/lib/ibex/generation_transaction_validation.rb +2 -2
  156. data/lib/ibex/grammar_tests.rb +1 -1
  157. data/lib/ibex/impact/action_impact.rb +148 -0
  158. data/lib/ibex/impact/automaton_impact.rb +57 -0
  159. data/lib/ibex/impact/coverage_impact.rb +53 -0
  160. data/lib/ibex/impact/graph.rb +132 -0
  161. data/lib/ibex/impact/propagation.rb +147 -0
  162. data/lib/ibex/impact/report.rb +273 -0
  163. data/lib/ibex/impact/seeds.rb +69 -0
  164. data/lib/ibex/impact/severity.rb +139 -0
  165. data/lib/ibex/impact.rb +16 -0
  166. data/lib/ibex/ir/automaton_ir.rb +61 -19
  167. data/lib/ibex/ir/grammar_ir.rb +76 -45
  168. data/lib/ibex/ir/lexer_ir.rb +5 -5
  169. data/lib/ibex/ir/parser_contract.rb +135 -0
  170. data/lib/ibex/ir/serialize.rb +203 -76
  171. data/lib/ibex/ir/validator/automaton.rb +64 -34
  172. data/lib/ibex/ir/validator/base.rb +51 -33
  173. data/lib/ibex/ir/validator/grammar.rb +113 -83
  174. data/lib/ibex/ir/validator/lexer.rb +2 -2
  175. data/lib/ibex/ir/validator.rb +2 -1
  176. data/lib/ibex/ir.rb +16 -3
  177. data/lib/ibex/lalr/build_metrics.rb +50 -2
  178. data/lib/ibex/lalr/builder.rb +191 -56
  179. data/lib/ibex/lalr/conflict_search.rb +15 -4
  180. data/lib/ibex/lalr/counterexample.rb +19 -14
  181. data/lib/ibex/lalr/direct_lookaheads.rb +110 -57
  182. data/lib/ibex/lalr/goto_follows.rb +229 -0
  183. data/lib/ibex/lalr/ielr/annotator.rb +214 -0
  184. data/lib/ibex/lalr/ielr/bits.rb +28 -0
  185. data/lib/ibex/lalr/ielr/inadequacy.rb +47 -0
  186. data/lib/ibex/lalr/ielr/item_lookaheads.rb +78 -0
  187. data/lib/ibex/lalr/ielr/pipeline.rb +75 -0
  188. data/lib/ibex/lalr/ielr/split_stability.rb +78 -0
  189. data/lib/ibex/lalr/ielr/split_state.rb +20 -0
  190. data/lib/ibex/lalr/ielr/state_splitter.rb +258 -0
  191. data/lib/ibex/lalr/ielr_partition.rb +22 -8
  192. data/lib/ibex/lalr/inadequacy_report.rb +50 -0
  193. data/lib/ibex/lalr/lookahead_propagation.rb +111 -0
  194. data/lib/ibex/lalr/lr0_collection.rb +121 -0
  195. data/lib/ibex/lalr/unreachable_states.rb +80 -0
  196. data/lib/ibex/lalr.rb +52 -1
  197. data/lib/ibex/location.rb +3 -3
  198. data/lib/ibex/lsp/document_handlers.rb +9 -7
  199. data/lib/ibex/lsp/initialization_handlers.rb +9 -7
  200. data/lib/ibex/lsp/navigation_handlers.rb +25 -11
  201. data/lib/ibex/lsp/parser_configuration_assistance.rb +149 -0
  202. data/lib/ibex/lsp/position_codec.rb +6 -2
  203. data/lib/ibex/lsp/request_handlers.rb +3 -2
  204. data/lib/ibex/lsp/request_support.rb +14 -7
  205. data/lib/ibex/lsp/server.rb +13 -11
  206. data/lib/ibex/lsp/symbol_index.rb +13 -13
  207. data/lib/ibex/lsp/symbol_index_builder.rb +30 -20
  208. data/lib/ibex/lsp/symbol_occurrence.rb +8 -2
  209. data/lib/ibex/lsp/transport.rb +13 -3
  210. data/lib/ibex/lsp/workspace.rb +5 -4
  211. data/lib/ibex/lsp/workspace_analyzer.rb +1 -1
  212. data/lib/ibex/lsp.rb +1 -0
  213. data/lib/ibex/messages/en.yml +28 -0
  214. data/lib/ibex/messages/ja.yml +28 -0
  215. data/lib/ibex/messages.rb +68 -0
  216. data/lib/ibex/metrics.rb +175 -0
  217. data/lib/ibex/normalize/declarations.rb +2 -0
  218. data/lib/ibex/normalize/diagnostics.rb +63 -38
  219. data/lib/ibex/normalize/expander.rb +5 -4
  220. data/lib/ibex/normalize/expression.rb +8 -6
  221. data/lib/ibex/normalize/grammar_builder.rb +26 -0
  222. data/lib/ibex/normalize/inline_expansion.rb +100 -49
  223. data/lib/ibex/normalize/lexer.rb +3 -3
  224. data/lib/ibex/normalize/parameter_ebnf_lowering.rb +11 -10
  225. data/lib/ibex/normalize/parameter_lowering.rb +40 -23
  226. data/lib/ibex/normalize/parameters.rb +31 -4
  227. data/lib/ibex/normalize/parser_configuration.rb +70 -0
  228. data/lib/ibex/normalize.rb +10 -20
  229. data/lib/ibex/racc_migration/report.rb +6 -3
  230. data/lib/ibex/rake_task.rb +1 -1
  231. data/lib/ibex/samples.rb +43 -13
  232. data/lib/ibex/table_artifact/builder.rb +266 -0
  233. data/lib/ibex/table_artifact/cst_projection.rb +70 -0
  234. data/lib/ibex/table_artifact/document.rb +39 -0
  235. data/lib/ibex/table_artifact/executor.rb +187 -0
  236. data/lib/ibex/table_artifact/serializer.rb +57 -0
  237. data/lib/ibex/table_artifact/validator/metadata.rb +179 -0
  238. data/lib/ibex/table_artifact/validator/support.rb +86 -0
  239. data/lib/ibex/table_artifact/validator/tables.rb +238 -0
  240. data/lib/ibex/table_artifact/validator.rb +253 -0
  241. data/lib/ibex/table_artifact.rb +88 -0
  242. data/lib/ibex/table_simulation/result.rb +6 -2
  243. data/lib/ibex/table_simulation/step.rb +3 -1
  244. data/lib/ibex/tables.rb +14 -0
  245. data/lib/ibex/verifiable_generation_bundle.rb +86 -0
  246. data/lib/ibex/verification_report/builder.rb +159 -0
  247. data/lib/ibex/verification_report/canonical_ir.rb +123 -0
  248. data/lib/ibex/verification_report/logical_path.rb +68 -0
  249. data/lib/ibex/verification_report/validator.rb +329 -0
  250. data/lib/ibex/verification_report.rb +69 -0
  251. data/lib/ibex/verify/action_correspondence.rb +138 -0
  252. data/lib/ibex/verify/language_witness.rb +295 -0
  253. data/lib/ibex/verify/reference_collection.rb +190 -0
  254. data/lib/ibex/verify/result.rb +73 -0
  255. data/lib/ibex/verify/verifier.rb +598 -0
  256. data/lib/ibex/verify.rb +15 -0
  257. data/lib/ibex/version.rb +1 -1
  258. data/lib/ibex/watch/runner.rb +10 -1
  259. data/lib/ibex/watch/source_snapshot.rb +13 -8
  260. data/lib/ibex.rb +11 -0
  261. data/schema/{automaton-ir-v1.schema.json → automaton-ir-definitions.schema.json} +6 -6
  262. data/schema/{automaton-ir-v2.schema.json → automaton-ir.schema.json} +14 -7
  263. data/schema/bison-import-v1.schema.json +106 -0
  264. data/schema/conflict-explanation-review-v1.schema.json +196 -0
  265. data/schema/conflict-explanation-study-v1.schema.json +528 -0
  266. data/schema/construction-profile-v1.schema.json +779 -0
  267. data/schema/diff-v1.schema.json +41 -0
  268. data/schema/direct-ielr-decision-v1.schema.json +200 -0
  269. data/schema/equiv-v1.schema.json +64 -0
  270. data/schema/error-ux-review-v1.schema.json +609 -0
  271. data/schema/error-ux-round2-review-v1.schema.json +191 -0
  272. data/schema/error-ux-round2-v1.schema.json +519 -0
  273. data/schema/explain-v1.schema.json +63 -1
  274. data/schema/fix-v1.schema.json +64 -0
  275. data/schema/fix-v2.schema.json +66 -0
  276. data/schema/fix-v3.schema.json +101 -0
  277. data/schema/fuzz-regression-v1.schema.json +79 -0
  278. data/schema/fuzz-v1.schema.json +114 -0
  279. data/schema/{grammar-ir-v2.schema.json → grammar-ir-extensions.schema.json} +20 -65
  280. data/schema/{grammar-ir-v1.schema.json → grammar-ir-foundation.schema.json} +3 -3
  281. data/schema/grammar-ir.schema.json +271 -0
  282. data/schema/ielr-benchmark-v1.schema.json +85 -0
  283. data/schema/impact-v1.schema.json +208 -0
  284. data/schema/lexer-profile-v1.schema.json +491 -0
  285. data/schema/metrics-v1.schema.json +60 -0
  286. data/schema/reduce-v1.schema.json +22 -0
  287. data/schema/reduce-v2.schema.json +63 -0
  288. data/schema/table-artifact-v1.schema.json +377 -0
  289. data/schema/verification-report-v1.schema.json +222 -0
  290. data/schema/verify-v1.schema.json +42 -0
  291. data/sig/ibex/analysis/digraph.rbs +19 -0
  292. data/sig/ibex/analysis/sets.rbs +10 -0
  293. data/sig/ibex/bison_import/importer.rbs +140 -0
  294. data/sig/ibex/bison_import/tokenizer.rbs +91 -0
  295. data/sig/ibex/bison_import.rbs +95 -0
  296. data/sig/ibex/bounded_subprocess.rbs +76 -0
  297. data/sig/ibex/cli/ambiguity.rbs +6 -0
  298. data/sig/ibex/cli/analysis.rbs +43 -0
  299. data/sig/ibex/cli/bison_import.rbs +32 -0
  300. data/sig/ibex/cli/config.rbs +35 -0
  301. data/sig/ibex/cli/diagnostics.rbs +16 -3
  302. data/sig/ibex/cli/documentation.rbs +5 -1
  303. data/sig/ibex/cli/equiv.rbs +49 -0
  304. data/sig/ibex/cli/error_messages.rbs +6 -0
  305. data/sig/ibex/cli/explain.rbs +6 -0
  306. data/sig/ibex/cli/fix.rbs +53 -0
  307. data/sig/ibex/cli/formatting.rbs +5 -1
  308. data/sig/ibex/cli/fuzz.rbs +47 -0
  309. data/sig/ibex/cli/fuzz_regressions.rbs +40 -0
  310. data/sig/ibex/cli/generation_artifacts.rbs +21 -2
  311. data/sig/ibex/cli/generation_error_messages.rbs +2 -2
  312. data/sig/ibex/cli/grammar_tests.rbs +15 -3
  313. data/sig/ibex/cli/impact.rbs +95 -0
  314. data/sig/ibex/cli/ir_tools.rbs +7 -19
  315. data/sig/ibex/cli/outputs.rbs +9 -3
  316. data/sig/ibex/cli/reduce.rbs +60 -0
  317. data/sig/ibex/cli/reduce_reporting.rbs +27 -0
  318. data/sig/ibex/cli/samples.rbs +8 -0
  319. data/sig/ibex/cli/verify.rbs +28 -0
  320. data/sig/ibex/cli/watch.rbs +2 -2
  321. data/sig/ibex/cli.rbs +79 -5
  322. data/sig/ibex/codegen/action_method_source.rbs +4 -4
  323. data/sig/ibex/codegen/explain.rbs +45 -25
  324. data/sig/ibex/codegen/generated_action_abi.rbs +14 -10
  325. data/sig/ibex/codegen/rbs.rbs +12 -10
  326. data/sig/ibex/codegen/report.rbs +12 -12
  327. data/sig/ibex/codegen/ruby.rbs +18 -15
  328. data/sig/ibex/codegen/ruby_actions.rbs +6 -6
  329. data/sig/ibex/codegen/ruby_syntax.rbs +10 -8
  330. data/sig/ibex/configuration/analysis_grammar.rbs +13 -0
  331. data/sig/ibex/configuration/explanation.rbs +195 -0
  332. data/sig/ibex/configuration/inspector.rbs +53 -0
  333. data/sig/ibex/configuration.rbs +242 -0
  334. data/sig/ibex/coverage/collector.rbs +16 -14
  335. data/sig/ibex/coverage/event_stream.rbs +12 -10
  336. data/sig/ibex/coverage/report.rbs +20 -18
  337. data/sig/ibex/coverage/runtime_event_validator.rbs +38 -36
  338. data/sig/ibex/delta_reducer.rbs +38 -0
  339. data/sig/ibex/diff.rbs +47 -0
  340. data/sig/ibex/equiv/machine.rbs +54 -0
  341. data/sig/ibex/equiv.rbs +129 -0
  342. data/sig/ibex/fix.rbs +138 -0
  343. data/sig/ibex/frontend/ast.rbs +33 -7
  344. data/sig/ibex/frontend/bootstrap_parser.rbs +2 -0
  345. data/sig/ibex/frontend/diagnostic.rbs +2 -2
  346. data/sig/ibex/frontend/formatter.rbs +38 -30
  347. data/sig/ibex/frontend/generated_parser.rbs +87 -77
  348. data/sig/ibex/frontend/generated_parser_base.rbs +4 -2
  349. data/sig/ibex/frontend/parser/declarations.rbs +3 -0
  350. data/sig/ibex/frontend/parser_configuration_support.rbs +27 -0
  351. data/sig/ibex/frontend/resolution.rbs +3 -2
  352. data/sig/ibex/frontend/source_cursor.rbs +2 -2
  353. data/sig/ibex/frontend/source_span.rbs +5 -2
  354. data/sig/ibex/frontend/token_adapter/declaration_state.rbs +13 -1
  355. data/sig/ibex/frontend/token_adapter.rbs +6 -0
  356. data/sig/ibex/fuzz.rbs +71 -0
  357. data/sig/ibex/generation_input.rbs +2 -2
  358. data/sig/ibex/generation_manifest.rbs +35 -32
  359. data/sig/ibex/generation_transaction_recovery.rbs +2 -2
  360. data/sig/ibex/generation_transaction_validation.rbs +2 -2
  361. data/sig/ibex/grammar_tests.rbs +2 -2
  362. data/sig/ibex/impact/action_impact.rbs +58 -0
  363. data/sig/ibex/impact/automaton_impact.rbs +37 -0
  364. data/sig/ibex/impact/coverage_impact.rbs +28 -0
  365. data/sig/ibex/impact/graph.rbs +67 -0
  366. data/sig/ibex/impact/propagation.rbs +56 -0
  367. data/sig/ibex/impact/report.rbs +107 -0
  368. data/sig/ibex/impact/seeds.rbs +36 -0
  369. data/sig/ibex/impact/severity.rbs +55 -0
  370. data/sig/ibex/impact.rbs +6 -0
  371. data/sig/ibex/ir/automaton_ir.rbs +30 -11
  372. data/sig/ibex/ir/grammar_ir.rbs +41 -27
  373. data/sig/ibex/ir/lexer_ir.rbs +8 -8
  374. data/sig/ibex/ir/parser_contract.rbs +69 -0
  375. data/sig/ibex/ir/serialize.rbs +63 -28
  376. data/sig/ibex/ir/validator/automaton.rbs +47 -44
  377. data/sig/ibex/ir/validator/base.rbs +35 -31
  378. data/sig/ibex/ir/validator/grammar.rbs +78 -67
  379. data/sig/ibex/ir/validator/lexer.rbs +4 -4
  380. data/sig/ibex/ir.rbs +9 -3
  381. data/sig/ibex/lalr/build_metrics.rbs +42 -2
  382. data/sig/ibex/lalr/builder.rbs +75 -24
  383. data/sig/ibex/lalr/conflict_search.rbs +5 -2
  384. data/sig/ibex/lalr/counterexample.rbs +17 -14
  385. data/sig/ibex/lalr/direct_lookaheads.rbs +35 -28
  386. data/sig/ibex/lalr/goto_follows.rbs +89 -0
  387. data/sig/ibex/lalr/ielr/annotator.rbs +67 -0
  388. data/sig/ibex/lalr/ielr/bits.rbs +14 -0
  389. data/sig/ibex/lalr/ielr/inadequacy.rbs +24 -0
  390. data/sig/ibex/lalr/ielr/item_lookaheads.rbs +35 -0
  391. data/sig/ibex/lalr/ielr/pipeline.rbs +17 -0
  392. data/sig/ibex/lalr/ielr/split_stability.rbs +27 -0
  393. data/sig/ibex/lalr/ielr/split_state.rbs +14 -0
  394. data/sig/ibex/lalr/ielr/state_splitter.rbs +76 -0
  395. data/sig/ibex/lalr/ielr_partition.rbs +17 -5
  396. data/sig/ibex/lalr/inadequacy_report.rbs +21 -0
  397. data/sig/ibex/lalr/lookahead_propagation.rbs +38 -0
  398. data/sig/ibex/lalr/lr0_collection.rbs +50 -0
  399. data/sig/ibex/lalr/unreachable_states.rbs +25 -0
  400. data/sig/ibex/lalr.rbs +11 -1
  401. data/sig/ibex/location.rbs +6 -6
  402. data/sig/ibex/lsp/document_handlers.rbs +8 -8
  403. data/sig/ibex/lsp/initialization_handlers.rbs +12 -12
  404. data/sig/ibex/lsp/navigation_handlers.rbs +15 -12
  405. data/sig/ibex/lsp/parser_configuration_assistance.rbs +49 -0
  406. data/sig/ibex/lsp/position_codec.rbs +8 -4
  407. data/sig/ibex/lsp/request_handlers.rbs +4 -4
  408. data/sig/ibex/lsp/request_support.rbs +8 -8
  409. data/sig/ibex/lsp/server.rbs +16 -16
  410. data/sig/ibex/lsp/symbol_index.rbs +20 -21
  411. data/sig/ibex/lsp/symbol_index_builder.rbs +9 -8
  412. data/sig/ibex/lsp/symbol_occurrence.rbs +11 -5
  413. data/sig/ibex/lsp/transport.rbs +18 -6
  414. data/sig/ibex/lsp/workspace.rbs +3 -2
  415. data/sig/ibex/lsp/workspace_analyzer.rbs +1 -1
  416. data/sig/ibex/messages.rbs +26 -0
  417. data/sig/ibex/metrics.rbs +50 -0
  418. data/sig/ibex/normalize/diagnostics.rbs +4 -4
  419. data/sig/ibex/normalize/expander.rbs +2 -2
  420. data/sig/ibex/normalize/expression.rbs +10 -8
  421. data/sig/ibex/normalize/grammar_builder.rbs +11 -0
  422. data/sig/ibex/normalize/inline_expansion.rbs +59 -50
  423. data/sig/ibex/normalize/lexer.rbs +4 -4
  424. data/sig/ibex/normalize/parameter_ebnf_lowering.rbs +15 -14
  425. data/sig/ibex/normalize/parameter_lowering.rbs +18 -18
  426. data/sig/ibex/normalize/parameters.rbs +13 -8
  427. data/sig/ibex/normalize/parser_configuration.rbs +23 -0
  428. data/sig/ibex/normalize.rbs +11 -5
  429. data/sig/ibex/racc_migration/report.rbs +8 -6
  430. data/sig/ibex/rake_task.rbs +1 -1
  431. data/sig/ibex/samples.rbs +13 -4
  432. data/sig/ibex/table_artifact/builder.rbs +93 -0
  433. data/sig/ibex/table_artifact/cst_projection.rbs +20 -0
  434. data/sig/ibex/table_artifact/document.rbs +27 -0
  435. data/sig/ibex/table_artifact/executor.rbs +66 -0
  436. data/sig/ibex/table_artifact/serializer.rbs +25 -0
  437. data/sig/ibex/table_artifact/validator/metadata.rbs +52 -0
  438. data/sig/ibex/table_artifact/validator/support.rbs +46 -0
  439. data/sig/ibex/table_artifact/validator/tables.rbs +63 -0
  440. data/sig/ibex/table_artifact/validator.rbs +66 -0
  441. data/sig/ibex/table_artifact.rbs +32 -0
  442. data/sig/ibex/table_simulation/result.rbs +8 -4
  443. data/sig/ibex/table_simulation/step.rbs +4 -2
  444. data/sig/ibex/tables.rbs +10 -9
  445. data/sig/ibex/verifiable_generation_bundle.rbs +24 -0
  446. data/sig/ibex/verification_report/builder.rbs +53 -0
  447. data/sig/ibex/verification_report/canonical_ir.rbs +41 -0
  448. data/sig/ibex/verification_report/logical_path.rbs +34 -0
  449. data/sig/ibex/verification_report/validator.rbs +114 -0
  450. data/sig/ibex/verification_report.rbs +44 -0
  451. data/sig/ibex/verify/action_correspondence.rbs +50 -0
  452. data/sig/ibex/verify/language_witness.rbs +112 -0
  453. data/sig/ibex/verify/reference_collection.rbs +63 -0
  454. data/sig/ibex/verify/result.rbs +52 -0
  455. data/sig/ibex/verify/verifier.rbs +170 -0
  456. data/sig/ibex/verify.rbs +6 -0
  457. data/sig/ibex/watch/runner.rbs +12 -2
  458. data/sig/ibex/watch/source_snapshot.rbs +13 -9
  459. metadata +254 -12
  460. data/lib/ibex/ir/migration.rb +0 -120
  461. data/sig/ibex/ir/migration.rbs +0 -34
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Ibex
5
+ module BisonImport
6
+ # Iterative scanner for the punctuation needed to recover Bison rules.
7
+ class Tokenizer
8
+ # One structural token with a byte-oriented source position.
9
+ class Token
10
+ attr_reader :type #: Symbol
11
+ attr_reader :value #: String
12
+ attr_reader :line #: Integer
13
+ attr_reader :column #: Integer
14
+
15
+ # @rbs (type: Symbol, value: String, line: Integer, column: Integer) -> void
16
+ def initialize(type:, value:, line:, column:)
17
+ @type = type
18
+ @value = value.freeze
19
+ @line = line
20
+ @column = column
21
+ freeze
22
+ end
23
+ end
24
+
25
+ IDENTIFIER_START = /[A-Za-z_$.]/ #: Regexp
26
+ IDENTIFIER_CONTINUE = /[A-Za-z0-9_$.-]/ #: Regexp
27
+
28
+ # @rbs (String source, start_line: Integer, max_tokens: Integer) -> void
29
+ def initialize(source, start_line:, max_tokens:)
30
+ @source = source
31
+ @index = 0
32
+ @line = start_line
33
+ @column = 1
34
+ @max_tokens = max_tokens
35
+ end
36
+
37
+ # @rbs () -> Array[Token]
38
+ def tokenize
39
+ tokens = [] #: Array[Token]
40
+ until eof?
41
+ skip_ignored
42
+ break if eof?
43
+
44
+ tokens << scan_token
45
+ check_token_budget(tokens.length)
46
+ end
47
+ tokens.freeze
48
+ end
49
+
50
+ private
51
+
52
+ # @rbs () -> Token
53
+ def scan_token
54
+ line = @line
55
+ column = @column
56
+ byte = current_byte || raise(Ibex::Error, "(bison-import):#{line}:#{column}: unexpected end of input")
57
+ if identifier_start?(byte)
58
+ Token.new(type: :symbol, value: scan_identifier, line: line, column: column)
59
+ elsif [34, 39].include?(byte)
60
+ Token.new(type: :literal, value: scan_quoted(byte), line: line, column: column)
61
+ elsif byte == 123
62
+ Token.new(type: :action, value: scan_action, line: line, column: column)
63
+ elsif byte == 37
64
+ Token.new(type: :directive, value: scan_directive, line: line, column: column)
65
+ elsif byte == 60
66
+ Token.new(type: :tag, value: scan_delimited(60, 62), line: line, column: column)
67
+ elsif byte == 91
68
+ Token.new(type: :tag, value: scan_delimited(91, 93), line: line, column: column)
69
+ else
70
+ value = byte ? byte.chr : ""
71
+ advance
72
+ Token.new(type: punctuation_type(value), value: value, line: line, column: column)
73
+ end
74
+ end
75
+
76
+ # @rbs () -> void
77
+ def skip_ignored
78
+ loop do
79
+ advance while whitespace?(current_byte)
80
+ if current_byte == 47 && peek_byte == 42
81
+ skip_block_comment
82
+ elsif (current_byte == 47 && peek_byte == 47) || current_byte == 35
83
+ skip_line
84
+ else
85
+ break
86
+ end
87
+ end
88
+ end
89
+
90
+ # @rbs () -> String
91
+ def scan_identifier
92
+ start = @index
93
+ advance while identifier_continue?(current_byte)
94
+ @source.byteslice(start, @index - start) || ""
95
+ end
96
+
97
+ # @rbs () -> String
98
+ def scan_directive
99
+ start = @index
100
+ advance
101
+ advance if current_byte == 37
102
+ advance while identifier_continue?(current_byte)
103
+ @source.byteslice(start, @index - start) || ""
104
+ end
105
+
106
+ # @rbs (Integer quote) -> String
107
+ def scan_quoted(quote)
108
+ start = @index
109
+ advance
110
+ until eof?
111
+ byte = current_byte
112
+ advance
113
+ if byte == 92
114
+ advance unless eof?
115
+ elsif byte == quote
116
+ break
117
+ end
118
+ end
119
+ @source.byteslice(start, @index - start) || ""
120
+ end
121
+
122
+ # @rbs () -> String
123
+ def scan_action
124
+ advance
125
+ start = @index
126
+ depth = 1
127
+ until eof?
128
+ byte = current_byte
129
+ if skip_action_opaque?(byte)
130
+ next
131
+ elsif byte == 123
132
+ depth += 1
133
+ advance
134
+ elsif byte == 125
135
+ depth -= 1
136
+ if depth.zero?
137
+ value = @source.byteslice(start, @index - start) || ""
138
+ advance
139
+ return value
140
+ end
141
+ advance
142
+ else
143
+ advance
144
+ end
145
+ end
146
+ raise Ibex::Error, "(bison-import):#{@line}:#{@column}: unterminated C action"
147
+ end
148
+
149
+ # @rbs (Integer? byte) -> bool
150
+ def skip_action_opaque?(byte)
151
+ if byte && [34, 39].include?(byte)
152
+ scan_quoted(byte)
153
+ elsif byte == 47 && peek_byte == 42
154
+ skip_block_comment
155
+ elsif byte == 47 && peek_byte == 47
156
+ skip_line
157
+ else
158
+ return false
159
+ end
160
+ true
161
+ end
162
+
163
+ # @rbs (Integer opener, Integer closer) -> String
164
+ def scan_delimited(opener, closer)
165
+ start = @index
166
+ advance
167
+ until eof? || current_byte == closer
168
+ current_byte == 92 ? 2.times { advance unless eof? } : advance
169
+ end
170
+ advance if current_byte == closer
171
+ value = @source.byteslice(start, @index - start) || ""
172
+ return value if value.getbyte(0) == opener
173
+
174
+ ""
175
+ end
176
+
177
+ # @rbs () -> void
178
+ def skip_block_comment
179
+ advance
180
+ advance
181
+ until eof?
182
+ if current_byte == 42 && peek_byte == 47
183
+ advance
184
+ advance
185
+ return
186
+ end
187
+ advance
188
+ end
189
+ end
190
+
191
+ # @rbs () -> void
192
+ def skip_line
193
+ advance until eof? || current_byte == 10
194
+ advance if current_byte == 10
195
+ end
196
+
197
+ # @rbs (String value) -> Symbol
198
+ def punctuation_type(value)
199
+ case value
200
+ when ":" then :colon
201
+ when "|" then :pipe
202
+ when ";" then :semicolon
203
+ else :other
204
+ end
205
+ end
206
+
207
+ # @rbs (Integer? byte) -> bool
208
+ def identifier_start?(byte)
209
+ byte ? IDENTIFIER_START.match?(byte.chr) : false
210
+ end
211
+
212
+ # @rbs (Integer? byte) -> bool
213
+ def identifier_continue?(byte)
214
+ byte ? IDENTIFIER_CONTINUE.match?(byte.chr) : false
215
+ end
216
+
217
+ # @rbs (Integer? byte) -> bool
218
+ def whitespace?(byte)
219
+ !byte.nil? && [9, 10, 11, 12, 13, 32].include?(byte)
220
+ end
221
+
222
+ # @rbs (Integer count) -> void
223
+ def check_token_budget(count)
224
+ return if count <= @max_tokens
225
+
226
+ raise BudgetExceeded.new(
227
+ result: "budget_exhausted", phase: "tokenization", max_tokens: @max_tokens
228
+ )
229
+ end
230
+
231
+ # @rbs () -> Integer?
232
+ def current_byte
233
+ @source.getbyte(@index)
234
+ end
235
+
236
+ # @rbs () -> Integer?
237
+ def peek_byte
238
+ @source.getbyte(@index + 1)
239
+ end
240
+
241
+ # @rbs () -> bool
242
+ def eof?
243
+ @index >= @source.bytesize
244
+ end
245
+
246
+ # @rbs () -> void
247
+ def advance
248
+ return if eof?
249
+
250
+ if current_byte == 10
251
+ @line += 1
252
+ @column = 1
253
+ else
254
+ @column += 1
255
+ end
256
+ @index += 1
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require_relative "error"
5
+ require_relative "ir"
6
+
7
+ module Ibex
8
+ # Clean-room, nonexecuting import of Bison grammar structure.
9
+ module BisonImport
10
+ DIRECTIVES = {
11
+ "token" => :converted,
12
+ "left" => :converted,
13
+ "right" => :converted,
14
+ "nonassoc" => :converted,
15
+ "precedence" => :converted,
16
+ "start" => :converted,
17
+ "expect" => :converted,
18
+ "expect-rr" => :converted,
19
+ "empty" => :converted,
20
+ "prec" => :converted,
21
+ "nterm" => :recognized_metadata,
22
+ "type" => :recognized_metadata,
23
+ "union" => :recognized_metadata,
24
+ "destructor" => :recognized_metadata,
25
+ "printer" => :recognized_metadata,
26
+ "param" => :recognized_metadata,
27
+ "parse-param" => :recognized_metadata,
28
+ "lex-param" => :recognized_metadata,
29
+ "define" => :recognized_metadata,
30
+ "code" => :recognized_metadata,
31
+ "dprec" => :unsupported,
32
+ "merge" => :unsupported,
33
+ "glr-parser" => :unsupported,
34
+ "nondeterministic-parser" => :unsupported,
35
+ "locations" => :unsupported,
36
+ "initial-action" => :unsupported,
37
+ "language" => :unsupported,
38
+ "skeleton" => :unsupported,
39
+ "require" => :unsupported,
40
+ "output" => :unsupported,
41
+ "file-prefix" => :unsupported,
42
+ "name-prefix" => :unsupported,
43
+ "verbose" => :unsupported,
44
+ "yacc" => :unsupported,
45
+ "debug" => :unsupported,
46
+ "token-table" => :unsupported,
47
+ "no-lines" => :unsupported,
48
+ "header" => :unsupported,
49
+ "defines" => :unsupported,
50
+ "error-verbose" => :unsupported,
51
+ "pure-parser" => :unsupported
52
+ }.freeze #: Hash[String, Symbol]
53
+
54
+ FOREIGN_ACTION_SENTINEL = "__ibex_foreign_action_c__" #: String
55
+ STRUCTURAL_STATUS_MARKER = "ibex-bison-structural-status" #: String
56
+ STRUCTURE_NEUTRAL_UNSUPPORTED = %w[
57
+ initial-action locations language skeleton require output file-prefix name-prefix
58
+ verbose yacc debug token-table no-lines header defines error-verbose pure-parser
59
+ ].freeze #: Array[String]
60
+
61
+ class BudgetExceeded < Ibex::Error
62
+ attr_reader :details #: Hash[Symbol, Object?]
63
+
64
+ # @rbs (Hash[Symbol, Object?] details) -> void
65
+ def initialize(details)
66
+ @details = IR.deep_freeze(details)
67
+ super("(bison-import):1:1: configured import budget was exhausted")
68
+ end
69
+ end
70
+
71
+ # One directive occurrence and its original source position.
72
+ class Directive
73
+ attr_reader :name #: String
74
+ attr_reader :status #: Symbol
75
+ attr_reader :line #: Integer
76
+ attr_reader :column #: Integer
77
+ attr_reader :detail #: String
78
+
79
+ # @rbs (name: String, status: Symbol, line: Integer, column: Integer, detail: String) -> void
80
+ def initialize(name:, status:, line:, column:, detail:)
81
+ @name = name.freeze
82
+ @status = status
83
+ @line = line
84
+ @column = column
85
+ @detail = detail.freeze
86
+ freeze
87
+ end
88
+
89
+ # @rbs () -> Hash[Symbol, Object?]
90
+ def to_h
91
+ { name: @name, status: @status, line: @line, column: @column, detail: @detail }
92
+ end
93
+ end
94
+
95
+ # One opaque C action and its mechanical reference rewrite.
96
+ class Action
97
+ attr_reader :id #: Integer
98
+ attr_reader :line #: Integer
99
+ attr_reader :column #: Integer
100
+ attr_reader :original #: String
101
+ attr_reader :transformed #: String
102
+ attr_reader :encoded #: String
103
+
104
+ # @rbs (id: Integer, line: Integer, column: Integer, original: String, transformed: String,
105
+ # encoded: String) -> void
106
+ def initialize(id:, line:, column:, original:, transformed:, encoded:)
107
+ @id = id
108
+ @line = line
109
+ @column = column
110
+ @original = original.freeze
111
+ @transformed = transformed.freeze
112
+ @encoded = encoded.freeze
113
+ freeze
114
+ end
115
+
116
+ # @rbs () -> Hash[Symbol, Object?]
117
+ def to_h
118
+ {
119
+ id: @id, line: @line, column: @column,
120
+ original: @original, transformed: @transformed,
121
+ source_encoding: "hex_sentinel"
122
+ }
123
+ end
124
+ end
125
+
126
+ # Immutable source artifact and versioned analysis report.
127
+ class Result
128
+ attr_reader :source #: String
129
+ attr_reader :file #: String
130
+ attr_reader :class_name #: String
131
+ attr_reader :directives #: Array[Directive]
132
+ attr_reader :actions #: Array[Action]
133
+ attr_reader :rule_count #: Integer
134
+ attr_reader :bounds #: Hash[Symbol, Integer]
135
+
136
+ # @rbs (source: String, file: String, class_name: String, directives: Array[Directive],
137
+ # actions: Array[Action], rule_count: Integer, bounds: Hash[Symbol, Integer]) -> void
138
+ def initialize(source:, file:, class_name:, directives:, actions:, rule_count:, bounds:)
139
+ @source = source.freeze
140
+ @file = file.freeze
141
+ @class_name = class_name.freeze
142
+ @directives = directives.freeze
143
+ @actions = actions.freeze
144
+ @rule_count = rule_count
145
+ @bounds = IR.deep_freeze(bounds)
146
+ freeze
147
+ end
148
+
149
+ # @rbs () -> Hash[Symbol, Object?]
150
+ def to_h
151
+ unsupported = @directives.select { |directive| directive.status == :unsupported }
152
+ structural = structural_unsupported
153
+ {
154
+ ibex_report: "bison_import",
155
+ schema_version: 1,
156
+ result: unsupported.empty? ? "imported" : "imported_with_unsupported",
157
+ file: @file,
158
+ class_name: @class_name,
159
+ generated_source: @source,
160
+ directive_contract: DIRECTIVES.sort.to_h,
161
+ directives: @directives.map(&:to_h),
162
+ unsupported: unsupported.map(&:to_h),
163
+ structurally_complete: structural.empty?,
164
+ structural_unsupported: structural.map(&:to_h),
165
+ actions: @actions.map(&:to_h),
166
+ counts: {
167
+ rules: @rule_count,
168
+ actions: @actions.length,
169
+ unsupported_directives: unsupported.length,
170
+ structural_unsupported_directives: structural.length
171
+ },
172
+ bounds: @bounds,
173
+ statement: "C semantic actions are opaque analysis data; Ruby generation is refused."
174
+ }
175
+ end
176
+
177
+ # @rbs () -> Array[Directive]
178
+ def structural_unsupported
179
+ @directives.select do |directive|
180
+ directive.status == :unsupported &&
181
+ !STRUCTURE_NEUTRAL_UNSUPPORTED.include?(directive.name)
182
+ end
183
+ end
184
+
185
+ # @rbs () -> bool
186
+ def structurally_complete?
187
+ structural_unsupported.empty?
188
+ end
189
+ end
190
+
191
+ # @rbs (String source) -> bool
192
+ def bison_source?(source)
193
+ source.lines.count { |line| line.match?(%r{^\s*%%(?:\s|/|$)}) } >= 2
194
+ end
195
+ module_function :bison_source?
196
+ end
197
+ end
198
+
199
+ require_relative "bison_import/tokenizer"
200
+ require_relative "bison_import/importer"
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ require "tempfile"
5
+
6
+ module Ibex
7
+ # Captures one shell-free child process under wall-clock and output budgets.
8
+ class BoundedSubprocess
9
+ DEFAULT_TIMEOUT_SECONDS = 10 #: Integer
10
+ DEFAULT_MAX_OUTPUT_BYTES = 1_048_576 #: Integer
11
+ TERMINATION_GRACE_SECONDS = 0.25 #: Float
12
+ POLL_SECONDS = 0.01 #: Float
13
+
14
+ # Immutable observation of one bounded child-process execution.
15
+ class Result
16
+ attr_reader :stdout #: String
17
+ attr_reader :stderr #: String
18
+ attr_reader :status #: Process::Status
19
+ attr_reader :timed_out #: bool
20
+ attr_reader :output_limited #: bool
21
+
22
+ # @rbs (stdout: String, stderr: String, status: Process::Status,
23
+ # timed_out: bool, output_limited: bool) -> void
24
+ def initialize(stdout:, stderr:, status:, timed_out:, output_limited:)
25
+ @stdout = stdout.freeze
26
+ @stderr = stderr.freeze
27
+ @status = status
28
+ @timed_out = timed_out
29
+ @output_limited = output_limited
30
+ freeze
31
+ end
32
+ end
33
+
34
+ # @rbs (timeout_seconds: Integer, max_output_bytes: Integer) -> void
35
+ def initialize(timeout_seconds: DEFAULT_TIMEOUT_SECONDS,
36
+ max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES)
37
+ raise ArgumentError, "timeout_seconds must be positive" unless timeout_seconds.positive?
38
+ raise ArgumentError, "max_output_bytes must be positive" unless max_output_bytes.positive?
39
+
40
+ @timeout_seconds = timeout_seconds
41
+ @max_output_bytes = max_output_bytes
42
+ end
43
+
44
+ # @rbs (Array[String] command, input: String) -> Result
45
+ def run(command, input:)
46
+ raise ArgumentError, "command must not be empty" if command.empty?
47
+
48
+ Tempfile.create("ibex-subprocess-input") do |stdin|
49
+ Tempfile.create("ibex-subprocess-stdout") do |stdout|
50
+ Tempfile.create("ibex-subprocess-stderr") do |stderr|
51
+ run_with_files(command, input, stdin, stdout, stderr)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ # @rbs (Array[String] command, String input, File stdin, File stdout, File stderr) -> Result
60
+ def run_with_files(command, input, stdin, stdout, stderr)
61
+ stdin.binmode
62
+ stdin.write(input)
63
+ stdin.flush
64
+ stdin.rewind
65
+ pid = spawn_child(command, stdin, stdout, stderr)
66
+ status, timed_out, output_limited = wait(pid, stdout.path, stderr.path)
67
+ terminate_descendants(pid)
68
+ Result.new(
69
+ stdout: read_bounded(stdout.path), stderr: read_bounded(stderr.path),
70
+ status: status, timed_out: timed_out, output_limited: output_limited
71
+ ).freeze
72
+ ensure
73
+ terminate(pid) if pid && !status
74
+ end
75
+
76
+ # @rbs (Array[String] command, File stdin, File stdout, File stderr) -> Integer
77
+ def spawn_child(command, stdin, stdout, stderr)
78
+ options = { in: stdin, out: stdout, err: stderr } #: Hash[Symbol, Object?]
79
+ options[:pgroup] = true if process_groups?
80
+ spawn(*command, **options)
81
+ end
82
+
83
+ # @rbs (Integer pid, String stdout_path, String stderr_path) ->
84
+ # [Process::Status, bool, bool]
85
+ def wait(pid, stdout_path, stderr_path)
86
+ deadline = monotonic_time + @timeout_seconds
87
+ loop do
88
+ waited = Process.waitpid2(pid, Process::WNOHANG)
89
+ return [waited.last, false, false] if waited
90
+
91
+ return [terminate(pid), false, true] if output_exceeded?(stdout_path, stderr_path)
92
+ return [terminate(pid), true, false] if monotonic_time >= deadline
93
+
94
+ sleep(POLL_SECONDS)
95
+ end
96
+ end
97
+
98
+ # @rbs (Integer pid) -> Process::Status
99
+ def terminate(pid)
100
+ signal_process_tree("TERM", pid)
101
+ deadline = monotonic_time + TERMINATION_GRACE_SECONDS
102
+ loop do
103
+ waited = Process.waitpid2(pid, Process::WNOHANG)
104
+ return waited.last if waited
105
+ break if monotonic_time >= deadline
106
+
107
+ sleep(POLL_SECONDS)
108
+ end
109
+ signal_process_tree("KILL", pid)
110
+ Process.waitpid2(pid).last
111
+ rescue Errno::ESRCH, Errno::ECHILD
112
+ process_status(pid)
113
+ end
114
+
115
+ # @rbs (Integer pid) -> void
116
+ def terminate_descendants(pid)
117
+ return unless process_groups?
118
+ return unless process_group_alive?(pid)
119
+
120
+ deadline = monotonic_time + TERMINATION_GRACE_SECONDS
121
+ Process.kill("TERM", -pid)
122
+ sleep(POLL_SECONDS) while process_group_alive?(pid) && monotonic_time < deadline
123
+ Process.kill("KILL", -pid) if process_group_alive?(pid)
124
+ rescue Errno::ESRCH
125
+ nil
126
+ end
127
+
128
+ # @rbs (String signal, Integer pid) -> void
129
+ def signal_process_tree(signal, pid)
130
+ Process.kill(signal, process_groups? ? -pid : pid)
131
+ end
132
+
133
+ # @rbs (Integer pid) -> bool
134
+ def process_group_alive?(pid)
135
+ Process.kill(0, -pid)
136
+ true
137
+ rescue Errno::ESRCH
138
+ false
139
+ rescue Errno::EPERM
140
+ true
141
+ end
142
+
143
+ # @rbs () -> bool
144
+ def process_groups?
145
+ !RUBY_PLATFORM.match?(/mswin|mingw|cygwin/)
146
+ end
147
+
148
+ # @rbs (Integer pid) -> Process::Status
149
+ def process_status(pid)
150
+ waited = Process.waitpid2(pid, Process::WNOHANG)
151
+ return waited.last if waited
152
+
153
+ raise Ibex::Error, "(subprocess):1:1: child #{pid} exited without a wait status"
154
+ end
155
+
156
+ # @rbs (String stdout_path, String stderr_path) -> bool
157
+ def output_exceeded?(stdout_path, stderr_path)
158
+ File.size(stdout_path) > @max_output_bytes || File.size(stderr_path) > @max_output_bytes
159
+ end
160
+
161
+ # @rbs (String path) -> String
162
+ def read_bounded(path)
163
+ File.binread(path, @max_output_bytes)
164
+ end
165
+
166
+ # @rbs () -> Float
167
+ def monotonic_time
168
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
169
+ end
170
+ end
171
+ end
@@ -12,6 +12,8 @@ module Ibex
12
12
  # private def input_path: (Array[String]) -> String
13
13
  # private def positive_counterexample_limit: (Integer, String) -> Integer
14
14
  # private def normalize_grammar_path: (String) -> IR::Grammar
15
+ # private def configuration_value: (String) -> untyped
16
+ # private def set_configuration_option: (Symbol, Object?) -> void
15
17
 
16
18
  private
17
19
 
@@ -22,9 +24,10 @@ module Ibex
22
24
  return print_help(parser) if @options[:help]
23
25
  raise Ibex::Error, "(cli):1:1: check command requires --ambiguity" unless @options[:check_ambiguity]
24
26
 
25
- grammar = normalize_grammar_path(input_path(remaining))
27
+ grammar = activate_analysis_grammar(normalize_grammar_path(input_path(remaining)))
26
28
  automaton = LALR::Builder.new(
27
- grammar, algorithm: @options[:algorithm] || :lalr, entry_isolation: @options[:entry_isolation] == true
29
+ grammar, algorithm: configuration_value("parser.algorithm"),
30
+ entry_isolation: configuration_value("parser.entries") == :isolated
28
31
  ).build
29
32
  check = Codegen::Ambiguity.new(
30
33
  automaton,
@@ -53,14 +56,20 @@ module Ibex
53
56
  @options[:check_format] = value
54
57
  end
55
58
  options.on("--algorithm=NAME", %w[slr lalr ielr lr1], "parser construction algorithm") do |value|
56
- @options[:algorithm] = value.to_sym
59
+ set_configuration_option(:algorithm, value.to_sym)
57
60
  end
58
61
  options.on("--entry-isolation", "build independent state sets for each start symbol") do
59
- @options[:entry_isolation] = true
62
+ set_configuration_option(:entry_isolation, true)
63
+ end
64
+ options.on("--mode=MODE", %w[default extended], "grammar mode") do |value|
65
+ set_configuration_option(:mode, value.to_sym)
60
66
  end
61
- options.on("--mode=MODE", %w[default extended], "grammar mode") { |value| @options[:mode] = value.to_sym }
62
67
  options.on("--help", "show help") { @options[:help] = true }
63
68
  end
64
69
  end
70
+
71
+ # @rbs!
72
+ # private def activate_analysis_grammar: (IR::Grammar, ?options: Hash[Symbol, Object?],
73
+ # ?explicit_keys: Array[Symbol]) -> IR::Grammar
65
74
  end
66
75
  end