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,812 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Express
5
+ # Semantic checks modeled on eeng kernel/check.lisp + check-notes.lisp
6
+ # (expressir parity-ee stage 07). Each note preserves the eeng note id
7
+ # so oracle differentials stay cross-referenceable.
8
+ #
9
+ # Coverage is a deliberate subset of eeng's check-p11 catalog:
10
+ # check-schema-interface-redundant
11
+ # check-schema-iface-resource-duplicate
12
+ # check-subtype-ref
13
+ # check-subtype-cycle
14
+ # check-duplicate-declaration
15
+ # check-where-name-pattern (entity, type, rule)
16
+ # check-unique-name-pattern
17
+ # check-unresolved-ref (aggregate of eeng type/entity unparsed notes)
18
+ # check-select-extended-type
19
+ # check-enumeration-extended-type
20
+ # check-inverse-entity/-attrib-ref/-inverse/-derive (tranche 3)
21
+ # qualified-attrib-group-not-found/-not-ancestor,
22
+ # qualified-attrib-attr-not-found (tranche 3)
23
+ #
24
+ # Severity: :error stops a clean SHTOLO run; :warning is advisory.
25
+ class Checker
26
+ Note = Struct.new(:id, :severity, :schema, :message, :node, keyword_init: true)
27
+
28
+ WHERE_LABEL = /\AWR\d+\z/i
29
+ UNIQUE_LABEL = /\AUR\d+\z/i
30
+ DECL_COLLECTIONS = %i[types entities functions procedures constants rules
31
+ subtype_constraints].freeze
32
+
33
+ attr_reader :notes
34
+
35
+ def initialize(repository)
36
+ @repository = repository
37
+ @notes = []
38
+ # Case-folded index to LISTS of schemas: EXPRESS schema ids are
39
+ # case-insensitive, and two schemas differing only in case are
40
+ # distinct declarations that must not silently overwrite each other.
41
+ @by_name = {}
42
+ repository.schemas.compact.each do |schema|
43
+ next unless schema.id
44
+
45
+ (@by_name[schema.id.safe_downcase] ||= []) << schema
46
+ end
47
+ end
48
+
49
+ def check
50
+ @notes = []
51
+ @repository.schemas.compact.each { |schema| check_schema(schema) }
52
+ self
53
+ end
54
+
55
+ def errors
56
+ notes.select { |n| n.severity == :error }
57
+ end
58
+
59
+ def warnings
60
+ notes.select { |n| n.severity == :warning }
61
+ end
62
+
63
+ def valid?
64
+ errors.empty?
65
+ end
66
+
67
+ def report(io = $stdout)
68
+ notes.each do |n|
69
+ io.puts "[#{n.severity}] #{n.id}: #{n.message}"
70
+ end
71
+ io.puts "#{errors.size} error(s), #{warnings.size} warning(s)"
72
+ io
73
+ end
74
+
75
+ BUILTINS = %w[
76
+ integer real number string binary boolean logical generic
77
+ generic_entity aggregate array bag list set
78
+ true false unknown self const_e pi
79
+ abs acos asin atan cos exp format hibound hiindex length
80
+ log log2 log10 lobound loindex nvl odd rolesof sin sizeof
81
+ sqrt tan typeof usedin value value_in value_unique exists
82
+ blength insert remove
83
+ ].freeze
84
+
85
+ private
86
+
87
+ def note!(id, severity, schema, message, node = nil)
88
+ @notes << Note.new(id: id, severity: severity, schema: schema&.id,
89
+ message: message, node: node)
90
+ end
91
+
92
+ def check_schema(schema)
93
+ check_duplicates(schema)
94
+ check_interfaces(schema)
95
+ check_self_schema_reference(schema)
96
+ schema.entities.each { |e| check_entity(schema, e) }
97
+ schema.types.each do |t|
98
+ check_type(schema, t)
99
+ check_select_items(schema, t)
100
+ check_where_rules(schema, t, t.where_rules)
101
+ end
102
+ schema.rules.each { |r| check_where_rules(schema, r, r.where_rules) }
103
+ schema.entities.each do |entity|
104
+ check_aggregate_bounds(schema, entity)
105
+ check_attrib_name_fun(schema, entity)
106
+ end
107
+ check_string_references(schema)
108
+ check_unresolved_refs(schema)
109
+ end
110
+
111
+ # Self-qualified literals naming the CURRENT schema (#125): local
112
+ # items must not be prefixed, foreign items must name their true
113
+ # defining schema (ISO 10303-11 scope rules).
114
+ def check_self_schema_reference(schema)
115
+ SelfSchemaReference.matches(schema).each do |match|
116
+ case match.status
117
+ when :local
118
+ note!(:check_self_schema_reference, :warning, schema,
119
+ "self-qualified '#{schema.id}.#{match.item}' — " \
120
+ "'#{match.item}' is local; drop the prefix", match.literal)
121
+ when :foreign
122
+ note!(:check_self_schema_reference, :warning, schema,
123
+ "self-qualified '#{schema.id}.#{match.item}' — item is " \
124
+ "defined in '#{match.source_schema.id}'", match.literal)
125
+ end
126
+ end
127
+ end
128
+
129
+ # Two declarations in one schema sharing a (case-insensitive) name is
130
+ # invalid EXPRESS — the second can never be referenced.
131
+ def check_duplicates(schema)
132
+ seen = {}
133
+ DECL_COLLECTIONS.each do |coll|
134
+ Array(schema.public_send(coll)).each do |decl|
135
+ next unless decl.respond_to?(:id) && decl.id
136
+
137
+ key = decl.id.safe_downcase
138
+ if seen.key?(key)
139
+ note!(:check_duplicate_declaration, :error, schema,
140
+ "duplicate declaration '#{decl.id}' (also declared in " \
141
+ "#{seen[key]})", decl)
142
+ else
143
+ seen[key] = coll.to_s
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ # eeng: check-schema-interface-redundant +
150
+ # check-schema-iface-resource-duplicate.
151
+ # USE FROM and REFERENCE FROM are different clauses with different
152
+ # semantics; only interfaces of the SAME kind are compared.
153
+ def check_interfaces(schema)
154
+ ifaces = Array(schema.interfaces)
155
+ ifaces.each_with_index do |iface, i|
156
+ target = iface_schema_name(iface)
157
+ next unless target
158
+
159
+ later = ifaces[(i + 1)..] || []
160
+ later.each do |other|
161
+ next unless iface_schema_name(other)&.safe_downcase == target.safe_downcase
162
+ next unless other.kind == iface.kind
163
+
164
+ a = resource_names(iface)
165
+ b = resource_names(other)
166
+ if a.empty? || b.empty? || (b - a).empty?
167
+ note!(:check_schema_interface_redundant, :warning, schema,
168
+ "redundant #{iface.kind} interface to #{target}", other)
169
+ else
170
+ dup = a & b
171
+ next if dup.empty?
172
+
173
+ note!(:check_schema_iface_resource_duplicate, :warning, schema,
174
+ "duplicate resources #{dup.join(', ')} on #{iface.kind} " \
175
+ "interface to #{target}", other)
176
+ end
177
+ end
178
+
179
+ foreign = resolve_schema(target)
180
+ unless foreign
181
+ note!(:check_unresolved_ref, :error, schema,
182
+ "interface schema '#{target}' not found", iface)
183
+ next
184
+ end
185
+
186
+ resource_names(iface).each do |name|
187
+ next if foreign_decl?(foreign, name)
188
+
189
+ note!(:check_unresolved_ref, :error, schema,
190
+ "interface resource '#{name}' not in schema '#{target}'", iface)
191
+ end
192
+ end
193
+ end
194
+
195
+ def iface_schema_name(iface)
196
+ iface.schema.is_a?(String) ? iface.schema : iface.schema&.id
197
+ end
198
+
199
+ def resource_names(iface)
200
+ Array(iface.items).filter_map do |item|
201
+ ref = item.ref.is_a?(String) ? item.ref : item.ref&.id
202
+ ref || item.id
203
+ end.map(&:safe_downcase)
204
+ end
205
+
206
+ def foreign_decl?(schema, name)
207
+ key = name.safe_downcase
208
+ DECL_COLLECTIONS.any? do |coll|
209
+ Array(schema.public_send(coll)).any? { |d| d.id&.safe_downcase == key }
210
+ end
211
+ end
212
+
213
+ def check_entity(schema, entity)
214
+ Array(entity.subtype_of).each do |ref|
215
+ id = ref.is_a?(String) ? ref : ref.id
216
+ next if id.nil? || find_entity(schema, id)
217
+
218
+ note!(:check_subtype_ref, :error, schema,
219
+ "ENTITY #{entity.id}: subtype '#{id}' not found", entity)
220
+ next unless declared_kind(schema, id) == :type
221
+
222
+ # eeng check-subtypeof-invalid: the name exists but is not
223
+ # an entity, so it cannot be a supertype.
224
+ note!(:check_subtypeof_invalid, :error, schema,
225
+ "ENTITY #{entity.id}: subtype '#{id}' is a TYPE, " \
226
+ "not an entity", entity)
227
+ end
228
+ check_supertype_refs(schema, entity)
229
+ check_subtype_cycles(schema, entity)
230
+
231
+ check_where_rules(schema, entity, entity.where_rules)
232
+ check_inverse_attributes(schema, entity)
233
+ check_group_references(schema, entity)
234
+ Array(entity.unique_rules).each do |ur|
235
+ next if ur.id.nil? || ur.id.match?(UNIQUE_LABEL)
236
+
237
+ note!(:check_unique_name_pattern, :warning, schema,
238
+ "ENTITY #{entity.id}: UNIQUE label '#{ur.id}' is not UR<n>", ur)
239
+ end
240
+ end
241
+
242
+ # Cycle detection over SUBTYPE OF edges: per-path tracking with a
243
+ # proven-acyclic memo, so convergent (diamond) branches are not
244
+ # misread as cycles (#411).
245
+ def check_subtype_cycles(schema, entity)
246
+ walk_subtype_edges(schema, entity, entity,
247
+ [entity.id.safe_downcase], subtype_clean_memo(schema))
248
+ end
249
+
250
+ def subtype_clean_memo(schema)
251
+ (@subtype_clean ||= {})[schema] ||= {}
252
+ end
253
+
254
+ # Returns true when a cycle is reachable from +node+; +path_keys+ is
255
+ # the current DFS chain and +clean+ memoizes nodes proven acyclic.
256
+ def walk_subtype_edges(schema, start, node, path_keys, clean)
257
+ cyclic = false
258
+ Array(node.subtype_of).each do |ref|
259
+ id = ref.is_a?(String) ? ref : ref.id
260
+ next unless id
261
+
262
+ nxt = find_entity(schema, id)
263
+ next unless nxt
264
+
265
+ key = nxt.id.safe_downcase
266
+ if path_keys.include?(key)
267
+ note!(:check_subtype_cycle, :error, schema,
268
+ "ENTITY #{start.id}: subtype inheritance cycle through " \
269
+ "'#{nxt.id}'", start)
270
+ cyclic = true
271
+ elsif clean[key]
272
+ next
273
+ else
274
+ cyclic ||= walk_subtype_edges(
275
+ schema, start, nxt, path_keys + [key], clean
276
+ )
277
+ end
278
+ end
279
+ clean[node.id.safe_downcase] = true unless cyclic
280
+ cyclic
281
+ end
282
+
283
+ # eeng check-supertype-ref: every entity named in the SUPERTYPE
284
+ # expression must resolve to an entity of this schema.
285
+ def check_supertype_refs(schema, entity)
286
+ expr = entity.supertype_expression
287
+ return unless expr
288
+
289
+ each_reference(expr) do |ref|
290
+ id = ref.id
291
+ next if id.nil? || find_entity(schema, id)
292
+
293
+ note!(:check_supertype_ref, :error, schema,
294
+ "ENTITY #{entity.id}: supertype '#{id}' not found", entity)
295
+ end
296
+ end
297
+
298
+ # eeng check-inverse-* (ISO 10303-11 9.2.1.4): every INVERSE
299
+ # attribute must name a real entity and pair with a real
300
+ # attribute on it — one that is neither DERIVE nor itself an
301
+ # INVERSE — whose domain includes the owning entity (through
302
+ # subtype relations and nested aggregates). check-inverse-agg
303
+ # (only SET/BAG may aggregate an inverse) is structurally
304
+ # impossible here: the grammar rejects other aggregate kinds.
305
+ def check_inverse_attributes(schema, entity)
306
+ entity.attributes.to_a
307
+ .grep(Model::Declarations::InverseAttribute)
308
+ .each { |inv| check_inverse_attribute(schema, entity, inv) }
309
+ end
310
+
311
+ def check_inverse_attribute(schema, entity, inv)
312
+ target = inverse_target(schema, inv)
313
+ if target.nil?
314
+ note!(:check_inverse_entity, :error, schema,
315
+ "ENTITY #{entity.id}: INVERSE '#{inv.id}' target " \
316
+ "'#{inverse_target_id(inv)}' is not an entity", inv)
317
+ return
318
+ end
319
+
320
+ attr_id = inverse_paired_attr_id(inv)
321
+ if attr_id.nil?
322
+ note!(:check_inverse_attrib_ref, :error, schema,
323
+ "ENTITY #{entity.id}: INVERSE '#{inv.id}' names no " \
324
+ "attribute after FOR", inv)
325
+ return
326
+ end
327
+
328
+ owner = inverse_attr_owner(schema, inv, target)
329
+ paired = owner && attribute_on_closure(schema, owner, attr_id)
330
+ if paired.nil?
331
+ note!(:check_inverse_attrib_ref, :error, schema,
332
+ "ENTITY #{entity.id}: INVERSE '#{inv.id}': entity " \
333
+ "'#{owner&.id || target.id}' has no attribute '#{attr_id}'", inv)
334
+ return
335
+ end
336
+
337
+ if paired.is_a?(Model::Declarations::InverseAttribute)
338
+ note!(:check_inverse_inverse, :error, schema,
339
+ "ENTITY #{entity.id}: INVERSE '#{inv.id}' pairs with " \
340
+ "'#{owner.id}.#{attr_id}', which is itself an INVERSE", inv)
341
+ elsif paired.kind == Expressir::Model::Declarations::Attribute::DERIVED
342
+ note!(:check_inverse_derive, :error, schema,
343
+ "ENTITY #{entity.id}: INVERSE '#{inv.id}' pairs with " \
344
+ "'#{owner.id}.#{attr_id}', which is a DERIVE", inv)
345
+ end
346
+ end
347
+
348
+ def inverse_target(schema, inv)
349
+ type = inv.type
350
+ type = type.base_type if type.respond_to?(:base_type)
351
+ return nil unless type.respond_to?(:id)
352
+
353
+ find_entity(schema, ref_id(type))
354
+ end
355
+
356
+ def inverse_target_id(inv)
357
+ type = inv.type
358
+ type = type.base_type if type.respond_to?(:base_type)
359
+ ref_id(type)
360
+ end
361
+
362
+ # The entity the FOR-attribute lives on: the qualified
363
+ # `e.a FOR` form overrides the type's entity.
364
+ def inverse_attr_owner(schema, inv, target)
365
+ expr = inv.expression
366
+ if expr.is_a?(Model::References::AttributeReference) &&
367
+ expr.ref.respond_to?(:id)
368
+ find_entity(schema, ref_id(expr.ref)) || target
369
+ else
370
+ target
371
+ end
372
+ end
373
+
374
+ def inverse_paired_attr_id(inv)
375
+ expr = inv.expression
376
+ return ref_id(expr) if expr.respond_to?(:id)
377
+
378
+ expr.respond_to?(:attribute) ? ref_id(expr.attribute) : nil
379
+ end
380
+
381
+ # Attribute on +entity+ or any of its supertype ancestors.
382
+ def attribute_on_closure(schema, entity, attr_id)
383
+ key = attr_id.safe_downcase
384
+ seen = {}.compare_by_identity
385
+ queue = [entity]
386
+ until queue.empty?
387
+ current = queue.shift
388
+ next if seen[current]
389
+
390
+ seen[current] = true
391
+ hit = Array(current.attributes).find do |a|
392
+ a.id&.safe_downcase == key
393
+ end
394
+ return hit if hit
395
+
396
+ Array(current.subtype_of).each do |ref|
397
+ id = ref_id(ref)
398
+ queue << find_entity(schema, id) if id
399
+ end
400
+ end
401
+ nil
402
+ end
403
+
404
+ # eeng qualified-attrib-* (ISO 10303-11 12.7.4). The group must
405
+ # resolve to an entity and the qualified attribute must exist on
406
+ # it. The ANCESTRY requirement applies only to redeclared
407
+ # attributes (SELF\T.attr as an attribute declaration) — eeng
408
+ # checks exactly those; TYPEOF-guarded sibling views in
409
+ # expressions (geometry_schema's SELF\subface.parent_face) are
410
+ # standard corpus usage and are not flagged.
411
+ def check_group_references(schema, entity)
412
+ closure = supertype_closure_ids(schema, entity)
413
+ closure << entity.id if entity.id
414
+ redeclared = {}.compare_by_identity
415
+ entity.attributes.to_a.each do |attr|
416
+ sa = attr.supertype_attribute
417
+ next unless sa.is_a?(Model::References::AttributeReference) &&
418
+ sa.ref.is_a?(Model::References::GroupReference)
419
+
420
+ redeclared[sa.ref] = true
421
+ group = sa.ref
422
+ target = resolve_group(schema, entity, group)
423
+ next if target.nil?
424
+ next if closure.include?(ref_id(group.entity)&.safe_downcase)
425
+
426
+ note!(:qualified_attrib_group_not_ancestor, :error, schema,
427
+ "ENTITY #{entity.id}: SELF\\'#{ref_id(group.entity)}' " \
428
+ "is not a supertype of #{entity.id}", group)
429
+ end
430
+
431
+ each_node(entity) do |node|
432
+ next unless node.is_a?(Model::References::GroupReference)
433
+ next if redeclared[node]
434
+
435
+ target = resolve_group(schema, entity, node)
436
+ next if target.nil?
437
+
438
+ parent = node.parent
439
+ attr_id = parent.respond_to?(:attribute) ? ref_id(parent.attribute) : nil
440
+ next if attr_id.nil?
441
+ next if Array(target.attributes).any? do |a|
442
+ a.id&.safe_downcase == attr_id.safe_downcase
443
+ end
444
+
445
+ note!(:qualified_attrib_attr_not_found, :error, schema,
446
+ "ENTITY #{entity.id}: '#{ref_id(node.entity)}.#{attr_id}': " \
447
+ "'#{ref_id(node.entity)}' has no attribute '#{attr_id}'", node)
448
+ end
449
+ end
450
+
451
+ # Resolve a group qualifier's entity, noting
452
+ # qualified-attrib-group-not-found when it does not resolve.
453
+ def resolve_group(schema, entity, node)
454
+ group_id = ref_id(node.entity)
455
+ target = group_id && find_entity(schema, group_id)
456
+ return target if target
457
+
458
+ note!(:qualified_attrib_group_not_found, :error, schema,
459
+ "ENTITY #{entity.id}: group '#{group_id}' in a qualified " \
460
+ "attribute does not resolve to an entity", node)
461
+ nil
462
+ end
463
+
464
+ def supertype_closure_ids(schema, entity)
465
+ seen = {}.compare_by_identity
466
+ queue = [entity]
467
+ ids = []
468
+ until queue.empty?
469
+ current = queue.shift
470
+ next if seen[current]
471
+
472
+ seen[current] = true
473
+ Array(current.subtype_of).each do |ref|
474
+ id = ref_id(ref)
475
+ next if id.nil?
476
+
477
+ ids << id
478
+ parent = find_entity(schema, id)
479
+ queue << parent if parent
480
+ end
481
+ end
482
+ ids
483
+ end
484
+
485
+ def each_reference(node, &block)
486
+ case node
487
+ when Model::References::SimpleReference then yield node
488
+ when Model::ModelElement
489
+ node.class.attributes.each_key do |attr|
490
+ next if Model::ModelElement::SKIP_ATTRIBUTES.include?(attr) || attr == :parent
491
+
492
+ value = node.public_send(attr)
493
+ case value
494
+ when Array then value.each { |item| each_reference(item, &block) }
495
+ when Model::ModelElement then each_reference(value, &block)
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ # eeng check-select-named-type: SELECT items must name types.
502
+ def check_select_items(schema, type)
503
+ return unless type.underlying_type.is_a?(Model::DataTypes::Select)
504
+
505
+ Array(type.underlying_type.items).each do |item|
506
+ next if item.id.nil?
507
+ next if select_item_resolves?(schema, item.id)
508
+
509
+ note!(:check_select_named_type, :error, schema,
510
+ "In the SELECT TYPE #{type.id}, '#{item.id}' does not " \
511
+ "name an existing TYPE or ENTITY", type)
512
+ end
513
+ end
514
+
515
+ def find_type(schema, id)
516
+ key = id.safe_downcase
517
+ schema.types.find { |t| t.id.safe_downcase == key }
518
+ end
519
+
520
+ # ISO 10303-11 rule 258: select_list items are named_types —
521
+ # entity_ref | type_ref. The item is fine when it names either,
522
+ # in this schema or any repository schema (interface-visible).
523
+ def select_item_resolves?(schema, id)
524
+ return true if find_entity(schema, id)
525
+ return true if schema.types.to_a.any? { |t| t.id.safe_downcase == id.safe_downcase }
526
+
527
+ @by_name.each_value.any? do |group|
528
+ group.any? do |other|
529
+ other.types.to_a.any? { |t| t.id.safe_downcase == id.safe_downcase } ||
530
+ other.entities.to_a.any? { |e| e.id.safe_downcase == id.safe_downcase }
531
+ end
532
+ end
533
+ end
534
+
535
+ # eeng declared-kind probe: :type when a TYPE of that name is
536
+ # visible in the schema (used to separate not-found from
537
+ # not-an-entity in subtype diagnostics).
538
+ def declared_kind(schema, id)
539
+ :type if find_type(schema, id)
540
+ end
541
+
542
+ # eeng check-agg-type: a two-bound aggregate whose lower bound
543
+ # exceeds its upper bound is invalid.
544
+ def check_aggregate_bounds(schema, entity)
545
+ entity.attributes.to_a.each do |attr|
546
+ type = attr.type
547
+ next unless type.respond_to?(:bound1) && type.respond_to?(:bound2)
548
+
549
+ lower = integer_bound(type.bound1)
550
+ upper = integer_bound(type.bound2)
551
+ next if lower.nil? || upper.nil?
552
+ next if lower <= upper
553
+
554
+ note!(:check_agg_type, :error, schema,
555
+ "ENTITY #{entity.id}: aggregate '#{attr.id}' bounds " \
556
+ "[#{lower}:#{upper}] are inverted", attr)
557
+ end
558
+ end
559
+
560
+ def integer_bound(bound)
561
+ return nil unless bound.is_a?(Model::Literals::Integer)
562
+
563
+ bound.value.to_i
564
+ rescue StandardError
565
+ nil
566
+ end
567
+
568
+ # eeng check-attrib-name-fun: an attribute sharing its name with
569
+ # a function of the same schema can never be referenced cleanly.
570
+ def check_attrib_name_fun(schema, entity)
571
+ funs = schema.functions.to_a.map { |f| f.id.safe_downcase }
572
+ entity.attributes.to_a.each do |attr|
573
+ next unless attr.id && funs.include?(attr.id.safe_downcase)
574
+
575
+ note!(:check_attrib_name_fun, :warning, schema,
576
+ "ENTITY #{entity.id}: attribute '#{attr.id}' shadows a " \
577
+ "function of the same name", attr)
578
+ end
579
+ end
580
+
581
+ # eeng check-string-no-entity: string literals of the shape
582
+ # 'SCHEMA.ITEM' whose SCHEMA is known in the repository but whose
583
+ # ITEM is not declared in it (the TYPEOF idiom).
584
+ def check_string_references(schema)
585
+ known = @by_name
586
+ each_string(schema) do |literal, value|
587
+ match = /\A(\w+)\.(\w+)\z/.match(value)
588
+ next unless match
589
+
590
+ schema_id = match[1].safe_downcase
591
+ item_id = match[2].safe_downcase
592
+ target = known[schema_id]&.find { |s| s.id.safe_downcase == schema_id }
593
+ next unless target
594
+ next if Expressir::Express::SelfSchemaReference.declared?(target, item_id)
595
+
596
+ note!(:check_string_no_entity, :warning, schema,
597
+ "string '#{value}' references '#{item_id}' which is not " \
598
+ "declared in schema '#{target.id}'", literal)
599
+ end
600
+ end
601
+
602
+ def each_string(node, &block)
603
+ case node
604
+ when Model::Literals::String then yield node, node.value
605
+ when Model::ModelElement
606
+ node.class.attributes.each_key do |attr|
607
+ next if Model::ModelElement::SKIP_ATTRIBUTES.include?(attr) || attr == :parent
608
+
609
+ value = node.public_send(attr)
610
+ case value
611
+ when Array then value.each { |item| each_string(item, &block) }
612
+ when Model::ModelElement then each_string(value, &block)
613
+ end
614
+ end
615
+ end
616
+ end
617
+
618
+ def check_where_rules(schema, owner, rules)
619
+ Array(rules).each do |wr|
620
+ next if wr.id.nil? || wr.id.match?(WHERE_LABEL)
621
+
622
+ note!(:check_where_name_pattern, :warning, schema,
623
+ "#{owner.class.name.split('::').last} #{owner.id}: " \
624
+ "WHERE label '#{wr.id}' is not WR<n>", wr)
625
+ end
626
+ end
627
+
628
+ def check_type(schema, type)
629
+ ut = type.underlying_type
630
+ case ut
631
+ when Model::DataTypes::Select
632
+ if ut.based_on && !resolve_type(schema, ut.based_on)
633
+ note!(:check_select_extended_type, :error, schema,
634
+ "TYPE #{type.id}: BASED_ON '#{ref_id(ut.based_on)}' not found", type)
635
+ end
636
+ when Model::DataTypes::Enumeration
637
+ if ut.based_on && !resolve_type(schema, ut.based_on)
638
+ note!(:check_enumeration_extended_type, :error, schema,
639
+ "TYPE #{type.id}: BASED_ON '#{ref_id(ut.based_on)}' not found", type)
640
+ end
641
+ end
642
+ end
643
+
644
+ # Walk SimpleReferences whose base_path was never filled in by the
645
+ # resolver — the expressir equivalent of eeng's unparsed-type notes.
646
+ # Locally bound identifiers are not unresolved: QUERY variables,
647
+ # REPEAT control variables, attribute names visible in the schema,
648
+ # and the `?` of aggregate bounds (#396).
649
+ def check_unresolved_refs(schema)
650
+ aliases = alias_map(schema)
651
+ bound = bound_identifiers(schema)
652
+ each_node(schema) do |node|
653
+ next unless node.is_a?(Model::References::SimpleReference)
654
+ next if node.base_path
655
+ next if node.parent.is_a?(Model::Declarations::InterfaceItem)
656
+ next if node.parent.is_a?(Model::References::AttributeReference)
657
+ next if node.id.nil? || builtin?(node.id)
658
+ next if aliases.key?(node.id.safe_downcase)
659
+ next if bound.include?(node.id.safe_downcase)
660
+
661
+ # Skip self-ids of declarations (entity/type names as the decl itself)
662
+ next if declaration_id?(schema, node)
663
+
664
+ note!(:check_unresolved_ref, :error, schema,
665
+ "unresolved reference '#{node.id}'", node)
666
+ end
667
+ end
668
+
669
+ # Names the schema's own constructs bind: QUERY variables, REPEAT
670
+ # control variables, attribute names of every entity (including the
671
+ # inherited ones visible through SUBTYPE OF — an over-approximation on
672
+ # purpose, so the error-severity walk never flags a legal identifier).
673
+ def bound_identifiers(schema)
674
+ @bound_identifiers ||= {}.compare_by_identity
675
+ @bound_identifiers[schema] ||= begin
676
+ bound = Set.new(["?"])
677
+ each_node(schema) do |node|
678
+ case node
679
+ when Model::Expressions::QueryExpression, Model::Statements::Repeat
680
+ bound << node.id.safe_downcase if node.respond_to?(:id) && node.id
681
+ end
682
+ end
683
+ schema.entities.each { |e| collect_attribute_names(e, bound) }
684
+ visible_entities(schema).each { |e| collect_attribute_names(e, bound) }
685
+ bound
686
+ end
687
+ end
688
+
689
+ def collect_attribute_names(entity, bound)
690
+ Array(entity.attributes).each do |attr|
691
+ bound << attr.id.safe_downcase if attr.respond_to?(:id) && attr.id
692
+ end
693
+ end
694
+
695
+ # `USE FROM s (orig AS alias)` makes `alias` the locally visible name;
696
+ # references written with the alias are resolved, not unresolved.
697
+ # Maps alias downcase => [foreign schema, original downcase].
698
+ def alias_map(schema)
699
+ @alias_maps ||= {}.compare_by_identity
700
+ @alias_maps[schema] ||= begin
701
+ map = {}
702
+ Array(schema.interfaces).each do |iface|
703
+ foreign = resolve_schema(iface_schema_name(iface))
704
+ next unless foreign
705
+
706
+ Array(iface.items).each do |item|
707
+ original = item.ref.is_a?(String) ? item.ref : item.ref&.id
708
+ next unless original && item.id
709
+
710
+ map[item.id.safe_downcase] = [foreign, original.safe_downcase]
711
+ end
712
+ end
713
+ map
714
+ end
715
+ end
716
+
717
+ def declaration_id?(_schema, node)
718
+ parent = node.parent
719
+ parent.respond_to?(:id) && parent.id&.safe_downcase == node.id.safe_downcase
720
+ end
721
+
722
+ def builtin?(id)
723
+ BUILTINS.include?(id.safe_downcase)
724
+ end
725
+
726
+ # Resolve a schema id case-insensitively, preferring the exact-case
727
+ # match when several folded ids collide (they are distinct schemas).
728
+ def resolve_schema(name)
729
+ return nil unless name
730
+
731
+ group = @by_name[name.safe_downcase]
732
+ return nil unless group
733
+
734
+ group.find { |s| s.id == name } || group.first
735
+ end
736
+
737
+ def find_entity(schema, id)
738
+ key = id.safe_downcase
739
+ own = schema.entities.find { |e| e.id.safe_downcase == key }
740
+ return own if own
741
+
742
+ # interface AS-renames: the alias names a foreign declaration.
743
+ if (aliased = alias_map(schema)[key])
744
+ foreign, original = aliased
745
+ return foreign.entities.find { |e| e.id.safe_downcase == original }
746
+ end
747
+
748
+ visible_entities(schema).find { |e| e.id.safe_downcase == key }
749
+ end
750
+
751
+ def resolve_type(schema, ref)
752
+ id = ref_id(ref)
753
+ return false unless id
754
+
755
+ key = id.safe_downcase
756
+ own = schema.types.find { |t| t.id.safe_downcase == key }
757
+ return true if own
758
+
759
+ if (aliased = alias_map(schema)[key])
760
+ foreign, original = aliased
761
+ return foreign.types.any? { |t| t.id.safe_downcase == original }
762
+ end
763
+
764
+ visible_types(schema).any? { |t| t.id.safe_downcase == key }
765
+ end
766
+
767
+ def ref_id(ref)
768
+ ref.is_a?(String) ? ref : ref&.id
769
+ end
770
+
771
+ # Foreign declarations visible through +schema+'s interfaces. With an
772
+ # item list, the LISTED names are AS-imported: match the originals.
773
+ def visible_from(schema, collection)
774
+ Array(schema.interfaces).flat_map do |iface|
775
+ foreign = resolve_schema(iface_schema_name(iface))
776
+ next [] unless foreign
777
+
778
+ names = resource_names(iface)
779
+ decls = Array(foreign.public_send(collection))
780
+ names.empty? ? decls : decls.select { |d| names.include?(d.id.safe_downcase) }
781
+ end
782
+ end
783
+
784
+ def visible_entities(schema)
785
+ visible_from(schema, :entities)
786
+ end
787
+
788
+ def visible_types(schema)
789
+ visible_from(schema, :types)
790
+ end
791
+
792
+ def each_node(root)
793
+ stack = [root]
794
+ until stack.empty?
795
+ node = stack.pop
796
+ yield node
797
+ next unless node.is_a?(Model::ModelElement)
798
+
799
+ node.class.attributes.each_key do |attr|
800
+ next if Model::ModelElement::SKIP_ATTRIBUTES.include?(attr)
801
+
802
+ value = node.public_send(attr)
803
+ case value
804
+ when Array then value.each { |item| stack << item if item.is_a?(Model::ModelElement) }
805
+ when Model::ModelElement then stack << value
806
+ end
807
+ end
808
+ end
809
+ end
810
+ end
811
+ end
812
+ end