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,510 @@
1
+ require "pathname"
2
+
3
+ module Expressir
4
+ # Coverage module for calculating documentation coverage of EXPRESS entities
5
+ module Coverage
6
+ # Single source of truth: EXPRESS type name → Ruby class name pairs.
7
+ # ENTITY_TYPE_MAP and CLASS_TO_EXPRESS_TYPE_MAP are derived from this
8
+ # so the two never drift out of sync.
9
+ TYPE_PAIRS = [
10
+ ["TYPE", "Expressir::Model::Declarations::Type"],
11
+ ["ENTITY", "Expressir::Model::Declarations::Entity"],
12
+ ["CONSTANT", "Expressir::Model::Declarations::Constant"],
13
+ ["FUNCTION", "Expressir::Model::Declarations::Function"],
14
+ ["RULE", "Expressir::Model::Declarations::Rule"],
15
+ ["PROCEDURE", "Expressir::Model::Declarations::Procedure"],
16
+ ["SUBTYPE_CONSTRAINT", "Expressir::Model::Declarations::SubtypeConstraint"],
17
+ ["PARAMETER", "Expressir::Model::Declarations::Parameter"],
18
+ ["VARIABLE", "Expressir::Model::Declarations::Variable"],
19
+ ["ATTRIBUTE", "Expressir::Model::Declarations::Attribute"],
20
+ ["DERIVED_ATTRIBUTE", "Expressir::Model::Declarations::DerivedAttribute"],
21
+ ["INVERSE_ATTRIBUTE", "Expressir::Model::Declarations::InverseAttribute"],
22
+ ["UNIQUE_RULE", "Expressir::Model::Declarations::UniqueRule"],
23
+ ["WHERE_RULE", "Expressir::Model::Declarations::WhereRule"],
24
+ ["ENUMERATION_ITEM", "Expressir::Model::DataTypes::EnumerationItem"],
25
+ ["INTERFACE", "Expressir::Model::Declarations::Interface"],
26
+ ["INTERFACE_ITEM", "Expressir::Model::Declarations::InterfaceItem"],
27
+ ["INTERFACED_ITEM", "Expressir::Model::Declarations::InterfacedItem"],
28
+ ["SCHEMA_VERSION", "Expressir::Model::Declarations::SchemaVersion"],
29
+ ["SCHEMA_VERSION_ITEM", "Expressir::Model::Declarations::SchemaVersionItem"],
30
+ ].freeze
31
+
32
+ # EXPRESS entity type name → Ruby class name.
33
+ ENTITY_TYPE_MAP = TYPE_PAIRS.to_h.freeze
34
+
35
+ # Short Ruby class name → EXPRESS entity type name.
36
+ CLASS_TO_EXPRESS_TYPE_MAP = TYPE_PAIRS.to_h do |express_name, ruby_class|
37
+ [ruby_class.split("::").last, express_name]
38
+ end.freeze
39
+
40
+ # Available TYPE subtypes based on data types
41
+ TYPE_SUBTYPES = %w[
42
+ AGGREGATE ARRAY BAG BINARY BOOLEAN ENUMERATION GENERIC GENERIC_ENTITY
43
+ INTEGER LIST LOGICAL NUMBER REAL SELECT SET STRING
44
+ ].freeze
45
+ # Represents a documentation coverage report for EXPRESS schemas
46
+ class Report
47
+ attr_reader :repository, :schema_reports, :total_entities, :documented_entities,
48
+ :undocumented_entities, :ignored_files
49
+
50
+ # Initialize a coverage report
51
+ # @param repository [Expressir::Model::Repository] The repository to analyze
52
+ # @param skip_types [Array<String>] Array of entity type names to skip from coverage
53
+ # @param ignored_files [Hash] Hash mapping absolute file paths to their matched patterns
54
+ def initialize(repository, skip_types = [], ignored_files = {})
55
+ @repository = repository
56
+ @skip_types = skip_types
57
+ @ignored_files = ignored_files
58
+ @schema_reports = []
59
+ @total_entities = []
60
+ @documented_entities = []
61
+ @undocumented_entities = []
62
+
63
+ process_repository
64
+ end
65
+
66
+ # Create a report from a repository
67
+ # @param repository [Expressir::Model::Repository] The repository to analyze
68
+ # @param skip_types [Array<String>] Array of entity type names to skip from coverage
69
+ # @param ignored_files [Hash] Hash mapping absolute file paths to their matched patterns
70
+ # @return [Report] The coverage report
71
+ def self.from_repository(repository, skip_types = [], ignored_files = {})
72
+ new(repository, skip_types, ignored_files)
73
+ end
74
+
75
+ # Create a report from a schema file
76
+ # @param path [String] Path to the schema file
77
+ # @param skip_types [Array<String>] Array of entity type names to skip from coverage
78
+ # @param ignored_files [Hash] Hash mapping absolute file paths to their matched patterns
79
+ # @return [Report] The coverage report
80
+ def self.from_file(path, skip_types = [], ignored_files = {})
81
+ repository = Expressir::Express::Parser.from_file(path)
82
+ new(repository, skip_types, ignored_files)
83
+ end
84
+
85
+ # Calculate the overall coverage percentage
86
+ # @return [Float] The coverage percentage
87
+ def coverage_percentage
88
+ return 100.0 if @total_entities.empty?
89
+
90
+ (@documented_entities.size.to_f / @total_entities.size) * 100
91
+ end
92
+
93
+ # Get file-level reports
94
+ # @return [Array<Hash>] Array of file report hashes
95
+ def file_reports
96
+ @schema_reports.map do |report|
97
+ absolute_path = report[:schema].file
98
+ relative_path = begin
99
+ Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
100
+ rescue ArgumentError
101
+ # If paths are on different drives or otherwise incompatible,
102
+ # fall back to the absolute path
103
+ absolute_path
104
+ end
105
+
106
+ file_report = {
107
+ "file" => relative_path,
108
+ "file_basename" => File.basename(absolute_path),
109
+ "directory" => File.dirname(absolute_path),
110
+ "total" => report[:total].size,
111
+ "documented" => report[:documented].size,
112
+ "undocumented" => report[:undocumented],
113
+ "coverage" => report[:coverage],
114
+ "ignored" => report[:ignored] || false,
115
+ }
116
+
117
+ # Add matched pattern for ignored files
118
+ if report[:ignored] && report[:matched_pattern]
119
+ file_report["matched_pattern"] = report[:matched_pattern]
120
+ end
121
+
122
+ file_report
123
+ end
124
+ end
125
+
126
+ # Get ignored file reports
127
+ # @return [Array<Hash>] Array of ignored file report hashes
128
+ def ignored_file_reports
129
+ @schema_reports.select { |report| report[:ignored] }.map do |report|
130
+ absolute_path = report[:schema].file
131
+ relative_path = begin
132
+ Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
133
+ rescue ArgumentError
134
+ # If paths are on different drives or otherwise incompatible,
135
+ # fall back to the absolute path
136
+ absolute_path
137
+ end
138
+
139
+ {
140
+ "file" => relative_path,
141
+ "file_basename" => File.basename(absolute_path),
142
+ "directory" => File.dirname(absolute_path),
143
+ "total" => report[:total].size,
144
+ "documented" => report[:documented].size,
145
+ "undocumented" => report[:undocumented],
146
+ "coverage" => report[:coverage],
147
+ "matched_pattern" => report[:matched_pattern],
148
+ }
149
+ end
150
+ end
151
+
152
+ # Get directory-level reports
153
+ # @return [Array<Hash>] Array of directory report hashes
154
+ def directory_reports
155
+ # Group by directory (absolute path)
156
+ by_directory = file_reports.group_by { |r| r["directory"] }
157
+
158
+ # Aggregate stats for each directory
159
+ by_directory.map do |directory, reports|
160
+ # Convert directory to relative path
161
+ relative_directory = begin
162
+ Pathname.new(directory).relative_path_from(Pathname.pwd).to_s
163
+ rescue ArgumentError
164
+ # If paths are on different drives or otherwise incompatible,
165
+ # fall back to the absolute path
166
+ directory
167
+ end
168
+
169
+ total = reports.sum { |r| r["total"] }
170
+ documented = reports.sum { |r| r["documented"] }
171
+ coverage = total.positive? ? (documented.to_f / total) * 100 : 100.0
172
+
173
+ {
174
+ "directory" => relative_directory,
175
+ "total" => total,
176
+ "documented" => documented,
177
+ "undocumented" => total - documented,
178
+ "coverage" => coverage,
179
+ "files" => reports.size,
180
+ }
181
+ end
182
+ end
183
+
184
+ # Convert report to a hash representation
185
+ # @return [Hash] The report as a hash
186
+ def to_h
187
+ {
188
+ "overall" => {
189
+ "total" => @total_entities.size,
190
+ "documented" => @documented_entities.size,
191
+ "undocumented" => @undocumented_entities.size,
192
+ "coverage" => coverage_percentage,
193
+ },
194
+ "directories" => directory_reports,
195
+ "files" => file_reports,
196
+ }
197
+ end
198
+
199
+ private
200
+
201
+ # Process the repository and collect coverage information
202
+ def process_repository
203
+ return unless @repository
204
+
205
+ @repository.schemas.each do |schema|
206
+ schema_report = process_schema(schema)
207
+ @schema_reports << schema_report
208
+
209
+ # Only include non-ignored files in overall statistics
210
+ unless schema_report[:ignored]
211
+ @total_entities.concat(schema_report[:total])
212
+ @documented_entities.concat(schema_report[:documented])
213
+ @undocumented_entities.concat(schema_report[:undocumented].map do |entity|
214
+ { schema: schema.id, entity: entity }
215
+ end)
216
+ end
217
+ end
218
+ end
219
+
220
+ # Process a schema and collect coverage information
221
+ # @param schema [Expressir::Model::Declarations::Schema] The schema to analyze
222
+ # @return [Hash] A hash with coverage information
223
+ def process_schema(schema)
224
+ entities = Expressir::Coverage.find_entities(schema, @skip_types)
225
+ documented = entities.select do |e|
226
+ Expressir::Coverage.entity_documented?(e)
227
+ end
228
+ undocumented = entities - documented
229
+
230
+ coverage = entities.empty? ? 100.0 : (documented.size.to_f / entities.size) * 100
231
+
232
+ # Check if this schema file is ignored
233
+ schema_file = File.expand_path(schema.file) if schema.file
234
+ ignored = @ignored_files.key?(schema_file)
235
+ matched_pattern = @ignored_files[schema_file] if ignored
236
+
237
+ {
238
+ schema: schema,
239
+ total: entities,
240
+ documented: documented,
241
+ undocumented: undocumented.map { |e| format_entity(e) },
242
+ coverage: coverage,
243
+ ignored: ignored,
244
+ matched_pattern: matched_pattern,
245
+ }
246
+ end
247
+
248
+ # Format an entity for display
249
+ # @param entity [Expressir::Model::ModelElement] The entity to format
250
+ # @return [Hash] A hash with type and name of the entity
251
+ def format_entity(entity)
252
+ # Get class name (e.g., "Type" from "Expressir::Model::Declarations::Type")
253
+ type_name = entity.class.name.split("::").last
254
+
255
+ # Use proper mapping to EXPRESS convention
256
+ express_type = CLASS_TO_EXPRESS_TYPE_MAP[type_name] || type_name.upcase
257
+
258
+ # Return structured format
259
+ {
260
+ "type" => express_type,
261
+ "name" => entity.id.to_s,
262
+ }
263
+ end
264
+ end
265
+
266
+ # Check if an entity has documentation (remarks)
267
+ # @param entity [Expressir::Model::ModelElement] The entity to check
268
+ # @return [Boolean] True if the entity has documentation
269
+ def self.entity_documented?(entity)
270
+ # Check for direct remarks (types with Identifier module have remarks)
271
+ if entity.is_a?(Model::HasRemarks) && entity.remarks && !entity.remarks.empty?
272
+ return true
273
+ end
274
+
275
+ # Check for remark_items (types with Identifier module have remark_items)
276
+ if entity.is_a?(Model::HasRemarkItems) && entity.remark_items && !entity.remark_items.empty?
277
+ return true
278
+ end
279
+
280
+ # For schema entities, check if there's a remark_item with their ID
281
+ parent = entity.parent
282
+ if parent.is_a?(Model::HasRemarkItems) && parent.remark_items
283
+ entity_id = entity.id.to_s.downcase
284
+ parent.remark_items.any? do |item|
285
+ item.id.to_s.downcase == entity_id || item.id.to_s.downcase.include?("#{entity_id}.")
286
+ end
287
+ else
288
+ false
289
+ end
290
+ end
291
+
292
+ # Find all entities in a schema or repository
293
+ # @param schema_or_repo [Expressir::Model::Declarations::Schema, Expressir::Model::Repository, Expressir::Model::ExpFile] The schema, repository, or file to analyze
294
+ # @param skip_types [Array<String>] Array of entity type names to skip from coverage
295
+ # @return [Array<Expressir::Model::ModelElement>] Array of entities
296
+ def self.find_entities(schema_or_repo, skip_types = [])
297
+ entities = []
298
+
299
+ # Handle repository, exp_file, and schema inputs
300
+ if schema_or_repo.is_a?(Expressir::Model::Repository)
301
+ # If it's a repository, process all files
302
+ schema_or_repo.files&.each do |file|
303
+ file.schemas&.each do |schema|
304
+ entities.concat(find_entities_in_schema(schema))
305
+ end
306
+ end
307
+ elsif schema_or_repo.is_a?(Expressir::Model::ExpFile)
308
+ # If it's an ExpFile, process all schemas in the file
309
+ schema_or_repo.schemas&.each do |schema|
310
+ entities.concat(find_entities_in_schema(schema))
311
+ end
312
+ else
313
+ # If it's a schema, process it directly
314
+ entities.concat(find_entities_in_schema(schema_or_repo))
315
+ end
316
+
317
+ # Filter out nil elements and ensure all have IDs
318
+ # Note: Some ModelElement types (like Interface) don't have id
319
+ entities = entities.compact.select do |e|
320
+ e.is_a?(Model::HasId) && e.id
321
+ end
322
+
323
+ # Filter out skipped entity types
324
+ apply_exclusions(entities, skip_types)
325
+ end
326
+
327
+ # Find all entities in a single schema
328
+ # @param schema [Expressir::Model::Declarations::Schema] The schema to analyze
329
+ # @return [Array<Expressir::Model::ModelElement>] Array of entities
330
+ def self.find_entities_in_schema(schema)
331
+ entities = []
332
+
333
+ # Add all schema-level entities
334
+ entities.concat(schema.constants) if schema.constants
335
+ entities.concat(schema.types) if schema.types
336
+ entities.concat(schema.entities) if schema.entities
337
+ entities.concat(schema.functions) if schema.functions
338
+ entities.concat(schema.rules) if schema.rules
339
+ entities.concat(schema.procedures) if schema.procedures
340
+ entities.concat(schema.subtype_constraints) if schema.subtype_constraints
341
+ entities.concat(schema.interfaces) if schema.interfaces
342
+
343
+ # Add nested entities recursively
344
+ entities.concat(find_nested_entities(schema))
345
+
346
+ entities
347
+ end
348
+
349
+ # Find all nested entities within a container (schema, entity, function, etc.)
350
+ # @param container [Expressir::Model::ModelElement] The container to search
351
+ # @return [Array<Expressir::Model::ModelElement>] Array of nested entities
352
+ def self.find_nested_entities(container)
353
+ entities = []
354
+
355
+ # Handle different container types
356
+ case container
357
+ when Expressir::Model::Declarations::Schema
358
+ # Schema-level nested entities
359
+ container.types&.each do |type|
360
+ entities.concat(find_nested_entities(type))
361
+ end
362
+ container.entities&.each do |entity|
363
+ entities.concat(find_nested_entities(entity))
364
+ end
365
+ container.functions&.each do |function|
366
+ entities.concat(find_nested_entities(function))
367
+ end
368
+ container.rules&.each do |rule|
369
+ entities.concat(find_nested_entities(rule))
370
+ end
371
+ container.procedures&.each do |procedure|
372
+ entities.concat(find_nested_entities(procedure))
373
+ end
374
+ container.interfaces&.each do |interface|
375
+ entities.concat(find_nested_entities(interface))
376
+ end
377
+
378
+ when Expressir::Model::Declarations::Type
379
+ # Type nested entities
380
+ if container.enumeration_items
381
+ entities.concat(container.enumeration_items)
382
+ end
383
+
384
+ when Expressir::Model::Declarations::Entity
385
+ # Entity nested entities
386
+ entities.concat(container.attributes) if container.attributes
387
+ entities.concat(container.unique_rules) if container.unique_rules
388
+ entities.concat(container.where_rules) if container.where_rules
389
+
390
+ when Expressir::Model::Declarations::Function,
391
+ Expressir::Model::Declarations::Procedure,
392
+ Expressir::Model::Declarations::Rule
393
+ # All three scope containers follow the same nested-entity pattern.
394
+ # Derive the collection list from the model's own declaration rather
395
+ # than maintaining three separate hand-coded branches.
396
+ nested_attrs = container.class.collection_attributes_list - %i[
397
+ statements remark_items where_rules informal_propositions applies_to
398
+ ]
399
+ nested_attrs.each do |attr|
400
+ collection = container.public_send(attr)
401
+ entities.concat(collection) if collection.is_a?(Array)
402
+ end
403
+
404
+ # Recursively find nested entities in nested scope containers
405
+ %i[types entities functions procedures].each do |attr|
406
+ collection = container.public_send(attr)
407
+ collection&.each { |item| entities.concat(find_nested_entities(item)) }
408
+ end
409
+
410
+ when Expressir::Model::Declarations::Interface
411
+ # Interface nested entities
412
+ entities.concat(container.items) if container.items
413
+ end
414
+
415
+ entities
416
+ end
417
+
418
+ # Apply exclusions to filter out entities based on skip_types (supports both simple and TYPE:SUBTYPE syntax)
419
+ # @param entities [Array<Expressir::Model::ModelElement>] The entities to filter
420
+ # @param exclusions [Array<String>] Array of entity type names to exclude from coverage
421
+ # @return [Array<Expressir::Model::ModelElement>] Filtered entities
422
+ def self.apply_exclusions(entities, exclusions)
423
+ filter_skipped_entities(entities, exclusions)
424
+ end
425
+
426
+ # Filter out entities based on skip_types (supports both simple and TYPE:SUBTYPE syntax)
427
+ # @param entities [Array<Expressir::Model::ModelElement>] The entities to filter
428
+ # @param skip_types [Array<String>] Array of entity type names to skip from coverage
429
+ # @return [Array<Expressir::Model::ModelElement>] Filtered entities
430
+ def self.filter_skipped_entities(entities, skip_types)
431
+ return entities if skip_types.empty?
432
+
433
+ # Parse skip_types into simple types, TYPE subtypes, and FUNCTION subtypes
434
+ simple_skips = []
435
+ type_subtype_skips = []
436
+ function_subtype_skips = []
437
+
438
+ skip_types.each do |skip_type|
439
+ if skip_type.include?(":")
440
+ # Handle TYPE:SUBTYPE and FUNCTION:SUBTYPE format
441
+ main_type, subtype = skip_type.split(":", 2)
442
+ if main_type == "TYPE" && TYPE_SUBTYPES.include?(subtype)
443
+ type_subtype_skips << subtype
444
+ elsif main_type == "FUNCTION" && subtype == "INNER"
445
+ function_subtype_skips << subtype
446
+ end
447
+ else
448
+ # Handle simple type format
449
+ simple_skips << skip_type
450
+ end
451
+ end
452
+
453
+ # Filter entities
454
+ entities.reject do |entity|
455
+ entity_class = entity.class.name
456
+
457
+ # Check simple type exclusions
458
+ # Convert entity class to EXPRESS type name for comparison
459
+ class_name = entity_class.split("::").last
460
+ express_type = CLASS_TO_EXPRESS_TYPE_MAP[class_name] || class_name.upcase
461
+
462
+ if simple_skips.include?(express_type)
463
+ true
464
+ # Check TYPE subtype exclusions
465
+ elsif entity_class == "Expressir::Model::Declarations::Type" && type_subtype_skips.any?
466
+ entity_subtype = get_type_subtype(entity)
467
+ type_subtype_skips.include?(entity_subtype)
468
+ # Check FUNCTION:INNER exclusions
469
+ elsif entity_class == "Expressir::Model::Declarations::Function" && function_subtype_skips.include?("INNER")
470
+ inner_function?(entity)
471
+ else
472
+ false
473
+ end
474
+ end
475
+ end
476
+
477
+ # Get the subtype of a TYPE entity based on its underlying_type
478
+ # @param type_entity [Expressir::Model::Declarations::Type] The TYPE entity
479
+ # @return [String] The subtype name (e.g., "SELECT", "ENUMERATION")
480
+ def self.get_type_subtype(type_entity)
481
+ return nil unless type_entity.is_a?(Model::Declarations::Type) && type_entity.underlying_type
482
+
483
+ # Get the class name of the underlying type
484
+ underlying_class = type_entity.underlying_type.class.name
485
+
486
+ # Extract the data type name (e.g., "Select" from "Expressir::Model::DataTypes::Select")
487
+ if underlying_class.start_with?("Expressir::Model::DataTypes::")
488
+ data_type_name = underlying_class.split("::").last
489
+ # Convert to uppercase (e.g., "Select" -> "SELECT")
490
+ data_type_name.upcase
491
+ else
492
+ # For other types, try to extract the last part of the class name
493
+ underlying_class.split("::").last&.upcase
494
+ end
495
+ end
496
+
497
+ # Check if a function is an inner function (nested within another function, rule, or procedure)
498
+ # @param function_entity [Expressir::Model::Declarations::Function] The function entity to check
499
+ # @return [Boolean] True if the function is nested within another function, rule, or procedure
500
+ def self.inner_function?(function_entity)
501
+ return false unless function_entity.is_a?(Model::Declarations::Function) && function_entity.parent
502
+
503
+ # Check if the parent is a function, rule, or procedure (not a schema)
504
+ parent = function_entity.parent
505
+ parent.is_a?(Expressir::Model::Declarations::Function) ||
506
+ parent.is_a?(Expressir::Model::Declarations::Rule) ||
507
+ parent.is_a?(Expressir::Model::Declarations::Procedure)
508
+ end
509
+ end
510
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Eengine
5
+ # Represents an Eengine ARM comparison XML report
6
+ class ArmCompareReport < Lutaml::Model::Serializable
7
+ attribute :modifications, ChangesSection
8
+ attribute :additions, ChangesSection
9
+ attribute :deletions, ChangesSection
10
+
11
+ xml do
12
+ element "arm.changes"
13
+ map_element "arm.modifications", to: :modifications
14
+ map_element "arm.additions", to: :additions
15
+ map_element "arm.deletions", to: :deletions
16
+ end
17
+
18
+ def mode
19
+ "arm"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Eengine
5
+ # Represents a section of changes (modifications, additions, or deletions)
6
+ # in an Eengine comparison report
7
+ class ChangesSection < Lutaml::Model::Serializable
8
+ attribute :modified_objects, ModifiedObject, collection: true
9
+
10
+ xml do
11
+ element "changes.section"
12
+ map_element "modified.object", to: :modified_objects
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Eengine
5
+ # Represents an Eengine comparison XML report
6
+ # Supports three modes: ARM, MIM, and Schema
7
+ class CompareReport < Lutaml::Model::Serializable
8
+ attribute :modifications, ChangesSection
9
+ attribute :additions, ChangesSection
10
+ attribute :deletions, ChangesSection
11
+
12
+ xml do
13
+ element "schema.changes"
14
+ map_element "schema.modifications", to: :modifications
15
+ map_element "schema.additions", to: :additions
16
+ map_element "schema.deletions", to: :deletions
17
+ end
18
+
19
+ class << self
20
+ # Parse XML and return appropriate report class based on mode
21
+ #
22
+ # @param xml_content [String] XML content
23
+ # @return [CompareReport, ArmCompareReport, MimCompareReport]
24
+ def from_xml(xml_content)
25
+ mode = detect_mode(xml_content)
26
+
27
+ case mode
28
+ when "arm"
29
+ ArmCompareReport.from_xml(xml_content)
30
+ when "mim"
31
+ MimCompareReport.from_xml(xml_content)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ # Load a CompareReport from an XML file
38
+ #
39
+ # @param path [String] Path to the XML file
40
+ # @return [CompareReport] The loaded comparison report
41
+ def from_file(path)
42
+ from_xml(File.read(path))
43
+ end
44
+
45
+ private
46
+
47
+ # Detect XML mode from content
48
+ #
49
+ # @param xml_content [String] XML content
50
+ # @return [String] "arm", "mim", or "schema"
51
+ def detect_mode(xml_content)
52
+ if xml_content.include?("<arm.changes")
53
+ "arm"
54
+ elsif xml_content.include?("<mim.changes")
55
+ "mim"
56
+ else
57
+ "schema"
58
+ end
59
+ end
60
+ end
61
+
62
+ # Detect XML mode from the report
63
+ # @return [String] "arm", "mim", or "schema"
64
+ def mode
65
+ "schema"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Eengine
5
+ # Represents an Eengine MIM comparison XML report
6
+ class MimCompareReport < Lutaml::Model::Serializable
7
+ attribute :modifications, ChangesSection
8
+ attribute :additions, ChangesSection
9
+ attribute :deletions, ChangesSection
10
+
11
+ xml do
12
+ element "mim.changes"
13
+ map_element "mim.modifications", to: :modifications
14
+ map_element "mim.additions", to: :additions
15
+ map_element "mim.deletions", to: :deletions
16
+ end
17
+
18
+ def mode
19
+ "mim"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Eengine
5
+ # Represents a modified EXPRESS object in an Eengine comparison report
6
+ class ModifiedObject < Lutaml::Model::Serializable
7
+ attribute :type, :string
8
+ attribute :name, :string
9
+ attribute :interfaced_items, :string
10
+ attribute :description, :string
11
+
12
+ xml do
13
+ element "modified.object"
14
+ map_attribute "type", to: :type
15
+ map_attribute "name", to: :name
16
+ map_attribute "interfaced.items", to: :interfaced_items
17
+ map_element "description", to: :description
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Expressir
2
+ module Eengine
3
+ autoload :ModifiedObject, "#{__dir__}/eengine/modified_object"
4
+ autoload :ChangesSection, "#{__dir__}/eengine/changes_section"
5
+ autoload :CompareReport, "#{__dir__}/eengine/compare_report"
6
+ autoload :ArmCompareReport, "#{__dir__}/eengine/arm_compare_report"
7
+ autoload :MimCompareReport, "#{__dir__}/eengine/mim_compare_report"
8
+ end
9
+ end