expressir 1.2.11-aarch64-linux → 2.4.29-aarch64-linux

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 (467) hide show
  1. checksums.yaml +4 -4
  2. data/.cargo/config.toml +3 -0
  3. data/.github/scripts/test_installed_gem.rb +34 -0
  4. data/.github/workflows/codeql.yml +35 -0
  5. data/.github/workflows/docs.yml +99 -0
  6. data/.github/workflows/links.yml +100 -0
  7. data/.github/workflows/native-gems.yml +201 -0
  8. data/.github/workflows/rake.yml +10 -296
  9. data/.github/workflows/release.yml +26 -114
  10. data/.github/workflows/rust-ext.yml +41 -0
  11. data/.github/workflows/stress.yml +36 -0
  12. data/.github/workflows/validate_schemas.yml +48 -0
  13. data/.github/workflows/verify_remarks.yml +33 -0
  14. data/.gitignore +33 -1
  15. data/.rubocop.yml +11 -11
  16. data/.rubocop_todo.yml +297 -0
  17. data/CHANGELOG.md +159 -0
  18. data/Gemfile +15 -1
  19. data/README.adoc +2675 -47
  20. data/Rakefile +34 -10
  21. data/TODO.bugs/01-stale-transformer-autoload.md +39 -0
  22. data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
  23. data/TODO.bugs/03-builder-mutable-state.md +43 -0
  24. data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
  25. data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
  26. data/TODO.bugs/06-collection-registry-single-source.md +53 -0
  27. data/TODO.bugs/07-require-relative-cleanup.md +42 -0
  28. data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
  29. data/TODO.bugs/09-parser-split.md +53 -0
  30. data/TODO.bugs/10-to-s-override.md +42 -0
  31. data/TODO.bugs/11-parser-class-variables.md +39 -0
  32. data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
  33. data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
  34. data/TODO.bugs/14-model-formatting-leak.md +30 -0
  35. data/TODO.bugs/15-expression-children-macro.md +27 -0
  36. data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
  37. data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
  38. data/TODO.bugs/18-const-get-private-constants.md +30 -0
  39. data/TODO.bugs/19-format-methods-public.md +22 -0
  40. data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
  41. data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
  42. data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
  43. data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
  44. data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
  45. data/TODO.bugs/25-debug-puts-in-production.md +21 -0
  46. data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
  47. data/TODO.bugs/27-package-build-god-method.md +19 -0
  48. data/TODO.bugs/28-package-god-class.md +30 -0
  49. data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
  50. data/TODO.bugs/30-unicode-map-extraction.md +19 -0
  51. data/TODO.bugs/README.md +43 -0
  52. data/TODO.max-perf/01-restore-ci-green.md +36 -0
  53. data/TODO.max-perf/02-streaming-parse-path.md +31 -0
  54. data/TODO.max-perf/03-cli-parallel-opt-in.md +27 -0
  55. data/TODO.max-perf/04-benchmark-harness.md +28 -0
  56. data/TODO.max-perf/05-parallel-fidelity-specs.md +22 -0
  57. data/TODO.max-perf/06-builder-cpu-audit.md +41 -0
  58. data/TODO.max-perf/07-upstream-parsanol-roadmap.md +27 -0
  59. data/TODO.max-perf/08-builder-build-perf.md +45 -0
  60. data/TODO.max-perf/09-grammar-cold-start.md +25 -0
  61. data/TODO.max-perf/10-parser-facade-hygiene.md +23 -0
  62. data/TODO.max-perf/11-ci-green-closeout.md +25 -0
  63. data/TODO.max-perf/12-require-boot-profile.md +25 -0
  64. data/TODO.max-perf/13-key-conversion-specs.md +26 -0
  65. data/TODO.max-perf/14-builder-call-handler-audit.md +28 -0
  66. data/TODO.parity-ee/00-overview.md +76 -0
  67. data/TODO.parity-ee/01-annex-g-rule-extraction.md +41 -0
  68. data/TODO.parity-ee/02-eeng-algorithm-inventory.md +8 -0
  69. data/TODO.parity-ee/03-eeng-oracle-harness.md +33 -0
  70. data/TODO.parity-ee/04-shtolo-concatenate.md +44 -0
  71. data/TODO.parity-ee/05-shtolo-longform-flatten.md +53 -0
  72. data/TODO.parity-ee/06-interface-scheduling-parity.md +47 -0
  73. data/TODO.parity-ee/07-semantic-checks-port.md +35 -0
  74. data/TODO.parity-ee/08-pretty-roundtrip-gate.md +27 -0
  75. data/TODO.parity-ee/09-smrl-index-and-listing.md +22 -0
  76. data/TODO.parity-ee/10-interface-dot-graph.md +21 -0
  77. data/TODO.parity-ee/11-import-eeng-tests.md +79 -0
  78. data/TODO.parity-ee/12-full-check-catalog.md +67 -0
  79. data/TODO.parity-ee/13-population-p21.md +54 -0
  80. data/TODO.parity-ee/14-shtolo-completion.md +44 -0
  81. data/TODO.parity-ee/15-part28-xml.md +51 -0
  82. data/TODO.parity-ee/16-compare-patch.md +48 -0
  83. data/TODO.parity-ee/17-official-tests.md +42 -0
  84. data/TODO.parity-ee/18-mim-mapping.md +47 -0
  85. data/TODO.parity-ee/19-architecture.md +97 -0
  86. data/TODO.parity-ee/parity-matrix.md +108 -0
  87. data/TODO.refpath-v2/00-overview.md +64 -0
  88. data/TODO.suma-improvements/00-overview.md +23 -0
  89. data/TODO.suma-improvements/01-prebuilt-platform-gems.md +33 -0
  90. data/TODO.suma-improvements/02-suma-e2e.md +39 -0
  91. data/TODO.suma-improvements/03-item-graph.md +22 -0
  92. data/TODO.suma-improvements/04-compiled-set-default.md +16 -0
  93. data/TODO.suma-improvements/05-validation-in-docs.md +17 -0
  94. data/TODO.suma-improvements/06-memory-remeasure.md +36 -0
  95. data/TODO.suma-improvements/07-part28-xml.md +15 -0
  96. data/TODO.suma-improvements/08-windows-gnu-toolchain.md +15 -0
  97. data/TODO.suma-improvements/09-checker-tranche3.md +13 -0
  98. data/TODO.suma-improvements/10-expressdoc.md +8 -0
  99. data/benchmark/srl_benchmark.rb +458 -0
  100. data/benchmark/srl_native_benchmark.rb +146 -0
  101. data/benchmark/srl_ruby_benchmark.rb +132 -0
  102. data/bin/rspec +2 -1
  103. data/docs/Gemfile +12 -0
  104. data/docs/_config.yml +141 -0
  105. data/docs/_guides/changes/changes-format.adoc +778 -0
  106. data/docs/_guides/changes/importing-eengine.adoc +898 -0
  107. data/docs/_guides/changes/index.adoc +396 -0
  108. data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
  109. data/docs/_guides/changes/validating-changes.adoc +681 -0
  110. data/docs/_guides/cli/benchmark-performance.adoc +834 -0
  111. data/docs/_guides/cli/coverage-analysis.adoc +921 -0
  112. data/docs/_guides/cli/format-schemas.adoc +547 -0
  113. data/docs/_guides/cli/index.adoc +8 -0
  114. data/docs/_guides/cli/managing-changes.adoc +927 -0
  115. data/docs/_guides/cli/validate-ascii.adoc +645 -0
  116. data/docs/_guides/cli/validate-schemas.adoc +534 -0
  117. data/docs/_guides/formatter/formatter-architecture.adoc +401 -0
  118. data/docs/_guides/index.adoc +165 -0
  119. data/docs/_guides/ler/creating-packages.adoc +664 -0
  120. data/docs/_guides/ler/index.adoc +305 -0
  121. data/docs/_guides/ler/loading-packages.adoc +707 -0
  122. data/docs/_guides/ler/package-formats.adoc +748 -0
  123. data/docs/_guides/ler/querying-packages.adoc +826 -0
  124. data/docs/_guides/ler/step-packages.adoc +385 -0
  125. data/docs/_guides/ler/validating-packages.adoc +750 -0
  126. data/docs/_guides/liquid/basic-templates.adoc +813 -0
  127. data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
  128. data/docs/_guides/liquid/drops-reference.adoc +829 -0
  129. data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
  130. data/docs/_guides/liquid/index.adoc +468 -0
  131. data/docs/_guides/manifests/creating-manifests.adoc +483 -0
  132. data/docs/_guides/manifests/index.adoc +307 -0
  133. data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
  134. data/docs/_guides/manifests/validating-manifests.adoc +713 -0
  135. data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
  136. data/docs/_guides/ruby-api/index.adoc +257 -0
  137. data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
  138. data/docs/_guides/ruby-api/search-engine.adoc +609 -0
  139. data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
  140. data/docs/_pages/data-model.adoc +665 -0
  141. data/docs/_pages/express-language.adoc +506 -0
  142. data/docs/_pages/getting-started.adoc +414 -0
  143. data/docs/_pages/index.adoc +116 -0
  144. data/docs/_pages/introduction.adoc +256 -0
  145. data/docs/_pages/ler-packages.adoc +837 -0
  146. data/docs/_pages/parsers.adoc +709 -0
  147. data/docs/_pages/schema-manifests.adoc +431 -0
  148. data/docs/_references/index.adoc +228 -0
  149. data/docs/_tutorials/creating-ler-package.adoc +735 -0
  150. data/docs/_tutorials/documentation-coverage.adoc +795 -0
  151. data/docs/_tutorials/formatting-schemas.adoc +89 -0
  152. data/docs/_tutorials/index.adoc +231 -0
  153. data/docs/_tutorials/liquid-templates.adoc +806 -0
  154. data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
  155. data/docs/_tutorials/querying-schemas.adoc +751 -0
  156. data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
  157. data/docs/index.adoc +242 -0
  158. data/docs/lychee.toml +90 -0
  159. data/examples/demo_ler_usage.sh +86 -0
  160. data/examples/ler/README.md +111 -0
  161. data/examples/ler/simple_example.ler +0 -0
  162. data/examples/ler/simple_schema.exp +33 -0
  163. data/examples/ler_build.rb +75 -0
  164. data/examples/ler_cli.rb +79 -0
  165. data/examples/ler_demo_complete.rb +276 -0
  166. data/examples/ler_query.rb +91 -0
  167. data/examples/ler_query_examples.rb +305 -0
  168. data/examples/ler_stats.rb +81 -0
  169. data/examples/phase3_demo.rb +159 -0
  170. data/examples/query_demo_simple.rb +131 -0
  171. data/exe/expressir +6 -17
  172. data/exe/expressir-format +14 -0
  173. data/exe/{format-test → expressir-format-test} +8 -7
  174. data/expressir.gemspec +50 -37
  175. data/lib/expressir/3.2/expressir_core.so +0 -0
  176. data/lib/expressir/3.3/expressir_core.so +0 -0
  177. data/lib/expressir/3.4/expressir_core.so +0 -0
  178. data/lib/expressir/4.0/expressir_core.so +0 -0
  179. data/lib/expressir/benchmark.rb +310 -0
  180. data/lib/expressir/changes/item_change.rb +20 -0
  181. data/lib/expressir/changes/mapping_change.rb +16 -0
  182. data/lib/expressir/changes/schema_change.rb +85 -0
  183. data/lib/expressir/changes/version_change.rb +26 -0
  184. data/lib/expressir/changes.rb +9 -0
  185. data/lib/expressir/cli.rb +138 -11
  186. data/lib/expressir/commands/base.rb +25 -0
  187. data/lib/expressir/commands/benchmark.rb +59 -0
  188. data/lib/expressir/commands/benchmark_cache.rb +80 -0
  189. data/lib/expressir/commands/changes.rb +34 -0
  190. data/lib/expressir/commands/changes_import_eengine.rb +146 -0
  191. data/lib/expressir/commands/changes_validate.rb +63 -0
  192. data/lib/expressir/commands/check.rb +56 -0
  193. data/lib/expressir/commands/clean.rb +20 -0
  194. data/lib/expressir/commands/coverage.rb +458 -0
  195. data/lib/expressir/commands/expand.rb +26 -0
  196. data/lib/expressir/commands/file_violations.rb +70 -0
  197. data/lib/expressir/commands/fix.rb +33 -0
  198. data/lib/expressir/commands/flatten.rb +29 -0
  199. data/lib/expressir/commands/format.rb +43 -0
  200. data/lib/expressir/commands/manifest.rb +420 -0
  201. data/lib/expressir/commands/mapping_validate.rb +68 -0
  202. data/lib/expressir/commands/non_ascii_character.rb +49 -0
  203. data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
  204. data/lib/expressir/commands/package.rb +1222 -0
  205. data/lib/expressir/commands/parity_inputs.rb +160 -0
  206. data/lib/expressir/commands/validate.rb +106 -0
  207. data/lib/expressir/commands/validate_ascii.rb +94 -0
  208. data/lib/expressir/commands/validate_load.rb +91 -0
  209. data/lib/expressir/commands/version.rb +9 -0
  210. data/lib/expressir/commands/xsd.rb +23 -0
  211. data/lib/expressir/commands.rb +30 -0
  212. data/lib/expressir/config.rb +40 -3
  213. data/lib/expressir/coverage.rb +510 -0
  214. data/lib/expressir/eengine/arm_compare_report.rb +23 -0
  215. data/lib/expressir/eengine/changes_section.rb +16 -0
  216. data/lib/expressir/eengine/compare_report.rb +69 -0
  217. data/lib/expressir/eengine/mim_compare_report.rb +23 -0
  218. data/lib/expressir/eengine/modified_object.rb +21 -0
  219. data/lib/expressir/eengine.rb +9 -0
  220. data/lib/expressir/errors.rb +113 -0
  221. data/lib/expressir/express/adoc_hyperlink_formatter.rb +30 -0
  222. data/lib/expressir/express/adoc_source_formatter.rb +12 -0
  223. data/lib/expressir/express/ast_key_converter.rb +114 -0
  224. data/lib/expressir/express/builder.rb +277 -0
  225. data/lib/expressir/express/builder_context.rb +22 -0
  226. data/lib/expressir/express/builder_registry.rb +397 -0
  227. data/lib/expressir/express/builders/attribute_decl_builder.rb +32 -0
  228. data/lib/expressir/express/builders/built_in_builder.rb +75 -0
  229. data/lib/expressir/express/builders/constant_builder.rb +108 -0
  230. data/lib/expressir/express/builders/declaration_builder.rb +20 -0
  231. data/lib/expressir/express/builders/derive_clause_builder.rb +14 -0
  232. data/lib/expressir/express/builders/derived_attr_builder.rb +28 -0
  233. data/lib/expressir/express/builders/domain_rule_builder.rb +21 -0
  234. data/lib/expressir/express/builders/entity_decl_builder.rb +116 -0
  235. data/lib/expressir/express/builders/explicit_attr_builder.rb +49 -0
  236. data/lib/expressir/express/builders/expression_builder.rb +416 -0
  237. data/lib/expressir/express/builders/function_decl_builder.rb +86 -0
  238. data/lib/expressir/express/builders/helpers.rb +148 -0
  239. data/lib/expressir/express/builders/interface_builder.rb +151 -0
  240. data/lib/expressir/express/builders/inverse_attr_builder.rb +43 -0
  241. data/lib/expressir/express/builders/inverse_attr_type_builder.rb +30 -0
  242. data/lib/expressir/express/builders/inverse_clause_builder.rb +14 -0
  243. data/lib/expressir/express/builders/literal_builder.rb +93 -0
  244. data/lib/expressir/express/builders/procedure_decl_builder.rb +82 -0
  245. data/lib/expressir/express/builders/qualifier_builder.rb +102 -0
  246. data/lib/expressir/express/builders/reference_builder.rb +18 -0
  247. data/lib/expressir/express/builders/rule_decl_builder.rb +97 -0
  248. data/lib/expressir/express/builders/schema_body_decl_builder.rb +18 -0
  249. data/lib/expressir/express/builders/schema_decl_builder.rb +56 -0
  250. data/lib/expressir/express/builders/schema_version_builder.rb +34 -0
  251. data/lib/expressir/express/builders/simple_id_builder.rb +17 -0
  252. data/lib/expressir/express/builders/statement_builder.rb +243 -0
  253. data/lib/expressir/express/builders/subtype_constraint_builder.rb +166 -0
  254. data/lib/expressir/express/builders/syntax_builder.rb +30 -0
  255. data/lib/expressir/express/builders/type_builder.rb +238 -0
  256. data/lib/expressir/express/builders/type_decl_builder.rb +26 -0
  257. data/lib/expressir/express/builders/unique_clause_builder.rb +20 -0
  258. data/lib/expressir/express/builders/unique_rule_builder.rb +41 -0
  259. data/lib/expressir/express/builders/where_clause_builder.rb +20 -0
  260. data/lib/expressir/express/builders.rb +55 -0
  261. data/lib/expressir/express/cache.rb +43 -18
  262. data/lib/expressir/express/checker.rb +812 -0
  263. data/lib/expressir/express/concatenator.rb +79 -0
  264. data/lib/expressir/express/core.rb +95 -0
  265. data/lib/expressir/express/error.rb +124 -0
  266. data/lib/expressir/express/formatter.rb +89 -1548
  267. data/lib/expressir/express/formatters/data_types_formatter.rb +362 -0
  268. data/lib/expressir/express/formatters/declarations_formatter.rb +753 -0
  269. data/lib/expressir/express/formatters/expressions_formatter.rb +180 -0
  270. data/lib/expressir/express/formatters/literals_formatter.rb +55 -0
  271. data/lib/expressir/express/formatters/references_formatter.rb +53 -0
  272. data/lib/expressir/express/formatters/remark_formatter.rb +270 -0
  273. data/lib/expressir/express/formatters/remark_item_formatter.rb +28 -0
  274. data/lib/expressir/express/formatters/statements_formatter.rb +272 -0
  275. data/lib/expressir/express/formatters/supertype_expressions_formatter.rb +54 -0
  276. data/lib/expressir/express/formatters.rb +22 -0
  277. data/lib/expressir/express/grammar/parser.rb +712 -0
  278. data/lib/expressir/express/grammar.rb +11 -0
  279. data/lib/expressir/express/hyperlink_formatter.rb +4 -5
  280. data/lib/expressir/express/interface_dot.rb +105 -0
  281. data/lib/expressir/express/lazy_repository.rb +211 -0
  282. data/lib/expressir/express/line_map.rb +48 -0
  283. data/lib/expressir/express/listing.rb +174 -0
  284. data/lib/expressir/express/model_traversal.rb +42 -0
  285. data/lib/expressir/express/model_visitor.rb +11 -10
  286. data/lib/expressir/express/node_position_index.rb +342 -0
  287. data/lib/expressir/express/parallel_files.rb +229 -0
  288. data/lib/expressir/express/parser.rb +501 -62
  289. data/lib/expressir/express/pretty_formatter.rb +586 -0
  290. data/lib/expressir/express/pretty_gate.rb +61 -0
  291. data/lib/expressir/express/refs_overlay.rb +47 -0
  292. data/lib/expressir/express/remark_attacher.rb +1098 -0
  293. data/lib/expressir/express/remark_overlay.rb +86 -0
  294. data/lib/expressir/express/remark_scanner.rb +245 -0
  295. data/lib/expressir/express/resolve_references_model_visitor.rb +7 -9
  296. data/lib/expressir/express/schema_block_scanner.rb +137 -0
  297. data/lib/expressir/express/schema_head_formatter.rb +4 -5
  298. data/lib/expressir/express/schema_plain_source_formatter.rb +11 -0
  299. data/lib/expressir/express/schema_source_formatter.rb +15 -0
  300. data/lib/expressir/express/scope_resolver.rb +223 -0
  301. data/lib/expressir/express/self_schema_reference.rb +126 -0
  302. data/lib/expressir/express/shtolo.rb +679 -0
  303. data/lib/expressir/express/smrl_xml.rb +125 -0
  304. data/lib/expressir/express/source_formatter.rb +15 -0
  305. data/lib/expressir/express/streaming_builder.rb +435 -0
  306. data/lib/expressir/express/xsd.rb +259 -0
  307. data/lib/expressir/express.rb +53 -0
  308. data/lib/expressir/liquid.rb +1 -0
  309. data/lib/expressir/manifest/resolver.rb +210 -0
  310. data/lib/expressir/manifest/validator.rb +192 -0
  311. data/lib/expressir/manifest.rb +6 -0
  312. data/lib/expressir/mapping/refpath.rb +857 -0
  313. data/lib/expressir/mapping.rb +253 -0
  314. data/lib/expressir/model/cache.rb +10 -9
  315. data/lib/expressir/model/concerns.rb +42 -0
  316. data/lib/expressir/model/data_types/aggregate.rb +8 -13
  317. data/lib/expressir/model/data_types/array.rb +15 -21
  318. data/lib/expressir/model/data_types/bag.rb +11 -15
  319. data/lib/expressir/model/data_types/binary.rb +9 -12
  320. data/lib/expressir/model/data_types/boolean.rb +7 -2
  321. data/lib/expressir/model/data_types/enumeration.rb +11 -15
  322. data/lib/expressir/model/data_types/enumeration_item.rb +5 -7
  323. data/lib/expressir/model/data_types/generic.rb +6 -8
  324. data/lib/expressir/model/data_types/generic_entity.rb +11 -13
  325. data/lib/expressir/model/data_types/integer.rb +7 -2
  326. data/lib/expressir/model/data_types/list.rb +13 -18
  327. data/lib/expressir/model/data_types/logical.rb +7 -2
  328. data/lib/expressir/model/data_types/number.rb +7 -2
  329. data/lib/expressir/model/data_types/real.rb +7 -9
  330. data/lib/expressir/model/data_types/select.rb +13 -18
  331. data/lib/expressir/model/data_types/set.rb +11 -15
  332. data/lib/expressir/model/data_types/string.rb +9 -12
  333. data/lib/expressir/model/data_types.rb +25 -0
  334. data/lib/expressir/model/declarations/attribute.rb +19 -28
  335. data/lib/expressir/model/declarations/constant.rb +10 -16
  336. data/lib/expressir/model/declarations/derived_attribute.rb +25 -0
  337. data/lib/expressir/model/declarations/entity.rb +39 -30
  338. data/lib/expressir/model/declarations/function.rb +32 -40
  339. data/lib/expressir/model/declarations/informal_proposition_rule.rb +25 -0
  340. data/lib/expressir/model/declarations/interface.rb +25 -16
  341. data/lib/expressir/model/declarations/interface_item.rb +9 -11
  342. data/lib/expressir/model/declarations/interfaced_item.rb +15 -19
  343. data/lib/expressir/model/declarations/inverse_attribute.rb +25 -0
  344. data/lib/expressir/model/declarations/parameter.rb +10 -16
  345. data/lib/expressir/model/declarations/procedure.rb +30 -37
  346. data/lib/expressir/model/declarations/remark_item.rb +11 -11
  347. data/lib/expressir/model/declarations/rule.rb +38 -43
  348. data/lib/expressir/model/declarations/schema.rb +162 -64
  349. data/lib/expressir/model/declarations/schema_version.rb +8 -11
  350. data/lib/expressir/model/declarations/schema_version_item.rb +8 -11
  351. data/lib/expressir/model/declarations/subtype_constraint.rb +14 -22
  352. data/lib/expressir/model/declarations/type.rb +18 -18
  353. data/lib/expressir/model/declarations/unique_rule.rb +8 -13
  354. data/lib/expressir/model/declarations/variable.rb +10 -16
  355. data/lib/expressir/model/declarations/where_rule.rb +8 -13
  356. data/lib/expressir/model/declarations.rb +31 -0
  357. data/lib/expressir/model/dependency_resolver.rb +268 -0
  358. data/lib/expressir/model/exp_file.rb +42 -0
  359. data/lib/expressir/model/expressions/aggregate_initializer.rb +8 -9
  360. data/lib/expressir/model/expressions/aggregate_initializer_item.rb +10 -12
  361. data/lib/expressir/model/expressions/binary_expression.rb +42 -36
  362. data/lib/expressir/model/expressions/entity_constructor.rb +10 -12
  363. data/lib/expressir/model/expressions/function_call.rb +10 -12
  364. data/lib/expressir/model/expressions/interval.rb +18 -23
  365. data/lib/expressir/model/expressions/query_expression.rb +11 -15
  366. data/lib/expressir/model/expressions/unary_expression.rb +13 -15
  367. data/lib/expressir/model/expressions.rb +18 -0
  368. data/lib/expressir/model/identifier.rb +18 -26
  369. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  370. data/lib/expressir/model/indexes/item_graph.rb +185 -0
  371. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  372. data/lib/expressir/model/indexes/type_index.rb +149 -0
  373. data/lib/expressir/model/indexes.rb +12 -0
  374. data/lib/expressir/model/interface_validator.rb +384 -0
  375. data/lib/expressir/model/literals/binary.rb +7 -9
  376. data/lib/expressir/model/literals/integer.rb +7 -9
  377. data/lib/expressir/model/literals/logical.rb +10 -12
  378. data/lib/expressir/model/literals/real.rb +7 -9
  379. data/lib/expressir/model/literals/string.rb +10 -12
  380. data/lib/expressir/model/literals.rb +13 -0
  381. data/lib/expressir/model/model_element.rb +286 -117
  382. data/lib/expressir/model/references/attribute_reference.rb +9 -12
  383. data/lib/expressir/model/references/group_reference.rb +9 -12
  384. data/lib/expressir/model/references/index_reference.rb +11 -15
  385. data/lib/expressir/model/references/simple_reference.rb +11 -14
  386. data/lib/expressir/model/references.rb +12 -0
  387. data/lib/expressir/model/remark_format.rb +17 -0
  388. data/lib/expressir/model/remark_info.rb +92 -0
  389. data/lib/expressir/model/remark_placement.rb +42 -0
  390. data/lib/expressir/model/repository.rb +472 -12
  391. data/lib/expressir/model/repository_validator.rb +293 -0
  392. data/lib/expressir/model/search_engine.rb +551 -0
  393. data/lib/expressir/model/statements/alias.rb +12 -16
  394. data/lib/expressir/model/statements/assignment.rb +12 -11
  395. data/lib/expressir/model/statements/case.rb +31 -14
  396. data/lib/expressir/model/statements/case_action.rb +9 -11
  397. data/lib/expressir/model/statements/compound.rb +10 -8
  398. data/lib/expressir/model/statements/escape.rb +10 -2
  399. data/lib/expressir/model/statements/if.rb +15 -14
  400. data/lib/expressir/model/statements/null.rb +10 -2
  401. data/lib/expressir/model/statements/procedure_call.rb +11 -11
  402. data/lib/expressir/model/statements/repeat.rb +21 -28
  403. data/lib/expressir/model/statements/return.rb +9 -8
  404. data/lib/expressir/model/statements/skip.rb +10 -2
  405. data/lib/expressir/model/statements.rb +20 -0
  406. data/lib/expressir/model/supertype_expressions/binary_supertype_expression.rb +13 -17
  407. data/lib/expressir/model/supertype_expressions/oneof_supertype_expression.rb +7 -9
  408. data/lib/expressir/model/supertype_expressions.rb +12 -0
  409. data/lib/expressir/model.rb +38 -78
  410. data/lib/expressir/package/builder.rb +231 -0
  411. data/lib/expressir/package/metadata.rb +79 -0
  412. data/lib/expressir/package/reader.rb +149 -0
  413. data/lib/expressir/package.rb +8 -0
  414. data/lib/expressir/schema_manifest.rb +159 -0
  415. data/lib/expressir/schema_manifest_entry.rb +15 -0
  416. data/lib/expressir/version.rb +6 -1
  417. data/lib/expressir.rb +79 -9
  418. data/lib/tasks/verify_remarks.rake +16 -0
  419. data/rakelib/native.rake +24 -0
  420. data/rakelib/oracle.rake +52 -0
  421. data/rakelib/verify_remarks.rake +16 -0
  422. metadata +431 -129
  423. data/.cross_rubies +0 -20
  424. data/.gitmodules +0 -6
  425. data/.yardopts +0 -11
  426. data/docs/development.md +0 -90
  427. data/exe/format +0 -18
  428. data/exe/generate-parser +0 -51
  429. data/lib/expressir/cli/ui.rb +0 -36
  430. data/lib/expressir/express/2.7/express_parser.so +0 -0
  431. data/lib/expressir/express/3.0/express_parser.so +0 -0
  432. data/lib/expressir/express/3.1/express_parser.so +0 -0
  433. data/lib/expressir/express/3.2/express_parser.so +0 -0
  434. data/lib/expressir/express/visitor.rb +0 -2588
  435. data/lib/expressir/model/data_type.rb +0 -9
  436. data/lib/expressir/model/declaration.rb +0 -9
  437. data/lib/expressir/model/expression.rb +0 -9
  438. data/lib/expressir/model/literal.rb +0 -9
  439. data/lib/expressir/model/reference.rb +0 -9
  440. data/lib/expressir/model/statement.rb +0 -9
  441. data/lib/expressir/model/supertype_expression.rb +0 -9
  442. data/rakelib/antlr4-native.rake +0 -161
  443. data/rakelib/cross-ruby.rake +0 -368
  444. data/spec/acceptance/version_spec.rb +0 -27
  445. data/spec/expressir/express/cache_spec.rb +0 -89
  446. data/spec/expressir/express/formatter_spec.rb +0 -173
  447. data/spec/expressir/express/parser_spec.rb +0 -141
  448. data/spec/expressir/model/model_element_spec.rb +0 -318
  449. data/spec/spec_helper.rb +0 -24
  450. data/spec/support/console_helper.rb +0 -29
  451. data/spec/syntax/multiple.exp +0 -23
  452. data/spec/syntax/multiple.yaml +0 -198
  453. data/spec/syntax/multiple_formatted.exp +0 -71
  454. data/spec/syntax/multiple_hyperlink_formatted.exp +0 -71
  455. data/spec/syntax/multiple_schema_head_hyperlink_formatted.exp +0 -13
  456. data/spec/syntax/remark.exp +0 -193
  457. data/spec/syntax/remark.yaml +0 -471
  458. data/spec/syntax/remark_formatted.exp +0 -228
  459. data/spec/syntax/single.exp +0 -4
  460. data/spec/syntax/single.yaml +0 -18
  461. data/spec/syntax/single_formatted.exp +0 -10
  462. data/spec/syntax/single_formatted.yaml +0 -36
  463. data/spec/syntax/syntax.exp +0 -333
  464. data/spec/syntax/syntax.yaml +0 -3509
  465. data/spec/syntax/syntax_formatted.exp +0 -902
  466. data/spec/syntax/syntax_hyperlink_formatted.exp +0 -902
  467. data/spec/syntax/syntax_schema_head_formatted.exp +0 -18
