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.
- checksums.yaml +4 -4
- data/.cargo/config.toml +3 -0
- data/.github/scripts/test_installed_gem.rb +34 -0
- data/.github/workflows/codeql.yml +35 -0
- data/.github/workflows/docs.yml +99 -0
- data/.github/workflows/links.yml +100 -0
- data/.github/workflows/native-gems.yml +201 -0
- data/.github/workflows/rake.yml +10 -296
- data/.github/workflows/release.yml +26 -114
- data/.github/workflows/rust-ext.yml +41 -0
- data/.github/workflows/stress.yml +36 -0
- data/.github/workflows/validate_schemas.yml +48 -0
- data/.github/workflows/verify_remarks.yml +33 -0
- data/.gitignore +33 -1
- data/.rubocop.yml +11 -11
- data/.rubocop_todo.yml +297 -0
- data/CHANGELOG.md +159 -0
- data/Gemfile +15 -1
- data/README.adoc +2675 -47
- data/Rakefile +34 -10
- data/TODO.bugs/01-stale-transformer-autoload.md +39 -0
- data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
- data/TODO.bugs/03-builder-mutable-state.md +43 -0
- data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
- data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
- data/TODO.bugs/06-collection-registry-single-source.md +53 -0
- data/TODO.bugs/07-require-relative-cleanup.md +42 -0
- data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
- data/TODO.bugs/09-parser-split.md +53 -0
- data/TODO.bugs/10-to-s-override.md +42 -0
- data/TODO.bugs/11-parser-class-variables.md +39 -0
- data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
- data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
- data/TODO.bugs/14-model-formatting-leak.md +30 -0
- data/TODO.bugs/15-expression-children-macro.md +27 -0
- data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
- data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
- data/TODO.bugs/18-const-get-private-constants.md +30 -0
- data/TODO.bugs/19-format-methods-public.md +22 -0
- data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
- data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
- data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
- data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
- data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
- data/TODO.bugs/25-debug-puts-in-production.md +21 -0
- data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
- data/TODO.bugs/27-package-build-god-method.md +19 -0
- data/TODO.bugs/28-package-god-class.md +30 -0
- data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
- data/TODO.bugs/30-unicode-map-extraction.md +19 -0
- data/TODO.bugs/README.md +43 -0
- data/TODO.max-perf/01-restore-ci-green.md +36 -0
- data/TODO.max-perf/02-streaming-parse-path.md +31 -0
- data/TODO.max-perf/03-cli-parallel-opt-in.md +27 -0
- data/TODO.max-perf/04-benchmark-harness.md +28 -0
- data/TODO.max-perf/05-parallel-fidelity-specs.md +22 -0
- data/TODO.max-perf/06-builder-cpu-audit.md +41 -0
- data/TODO.max-perf/07-upstream-parsanol-roadmap.md +27 -0
- data/TODO.max-perf/08-builder-build-perf.md +45 -0
- data/TODO.max-perf/09-grammar-cold-start.md +25 -0
- data/TODO.max-perf/10-parser-facade-hygiene.md +23 -0
- data/TODO.max-perf/11-ci-green-closeout.md +25 -0
- data/TODO.max-perf/12-require-boot-profile.md +25 -0
- data/TODO.max-perf/13-key-conversion-specs.md +26 -0
- data/TODO.max-perf/14-builder-call-handler-audit.md +28 -0
- data/TODO.parity-ee/00-overview.md +76 -0
- data/TODO.parity-ee/01-annex-g-rule-extraction.md +41 -0
- data/TODO.parity-ee/02-eeng-algorithm-inventory.md +8 -0
- data/TODO.parity-ee/03-eeng-oracle-harness.md +33 -0
- data/TODO.parity-ee/04-shtolo-concatenate.md +44 -0
- data/TODO.parity-ee/05-shtolo-longform-flatten.md +53 -0
- data/TODO.parity-ee/06-interface-scheduling-parity.md +47 -0
- data/TODO.parity-ee/07-semantic-checks-port.md +35 -0
- data/TODO.parity-ee/08-pretty-roundtrip-gate.md +27 -0
- data/TODO.parity-ee/09-smrl-index-and-listing.md +22 -0
- data/TODO.parity-ee/10-interface-dot-graph.md +21 -0
- data/TODO.parity-ee/11-import-eeng-tests.md +79 -0
- data/TODO.parity-ee/12-full-check-catalog.md +67 -0
- data/TODO.parity-ee/13-population-p21.md +54 -0
- data/TODO.parity-ee/14-shtolo-completion.md +44 -0
- data/TODO.parity-ee/15-part28-xml.md +51 -0
- data/TODO.parity-ee/16-compare-patch.md +48 -0
- data/TODO.parity-ee/17-official-tests.md +42 -0
- data/TODO.parity-ee/18-mim-mapping.md +47 -0
- data/TODO.parity-ee/19-architecture.md +97 -0
- data/TODO.parity-ee/parity-matrix.md +108 -0
- data/TODO.refpath-v2/00-overview.md +64 -0
- data/TODO.suma-improvements/00-overview.md +23 -0
- data/TODO.suma-improvements/01-prebuilt-platform-gems.md +33 -0
- data/TODO.suma-improvements/02-suma-e2e.md +39 -0
- data/TODO.suma-improvements/03-item-graph.md +22 -0
- data/TODO.suma-improvements/04-compiled-set-default.md +16 -0
- data/TODO.suma-improvements/05-validation-in-docs.md +17 -0
- data/TODO.suma-improvements/06-memory-remeasure.md +36 -0
- data/TODO.suma-improvements/07-part28-xml.md +15 -0
- data/TODO.suma-improvements/08-windows-gnu-toolchain.md +15 -0
- data/TODO.suma-improvements/09-checker-tranche3.md +13 -0
- data/TODO.suma-improvements/10-expressdoc.md +8 -0
- data/benchmark/srl_benchmark.rb +458 -0
- data/benchmark/srl_native_benchmark.rb +146 -0
- data/benchmark/srl_ruby_benchmark.rb +132 -0
- data/bin/rspec +2 -1
- data/docs/Gemfile +12 -0
- data/docs/_config.yml +141 -0
- data/docs/_guides/changes/changes-format.adoc +778 -0
- data/docs/_guides/changes/importing-eengine.adoc +898 -0
- data/docs/_guides/changes/index.adoc +396 -0
- data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
- data/docs/_guides/changes/validating-changes.adoc +681 -0
- data/docs/_guides/cli/benchmark-performance.adoc +834 -0
- data/docs/_guides/cli/coverage-analysis.adoc +921 -0
- data/docs/_guides/cli/format-schemas.adoc +547 -0
- data/docs/_guides/cli/index.adoc +8 -0
- data/docs/_guides/cli/managing-changes.adoc +927 -0
- data/docs/_guides/cli/validate-ascii.adoc +645 -0
- data/docs/_guides/cli/validate-schemas.adoc +534 -0
- data/docs/_guides/formatter/formatter-architecture.adoc +401 -0
- data/docs/_guides/index.adoc +165 -0
- data/docs/_guides/ler/creating-packages.adoc +664 -0
- data/docs/_guides/ler/index.adoc +305 -0
- data/docs/_guides/ler/loading-packages.adoc +707 -0
- data/docs/_guides/ler/package-formats.adoc +748 -0
- data/docs/_guides/ler/querying-packages.adoc +826 -0
- data/docs/_guides/ler/step-packages.adoc +385 -0
- data/docs/_guides/ler/validating-packages.adoc +750 -0
- data/docs/_guides/liquid/basic-templates.adoc +813 -0
- data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
- data/docs/_guides/liquid/drops-reference.adoc +829 -0
- data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
- data/docs/_guides/liquid/index.adoc +468 -0
- data/docs/_guides/manifests/creating-manifests.adoc +483 -0
- data/docs/_guides/manifests/index.adoc +307 -0
- data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
- data/docs/_guides/manifests/validating-manifests.adoc +713 -0
- data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
- data/docs/_guides/ruby-api/index.adoc +257 -0
- data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
- data/docs/_guides/ruby-api/search-engine.adoc +609 -0
- data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
- data/docs/_pages/data-model.adoc +665 -0
- data/docs/_pages/express-language.adoc +506 -0
- data/docs/_pages/getting-started.adoc +414 -0
- data/docs/_pages/index.adoc +116 -0
- data/docs/_pages/introduction.adoc +256 -0
- data/docs/_pages/ler-packages.adoc +837 -0
- data/docs/_pages/parsers.adoc +709 -0
- data/docs/_pages/schema-manifests.adoc +431 -0
- data/docs/_references/index.adoc +228 -0
- data/docs/_tutorials/creating-ler-package.adoc +735 -0
- data/docs/_tutorials/documentation-coverage.adoc +795 -0
- data/docs/_tutorials/formatting-schemas.adoc +89 -0
- data/docs/_tutorials/index.adoc +231 -0
- data/docs/_tutorials/liquid-templates.adoc +806 -0
- data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
- data/docs/_tutorials/querying-schemas.adoc +751 -0
- data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
- data/docs/index.adoc +242 -0
- data/docs/lychee.toml +90 -0
- data/examples/demo_ler_usage.sh +86 -0
- data/examples/ler/README.md +111 -0
- data/examples/ler/simple_example.ler +0 -0
- data/examples/ler/simple_schema.exp +33 -0
- data/examples/ler_build.rb +75 -0
- data/examples/ler_cli.rb +79 -0
- data/examples/ler_demo_complete.rb +276 -0
- data/examples/ler_query.rb +91 -0
- data/examples/ler_query_examples.rb +305 -0
- data/examples/ler_stats.rb +81 -0
- data/examples/phase3_demo.rb +159 -0
- data/examples/query_demo_simple.rb +131 -0
- data/exe/expressir +6 -17
- data/exe/expressir-format +14 -0
- data/exe/{format-test → expressir-format-test} +8 -7
- data/expressir.gemspec +50 -37
- data/lib/expressir/3.2/expressir_core.so +0 -0
- data/lib/expressir/3.3/expressir_core.so +0 -0
- data/lib/expressir/3.4/expressir_core.so +0 -0
- data/lib/expressir/4.0/expressir_core.so +0 -0
- data/lib/expressir/benchmark.rb +310 -0
- data/lib/expressir/changes/item_change.rb +20 -0
- data/lib/expressir/changes/mapping_change.rb +16 -0
- data/lib/expressir/changes/schema_change.rb +85 -0
- data/lib/expressir/changes/version_change.rb +26 -0
- data/lib/expressir/changes.rb +9 -0
- data/lib/expressir/cli.rb +138 -11
- data/lib/expressir/commands/base.rb +25 -0
- data/lib/expressir/commands/benchmark.rb +59 -0
- data/lib/expressir/commands/benchmark_cache.rb +80 -0
- data/lib/expressir/commands/changes.rb +34 -0
- data/lib/expressir/commands/changes_import_eengine.rb +146 -0
- data/lib/expressir/commands/changes_validate.rb +63 -0
- data/lib/expressir/commands/check.rb +56 -0
- data/lib/expressir/commands/clean.rb +20 -0
- data/lib/expressir/commands/coverage.rb +458 -0
- data/lib/expressir/commands/expand.rb +26 -0
- data/lib/expressir/commands/file_violations.rb +70 -0
- data/lib/expressir/commands/fix.rb +33 -0
- data/lib/expressir/commands/flatten.rb +29 -0
- data/lib/expressir/commands/format.rb +43 -0
- data/lib/expressir/commands/manifest.rb +420 -0
- data/lib/expressir/commands/mapping_validate.rb +68 -0
- data/lib/expressir/commands/non_ascii_character.rb +49 -0
- data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
- data/lib/expressir/commands/package.rb +1222 -0
- data/lib/expressir/commands/parity_inputs.rb +160 -0
- data/lib/expressir/commands/validate.rb +106 -0
- data/lib/expressir/commands/validate_ascii.rb +94 -0
- data/lib/expressir/commands/validate_load.rb +91 -0
- data/lib/expressir/commands/version.rb +9 -0
- data/lib/expressir/commands/xsd.rb +23 -0
- data/lib/expressir/commands.rb +30 -0
- data/lib/expressir/config.rb +40 -3
- data/lib/expressir/coverage.rb +510 -0
- data/lib/expressir/eengine/arm_compare_report.rb +23 -0
- data/lib/expressir/eengine/changes_section.rb +16 -0
- data/lib/expressir/eengine/compare_report.rb +69 -0
- data/lib/expressir/eengine/mim_compare_report.rb +23 -0
- data/lib/expressir/eengine/modified_object.rb +21 -0
- data/lib/expressir/eengine.rb +9 -0
- data/lib/expressir/errors.rb +113 -0
- data/lib/expressir/express/adoc_hyperlink_formatter.rb +30 -0
- data/lib/expressir/express/adoc_source_formatter.rb +12 -0
- data/lib/expressir/express/ast_key_converter.rb +114 -0
- data/lib/expressir/express/builder.rb +277 -0
- data/lib/expressir/express/builder_context.rb +22 -0
- data/lib/expressir/express/builder_registry.rb +397 -0
- data/lib/expressir/express/builders/attribute_decl_builder.rb +32 -0
- data/lib/expressir/express/builders/built_in_builder.rb +75 -0
- data/lib/expressir/express/builders/constant_builder.rb +108 -0
- data/lib/expressir/express/builders/declaration_builder.rb +20 -0
- data/lib/expressir/express/builders/derive_clause_builder.rb +14 -0
- data/lib/expressir/express/builders/derived_attr_builder.rb +28 -0
- data/lib/expressir/express/builders/domain_rule_builder.rb +21 -0
- data/lib/expressir/express/builders/entity_decl_builder.rb +116 -0
- data/lib/expressir/express/builders/explicit_attr_builder.rb +49 -0
- data/lib/expressir/express/builders/expression_builder.rb +416 -0
- data/lib/expressir/express/builders/function_decl_builder.rb +86 -0
- data/lib/expressir/express/builders/helpers.rb +148 -0
- data/lib/expressir/express/builders/interface_builder.rb +151 -0
- data/lib/expressir/express/builders/inverse_attr_builder.rb +43 -0
- data/lib/expressir/express/builders/inverse_attr_type_builder.rb +30 -0
- data/lib/expressir/express/builders/inverse_clause_builder.rb +14 -0
- data/lib/expressir/express/builders/literal_builder.rb +93 -0
- data/lib/expressir/express/builders/procedure_decl_builder.rb +82 -0
- data/lib/expressir/express/builders/qualifier_builder.rb +102 -0
- data/lib/expressir/express/builders/reference_builder.rb +18 -0
- data/lib/expressir/express/builders/rule_decl_builder.rb +97 -0
- data/lib/expressir/express/builders/schema_body_decl_builder.rb +18 -0
- data/lib/expressir/express/builders/schema_decl_builder.rb +56 -0
- data/lib/expressir/express/builders/schema_version_builder.rb +34 -0
- data/lib/expressir/express/builders/simple_id_builder.rb +17 -0
- data/lib/expressir/express/builders/statement_builder.rb +243 -0
- data/lib/expressir/express/builders/subtype_constraint_builder.rb +166 -0
- data/lib/expressir/express/builders/syntax_builder.rb +30 -0
- data/lib/expressir/express/builders/type_builder.rb +238 -0
- data/lib/expressir/express/builders/type_decl_builder.rb +26 -0
- data/lib/expressir/express/builders/unique_clause_builder.rb +20 -0
- data/lib/expressir/express/builders/unique_rule_builder.rb +41 -0
- data/lib/expressir/express/builders/where_clause_builder.rb +20 -0
- data/lib/expressir/express/builders.rb +55 -0
- data/lib/expressir/express/cache.rb +43 -18
- data/lib/expressir/express/checker.rb +812 -0
- data/lib/expressir/express/concatenator.rb +79 -0
- data/lib/expressir/express/core.rb +95 -0
- data/lib/expressir/express/error.rb +124 -0
- data/lib/expressir/express/formatter.rb +89 -1548
- data/lib/expressir/express/formatters/data_types_formatter.rb +362 -0
- data/lib/expressir/express/formatters/declarations_formatter.rb +753 -0
- data/lib/expressir/express/formatters/expressions_formatter.rb +180 -0
- data/lib/expressir/express/formatters/literals_formatter.rb +55 -0
- data/lib/expressir/express/formatters/references_formatter.rb +53 -0
- data/lib/expressir/express/formatters/remark_formatter.rb +270 -0
- data/lib/expressir/express/formatters/remark_item_formatter.rb +28 -0
- data/lib/expressir/express/formatters/statements_formatter.rb +272 -0
- data/lib/expressir/express/formatters/supertype_expressions_formatter.rb +54 -0
- data/lib/expressir/express/formatters.rb +22 -0
- data/lib/expressir/express/grammar/parser.rb +712 -0
- data/lib/expressir/express/grammar.rb +11 -0
- data/lib/expressir/express/hyperlink_formatter.rb +4 -5
- data/lib/expressir/express/interface_dot.rb +105 -0
- data/lib/expressir/express/lazy_repository.rb +211 -0
- data/lib/expressir/express/line_map.rb +48 -0
- data/lib/expressir/express/listing.rb +174 -0
- data/lib/expressir/express/model_traversal.rb +42 -0
- data/lib/expressir/express/model_visitor.rb +11 -10
- data/lib/expressir/express/node_position_index.rb +342 -0
- data/lib/expressir/express/parallel_files.rb +229 -0
- data/lib/expressir/express/parser.rb +501 -62
- data/lib/expressir/express/pretty_formatter.rb +586 -0
- data/lib/expressir/express/pretty_gate.rb +61 -0
- data/lib/expressir/express/refs_overlay.rb +47 -0
- data/lib/expressir/express/remark_attacher.rb +1098 -0
- data/lib/expressir/express/remark_overlay.rb +86 -0
- data/lib/expressir/express/remark_scanner.rb +245 -0
- data/lib/expressir/express/resolve_references_model_visitor.rb +7 -9
- data/lib/expressir/express/schema_block_scanner.rb +137 -0
- data/lib/expressir/express/schema_head_formatter.rb +4 -5
- data/lib/expressir/express/schema_plain_source_formatter.rb +11 -0
- data/lib/expressir/express/schema_source_formatter.rb +15 -0
- data/lib/expressir/express/scope_resolver.rb +223 -0
- data/lib/expressir/express/self_schema_reference.rb +126 -0
- data/lib/expressir/express/shtolo.rb +679 -0
- data/lib/expressir/express/smrl_xml.rb +125 -0
- data/lib/expressir/express/source_formatter.rb +15 -0
- data/lib/expressir/express/streaming_builder.rb +435 -0
- data/lib/expressir/express/xsd.rb +259 -0
- data/lib/expressir/express.rb +53 -0
- data/lib/expressir/liquid.rb +1 -0
- data/lib/expressir/manifest/resolver.rb +210 -0
- data/lib/expressir/manifest/validator.rb +192 -0
- data/lib/expressir/manifest.rb +6 -0
- data/lib/expressir/mapping/refpath.rb +857 -0
- data/lib/expressir/mapping.rb +253 -0
- data/lib/expressir/model/cache.rb +10 -9
- data/lib/expressir/model/concerns.rb +42 -0
- data/lib/expressir/model/data_types/aggregate.rb +8 -13
- data/lib/expressir/model/data_types/array.rb +15 -21
- data/lib/expressir/model/data_types/bag.rb +11 -15
- data/lib/expressir/model/data_types/binary.rb +9 -12
- data/lib/expressir/model/data_types/boolean.rb +7 -2
- data/lib/expressir/model/data_types/enumeration.rb +11 -15
- data/lib/expressir/model/data_types/enumeration_item.rb +5 -7
- data/lib/expressir/model/data_types/generic.rb +6 -8
- data/lib/expressir/model/data_types/generic_entity.rb +11 -13
- data/lib/expressir/model/data_types/integer.rb +7 -2
- data/lib/expressir/model/data_types/list.rb +13 -18
- data/lib/expressir/model/data_types/logical.rb +7 -2
- data/lib/expressir/model/data_types/number.rb +7 -2
- data/lib/expressir/model/data_types/real.rb +7 -9
- data/lib/expressir/model/data_types/select.rb +13 -18
- data/lib/expressir/model/data_types/set.rb +11 -15
- data/lib/expressir/model/data_types/string.rb +9 -12
- data/lib/expressir/model/data_types.rb +25 -0
- data/lib/expressir/model/declarations/attribute.rb +19 -28
- data/lib/expressir/model/declarations/constant.rb +10 -16
- data/lib/expressir/model/declarations/derived_attribute.rb +25 -0
- data/lib/expressir/model/declarations/entity.rb +39 -30
- data/lib/expressir/model/declarations/function.rb +32 -40
- data/lib/expressir/model/declarations/informal_proposition_rule.rb +25 -0
- data/lib/expressir/model/declarations/interface.rb +25 -16
- data/lib/expressir/model/declarations/interface_item.rb +9 -11
- data/lib/expressir/model/declarations/interfaced_item.rb +15 -19
- data/lib/expressir/model/declarations/inverse_attribute.rb +25 -0
- data/lib/expressir/model/declarations/parameter.rb +10 -16
- data/lib/expressir/model/declarations/procedure.rb +30 -37
- data/lib/expressir/model/declarations/remark_item.rb +11 -11
- data/lib/expressir/model/declarations/rule.rb +38 -43
- data/lib/expressir/model/declarations/schema.rb +162 -64
- data/lib/expressir/model/declarations/schema_version.rb +8 -11
- data/lib/expressir/model/declarations/schema_version_item.rb +8 -11
- data/lib/expressir/model/declarations/subtype_constraint.rb +14 -22
- data/lib/expressir/model/declarations/type.rb +18 -18
- data/lib/expressir/model/declarations/unique_rule.rb +8 -13
- data/lib/expressir/model/declarations/variable.rb +10 -16
- data/lib/expressir/model/declarations/where_rule.rb +8 -13
- data/lib/expressir/model/declarations.rb +31 -0
- data/lib/expressir/model/dependency_resolver.rb +268 -0
- data/lib/expressir/model/exp_file.rb +42 -0
- data/lib/expressir/model/expressions/aggregate_initializer.rb +8 -9
- data/lib/expressir/model/expressions/aggregate_initializer_item.rb +10 -12
- data/lib/expressir/model/expressions/binary_expression.rb +42 -36
- data/lib/expressir/model/expressions/entity_constructor.rb +10 -12
- data/lib/expressir/model/expressions/function_call.rb +10 -12
- data/lib/expressir/model/expressions/interval.rb +18 -23
- data/lib/expressir/model/expressions/query_expression.rb +11 -15
- data/lib/expressir/model/expressions/unary_expression.rb +13 -15
- data/lib/expressir/model/expressions.rb +18 -0
- data/lib/expressir/model/identifier.rb +18 -26
- data/lib/expressir/model/indexes/entity_index.rb +103 -0
- data/lib/expressir/model/indexes/item_graph.rb +185 -0
- data/lib/expressir/model/indexes/reference_index.rb +148 -0
- data/lib/expressir/model/indexes/type_index.rb +149 -0
- data/lib/expressir/model/indexes.rb +12 -0
- data/lib/expressir/model/interface_validator.rb +384 -0
- data/lib/expressir/model/literals/binary.rb +7 -9
- data/lib/expressir/model/literals/integer.rb +7 -9
- data/lib/expressir/model/literals/logical.rb +10 -12
- data/lib/expressir/model/literals/real.rb +7 -9
- data/lib/expressir/model/literals/string.rb +10 -12
- data/lib/expressir/model/literals.rb +13 -0
- data/lib/expressir/model/model_element.rb +286 -117
- data/lib/expressir/model/references/attribute_reference.rb +9 -12
- data/lib/expressir/model/references/group_reference.rb +9 -12
- data/lib/expressir/model/references/index_reference.rb +11 -15
- data/lib/expressir/model/references/simple_reference.rb +11 -14
- data/lib/expressir/model/references.rb +12 -0
- data/lib/expressir/model/remark_format.rb +17 -0
- data/lib/expressir/model/remark_info.rb +92 -0
- data/lib/expressir/model/remark_placement.rb +42 -0
- data/lib/expressir/model/repository.rb +472 -12
- data/lib/expressir/model/repository_validator.rb +293 -0
- data/lib/expressir/model/search_engine.rb +551 -0
- data/lib/expressir/model/statements/alias.rb +12 -16
- data/lib/expressir/model/statements/assignment.rb +12 -11
- data/lib/expressir/model/statements/case.rb +31 -14
- data/lib/expressir/model/statements/case_action.rb +9 -11
- data/lib/expressir/model/statements/compound.rb +10 -8
- data/lib/expressir/model/statements/escape.rb +10 -2
- data/lib/expressir/model/statements/if.rb +15 -14
- data/lib/expressir/model/statements/null.rb +10 -2
- data/lib/expressir/model/statements/procedure_call.rb +11 -11
- data/lib/expressir/model/statements/repeat.rb +21 -28
- data/lib/expressir/model/statements/return.rb +9 -8
- data/lib/expressir/model/statements/skip.rb +10 -2
- data/lib/expressir/model/statements.rb +20 -0
- data/lib/expressir/model/supertype_expressions/binary_supertype_expression.rb +13 -17
- data/lib/expressir/model/supertype_expressions/oneof_supertype_expression.rb +7 -9
- data/lib/expressir/model/supertype_expressions.rb +12 -0
- data/lib/expressir/model.rb +38 -78
- data/lib/expressir/package/builder.rb +231 -0
- data/lib/expressir/package/metadata.rb +79 -0
- data/lib/expressir/package/reader.rb +149 -0
- data/lib/expressir/package.rb +8 -0
- data/lib/expressir/schema_manifest.rb +159 -0
- data/lib/expressir/schema_manifest_entry.rb +15 -0
- data/lib/expressir/version.rb +6 -1
- data/lib/expressir.rb +79 -9
- data/lib/tasks/verify_remarks.rake +16 -0
- data/rakelib/native.rake +24 -0
- data/rakelib/oracle.rake +52 -0
- data/rakelib/verify_remarks.rake +16 -0
- metadata +431 -129
- data/.cross_rubies +0 -20
- data/.gitmodules +0 -6
- data/.yardopts +0 -11
- data/docs/development.md +0 -90
- data/exe/format +0 -18
- data/exe/generate-parser +0 -51
- data/lib/expressir/cli/ui.rb +0 -36
- data/lib/expressir/express/2.7/express_parser.so +0 -0
- data/lib/expressir/express/3.0/express_parser.so +0 -0
- data/lib/expressir/express/3.1/express_parser.so +0 -0
- data/lib/expressir/express/3.2/express_parser.so +0 -0
- data/lib/expressir/express/visitor.rb +0 -2588
- data/lib/expressir/model/data_type.rb +0 -9
- data/lib/expressir/model/declaration.rb +0 -9
- data/lib/expressir/model/expression.rb +0 -9
- data/lib/expressir/model/literal.rb +0 -9
- data/lib/expressir/model/reference.rb +0 -9
- data/lib/expressir/model/statement.rb +0 -9
- data/lib/expressir/model/supertype_expression.rb +0 -9
- data/rakelib/antlr4-native.rake +0 -161
- data/rakelib/cross-ruby.rake +0 -368
- data/spec/acceptance/version_spec.rb +0 -27
- data/spec/expressir/express/cache_spec.rb +0 -89
- data/spec/expressir/express/formatter_spec.rb +0 -173
- data/spec/expressir/express/parser_spec.rb +0 -141
- data/spec/expressir/model/model_element_spec.rb +0 -318
- data/spec/spec_helper.rb +0 -24
- data/spec/support/console_helper.rb +0 -29
- data/spec/syntax/multiple.exp +0 -23
- data/spec/syntax/multiple.yaml +0 -198
- data/spec/syntax/multiple_formatted.exp +0 -71
- data/spec/syntax/multiple_hyperlink_formatted.exp +0 -71
- data/spec/syntax/multiple_schema_head_hyperlink_formatted.exp +0 -13
- data/spec/syntax/remark.exp +0 -193
- data/spec/syntax/remark.yaml +0 -471
- data/spec/syntax/remark_formatted.exp +0 -228
- data/spec/syntax/single.exp +0 -4
- data/spec/syntax/single.yaml +0 -18
- data/spec/syntax/single_formatted.exp +0 -10
- data/spec/syntax/single_formatted.yaml +0 -36
- data/spec/syntax/syntax.exp +0 -333
- data/spec/syntax/syntax.yaml +0 -3509
- data/spec/syntax/syntax_formatted.exp +0 -902
- data/spec/syntax/syntax_hyperlink_formatted.exp +0 -902
- data/spec/syntax/syntax_schema_head_formatted.exp +0 -18
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Expressir
|
|
4
|
+
module Mapping
|
|
5
|
+
# Formalization, parser, and validator for MIM reference paths
|
|
6
|
+
# (expressir#88): the "Reference path" sections of module
|
|
7
|
+
# mapping.yaml files, written in the EXPRESS mapping language
|
|
8
|
+
# (ELF 5006:2025). Until now the notation was prose for humans
|
|
9
|
+
# only; this makes it machine-readable and checks it against real
|
|
10
|
+
# schemas.
|
|
11
|
+
#
|
|
12
|
+
# Grammar (ELF 5006 §5; corpus-verified over all module
|
|
13
|
+
# mapping.yaml refpaths):
|
|
14
|
+
# path := step+
|
|
15
|
+
# step := [link] node [index] [constraint]
|
|
16
|
+
# | constraint
|
|
17
|
+
# | MARKER
|
|
18
|
+
# link := "->" | "<-" | "<=" | "=>" | "*>"
|
|
19
|
+
# | "<*" | "=" NAME (relation to the previous node)
|
|
20
|
+
# node := NAME ["." ATTRIBUTE]
|
|
21
|
+
# | LINK (<<express:SCHEMA.ITEM,ITEM>>,
|
|
22
|
+
# #460: a link may stand
|
|
23
|
+
# wherever a declaration
|
|
24
|
+
# is referenced)
|
|
25
|
+
# index := "[" ("i" | DIGITS) "]"
|
|
26
|
+
# constraint := ["!"] "{" NAME [NAME "."] "=" STRING "}"
|
|
27
|
+
# MARKER := "{" | "}" | "[" | "]" | "(" | ")" | ":" | "|" | "!"
|
|
28
|
+
# | "," | "/" | "*" | "<" | ">" (grouping/annotation;
|
|
29
|
+
# never breaks a
|
|
30
|
+
# pending link)
|
|
31
|
+
#
|
|
32
|
+
# Validation (ELF 5006 §8): a running path context tracks the
|
|
33
|
+
# entity a path is "at"; each step must connect to it, attribute
|
|
34
|
+
# navigations must be type-compatible with their target, and
|
|
35
|
+
# constraints must reference an entity in the path context with an
|
|
36
|
+
# existing, constrainable attribute whose type accepts the literal.
|
|
37
|
+
module RefPath
|
|
38
|
+
# Wire-name mapping is declared here so a parsed path can itself
|
|
39
|
+
# be serialized with lutaml-model.
|
|
40
|
+
class Step < Lutaml::Model::Serializable
|
|
41
|
+
attribute :operator, :string
|
|
42
|
+
attribute :name, :string
|
|
43
|
+
attribute :attribute, :string
|
|
44
|
+
attribute :index, :string
|
|
45
|
+
attribute :literal, :string
|
|
46
|
+
attribute :link, :string
|
|
47
|
+
attribute :negated, :boolean
|
|
48
|
+
# For constraint steps: the entity owning the constrained
|
|
49
|
+
# attribute when it differs from the constrained entity.
|
|
50
|
+
attribute :owner, :string
|
|
51
|
+
|
|
52
|
+
key_value do
|
|
53
|
+
map "operator", to: :operator
|
|
54
|
+
map "name", to: :name
|
|
55
|
+
map "attribute", to: :attribute
|
|
56
|
+
map "index", to: :index
|
|
57
|
+
map "literal", to: :literal
|
|
58
|
+
map "link", to: :link
|
|
59
|
+
map "negated", to: :negated
|
|
60
|
+
map "owner", to: :owner
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Path < Lutaml::Model::Serializable
|
|
65
|
+
attribute :steps, Step, collection: true
|
|
66
|
+
attribute :parse_errors, :string, collection: true
|
|
67
|
+
|
|
68
|
+
key_value do
|
|
69
|
+
map "steps", to: :steps
|
|
70
|
+
map "parse_errors", to: :parse_errors
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# ELF 5006 §8 "Validation output": one structured verdict per
|
|
75
|
+
# path, serializable to YAML/JSON for the toolchain.
|
|
76
|
+
class ReportIssue < Lutaml::Model::Serializable
|
|
77
|
+
attribute :step, :integer
|
|
78
|
+
attribute :message, :string
|
|
79
|
+
|
|
80
|
+
key_value do
|
|
81
|
+
map "step", to: :step
|
|
82
|
+
map "message", to: :message
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Report < Lutaml::Model::Serializable
|
|
87
|
+
attribute :status, :string
|
|
88
|
+
attribute :start, :string
|
|
89
|
+
attribute :ending, :string
|
|
90
|
+
attribute :steps, :integer
|
|
91
|
+
attribute :entities, :string, collection: true
|
|
92
|
+
attribute :issues, ReportIssue, collection: true
|
|
93
|
+
|
|
94
|
+
key_value do
|
|
95
|
+
map "status", to: :status
|
|
96
|
+
map "start", to: :start
|
|
97
|
+
map "ending", to: :ending
|
|
98
|
+
map "steps", to: :steps
|
|
99
|
+
map "entities", to: :entities
|
|
100
|
+
map "issues", to: :issues
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
EXPRESS_LINK_PATH = /\A<<express:([^,>]+)(?:,[^>]+)?>>\z/
|
|
105
|
+
private_constant :EXPRESS_LINK_PATH
|
|
106
|
+
|
|
107
|
+
TOKEN = /<<express:[^,>]+(?:,[^>]+)?>>|->|<-|<=|=>|\*>|<\*|!?\{|[{}\[\]()=:|!,*\/<>]|'(?:''|[^'])*'|\w+(?:\.\w+)?/
|
|
108
|
+
MARKERS = ["{", "}", "[", "]", "(", ")", ":", "|", "!", ",", "/",
|
|
109
|
+
"*", "<", ">", "MAPPING_OF", "!{"].freeze
|
|
110
|
+
LINKS = ["->", "<-", "<=", "=>", "*>", "<*"].freeze
|
|
111
|
+
DECLARATION_COLLECTIONS = %i[types entities].freeze
|
|
112
|
+
private_constant :TOKEN, :MARKERS, :LINKS, :DECLARATION_COLLECTIONS
|
|
113
|
+
|
|
114
|
+
Issue = Struct.new(:step, :message, keyword_init: true)
|
|
115
|
+
|
|
116
|
+
module_function
|
|
117
|
+
|
|
118
|
+
# @return [Report] the structured VALID/INVALID verdict for
|
|
119
|
+
# +content+ (ELF 5006 §8 Validation output), carrying the
|
|
120
|
+
# traversed entities and every collected issue.
|
|
121
|
+
def report(content, repository, start: nil)
|
|
122
|
+
path = parse(content)
|
|
123
|
+
issues = validate(path, repository, start: start)
|
|
124
|
+
entities = path.steps.filter_map(&:name).uniq
|
|
125
|
+
Report.new(
|
|
126
|
+
status: (issues.empty? && path.parse_errors.empty? ? "VALID" : "INVALID"),
|
|
127
|
+
start: start || entities.first,
|
|
128
|
+
ending: entities.last,
|
|
129
|
+
steps: path.steps.size,
|
|
130
|
+
entities: entities,
|
|
131
|
+
issues: issues.map { |i| ReportIssue.new(step: i.step, message: i.message) },
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @param content [String] the mapping.yaml refpath content block
|
|
136
|
+
# @return [Path]
|
|
137
|
+
def parse(content)
|
|
138
|
+
tokens = content.to_s.scan(TOKEN)
|
|
139
|
+
steps = []
|
|
140
|
+
errors = []
|
|
141
|
+
pending_link = nil
|
|
142
|
+
pending_type_assign = false
|
|
143
|
+
false
|
|
144
|
+
i = 0
|
|
145
|
+
while i < tokens.length
|
|
146
|
+
token = tokens[i]
|
|
147
|
+
case token
|
|
148
|
+
when "!{"
|
|
149
|
+
true
|
|
150
|
+
step, consumed = parse_constraint(tokens[i..], negated: true)
|
|
151
|
+
steps << step
|
|
152
|
+
i += consumed
|
|
153
|
+
pending_link = nil
|
|
154
|
+
pending_type_assign = false
|
|
155
|
+
next
|
|
156
|
+
when "{"
|
|
157
|
+
step, consumed = parse_constraint(tokens[i..], negated: false)
|
|
158
|
+
steps << step
|
|
159
|
+
i += consumed
|
|
160
|
+
pending_link = nil
|
|
161
|
+
pending_type_assign = false
|
|
162
|
+
next
|
|
163
|
+
when *LINKS
|
|
164
|
+
pending_link = token
|
|
165
|
+
when "="
|
|
166
|
+
literal = tokens[i + 1]
|
|
167
|
+
if literal&.start_with?("'")
|
|
168
|
+
attach(steps, errors, :literal, literal)
|
|
169
|
+
i += 1
|
|
170
|
+
elsif literal.nil?
|
|
171
|
+
errors << "constraint '=' with nothing after it"
|
|
172
|
+
else
|
|
173
|
+
# `x = Type` type assignment: the following node (however
|
|
174
|
+
# many markers it sits behind, e.g. /MAPPING_OF(X)/)
|
|
175
|
+
# carries the "=" link.
|
|
176
|
+
pending_type_assign = true
|
|
177
|
+
end
|
|
178
|
+
when "["
|
|
179
|
+
if index_token?(tokens[i + 1])
|
|
180
|
+
attach(steps, errors, :index, tokens[i + 1])
|
|
181
|
+
i += 2 # the index token and the closing "]"
|
|
182
|
+
else
|
|
183
|
+
steps << Step.new(operator: "[")
|
|
184
|
+
end
|
|
185
|
+
when *MARKERS
|
|
186
|
+
steps << Step.new(operator: token)
|
|
187
|
+
when /\A<<express:/
|
|
188
|
+
steps << Step.new(operator: pending_link, link: token)
|
|
189
|
+
pending_link = nil
|
|
190
|
+
pending_type_assign = false
|
|
191
|
+
else
|
|
192
|
+
name, _, attribute = token.partition(".")
|
|
193
|
+
operator = pending_link || ("=" if pending_type_assign)
|
|
194
|
+
steps << Step.new(operator: operator, name: name,
|
|
195
|
+
attribute: attribute.empty? ? nil : attribute)
|
|
196
|
+
pending_link = nil
|
|
197
|
+
pending_type_assign = false
|
|
198
|
+
end
|
|
199
|
+
i += 1
|
|
200
|
+
end
|
|
201
|
+
Path.new(steps: steps, parse_errors: errors)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# A constraint block from +tokens+ (which start at "{" or "!{"):
|
|
205
|
+
# "{entity" or "{entity attr = literal}". Returns
|
|
206
|
+
# [Step, tokens_consumed].
|
|
207
|
+
def parse_constraint(tokens, negated:)
|
|
208
|
+
inner = []
|
|
209
|
+
consumed = 1
|
|
210
|
+
tokens[1..].each do |token|
|
|
211
|
+
consumed += 1
|
|
212
|
+
break if token == "}"
|
|
213
|
+
|
|
214
|
+
inner << token
|
|
215
|
+
end
|
|
216
|
+
eq = inner.index("=")
|
|
217
|
+
value = eq && inner[eq + 1]
|
|
218
|
+
literal = value || inner.find { |t| t.start_with?("'") }
|
|
219
|
+
names = inner.select { |t| t.match?(/\A\w+(\.\w+)?\z/) && !t.start_with?("'") }
|
|
220
|
+
qualified = names.find { |t| t.include?(".") }
|
|
221
|
+
entity = if qualified && names.size == 1
|
|
222
|
+
# {entity.attr = value} with the entity named only inside
|
|
223
|
+
# the qualified reference
|
|
224
|
+
qualified.split(".", 2)[0]
|
|
225
|
+
else
|
|
226
|
+
names.find { |t| !t.include?(".") }
|
|
227
|
+
end
|
|
228
|
+
owner, attribute = qualified ? qualified.split(".", 2) : [nil, nil]
|
|
229
|
+
owner = nil if entity && owner&.safe_downcase == entity.safe_downcase
|
|
230
|
+
step = Step.new(operator: "{", name: entity,
|
|
231
|
+
attribute: attribute, literal: literal,
|
|
232
|
+
negated: negated, owner: owner)
|
|
233
|
+
[step, consumed]
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def index_token?(token)
|
|
237
|
+
token == "i" || token&.match?(/\A\d+\z/)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Attach an index or constraint literal to the most recent node
|
|
241
|
+
# step that does not carry one yet.
|
|
242
|
+
def attach(steps, errors, field, value)
|
|
243
|
+
step = steps.reverse.each.find { |s| s.name && !s.public_send(field) }
|
|
244
|
+
if step
|
|
245
|
+
step.public_send("#{field}=", value)
|
|
246
|
+
else
|
|
247
|
+
case field
|
|
248
|
+
when :index then errors << "aggregate index [#{value}] has no node"
|
|
249
|
+
when :literal then errors << "constraint #{value} has no node"
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Validate a parsed path against a repository. Issues reference
|
|
255
|
+
# the step index they attach to. +start+ names the ARM entity the
|
|
256
|
+
# path is expected to begin at (the ae entity); when given and
|
|
257
|
+
# the path's first entity differs, a warning-level issue is
|
|
258
|
+
# recorded (ELF 5006 §8: the rule "may be relaxed for attribute
|
|
259
|
+
# mappings").
|
|
260
|
+
# @return [Array<Issue>]
|
|
261
|
+
def validate(path, repository, start: nil)
|
|
262
|
+
by_name = name_index(repository)
|
|
263
|
+
issues = []
|
|
264
|
+
ctx = nil
|
|
265
|
+
seen = {}
|
|
266
|
+
last_qualified = nil
|
|
267
|
+
path.steps.each_with_index do |step, pos|
|
|
268
|
+
# markers never break the running link, mirroring parse
|
|
269
|
+
next if marker_step?(step)
|
|
270
|
+
|
|
271
|
+
if step.link
|
|
272
|
+
issues.concat(check_link_step(step, pos, repository))
|
|
273
|
+
prev = preceding_name(steps_before(path, pos))
|
|
274
|
+
if step.name
|
|
275
|
+
check_exists(issues, pos, step.name, by_name)
|
|
276
|
+
ctx = step.name&.safe_downcase
|
|
277
|
+
elsif prev
|
|
278
|
+
ctx = prev&.safe_downcase
|
|
279
|
+
end
|
|
280
|
+
last_qualified = nil
|
|
281
|
+
next
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
case step.operator
|
|
285
|
+
when "(", ")"
|
|
286
|
+
# alternative sections: each starts a fresh path context
|
|
287
|
+
ctx = nil
|
|
288
|
+
last_qualified = nil
|
|
289
|
+
when "{"
|
|
290
|
+
issues.concat(check_constraint(step, pos, ctx, seen, by_name))
|
|
291
|
+
when "<=", "=>"
|
|
292
|
+
issues.concat(relation_issues(step, pos, ctx, by_name))
|
|
293
|
+
ctx = step.name&.safe_downcase
|
|
294
|
+
when "->"
|
|
295
|
+
issues.concat(forward_issues(step, pos, ctx, last_qualified, by_name))
|
|
296
|
+
ctx = step.name&.safe_downcase
|
|
297
|
+
last_qualified = nil
|
|
298
|
+
when "<-"
|
|
299
|
+
issues.concat(inverse_issues(step, pos, ctx, by_name))
|
|
300
|
+
ctx = step.name&.safe_downcase
|
|
301
|
+
last_qualified = step.attribute ? step : nil
|
|
302
|
+
when "*>", "<*"
|
|
303
|
+
issues.concat(select_extension_issues(step, pos, ctx, by_name))
|
|
304
|
+
ctx = step.name&.safe_downcase
|
|
305
|
+
else
|
|
306
|
+
issues.concat(check_step(step, pos, ctx, last_qualified, by_name))
|
|
307
|
+
if step.name
|
|
308
|
+
seen[step.name.safe_downcase] = true
|
|
309
|
+
last_qualified = step.attribute ? step : nil
|
|
310
|
+
ctx = step.name.safe_downcase unless ctx == step.name.safe_downcase && step.attribute
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
if start && path.steps.any?(&:name)
|
|
315
|
+
first = path.steps.find { |s| s.name && !s.link }
|
|
316
|
+
if first && first.name&.safe_downcase != start.safe_downcase
|
|
317
|
+
issues << Issue.new(step: 0,
|
|
318
|
+
message: "path starts at '#{first.name}' but " \
|
|
319
|
+
"the mapped entity is '#{start}'")
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
issues
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def marker_step?(step)
|
|
326
|
+
step.name.nil? && step.attribute.nil? && step.link.nil? &&
|
|
327
|
+
step.operator != "{" && step.operator != "(" && step.operator != ")"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def steps_before(path, pos)
|
|
331
|
+
path.steps[0...pos].reverse_each
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def preceding_name(steps)
|
|
335
|
+
steps.find { |s| s.name && !s.link }&.name
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def check_exists(issues, pos, name, by_name)
|
|
339
|
+
return if name.nil? || by_name.key?(name.safe_downcase)
|
|
340
|
+
|
|
341
|
+
issues << Issue.new(step: pos,
|
|
342
|
+
message: "unknown type '#{name}'")
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# ELF 5006 §8 constraint validation: the constrained entity must
|
|
346
|
+
# exist and appear in the path context; the constrained attribute
|
|
347
|
+
# must exist on it and be constrainable (not DERIVED/INVERSE);
|
|
348
|
+
# the literal must type-check against the attribute's type.
|
|
349
|
+
def check_constraint(step, pos, ctx, seen, by_name)
|
|
350
|
+
issues = []
|
|
351
|
+
entity = step.name
|
|
352
|
+
check_exists(issues, pos, entity, by_name)
|
|
353
|
+
return issues if entity.nil?
|
|
354
|
+
|
|
355
|
+
key = entity.safe_downcase
|
|
356
|
+
in_context = (ctx && ctx == key) || seen[key]
|
|
357
|
+
unless in_context
|
|
358
|
+
issues << Issue.new(step: pos,
|
|
359
|
+
message: "constraint entity '#{entity}' is not " \
|
|
360
|
+
"in the path context")
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
owner_key = (step.owner || entity).safe_downcase
|
|
364
|
+
decl = by_name[owner_key]&.last
|
|
365
|
+
return issues unless decl.is_a?(Model::Declarations::Entity)
|
|
366
|
+
|
|
367
|
+
if step.attribute
|
|
368
|
+
attribute = find_attribute(decl, step.attribute, by_name)
|
|
369
|
+
unless attribute
|
|
370
|
+
issues << Issue.new(step: pos,
|
|
371
|
+
message: "entity '#{entity}' has no attribute " \
|
|
372
|
+
"'#{step.attribute}'")
|
|
373
|
+
return issues
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
if attribute.respond_to?(:kind) &&
|
|
377
|
+
[Model::Declarations::Attribute::DERIVED, Model::Declarations::Attribute::INVERSE].include?(attribute.kind)
|
|
378
|
+
issues << Issue.new(step: pos,
|
|
379
|
+
message: "attribute '#{entity}." \
|
|
380
|
+
"#{step.attribute}' is " \
|
|
381
|
+
"#{attribute.kind} and cannot be " \
|
|
382
|
+
"constrained")
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
type = attribute.respond_to?(:type) ? attribute.type : nil
|
|
386
|
+
issue = value_type_issue(type, step.literal, entity,
|
|
387
|
+
step.attribute, by_name)
|
|
388
|
+
issues << Issue.new(step: pos, message: issue) if issue
|
|
389
|
+
elsif step.literal
|
|
390
|
+
issues << Issue.new(step: pos,
|
|
391
|
+
message: "constraint literal '#{step.literal}' " \
|
|
392
|
+
"has no attribute")
|
|
393
|
+
end
|
|
394
|
+
issues
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# ELF 5006 §8 value-type compatibility table.
|
|
398
|
+
def value_type_issue(type, literal, entity, attribute, by_name)
|
|
399
|
+
return nil if literal.nil?
|
|
400
|
+
|
|
401
|
+
kind = value_kind(literal)
|
|
402
|
+
resolved = resolve_type_name(type, by_name)
|
|
403
|
+
case resolved
|
|
404
|
+
when :string
|
|
405
|
+
return nil if kind == :string
|
|
406
|
+
|
|
407
|
+
"value #{literal} is not a single-quoted string " \
|
|
408
|
+
"(#{entity}.#{attribute} is a STRING)"
|
|
409
|
+
when :integer
|
|
410
|
+
return nil if kind == :integer
|
|
411
|
+
|
|
412
|
+
"value #{literal} is not an unquoted integer " \
|
|
413
|
+
"(#{entity}.#{attribute} is an INTEGER)"
|
|
414
|
+
when :real, :number
|
|
415
|
+
return nil if %i[integer real].include?(kind)
|
|
416
|
+
|
|
417
|
+
"value #{literal} is not an unquoted number " \
|
|
418
|
+
"(#{entity}.#{attribute} is a #{resolved.to_s.upcase})"
|
|
419
|
+
when :boolean
|
|
420
|
+
return nil if kind == :boolean
|
|
421
|
+
|
|
422
|
+
"value #{literal} is not TRUE or FALSE " \
|
|
423
|
+
"(#{entity}.#{attribute} is a BOOLEAN)"
|
|
424
|
+
when :logical
|
|
425
|
+
return nil if %i[boolean logical].include?(kind)
|
|
426
|
+
|
|
427
|
+
"value #{literal} is not TRUE, FALSE or UNKNOWN " \
|
|
428
|
+
"(#{entity}.#{attribute} is a LOGICAL)"
|
|
429
|
+
when :enumeration
|
|
430
|
+
return nil if kind == :enumeration && enumeration_item?(type, literal, by_name)
|
|
431
|
+
|
|
432
|
+
"value #{literal} is not an item of the enumeration type of " \
|
|
433
|
+
"#{entity}.#{attribute}"
|
|
434
|
+
when :select
|
|
435
|
+
nil # any value shape may be legitimate against a SELECT arm
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def value_kind(literal)
|
|
440
|
+
case literal
|
|
441
|
+
when /\A'/ then :string
|
|
442
|
+
when /\A(TRUE|FALSE)\z/ then :boolean
|
|
443
|
+
when /\AUNKNOWN\z/ then :logical
|
|
444
|
+
when /\A-?\d+\z/ then :integer
|
|
445
|
+
when /\A-?\d+\.\d+\z/ then :real
|
|
446
|
+
else :enumeration
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Resolve a parsed type node to a value-domain tag for the
|
|
451
|
+
# §8 table, or nil when the type cannot be determined.
|
|
452
|
+
def resolve_type_name(type, by_name)
|
|
453
|
+
case type
|
|
454
|
+
when Model::DataTypes::Boolean then :boolean
|
|
455
|
+
when Model::DataTypes::Logical then :logical
|
|
456
|
+
when Model::DataTypes::String then :string
|
|
457
|
+
when Model::DataTypes::Integer then :integer
|
|
458
|
+
when Model::DataTypes::Real then :real
|
|
459
|
+
when Model::DataTypes::Number then :number
|
|
460
|
+
when Model::DataTypes::Enumeration then :enumeration
|
|
461
|
+
when Model::DataTypes::Select then :select
|
|
462
|
+
when Model::ModelElement
|
|
463
|
+
return resolve_type_name(type.base_type, by_name) if type.respond_to?(:base_type) && type.base_type
|
|
464
|
+
|
|
465
|
+
id = if type.respond_to?(:base_path) && type.base_path
|
|
466
|
+
type.base_path.split(".").last
|
|
467
|
+
elsif type.respond_to?(:id)
|
|
468
|
+
type.id
|
|
469
|
+
end
|
|
470
|
+
return nil unless id
|
|
471
|
+
|
|
472
|
+
decl = by_name[id.safe_downcase]&.last
|
|
473
|
+
return nil unless decl.is_a?(Model::Declarations::Type)
|
|
474
|
+
|
|
475
|
+
resolve_type_name(decl.underlying_type, by_name)
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def enumeration_item?(type, literal, by_name)
|
|
480
|
+
item = literal.delete_prefix("'").delete_suffix("'").safe_downcase
|
|
481
|
+
case type
|
|
482
|
+
when Model::DataTypes::Enumeration
|
|
483
|
+
type.items.to_a.any? { |i| i.id&.safe_downcase == item }
|
|
484
|
+
when Model::ModelElement
|
|
485
|
+
return enumeration_item?(type.base_type, literal, by_name) if type.respond_to?(:base_type) && type.base_type
|
|
486
|
+
|
|
487
|
+
id = if type.respond_to?(:base_path) && type.base_path
|
|
488
|
+
type.base_path.split(".").last
|
|
489
|
+
elsif type.respond_to?(:id)
|
|
490
|
+
type.id
|
|
491
|
+
end
|
|
492
|
+
decl = id && by_name[id.safe_downcase]&.last
|
|
493
|
+
decl.is_a?(Model::Declarations::Type) &&
|
|
494
|
+
enumeration_item?(decl.underlying_type, literal, by_name)
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# ELF 5006 §8 navigation validation with a running context: the
|
|
499
|
+
# attribute must exist on the context entity, and its type must
|
|
500
|
+
# be able to reference the step's target entity.
|
|
501
|
+
def forward_issues(step, pos, ctx, last_qualified, by_name)
|
|
502
|
+
issues = []
|
|
503
|
+
unless ctx
|
|
504
|
+
issues << Issue.new(step: pos,
|
|
505
|
+
message: "'->' with no preceding entity in the " \
|
|
506
|
+
"path context")
|
|
507
|
+
return issues
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
owner = by_name[ctx]&.last
|
|
511
|
+
return issues unless owner.is_a?(Model::Declarations::Entity)
|
|
512
|
+
|
|
513
|
+
attr_name = last_qualified&.attribute || step.attribute
|
|
514
|
+
if attr_name.nil?
|
|
515
|
+
issues << Issue.new(step: pos,
|
|
516
|
+
message: "'->' requires an attribute-qualified " \
|
|
517
|
+
"node before it")
|
|
518
|
+
return issues
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
attribute = find_attribute(owner, attr_name, by_name)
|
|
522
|
+
unless attribute
|
|
523
|
+
issues << Issue.new(step: pos,
|
|
524
|
+
message: "entity '#{owner.id}' has no attribute " \
|
|
525
|
+
"'#{attr_name}'")
|
|
526
|
+
return issues
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
issues.concat(target_compat_issues(attribute, step, pos, ctx, by_name))
|
|
530
|
+
issues
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def inverse_issues(step, pos, ctx, by_name)
|
|
534
|
+
issues = []
|
|
535
|
+
entity = step.name
|
|
536
|
+
check_exists(issues, pos, entity, by_name)
|
|
537
|
+
return issues if entity.nil?
|
|
538
|
+
|
|
539
|
+
decl = by_name[entity.safe_downcase]&.last
|
|
540
|
+
return issues unless decl.is_a?(Model::Declarations::Entity)
|
|
541
|
+
|
|
542
|
+
attr_name = step.attribute
|
|
543
|
+
if attr_name.nil?
|
|
544
|
+
issues << Issue.new(step: pos,
|
|
545
|
+
message: "'<-' requires an attribute-qualified " \
|
|
546
|
+
"node after it")
|
|
547
|
+
return issues
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
attribute = find_attribute(decl, attr_name, by_name)
|
|
551
|
+
unless attribute
|
|
552
|
+
issues << Issue.new(step: pos,
|
|
553
|
+
message: "entity '#{entity}' has no attribute " \
|
|
554
|
+
"'#{attr_name}'")
|
|
555
|
+
return issues
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
if ctx
|
|
559
|
+
issues.concat(target_compat_issues(attribute, step, pos, ctx, by_name,
|
|
560
|
+
inverse: true))
|
|
561
|
+
end
|
|
562
|
+
issues
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# ELF 5006 §8: the attribute type must reference the target
|
|
566
|
+
# entity, a supertype of it, or a SELECT whose options reach it.
|
|
567
|
+
# `ctx` is the target of the navigation either way: for `->` the
|
|
568
|
+
# step names it; for `<-` the path context carries it.
|
|
569
|
+
def target_compat_issues(attribute, step, pos, ctx, by_name, inverse: false)
|
|
570
|
+
type = attribute.respond_to?(:type) ? attribute.type : nil
|
|
571
|
+
target = inverse ? ctx : step.name
|
|
572
|
+
return [] if type.nil? || target.nil?
|
|
573
|
+
return [] if references_entity?(type, target, by_name, {}.compare_by_identity)
|
|
574
|
+
|
|
575
|
+
owner = attribute.parent.respond_to?(:id) ? attribute.parent.id : nil
|
|
576
|
+
[Issue.new(step: pos,
|
|
577
|
+
message: "attribute '#{"#{owner}." if owner}" \
|
|
578
|
+
"#{attribute.id}' cannot reference '#{target}'")]
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def references_entity?(type, target, by_name, seen)
|
|
582
|
+
return false if type.nil? || seen[type]
|
|
583
|
+
|
|
584
|
+
seen[type] = true
|
|
585
|
+
if type.is_a?(Model::References::SimpleReference)
|
|
586
|
+
id = type.respond_to?(:base_path) && type.base_path ? type.base_path.to_s.split(".").last : type.id
|
|
587
|
+
return false if id.nil?
|
|
588
|
+
|
|
589
|
+
key = id.safe_downcase
|
|
590
|
+
return true if key == target
|
|
591
|
+
|
|
592
|
+
decl = by_name[key]&.last
|
|
593
|
+
return select_reaches?(decl, target, by_name, seen) if decl.is_a?(Model::Declarations::Type)
|
|
594
|
+
|
|
595
|
+
return entity_reaches?(decl, target, by_name, seen)
|
|
596
|
+
end
|
|
597
|
+
if type.respond_to?(:base_type) && type.base_type
|
|
598
|
+
return references_entity?(type.base_type, target, by_name, seen)
|
|
599
|
+
end
|
|
600
|
+
if type.is_a?(Model::DataTypes::Select)
|
|
601
|
+
return type.items.to_a.any? do |item|
|
|
602
|
+
id = item.respond_to?(:id) ? item.id&.safe_downcase : nil
|
|
603
|
+
next false if id.nil?
|
|
604
|
+
next true if id == target
|
|
605
|
+
|
|
606
|
+
decl = by_name[id]&.last
|
|
607
|
+
decl ? entity_reaches?(decl, target, by_name, seen) : false
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
false
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
def select_reaches?(type_decl, target, by_name, seen)
|
|
615
|
+
return false unless type_decl.is_a?(Model::Declarations::Type)
|
|
616
|
+
|
|
617
|
+
underlying = type_decl.underlying_type
|
|
618
|
+
references_entity?(underlying, target, by_name, seen)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
# target is a subtype of the referenced entity decl (or equal).
|
|
622
|
+
def entity_reaches?(decl, target, by_name, _seen)
|
|
623
|
+
return false unless decl.is_a?(Model::Declarations::Entity)
|
|
624
|
+
|
|
625
|
+
key = decl.id&.safe_downcase
|
|
626
|
+
return true if key == target
|
|
627
|
+
|
|
628
|
+
supertype_ids(decl, by_name).any? { |s| s&.safe_downcase == target }
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def select_extension_issues(step, pos, ctx, by_name)
|
|
632
|
+
issues = []
|
|
633
|
+
check_exists(issues, pos, step.name, by_name)
|
|
634
|
+
return issues if step.name.nil? || ctx.nil?
|
|
635
|
+
|
|
636
|
+
base = by_name[ctx]&.last
|
|
637
|
+
ext = by_name[step.name.safe_downcase]&.last
|
|
638
|
+
return issues unless base.is_a?(Model::Declarations::Type) &&
|
|
639
|
+
ext.is_a?(Model::Declarations::Type)
|
|
640
|
+
|
|
641
|
+
return issues if select_extends?(ext, base, by_name, {}.compare_by_identity)
|
|
642
|
+
|
|
643
|
+
issues << Issue.new(step: pos,
|
|
644
|
+
message: "SELECT '#{step.name}' does not extend '#{ctx}'")
|
|
645
|
+
issues
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# select_b <* select_a / select_a *> select_b: select_b extends
|
|
649
|
+
# select_a when select_b's BASED ON chain reaches select_a.
|
|
650
|
+
def select_extends?(ext, base, by_name, seen)
|
|
651
|
+
return false if seen[ext]
|
|
652
|
+
|
|
653
|
+
seen[ext] = true
|
|
654
|
+
underlying = ext.underlying_type
|
|
655
|
+
return false unless underlying.is_a?(Model::DataTypes::Select)
|
|
656
|
+
|
|
657
|
+
based = underlying.based_on
|
|
658
|
+
raw = based && (based.respond_to?(:base_path) ? based.base_path : based.id)
|
|
659
|
+
based_id = raw && raw.to_s.split(".").last
|
|
660
|
+
return true if based_id&.safe_downcase == base.id&.safe_downcase
|
|
661
|
+
|
|
662
|
+
based_decl = based_id && by_name[based_id.safe_downcase]&.last
|
|
663
|
+
based_decl ? select_extends?(based_decl, base, by_name, seen) : false
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def relation_issues(step, pos, ctx, by_name)
|
|
667
|
+
target = by_name[step.name&.safe_downcase]&.last
|
|
668
|
+
source = ctx && by_name[ctx]&.last
|
|
669
|
+
return [] unless source.is_a?(Model::Declarations::Entity) &&
|
|
670
|
+
target.is_a?(Model::Declarations::Entity)
|
|
671
|
+
|
|
672
|
+
if step.operator == "<="
|
|
673
|
+
return [] if supertype_ids(source, by_name)
|
|
674
|
+
.any? { |id| id&.safe_downcase == step.name&.safe_downcase }
|
|
675
|
+
|
|
676
|
+
[Issue.new(step: pos,
|
|
677
|
+
message: "'#{ctx}' is not a subtype of '#{step.name}'")]
|
|
678
|
+
else
|
|
679
|
+
return [] if supertype_ids(target, by_name)
|
|
680
|
+
.any? { |id| id&.safe_downcase == ctx }
|
|
681
|
+
|
|
682
|
+
[Issue.new(step: pos,
|
|
683
|
+
message: "'#{step.name}' is not a subtype of '#{ctx}'")]
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
def check_step(step, pos, ctx, _last_qualified, by_name)
|
|
688
|
+
found = by_name[step.name&.safe_downcase]
|
|
689
|
+
return [Issue.new(step: pos, message: "unknown type '#{step.name}'")] unless found
|
|
690
|
+
|
|
691
|
+
issues = attribute_issues(step, pos, found[1], by_name)
|
|
692
|
+
issues.concat(bare_continuity_issue(step, pos, ctx, found, by_name))
|
|
693
|
+
issues
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# ELF 5006 §8 navigation continuity: a bare node naming a
|
|
697
|
+
# different entity than the context must still be related to it
|
|
698
|
+
# (either one a supertype of the other).
|
|
699
|
+
def bare_continuity_issue(step, pos, ctx, found, by_name)
|
|
700
|
+
return [] unless ctx
|
|
701
|
+
return [] if ctx == step.name&.safe_downcase
|
|
702
|
+
|
|
703
|
+
decl = found[1]
|
|
704
|
+
return [] unless decl.is_a?(Model::Declarations::Entity)
|
|
705
|
+
|
|
706
|
+
down = ->(id) { id&.safe_downcase }
|
|
707
|
+
if supertype_ids(decl, by_name).any? { |id| down.(id) == ctx }
|
|
708
|
+
return []
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
ctx_decl = by_name[ctx]&.last
|
|
712
|
+
if ctx_decl.is_a?(Model::Declarations::Entity) &&
|
|
713
|
+
supertype_ids(ctx_decl, by_name).any? { |id| down.(id) == decl.id&.safe_downcase }
|
|
714
|
+
return []
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
[Issue.new(step: pos,
|
|
718
|
+
message: "'#{step.name}' breaks navigation continuity " \
|
|
719
|
+
"(context is '#{ctx}')")]
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
EXPRESS_BUILTINS = %w[NUMBER INTEGER REAL STRING BOOLEAN LOGICAL
|
|
723
|
+
BINARY].freeze
|
|
724
|
+
private_constant :EXPRESS_BUILTINS
|
|
725
|
+
|
|
726
|
+
def name_index(repository)
|
|
727
|
+
index = EXPRESS_BUILTINS.to_h { |b| [b.downcase, [nil, nil]] }
|
|
728
|
+
repository.schemas.flat_map do |schema|
|
|
729
|
+
DECLARATION_COLLECTIONS.flat_map do |coll|
|
|
730
|
+
Array(schema.public_send(coll))
|
|
731
|
+
.select { |d| d.respond_to?(:id) && d.id }
|
|
732
|
+
.map { |d| [d.id.safe_downcase, [schema, d]] }
|
|
733
|
+
end
|
|
734
|
+
end.each { |pair| index[pair[0]] = pair[1] }
|
|
735
|
+
index
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
# Validate links in aa.assertion_to (#460) and ae/sc entries.
|
|
739
|
+
def check_link_step(step, pos, repository)
|
|
740
|
+
parts = step.link.match(EXPRESS_LINK_PATH)
|
|
741
|
+
return [Issue.new(step: pos, message: "malformed link '#{step.link}'")] unless parts
|
|
742
|
+
|
|
743
|
+
schema_id, item_id = parts[1].split(".", 2).compact
|
|
744
|
+
schema = repository.schemas
|
|
745
|
+
.find { |s| s.id&.safe_downcase == schema_id&.safe_downcase }
|
|
746
|
+
unless schema
|
|
747
|
+
return [Issue.new(step: pos,
|
|
748
|
+
message: "unknown schema '#{schema_id}' in #{step.link}")]
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
return [] if item_id.nil?
|
|
752
|
+
return [] if Expressir::Mapping.item_declared?(schema, item_id)
|
|
753
|
+
|
|
754
|
+
[Issue.new(step: pos,
|
|
755
|
+
message: "schema '#{schema_id}' does not declare " \
|
|
756
|
+
"'#{item_id}' (#{step.link})")]
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
def attribute_issues(step, pos, decl, by_name)
|
|
760
|
+
return [] unless step.attribute
|
|
761
|
+
|
|
762
|
+
unless decl.is_a?(Model::Declarations::Entity)
|
|
763
|
+
return [Issue.new(step: pos,
|
|
764
|
+
message: "'#{step.name}' is not an entity, " \
|
|
765
|
+
"cannot carry '#{step.name}.#{step.attribute}'")]
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
attribute = find_attribute(decl, step.attribute, by_name)
|
|
769
|
+
unless attribute
|
|
770
|
+
return [Issue.new(step: pos,
|
|
771
|
+
message: "entity '#{step.name}' has no attribute " \
|
|
772
|
+
"'#{step.attribute}'")]
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
aggregate_issue(step, pos, attribute)
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
# Direct or inherited attribute: inheritance follows the
|
|
779
|
+
# subtype_of chain, resolved through the repository-wide index.
|
|
780
|
+
def find_attribute(entity, attribute_id, by_name)
|
|
781
|
+
key = attribute_id.safe_downcase
|
|
782
|
+
supertype_chain(entity, by_name).lazy.flat_map do |candidate|
|
|
783
|
+
Array(candidate.attributes)
|
|
784
|
+
end.find { |a| a.id&.safe_downcase == key }
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def supertype_chain(entity, by_name)
|
|
788
|
+
seen = {}.compare_by_identity
|
|
789
|
+
queue = [entity]
|
|
790
|
+
result = []
|
|
791
|
+
until queue.empty?
|
|
792
|
+
current = queue.shift
|
|
793
|
+
next if seen[current]
|
|
794
|
+
|
|
795
|
+
seen[current] = true
|
|
796
|
+
result << current
|
|
797
|
+
current.subtype_of.to_a.each do |ref|
|
|
798
|
+
sup = by_name[ref.respond_to?(:id) ? ref.id&.safe_downcase : nil]&.last
|
|
799
|
+
queue << sup if sup.is_a?(Model::Declarations::Entity)
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
result
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
def aggregate_issue(step, pos, attribute)
|
|
806
|
+
return [] unless step.index
|
|
807
|
+
return [] if attribute.type.respond_to?(:bound1)
|
|
808
|
+
|
|
809
|
+
[Issue.new(step: pos,
|
|
810
|
+
message: "'#{step.name}.#{step.attribute}' is not an " \
|
|
811
|
+
"aggregate but carries [#{step.index}]")]
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
# ELF 5006 §8 subtype/supertype relations, resolved through the
|
|
815
|
+
# repository-wide index so inherited chains (including
|
|
816
|
+
# cross-schema USE FROM) walk correctly.
|
|
817
|
+
def supertype_ids(entity, by_name)
|
|
818
|
+
seen = {}.compare_by_identity
|
|
819
|
+
queue = [entity]
|
|
820
|
+
names = []
|
|
821
|
+
until queue.empty?
|
|
822
|
+
current = queue.shift
|
|
823
|
+
next if seen[current]
|
|
824
|
+
|
|
825
|
+
seen[current] = true
|
|
826
|
+
direct = current.subtype_of.to_a.filter_map do |ref|
|
|
827
|
+
ref.respond_to?(:id) ? ref.id : nil
|
|
828
|
+
end
|
|
829
|
+
leaves = expression_leaves(current.supertype_expression)
|
|
830
|
+
.filter_map { |leaf| leaf.respond_to?(:id) ? leaf.id : nil }
|
|
831
|
+
names.concat(direct)
|
|
832
|
+
names.concat(leaves)
|
|
833
|
+
direct.concat(leaves).each do |id|
|
|
834
|
+
sup = by_name[id&.safe_downcase]&.last
|
|
835
|
+
queue << sup if sup.is_a?(Model::Declarations::Entity)
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
names.uniq
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
# Leaf reference nodes of a supertype expression tree.
|
|
842
|
+
def expression_leaves(expr)
|
|
843
|
+
return [] unless expr
|
|
844
|
+
|
|
845
|
+
if expr.respond_to?(:operands)
|
|
846
|
+
Array(expr.operands).flat_map { |o| expression_leaves(o) }
|
|
847
|
+
elsif expr.respond_to?(:operand1)
|
|
848
|
+
expression_leaves(expr.operand1) + expression_leaves(expr.operand2)
|
|
849
|
+
elsif expr.respond_to?(:id) && expr.id
|
|
850
|
+
[expr]
|
|
851
|
+
else
|
|
852
|
+
[]
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
end
|