expressir 1.2.10-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,586 @@
1
+ module Expressir
2
+ module Express
3
+ # Pretty formatter for EXPRESS schemas following ELF specification
4
+ # @see https://www.express-language-foundation.org/pretty-print-spec/
5
+ class PrettyFormatter < Formatter
6
+ # Configuration class for MECE parameter management
7
+ # Follows the hierarchy: Options > ENV > Defaults
8
+ class Configuration
9
+ attr_accessor :indent, :line_length, :insert_newline, :insert_space,
10
+ :provenance_enabled, :provenance_name, :provenance_version
11
+
12
+ # Initialize configuration with options, environment variables, or defaults
13
+ # @param options [Hash] Configuration options
14
+ def initialize(options = {})
15
+ @indent = resolve_param(options, :indent, "EXPRESSIR_INDENT", 4)
16
+ @line_length = resolve_param(options, :line_length,
17
+ "EXPRESSIR_LINE_LENGTH", nil)
18
+ @insert_newline = resolve_param(options, :insert_newline,
19
+ "EXPRESSIR_INSERT_NEWLINE", true)
20
+ @insert_space = resolve_param(options, :insert_space,
21
+ "EXPRESSIR_INSERT_SPACE", true)
22
+ @provenance_enabled = resolve_param(options, :provenance,
23
+ "EXPRESSIR_PROVENANCE", true)
24
+ @provenance_name = resolve_param(options, :provenance_name,
25
+ "EXPRESSIR_PROVENANCE_NAME", "Expressir")
26
+ @provenance_version = resolve_param(options, :provenance_version,
27
+ "EXPRESSIR_PROVENANCE_VERSION", Expressir::Version::VERSION)
28
+ end
29
+
30
+ private
31
+
32
+ # Resolve parameter value following MECE hierarchy: Options > ENV > Defaults
33
+ # @param options [Hash] Options hash
34
+ # @param key [Symbol] Option key
35
+ # @param env_key [String] Environment variable key
36
+ # @param default [Object] Default value
37
+ # @return [Object] Resolved value
38
+ def resolve_param(options, key, env_key, default)
39
+ return options[key] if options.key?(key)
40
+
41
+ env_value = ENV.fetch(env_key, nil)
42
+ return parse_env_value(env_value) unless env_value.nil?
43
+
44
+ default
45
+ end
46
+
47
+ # Parse environment variable value to appropriate type
48
+ # @param value [String] Environment variable value
49
+ # @return [Object] Parsed value
50
+ def parse_env_value(value)
51
+ return nil if value.nil? || value.empty?
52
+ return true if value.downcase == "true"
53
+ return false if value.downcase == "false"
54
+ return value.to_i if /^\d+$/.match?(value)
55
+
56
+ value
57
+ end
58
+ end
59
+
60
+ attr_reader :config
61
+
62
+ # Initialize PrettyFormatter with options
63
+ # @param options [Hash] Formatting options
64
+ # @option options [Integer] :indent Number of spaces per indentation level (default: 4)
65
+ # @option options [Integer, nil] :line_length Maximum line length (default: nil)
66
+ # @option options [Boolean] :insert_newline Insert newlines for readability (default: true)
67
+ # @option options [Boolean] :insert_space Insert spaces for readability (default: true)
68
+ # @option options [Boolean] :provenance Include provenance information (default: true)
69
+ # @option options [String] :provenance_name Tool name for provenance (default: "Expressir")
70
+ # @option options [String] :provenance_version Tool version for provenance (default: Expressir::Version::VERSION)
71
+ # @option options [Boolean] :no_remarks Suppress remarks in output (default: false)
72
+ def initialize(options = {})
73
+ @config = Configuration.new(options)
74
+ super(no_remarks: options.fetch(:no_remarks, false))
75
+ end
76
+
77
+ # Override indent to use configured width
78
+ # @param str [String] String to indent
79
+ # @return [String] Indented string
80
+ def indent(str)
81
+ return if str.nil?
82
+
83
+ indent_str = " " * @config.indent
84
+ str.split("\n").map { |x| "#{indent_str}#{x}" }.join("\n")
85
+ end
86
+
87
+ # Format inline tail remark
88
+ # @param node [Model::ModelElement] Node with potential tail remark
89
+ # @return [String] Tail remark or empty string
90
+ def format_inline_tail_remark(node)
91
+ return "" if @no_remarks
92
+ return "" unless node.is_a?(Model::ModelElement)
93
+ return "" if node.untagged_remarks.nil? || node.untagged_remarks.empty?
94
+
95
+ # Get first untagged remark
96
+ remark = node.untagged_remarks.first
97
+ return "" if remark.nil?
98
+
99
+ return "" unless remark.is_a?(Model::RemarkInfo)
100
+
101
+ text = remark.text
102
+ return "" if text.nil? || text.empty?
103
+
104
+ # Include tag if present
105
+ formatted_text = remark.tagged? ? "\"#{remark.tag}\" #{text}" : text
106
+
107
+ # Use format from RemarkInfo
108
+ remark.tail? ? " -- #{formatted_text}" : " (* #{formatted_text} *)"
109
+ end
110
+
111
+ # Override to preserve tail remarks on attributes
112
+ # @param node [Model::Declarations::Entity] Entity node
113
+ # @return [String] Formatted entity
114
+ def format_declarations_entity(node)
115
+ if node.attributes.nil?
116
+ explicit_attributes = []
117
+ derived_attributes = []
118
+ inverse_attributes = []
119
+ else
120
+ explicit_attributes = node.attributes.select do |x|
121
+ x.kind == Model::Declarations::Attribute::EXPLICIT
122
+ end
123
+ derived_attributes = node.attributes.select do |x|
124
+ x.kind == Model::Declarations::Attribute::DERIVED
125
+ end
126
+ inverse_attributes = node.attributes.select do |x|
127
+ x.kind == Model::Declarations::Attribute::INVERSE
128
+ end
129
+ end
130
+
131
+ indent_str = " " * @config.indent
132
+ preamble = format_preamble_remarks(node, indent_str)
133
+
134
+ [
135
+ [
136
+ "ENTITY",
137
+ " ",
138
+ node.id,
139
+ *if node.abstract && !node.supertype_expression
140
+ [
141
+ "\n",
142
+ indent([
143
+ "ABSTRACT",
144
+ " ",
145
+ "SUPERTYPE",
146
+ ].join),
147
+ ].join
148
+ end,
149
+ *if node.abstract && node.supertype_expression
150
+ [
151
+ "\n",
152
+ indent([
153
+ "ABSTRACT",
154
+ " ",
155
+ "SUPERTYPE",
156
+ " ",
157
+ "OF",
158
+ " ",
159
+ "(",
160
+ format(node.supertype_expression),
161
+ ")",
162
+ ].join),
163
+ ].join
164
+ end,
165
+ *if !node.abstract && node.supertype_expression
166
+ [
167
+ "\n",
168
+ indent([
169
+ "SUPERTYPE",
170
+ " ",
171
+ "OF",
172
+ " ",
173
+ "(",
174
+ format(node.supertype_expression),
175
+ ")",
176
+ ].join),
177
+ ].join
178
+ end,
179
+ *if node.subtype_of&.length&.positive?
180
+ [
181
+ "\n",
182
+ indent([
183
+ "SUBTYPE",
184
+ " ",
185
+ "OF",
186
+ " ",
187
+ "(",
188
+ node.subtype_of.map { |x| format(x) }.join(", "),
189
+ ")",
190
+ ].join),
191
+ ].join
192
+ end,
193
+ ";",
194
+ ].join,
195
+ *if preamble && !preamble.empty?
196
+ preamble.rstrip
197
+ end,
198
+ *if explicit_attributes&.length&.positive?
199
+ indent(explicit_attributes.map { |x| format(x) }.join("\n"))
200
+ end,
201
+ *if derived_attributes&.length&.positive?
202
+ [
203
+ "DERIVE",
204
+ indent(derived_attributes.map { |x| format(x) }.join("\n")),
205
+ ]
206
+ end,
207
+ *if inverse_attributes&.length&.positive?
208
+ [
209
+ "INVERSE",
210
+ indent(inverse_attributes.map { |x| format(x) }.join("\n")),
211
+ ]
212
+ end,
213
+ *if node.unique_rules&.length&.positive?
214
+ [
215
+ "UNIQUE",
216
+ indent(node.unique_rules.map { |x| format(x) }.join("\n")),
217
+ ]
218
+ end,
219
+ *if node.where_rules&.length&.positive?
220
+ [
221
+ "WHERE",
222
+ indent(node.where_rules.map { |x| format(x) }.join("\n")),
223
+ ]
224
+ end,
225
+ [
226
+ "END_ENTITY",
227
+ ";",
228
+ format_end_scope_remark(node),
229
+ ].join,
230
+ ].join("\n")
231
+ end
232
+
233
+ # Format preamble remarks before SCHEMA
234
+ # @param source_remarks [Array<String>] Source remarks
235
+ # @return [Array<String>] Formatted preamble lines
236
+ def format_preamble(source_remarks)
237
+ return [] if source_remarks.nil? || source_remarks.empty?
238
+
239
+ preamble = source_remarks.map do |remark|
240
+ format_preamble_remark(remark)
241
+ end
242
+ preamble << "" # Blank line after preamble
243
+ preamble
244
+ end
245
+
246
+ # Format a single preamble remark
247
+ # @param remark [RemarkInfo] RemarkInfo object
248
+ # @param indent_str [String] Indentation to use (optional)
249
+ # @return [String] Formatted remark
250
+ def format_preamble_remark(remark, _indent_str = "")
251
+ return "" unless remark.is_a?(Model::RemarkInfo)
252
+
253
+ text = remark.text
254
+ return "" if text.nil? || text.empty?
255
+
256
+ # Include tag if present
257
+ text = "\"#{remark.tag}\" #{text}" if remark.tagged?
258
+
259
+ # Use format from RemarkInfo
260
+ if remark.tail?
261
+ "-- #{text}"
262
+ elsif text.include?("\n")
263
+ # Embedded remark - always use embedded format for preamble
264
+ ["(*", text,
265
+ "*)"].join("\n")
266
+ else
267
+ "(* #{text} *)"
268
+ end
269
+ end
270
+
271
+ # Generate provenance remark
272
+ # @return [String, nil] Provenance comment or nil if disabled
273
+ def format_provenance
274
+ return nil unless @config.provenance_enabled
275
+
276
+ params = []
277
+ params << "indent: #{@config.indent}"
278
+ params << "line_length: #{@config.line_length}" if @config.line_length
279
+
280
+ [
281
+ "(*",
282
+ " Generated by: #{@config.provenance_name} version #{@config.provenance_version}",
283
+ " Format parameters: #{params.join(', ')}",
284
+ "*)",
285
+ ].join("\n")
286
+ end
287
+
288
+ # Override to add preamble and provenance
289
+ # @param node [Model::Repository] Repository node
290
+ # @return [String] Formatted repository
291
+ def format_repository(node)
292
+ result = []
293
+
294
+ # Format files if present
295
+ node.files&.each do |exp_file|
296
+ file_output = format_exp_file(exp_file)
297
+ result << file_output if file_output && !file_output.empty?
298
+ end
299
+
300
+ # Handle schemas directly added to repository (not via files)
301
+ direct_schemas = node.schemas.select do |s|
302
+ # Schema is direct if it's not in any file
303
+ node.files.nil? || node.files.none? { |f| f.schemas&.include?(s) }
304
+ end
305
+
306
+ if direct_schemas.any?
307
+ # Add preamble if repository has untagged remarks
308
+ if node.untagged_remarks && !node.untagged_remarks.empty?
309
+ result.concat(format_preamble(node.untagged_remarks))
310
+ end
311
+
312
+ # Add provenance
313
+ provenance = format_provenance
314
+ result << provenance if provenance
315
+ result << "" if provenance
316
+
317
+ # Add schemas
318
+ schemas_output = direct_schemas.map { |x| format(x) }.join("\n\n")
319
+ result << schemas_output if schemas_output
320
+ elsif result.empty?
321
+ # Empty repository - just add provenance
322
+ provenance = format_provenance
323
+ result << provenance if provenance
324
+ end
325
+
326
+ result.any? ? "#{result.join("\n")}\n" : ""
327
+ end
328
+
329
+ # Format ExpFile with file-level preamble
330
+ # @param node [Model::ExpFile] ExpFile node
331
+ # @return [String] Formatted file
332
+ def format_exp_file(node)
333
+ result = []
334
+
335
+ # Add file-level preamble if present (skip provenance remarks from previous parses)
336
+ unless @no_remarks
337
+ source_remarks = (node.untagged_remarks || []).reject do |remark|
338
+ remark.is_a?(Model::RemarkInfo) && provenance_remark?(remark)
339
+ end
340
+ result.concat(format_preamble(source_remarks)) if source_remarks.any?
341
+ end
342
+
343
+ # Add provenance (only for first file or single file)
344
+ provenance = format_provenance
345
+ result << provenance if provenance
346
+ result << "" if provenance
347
+
348
+ # Add schemas
349
+ schemas = node.schemas&.map { |x| format(x) }&.join("\n\n")
350
+ result << schemas if schemas
351
+
352
+ result.empty? ? "" : "#{result.join("\n")}\n"
353
+ end
354
+
355
+ # Format a block of constants with aligned colons and assignments
356
+ # @param constants [Array<Model::Declarations::Constant>] Constants to format
357
+ # @return [String] Formatted constants with alignment
358
+ def format_constant_block(constants)
359
+ return "" if constants.nil? || constants.empty?
360
+
361
+ # Calculate alignment positions
362
+ max_name_len = constants.map { |c| c.id.length }.max
363
+ type_strings = constants.map { |c| format(c.type) }
364
+ max_type_len = type_strings.map(&:length).max
365
+
366
+ # Format each constant with alignment
367
+ formatted = constants.zip(type_strings).map do |constant, type_str|
368
+ name_padding = " " * (max_name_len - constant.id.length)
369
+ type_padding = " " * (max_type_len - type_str.length)
370
+
371
+ "#{constant.id}#{name_padding} : #{type_str}#{type_padding} := #{format(constant.expression)};"
372
+ end
373
+
374
+ formatted.join("\n")
375
+ end
376
+
377
+ # Override format_declarations_schema to use aligned constants
378
+ # @param node [Model::Declarations::Schema] Schema node
379
+ # @return [String] Formatted schema
380
+ def format_declarations_schema(node)
381
+ indent_str = " " * @config.indent
382
+ preamble = format_preamble_remarks(node, indent_str)
383
+ schema_declarations = [
384
+ *if node.constants&.length&.positive?
385
+ [
386
+ "CONSTANT",
387
+ indent(format_constant_block(node.constants)),
388
+ [
389
+ "END_CONSTANT",
390
+ ";",
391
+ ].join,
392
+ ].join("\n")
393
+ end,
394
+ *if node.types&.length&.positive?
395
+ node.types.map { |x| format(x) }
396
+ end,
397
+ *if node.entities&.length&.positive?
398
+ node.entities.map { |x| format(x) }
399
+ end,
400
+ *if node.subtype_constraints&.length&.positive?
401
+ node.subtype_constraints.map { |x| format(x) }
402
+ end,
403
+ *if node.functions&.length&.positive?
404
+ node.functions.map { |x| format(x) }
405
+ end,
406
+ *if node.rules&.length&.positive?
407
+ node.rules.map { |x| format(x) }
408
+ end,
409
+ *if node.procedures&.length&.positive?
410
+ node.procedures.map { |x| format(x) }
411
+ end,
412
+ ]
413
+
414
+ [
415
+ format_declarations_schema_head(node),
416
+ *if preamble && !preamble.empty?
417
+ [
418
+ "",
419
+ preamble.rstrip,
420
+ ]
421
+ end,
422
+ *if schema_declarations&.length&.positive?
423
+ [
424
+ "",
425
+ schema_declarations.join("\n\n"),
426
+ "",
427
+ ]
428
+ end,
429
+ [
430
+ "END_SCHEMA",
431
+ ";",
432
+ format_end_scope_remark(node),
433
+ ].join,
434
+ ].join("\n")
435
+ end
436
+
437
+ # Override format_declarations_function to use aligned constants
438
+ def format_declarations_function(node)
439
+ [
440
+ [
441
+ "FUNCTION",
442
+ " ",
443
+ node.id,
444
+ *if node.parameters&.length&.positive?
445
+ parameter_indent = " " * "FUNCTION #{node.id}(".length
446
+ [
447
+ "(",
448
+ node.parameters.map do |x|
449
+ format(x)
450
+ end.join(";\n#{parameter_indent}"),
451
+ ")",
452
+ ].join
453
+ end,
454
+ " ",
455
+ ":",
456
+ " ",
457
+ format(node.return_type),
458
+ ";",
459
+ ].join,
460
+ *format_scope_body(node),
461
+ format_scope_footer("END_FUNCTION", node),
462
+ ].join("\n")
463
+ end
464
+
465
+ # Override format_declarations_procedure to use aligned constants
466
+ def format_declarations_procedure(node)
467
+ [
468
+ [
469
+ "PROCEDURE",
470
+ " ",
471
+ node.id,
472
+ *if node.parameters&.length&.positive?
473
+ parameter_indent = " " * "PROCEDURE #{node.id}(".length
474
+ [
475
+ "(",
476
+ node.parameters.map do |x|
477
+ format(x)
478
+ end.join(";\n#{parameter_indent}"),
479
+ ")",
480
+ ].join
481
+ end,
482
+ ";",
483
+ ].join,
484
+ *format_scope_body(node),
485
+ format_scope_footer("END_PROCEDURE", node),
486
+ ].join("\n")
487
+ end
488
+
489
+ # Override format_declarations_rule to use aligned constants
490
+ def format_declarations_rule(node)
491
+ [
492
+ [
493
+ "RULE",
494
+ " ",
495
+ node.id,
496
+ " ",
497
+ "FOR",
498
+ " ",
499
+ "(",
500
+ Array(node.applies_to).map { |x| format(x) }.join(", "),
501
+ ")",
502
+ ";",
503
+ ].join,
504
+ *format_scope_body(node),
505
+ *if node.where_rules&.length&.positive?
506
+ [
507
+ "WHERE",
508
+ indent(node.where_rules.map { |x| format(x) }.join("\n")),
509
+ ]
510
+ end,
511
+ *format_trailing_region_remarks(node, :where_rules).map { |x| indent(x) },
512
+ format_scope_footer("END_RULE", node),
513
+ ].join("\n")
514
+ end
515
+
516
+ # Shared scope body for function, procedure, and rule declarations.
517
+ # These declaration types share the same internal structure:
518
+ # types, entities, subtype_constraints, functions, procedures,
519
+ # constants, variables, statements.
520
+ def format_scope_body(node)
521
+ [
522
+ *if node.types&.length&.positive?
523
+ indent(node.types.map { |x| format(x) }.join("\n"))
524
+ end,
525
+ *if node.entities&.length&.positive?
526
+ indent(node.entities.map { |x| format(x) }.join("\n"))
527
+ end,
528
+ *if node.subtype_constraints&.length&.positive?
529
+ indent(node.subtype_constraints.map { |x| format(x) }.join("\n"))
530
+ end,
531
+ *if node.functions&.length&.positive?
532
+ indent(node.functions.map { |x| format(x) }.join("\n"))
533
+ end,
534
+ *if node.procedures&.length&.positive?
535
+ indent(node.procedures.map { |x| format(x) }.join("\n"))
536
+ end,
537
+ *if node.constants&.length&.positive?
538
+ indent([
539
+ "CONSTANT",
540
+ indent(format_constant_block(node.constants)),
541
+ [
542
+ "END_CONSTANT",
543
+ ";",
544
+ ].join,
545
+ ].join("\n"))
546
+ end,
547
+ *if node.variables&.length&.positive?
548
+ indent([
549
+ "LOCAL",
550
+ indent(node.variables.map { |x| format(x) }.join("\n")),
551
+ [
552
+ "END_LOCAL",
553
+ ";",
554
+ ].join,
555
+ ].join("\n"))
556
+ end,
557
+ *if node.statements&.length&.positive?
558
+ indent(node.statements.map { |x| format(x) }.join("\n"))
559
+ end,
560
+ # Comments closing the executable body. This formatter overrides
561
+ # the declaration formatters, so the emitter is repeated here or
562
+ # trailing remarks would render only in standard output. For a
563
+ # RULE the body ends at WHERE, which follows this block.
564
+ *format_trailing_region_remarks(node, :statements).map { |x| indent(x) },
565
+ ]
566
+ end
567
+
568
+ def format_scope_footer(keyword, node)
569
+ [
570
+ keyword,
571
+ ";",
572
+ format_end_scope_remark(node),
573
+ ].join
574
+ end
575
+
576
+ # Check if a remark was generated by this formatter's provenance output.
577
+ # Prevents provenance comments from accumulating on round-trip re-parses.
578
+ def provenance_remark?(remark)
579
+ return false unless remark.is_a?(Model::RemarkInfo)
580
+
581
+ text = remark.text.to_s
582
+ text.match?(/\A\s*(Generated by:|Format parameters:)/)
583
+ end
584
+ end
585
+ end
586
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Express
5
+ # Pretty round-trip gate (parity-ee stage 08 / eeng qualify.sh
6
+ # self-check): parse → format → re-parse and compare a structural
7
+ # fingerprint of the model. Byte identity is deliberately not
8
+ # required — remark placement and whitespace drift are tolerated;
9
+ # declaration inventory and interface shape must match.
10
+ class PrettyGate
11
+ Result = Struct.new(:path, :ok, :before, :after, :error, keyword_init: true)
12
+
13
+ def self.fingerprint(schema)
14
+ {
15
+ id: schema.id&.safe_downcase,
16
+ interfaces: Array(schema.interfaces).map do |i|
17
+ {
18
+ kind: i.kind,
19
+ schema: (i.schema.is_a?(String) ? i.schema : i.schema&.id)&.safe_downcase,
20
+ items: Array(i.items).map do |entry|
21
+ ref = entry.ref.is_a?(String) ? entry.ref : entry.ref&.id
22
+ [entry.id&.safe_downcase, ref&.safe_downcase]
23
+ end.sort,
24
+ }
25
+ end,
26
+ constants: ids_of(schema.constants),
27
+ types: ids_of(schema.types),
28
+ entities: ids_of(schema.entities),
29
+ subtype_constraints: ids_of(schema.subtype_constraints),
30
+ functions: ids_of(schema.functions),
31
+ procedures: ids_of(schema.procedures),
32
+ rules: ids_of(schema.rules),
33
+ entity_attrs: Array(schema.entities).to_h do |e|
34
+ [e.id.safe_downcase, Array(e.attributes).filter_map { |a| a.id&.safe_downcase }.sort]
35
+ end,
36
+ }
37
+ end
38
+
39
+ def self.ids_of(coll)
40
+ Array(coll).filter_map { |d| d.id&.safe_downcase }.sort
41
+ end
42
+
43
+ def self.check_file(path)
44
+ source = File.read(path)
45
+ original = Parser.from_exp(source, skip_references: true)
46
+ formatted = Formatter.format(original)
47
+ roundtrip = Parser.from_exp(formatted, skip_references: true)
48
+
49
+ before = original.schemas.map { |s| fingerprint(s) }
50
+ after = roundtrip.schemas.map { |s| fingerprint(s) }
51
+ Result.new(path: path, ok: before == after, before: before, after: after)
52
+ rescue StandardError => e
53
+ Result.new(path: path, ok: false, error: e)
54
+ end
55
+
56
+ def self.check_files(paths)
57
+ paths.map { |p| check_file(p) }
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Express
5
+ # Resolved references are deterministic given the sources and the
6
+ # repository composition, so the batch path persists every
7
+ # SimpleReference's final base_path as an overlay next to the
8
+ # compiled-set artifact; warm loads re-apply them instead of
9
+ # re-running the {ResolveReferencesModelVisitor}.
10
+ module RefsOverlay
11
+ module_function
12
+
13
+ def write(set_path, models)
14
+ refs = {}
15
+ ModelTraversal.each_model(models) do |node, key|
16
+ next unless node.is_a?(Model::References::SimpleReference)
17
+ next if node.base_path.nil?
18
+
19
+ refs[key] = node.base_path
20
+ end
21
+ return if refs.empty?
22
+
23
+ require "json"
24
+ File.write("#{set_path}.refs.json", JSON.generate(refs))
25
+ end
26
+
27
+ # Re-binds resolved references from the compiled-set refs
28
+ # sidecar. Returns the sidecar path when it was applied (caller
29
+ # then skips the resolver); nil when absent — caller falls back
30
+ # to resolving.
31
+ def apply_to(set_path, models)
32
+ refs_path = "#{set_path}.refs.json"
33
+ return nil unless File.exist?(refs_path)
34
+
35
+ require "json"
36
+ refs = JSON.parse(File.read(refs_path))
37
+ ModelTraversal.each_model(models) do |node, key|
38
+ next unless node.is_a?(Model::References::SimpleReference)
39
+
40
+ base_path = refs[key]
41
+ node.base_path = base_path unless base_path.nil?
42
+ end
43
+ refs_path
44
+ end
45
+ end
46
+ end
47
+ end