@@ -0,0 +1,458 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # SRL Benchmark Script for Expressir
5
+ # Compares Parsanol Ruby vs Native performance on full STEPmod Resource Library
6
+ # Features: Live progress, emojis, colors, per-schema stats
7
+
8
+ require "bundler/setup"
9
+ require "benchmark"
10
+ require "fileutils"
11
+
12
+ # Force loading of native extension
13
+ require "parsanol"
14
+ require "parsanol/native"
15
+
16
+ # Now require expressir
17
+ require "expressir"
18
+
19
+ # Configuration
20
+ SRL_PATH = ENV["SRL_PATH"] ||
21
+ "/Users/mulgogi/src/mn/iso-10303/schemas/resources"
22
+ ITERATIONS = (ENV["ITERATIONS"] || 1).to_i
23
+ TIMEOUT_SECONDS = (ENV["TIMEOUT"] || 30).to_i # Timeout per file
24
+
25
+ # Check if we're running in an interactive terminal
26
+ INTERACTIVE = $stdout.tty?
27
+
28
+ # ANSI Color codes
29
+ module Colors
30
+ RESET = "\e[0m"
31
+ BOLD = "\e[1m"
32
+ DIM = "\e[2m"
33
+
34
+ # Foreground colors
35
+ BLACK = "\e[30m"
36
+ RED = "\e[31m"
37
+ GREEN = "\e[32m"
38
+ YELLOW = "\e[33m"
39
+ BLUE = "\e[34m"
40
+ MAGENTA = "\e[35m"
41
+ CYAN = "\e[36m"
42
+ WHITE = "\e[37m"
43
+
44
+ # Bright foreground colors
45
+ BRIGHT_RED = "\e[91m"
46
+ BRIGHT_GREEN = "\e[92m"
47
+ BRIGHT_YELLOW = "\e[93m"
48
+ BRIGHT_BLUE = "\e[94m"
49
+ BRIGHT_MAGENTA = "\e[95m"
50
+ BRIGHT_CYAN = "\e[96m"
51
+ BRIGHT_WHITE = "\e[97m"
52
+
53
+ # Background colors
54
+ BG_RED = "\e[41m"
55
+ BG_GREEN = "\e[42m"
56
+ BG_YELLOW = "\e[43m"
57
+ BG_BLUE = "\e[44m"
58
+ BG_MAGENTA = "\e[45m"
59
+ BG_CYAN = "\e[46m"
60
+ end
61
+
62
+ include Colors
63
+
64
+ # Terminal utilities
65
+ module Terminal
66
+ class << self
67
+ def width
68
+ IO.console.winsize[1]
69
+ rescue StandardError
70
+ 80
71
+ end
72
+
73
+ def clear_line
74
+ INTERACTIVE ? "\r\e[K" : ""
75
+ end
76
+
77
+ def move_to_start
78
+ INTERACTIVE ? "\r" : ""
79
+ end
80
+ end
81
+ end
82
+
83
+ # Progress bar
84
+ class ProgressBar
85
+ def initialize(total, width = 30)
86
+ @total = total
87
+ @width = width
88
+ @current = 0
89
+ end
90
+
91
+ def render(current, label = "")
92
+ @current = current
93
+ percentage = (@current.to_f / @total * 100).round(1)
94
+ filled = (@width * @current / @total.to_f).round
95
+ empty = @width - filled
96
+
97
+ bar = "#{BRIGHT_CYAN}#{'█' * filled}#{DIM}#{'░' * empty}#{RESET}"
98
+ "#{bar} #{BRIGHT_WHITE}#{percentage.to_s.rjust(5)}%#{RESET} #{DIM}#{label}#{RESET}"
99
+ end
100
+ end
101
+
102
+ def find_exp_files
103
+ Dir.glob("#{SRL_PATH}/*/*.exp").reject do |f|
104
+ f.include?("quantities_and_units")
105
+ end
106
+ end
107
+
108
+ # Parse one file in a forked child with a hard wall-clock kill.
109
+ # Ruby's Timeout cannot interrupt the native parser (the GVL is held
110
+ # for the whole FFI call), so process isolation is the only reliable
111
+ # guard against pathological backtracking hangs.
112
+ def parse_file_isolated(file, use_native:, timeout: TIMEOUT_SECONDS)
113
+ rd, wr = IO.pipe
114
+ pid = fork do
115
+ rd.close
116
+ t = Time.now
117
+ result = { status: "ok", elapsed: 0.0, error: nil }
118
+ begin
119
+ if use_native
120
+ content = File.read(file)
121
+ Expressir::Express::Parser.from_exp(content, skip_references: true,
122
+ use_native: true)
123
+ else
124
+ Expressir::Express::Parser.from_file(file, skip_references: true,
125
+ use_native: false)
126
+ end
127
+ rescue StandardError => e
128
+ result[:status] = "err"
129
+ result[:error] = "#{e.class}: #{e.message[0..60]}"
130
+ end
131
+ result[:elapsed] = Time.now - t
132
+ data = Marshal.dump(result)
133
+ wr.write([data.bytesize].pack("N"))
134
+ wr.write(data)
135
+ wr.close
136
+ exit!(0)
137
+ end
138
+ wr.close
139
+
140
+ deadline = Time.now + timeout
141
+ timed_out = false
142
+ loop do
143
+ done = Process.waitpid(pid, Process::WNOHANG)
144
+ break if done
145
+
146
+ if Time.now > deadline
147
+ Process.kill("TERM", pid)
148
+ sleep 1
149
+ begin
150
+ Process.kill("KILL", pid) if Process.waitpid(pid, Process::WNOHANG).nil?
151
+ rescue StandardError
152
+ nil
153
+ end
154
+ timed_out = true
155
+ break
156
+ end
157
+ sleep 0.1
158
+ end
159
+
160
+ if timed_out
161
+ rd.close
162
+ { status: "timeout", elapsed: timeout.to_f, error: "Timeout after #{timeout}s" }
163
+ else
164
+ header = rd.read(4)
165
+ payload = header && Marshal.load(rd.read(header.unpack1("N"))) # rubocop:disable Security/MarshalLoad
166
+ rd.close
167
+ payload
168
+ end
169
+ end
170
+
171
+ def count_lines(files)
172
+ files.sum { |f| File.read(f).lines.count }
173
+ end
174
+
175
+ def format_time(seconds)
176
+ if seconds < 60
177
+ "#{seconds.round(2)}s"
178
+ else
179
+ minutes = (seconds / 60).floor
180
+ secs = (seconds % 60).round(1)
181
+ "#{minutes}m #{secs}s"
182
+ end
183
+ end
184
+
185
+ def format_number(num)
186
+ num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
187
+ end
188
+
189
+ def print_header
190
+ puts
191
+ puts "#{BRIGHT_MAGENTA}#{'═' * 60}#{RESET}"
192
+ puts "#{BRIGHT_MAGENTA}║#{RESET}#{BOLD}#{BRIGHT_WHITE} 🚀 EXPRESSIR SRL BENCHMARK 🚀 #{RESET}#{BRIGHT_MAGENTA}║#{RESET}"
193
+ puts "#{BRIGHT_MAGENTA}║#{RESET}#{DIM} Ruby vs Native Parser Showdown #{RESET}#{BRIGHT_MAGENTA}║#{RESET}"
194
+ puts "#{BRIGHT_MAGENTA}#{'═' * 60}#{RESET}"
195
+ puts
196
+ end
197
+
198
+ def print_config(files, total_lines)
199
+ puts "#{BRIGHT_CYAN}⚙️ Configuration#{RESET}"
200
+ puts "#{DIM}┌─────────────────────────────────────────────┐#{RESET}"
201
+ puts "#{DIM}│#{RESET} 📁 Files: #{BRIGHT_WHITE}#{format_number(files.size).rjust(10)}#{RESET} #{DIM}│#{RESET}"
202
+ puts "#{DIM}│#{RESET} 📊 Lines: #{BRIGHT_WHITE}#{format_number(total_lines).rjust(10)}#{RESET} #{DIM}│#{RESET}"
203
+ puts "#{DIM}│#{RESET} 🔄 Iterations: #{BRIGHT_WHITE}#{ITERATIONS.to_s.rjust(10)}#{RESET} #{DIM}│#{RESET}"
204
+ puts "#{DIM}│#{RESET} 🦀 Native: #{BRIGHT_WHITE}#{Parsanol::Native.available?.to_s.upcase.rjust(10)}#{RESET} #{DIM}│#{RESET}"
205
+ puts "#{DIM}└─────────────────────────────────────────────┘#{RESET}"
206
+ puts
207
+ end
208
+
209
+ def print_warmup_start
210
+ print "#{BRIGHT_YELLOW}🔥 Warming up...#{RESET} "
211
+ end
212
+
213
+ def print_warmup_done
214
+ puts "#{BRIGHT_GREEN}✅ Done!#{RESET}"
215
+ puts
216
+ end
217
+
218
+ class ParserBenchmark
219
+ attr_reader :name, :emoji, :color, :results
220
+
221
+ def initialize(name:, emoji:, color:, use_native:)
222
+ @name = name
223
+ @emoji = emoji
224
+ @color = color
225
+ @use_native = use_native
226
+ @results = []
227
+ end
228
+
229
+ def run(files, _total_lines)
230
+ puts "#{@color}#{@emoji} #{@name}#{@RESET}"
231
+ puts "#{DIM}┌──────────────────────────────────────────────────────────┐#{RESET}"
232
+
233
+ progress_bar = ProgressBar.new(files.size, 25)
234
+ iteration_results = { success: 0, failed: 0, errors: [], time: 0,
235
+ schema_times: [] }
236
+ start_time = Time.now
237
+
238
+ files.each_with_index do |file, idx|
239
+ schema_name = File.basename(file, ".exp")
240
+ file_start = Time.now
241
+ schema_lines = File.read(file).lines.count
242
+
243
+ result = parse_file_isolated(file, use_native: @use_native)
244
+ result[:elapsed]
245
+
246
+ case result[:status]
247
+ when "ok"
248
+ iteration_results[:success] += 1
249
+ status = "#{BRIGHT_GREEN}✓#{RESET}"
250
+ when "timeout"
251
+ iteration_results[:failed] += 1
252
+ iteration_results[:errors] << { file: File.basename(file),
253
+ error: result[:error] }
254
+ status = "#{BRIGHT_YELLOW}⏱#{RESET}"
255
+ else
256
+ iteration_results[:failed] += 1
257
+ iteration_results[:errors] << { file: File.basename(file),
258
+ error: result[:error] }
259
+ status = "#{BRIGHT_RED}✗#{RESET}"
260
+ end
261
+
262
+ elapsed = Time.now - file_start
263
+ iteration_results[:schema_times] << { name: schema_name, time: elapsed,
264
+ lines: schema_lines }
265
+
266
+ # Live progress update
267
+ if INTERACTIVE
268
+ progress_label = "#{status} #{schema_name[0..20].ljust(21)}"
269
+ print "#{Terminal.clear_line} #{progress_bar.render(idx + 1,
270
+ progress_label)}"
271
+ $stdout.flush
272
+ elsif ((idx + 1) % 10).zero? || idx.zero?
273
+ # Print progress every 10 files when not interactive
274
+ pct = ((idx + 1).to_f / files.size * 100).round(1)
275
+ puts " #{progress_bar.render(idx + 1, "#{pct}% complete")}"
276
+ $stdout.flush
277
+ end
278
+ end
279
+
280
+ if INTERACTIVE
281
+ puts
282
+ end
283
+
284
+ iteration_results[:time] = Time.now - start_time
285
+ @results << iteration_results
286
+ iteration_results
287
+ end
288
+
289
+ def print_summary(files, total_lines, result)
290
+ avg_time = result[:time]
291
+ lines_per_sec = (total_lines / avg_time).round(1)
292
+ files_per_sec = (files.size / avg_time).round(2)
293
+
294
+ puts "#{DIM}├──────────────────────────────────────────────────────────┤#{RESET}"
295
+ puts "#{DIM}│#{RESET} #{BOLD}Results:#{RESET}"
296
+ puts "#{DIM}│#{RESET} #{BRIGHT_GREEN}✅ Success:#{RESET} #{BRIGHT_WHITE}#{result[:success].to_s.rjust(5)}#{RESET} / #{files.size} files"
297
+ puts "#{DIM}│#{RESET} #{BRIGHT_RED}❌ Failed:#{RESET} #{BRIGHT_WHITE}#{result[:failed].to_s.rjust(5)}#{RESET} files"
298
+ puts "#{DIM}│#{RESET} #{BRIGHT_CYAN}⏱️ Time:#{RESET} #{BRIGHT_WHITE}#{format_time(avg_time).rjust(5)}#{RESET}"
299
+ puts "#{DIM}│#{RESET} #{BRIGHT_YELLOW}⚡ Speed:#{RESET} #{BRIGHT_WHITE}#{format_number(lines_per_sec).rjust(5)}#{RESET} lines/sec"
300
+
301
+ if result[:failed].positive? && result[:failed] <= 5
302
+ puts "#{DIM}├──────────────────────────────────────────────────────────┤#{RESET}"
303
+ puts "#{DIM}│#{RESET} #{BRIGHT_RED}⚠️ Errors:#{RESET}"
304
+ result[:errors].first(3).each do |err|
305
+ puts "#{DIM}│#{RESET} #{DIM}• #{err[:file]}: #{err[:error][0..40]}#{RESET}"
306
+ end
307
+ end
308
+
309
+ puts "#{DIM}└──────────────────────────────────────────────────────────┘#{RESET}"
310
+ puts
311
+
312
+ { avg_time: avg_time, lines_per_sec: lines_per_sec,
313
+ files_per_sec: files_per_sec }
314
+ end
315
+
316
+ def print_slowest_schemas(result, count = 5)
317
+ return if result[:schema_times].empty?
318
+
319
+ slowest = result[:schema_times].sort_by { |s| -s[:time] }.first(count)
320
+
321
+ puts "#{@color}#{@emoji} Slowest Schemas#{@RESET}"
322
+ puts "#{DIM}┌─────────────────────────────────────────────────────┐#{RESET}"
323
+ slowest.each_with_index do |s, i|
324
+ rank = "#{BRIGHT_WHITE}#{(i + 1).to_s.rjust(2)}.#{RESET}"
325
+ name = s[:name][0..25].ljust(26)
326
+ time = "#{BRIGHT_CYAN}#{s[:time].round(1)}s#{RESET}".rjust(8)
327
+ lines = "#{DIM}#{s[:lines]} lines#{RESET}"
328
+ puts "#{DIM}│#{RESET} #{rank} #{name} #{time} #{lines}"
329
+ end
330
+ puts "#{DIM}└─────────────────────────────────────────────────────┘#{RESET}"
331
+ puts
332
+ end
333
+ end
334
+
335
+ def print_comparison(ruby_stats, native_stats, files, total_lines)
336
+ speedup = (ruby_stats[:avg_time] / native_stats[:avg_time]).round(1)
337
+ time_saved = (ruby_stats[:avg_time] - native_stats[:avg_time]).round(2)
338
+
339
+ puts "#{BRIGHT_MAGENTA}#{'═' * 60}#{RESET}"
340
+ puts "#{BOLD}#{BRIGHT_WHITE} 📊 PERFORMANCE COMPARISON 📊 #{RESET}"
341
+ puts "#{BRIGHT_MAGENTA}#{'═' * 60}#{RESET}"
342
+ puts
343
+
344
+ puts "#{DIM}┌─────────────────────────────────────────────────────┐#{RESET}"
345
+ puts "#{DIM}│#{RESET}#{BOLD} HEAD-TO-HEAD #{RESET}#{DIM}│#{RESET}"
346
+ puts "#{DIM}├─────────────────────────────────────────────────────┤#{RESET}"
347
+ puts "#{DIM}│#{RESET} #{DIM}│#{RESET}"
348
+ puts "#{DIM}│#{RESET} #{BRIGHT_BLUE}💎 Ruby Parser#{RESET} #{format_time(ruby_stats[:avg_time]).rjust(10)} #{DIM}│#{RESET}"
349
+ puts "#{DIM}│#{RESET} #{DIM}#{format_number(ruby_stats[:lines_per_sec])} lines/sec#{RESET} #{DIM}│#{RESET}"
350
+ puts "#{DIM}│#{RESET} #{DIM}│#{RESET}"
351
+ puts "#{DIM}│#{RESET} #{BRIGHT_CYAN}🦀 Native Parser#{RESET} #{format_time(native_stats[:avg_time]).rjust(10)} #{DIM}│#{RESET}"
352
+ puts "#{DIM}│#{RESET} #{DIM}#{format_number(native_stats[:lines_per_sec])} lines/sec#{RESET} #{DIM}│#{RESET}"
353
+ puts "#{DIM}│#{RESET} #{DIM}│#{RESET}"
354
+ puts "#{DIM}├─────────────────────────────────────────────────────┤#{RESET}"
355
+
356
+ if speedup > 1
357
+ puts "#{DIM}│#{RESET} #{BOLD}#{BRIGHT_GREEN}🏆 WINNER: Native Parser#{RESET} #{DIM}│#{RESET}"
358
+ puts "#{DIM}│#{RESET} #{BRIGHT_YELLOW}⚡ Speedup:#{RESET} #{BOLD}#{BRIGHT_GREEN}#{speedup}x FASTER#{RESET} #{DIM}│#{RESET}"
359
+ puts "#{DIM}│#{RESET} #{BRIGHT_YELLOW}⏱️ Time Saved:#{RESET} #{BRIGHT_GREEN}#{format_time(time_saved)} per run#{RESET} #{DIM}│#{RESET}"
360
+ else
361
+ puts "#{DIM}│#{RESET} #{BOLD}#{BRIGHT_BLUE}🏆 WINNER: Ruby Parser#{RESET} #{DIM}│#{RESET}"
362
+ puts "#{DIM}│#{RESET} #{BRIGHT_YELLOW}⚡ Speedup:#{RESET} #{BOLD}#{BRIGHT_BLUE}#{(1 / speedup).round(1)}x FASTER#{RESET} #{DIM}│#{RESET}"
363
+ end
364
+
365
+ puts "#{DIM}│#{RESET} #{DIM}│#{RESET}"
366
+ puts "#{DIM}└─────────────────────────────────────────────────────┘#{RESET}"
367
+ puts
368
+
369
+ # Summary
370
+ puts "#{DIM}┌─────────────────────────────────────────────────────┐#{RESET}"
371
+ puts "#{DIM}│#{RESET}#{BOLD} SUMMARY #{RESET}#{DIM}│#{RESET}"
372
+ puts "#{DIM}├─────────────────────────────────────────────────────┤#{RESET}"
373
+ puts "#{DIM}│#{RESET} 📦 #{format_number(files.size)} schemas (#{format_number(total_lines)} lines) #{DIM}│#{RESET}"
374
+ puts "#{DIM}│#{RESET} #{BRIGHT_BLUE}Ruby:#{RESET} #{format_time(ruby_stats[:avg_time]).rjust(8)} #{DIM}│#{RESET}"
375
+ puts "#{DIM}│#{RESET} #{BRIGHT_CYAN}Native:#{RESET} #{format_time(native_stats[:avg_time]).rjust(8)} #{DIM}│#{RESET}"
376
+ puts "#{DIM}└─────────────────────────────────────────────────────┘#{RESET}"
377
+ puts
378
+ end
379
+
380
+ def print_footer
381
+ puts "#{BRIGHT_MAGENTA}#{'═' * 60}#{RESET}"
382
+ puts "#{BOLD}#{BRIGHT_WHITE} ✨ BENCHMARK COMPLETE ✨ #{RESET}"
383
+ puts "#{BRIGHT_MAGENTA}#{'═' * 60}#{RESET}"
384
+ puts
385
+ end
386
+
387
+ # ==================== MAIN ====================
388
+
389
+ print_header
390
+
391
+ # Find all EXPRESS files
392
+ files = find_exp_files
393
+ total_lines = count_lines(files)
394
+
395
+ if files.empty?
396
+ puts "#{BRIGHT_RED}❌ ERROR: No EXPRESS files found at #{SRL_PATH}#{RESET}"
397
+ exit 1
398
+ end
399
+
400
+ print_config(files, total_lines)
401
+
402
+ # Warmup
403
+ print_warmup_start
404
+ warmup_file = files.first
405
+
406
+ begin
407
+ Expressir::Express::Parser.from_file(warmup_file, skip_references: true,
408
+ use_native: false)
409
+ rescue StandardError => e
410
+ puts "#{BRIGHT_YELLOW}⚠️ Ruby warmup warning: #{e.message[0..40]}#{RESET}"
411
+ end
412
+
413
+ if Parsanol::Native.available?
414
+ begin
415
+ content = File.read(warmup_file)
416
+ Expressir::Express::Parser.from_exp(content, skip_references: true,
417
+ use_native: true)
418
+ rescue StandardError => e
419
+ puts "#{BRIGHT_YELLOW}⚠️ Native warmup warning: #{e.message[0..40]}#{RESET}"
420
+ end
421
+ end
422
+
423
+ print_warmup_done
424
+
425
+ # Ruby Benchmark
426
+ ruby_benchmark = ParserBenchmark.new(
427
+ name: "Ruby Parser",
428
+ emoji: "💎",
429
+ color: BRIGHT_BLUE,
430
+ use_native: false,
431
+ )
432
+
433
+ ruby_result = ruby_benchmark.run(files, total_lines)
434
+ ruby_stats = ruby_benchmark.print_summary(files, total_lines, ruby_result)
435
+ ruby_benchmark.print_slowest_schemas(ruby_result)
436
+
437
+ # Native Benchmark (if available)
438
+ if Parsanol::Native.available?
439
+ native_benchmark = ParserBenchmark.new(
440
+ name: "Native Parser (Rust)",
441
+ emoji: "🦀",
442
+ color: BRIGHT_CYAN,
443
+ use_native: true,
444
+ )
445
+
446
+ native_result = native_benchmark.run(files, total_lines)
447
+ native_stats = native_benchmark.print_summary(files, total_lines,
448
+ native_result)
449
+ native_benchmark.print_slowest_schemas(native_result)
450
+
451
+ # Print comparison
452
+ print_comparison(ruby_stats, native_stats, files, total_lines)
453
+ else
454
+ puts "#{BRIGHT_YELLOW}⚠️ Native parser not available. Run `rake compile` in parsanol-ruby to build.#{RESET}"
455
+ puts
456
+ end
457
+
458
+ print_footer
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # SRL Benchmark Script for Expressir - Native Parser Only
5
+ # Tests Rust native parser performance on full STEPmod Resource Library
6
+
7
+ require "bundler/setup"
8
+ require "benchmark"
9
+ require "fileutils"
10
+
11
+ # Force loading of native extension first
12
+ require "parsanol"
13
+ require "parsanol/native"
14
+
15
+ # Now require expressir
16
+ require "expressir"
17
+
18
+ # Configuration
19
+ SRL_PATH = "/Users/mulgogi/src/mn/iso-10303/schemas/resources"
20
+
21
+ def find_exp_files
22
+ Dir.glob("#{SRL_PATH}/*/*.exp")
23
+ end
24
+
25
+ def count_lines(files)
26
+ files.sum { |f| File.read(f).lines.count }
27
+ end
28
+
29
+ puts "=" * 70
30
+ puts "EXPRESSIR SRL BENCHMARK: Native Parser (Rust)"
31
+ puts "=" * 70
32
+ puts
33
+
34
+ # Check native availability
35
+ native_available = Parsanol::Native.available?
36
+ puts "Native parser available: #{native_available ? 'YES' : 'NO'}"
37
+ puts
38
+
39
+ if !native_available
40
+ puts "Native parser not available. Run `rake compile` in parsanol-ruby."
41
+ exit 1
42
+ end
43
+
44
+ # Find all EXPRESS files
45
+ files = find_exp_files
46
+ total_lines = count_lines(files)
47
+
48
+ puts "Files to parse: #{files.size}"
49
+ puts "Total lines of code: #{total_lines}"
50
+ puts
51
+
52
+ if files.empty?
53
+ puts "ERROR: No EXPRESS files found at #{SRL_PATH}"
54
+ exit 1
55
+ end
56
+
57
+ # Warmup - parse first file to ensure everything is loaded
58
+ puts "Warming up..."
59
+ warmup_file = files.first
60
+ begin
61
+ content = File.read(warmup_file)
62
+ Expressir::Express::Parser.from_exp(content, skip_references: true,
63
+ use_native: true)
64
+ puts "Warmup complete."
65
+ rescue StandardError => e
66
+ puts "Warning: Warmup failed: #{e.message}"
67
+ puts e.backtrace.first(5)
68
+ end
69
+ puts
70
+
71
+ # Run Native benchmark
72
+ puts "-" * 70
73
+ puts "NATIVE PARSER BENCHMARK"
74
+ puts "-" * 70
75
+
76
+ results = { success: 0, failed: 0, errors: [], total_time: 0 }
77
+ file_times = []
78
+
79
+ time = Benchmark.realtime do
80
+ files.each_with_index do |file, idx|
81
+ file_start = Time.now
82
+ begin
83
+ content = File.read(file)
84
+ Expressir::Express::Parser.from_exp(content, skip_references: true,
85
+ use_native: true)
86
+ results[:success] += 1
87
+ file_time = Time.now - file_start
88
+ file_times << { file: File.basename(file), time: file_time,
89
+ lines: content.lines.count }
90
+
91
+ # Progress every 10 files
92
+ if ((idx + 1) % 10).zero?
93
+ print "."
94
+ $stdout.flush
95
+ end
96
+ rescue StandardError => e
97
+ results[:failed] += 1
98
+ results[:errors] << { file: File.basename(file),
99
+ error: e.message[0..100] }
100
+ end
101
+ end
102
+ end
103
+ puts
104
+
105
+ results[:total_time] = time
106
+
107
+ # Calculate stats
108
+ lines_per_sec = (total_lines / results[:total_time]).round(1)
109
+ files_per_sec = (files.size / results[:total_time]).round(2)
110
+
111
+ puts
112
+ puts "Native Parser Results:"
113
+ puts " Success: #{results[:success]}/#{files.size} files"
114
+ puts " Failed: #{results[:failed]} files"
115
+ puts " Time: #{results[:total_time].round(2)}s"
116
+ puts " Speed: #{lines_per_sec} lines/sec"
117
+ puts " Speed: #{files_per_sec} files/sec"
118
+
119
+ if results[:failed].positive?
120
+ puts
121
+ puts "Errors (first 10):"
122
+ results[:errors].first(10).each do |err|
123
+ puts " - #{err[:file]}: #{err[:error]}"
124
+ end
125
+ end
126
+
127
+ # Show top 5 slowest files
128
+ puts
129
+ puts "Top 5 slowest files:"
130
+ sorted = file_times.sort_by { |f| -f[:time] }.first(5)
131
+ sorted.each do |f|
132
+ puts " #{f[:file]}: #{f[:time].round(2)}s (#{f[:lines]} lines)"
133
+ end
134
+
135
+ # Show top 5 fastest files
136
+ puts
137
+ puts "Top 5 fastest files:"
138
+ sorted = file_times.sort_by { |f| f[:time] }.first(5)
139
+ sorted.each do |f|
140
+ puts " #{f[:file]}: #{f[:time].round(3)}s (#{f[:lines]} lines)"
141
+ end
142
+
143
+ puts
144
+ puts "=" * 70
145
+ puts "BENCHMARK COMPLETE"
146
+ puts "=" * 70