expressir 1.2.11-aarch64-linux → 2.4.29-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,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
|