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,199 @@
1
+ # Verifier trust boundary
2
+
3
+ `ibex verify` checks a validated Automaton IR document against expectations
4
+ derived from the Grammar IR embedded in that document. It is a semantic
5
+ cross-check for the current Automaton IR. It is not a proof checker for a
6
+ generated parser, a verifier for arbitrary Ruby, or a security sandbox.
7
+
8
+ ## Input and result boundary
9
+
10
+ The command reads one complete JSON document with `File.binread`.
11
+ `IR::Validator` first parses it and validates its raw shape, schema version,
12
+ symbol/state/production references, and conflict records and summaries. It
13
+ then calls `IR::Serialize.load` to construct immutable Grammar and Automaton
14
+ IR objects. Finally, before returning the Automaton, it recomputes and checks
15
+ the embedded-grammar digest. Those validation and construction steps are a
16
+ prerequisite to, not a substitute for, the semantic checks below.
17
+
18
+ The embedded Grammar IR is the verifier's semantic source of truth. Supplying
19
+ `--grammar=GRAMMAR.json` requires byte-deterministic serialized equality with
20
+ that embedded Grammar IR, but does not bind either IR to an original `.y`
21
+ file. Recomputing `grammar_digest` detects accidental inconsistency inside the
22
+ document; an author who changes both the embedded grammar and its digest has
23
+ created a different self-consistent input, not a verifier failure.
24
+
25
+ A semantic violation produces an invalid result and CLI exit status 1. A
26
+ completed run with no named violation produces a valid result and exit status
27
+ 0 within this document's boundary. Reference-construction exhaustion is a
28
+ separate result with exit status 2; it is neither validity nor a violation.
29
+
30
+ ## Named invariants
31
+
32
+ The report's invariant IDs are stable schema values. “Default” means every
33
+ run; “strict” means the ID is listed only for `--strict`.
34
+
35
+ <!-- verifier-checks:start -->
36
+ | ID | Mode | What the implementation checks |
37
+ | --- | --- | --- |
38
+ | V1 | default | The embedded grammar digest, state item soundness against the algorithm-specific reference, transition shift preservation, nonterminal transition/GOTO agreement, and exact single-candidate ACTION selection. Unsupported programmatic algorithm values are also V1. |
39
+ | V2 | strict | Collection completeness: exact LR(0) core coverage for SLR; exact derived-state/item coverage for LALR(1) and LR(1); and canonical-core lookahead-union coverage for IELR(1). |
40
+ | V3 | default | Every completed SLR item has exactly its independently requested FOLLOW lookaheads (or EOF for an augmented production). Non-completed SLR lookaheads cannot select ACTION cells and are deliberately outside this check. |
41
+ | V4 | default | Default actions are reductions, terminals with no candidate remain errors after expanding defaults and explicit masks, and rebuilt plain/compact ACTION, GOTO, and default rows are equal. A table transformation failure is also V4 in default mode. |
42
+ | V5 | strict | The same rebuilt plain/compact row equality that default mode reports as V4 is classified as V5 in strict mode. Strict mode does not consume a separately supplied table artifact. |
43
+ | V6 | default | Every submitted state is reachable from an entry through submitted transitions, GOTOs, or shift actions, and every embedded-grammar nonterminal derives some terminal sentence. |
44
+ | V7 | default | For each terminal, effective zero-length reductions and their submitted GOTOs do not form a state cycle that can reduce forever without consuming that terminal. |
45
+ | V8 | default | A cell with multiple derived candidates has a declared resolver; resolver records are unique and complete for their token, name only cell candidates, and choose the effective ACTION (or an allowed error). |
46
+ | V9 | strict | For IELR, a bounded canonical-reference witness compares accepted/error outcomes for terminal sequences up to three tokens and fails closed on witness-budget exhaustion. It is a semantic regression witness, not unbounded language equivalence or split-proof verification. Non-IELR algorithms do not execute the witness. |
47
+ <!-- verifier-checks:end -->
48
+
49
+ Default and strict both construct the algorithm-specific reference and both
50
+ rebuild and compare plain and compact rows. Strict adds the V2 completeness
51
+ requirements. It also changes a row mismatch's identifier from V4 to V5; it
52
+ does not add another table representation or inspect generated table bytes.
53
+ In particular, default-mode item checks are soundness/subset checks and can
54
+ accept an incomplete collection that strict mode rejects. For `ielr1`, strict
55
+ mode additionally runs V9. V9 compares the submitted table with a
56
+ verifier-owned canonical LR(1) machine over a finite breadth-first token set;
57
+ a clean result is evidence only within the recorded token and case bounds.
58
+
59
+ ## Supported algorithms and reference cost
60
+
61
+ The CLI validator accepts only the following serialized algorithm identities.
62
+
63
+ <!-- verifier-algorithms:start -->
64
+ | Automaton IR value | Reference and comparison |
65
+ | --- | --- |
66
+ | `slr` | Build the full LR(0) collection. Completed submitted items use FOLLOW sets for lookaheads. Strict mode requires exact LR(0) core coverage. |
67
+ | `lalr1` | Build the full canonical LR(1) collection and union states with the same LR(0) core. Submitted items are subsets by default; strict mode requires the complete merged states. |
68
+ | `ielr1` | Build the full canonical LR(1) collection and its LALR core unions. Each submitted partition must be a subset of its core union; strict mode requires the union of submitted partitions to cover it and runs the bounded V9 acceptance witness. This does not prove unbounded IELR adequacy, the reason for each split, or split witnesses. |
69
+ | `lr1` | Build the full canonical LR(1) collection. Submitted items are subsets by default; strict mode requires the complete canonical states. |
70
+ <!-- verifier-algorithms:end -->
71
+
72
+ Thus LALR(1), IELR(1), and LR(1) verification enumerate the canonical LR(1)
73
+ collection even in default mode. That collection can grow exponentially with
74
+ the grammar. SLR enumerates LR(0), but is still a whole-collection operation.
75
+ The current verifier is consequently a bounded reference oracle, not a
76
+ scale-independent certificate checker. A serialized unknown algorithm is
77
+ rejected structurally before the CLI verifier runs; an Automaton object made
78
+ directly with an unknown value receives V1.
79
+
80
+ The supported `ielr1` check is intentionally narrower than a future direct
81
+ IELR verifier: it checks membership, union coverage, and (in strict mode) a
82
+ bounded acceptance witness. It does not establish conflict preservation,
83
+ canonical state correspondence, or split witnesses. Raw ACTION-cell
84
+ correspondence remains a diagnostic: IELR state merging can produce a
85
+ canonical error cell versus a target reduction cell without an observed
86
+ acceptance difference in the bounded witness.
87
+
88
+ ## Limits and exhaustion
89
+
90
+ The two CLI limits must be positive and apply only to reference collection
91
+ construction.
92
+
93
+ <!-- verifier-limits:start -->
94
+ | Limit | Default | Meaning |
95
+ | --- | ---: | --- |
96
+ | `--max-states` | `100000` | Maximum distinct reference states inserted. |
97
+ | `--max-items` | `1000000` | Work-account limit for augmented seed items and new closure-expansion items. It is not the final collection's unique-item count and does not count every moved GOTO kernel item. |
98
+ <!-- verifier-limits:end -->
99
+
100
+ Exceeding either limit raises `Verify::BudgetExceeded`; the CLI emits
101
+ `result: "budget_exhausted"` with the bounds and exits 2. Strict mode uses the
102
+ same two limits and has no separate completeness budget.
103
+
104
+ These limits do **not** bound input bytes, JSON parsing, IR validation,
105
+ embedded grammar size, submitted state/table size, nullable/FIRST/FOLLOW
106
+ analysis, table compression, all verifier loops, wall-clock time, Ruby heap,
107
+ or process resources. An operating-system failure, timeout, or memory
108
+ exhaustion is not converted into a verifier budget result. Callers handling
109
+ hostile inputs need an outer byte/time/memory/process boundary.
110
+
111
+ ## Trusted computing base and independence
112
+
113
+ Here, “independent” means only that `Verify::ReferenceCollection` implements
114
+ its own LR(0)/LR(1) closure, GOTO, and collection traversal and does not require
115
+ or call `LALR::Builder`. It does not mean independently authored, third-party
116
+ reviewed, formally verified, cryptographically certified, or free of shared
117
+ implementation defects.
118
+
119
+ The result trusts all of the following:
120
+
121
+ | Trusted component or input | Why it is in the TCB |
122
+ | --- | --- |
123
+ | Embedded Grammar IR | It defines the grammar against which the submitted automaton is checked. |
124
+ | `IR::Validator`, `IR::Serialize`, IR migration and IR model classes | They validate, decode, canonicalize, hash, and expose the input. |
125
+ | `Verify::Verifier`, `Verify::ReferenceCollection`, and result classes | They derive and evaluate all named semantic invariants. |
126
+ | `Analysis::Sets` | It supplies nullable, FIRST, and FOLLOW data. The parser builder also uses this component, so defects here can be correlated. |
127
+ | `Tables.build`, `Tables::Compact`, and `Tables::CompactActions` | The verifier rebuilds both in-memory table views with production table code. Plain/compact comparison is therefore not an independent compact-table implementation. |
128
+ | Ruby runtime and standard library (`JSON`, `Set`, `Digest`, file I/O) | They parse, enumerate, compare, hash, and allocate the checked data. |
129
+ | Host resource isolation | The verifier itself supplies only the two reference-work limits above. |
130
+
131
+ Excluding `LALR::Builder` is useful deliberate duplication, but the shared
132
+ sets, IR, table transformation, language runtime, and embedded input remain in
133
+ the TCB. A valid report is evidence against the implemented fault model, not
134
+ an unqualified correctness proof.
135
+
136
+ ## Explicit non-goals
137
+
138
+ <!-- verifier-non-goals:start -->
139
+ | Boundary | What is not verified |
140
+ | --- | --- |
141
+ | `source-to-ir` | Correct parsing or normalization of the original grammar source, or provenance from that source to the embedded Grammar IR. |
142
+ | `generated-ruby` | Generated Ruby source, wrapper structure, digests, loading behavior, or binding between generated output and this Automaton IR. |
143
+ | `runtime` | The parser runtime implementation, actual parser execution, recovery behavior under application callbacks, or plain/compact behavior after code generation. |
144
+ | `semantic-actions` | Meaning, type safety, termination, side effects, or correctness of parser actions and user-code sections. Their text is opaque and is never evaluated. |
145
+ | `lexer-actions` | Lexer IR, tokenization, generated lexer actions, handwritten lexers, or parser-to-lexer feedback. |
146
+ | `resolver-policy` | Whether a resolver's claimed `by` reason correctly applies grammar precedence/associativity policy; V8 checks record/candidate/selected-cell consistency. |
147
+ | `grammar-properties` | Unambiguity, language equivalence to another grammar/parser, completeness of `%expect` as a design claim, or application semantics. |
148
+ | `ielr-adequacy` | Unbounded language equivalence, conflict preservation, why an IELR state was split, or a proof that every split has the required canonical correspondence. Strict V9 supplies only a bounded acceptance witness. |
149
+ | `data-only-artifact` | The standalone `ibex verify` command does not consume a supplied executable table artifact, compact-table bytes, generated bundle, manifest/report binding, or artifact digest. The current Automaton IR contains states and actions, and the verifier rebuilds table views from them. The separate bundle report records this verifier result and binds table identities without widening V1-V9. |
150
+ | `security` | Authenticity, trusted publication, signatures, sandboxing, confidentiality, or availability under hostile resource use. |
151
+ <!-- verifier-non-goals:end -->
152
+
153
+ Opaque action and user-code strings participate in serialized Grammar IR and
154
+ therefore its digest, but checking that self-contained digest is not semantic
155
+ verification of the code and never executes it.
156
+
157
+ The data-only table and scoped bundle are separate layers around this
158
+ verifier. Their validators check closed formats and digest relationships, but
159
+ they do not make the standalone V1-V9 implementation consume or independently
160
+ verify sidecar bytes. Consequently `ibex verify` must still not be described
161
+ as validating generated Ruby, table artifacts, or manifests.
162
+
163
+ ## Committed fault corpus
164
+
165
+ `test/verify/verifier_test.rb` mutates a generated document in twenty named
166
+ ways while keeping it acceptable to `IR::Validator`, then requires strict
167
+ verification to reject it. The table records the invariant IDs currently
168
+ observed for each mutation. Multiple IDs mean one mutation violates more than
169
+ one invariant; they do not turn the finite corpus into completeness evidence.
170
+
171
+ <!-- verifier-faults:start -->
172
+ | Fault | Observed invariant IDs |
173
+ | --- | --- |
174
+ | `add_lookahead` | V1, V2 |
175
+ | `remove_item` | V1, V2 |
176
+ | `remove_lookahead` | V2 |
177
+ | `move_item_dot` | V1, V2 |
178
+ | `remove_transition` | V4 |
179
+ | `redirect_transition` | V1 |
180
+ | `remove_action` | V1 |
181
+ | `redirect_shift` | V1 |
182
+ | `replace_reduction` | V1 |
183
+ | `remove_goto` | V1 |
184
+ | `redirect_goto` | V1 |
185
+ | `add_default` | V4 |
186
+ | `remove_default` | V1 |
187
+ | `remove_error_mask` | V4 |
188
+ | `remove_conflict` | V8 |
189
+ | `duplicate_conflict` | V8 |
190
+ | `redirect_conflict_shift` | V8 |
191
+ | `change_conflict_choice` | V8 |
192
+ | `add_unreachable_state` | V1, V6 |
193
+ | `epsilon_cycle` | V1, V4, V7 |
194
+ <!-- verifier-faults:end -->
195
+
196
+ The corpus demonstrates detection of these twenty constructed, structurally
197
+ valid mutations on its committed grammars and construction harness. It does
198
+ not establish that all corruptions, all combinations of faults, or faults in
199
+ the TCB are detected.
data/docs/workloads.md ADDED
@@ -0,0 +1,132 @@
1
+ # Public workload and problem registry
2
+
3
+ [`workloads.yml`](workloads.yml) is the machine-readable authority for workload
4
+ identity, source and license evidence, structural measurements, known problems,
5
+ workarounds, and benchmark eligibility. It is a planning registry, not a claim
6
+ that every grammar is fully supported or that every eligible scope has a
7
+ published result.
8
+
9
+ ## Source classes
10
+
11
+ The `classification` field prevents unlike evidence from being combined:
12
+
13
+ | Class | Meaning |
14
+ | --- | --- |
15
+ | `public_real` | A public third-party application grammar with an exact repository revision. |
16
+ | `external_real` | A public third-party grammar used by the download-only Bison import gate. |
17
+ | `repository_real` | A production Ibex workload, currently the self-hosted frontend. |
18
+ | `repository_synthetic` | A repository-authored correctness fixture; it is not adoption or public-language evidence. |
19
+
20
+ In particular, the gallery is `repository_synthetic`. Its results can support
21
+ correctness, fuzzing, and verifier claims, but cannot by themselves establish a
22
+ benefit for Namae, BCDice, Nokogiri, CRuby, jq, PHP, or PostgreSQL. The three
23
+ `public_real` workloads may support only the scopes listed in their eligibility
24
+ records and only after the benchmark's checkout and behavior checks pass.
25
+
26
+ ## Counting contract
27
+
28
+ `ibex-normalized-lalr-v1` parses the exact pinned bytes in their admitted mode,
29
+ normalizes them to Grammar IR, counts user productions and all normalized
30
+ terminals (including `$eof` and `error`), then builds the direct LALR automaton
31
+ and counts states and unresolved conflicts. Bison-family sources are first
32
+ processed by the Ibex importer, which strips actions and reports unsupported
33
+ directives.
34
+
35
+ `measured` means that the complete grammar admitted by that method was counted.
36
+ `not_measured` requires a reason and carries no numeric value. The pinned current
37
+ CRuby Lrama grammar has 22 structural import gaps, so its partial importer
38
+ figures are deliberately not promoted to production, token, state, or conflict
39
+ counts.
40
+
41
+ The registry validator recomputes repository-owned grammar counts, cross-checks
42
+ the three public identities, grammar digests, production/state/token counts,
43
+ and conflicts against
44
+ [`benchmark/public_workloads.json`](../benchmark/public_workloads.json), and
45
+ cross-checks Bison identities and every structurally complete expected metric,
46
+ including tokens, against
47
+ [`tool/quality/bison_external.rb`](../tool/quality/bison_external.rb). It does
48
+ not download third-party source in ordinary CI.
49
+
50
+ Remote source, license, and permission records have a second reviewed binding in
51
+ [`tool/quality/workloads/evidence.yml`](../tool/quality/workloads/evidence.yml).
52
+ The validator requires exact owner/project/revision/path-derived GitHub
53
+ locators, license expressions, evidence digests, and classification-appropriate
54
+ permission states to match that fixture. Repository-owned evidence is instead
55
+ checked against both the current file and its pinned Git object.
56
+
57
+ ## Registered workloads
58
+
59
+ Counts are production/state/token. `n/m` means `not_measured`, not zero.
60
+
61
+ | Stable workload ID | Class | Grammar | Counts | Conflicts SR/RR | Benchmark status and scope |
62
+ | --- | --- | --- | ---: | ---: | --- |
63
+ | `bcdice-command-parser` | `public_real` | BCDice command parser | 34/56/19 | 2/0 | eligible: public generation/runtime |
64
+ | `bison-gnu-calc` | `external_real` | GNU Bison C calculator | 13/22/11 | 0/0 | diagnostic only: Bison import compatibility |
65
+ | `bison-jq-parser` | `external_real` | jq parser | 167/311/100 | 408/0 | diagnostic only: import/conflict analysis |
66
+ | `bison-php-parser` | `external_real` | PHP Zend language parser | 635/1203/185 | 0/0 | diagnostic only: import/scale construction |
67
+ | `bison-postgresql-parser` | `external_real` | PostgreSQL backend SQL grammar | 3640/6942/562 | 0/0 | diagnostic only: import/scale construction |
68
+ | `bison-ruby-bison-era` | `external_real` | CRuby Bison-era parse.y | 781/1303/158 | 0/0 | diagnostic only: import/external state comparison |
69
+ | `bison-ruby-current-lrama` | `external_real` | Pinned current CRuby Lrama parse.y | n/m | n/m | ineligible: structurally incomplete import |
70
+ | `gallery-calc` | `repository_synthetic` | Calculator fixture | 7/15/9 | 0/0 | eligible: correctness/fuzzing/verification |
71
+ | `gallery-json` | `repository_synthetic` | JSON fixture | 17/27/13 | 0/0 | eligible: correctness/fuzzing/verification |
72
+ | `gallery-sql-lite` | `repository_synthetic` | SQL-subset fixture | 14/26/14 | 0/0 | eligible: correctness/fuzzing/verification |
73
+ | `ibex-frontend` | `repository_real` | Production self-hosted grammar | 146/227/60 | 0/0 | eligible: self-host/regression |
74
+ | `namae-parser` | `public_real` | Namae name parser | 51/78/12 | 0/0 | eligible: public generation/runtime |
75
+ | `nokogiri-css-parser` | `public_real` | Nokogiri CSS parser | 81/117/32 | 0/1 | eligible: public generation/runtime |
76
+
77
+ The exact owner/project, full revision, path, source SHA-256, license expression
78
+ and evidence digest, public-source permission evidence, feature inventory,
79
+ current pain, current workaround, and eligibility reasons live in the YAML
80
+ record. Third-party license locators include the full pinned revision; their
81
+ digests were calculated from those exact bytes. The registry records evidence
82
+ and does not replace a project's license terms or legal review.
83
+
84
+ ## Stable problem IDs
85
+
86
+ Future design and profiling work should cite a problem and at least one workload
87
+ ID instead of saying only that a change helps “real grammars.”
88
+
89
+ | Stable problem ID | Current evidence boundary |
90
+ | --- | --- |
91
+ | `problem-bison-dialect-coverage` | Every pinned Bison-family import reports at least one unsupported directive; current CRuby has structural gaps. |
92
+ | `problem-conflict-volume` | `bison-jq-parser` has 408 unresolved shift/reduce conflicts in the measured imported direct-LALR automaton. This is not a claim about jq's production parser behavior. |
93
+ | `problem-public-runtime-performance` | The evidence-pending historical projection retains slower cold-generation and new-instance rows for the three `public_real` workloads. |
94
+
95
+ For example, a direct-IELR proposal can cite
96
+ `problem-conflict-volume` + `bison-jq-parser`, but it must still establish that
97
+ the imported conflicts are semantically meaningful before claiming an
98
+ application benefit. A runtime optimization can cite
99
+ `problem-public-runtime-performance` plus the affected public workload IDs and
100
+ must publish a new environment-bound comparison artifact before making a
101
+ current performance claim.
102
+
103
+ The H005 [construction profile](construction-profiling.md) consumes these exact
104
+ identities and keeps unavailable public checkouts as `not_run`. Synthetic and
105
+ repository-real construction observations never stand in for third-party
106
+ evidence.
107
+
108
+ ## Updating the registry
109
+
110
+ Run:
111
+
112
+ ```sh
113
+ bundle exec rake quality:workloads
114
+ bundle exec ruby -Itest test/quality/workloads_test.rb
115
+ ```
116
+
117
+ For a repository grammar, update its revision to a commit that already contains
118
+ the recorded source bytes, update its digest, and let the validator recompute
119
+ counts. The initial repository entries intentionally use pre-registry revision
120
+ `d901b787c7e10c1ab3344ac0156f802ab29ad207`, avoiding a self-referential
121
+ registry commit.
122
+
123
+ For a public benchmark grammar, change
124
+ `benchmark/public_workloads.json` and this registry together. For a Bison input,
125
+ change the source constant, digest, expected measurements, exact-revision
126
+ license evidence, and registry together. Any remote source/license change also
127
+ requires an explicitly reviewed update to `tool/quality/workloads/evidence.yml`.
128
+ When a pinned public checkout is supplied, the benchmark recomputes and rejects
129
+ drift in all five structural metric families. A moving branch, tag without a
130
+ resolved commit, short SHA, missing/changed local file, digest mismatch,
131
+ unexplained numeric placeholder, or eligible record without complete count,
132
+ license, and permission evidence fails the quality gate.
@@ -0,0 +1,60 @@
1
+ # Ibex examples
2
+
3
+ These examples pair an Ibex grammar with either the declarative lexer DSL or a
4
+ small handwritten lexer. Generate any example from the repository
5
+ root, then run the generated file:
6
+
7
+ ```sh
8
+ bundle exec ruby -Ilib exe/ibex examples/calculator.y
9
+ bundle exec ruby -Ilib examples/calculator.rb "2 + 3 * (4 - 1)"
10
+ ```
11
+
12
+ The generated `.rb` file is disposable. The `.y` file is the maintained
13
+ source.
14
+
15
+ Each grammar also keeps accept/reject source examples beside its rules. Run all
16
+ of them without generating repository files. The task also requires 100%
17
+ production coverage from the declared cases:
18
+
19
+ ```sh
20
+ bundle exec rake grammar:test
21
+ ```
22
+
23
+ ## Included grammars
24
+
25
+ - `calculator.y` evaluates integer arithmetic with parentheses.
26
+ - `csv.y` parses rows and quoted fields with a generated lexer.
27
+ - `json.y` parses JSON values into Ruby Hashes, Arrays, Strings, numbers,
28
+ booleans, and `nil`. It demonstrates the generated lexer, including conversion
29
+ actions and punctuation emission.
30
+ - `ini.y` parses sections and key/value entries into a nested Hash.
31
+ - `tiny_language.y` parses assignments, arithmetic, and `print` statements,
32
+ then executes the resulting small AST.
33
+
34
+ The JSON, INI, and tiny-language runners read standard input:
35
+
36
+ ```sh
37
+ printf '%s\n' '{"name":"Ibex","values":[1,true,null]}' |
38
+ bundle exec ruby -Ilib examples/json.rb
39
+ ```
40
+
41
+ ## Lexer integration patterns
42
+
43
+ The JSON example declares `lexer ... end`; generated `parse` accepts String,
44
+ IO, and Fiber input. Rules are anchored, choose the longest match, and use
45
+ declaration order for ties.
46
+
47
+ Handwritten lexers remain useful when an application already owns tokenization.
48
+ They implement `next_token`, returning `[token, semantic_value]`; `false` or
49
+ `nil` marks EOF. The remaining examples demonstrate two `StringScanner`
50
+ patterns:
51
+
52
+ 1. calculator and tiny language scan the source incrementally and
53
+ return one token per `next_token` call;
54
+ 2. INI tokenizes line-oriented records into a small queue before parsing.
55
+
56
+ Quoted grammar terminals such as `'+'` are returned as Strings. Declared bare
57
+ tokens such as `NUMBER` are returned as Symbols. A production lexer can retain
58
+ offset or line information alongside its own semantic values and use that
59
+ information in application diagnostics; these examples keep the contract
60
+ small enough to read in one file.
@@ -0,0 +1,44 @@
1
+ class Examples::CalculatorParser
2
+ pragma extended
3
+ token NUMBER
4
+ %test accept "1+2*3"
5
+ %test accept "8-3"
6
+ %test accept "8/2"
7
+ %test accept "(1)"
8
+ %test reject "1+"
9
+ rule
10
+ expression : expression '+' term { result = val[0] + val[2] }
11
+ | expression '-' term { result = val[0] - val[2] }
12
+ | term { result = val[0] }
13
+ term : term '*' factor { result = val[0] * val[2] }
14
+ | term '/' factor { result = val[0] / val[2] }
15
+ | factor { result = val[0] }
16
+ factor : NUMBER { result = val[0] }
17
+ | '(' expression ')' { result = val[1] }
18
+ end
19
+ ---- header
20
+ require "strscan"
21
+ ---- inner
22
+ def parse(source)
23
+ @scanner = StringScanner.new(source)
24
+ do_parse
25
+ end
26
+
27
+ def next_token
28
+ @scanner.skip(/\s+/)
29
+ return false if @scanner.eos?
30
+
31
+ if (number = @scanner.scan(/\d+/))
32
+ [:NUMBER, Integer(number, 10)]
33
+ elsif (operator = @scanner.scan(/[()+*\/-]/))
34
+ [operator, nil]
35
+ else
36
+ raise ArgumentError, "unexpected calculator input at offset #{@scanner.pos}"
37
+ end
38
+ end
39
+ ---- footer
40
+ if $PROGRAM_NAME == __FILE__
41
+ abort "usage: ruby calculator.rb EXPRESSION" if ARGV.empty?
42
+
43
+ puts Examples::CalculatorParser.new.parse(ARGV.join(" "))
44
+ end
data/examples/csv.y ADDED
@@ -0,0 +1,32 @@
1
+ class Examples::CSVParser
2
+ pragma extended
3
+ token FIELD NEWLINE
4
+ %test accept "name,age\n\"Doe, Jane\",36\n"
5
+ %test reject "name,age"
6
+ lexer
7
+ FIELD /"(?:[^"]|"")*"|[^,\r\n]+/ { |source| decode_csv_field(source) }
8
+ NEWLINE /\r\n|\n|\r/
9
+ on /,/ { |source| emit source, nil }
10
+ end
11
+ rule
12
+ document : rows { result = val[0] }
13
+ rows : rows row { result = val[0] + [val[1]] }
14
+ | row { result = [val[0]] }
15
+ row : fields NEWLINE { result = val[0] }
16
+ fields : fields ',' field { result = val[0] + [val[2]] }
17
+ | field { result = [val[0]] }
18
+ field : FIELD { result = val[0] }
19
+ end
20
+ ---- inner
21
+ def parse(source, file: "(csv)") = super
22
+
23
+ def decode_csv_field(source)
24
+ return source unless source.start_with?('"')
25
+
26
+ source[1...-1].gsub('""', '"')
27
+ end
28
+ ---- footer
29
+ if $PROGRAM_NAME == __FILE__
30
+ require "json"
31
+ puts JSON.generate(Examples::CSVParser.new.parse(ARGF.read))
32
+ end
data/examples/ini.y ADDED
@@ -0,0 +1,70 @@
1
+ class Examples::INIParser
2
+ pragma extended
3
+ token SECTION KEY VALUE NEWLINE
4
+ %test accept "[app]\nname=ibex\n"
5
+ %test accept "\n"
6
+ rule
7
+ document : lines { result = build_document(val[0]) }
8
+ lines : lines line { result = val[1] ? val[0] + [val[1]] : val[0] }
9
+ | { result = [] }
10
+ line : SECTION NEWLINE { result = [:section, val[0]] }
11
+ | KEY '=' VALUE NEWLINE { result = [:entry, val[0], val[2]] }
12
+ | NEWLINE { result = nil }
13
+ end
14
+ ---- header
15
+ require "strscan"
16
+ ---- inner
17
+ def parse(source)
18
+ @tokens = tokenize_ini(source)
19
+ do_parse
20
+ end
21
+
22
+ def next_token
23
+ @tokens.shift || false
24
+ end
25
+
26
+ def tokenize_ini(source)
27
+ source.lines.flat_map.with_index(1) do |line, line_number|
28
+ scanner = StringScanner.new(line.chomp)
29
+ scanner.skip(/\s*/)
30
+ if scanner.eos? || scanner.peek(1).match?(/[;#]/)
31
+ [[:NEWLINE, nil]]
32
+ elsif scanner.scan(/\[/)
33
+ name = scanner.scan(/[^\]]+/)&.strip
34
+ closer = scanner.scan(/\]/)
35
+ scanner.skip(/\s*/)
36
+ invalid_ini_line!(line_number) unless name && !name.empty? && closer && scanner.eos?
37
+ [[:SECTION, name], [:NEWLINE, nil]]
38
+ else
39
+ key = scanner.scan(/[A-Za-z0-9_.-]+/)
40
+ scanner.skip(/\s*/)
41
+ separator = scanner.scan(/=/)
42
+ scanner.skip(/\s*/)
43
+ value = scanner.rest.strip
44
+ invalid_ini_line!(line_number) unless key && separator
45
+ [[:KEY, key], ["=", nil], [:VALUE, value], [:NEWLINE, nil]]
46
+ end
47
+ end
48
+ end
49
+
50
+ def invalid_ini_line!(line_number)
51
+ raise ArgumentError, "invalid INI input on line #{line_number}"
52
+ end
53
+
54
+ def build_document(records)
55
+ document = {}
56
+ current = document
57
+ records.each do |record|
58
+ if record[0] == :section
59
+ current = (document[record[1]] ||= {})
60
+ else
61
+ current[record[1]] = record[2]
62
+ end
63
+ end
64
+ document
65
+ end
66
+ ---- footer
67
+ if $PROGRAM_NAME == __FILE__
68
+ require "json"
69
+ puts JSON.generate(Examples::INIParser.new.parse(ARGF.read))
70
+ end
data/examples/json.y ADDED
@@ -0,0 +1,48 @@
1
+ class Examples::JSONParser
2
+ pragma extended
3
+ token STRING NUMBER TRUE FALSE NULL
4
+ %test accept "{\"ok\":[true,null]}"
5
+ %test accept "{\"object\":{},\"array\":[],\"string\":\"ibex\",\"number\":1,\"false\":false,\"many\":[1,2]}"
6
+ %test reject "{\"ok\":}"
7
+ lexer
8
+ skip /\s+/
9
+ STRING /"(?:\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})|[^"\\])*"/ { |source| decode_json_string(source) }
10
+ NUMBER /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/ {
11
+ |source| source.match?(/[.eE]/) ? Float(source) : Integer(source, 10)
12
+ }
13
+ TRUE /true/ { true }
14
+ FALSE /false/ { false }
15
+ NULL /null/ { nil }
16
+ on /[{}\[\],:]/ { |source| emit source, nil }
17
+ end
18
+ rule
19
+ document : value { result = val[0] }
20
+ value : object { result = val[0] }
21
+ | array { result = val[0] }
22
+ | STRING { result = val[0] }
23
+ | NUMBER { result = val[0] }
24
+ | TRUE { result = true }
25
+ | FALSE { result = false }
26
+ | NULL { result = nil }
27
+ object : '{' '}' { result = {} }
28
+ | '{' members '}' { result = val[1] }
29
+ members : pair { result = { val[0][0] => val[0][1] } }
30
+ | members ',' pair { result = val[0].merge(val[2][0] => val[2][1]) }
31
+ pair : STRING ':' value { result = [val[0], val[2]] }
32
+ array : '[' ']' { result = [] }
33
+ | '[' elements ']' { result = val[1] }
34
+ elements : value { result = [val[0]] }
35
+ | elements ',' value { result = val[0] + [val[2]] }
36
+ end
37
+ ---- header
38
+ require "json"
39
+ ---- inner
40
+ def parse(source, file: "(json)") = super
41
+
42
+ def decode_json_string(source)
43
+ JSON.parse(source)
44
+ end
45
+ ---- footer
46
+ if $PROGRAM_NAME == __FILE__
47
+ puts JSON.generate(Examples::JSONParser.new.parse(ARGF.read))
48
+ end
@@ -0,0 +1,75 @@
1
+ class Examples::TinyLanguageParser
2
+ pragma extended
3
+ token IDENTIFIER NUMBER PRINT
4
+ %test accept "value=1+2*3; print value;"
5
+ %test accept "value=(8-2)/3;"
6
+ %test reject "print ;"
7
+ rule
8
+ program : statements { result = val[0] }
9
+ statements : statements statement { result = val[0] + [val[1]] }
10
+ | { result = [] }
11
+ statement : IDENTIFIER '=' expression ';' { result = [:assign, val[0], val[2]] }
12
+ | PRINT expression ';' { result = [:print, val[1]] }
13
+ expression : expression '+' term { result = [:add, val[0], val[2]] }
14
+ | expression '-' term { result = [:subtract, val[0], val[2]] }
15
+ | term { result = val[0] }
16
+ term : term '*' factor { result = [:multiply, val[0], val[2]] }
17
+ | term '/' factor { result = [:divide, val[0], val[2]] }
18
+ | factor { result = val[0] }
19
+ factor : NUMBER { result = [:number, val[0]] }
20
+ | IDENTIFIER { result = [:variable, val[0]] }
21
+ | '(' expression ')' { result = val[1] }
22
+ end
23
+ ---- header
24
+ require "strscan"
25
+ ---- inner
26
+ def parse(source)
27
+ @scanner = StringScanner.new(source)
28
+ do_parse
29
+ end
30
+
31
+ def next_token
32
+ @scanner.skip(/\s+/)
33
+ return false if @scanner.eos?
34
+
35
+ if @scanner.scan(/print\b/)
36
+ [:PRINT, "print"]
37
+ elsif (identifier = @scanner.scan(/[A-Za-z_][A-Za-z0-9_]*/))
38
+ [:IDENTIFIER, identifier]
39
+ elsif (number = @scanner.scan(/\d+/))
40
+ [:NUMBER, Integer(number, 10)]
41
+ elsif (punctuation = @scanner.scan(/[=;()+*\/-]/))
42
+ [punctuation, nil]
43
+ else
44
+ raise ArgumentError, "unexpected language input at offset #{@scanner.pos}"
45
+ end
46
+ end
47
+
48
+ def execute(statements, output: $stdout)
49
+ environment = {}
50
+ statements.each do |statement|
51
+ case statement[0]
52
+ when :assign
53
+ environment[statement[1]] = evaluate_expression(statement[2], environment)
54
+ when :print
55
+ output.puts(evaluate_expression(statement[1], environment))
56
+ end
57
+ end
58
+ environment
59
+ end
60
+
61
+ def evaluate_expression(expression, environment)
62
+ case expression[0]
63
+ when :number then expression[1]
64
+ when :variable then environment.fetch(expression[1])
65
+ when :add then evaluate_expression(expression[1], environment) + evaluate_expression(expression[2], environment)
66
+ when :subtract then evaluate_expression(expression[1], environment) - evaluate_expression(expression[2], environment)
67
+ when :multiply then evaluate_expression(expression[1], environment) * evaluate_expression(expression[2], environment)
68
+ when :divide then evaluate_expression(expression[1], environment) / evaluate_expression(expression[2], environment)
69
+ end
70
+ end
71
+ ---- footer
72
+ if $PROGRAM_NAME == __FILE__
73
+ parser = Examples::TinyLanguageParser.new
74
+ parser.execute(parser.parse(ARGF.read))
75
+ end