aas-core-codegen 0.0.16__py3-none-any.whl
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.
- aas_core_codegen/__init__.py +6 -0
- aas_core_codegen/__main__.py +8 -0
- aas_core_codegen/common.py +500 -0
- aas_core_codegen/cpp/__init__.py +1 -0
- aas_core_codegen/cpp/aas_common/__init__.py +6 -0
- aas_core_codegen/cpp/aas_common/_generate.py +713 -0
- aas_core_codegen/cpp/common.py +681 -0
- aas_core_codegen/cpp/constants/__init__.py +6 -0
- aas_core_codegen/cpp/constants/_generate.py +568 -0
- aas_core_codegen/cpp/description.py +654 -0
- aas_core_codegen/cpp/enhancing/__init__.py +4 -0
- aas_core_codegen/cpp/enhancing/_generate.py +993 -0
- aas_core_codegen/cpp/iteration/__init__.py +6 -0
- aas_core_codegen/cpp/iteration/_generate.py +2332 -0
- aas_core_codegen/cpp/jsonization/__init__.py +6 -0
- aas_core_codegen/cpp/jsonization/_generate.py +2619 -0
- aas_core_codegen/cpp/main.py +694 -0
- aas_core_codegen/cpp/naming.py +170 -0
- aas_core_codegen/cpp/optionaling.py +557 -0
- aas_core_codegen/cpp/pattern/__init__.py +6 -0
- aas_core_codegen/cpp/pattern/_generate.py +508 -0
- aas_core_codegen/cpp/revm/__init__.py +6 -0
- aas_core_codegen/cpp/revm/_generate.py +1149 -0
- aas_core_codegen/cpp/stringification/__init__.py +5 -0
- aas_core_codegen/cpp/stringification/_generate.py +955 -0
- aas_core_codegen/cpp/structure/__init__.py +7 -0
- aas_core_codegen/cpp/structure/_generate.py +1503 -0
- aas_core_codegen/cpp/transpilation.py +1383 -0
- aas_core_codegen/cpp/unrolling.py +159 -0
- aas_core_codegen/cpp/verification/__init__.py +6 -0
- aas_core_codegen/cpp/verification/_generate.py +3073 -0
- aas_core_codegen/cpp/visitation/__init__.py +6 -0
- aas_core_codegen/cpp/visitation/_generate.py +521 -0
- aas_core_codegen/cpp/wstringification/__init__.py +5 -0
- aas_core_codegen/cpp/wstringification/_generate.py +586 -0
- aas_core_codegen/cpp/xmlization/__init__.py +6 -0
- aas_core_codegen/cpp/xmlization/_generate.py +5373 -0
- aas_core_codegen/cpp/yielding.py +201 -0
- aas_core_codegen/csharp/__init__.py +1 -0
- aas_core_codegen/csharp/common.py +224 -0
- aas_core_codegen/csharp/constants/__init__.py +5 -0
- aas_core_codegen/csharp/constants/_generate.py +409 -0
- aas_core_codegen/csharp/copying/__init__.py +4 -0
- aas_core_codegen/csharp/copying/_generate.py +498 -0
- aas_core_codegen/csharp/description.py +1103 -0
- aas_core_codegen/csharp/enhancing/__init__.py +4 -0
- aas_core_codegen/csharp/enhancing/_generate.py +667 -0
- aas_core_codegen/csharp/jsonization/__init__.py +4 -0
- aas_core_codegen/csharp/jsonization/_generate.py +1630 -0
- aas_core_codegen/csharp/main.py +421 -0
- aas_core_codegen/csharp/naming.py +157 -0
- aas_core_codegen/csharp/reporting/__init__.py +4 -0
- aas_core_codegen/csharp/reporting/_generate.py +266 -0
- aas_core_codegen/csharp/stringification/__init__.py +4 -0
- aas_core_codegen/csharp/stringification/_generate.py +243 -0
- aas_core_codegen/csharp/structure/__init__.py +6 -0
- aas_core_codegen/csharp/structure/_generate.py +1341 -0
- aas_core_codegen/csharp/transpilation.py +990 -0
- aas_core_codegen/csharp/unrolling.py +211 -0
- aas_core_codegen/csharp/verification/__init__.py +6 -0
- aas_core_codegen/csharp/verification/_generate.py +1457 -0
- aas_core_codegen/csharp/visitation/__init__.py +5 -0
- aas_core_codegen/csharp/visitation/_generate.py +579 -0
- aas_core_codegen/csharp/xmlization/__init__.py +4 -0
- aas_core_codegen/csharp/xmlization/_generate.py +1980 -0
- aas_core_codegen/golang/__init__.py +1 -0
- aas_core_codegen/golang/aas_common/__init__.py +4 -0
- aas_core_codegen/golang/aas_common/_generate.py +152 -0
- aas_core_codegen/golang/common.py +303 -0
- aas_core_codegen/golang/constants/__init__.py +5 -0
- aas_core_codegen/golang/constants/_generate.py +339 -0
- aas_core_codegen/golang/description.py +501 -0
- aas_core_codegen/golang/enhancing/__init__.py +4 -0
- aas_core_codegen/golang/enhancing/_generate.py +527 -0
- aas_core_codegen/golang/jsonization/__init__.py +4 -0
- aas_core_codegen/golang/jsonization/_generate.py +1740 -0
- aas_core_codegen/golang/main.py +368 -0
- aas_core_codegen/golang/naming.py +412 -0
- aas_core_codegen/golang/pointering.py +631 -0
- aas_core_codegen/golang/reporting/__init__.py +4 -0
- aas_core_codegen/golang/reporting/_generate.py +218 -0
- aas_core_codegen/golang/stringification/__init__.py +4 -0
- aas_core_codegen/golang/stringification/_generate.py +394 -0
- aas_core_codegen/golang/structure/__init__.py +6 -0
- aas_core_codegen/golang/structure/_generate.py +1493 -0
- aas_core_codegen/golang/transpilation.py +1191 -0
- aas_core_codegen/golang/unrolling.py +159 -0
- aas_core_codegen/golang/verification/__init__.py +6 -0
- aas_core_codegen/golang/verification/_generate.py +1513 -0
- aas_core_codegen/golang/xmlization/__init__.py +4 -0
- aas_core_codegen/golang/xmlization/_generate.py +2507 -0
- aas_core_codegen/infer_for_schema/__init__.py +21 -0
- aas_core_codegen/infer_for_schema/_inline.py +693 -0
- aas_core_codegen/infer_for_schema/_len.py +527 -0
- aas_core_codegen/infer_for_schema/_pattern.py +311 -0
- aas_core_codegen/infer_for_schema/_set.py +394 -0
- aas_core_codegen/infer_for_schema/_stringify.py +201 -0
- aas_core_codegen/infer_for_schema/_types.py +135 -0
- aas_core_codegen/infer_for_schema/match.py +122 -0
- aas_core_codegen/intermediate/__init__.py +78 -0
- aas_core_codegen/intermediate/_hierarchy.py +397 -0
- aas_core_codegen/intermediate/_stringify.py +989 -0
- aas_core_codegen/intermediate/_translate.py +5128 -0
- aas_core_codegen/intermediate/_types.py +2901 -0
- aas_core_codegen/intermediate/construction.py +750 -0
- aas_core_codegen/intermediate/doc.py +344 -0
- aas_core_codegen/intermediate/pattern_verification.py +428 -0
- aas_core_codegen/intermediate/revm.py +985 -0
- aas_core_codegen/intermediate/type_inference.py +2266 -0
- aas_core_codegen/java/__init__.py +1 -0
- aas_core_codegen/java/common.py +197 -0
- aas_core_codegen/java/constants/__init__.py +5 -0
- aas_core_codegen/java/constants/_generate.py +334 -0
- aas_core_codegen/java/copying/__init__.py +4 -0
- aas_core_codegen/java/copying/_generate.py +502 -0
- aas_core_codegen/java/description.py +774 -0
- aas_core_codegen/java/enhancing/__init__.py +4 -0
- aas_core_codegen/java/enhancing/_generate.py +820 -0
- aas_core_codegen/java/generation/__init__.py +5 -0
- aas_core_codegen/java/generation/_generate.py +285 -0
- aas_core_codegen/java/jsonization/__init__.py +4 -0
- aas_core_codegen/java/jsonization/_generate.py +1472 -0
- aas_core_codegen/java/main.py +438 -0
- aas_core_codegen/java/naming.py +187 -0
- aas_core_codegen/java/optional.py +514 -0
- aas_core_codegen/java/reporting/__init__.py +4 -0
- aas_core_codegen/java/reporting/_generate.py +248 -0
- aas_core_codegen/java/stringification/__init__.py +4 -0
- aas_core_codegen/java/stringification/_generate.py +212 -0
- aas_core_codegen/java/structure/__init__.py +6 -0
- aas_core_codegen/java/structure/_generate.py +1767 -0
- aas_core_codegen/java/transpilation.py +1111 -0
- aas_core_codegen/java/verification/__init__.py +6 -0
- aas_core_codegen/java/verification/_generate.py +1536 -0
- aas_core_codegen/java/visitation/__init__.py +5 -0
- aas_core_codegen/java/visitation/_generate.py +689 -0
- aas_core_codegen/java/xmlization/__init__.py +4 -0
- aas_core_codegen/java/xmlization/_generate.py +2274 -0
- aas_core_codegen/jsonld/__init__.py +1 -0
- aas_core_codegen/jsonld/main.py +455 -0
- aas_core_codegen/jsonschema/__init__.py +1 -0
- aas_core_codegen/jsonschema/main.py +982 -0
- aas_core_codegen/main.py +245 -0
- aas_core_codegen/naming.py +133 -0
- aas_core_codegen/opcua/__init__.py +1 -0
- aas_core_codegen/opcua/main.py +1525 -0
- aas_core_codegen/opcua/naming.py +126 -0
- aas_core_codegen/parse/__init__.py +46 -0
- aas_core_codegen/parse/_rules.py +796 -0
- aas_core_codegen/parse/_stringify.py +532 -0
- aas_core_codegen/parse/_translate.py +3940 -0
- aas_core_codegen/parse/_types.py +973 -0
- aas_core_codegen/parse/retree/__init__.py +46 -0
- aas_core_codegen/parse/retree/_fix.py +434 -0
- aas_core_codegen/parse/retree/_parse.py +1143 -0
- aas_core_codegen/parse/retree/_render.py +298 -0
- aas_core_codegen/parse/retree/_stringify.py +199 -0
- aas_core_codegen/parse/retree/_types.py +362 -0
- aas_core_codegen/parse/retree/_visitor.py +70 -0
- aas_core_codegen/parse/tree.py +1303 -0
- aas_core_codegen/protobuf/__init__.py +1 -0
- aas_core_codegen/protobuf/common.py +225 -0
- aas_core_codegen/protobuf/description.py +1102 -0
- aas_core_codegen/protobuf/main.py +115 -0
- aas_core_codegen/protobuf/naming.py +143 -0
- aas_core_codegen/protobuf/structure/__init__.py +6 -0
- aas_core_codegen/protobuf/structure/_generate.py +502 -0
- aas_core_codegen/py.typed +1 -0
- aas_core_codegen/python/__init__.py +1 -0
- aas_core_codegen/python/aas_common/__init__.py +4 -0
- aas_core_codegen/python/aas_common/_generate.py +63 -0
- aas_core_codegen/python/common.py +406 -0
- aas_core_codegen/python/constants/__init__.py +5 -0
- aas_core_codegen/python/constants/_generate.py +377 -0
- aas_core_codegen/python/description.py +508 -0
- aas_core_codegen/python/jsonization/__init__.py +4 -0
- aas_core_codegen/python/jsonization/_generate.py +1391 -0
- aas_core_codegen/python/main.py +323 -0
- aas_core_codegen/python/naming.py +255 -0
- aas_core_codegen/python/stringification/__init__.py +4 -0
- aas_core_codegen/python/stringification/_generate.py +129 -0
- aas_core_codegen/python/structure/__init__.py +6 -0
- aas_core_codegen/python/structure/_generate.py +1801 -0
- aas_core_codegen/python/transpilation.py +958 -0
- aas_core_codegen/python/unrolling.py +156 -0
- aas_core_codegen/python/verification/__init__.py +6 -0
- aas_core_codegen/python/verification/_generate.py +1471 -0
- aas_core_codegen/python/xmlization/__init__.py +4 -0
- aas_core_codegen/python/xmlization/_generate.py +3003 -0
- aas_core_codegen/python_protobuf/__init__.py +1 -0
- aas_core_codegen/python_protobuf/main.py +1424 -0
- aas_core_codegen/python_protobuf/naming.py +85 -0
- aas_core_codegen/rdf_shacl/__init__.py +1 -0
- aas_core_codegen/rdf_shacl/_description.py +351 -0
- aas_core_codegen/rdf_shacl/common.py +206 -0
- aas_core_codegen/rdf_shacl/main.py +114 -0
- aas_core_codegen/rdf_shacl/naming.py +145 -0
- aas_core_codegen/rdf_shacl/rdf.py +435 -0
- aas_core_codegen/rdf_shacl/shacl.py +453 -0
- aas_core_codegen/run.py +124 -0
- aas_core_codegen/smoke/__init__.py +1 -0
- aas_core_codegen/smoke/main.py +219 -0
- aas_core_codegen/specific_implementations.py +72 -0
- aas_core_codegen/stringify.py +333 -0
- aas_core_codegen/typescript/__init__.py +1 -0
- aas_core_codegen/typescript/aas_common/__init__.py +4 -0
- aas_core_codegen/typescript/aas_common/_generate.py +472 -0
- aas_core_codegen/typescript/common.py +340 -0
- aas_core_codegen/typescript/constants/__init__.py +5 -0
- aas_core_codegen/typescript/constants/_generate.py +347 -0
- aas_core_codegen/typescript/description.py +530 -0
- aas_core_codegen/typescript/jsonization/__init__.py +4 -0
- aas_core_codegen/typescript/jsonization/_generate.py +1510 -0
- aas_core_codegen/typescript/main.py +258 -0
- aas_core_codegen/typescript/naming.py +189 -0
- aas_core_codegen/typescript/stringification/__init__.py +4 -0
- aas_core_codegen/typescript/stringification/_generate.py +367 -0
- aas_core_codegen/typescript/structure/__init__.py +6 -0
- aas_core_codegen/typescript/structure/_generate.py +2500 -0
- aas_core_codegen/typescript/transpilation.py +1051 -0
- aas_core_codegen/typescript/unrolling.py +159 -0
- aas_core_codegen/typescript/verification/__init__.py +6 -0
- aas_core_codegen/typescript/verification/_generate.py +1578 -0
- aas_core_codegen/xsd/__init__.py +1 -0
- aas_core_codegen/xsd/main.py +1187 -0
- aas_core_codegen/xsd/naming.py +83 -0
- aas_core_codegen/yielding/__init__.py +1 -0
- aas_core_codegen/yielding/flow.py +139 -0
- aas_core_codegen/yielding/linear.py +754 -0
- aas_core_codegen-0.0.16.dist-info/METADATA +211 -0
- aas_core_codegen-0.0.16.dist-info/RECORD +604 -0
- aas_core_codegen-0.0.16.dist-info/WHEEL +5 -0
- aas_core_codegen-0.0.16.dist-info/entry_points.txt +3 -0
- aas_core_codegen-0.0.16.dist-info/licenses/AUTHORS +9 -0
- aas_core_codegen-0.0.16.dist-info/licenses/LICENSE +23 -0
- aas_core_codegen-0.0.16.dist-info/top_level.txt +2 -0
- dev/continuous_integration/__init__.py +1 -0
- dev/continuous_integration/check_help_in_readme.py +208 -0
- dev/continuous_integration/check_init_and_pyproject_consistent.py +154 -0
- dev/continuous_integration/precommit.py +400 -0
- dev/dev_scripts/__init__.py +1 -0
- dev/dev_scripts/compare_rendered_regexes_against_source_py.py +42 -0
- dev/dev_scripts/copy_to_aas_core3_cpp.py +100 -0
- dev/dev_scripts/copy_to_aas_core3_java.py +90 -0
- dev/dev_scripts/download_latest_aas_core_meta_v3.py +114 -0
- dev/dev_scripts/draw_bipartite_graph_based_on_lines.py +37 -0
- dev/dev_scripts/run_tests_with_rerecord.py +69 -0
- dev/dev_scripts/update_to_aas_core_meta.py +174 -0
- dev/integration_tests/input/jsonschema/boilerplate/main.py +55 -0
- dev/integration_tests/input/meta_model.py +38 -0
- dev/integration_tests/input/python/boilerplate/main.py +153 -0
- dev/integration_tests/main.py +258 -0
- dev/test_data/csharp/test_structure/concrete_class_with_descendants/meta_model.py +15 -0
- dev/test_data/csharp/test_structure/constructor_without_arguments/all_properties_optional/meta_model.py +9 -0
- dev/test_data/csharp/test_structure/constructor_without_arguments/no_properties/meta_model.py +6 -0
- dev/test_data/csharp/test_verification/builtin_functions/len/on_list/meta_model.py +20 -0
- dev/test_data/csharp/test_verification/builtin_functions/len/on_str/meta_model.py +16 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/as_prefix/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/as_suffix/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/in_group_with_quantifier/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/in_the_middle/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/in_union/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/single_utf32_literal/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/with_quantifier_within_group/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_as_literal/with_quantifier_without_group/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/at_the_beginning/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/at_the_end/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/in_the_middle/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/multiple/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/single/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/literal/single_with_quantifier/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/mixed_with_non_utf32/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/more_than_two_high_surrogates/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/multiple_utf32_ranges/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/multiple_utf32_ranges_mixed_with_non_utf32/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/same_high_surrogate/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/same_high_surrogate_with_quantifier/meta_model.py +8 -0
- dev/test_data/csharp/test_verification/pattern_verification/utf32_in_character_set/range/two_high_surrogates/meta_model.py +8 -0
- dev/test_data/intermediate/expected/class/empty/meta_model.py +6 -0
- dev/test_data/intermediate/expected/class/implementation_specific_method/meta_model.py +8 -0
- dev/test_data/intermediate/expected/class/inheritance/meta_model.py +41 -0
- dev/test_data/intermediate/expected/class/methods_with_contracts/meta_model.py +15 -0
- dev/test_data/intermediate/expected/class/only_method_no_property/meta_model.py +7 -0
- dev/test_data/intermediate/expected/class/only_property_no_method/meta_model.py +9 -0
- dev/test_data/intermediate/expected/constant/constant_set/of_enum/meta_model.py +12 -0
- dev/test_data/intermediate/expected/constant/constant_set/of_str/meta_model.py +4 -0
- dev/test_data/intermediate/expected/constant/constant_set/with_description/meta_model.py +6 -0
- dev/test_data/intermediate/expected/constant/constant_set/with_superset_of/meta_model.py +14 -0
- dev/test_data/intermediate/expected/constant/constant_str/only_value/meta_model.py +7 -0
- dev/test_data/intermediate/expected/constant/constant_str/with_description/meta_model.py +5 -0
- dev/test_data/intermediate/expected/documentation/docstring_with_special_characters_in_literal/meta_model.py +20 -0
- dev/test_data/intermediate/expected/documentation/docstring_with_special_characters_outside_literal/meta_model.py +20 -0
- dev/test_data/intermediate/expected/empty/meta_model.py +2 -0
- dev/test_data/intermediate/expected/enumeration/meta_model.py +9 -0
- dev/test_data/intermediate/expected/interface/basic/meta_model.py +14 -0
- dev/test_data/intermediate/expected/interface/empty/meta_model.py +7 -0
- dev/test_data/intermediate/expected/interface/inheritance/meta_model.py +27 -0
- dev/test_data/intermediate/expected/interface/method_signature/meta_model.py +10 -0
- dev/test_data/intermediate/expected/interface/only_constructor/meta_model.py +11 -0
- dev/test_data/intermediate/expected/method/non_mutating/implementation_specific/meta_model.py +12 -0
- dev/test_data/intermediate/expected/method/non_mutating/understood/meta_model.py +11 -0
- dev/test_data/intermediate/expected/type_annotation/atomic/meta_model.py +9 -0
- dev/test_data/intermediate/expected/type_annotation/subscripted/class/meta_model.py +13 -0
- dev/test_data/intermediate/expected/type_annotation/subscripted/primitive/meta_model.py +9 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/enumeration_literals_in_subset_outside_of_superset/meta_model.py +20 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/invalid_literal/meta_model.py +11 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/mismatch_between_enumeration_and_literal/meta_model.py +30 -0
- dev/test_data/intermediate/unexpected/constant_set/of_enum/mismatch_in_enumerations_between_subset_and_superset/meta_model.py +21 -0
- dev/test_data/intermediate/unexpected/constant_set/of_str/literals_in_subset_outside_of_superset/meta_model.py +18 -0
- dev/test_data/intermediate/unexpected/constant_set/of_str/mismatch_between_type_annotation_and_literals/meta_model.py +10 -0
- dev/test_data/intermediate/unexpected/constant_set/of_str/superset_and_subset_mismatch_in_type/meta_model.py +12 -0
- dev/test_data/intermediate/unexpected/constraints/dangling_constraintref/meta_model.py +18 -0
- dev/test_data/intermediate/unexpected/constraints/duplicate_constraints/meta_model.py +20 -0
- dev/test_data/intermediate/unexpected/documentation/unexpected_documentation_elements/meta_model.py +34 -0
- dev/test_data/intermediate/unexpected/invariant/class_invariant_uses_re/meta_model.py +10 -0
- dev/test_data/intermediate/unexpected/invariant/invariant_of_constrained_primitive_uses_re/meta_model.py +7 -0
- dev/test_data/intermediate/unexpected/invariant/unexpected_argument_count_to_len/meta_model.py +20 -0
- dev/test_data/intermediate/unexpected/invariant/unhandled_built_in_function/meta_model.py +16 -0
- dev/test_data/intermediate/unexpected/method_definitions/non_constant_default/meta_model.py +7 -0
- dev/test_data/intermediate/unexpected/optional_constructor_arguments_wo_default/default_non_none/meta_model.py +16 -0
- dev/test_data/intermediate/unexpected/optional_constructor_arguments_wo_default/no_default/meta_model.py +16 -0
- dev/test_data/intermediate/unexpected/properties_and_constructor_arguments_do_not_match/after_inheritance/meta_model.py +58 -0
- dev/test_data/intermediate/unexpected/properties_and_constructor_arguments_do_not_match/type_missmatch/meta_model.py +9 -0
- dev/test_data/intermediate/unexpected/properties_and_constructor_arguments_do_not_match/within_class/meta_model.py +22 -0
- dev/test_data/jsonschema/test_main/regression_when_len_constraints_on_inherited_property/meta_model.py +28 -0
- dev/test_data/opcua/test_main/abstract_and_concrete_classes/meta_model.py +37 -0
- dev/test_data/opcua/test_main/classes_with_invariants/meta_model.py +21 -0
- dev/test_data/opcua/test_main/concrete_class_with_descendant/meta_model.py +27 -0
- dev/test_data/opcua/test_main/concrete_class_with_enum/meta_model.py +21 -0
- dev/test_data/opcua/test_main/concrete_class_with_list_of_instances/meta_model.py +21 -0
- dev/test_data/opcua/test_main/concrete_class_with_primitive_attributes/meta_model.py +41 -0
- dev/test_data/opcua/test_main/concrete_class_with_string/meta_model.py +13 -0
- dev/test_data/opcua/test_main/constrained_primitive/meta_model.py +20 -0
- dev/test_data/opcua/test_main/multiple_inheritance/meta_model.py +25 -0
- dev/test_data/parse/expected/constant/constant_set/of_enum/meta_model.py +12 -0
- dev/test_data/parse/expected/constant/constant_set/of_str/meta_model.py +4 -0
- dev/test_data/parse/expected/constant/constant_set/with_description/meta_model.py +6 -0
- dev/test_data/parse/expected/constant/constant_set/with_superset_of/meta_model.py +14 -0
- dev/test_data/parse/expected/constant/constant_str/only_value/meta_model.py +7 -0
- dev/test_data/parse/expected/constant/constant_str/with_description/meta_model.py +5 -0
- dev/test_data/parse/expected/enum/ok/meta_model.py +15 -0
- dev/test_data/parse/expected/implementation_specific_class/properties_and_methods_in_implementation_specific_class/meta_model.py +17 -0
- dev/test_data/parse/expected/inheritance/basic/meta_model.py +11 -0
- dev/test_data/parse/expected/inheritance/diamond/meta_model.py +26 -0
- dev/test_data/parse/expected/inheritance/inheritance_from_concrete_class/meta_model.py +10 -0
- dev/test_data/parse/expected/invariants/in_relation/meta_model.py +16 -0
- dev/test_data/parse/expected/method/arguments/meta_model.py +7 -0
- dev/test_data/parse/expected/method/basic/meta_model.py +7 -0
- dev/test_data/parse/expected/method/contracts/condition_as_keyword_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/condition_as_positional_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/description_as_keyword_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/description_as_positional_argument/meta_model.py +8 -0
- dev/test_data/parse/expected/method/contracts/multiple_contracts_in_order/meta_model.py +17 -0
- dev/test_data/parse/expected/method/contracts/postcondition/basic/meta_model.py +10 -0
- dev/test_data/parse/expected/method/contracts/postcondition/snapshot/with_keyword_arguments/meta_model.py +9 -0
- dev/test_data/parse/expected/method/contracts/postcondition/snapshot/with_positional_arguments/meta_model.py +9 -0
- dev/test_data/parse/expected/method/default/meta_model.py +9 -0
- dev/test_data/parse/expected/method/description/meta_model.py +8 -0
- dev/test_data/parse/expected/method/is_implementation_specific/meta_model.py +8 -0
- dev/test_data/parse/expected/method/non_mutating/meta_model.py +11 -0
- dev/test_data/parse/expected/method/returns_none/meta_model.py +7 -0
- dev/test_data/parse/expected/method/returns_something/meta_model.py +7 -0
- dev/test_data/parse/expected/single_class/description/meta_model.py +12 -0
- dev/test_data/parse/expected/single_class/empty/meta_model.py +6 -0
- dev/test_data/parse/expected/single_class/property/description/meta_model.py +14 -0
- dev/test_data/parse/expected/single_class/property/mandatory/meta_model.py +6 -0
- dev/test_data/parse/expected/single_class/property/optional/meta_model.py +6 -0
- dev/test_data/parse/expected/single_class/property/recursion_to_entity/meta_model.py +6 -0
- dev/test_data/parse/unexpected/class_decorators/non_name_decorator/meta_model.py +7 -0
- dev/test_data/parse/unexpected/class_decorators/unknown_decorator/meta_model.py +7 -0
- dev/test_data/parse/unexpected/class_definitions/is_abstract_and_implementation_specific/meta_model.py +8 -0
- dev/test_data/parse/unexpected/class_definitions/unexpected_docstring_before_a_method/meta_model.py +11 -0
- dev/test_data/parse/unexpected/class_definitions/unexpected_docstring_for_a_pass/meta_model.py +9 -0
- dev/test_data/parse/unexpected/class_definitions/unexpected_double_description_for_a_property/meta_model.py +10 -0
- dev/test_data/parse/unexpected/class_inheritances/inheriting_from_implementation_specific_parent/meta_model.py +20 -0
- dev/test_data/parse/unexpected/class_inheritances/non_name_super_class/meta_model.py +6 -0
- dev/test_data/parse/unexpected/enum/expression_as_assignment_value/meta_model.py +6 -0
- dev/test_data/parse/unexpected/enum/non_assignment/meta_model.py +8 -0
- dev/test_data/parse/unexpected/enum/non_string_literal/meta_model.py +6 -0
- dev/test_data/parse/unexpected/enum/unexpected_inheritance/meta_model.py +6 -0
- dev/test_data/parse/unexpected/method_contracts/contract/non_lambda_condition/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/contract/non_string_literal_description/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/contract/without_any_arguments/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/contract/without_condition/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/postcondition/OLD_in_postcondition_without_snapshot/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/postcondition/argument_missing_in_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/precondition/argument_missing_in_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/argument_missing_in_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/capture_not_a_lambda/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/invalid_name/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/name_not_a_string_literal/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/without_a_capture/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_contracts/snapshot/without_a_name/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/non_mutating/non_mutating_constructor/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/non_mutating/non_mutating_verification_function/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/non_name_decorator/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/unknown_call_decorator/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_decorators/unknown_name_decorator/meta_model.py +8 -0
- dev/test_data/parse/unexpected/method_definitions/argument_with_final/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/argument_without_a_type_annotation/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/default_for_self/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/dunder/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/init_with_return_type/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_keyword_only_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_positional_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_type_annotation_for_self/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_variable_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/with_variable_keyword_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/without_arguments/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/without_self/meta_model.py +7 -0
- dev/test_data/parse/unexpected/method_definitions/without_type_annotation_for_result/meta_model.py +7 -0
- dev/test_data/parse/unexpected/property_definitions/final_without_subscript/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/nested_final/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/non_simple/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/unexpected_assignment/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/unexpected_non_name_property/meta_model.py +6 -0
- dev/test_data/parse/unexpected/property_definitions/without_type_annotation/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/constant_set_with_a_non_set_subset/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_inheritance/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_reference_in_type_annotation_of_a_property/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_reference_in_type_annotation_of_an_argument/meta_model.py +7 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_reference_in_type_annotation_of_constant_set/meta_model.py +6 -0
- dev/test_data/parse/unexpected/symbol_table/dangling_subset_in_constant_set/meta_model.py +4 -0
- dev/test_data/parse/unexpected/symbol_table/inheritance_from_non_class/meta_model.py +10 -0
- dev/test_data/parse_retree/expected/character_set/common_escaping/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/complementing/double_caret/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/complementing/multiple_ranges/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/complementing/suffix_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/escape_first_caret/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/literals_which_need_no_escaping_in_characters_set_but_need_escaping_outside/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/multiple_ranges/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/single_literal/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/single_range/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/unescaped_dash/only_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/unescaped_dash/prefix_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/character_set/unescaped_dash/suffix_dash/source.py +1 -0
- dev/test_data/parse_retree/expected/dot/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/group/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/group_in_a_group/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/group_of_union_of_empty_concatenations/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/regex/source.py +1 -0
- dev/test_data/parse_retree/expected/empty/union_of_empty_concatenations/source.py +1 -0
- dev/test_data/parse_retree/expected/escaped_literals/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/at_the_beginning/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/at_the_end/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/in_the_middle/source.py +1 -0
- dev/test_data/parse_retree/expected/formatted_value/single_formatted_value/source.py +1 -0
- dev/test_data/parse_retree/expected/literal/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/at_least_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/at_least_one/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/at_most_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/exactly_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/maybe/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/greedy/zero_or_more/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/at_least_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/at_least_one/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/at_most_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/exactly_3/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/maybe/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/non_greedy/zero_or_more/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_character_set/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_formatted_value/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_group/source.py +1 -0
- dev/test_data/parse_retree/expected/quantifier/on_a_literal/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/double_end_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/double_start_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/end_symbol_in_the_middle/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/only_start_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/only_stop_symbol/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/start_symbol_at_the_beginning/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/start_symbol_in_the_middle/source.py +1 -0
- dev/test_data/parse_retree/expected/start_and_stop_symbols/stop_symbol_at_the_end/source.py +1 -0
- dev/test_data/parse_retree/expected/union/of_character_sets/source.py +1 -0
- dev/test_data/parse_retree/expected/union/of_groups/source.py +1 -0
- dev/test_data/parse_retree/expected/union/of_string_literals/source.py +1 -0
- dev/test_data/parse_retree/expected/union/within_group/source.py +1 -0
- dev/test_data/parse_retree/expected/whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/Uxxxxxxxx_out_of_range/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/only_backslash/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/short_Uxxxxxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/short_uxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/short_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unexpected_escaping/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/not_digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/not_whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/not_word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/character_literal/unhandled/word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/Uxxxxxxxx_out_of_range/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/only_backslash/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/short_Uxxxxxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/short_uxxxx/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/short_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unexpected_escaping/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/not_digit/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/not_whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/not_word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/whitespace/source.py +1 -0
- dev/test_data/parse_retree/unexpected/improper_escaping/range_character/unhandled/word/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_character_range/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_quantifier/at_least_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_quantifier/between_3_and_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/invalid_quantifier/exactly_x/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unhandled_group_directives/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/character_set/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/group/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/group_of_union_of_empty_concatenations/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/quantifier/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/quantifier_with_comma/source.py +1 -0
- dev/test_data/parse_retree/unexpected/unterminated/quantifier_with_number_and_comma/source.py +1 -0
- dev/test_data/proto/test_main/expected/abstract_and_concrete_classes/meta_model.py +37 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_descendants/meta_model.py +30 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_enum/meta_model.py +21 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_list_of_instances/meta_model.py +21 -0
- dev/test_data/proto/test_main/expected/concrete_class_with_primitive_attributes/meta_model.py +41 -0
- dev/test_data/python_protobuf/test_main/abstract_and_concrete_classes/expected_output/pbization.py +532 -0
- dev/test_data/python_protobuf/test_main/abstract_and_concrete_classes/meta_model.py +37 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_descendant/expected_output/pbization.py +527 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_descendant/meta_model.py +27 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_enum/expected_output/pbization.py +290 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_enum/meta_model.py +21 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_list_of_instances/expected_output/pbization.py +328 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_list_of_instances/meta_model.py +23 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_primitive_attributes/expected_output/pbization.py +274 -0
- dev/test_data/python_protobuf/test_main/concrete_class_with_primitive_attributes/meta_model.py +41 -0
- dev/test_data/rdf_shacl/test_main/expected/regression_when_lang_string_class_is_missing/meta_model.py +29 -0
- dev/test_data/rdf_shacl/test_main/expected/regression_when_len_constraints_on_inherited_property/meta_model.py +27 -0
- dev/test_data/rdf_shacl/test_main/unexpected/regression_len_constraint_on_class_property/meta_model.py +61 -0
- dev/test_data/real_meta_models/aas_core_meta.v3.py +5721 -0
- dev/test_data/smoke/test_main/unexpected/infer_for_schema_error/meta_model.py +12 -0
- dev/test_data/smoke/test_main/unexpected/intermediate_error/meta_model.py +18 -0
- dev/test_data/smoke/test_main/unexpected/parse_error/meta_model.py +5 -0
- dev/test_data/smoke/test_main/unexpected/pattern_verification_unparsable_regex/direct_match/meta_model.py +8 -0
- dev/test_data/smoke/test_main/unexpected/type_error/meta_model.py +18 -0
- dev/tests/__init__.py +1 -0
- dev/tests/common.py +197 -0
- dev/tests/cpp/__init__.py +0 -0
- dev/tests/cpp/test_common.py +32 -0
- dev/tests/cpp/test_main.py +144 -0
- dev/tests/cpp/test_pattern.py +188 -0
- dev/tests/cpp/test_verification.py +189 -0
- dev/tests/cpp/test_yielding.py +225 -0
- dev/tests/csharp/__init__.py +0 -0
- dev/tests/csharp/live_test_main.py +109 -0
- dev/tests/csharp/test_common.py +28 -0
- dev/tests/csharp/test_description.py +684 -0
- dev/tests/csharp/test_main.py +129 -0
- dev/tests/csharp/test_structure.py +93 -0
- dev/tests/csharp/test_verification.py +82 -0
- dev/tests/description.py +29 -0
- dev/tests/golang/__init__.py +0 -0
- dev/tests/golang/test_common.py +78 -0
- dev/tests/golang/test_main.py +128 -0
- dev/tests/infer_for_schema/__init__.py +0 -0
- dev/tests/infer_for_schema/common.py +47 -0
- dev/tests/infer_for_schema/test_len_on_properties.py +955 -0
- dev/tests/infer_for_schema/test_len_on_self.py +580 -0
- dev/tests/infer_for_schema/test_patterns_on_properties.py +686 -0
- dev/tests/infer_for_schema/test_patterns_on_self.py +258 -0
- dev/tests/infer_for_schema/test_property_in_set_of_enumeration_literals.py +600 -0
- dev/tests/infer_for_schema/test_property_in_set_of_primitives.py +549 -0
- dev/tests/intermediate/__init__.py +0 -0
- dev/tests/intermediate/test_constructor.py +719 -0
- dev/tests/intermediate/test_hierarchy.py +221 -0
- dev/tests/intermediate/test_revm.py +134 -0
- dev/tests/intermediate/test_translate.py +337 -0
- dev/tests/intermediate/test_type_inference.py +333 -0
- dev/tests/intermediate/test_types.py +169 -0
- dev/tests/java/__init__.py +0 -0
- dev/tests/java/test_common.py +20 -0
- dev/tests/java/test_description.py +128 -0
- dev/tests/java/test_main.py +234 -0
- dev/tests/jsonld_context/test_main.py +79 -0
- dev/tests/opcua/__init__.py +3 -0
- dev/tests/opcua/test_main.py +110 -0
- dev/tests/our_jsonschema/__init__.py +3 -0
- dev/tests/our_jsonschema/test_main.py +232 -0
- dev/tests/parse/__init__.py +0 -0
- dev/tests/parse/test_parse.py +503 -0
- dev/tests/parse/test_retree.py +272 -0
- dev/tests/proto/__init__.py +0 -0
- dev/tests/proto/test_main.py +112 -0
- dev/tests/python/__init__.py +0 -0
- dev/tests/python/test_common.py +124 -0
- dev/tests/python/test_main.py +126 -0
- dev/tests/python/test_xml_playground.py +254 -0
- dev/tests/python_protobuf/__init__.py +0 -0
- dev/tests/python_protobuf/test_main.py +111 -0
- dev/tests/rdf_shacl/__init__.py +0 -0
- dev/tests/rdf_shacl/test_common.py +32 -0
- dev/tests/rdf_shacl/test_description.py +223 -0
- dev/tests/rdf_shacl/test_main.py +194 -0
- dev/tests/smoke/__init__.py +0 -0
- dev/tests/smoke/test_main.py +83 -0
- dev/tests/test_common.py +94 -0
- dev/tests/typescript/__init__.py +0 -0
- dev/tests/typescript/test_common.py +108 -0
- dev/tests/typescript/test_main.py +125 -0
- dev/tests/xsd/__init__.py +0 -0
- dev/tests/xsd/test_main.py +227 -0
- dev/tests/yielding/__init__.py +0 -0
- dev/tests/yielding/test_linear.py +558 -0
|
@@ -0,0 +1,3940 @@
|
|
|
1
|
+
"""Translate the abstract syntax tree of the meta-model into parsed structures."""
|
|
2
|
+
import ast
|
|
3
|
+
import collections
|
|
4
|
+
import enum
|
|
5
|
+
import io
|
|
6
|
+
import itertools
|
|
7
|
+
import sys
|
|
8
|
+
import textwrap
|
|
9
|
+
from typing import (
|
|
10
|
+
List,
|
|
11
|
+
Any,
|
|
12
|
+
Optional,
|
|
13
|
+
Type,
|
|
14
|
+
Tuple,
|
|
15
|
+
Union,
|
|
16
|
+
Mapping,
|
|
17
|
+
MutableMapping,
|
|
18
|
+
Dict,
|
|
19
|
+
Set,
|
|
20
|
+
Iterable,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
import asttokens
|
|
24
|
+
import docutils.core
|
|
25
|
+
import docutils.io
|
|
26
|
+
import docutils.nodes
|
|
27
|
+
|
|
28
|
+
from icontract import ensure, require
|
|
29
|
+
|
|
30
|
+
from aas_core_codegen.common import (
|
|
31
|
+
Error,
|
|
32
|
+
Identifier,
|
|
33
|
+
IDENTIFIER_RE,
|
|
34
|
+
LinenoColumner,
|
|
35
|
+
assert_never,
|
|
36
|
+
is_stripped,
|
|
37
|
+
Stripped,
|
|
38
|
+
)
|
|
39
|
+
from aas_core_codegen.parse import tree, _rules
|
|
40
|
+
from aas_core_codegen.parse._types import (
|
|
41
|
+
AbstractClass,
|
|
42
|
+
Argument,
|
|
43
|
+
AtomicTypeAnnotation,
|
|
44
|
+
ConcreteClass,
|
|
45
|
+
Invariant,
|
|
46
|
+
Contract,
|
|
47
|
+
Contracts,
|
|
48
|
+
Default,
|
|
49
|
+
Class,
|
|
50
|
+
Enumeration,
|
|
51
|
+
EnumerationLiteral,
|
|
52
|
+
is_string_expr,
|
|
53
|
+
Serialization,
|
|
54
|
+
Property,
|
|
55
|
+
SelfTypeAnnotation,
|
|
56
|
+
Snapshot,
|
|
57
|
+
SubscriptedTypeAnnotation,
|
|
58
|
+
OurType,
|
|
59
|
+
SymbolTable,
|
|
60
|
+
TypeAnnotation,
|
|
61
|
+
UnverifiedSymbolTable,
|
|
62
|
+
PRIMITIVE_TYPES,
|
|
63
|
+
GENERIC_TYPES,
|
|
64
|
+
Description,
|
|
65
|
+
MetaModel,
|
|
66
|
+
ImplementationSpecificMethod,
|
|
67
|
+
UnderstoodMethod,
|
|
68
|
+
ConstructorToBeUnderstood,
|
|
69
|
+
FunctionUnion,
|
|
70
|
+
MethodUnion,
|
|
71
|
+
ConstantSet,
|
|
72
|
+
Constant,
|
|
73
|
+
ConstantPrimitive,
|
|
74
|
+
ConstantUnion,
|
|
75
|
+
SetLiteral,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# noinspection GrazieInspection
|
|
80
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
81
|
+
def source_to_atok(
|
|
82
|
+
source: str,
|
|
83
|
+
) -> Tuple[Optional[asttokens.ASTTokens], Optional[Exception]]:
|
|
84
|
+
"""
|
|
85
|
+
Parse the Python code.
|
|
86
|
+
|
|
87
|
+
:param source: Python code as text
|
|
88
|
+
:return: parsed module or error, if any
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
atok = asttokens.ASTTokens(source, parse=True)
|
|
92
|
+
except Exception as error:
|
|
93
|
+
return None, error
|
|
94
|
+
|
|
95
|
+
return atok, None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class _ExpectedImportsVisitor(ast.NodeVisitor):
|
|
99
|
+
_EXPECTED_NAME_FROM_MODULE = collections.OrderedDict(
|
|
100
|
+
[
|
|
101
|
+
("match", "re"),
|
|
102
|
+
("Enum", "enum"),
|
|
103
|
+
("List", "typing"),
|
|
104
|
+
("Optional", "typing"),
|
|
105
|
+
("Set", "typing"),
|
|
106
|
+
("DBC", "icontract"),
|
|
107
|
+
("invariant", "icontract"),
|
|
108
|
+
("ensure", "icontract"),
|
|
109
|
+
("require", "icontract"),
|
|
110
|
+
("abstract", "aas_core_meta.marker"),
|
|
111
|
+
("constant_set", "aas_core_meta.marker"),
|
|
112
|
+
("implementation_specific", "aas_core_meta.marker"),
|
|
113
|
+
("serialization", "aas_core_meta.marker"),
|
|
114
|
+
("verification", "aas_core_meta.marker"),
|
|
115
|
+
("non_mutating", "aas_core_meta.marker"),
|
|
116
|
+
]
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# pylint: disable=missing-docstring
|
|
120
|
+
|
|
121
|
+
def __init__(self) -> None:
|
|
122
|
+
self.errors = [] # type: List[Error]
|
|
123
|
+
|
|
124
|
+
def visit_Import(self, node: ast.Import) -> Any:
|
|
125
|
+
self.errors.append(
|
|
126
|
+
Error(
|
|
127
|
+
node,
|
|
128
|
+
"Unexpected ``import ...``. "
|
|
129
|
+
"Only ``from ... import...`` statements are expected.",
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# noinspection PyTypeChecker
|
|
134
|
+
def visit_ImportFrom(self, node: ast.ImportFrom) -> Any:
|
|
135
|
+
for name in node.names:
|
|
136
|
+
assert isinstance(name, ast.alias)
|
|
137
|
+
if name.asname is not None:
|
|
138
|
+
self.errors.append(
|
|
139
|
+
Error(
|
|
140
|
+
name,
|
|
141
|
+
"Unexpected ``from ... import ... as ...``. "
|
|
142
|
+
"Only ``from ... import...`` statements are expected.",
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
else:
|
|
146
|
+
if name.name not in self._EXPECTED_NAME_FROM_MODULE:
|
|
147
|
+
self.errors.append(
|
|
148
|
+
Error(name, f"Unexpected import of a name {name.name!r}.")
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
else:
|
|
152
|
+
expected_module = self._EXPECTED_NAME_FROM_MODULE[name.name]
|
|
153
|
+
if expected_module != node.module:
|
|
154
|
+
self.errors.append(
|
|
155
|
+
Error(
|
|
156
|
+
name,
|
|
157
|
+
f"Expected to import {name.name!r} "
|
|
158
|
+
f"from the module {expected_module}, "
|
|
159
|
+
f"but it is imported from {node.module}.",
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def check_expected_imports(atok: asttokens.ASTTokens) -> List[str]:
|
|
165
|
+
"""
|
|
166
|
+
Check that only expected imports are stated in the module.
|
|
167
|
+
|
|
168
|
+
This is important so that we can parse type annotations and inheritances.
|
|
169
|
+
|
|
170
|
+
Return errors, if any.
|
|
171
|
+
"""
|
|
172
|
+
visitor = _ExpectedImportsVisitor()
|
|
173
|
+
assert atok.tree is not None
|
|
174
|
+
visitor.visit(atok.tree)
|
|
175
|
+
|
|
176
|
+
if len(visitor.errors) == 0:
|
|
177
|
+
return []
|
|
178
|
+
|
|
179
|
+
lineno_columner = LinenoColumner(atok=atok)
|
|
180
|
+
return [lineno_columner.error_message(error) for error in visitor.errors]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# noinspection PyTypeChecker
|
|
184
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
185
|
+
def _type_annotation(
|
|
186
|
+
node: ast.AST, atok: asttokens.ASTTokens
|
|
187
|
+
) -> Tuple[Optional[TypeAnnotation], Optional[Error]]:
|
|
188
|
+
"""Parse the type annotation."""
|
|
189
|
+
if isinstance(node, ast.Name):
|
|
190
|
+
return AtomicTypeAnnotation(identifier=Identifier(node.id), node=node), None
|
|
191
|
+
|
|
192
|
+
elif isinstance(node, ast.Constant):
|
|
193
|
+
if not isinstance(node.value, str):
|
|
194
|
+
return (
|
|
195
|
+
None,
|
|
196
|
+
Error(
|
|
197
|
+
node.value,
|
|
198
|
+
f"Expected a string literal "
|
|
199
|
+
f"if the type annotation is given as a constant, "
|
|
200
|
+
f"but got: "
|
|
201
|
+
f"{node.value!r} (as {type(node.value)})",
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return AtomicTypeAnnotation(identifier=Identifier(node.value), node=node), None
|
|
206
|
+
|
|
207
|
+
elif isinstance(node, ast.Subscript):
|
|
208
|
+
if not isinstance(node.value, ast.Name):
|
|
209
|
+
return (
|
|
210
|
+
None,
|
|
211
|
+
Error(
|
|
212
|
+
node.value,
|
|
213
|
+
f"Expected a name to define "
|
|
214
|
+
f"a subscripted type annotation,"
|
|
215
|
+
f"but got: {atok.get_text(node.value)}",
|
|
216
|
+
),
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# NOTE (mristin, 2022-01-22):
|
|
220
|
+
# There were breaking changes between Python 3.8 and 3.9 in ``ast`` module.
|
|
221
|
+
# Relevant to this particular piece of parsing logic is the deprecation of
|
|
222
|
+
# ``ast.Index`` and ``ast.ExtSlice`` which is replaced with their actual value
|
|
223
|
+
# and ``ast.Tuple``, respectively.
|
|
224
|
+
#
|
|
225
|
+
# Hence, we need to switch on Python version and get the underlying slice value
|
|
226
|
+
# explicitly.
|
|
227
|
+
#
|
|
228
|
+
# See deprecation notes just at the end of:
|
|
229
|
+
# https://docs.python.org/3/library/ast.html#ast.AST
|
|
230
|
+
|
|
231
|
+
if isinstance(node.slice, ast.Slice):
|
|
232
|
+
return (
|
|
233
|
+
None,
|
|
234
|
+
Error(
|
|
235
|
+
node.slice,
|
|
236
|
+
f"Expected an index to define a subscripted type annotation, "
|
|
237
|
+
f"but got a slice: {atok.get_text(node.slice)}",
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# noinspection PyUnresolvedReferences
|
|
242
|
+
if (sys.version_info < (3, 9) and isinstance(node.slice, ast.ExtSlice)) or (
|
|
243
|
+
sys.version_info >= (3, 9)
|
|
244
|
+
and isinstance(node.slice, ast.Tuple)
|
|
245
|
+
and any(isinstance(elt, ast.Slice) for elt in node.slice.elts)
|
|
246
|
+
):
|
|
247
|
+
return (
|
|
248
|
+
None,
|
|
249
|
+
Error(
|
|
250
|
+
node.slice,
|
|
251
|
+
f"Expected an index to define a subscripted type annotation, "
|
|
252
|
+
f"but got an extended slice: {atok.get_text(node.slice)}",
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# NOTE (mristin, 2022-01-22):
|
|
257
|
+
# Please see the note about the deprecation of ``ast.Index`` above.
|
|
258
|
+
index_node: ast.AST
|
|
259
|
+
if sys.version_info < (3, 9):
|
|
260
|
+
# noinspection PyUnresolvedReferences
|
|
261
|
+
if isinstance(node.slice, ast.Index):
|
|
262
|
+
index_node = node.slice.value
|
|
263
|
+
else:
|
|
264
|
+
return (
|
|
265
|
+
None,
|
|
266
|
+
Error(
|
|
267
|
+
node.slice,
|
|
268
|
+
f"Expected an index to define a subscripted type annotation, "
|
|
269
|
+
f"but got: {atok.get_text(node.slice)}",
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
index_node = node.slice
|
|
274
|
+
|
|
275
|
+
subscripts = [] # type: List[TypeAnnotation]
|
|
276
|
+
|
|
277
|
+
if isinstance(index_node, ast.Tuple):
|
|
278
|
+
for elt in index_node.elts:
|
|
279
|
+
subscript_annotation, error = _type_annotation(node=elt, atok=atok)
|
|
280
|
+
if error is not None:
|
|
281
|
+
return None, error
|
|
282
|
+
|
|
283
|
+
assert subscript_annotation is not None
|
|
284
|
+
|
|
285
|
+
subscripts.append(subscript_annotation)
|
|
286
|
+
|
|
287
|
+
elif isinstance(index_node, (ast.Name, ast.Subscript, ast.Constant)):
|
|
288
|
+
subscript_annotation, error = _type_annotation(node=index_node, atok=atok)
|
|
289
|
+
if error is not None:
|
|
290
|
+
return None, error
|
|
291
|
+
|
|
292
|
+
assert subscript_annotation is not None
|
|
293
|
+
|
|
294
|
+
subscripts.append(subscript_annotation)
|
|
295
|
+
|
|
296
|
+
else:
|
|
297
|
+
return (
|
|
298
|
+
None,
|
|
299
|
+
Error(
|
|
300
|
+
index_node,
|
|
301
|
+
f"Expected a tuple, a name, a subscript or a string literal "
|
|
302
|
+
f"for a subscripted type annotation, "
|
|
303
|
+
f"but got: {atok.get_text(index_node)}",
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
SubscriptedTypeAnnotation(
|
|
309
|
+
identifier=Identifier(node.value.id),
|
|
310
|
+
subscripts=subscripts,
|
|
311
|
+
node=node,
|
|
312
|
+
),
|
|
313
|
+
None,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
else:
|
|
317
|
+
return (
|
|
318
|
+
None,
|
|
319
|
+
Error(
|
|
320
|
+
node,
|
|
321
|
+
f"Expected either atomic type annotation (as name or string literal) "
|
|
322
|
+
f"or a subscripted one (as a subscript), "
|
|
323
|
+
f"but got: {atok.get_text(node)} (as {type(node)})",
|
|
324
|
+
),
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
_PRIMITIVE_TYPE_NAMES_TO_CONSTANT_FUNCTION_NAMES: Dict[Identifier, Identifier] = {
|
|
329
|
+
Identifier("bool"): Identifier("constant_bool"),
|
|
330
|
+
Identifier("int"): Identifier("constant_int"),
|
|
331
|
+
Identifier("float"): Identifier("constant_float"),
|
|
332
|
+
Identifier("str"): Identifier("constant_str"),
|
|
333
|
+
Identifier("bytearray"): Identifier("constant_bytearray"),
|
|
334
|
+
}
|
|
335
|
+
# fmt: off
|
|
336
|
+
assert all(
|
|
337
|
+
primitive_type in _PRIMITIVE_TYPE_NAMES_TO_CONSTANT_FUNCTION_NAMES
|
|
338
|
+
for primitive_type in PRIMITIVE_TYPES
|
|
339
|
+
)
|
|
340
|
+
# fmt: on
|
|
341
|
+
|
|
342
|
+
# fmt: off
|
|
343
|
+
_PRIMITIVE_TYPE_NAMES_TO_PYTHON_TYPES: Mapping[
|
|
344
|
+
Identifier,
|
|
345
|
+
Union[Type[bool], Type[int], Type[float], Type[str], Type[bytearray]]
|
|
346
|
+
] = {
|
|
347
|
+
Identifier("bool"): bool,
|
|
348
|
+
Identifier("int"): int,
|
|
349
|
+
Identifier("float"): float,
|
|
350
|
+
Identifier("str"): str,
|
|
351
|
+
Identifier("bytearray"): bytearray,
|
|
352
|
+
}
|
|
353
|
+
assert all(
|
|
354
|
+
primitive_type in _PRIMITIVE_TYPE_NAMES_TO_PYTHON_TYPES
|
|
355
|
+
for primitive_type in PRIMITIVE_TYPES
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@require(lambda constant: isinstance(constant.value, str))
|
|
360
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
361
|
+
def _ast_constant_string_to_description(
|
|
362
|
+
constant: ast.Constant,
|
|
363
|
+
) -> Tuple[Optional[Description], Optional[Error]]:
|
|
364
|
+
"""Extract the docstring from the given string constant."""
|
|
365
|
+
text = constant.value
|
|
366
|
+
assert isinstance(
|
|
367
|
+
text, str
|
|
368
|
+
), f"Expected a string constant node, but got: {ast.dump(constant)!r}"
|
|
369
|
+
|
|
370
|
+
dedented = textwrap.dedent(text)
|
|
371
|
+
|
|
372
|
+
warnings = io.StringIO()
|
|
373
|
+
|
|
374
|
+
document: docutils.nodes.document
|
|
375
|
+
try:
|
|
376
|
+
document = docutils.core.publish_doctree(
|
|
377
|
+
dedented, settings_overrides={"warning_stream": warnings}
|
|
378
|
+
)
|
|
379
|
+
except Exception as err:
|
|
380
|
+
return None, Error(
|
|
381
|
+
constant, f"Failed to parse the description with docutils: {err}"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
warnings_text = warnings.getvalue()
|
|
385
|
+
if warnings_text:
|
|
386
|
+
return None, Error(
|
|
387
|
+
constant,
|
|
388
|
+
f"Failed to parse the description with docutils:\n"
|
|
389
|
+
f"{warnings_text.strip()}\n\n"
|
|
390
|
+
f"The original text was: {dedented!r}",
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
assert document is not None
|
|
394
|
+
|
|
395
|
+
return Description(document=document, node=constant), None
|
|
396
|
+
# fmt: on
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
# noinspection PyTypeChecker
|
|
400
|
+
# fmt: off
|
|
401
|
+
@require(
|
|
402
|
+
lambda primitive_type: primitive_type in PRIMITIVE_TYPES,
|
|
403
|
+
"Expected the type annotation as primitive type"
|
|
404
|
+
)
|
|
405
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
406
|
+
# fmt: on
|
|
407
|
+
def _parse_constant_primitive(
|
|
408
|
+
name: Identifier,
|
|
409
|
+
primitive_type: Identifier,
|
|
410
|
+
node: ast.AnnAssign,
|
|
411
|
+
atok: asttokens.ASTTokens,
|
|
412
|
+
) -> Tuple[Optional[ConstantPrimitive], Optional[Error]]:
|
|
413
|
+
"""Parse the definition of a constant as a call to ``constant_primitive`` marker."""
|
|
414
|
+
expected_func_name = _PRIMITIVE_TYPE_NAMES_TO_CONSTANT_FUNCTION_NAMES[
|
|
415
|
+
primitive_type
|
|
416
|
+
]
|
|
417
|
+
|
|
418
|
+
if (
|
|
419
|
+
not isinstance(node.value, ast.Call)
|
|
420
|
+
or not isinstance(node.value.func, ast.Name)
|
|
421
|
+
or node.value.func.id != expected_func_name
|
|
422
|
+
):
|
|
423
|
+
return (
|
|
424
|
+
None,
|
|
425
|
+
Error(
|
|
426
|
+
node.value,
|
|
427
|
+
f"Expected the value of the constant definition {name!r} "
|
|
428
|
+
f"of type {primitive_type} to be "
|
|
429
|
+
f"a call to the function {expected_func_name}, "
|
|
430
|
+
f"but got: {atok.get_text(node.value)}",
|
|
431
|
+
),
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
value_arg_node = None # type: Optional[ast.expr]
|
|
435
|
+
description_arg_node = None # type: Optional[ast.expr]
|
|
436
|
+
|
|
437
|
+
if len(node.value.args) > 0:
|
|
438
|
+
value_arg_node = node.value.args[0]
|
|
439
|
+
|
|
440
|
+
if len(node.value.args) > 1:
|
|
441
|
+
description_arg_node = node.value.args[1]
|
|
442
|
+
|
|
443
|
+
if len(node.value.args) > 3:
|
|
444
|
+
return None, Error(
|
|
445
|
+
node.value.args[3],
|
|
446
|
+
f"Expected only 3 arguments to {expected_func_name}, "
|
|
447
|
+
f"but got {len(node.value.args)}: {atok.get_text(node.value)}",
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
for kwarg in node.value.keywords:
|
|
451
|
+
if kwarg.arg == "value":
|
|
452
|
+
value_arg_node = kwarg.value
|
|
453
|
+
elif kwarg.arg == "description":
|
|
454
|
+
description_arg_node = kwarg.value
|
|
455
|
+
else:
|
|
456
|
+
return None, Error(
|
|
457
|
+
kwarg,
|
|
458
|
+
f"Unexpected keyword argument "
|
|
459
|
+
f"to {expected_func_name}: {atok.get_text(kwarg)}",
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
# region Parse ``value``
|
|
463
|
+
|
|
464
|
+
if not isinstance(value_arg_node, ast.Constant):
|
|
465
|
+
return (
|
|
466
|
+
None,
|
|
467
|
+
Error(
|
|
468
|
+
value_arg_node,
|
|
469
|
+
f"Expected a literal value, but got: {atok.get_text(value_arg_node)}",
|
|
470
|
+
),
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
if value_arg_node.value is None:
|
|
474
|
+
return (
|
|
475
|
+
None,
|
|
476
|
+
Error(
|
|
477
|
+
value_arg_node,
|
|
478
|
+
"We do not handle None as a constant at this moment. "
|
|
479
|
+
"Please contact the developers if you need this feature",
|
|
480
|
+
),
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
expected_type = _PRIMITIVE_TYPE_NAMES_TO_PYTHON_TYPES[primitive_type]
|
|
484
|
+
# noinspection PyTypeHints
|
|
485
|
+
if not isinstance(value_arg_node.value, expected_type):
|
|
486
|
+
return None, Error(
|
|
487
|
+
value_arg_node,
|
|
488
|
+
f"Expected the value as {expected_type}, "
|
|
489
|
+
f"but got {type(value_arg_node.value)}",
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
# endregion
|
|
493
|
+
|
|
494
|
+
# region Parse ``description``
|
|
495
|
+
|
|
496
|
+
description = None # type: Optional[Description]
|
|
497
|
+
if description_arg_node is not None:
|
|
498
|
+
if not isinstance(description_arg_node, ast.Constant) or not isinstance(
|
|
499
|
+
description_arg_node.value, str
|
|
500
|
+
):
|
|
501
|
+
return None, Error(
|
|
502
|
+
description_arg_node,
|
|
503
|
+
f"Expected string literal as the ``description`` argument, "
|
|
504
|
+
f"but got: {atok.get_text(description_arg_node)}",
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
description, error = _ast_constant_string_to_description(
|
|
508
|
+
constant=description_arg_node
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
if error is not None:
|
|
512
|
+
return None, Error(
|
|
513
|
+
node.value, "Failed to parse the ``description`` argument", [error]
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
assert description is not None
|
|
517
|
+
|
|
518
|
+
# endregion
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
ConstantPrimitive(
|
|
522
|
+
name=name,
|
|
523
|
+
value=value_arg_node.value,
|
|
524
|
+
description=description,
|
|
525
|
+
node=node,
|
|
526
|
+
),
|
|
527
|
+
None,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
# noinspection PyTypeChecker
|
|
532
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
533
|
+
def _parse_constant_set(
|
|
534
|
+
name: Identifier,
|
|
535
|
+
items_type_annotation: AtomicTypeAnnotation,
|
|
536
|
+
node: ast.AnnAssign,
|
|
537
|
+
atok: asttokens.ASTTokens,
|
|
538
|
+
) -> Tuple[Optional[ConstantSet], Optional[Error]]:
|
|
539
|
+
expected_func_name = "constant_set"
|
|
540
|
+
|
|
541
|
+
if (
|
|
542
|
+
not isinstance(node.value, ast.Call)
|
|
543
|
+
or not isinstance(node.value.func, ast.Name)
|
|
544
|
+
or node.value.func.id != expected_func_name
|
|
545
|
+
):
|
|
546
|
+
return (
|
|
547
|
+
None,
|
|
548
|
+
Error(
|
|
549
|
+
node.value,
|
|
550
|
+
f"Expected the value of the constant set definition {name!r} "
|
|
551
|
+
f"to be a call to the function {expected_func_name}, "
|
|
552
|
+
f"but got: {atok.get_text(node.value)}",
|
|
553
|
+
),
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# region Determine arguments
|
|
557
|
+
|
|
558
|
+
values_arg_node = None # type: Optional[ast.expr]
|
|
559
|
+
description_arg_node = None # type: Optional[ast.expr]
|
|
560
|
+
superset_of_arg_node = None # type: Optional[ast.expr]
|
|
561
|
+
|
|
562
|
+
if len(node.value.args) > 0:
|
|
563
|
+
values_arg_node = node.value.args[0]
|
|
564
|
+
|
|
565
|
+
if len(node.value.args) > 1:
|
|
566
|
+
description_arg_node = node.value.args[1]
|
|
567
|
+
|
|
568
|
+
if len(node.value.args) > 2:
|
|
569
|
+
superset_of_arg_node = node.value.args[3]
|
|
570
|
+
|
|
571
|
+
if len(node.value.args) > 3:
|
|
572
|
+
return None, Error(
|
|
573
|
+
node.value.args[4],
|
|
574
|
+
f"Expected only 4 arguments to {expected_func_name}, "
|
|
575
|
+
f"but got {len(node.value.args)}: {atok.get_text(node.value)}",
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
for kwarg in node.value.keywords:
|
|
579
|
+
if kwarg.arg == "values":
|
|
580
|
+
values_arg_node = kwarg.value
|
|
581
|
+
elif kwarg.arg == "description":
|
|
582
|
+
description_arg_node = kwarg.value
|
|
583
|
+
elif kwarg.arg == "superset_of":
|
|
584
|
+
superset_of_arg_node = kwarg.value
|
|
585
|
+
else:
|
|
586
|
+
return None, Error(
|
|
587
|
+
kwarg,
|
|
588
|
+
f"Unexpected keyword argument "
|
|
589
|
+
f"to {expected_func_name}: {atok.get_text(kwarg)}",
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
# endregion
|
|
593
|
+
|
|
594
|
+
# region Parse ``values``
|
|
595
|
+
|
|
596
|
+
if values_arg_node is None:
|
|
597
|
+
return None, Error(node.value, "Missing values argument")
|
|
598
|
+
|
|
599
|
+
if not isinstance(values_arg_node, ast.List):
|
|
600
|
+
return None, Error(
|
|
601
|
+
values_arg_node,
|
|
602
|
+
f"Expected the values of a constant set to be a list literal, "
|
|
603
|
+
f"but got: {atok.get_text(values_arg_node)}; "
|
|
604
|
+
f"in AST: {ast.dump(values_arg_node)}",
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
set_literals = [] # type: List[SetLiteral]
|
|
608
|
+
for i, elt in enumerate(values_arg_node.elts):
|
|
609
|
+
if not isinstance(elt, (ast.Attribute, ast.Constant)):
|
|
610
|
+
return None, Error(
|
|
611
|
+
elt,
|
|
612
|
+
f"Expected the values of a constant set to be all literals "
|
|
613
|
+
f"(either an enumeration literal or a literal of a primitive type "
|
|
614
|
+
f"such as str or int), but got at the index {i}: "
|
|
615
|
+
f"{atok.get_text(elt)}; in AST: {ast.dump(elt)}",
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
set_literals.append(SetLiteral(node=elt))
|
|
619
|
+
|
|
620
|
+
# endregion
|
|
621
|
+
|
|
622
|
+
# region Parse ``description``
|
|
623
|
+
|
|
624
|
+
description = None # type: Optional[Description]
|
|
625
|
+
|
|
626
|
+
if description_arg_node is not None:
|
|
627
|
+
if not isinstance(description_arg_node, ast.Constant) or not isinstance(
|
|
628
|
+
description_arg_node.value, str
|
|
629
|
+
):
|
|
630
|
+
return None, Error(
|
|
631
|
+
description_arg_node,
|
|
632
|
+
f"Expected string literal as the ``description`` argument, "
|
|
633
|
+
f"but got: {atok.get_text(description_arg_node)}; "
|
|
634
|
+
f"in AST: {ast.dump(description_arg_node)}",
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
description, error = _ast_constant_string_to_description(
|
|
638
|
+
constant=description_arg_node
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
if error is not None:
|
|
642
|
+
return None, Error(
|
|
643
|
+
node.value, "Failed to parse the ``description`` argument", [error]
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
assert description is not None
|
|
647
|
+
|
|
648
|
+
# endregion
|
|
649
|
+
|
|
650
|
+
# region Parse ``superset_of``
|
|
651
|
+
|
|
652
|
+
subsets = [] # type: List[Identifier]
|
|
653
|
+
|
|
654
|
+
if superset_of_arg_node is not None:
|
|
655
|
+
if not isinstance(superset_of_arg_node, ast.List):
|
|
656
|
+
return None, Error(
|
|
657
|
+
superset_of_arg_node,
|
|
658
|
+
f"Expected the ``superset_of`` of a constant set to be a list literal, "
|
|
659
|
+
f"but got: {atok.get_text(superset_of_arg_node)}; "
|
|
660
|
+
f"in AST: {ast.dump(superset_of_arg_node)}",
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
for i, elt in enumerate(superset_of_arg_node.elts):
|
|
664
|
+
if not isinstance(elt, ast.Name) or not IDENTIFIER_RE.fullmatch(elt.id):
|
|
665
|
+
return None, Error(
|
|
666
|
+
elt,
|
|
667
|
+
f"Expected the elements of the ``superset_of`` of a constant set "
|
|
668
|
+
f"to be a list of variables (referring to the other sets), "
|
|
669
|
+
f"but got at index {i}: "
|
|
670
|
+
f"{atok.get_text(elt)}; in AST: {ast.dump(elt)}",
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
subsets.append(Identifier(elt.id))
|
|
674
|
+
|
|
675
|
+
# endregion
|
|
676
|
+
|
|
677
|
+
return (
|
|
678
|
+
ConstantSet(
|
|
679
|
+
name=name,
|
|
680
|
+
items_type_annotation=items_type_annotation,
|
|
681
|
+
set_literals=set_literals,
|
|
682
|
+
subsets=subsets,
|
|
683
|
+
description=description,
|
|
684
|
+
node=node,
|
|
685
|
+
),
|
|
686
|
+
None,
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
# noinspection PyTypeChecker
|
|
691
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
692
|
+
def _ann_assign_to_constant(
|
|
693
|
+
node: ast.AnnAssign, atok: asttokens.ASTTokens
|
|
694
|
+
) -> Tuple[Optional[ConstantUnion], Optional[Error]]:
|
|
695
|
+
if not isinstance(node.target, ast.Name):
|
|
696
|
+
return (
|
|
697
|
+
None,
|
|
698
|
+
Error(
|
|
699
|
+
node.target,
|
|
700
|
+
f"Expected target of a constant to be a name, "
|
|
701
|
+
f"but got: {atok.get_text(node.target)}",
|
|
702
|
+
),
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
if not node.simple:
|
|
706
|
+
return (
|
|
707
|
+
None,
|
|
708
|
+
Error(
|
|
709
|
+
node.target,
|
|
710
|
+
"Expected a constant definition with a simple target (no parentheses!)",
|
|
711
|
+
),
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
if node.annotation is None:
|
|
715
|
+
return (
|
|
716
|
+
None,
|
|
717
|
+
Error(node.target, "Expected the constant to be annotated with a type"),
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
if node.value is None:
|
|
721
|
+
return (
|
|
722
|
+
None,
|
|
723
|
+
Error(node.value, "Unexpected constant definition without a value"),
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
type_annotation, error = _type_annotation(node=node.annotation, atok=atok)
|
|
727
|
+
if error is not None:
|
|
728
|
+
return None, error
|
|
729
|
+
|
|
730
|
+
assert type_annotation is not None
|
|
731
|
+
|
|
732
|
+
if isinstance(type_annotation, AtomicTypeAnnotation):
|
|
733
|
+
if type_annotation.identifier not in PRIMITIVE_TYPES:
|
|
734
|
+
return (
|
|
735
|
+
None,
|
|
736
|
+
Error(
|
|
737
|
+
node.annotation,
|
|
738
|
+
"We only handle definition of constant sets and primitive values "
|
|
739
|
+
f"at the moment, but you defined a constant "
|
|
740
|
+
f"of type {type_annotation.identifier!r}. "
|
|
741
|
+
f"Please contact the developers if you really need this feature",
|
|
742
|
+
),
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
return _parse_constant_primitive(
|
|
746
|
+
name=Identifier(node.target.id),
|
|
747
|
+
primitive_type=type_annotation.identifier,
|
|
748
|
+
node=node,
|
|
749
|
+
atok=atok,
|
|
750
|
+
)
|
|
751
|
+
elif isinstance(type_annotation, SubscriptedTypeAnnotation):
|
|
752
|
+
if type_annotation.identifier == "Set":
|
|
753
|
+
if len(type_annotation.subscripts) != 1:
|
|
754
|
+
return (
|
|
755
|
+
None,
|
|
756
|
+
Error(
|
|
757
|
+
node.annotation,
|
|
758
|
+
f"Expected exactly one subscript in the type annotation "
|
|
759
|
+
f"of the constant set {node.target.id!r}, "
|
|
760
|
+
f"but got {len(type_annotation.subscripts)}: "
|
|
761
|
+
f"{atok.get_text(node.annotation)}",
|
|
762
|
+
),
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
items_type_annotation = type_annotation.subscripts[0]
|
|
766
|
+
if not isinstance(items_type_annotation, AtomicTypeAnnotation):
|
|
767
|
+
return (
|
|
768
|
+
None,
|
|
769
|
+
Error(
|
|
770
|
+
node.annotation,
|
|
771
|
+
f"We only support constant sets of atomic types at the moment, "
|
|
772
|
+
f"but we got a subscripted type "
|
|
773
|
+
f"for the items: {atok.get_text(items_type_annotation)}. "
|
|
774
|
+
f"Please contact the developers if you need this feature",
|
|
775
|
+
),
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
return _parse_constant_set(
|
|
779
|
+
name=Identifier(node.target.id),
|
|
780
|
+
items_type_annotation=items_type_annotation,
|
|
781
|
+
node=node,
|
|
782
|
+
atok=atok,
|
|
783
|
+
)
|
|
784
|
+
else:
|
|
785
|
+
return (
|
|
786
|
+
None,
|
|
787
|
+
Error(
|
|
788
|
+
node.annotation,
|
|
789
|
+
f"We do not know how to handle "
|
|
790
|
+
f"the type annotation: {type_annotation.identifier!r}",
|
|
791
|
+
),
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
elif isinstance(type_annotation, SelfTypeAnnotation):
|
|
795
|
+
raise AssertionError(
|
|
796
|
+
f"Unexpected {SelfTypeAnnotation.__name__} in the constant definition. "
|
|
797
|
+
f"This is a bug as {SelfTypeAnnotation.__name__} are generated by our "
|
|
798
|
+
f"code, but can not be supplied through user input"
|
|
799
|
+
)
|
|
800
|
+
else:
|
|
801
|
+
assert_never(type_annotation)
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
# noinspection PyTypeChecker
|
|
805
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
806
|
+
def _ann_assign_to_property(
|
|
807
|
+
node: ast.AnnAssign, description: Optional[Description], atok: asttokens.ASTTokens
|
|
808
|
+
) -> Tuple[Optional[Property], Optional[Error]]:
|
|
809
|
+
if not isinstance(node.target, ast.Name):
|
|
810
|
+
return (
|
|
811
|
+
None,
|
|
812
|
+
Error(
|
|
813
|
+
node.target,
|
|
814
|
+
f"Expected property target to be a name, "
|
|
815
|
+
f"but got: {atok.get_text(node.target)}",
|
|
816
|
+
),
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
if not node.simple:
|
|
820
|
+
return (
|
|
821
|
+
None,
|
|
822
|
+
Error(
|
|
823
|
+
node.target,
|
|
824
|
+
"Expected a property with a simple target (no parentheses!)",
|
|
825
|
+
),
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
if node.annotation is None:
|
|
829
|
+
return (
|
|
830
|
+
None,
|
|
831
|
+
Error(node.target, "Expected property to be annotated with a type"),
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
type_annotation, error = _type_annotation(node=node.annotation, atok=atok)
|
|
835
|
+
if error is not None:
|
|
836
|
+
return None, error
|
|
837
|
+
|
|
838
|
+
assert type_annotation is not None
|
|
839
|
+
|
|
840
|
+
if node.value is not None:
|
|
841
|
+
return (
|
|
842
|
+
None,
|
|
843
|
+
Error(node.value, "Unexpected assignment of a value to a property"),
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
return (
|
|
847
|
+
Property(
|
|
848
|
+
name=Identifier(node.target.id),
|
|
849
|
+
type_annotation=type_annotation,
|
|
850
|
+
description=description,
|
|
851
|
+
node=node,
|
|
852
|
+
),
|
|
853
|
+
None,
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
# noinspection PyTypeChecker
|
|
858
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
859
|
+
def _args_to_arguments(
|
|
860
|
+
node: ast.arguments, atok: asttokens.ASTTokens
|
|
861
|
+
) -> Tuple[Optional[List[Argument]], Optional[Error]]:
|
|
862
|
+
"""Parse arguments of a method."""
|
|
863
|
+
if hasattr(node, "posonlyargs") and len(node.posonlyargs) > 0:
|
|
864
|
+
return None, Error(node, "Unexpected positional-only arguments")
|
|
865
|
+
|
|
866
|
+
if node.vararg is not None or node.kwarg is not None:
|
|
867
|
+
return None, Error(node, "Unexpected variable arguments")
|
|
868
|
+
|
|
869
|
+
if len(node.kwonlyargs) > 0:
|
|
870
|
+
return None, Error(node, "Unexpected keyword-only arguments")
|
|
871
|
+
|
|
872
|
+
assert len(node.kw_defaults) == 0, (
|
|
873
|
+
"No keyword-only arguments implies "
|
|
874
|
+
"there should be no defaults "
|
|
875
|
+
"for keyword-only arguments either."
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
if len(node.args) == 0:
|
|
879
|
+
return None, Error(node, "Unexpected no arguments")
|
|
880
|
+
|
|
881
|
+
arguments = [] # type: List[Argument]
|
|
882
|
+
|
|
883
|
+
# region ``self``
|
|
884
|
+
|
|
885
|
+
found_self = False
|
|
886
|
+
|
|
887
|
+
if len(node.args) >= 1 and node.args[0].arg == "self":
|
|
888
|
+
found_self = True
|
|
889
|
+
|
|
890
|
+
if node.args[0].annotation is not None:
|
|
891
|
+
return (
|
|
892
|
+
None,
|
|
893
|
+
Error(
|
|
894
|
+
node.args[0],
|
|
895
|
+
"Unexpected type annotation for the method argument ``self``",
|
|
896
|
+
),
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
if len(node.defaults) == len(node.args):
|
|
900
|
+
return (
|
|
901
|
+
None,
|
|
902
|
+
Error(
|
|
903
|
+
node.args[0],
|
|
904
|
+
"Unexpected default value for the method argument ``self``",
|
|
905
|
+
),
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
arguments.append(
|
|
909
|
+
Argument(
|
|
910
|
+
name=Identifier("self"),
|
|
911
|
+
type_annotation=SelfTypeAnnotation(),
|
|
912
|
+
default=None,
|
|
913
|
+
node=node.args[0],
|
|
914
|
+
)
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
# endregion
|
|
918
|
+
|
|
919
|
+
# region Non-self arguments
|
|
920
|
+
|
|
921
|
+
# We skip the first argument if we found ``self`` as it has been added to
|
|
922
|
+
# ``arguments`` already.
|
|
923
|
+
|
|
924
|
+
for i in range(1 if found_self else 0, len(node.args)):
|
|
925
|
+
arg_node = node.args[i]
|
|
926
|
+
|
|
927
|
+
# region Type annotation
|
|
928
|
+
if arg_node.annotation is None:
|
|
929
|
+
return (
|
|
930
|
+
None,
|
|
931
|
+
Error(
|
|
932
|
+
arg_node,
|
|
933
|
+
f"Unexpected method argument without a type annotation: "
|
|
934
|
+
f"{arg_node.arg}",
|
|
935
|
+
),
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
type_annotation, error = _type_annotation(node=arg_node.annotation, atok=atok)
|
|
939
|
+
if error is not None:
|
|
940
|
+
return (
|
|
941
|
+
None,
|
|
942
|
+
Error(
|
|
943
|
+
arg_node,
|
|
944
|
+
f"Failed to parse the type annotation "
|
|
945
|
+
f"of the method argument {arg_node.arg}: "
|
|
946
|
+
f"{atok.get_text(arg_node.annotation)}",
|
|
947
|
+
underlying=[error],
|
|
948
|
+
),
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
assert type_annotation is not None
|
|
952
|
+
|
|
953
|
+
# endregion
|
|
954
|
+
|
|
955
|
+
# region Default
|
|
956
|
+
default = None # type: Optional[Default]
|
|
957
|
+
|
|
958
|
+
# NOTE (mristin, 2021-12-16):
|
|
959
|
+
# A simple hypothetical test calculation:
|
|
960
|
+
# 5 args
|
|
961
|
+
# 2 defaults
|
|
962
|
+
#
|
|
963
|
+
# i = 3
|
|
964
|
+
# i - offset = 0 🠒 index in the node.defaults
|
|
965
|
+
# offset must be 3
|
|
966
|
+
# offset = len(node.args) - len(node.defaults) = 5 - 2 = 3
|
|
967
|
+
|
|
968
|
+
offset = len(node.args) - len(node.defaults)
|
|
969
|
+
if i >= offset:
|
|
970
|
+
default = Default(node=node.defaults[i - offset])
|
|
971
|
+
|
|
972
|
+
# endregion
|
|
973
|
+
|
|
974
|
+
arguments.append(
|
|
975
|
+
Argument(
|
|
976
|
+
name=Identifier(arg_node.arg),
|
|
977
|
+
type_annotation=type_annotation,
|
|
978
|
+
default=default,
|
|
979
|
+
node=arg_node,
|
|
980
|
+
)
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
# endregion
|
|
984
|
+
|
|
985
|
+
return arguments, None
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
# noinspection PyTypeChecker
|
|
989
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
990
|
+
def _parse_contract_condition(
|
|
991
|
+
node: ast.Call, atok: asttokens.ASTTokens
|
|
992
|
+
) -> Tuple[Optional[Contract], Optional[Error]]:
|
|
993
|
+
"""Parse the contract decorator."""
|
|
994
|
+
condition_node = None # type: Optional[ast.AST]
|
|
995
|
+
description_node = None # type: Optional[ast.AST]
|
|
996
|
+
|
|
997
|
+
if len(node.args) >= 1:
|
|
998
|
+
condition_node = node.args[0]
|
|
999
|
+
|
|
1000
|
+
if len(node.args) >= 2:
|
|
1001
|
+
description_node = node.args[1]
|
|
1002
|
+
|
|
1003
|
+
for keyword in node.keywords:
|
|
1004
|
+
if keyword.arg == "condition":
|
|
1005
|
+
condition_node = keyword.value
|
|
1006
|
+
|
|
1007
|
+
elif keyword.arg == "description":
|
|
1008
|
+
description_node = keyword.value
|
|
1009
|
+
|
|
1010
|
+
else:
|
|
1011
|
+
# We simply ignore to parse the argument.
|
|
1012
|
+
pass
|
|
1013
|
+
|
|
1014
|
+
if condition_node is None:
|
|
1015
|
+
return (
|
|
1016
|
+
None,
|
|
1017
|
+
Error(node, "Expected the condition to be defined for a contract"),
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
if not isinstance(condition_node, ast.Lambda):
|
|
1021
|
+
return (
|
|
1022
|
+
None,
|
|
1023
|
+
Error(
|
|
1024
|
+
condition_node,
|
|
1025
|
+
f"Expected a lambda function as a contract condition, "
|
|
1026
|
+
f"but got: {atok.get_text(condition_node)}",
|
|
1027
|
+
),
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
description = None # type: Optional[str]
|
|
1031
|
+
if description_node is not None:
|
|
1032
|
+
if not (
|
|
1033
|
+
isinstance(description_node, ast.Constant)
|
|
1034
|
+
and isinstance(description_node.value, str)
|
|
1035
|
+
):
|
|
1036
|
+
return (
|
|
1037
|
+
None,
|
|
1038
|
+
Error(
|
|
1039
|
+
description_node,
|
|
1040
|
+
f"Expected a string literal as a contract description, "
|
|
1041
|
+
f"but got: {atok.get_text(description_node)!r}",
|
|
1042
|
+
),
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
description = description_node.value
|
|
1046
|
+
|
|
1047
|
+
body, error = _rules.ast_node_to_our_node(node=condition_node.body)
|
|
1048
|
+
if error is not None:
|
|
1049
|
+
return None, Error(condition_node.body, "Failed to parse the contract", [error])
|
|
1050
|
+
|
|
1051
|
+
assert body is not None
|
|
1052
|
+
|
|
1053
|
+
if not isinstance(body, tree.Expression):
|
|
1054
|
+
return None, Error(
|
|
1055
|
+
condition_node.body,
|
|
1056
|
+
f"Expected an expression in the contract condition body, "
|
|
1057
|
+
f"but got: {tree.dump(body)}",
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
return (
|
|
1061
|
+
Contract(
|
|
1062
|
+
args=[Identifier(arg.arg) for arg in condition_node.args.args],
|
|
1063
|
+
description=description,
|
|
1064
|
+
body=body,
|
|
1065
|
+
node=node,
|
|
1066
|
+
),
|
|
1067
|
+
None,
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
# noinspection PyTypeChecker
|
|
1072
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
1073
|
+
def _parse_snapshot(
|
|
1074
|
+
node: ast.Call, atok: asttokens.ASTTokens
|
|
1075
|
+
) -> Tuple[Optional[Snapshot], Optional[Error]]:
|
|
1076
|
+
"""Parse the snapshot decorator."""
|
|
1077
|
+
capture_node = None # type: Optional[ast.AST]
|
|
1078
|
+
name_node = None # type: Optional[ast.AST]
|
|
1079
|
+
|
|
1080
|
+
if len(node.args) >= 1:
|
|
1081
|
+
capture_node = node.args[0]
|
|
1082
|
+
|
|
1083
|
+
if len(node.args) >= 2:
|
|
1084
|
+
name_node = node.args[1]
|
|
1085
|
+
|
|
1086
|
+
for keyword in node.keywords:
|
|
1087
|
+
if keyword.arg == "capture":
|
|
1088
|
+
capture_node = keyword.value
|
|
1089
|
+
|
|
1090
|
+
elif keyword.arg == "name":
|
|
1091
|
+
name_node = keyword.value
|
|
1092
|
+
|
|
1093
|
+
else:
|
|
1094
|
+
# We simply ignore to parse the argument.
|
|
1095
|
+
pass
|
|
1096
|
+
|
|
1097
|
+
if capture_node is None:
|
|
1098
|
+
return None, Error(node, "Expected the capture to be defined for a snapshot")
|
|
1099
|
+
|
|
1100
|
+
if not isinstance(capture_node, ast.Lambda):
|
|
1101
|
+
return (
|
|
1102
|
+
None,
|
|
1103
|
+
Error(
|
|
1104
|
+
capture_node,
|
|
1105
|
+
f"Expected a lambda function as a capture of a snapshot, "
|
|
1106
|
+
f"but got: {atok.get_text(capture_node)}",
|
|
1107
|
+
),
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
if name_node is not None and not (
|
|
1111
|
+
isinstance(name_node, ast.Constant) and isinstance(name_node.value, str)
|
|
1112
|
+
):
|
|
1113
|
+
return (
|
|
1114
|
+
None,
|
|
1115
|
+
Error(
|
|
1116
|
+
name_node,
|
|
1117
|
+
f"Expected a string literal as a capture name, "
|
|
1118
|
+
f"but got: {atok.get_text(name_node)}",
|
|
1119
|
+
),
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
if name_node is not None:
|
|
1123
|
+
name = name_node.value
|
|
1124
|
+
elif len(capture_node.args.args) == 1 and name_node is None:
|
|
1125
|
+
name = capture_node.args.args[0].arg
|
|
1126
|
+
else:
|
|
1127
|
+
return (
|
|
1128
|
+
None,
|
|
1129
|
+
Error(
|
|
1130
|
+
node,
|
|
1131
|
+
"Expected the name of the snapshot to be defined, "
|
|
1132
|
+
"but there was neither the single argument in the capture "
|
|
1133
|
+
"nor explicit ``name`` given",
|
|
1134
|
+
),
|
|
1135
|
+
)
|
|
1136
|
+
|
|
1137
|
+
if not IDENTIFIER_RE.fullmatch(name):
|
|
1138
|
+
return (
|
|
1139
|
+
None,
|
|
1140
|
+
Error(
|
|
1141
|
+
name_node if name_node is not None else node,
|
|
1142
|
+
f"Expected a capture name to be a valid identifier, but got: {name!r}",
|
|
1143
|
+
),
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
body, error = _rules.ast_node_to_our_node(node=capture_node.body)
|
|
1147
|
+
if error is not None:
|
|
1148
|
+
return None, Error(capture_node.body, "Failed to parse the snapshot", [error])
|
|
1149
|
+
|
|
1150
|
+
assert body is not None
|
|
1151
|
+
|
|
1152
|
+
if not isinstance(body, tree.Expression):
|
|
1153
|
+
return None, Error(
|
|
1154
|
+
capture_node.body,
|
|
1155
|
+
f"Expected an expression in the contract condition body, "
|
|
1156
|
+
f"but got: {tree.dump(body)}",
|
|
1157
|
+
)
|
|
1158
|
+
|
|
1159
|
+
return (
|
|
1160
|
+
Snapshot(
|
|
1161
|
+
args=[Identifier(arg.arg) for arg in capture_node.args.args],
|
|
1162
|
+
name=Identifier(name),
|
|
1163
|
+
body=body,
|
|
1164
|
+
node=node,
|
|
1165
|
+
),
|
|
1166
|
+
None,
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
# fmt: off
|
|
1171
|
+
# noinspection PyTypeChecker,PyUnresolvedReferences
|
|
1172
|
+
@ensure(
|
|
1173
|
+
lambda expect_self, result:
|
|
1174
|
+
not (result[0] is not None and not expect_self)
|
|
1175
|
+
or 'self' not in result[0].arguments_by_name,
|
|
1176
|
+
"No ``self`` argument if not ``expect_self``"
|
|
1177
|
+
)
|
|
1178
|
+
@ensure(
|
|
1179
|
+
lambda expect_self, result:
|
|
1180
|
+
not (result[0] is not None and expect_self)
|
|
1181
|
+
or (
|
|
1182
|
+
len(result[0].arguments) >= 0
|
|
1183
|
+
and result[0].arguments[0].name == 'self'
|
|
1184
|
+
and isinstance(result[0].arguments[0].type_annotation, SelfTypeAnnotation)
|
|
1185
|
+
),
|
|
1186
|
+
"If ``expect_self`` set, expect at least one argument and that should be ``self``"
|
|
1187
|
+
)
|
|
1188
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
1189
|
+
# fmt: on
|
|
1190
|
+
def _function_def_to_method(
|
|
1191
|
+
node: ast.FunctionDef, expect_self: bool, atok: asttokens.ASTTokens
|
|
1192
|
+
) -> Tuple[Optional[MethodUnion], Optional[Error]]:
|
|
1193
|
+
"""
|
|
1194
|
+
Parse the function definition into a method.
|
|
1195
|
+
|
|
1196
|
+
Though we have to distinguish in Python between a function and a method, we term
|
|
1197
|
+
both of them "methods" in our model.
|
|
1198
|
+
|
|
1199
|
+
If ``expect_self`` is set, the first argument is expected to be ``self``. Otherwise,
|
|
1200
|
+
no ``self`` argument is expected.
|
|
1201
|
+
"""
|
|
1202
|
+
# NOTE (mristin, 2021-12-19):
|
|
1203
|
+
# This run-time check is necessary as we already burned our fingers with it.
|
|
1204
|
+
assert isinstance(node, ast.FunctionDef)
|
|
1205
|
+
|
|
1206
|
+
name = node.name
|
|
1207
|
+
|
|
1208
|
+
if name != "__init__" and name.startswith("__") and name.endswith("__"):
|
|
1209
|
+
return (
|
|
1210
|
+
None,
|
|
1211
|
+
Error(
|
|
1212
|
+
node,
|
|
1213
|
+
f"Among all dunder methods, only ``__init__`` is expected, "
|
|
1214
|
+
f"but got: {name}",
|
|
1215
|
+
),
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
preconditions = [] # type: List[Contract]
|
|
1219
|
+
postconditions = [] # type: List[Contract]
|
|
1220
|
+
snapshots = [] # type: List[Snapshot]
|
|
1221
|
+
|
|
1222
|
+
is_implementation_specific = False
|
|
1223
|
+
verification = False # Set if the function is decorated with ``@verification``
|
|
1224
|
+
non_mutating = False # Set if the function is decorated with ``@non_mutating``
|
|
1225
|
+
|
|
1226
|
+
# region Parse decorators
|
|
1227
|
+
|
|
1228
|
+
for decorator in node.decorator_list:
|
|
1229
|
+
if isinstance(decorator, ast.Call):
|
|
1230
|
+
if isinstance(decorator.func, ast.Name):
|
|
1231
|
+
if decorator.func.id == "require":
|
|
1232
|
+
precondition, error = _parse_contract_condition(
|
|
1233
|
+
node=decorator, atok=atok
|
|
1234
|
+
)
|
|
1235
|
+
if error is not None:
|
|
1236
|
+
return None, error
|
|
1237
|
+
|
|
1238
|
+
assert precondition is not None
|
|
1239
|
+
|
|
1240
|
+
preconditions.append(precondition)
|
|
1241
|
+
|
|
1242
|
+
elif decorator.func.id == "ensure":
|
|
1243
|
+
postcondition, error = _parse_contract_condition(
|
|
1244
|
+
node=decorator, atok=atok
|
|
1245
|
+
)
|
|
1246
|
+
if error is not None:
|
|
1247
|
+
return None, error
|
|
1248
|
+
|
|
1249
|
+
assert postcondition is not None
|
|
1250
|
+
|
|
1251
|
+
postconditions.append(postcondition)
|
|
1252
|
+
|
|
1253
|
+
elif decorator.func.id == "snapshot":
|
|
1254
|
+
snapshot, error = _parse_snapshot(node=decorator, atok=atok)
|
|
1255
|
+
if error is not None:
|
|
1256
|
+
return None, error
|
|
1257
|
+
|
|
1258
|
+
assert snapshot is not None
|
|
1259
|
+
|
|
1260
|
+
snapshots.append(snapshot)
|
|
1261
|
+
|
|
1262
|
+
else:
|
|
1263
|
+
return (
|
|
1264
|
+
None,
|
|
1265
|
+
Error(
|
|
1266
|
+
decorator,
|
|
1267
|
+
f"Unexpected decorator of a method: {decorator.func.id}; "
|
|
1268
|
+
f"expected at most "
|
|
1269
|
+
f"``require``, ``ensure`` or ``snapshot``",
|
|
1270
|
+
),
|
|
1271
|
+
)
|
|
1272
|
+
else:
|
|
1273
|
+
return (
|
|
1274
|
+
None,
|
|
1275
|
+
Error(
|
|
1276
|
+
decorator,
|
|
1277
|
+
f"Unexpected non-name decorator of a method: "
|
|
1278
|
+
f"{atok.get_text(decorator.func)!r}",
|
|
1279
|
+
),
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
elif isinstance(decorator, ast.Name):
|
|
1283
|
+
if decorator.id == "implementation_specific":
|
|
1284
|
+
is_implementation_specific = True
|
|
1285
|
+
|
|
1286
|
+
elif decorator.id == "verification":
|
|
1287
|
+
verification = True
|
|
1288
|
+
|
|
1289
|
+
elif decorator.id == "non_mutating":
|
|
1290
|
+
non_mutating = True
|
|
1291
|
+
|
|
1292
|
+
else:
|
|
1293
|
+
return (
|
|
1294
|
+
None,
|
|
1295
|
+
Error(
|
|
1296
|
+
decorator,
|
|
1297
|
+
f"Unexpected simple decorator of a method: {decorator.id}; "
|
|
1298
|
+
f"expected at most ``implementation_specific``",
|
|
1299
|
+
),
|
|
1300
|
+
)
|
|
1301
|
+
else:
|
|
1302
|
+
return (
|
|
1303
|
+
None,
|
|
1304
|
+
Error(
|
|
1305
|
+
decorator,
|
|
1306
|
+
f"Expected decorators of a method to be "
|
|
1307
|
+
f"only ``ast.Name`` and ``ast.Call``, "
|
|
1308
|
+
f"but got: {atok.get_text(decorator)!r}",
|
|
1309
|
+
),
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1312
|
+
# endregion
|
|
1313
|
+
|
|
1314
|
+
# region Reverse contracts
|
|
1315
|
+
|
|
1316
|
+
# We need to reverse the contracts since the decorators are evaluated from bottom
|
|
1317
|
+
# up, while we parsed them from top to bottom.
|
|
1318
|
+
preconditions = list(reversed(preconditions))
|
|
1319
|
+
snapshots = list(reversed(snapshots))
|
|
1320
|
+
postconditions = list(reversed(postconditions))
|
|
1321
|
+
|
|
1322
|
+
# endregion
|
|
1323
|
+
|
|
1324
|
+
# region Parse arguments and body
|
|
1325
|
+
|
|
1326
|
+
description = None # type: Optional[Description]
|
|
1327
|
+
body = node.body
|
|
1328
|
+
|
|
1329
|
+
if len(node.body) >= 1 and is_string_expr(expr=node.body[0]):
|
|
1330
|
+
assert isinstance(node.body[0], ast.Expr)
|
|
1331
|
+
assert isinstance(node.body[0].value, ast.Constant)
|
|
1332
|
+
|
|
1333
|
+
description, error = _ast_constant_string_to_description(
|
|
1334
|
+
constant=node.body[0].value
|
|
1335
|
+
)
|
|
1336
|
+
|
|
1337
|
+
if error is not None:
|
|
1338
|
+
return None, error
|
|
1339
|
+
|
|
1340
|
+
body = node.body[1:]
|
|
1341
|
+
|
|
1342
|
+
arguments, error = _args_to_arguments(node=node.args, atok=atok)
|
|
1343
|
+
if error is not None:
|
|
1344
|
+
return (
|
|
1345
|
+
None,
|
|
1346
|
+
Error(
|
|
1347
|
+
node,
|
|
1348
|
+
f"Failed to parse arguments of the method: {name}",
|
|
1349
|
+
underlying=[error],
|
|
1350
|
+
),
|
|
1351
|
+
)
|
|
1352
|
+
|
|
1353
|
+
assert arguments is not None
|
|
1354
|
+
|
|
1355
|
+
returns = None # type: Optional[TypeAnnotation]
|
|
1356
|
+
if node.returns is None:
|
|
1357
|
+
return (
|
|
1358
|
+
None,
|
|
1359
|
+
Error(
|
|
1360
|
+
node,
|
|
1361
|
+
f"Unexpected method without a type annotation for the result: {name}",
|
|
1362
|
+
),
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
if not (isinstance(node.returns, ast.Constant) and node.returns.value is None):
|
|
1366
|
+
returns, error = _type_annotation(node=node.returns, atok=atok)
|
|
1367
|
+
if error is not None:
|
|
1368
|
+
return None, error
|
|
1369
|
+
|
|
1370
|
+
# endregion
|
|
1371
|
+
|
|
1372
|
+
# region All contract arguments are included in the function arguments
|
|
1373
|
+
|
|
1374
|
+
function_arg_set = set(arg.name for arg in arguments)
|
|
1375
|
+
|
|
1376
|
+
for contract in preconditions:
|
|
1377
|
+
for arg in contract.args:
|
|
1378
|
+
if arg not in function_arg_set:
|
|
1379
|
+
return (
|
|
1380
|
+
None,
|
|
1381
|
+
Error(
|
|
1382
|
+
contract.node,
|
|
1383
|
+
f"The argument of the precondition is not provided "
|
|
1384
|
+
f"in the method: {arg}",
|
|
1385
|
+
),
|
|
1386
|
+
)
|
|
1387
|
+
|
|
1388
|
+
has_snapshots = len(snapshots) > 0
|
|
1389
|
+
for contract in postconditions:
|
|
1390
|
+
for arg in contract.args:
|
|
1391
|
+
if arg == "OLD":
|
|
1392
|
+
if not has_snapshots and arg == "OLD":
|
|
1393
|
+
return (
|
|
1394
|
+
None,
|
|
1395
|
+
Error(
|
|
1396
|
+
contract.node,
|
|
1397
|
+
f"The argument OLD of the postcondition is not provided "
|
|
1398
|
+
f"since there were no snapshots defined "
|
|
1399
|
+
f"for the method: {name}",
|
|
1400
|
+
),
|
|
1401
|
+
)
|
|
1402
|
+
|
|
1403
|
+
elif arg == "result":
|
|
1404
|
+
continue
|
|
1405
|
+
|
|
1406
|
+
elif arg not in function_arg_set:
|
|
1407
|
+
return (
|
|
1408
|
+
None,
|
|
1409
|
+
Error(
|
|
1410
|
+
contract.node,
|
|
1411
|
+
f"The argument of the postcondition is not provided "
|
|
1412
|
+
f"in the method: {arg}",
|
|
1413
|
+
),
|
|
1414
|
+
)
|
|
1415
|
+
else:
|
|
1416
|
+
# Everything is OK.
|
|
1417
|
+
pass
|
|
1418
|
+
|
|
1419
|
+
for snapshot in snapshots:
|
|
1420
|
+
for arg in snapshot.args:
|
|
1421
|
+
if arg not in function_arg_set:
|
|
1422
|
+
return (
|
|
1423
|
+
None,
|
|
1424
|
+
Error(
|
|
1425
|
+
snapshot.node,
|
|
1426
|
+
f"The argument of the snapshot is not provided "
|
|
1427
|
+
f"in the method: {arg}",
|
|
1428
|
+
),
|
|
1429
|
+
)
|
|
1430
|
+
|
|
1431
|
+
# endregion
|
|
1432
|
+
|
|
1433
|
+
# region Check __init__ constraints
|
|
1434
|
+
if name == "__init__":
|
|
1435
|
+
# Must return None
|
|
1436
|
+
if returns is not None:
|
|
1437
|
+
return (
|
|
1438
|
+
None,
|
|
1439
|
+
Error(
|
|
1440
|
+
node,
|
|
1441
|
+
f"Expected __init__ to return None, "
|
|
1442
|
+
f"but got: {atok.get_text(node.returns)}",
|
|
1443
|
+
),
|
|
1444
|
+
)
|
|
1445
|
+
|
|
1446
|
+
# Must not be a verification
|
|
1447
|
+
if verification:
|
|
1448
|
+
return (
|
|
1449
|
+
None,
|
|
1450
|
+
Error(
|
|
1451
|
+
node,
|
|
1452
|
+
"Expected __init__ not to be a verification function",
|
|
1453
|
+
),
|
|
1454
|
+
)
|
|
1455
|
+
|
|
1456
|
+
# endregion
|
|
1457
|
+
|
|
1458
|
+
# region Check that the parsed method conforms to ``expect_self``
|
|
1459
|
+
|
|
1460
|
+
if expect_self and len(arguments) == 0:
|
|
1461
|
+
return (
|
|
1462
|
+
None,
|
|
1463
|
+
Error(
|
|
1464
|
+
node,
|
|
1465
|
+
f"A ``self`` argument is expected, but no arguments were specified "
|
|
1466
|
+
f"in the method {name!r}",
|
|
1467
|
+
),
|
|
1468
|
+
)
|
|
1469
|
+
|
|
1470
|
+
if expect_self and len(arguments) >= 1:
|
|
1471
|
+
if arguments[0].name != "self":
|
|
1472
|
+
return (
|
|
1473
|
+
None,
|
|
1474
|
+
Error(
|
|
1475
|
+
node,
|
|
1476
|
+
f"Expected the first argument to be ``self`` "
|
|
1477
|
+
f"in the method {name!r}, but got {arguments[0].name!r}",
|
|
1478
|
+
),
|
|
1479
|
+
)
|
|
1480
|
+
|
|
1481
|
+
if not isinstance(arguments[0].type_annotation, SelfTypeAnnotation):
|
|
1482
|
+
return (
|
|
1483
|
+
None,
|
|
1484
|
+
Error(
|
|
1485
|
+
node,
|
|
1486
|
+
f"Expected the ``self`` argument to have no annotation "
|
|
1487
|
+
f"in the method {name!r}, but got {arguments[0].type_annotation!r}",
|
|
1488
|
+
),
|
|
1489
|
+
)
|
|
1490
|
+
|
|
1491
|
+
# endregion
|
|
1492
|
+
|
|
1493
|
+
if is_implementation_specific:
|
|
1494
|
+
return (
|
|
1495
|
+
ImplementationSpecificMethod(
|
|
1496
|
+
name=Identifier(name),
|
|
1497
|
+
verification=verification,
|
|
1498
|
+
arguments=arguments,
|
|
1499
|
+
returns=returns,
|
|
1500
|
+
description=description,
|
|
1501
|
+
contracts=Contracts(
|
|
1502
|
+
preconditions=preconditions,
|
|
1503
|
+
snapshots=snapshots,
|
|
1504
|
+
postconditions=postconditions,
|
|
1505
|
+
),
|
|
1506
|
+
non_mutating=non_mutating,
|
|
1507
|
+
node=node,
|
|
1508
|
+
),
|
|
1509
|
+
None,
|
|
1510
|
+
)
|
|
1511
|
+
else:
|
|
1512
|
+
if name == "__init__":
|
|
1513
|
+
assert not verification
|
|
1514
|
+
assert returns is None
|
|
1515
|
+
|
|
1516
|
+
if non_mutating:
|
|
1517
|
+
return (
|
|
1518
|
+
None,
|
|
1519
|
+
Error(
|
|
1520
|
+
node,
|
|
1521
|
+
"Unexpected non-mutating constructor",
|
|
1522
|
+
),
|
|
1523
|
+
)
|
|
1524
|
+
|
|
1525
|
+
return (
|
|
1526
|
+
ConstructorToBeUnderstood(
|
|
1527
|
+
arguments=arguments,
|
|
1528
|
+
description=description,
|
|
1529
|
+
contracts=Contracts(
|
|
1530
|
+
preconditions=preconditions,
|
|
1531
|
+
snapshots=snapshots,
|
|
1532
|
+
postconditions=postconditions,
|
|
1533
|
+
),
|
|
1534
|
+
body=body,
|
|
1535
|
+
node=node,
|
|
1536
|
+
),
|
|
1537
|
+
None,
|
|
1538
|
+
)
|
|
1539
|
+
else:
|
|
1540
|
+
understanding_errors = [] # type: List[Error]
|
|
1541
|
+
|
|
1542
|
+
understood_body = [] # type: List[tree.Node]
|
|
1543
|
+
|
|
1544
|
+
for body_child in body:
|
|
1545
|
+
# NOTE (mristin, 2021-12-27):
|
|
1546
|
+
# We deliberately ignore ``pass`` as it makes no sense in our
|
|
1547
|
+
# context of multiple programming languages.
|
|
1548
|
+
if isinstance(body_child, ast.Pass):
|
|
1549
|
+
continue
|
|
1550
|
+
|
|
1551
|
+
understood_node, understanding_error = _rules.ast_node_to_our_node(
|
|
1552
|
+
body_child
|
|
1553
|
+
)
|
|
1554
|
+
|
|
1555
|
+
if understanding_error is not None:
|
|
1556
|
+
understanding_errors.append(understanding_error)
|
|
1557
|
+
continue
|
|
1558
|
+
|
|
1559
|
+
assert understood_node is not None
|
|
1560
|
+
understood_body.append(understood_node)
|
|
1561
|
+
|
|
1562
|
+
if len(understanding_errors) > 0:
|
|
1563
|
+
return None, Error(
|
|
1564
|
+
node,
|
|
1565
|
+
f"Failed to understand the body of the function {name!r}",
|
|
1566
|
+
understanding_errors,
|
|
1567
|
+
)
|
|
1568
|
+
|
|
1569
|
+
return (
|
|
1570
|
+
UnderstoodMethod(
|
|
1571
|
+
name=Identifier(name),
|
|
1572
|
+
verification=verification,
|
|
1573
|
+
arguments=arguments,
|
|
1574
|
+
returns=returns,
|
|
1575
|
+
description=description,
|
|
1576
|
+
contracts=Contracts(
|
|
1577
|
+
preconditions=preconditions,
|
|
1578
|
+
snapshots=snapshots,
|
|
1579
|
+
postconditions=postconditions,
|
|
1580
|
+
),
|
|
1581
|
+
non_mutating=non_mutating,
|
|
1582
|
+
body=understood_body,
|
|
1583
|
+
node=node,
|
|
1584
|
+
),
|
|
1585
|
+
None,
|
|
1586
|
+
)
|
|
1587
|
+
|
|
1588
|
+
|
|
1589
|
+
class _ClassMarker(enum.Enum):
|
|
1590
|
+
ABSTRACT = "abstract"
|
|
1591
|
+
IMPLEMENTATION_SPECIFIC = "implementation_specific"
|
|
1592
|
+
TEMPLATE = "template"
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
_CLASS_MARKER_FROM_STRING: Mapping[str, _ClassMarker] = {
|
|
1596
|
+
marker.value: marker for marker in _ClassMarker
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
|
|
1600
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
1601
|
+
def _class_decorator_to_marker(
|
|
1602
|
+
decorator: ast.Name,
|
|
1603
|
+
) -> Tuple[Optional[_ClassMarker], Optional[Error]]:
|
|
1604
|
+
"""Parse a simple decorator as a class marker."""
|
|
1605
|
+
class_marker = _CLASS_MARKER_FROM_STRING.get(decorator.id, None)
|
|
1606
|
+
|
|
1607
|
+
if class_marker is None:
|
|
1608
|
+
return (
|
|
1609
|
+
None,
|
|
1610
|
+
Error(
|
|
1611
|
+
decorator,
|
|
1612
|
+
f"The handling of the marker has not been "
|
|
1613
|
+
f"implemented: {decorator.id!r}",
|
|
1614
|
+
),
|
|
1615
|
+
)
|
|
1616
|
+
|
|
1617
|
+
return class_marker, None
|
|
1618
|
+
|
|
1619
|
+
|
|
1620
|
+
# fmt: off
|
|
1621
|
+
# noinspection PyTypeChecker
|
|
1622
|
+
@require(
|
|
1623
|
+
lambda decorator:
|
|
1624
|
+
isinstance(decorator.func, ast.Name)
|
|
1625
|
+
and isinstance(decorator.func.ctx, ast.Load)
|
|
1626
|
+
and decorator.func.id == 'serialization'
|
|
1627
|
+
)
|
|
1628
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
1629
|
+
# fmt: on
|
|
1630
|
+
def _class_decorator_to_serialization(
|
|
1631
|
+
decorator: ast.Call,
|
|
1632
|
+
) -> Tuple[Optional[Serialization], Optional[Error]]:
|
|
1633
|
+
"""Translate a decorator to general serialization settings."""
|
|
1634
|
+
with_model_type_node = None # type: Optional[ast.AST]
|
|
1635
|
+
|
|
1636
|
+
if len(decorator.args) >= 1:
|
|
1637
|
+
with_model_type_node = decorator.args[0]
|
|
1638
|
+
|
|
1639
|
+
if len(decorator.keywords) > 0:
|
|
1640
|
+
for kwarg in decorator.keywords:
|
|
1641
|
+
if kwarg.arg == "with_model_type":
|
|
1642
|
+
with_model_type_node = kwarg.value
|
|
1643
|
+
else:
|
|
1644
|
+
return (
|
|
1645
|
+
None,
|
|
1646
|
+
Error(
|
|
1647
|
+
decorator,
|
|
1648
|
+
f"Handling of the keyword argument {kwarg.arg!r} "
|
|
1649
|
+
f"for the serialization decorator has not been implemented",
|
|
1650
|
+
),
|
|
1651
|
+
)
|
|
1652
|
+
|
|
1653
|
+
with_model_type = None # type: Optional[bool]
|
|
1654
|
+
if with_model_type_node is not None:
|
|
1655
|
+
if not isinstance(with_model_type_node, ast.Constant):
|
|
1656
|
+
return (
|
|
1657
|
+
None,
|
|
1658
|
+
Error(
|
|
1659
|
+
with_model_type_node,
|
|
1660
|
+
f"Expected the value for ``with_model_type`` parameter "
|
|
1661
|
+
f"to be a constant, but got: {ast.dump(with_model_type_node)}",
|
|
1662
|
+
),
|
|
1663
|
+
)
|
|
1664
|
+
|
|
1665
|
+
if not isinstance(with_model_type_node.value, bool):
|
|
1666
|
+
return (
|
|
1667
|
+
None,
|
|
1668
|
+
Error(
|
|
1669
|
+
with_model_type_node,
|
|
1670
|
+
f"Expected the value for ``with_model_type`` parameter "
|
|
1671
|
+
f"to be a boolean, but got: {with_model_type_node.value}",
|
|
1672
|
+
),
|
|
1673
|
+
)
|
|
1674
|
+
|
|
1675
|
+
with_model_type = with_model_type_node.value
|
|
1676
|
+
|
|
1677
|
+
return Serialization(with_model_type=with_model_type), None
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
# fmt: off
|
|
1681
|
+
# noinspection PyTypeChecker
|
|
1682
|
+
@require(
|
|
1683
|
+
lambda decorator:
|
|
1684
|
+
isinstance(decorator.func, ast.Name)
|
|
1685
|
+
and isinstance(decorator.func.ctx, ast.Load)
|
|
1686
|
+
and decorator.func.id == 'invariant'
|
|
1687
|
+
)
|
|
1688
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
1689
|
+
# fmt: on
|
|
1690
|
+
def _class_decorator_to_invariant(
|
|
1691
|
+
decorator: ast.Call, atok: asttokens.ASTTokens
|
|
1692
|
+
) -> Tuple[Optional[Invariant], Optional[Error]]:
|
|
1693
|
+
"""Parse the decorator node as a class invariant."""
|
|
1694
|
+
condition_node = None # type: Optional[ast.AST]
|
|
1695
|
+
description_node = None # type: Optional[ast.AST]
|
|
1696
|
+
|
|
1697
|
+
if len(decorator.args) >= 1:
|
|
1698
|
+
condition_node = decorator.args[0]
|
|
1699
|
+
|
|
1700
|
+
if len(decorator.args) >= 2:
|
|
1701
|
+
description_node = decorator.args[1]
|
|
1702
|
+
|
|
1703
|
+
if len(decorator.keywords) > 0:
|
|
1704
|
+
for kwarg in decorator.keywords:
|
|
1705
|
+
if kwarg.arg == "condition":
|
|
1706
|
+
condition_node = kwarg.value
|
|
1707
|
+
elif kwarg.arg == "description":
|
|
1708
|
+
description_node = kwarg.value
|
|
1709
|
+
else:
|
|
1710
|
+
return (
|
|
1711
|
+
None,
|
|
1712
|
+
Error(
|
|
1713
|
+
decorator,
|
|
1714
|
+
f"Handling of the keyword argument {kwarg.arg!r} "
|
|
1715
|
+
f"for the invariant has not been implemented",
|
|
1716
|
+
),
|
|
1717
|
+
)
|
|
1718
|
+
|
|
1719
|
+
if not isinstance(condition_node, ast.Lambda):
|
|
1720
|
+
return (
|
|
1721
|
+
None,
|
|
1722
|
+
Error(
|
|
1723
|
+
decorator,
|
|
1724
|
+
f"Expected the condition of an invariant to be a lambda, "
|
|
1725
|
+
f"but got {type(condition_node)}: {atok.get_text(decorator)}",
|
|
1726
|
+
),
|
|
1727
|
+
)
|
|
1728
|
+
|
|
1729
|
+
if description_node is None:
|
|
1730
|
+
return (
|
|
1731
|
+
None,
|
|
1732
|
+
Error(decorator, "The invariant must have a human-readable description"),
|
|
1733
|
+
)
|
|
1734
|
+
|
|
1735
|
+
if not isinstance(description_node, ast.Constant) or not isinstance(
|
|
1736
|
+
description_node.value, str
|
|
1737
|
+
):
|
|
1738
|
+
return (
|
|
1739
|
+
None,
|
|
1740
|
+
Error(
|
|
1741
|
+
decorator,
|
|
1742
|
+
f"Expected the description of an invariant to be "
|
|
1743
|
+
f"a string literal, but got: {type(description_node)}",
|
|
1744
|
+
),
|
|
1745
|
+
)
|
|
1746
|
+
|
|
1747
|
+
if len(condition_node.args.args) != 1 or condition_node.args.args[0].arg != "self":
|
|
1748
|
+
return (
|
|
1749
|
+
None,
|
|
1750
|
+
Error(
|
|
1751
|
+
decorator, "Expected the invariant to have a single argument, ``self``"
|
|
1752
|
+
),
|
|
1753
|
+
)
|
|
1754
|
+
|
|
1755
|
+
body, error = _rules.ast_node_to_our_node(node=condition_node.body)
|
|
1756
|
+
if error is not None:
|
|
1757
|
+
return None, Error(
|
|
1758
|
+
condition_node.body, "Failed to parse the invariant", [error]
|
|
1759
|
+
)
|
|
1760
|
+
|
|
1761
|
+
assert body is not None
|
|
1762
|
+
|
|
1763
|
+
if not isinstance(body, tree.Expression):
|
|
1764
|
+
return None, Error(
|
|
1765
|
+
condition_node.body,
|
|
1766
|
+
f"Expected an expression in an invariant, but got: {tree.dump(body)}",
|
|
1767
|
+
)
|
|
1768
|
+
|
|
1769
|
+
return (
|
|
1770
|
+
Invariant(
|
|
1771
|
+
description=description_node.value,
|
|
1772
|
+
body=body,
|
|
1773
|
+
node=decorator,
|
|
1774
|
+
),
|
|
1775
|
+
None,
|
|
1776
|
+
)
|
|
1777
|
+
|
|
1778
|
+
|
|
1779
|
+
_ClassDecoratorUnion = Union[
|
|
1780
|
+
_ClassMarker,
|
|
1781
|
+
Serialization,
|
|
1782
|
+
Invariant,
|
|
1783
|
+
]
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
# noinspection PyTypeChecker
|
|
1787
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
1788
|
+
def _parse_class_decorator(
|
|
1789
|
+
decorator: ast.AST, atok: asttokens.ASTTokens
|
|
1790
|
+
) -> Tuple[Optional[_ClassDecoratorUnion], Optional[Error]]:
|
|
1791
|
+
"""
|
|
1792
|
+
Parse a class decorator.
|
|
1793
|
+
|
|
1794
|
+
The decorator needs to be further interpreted in the context of the class.
|
|
1795
|
+
The class here refers to a general Python class, not the concrete or abstract
|
|
1796
|
+
class of the meta-model. For example, an enumeration is also defined as a Python
|
|
1797
|
+
class inheriting from ``Enum``.
|
|
1798
|
+
"""
|
|
1799
|
+
if isinstance(decorator, ast.Name):
|
|
1800
|
+
return _class_decorator_to_marker(decorator=decorator)
|
|
1801
|
+
elif isinstance(decorator, ast.Call):
|
|
1802
|
+
if not isinstance(decorator.func, ast.Name):
|
|
1803
|
+
return None, Error(
|
|
1804
|
+
decorator,
|
|
1805
|
+
f"Expected a name for a decorator function, "
|
|
1806
|
+
f"but got: {ast.dump(decorator.func)}",
|
|
1807
|
+
)
|
|
1808
|
+
|
|
1809
|
+
if not isinstance(decorator.func.ctx, ast.Load):
|
|
1810
|
+
return None, Error(
|
|
1811
|
+
decorator,
|
|
1812
|
+
f"Unexpected decorator function in "
|
|
1813
|
+
f"a non-Load context: {decorator.func.ctx=}",
|
|
1814
|
+
)
|
|
1815
|
+
|
|
1816
|
+
if decorator.func.id == "serialization":
|
|
1817
|
+
return _class_decorator_to_serialization(decorator=decorator)
|
|
1818
|
+
elif decorator.func.id == "invariant":
|
|
1819
|
+
return _class_decorator_to_invariant(decorator=decorator, atok=atok)
|
|
1820
|
+
else:
|
|
1821
|
+
return None, Error(
|
|
1822
|
+
decorator,
|
|
1823
|
+
f"We do not know how to handle "
|
|
1824
|
+
f"the class decorator: {decorator.func.id}.",
|
|
1825
|
+
)
|
|
1826
|
+
else:
|
|
1827
|
+
return None, Error(
|
|
1828
|
+
decorator,
|
|
1829
|
+
f"Handling of a non-name or a non-call class decorator "
|
|
1830
|
+
f"has not been implemented: {ast.dump(decorator)}",
|
|
1831
|
+
)
|
|
1832
|
+
|
|
1833
|
+
|
|
1834
|
+
# noinspection PyTypeChecker,PyUnresolvedReferences
|
|
1835
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
1836
|
+
def _classdef_to_enumeration(
|
|
1837
|
+
node: ast.ClassDef, atok: asttokens.ASTTokens
|
|
1838
|
+
) -> Tuple[Optional[Enumeration], Optional[Error]]:
|
|
1839
|
+
"""Interpret a class which defines an enumeration."""
|
|
1840
|
+
for decorator_node in node.decorator_list:
|
|
1841
|
+
decorator, error = _parse_class_decorator(decorator=decorator_node, atok=atok)
|
|
1842
|
+
if error is not None:
|
|
1843
|
+
return None, error
|
|
1844
|
+
assert decorator is not None
|
|
1845
|
+
|
|
1846
|
+
return None, Error(
|
|
1847
|
+
node,
|
|
1848
|
+
f"Unexpected class decorator {decorator} "
|
|
1849
|
+
f"for the enumeration {node.name!r}",
|
|
1850
|
+
)
|
|
1851
|
+
|
|
1852
|
+
if len(node.body) == 0:
|
|
1853
|
+
return (
|
|
1854
|
+
Enumeration(
|
|
1855
|
+
name=Identifier(node.name),
|
|
1856
|
+
literals=[],
|
|
1857
|
+
description=None,
|
|
1858
|
+
node=node,
|
|
1859
|
+
),
|
|
1860
|
+
None,
|
|
1861
|
+
)
|
|
1862
|
+
|
|
1863
|
+
enumeration_literals = [] # type: List[EnumerationLiteral]
|
|
1864
|
+
|
|
1865
|
+
description = None # type: Optional[Description]
|
|
1866
|
+
|
|
1867
|
+
cursor = 0
|
|
1868
|
+
while cursor < len(node.body):
|
|
1869
|
+
old_cursor = cursor
|
|
1870
|
+
|
|
1871
|
+
body_node = node.body[cursor] # type: ast.AST
|
|
1872
|
+
|
|
1873
|
+
if cursor == 0 and is_string_expr(body_node):
|
|
1874
|
+
assert isinstance(body_node, ast.Expr)
|
|
1875
|
+
assert isinstance(body_node.value, ast.Constant)
|
|
1876
|
+
description, error = _ast_constant_string_to_description(body_node.value)
|
|
1877
|
+
if error is not None:
|
|
1878
|
+
return None, error
|
|
1879
|
+
|
|
1880
|
+
cursor += 1
|
|
1881
|
+
|
|
1882
|
+
elif isinstance(body_node, ast.Pass):
|
|
1883
|
+
cursor += 1
|
|
1884
|
+
|
|
1885
|
+
elif isinstance(body_node, ast.Assign):
|
|
1886
|
+
assign = body_node
|
|
1887
|
+
|
|
1888
|
+
if len(assign.targets) != 1:
|
|
1889
|
+
return (
|
|
1890
|
+
None,
|
|
1891
|
+
Error(
|
|
1892
|
+
assign,
|
|
1893
|
+
f"Expected a single target in the assignment, "
|
|
1894
|
+
f"but got: {len(assign.targets)}",
|
|
1895
|
+
),
|
|
1896
|
+
)
|
|
1897
|
+
|
|
1898
|
+
if not isinstance(assign.targets[0], ast.Name):
|
|
1899
|
+
return (
|
|
1900
|
+
None,
|
|
1901
|
+
Error(
|
|
1902
|
+
assign.targets[0],
|
|
1903
|
+
f"Expected a name as a target of the assignment, "
|
|
1904
|
+
f"but got: {assign.targets[0]}",
|
|
1905
|
+
),
|
|
1906
|
+
)
|
|
1907
|
+
|
|
1908
|
+
if not isinstance(assign.value, ast.Constant):
|
|
1909
|
+
return (
|
|
1910
|
+
None,
|
|
1911
|
+
Error(
|
|
1912
|
+
assign.value,
|
|
1913
|
+
f"Expected a constant in the enumeration assignment, "
|
|
1914
|
+
f"but got: {atok.get_text(assign.value)}",
|
|
1915
|
+
),
|
|
1916
|
+
)
|
|
1917
|
+
|
|
1918
|
+
if not isinstance(assign.value.value, str):
|
|
1919
|
+
return (
|
|
1920
|
+
None,
|
|
1921
|
+
Error(
|
|
1922
|
+
assign.value,
|
|
1923
|
+
f"Expected a string literal in the enumeration, "
|
|
1924
|
+
f"but got: {assign.value.value}",
|
|
1925
|
+
),
|
|
1926
|
+
)
|
|
1927
|
+
|
|
1928
|
+
literal_name = Identifier(assign.targets[0].id)
|
|
1929
|
+
literal_value = assign.value.value
|
|
1930
|
+
|
|
1931
|
+
literal_description = None # type: Optional[Description]
|
|
1932
|
+
next_expr = node.body[cursor + 1] if cursor < len(node.body) - 1 else None
|
|
1933
|
+
|
|
1934
|
+
if next_expr is not None and is_string_expr(next_expr):
|
|
1935
|
+
assert isinstance(next_expr, ast.Expr)
|
|
1936
|
+
assert isinstance(next_expr.value, ast.Constant)
|
|
1937
|
+
literal_description, error = _ast_constant_string_to_description(
|
|
1938
|
+
next_expr.value
|
|
1939
|
+
)
|
|
1940
|
+
|
|
1941
|
+
if error is not None:
|
|
1942
|
+
return None, error
|
|
1943
|
+
|
|
1944
|
+
cursor += 1
|
|
1945
|
+
|
|
1946
|
+
enumeration_literals.append(
|
|
1947
|
+
EnumerationLiteral(
|
|
1948
|
+
name=literal_name,
|
|
1949
|
+
value=literal_value,
|
|
1950
|
+
description=literal_description,
|
|
1951
|
+
node=assign,
|
|
1952
|
+
)
|
|
1953
|
+
)
|
|
1954
|
+
|
|
1955
|
+
cursor += 1
|
|
1956
|
+
|
|
1957
|
+
else:
|
|
1958
|
+
|
|
1959
|
+
return (
|
|
1960
|
+
None,
|
|
1961
|
+
Error(
|
|
1962
|
+
node.body[cursor],
|
|
1963
|
+
f"Expected either a docstring at the beginning or an assignment "
|
|
1964
|
+
f"in an enumeration, "
|
|
1965
|
+
f"but got an unexpected body element at index {cursor} "
|
|
1966
|
+
f"of the class definition {node.name!r}: "
|
|
1967
|
+
f"{atok.get_text(node.body[cursor])}",
|
|
1968
|
+
),
|
|
1969
|
+
)
|
|
1970
|
+
|
|
1971
|
+
assert cursor > old_cursor, f"Loop invariant: {cursor=}, {old_cursor=}"
|
|
1972
|
+
|
|
1973
|
+
return (
|
|
1974
|
+
Enumeration(
|
|
1975
|
+
name=Identifier(node.name),
|
|
1976
|
+
literals=enumeration_literals,
|
|
1977
|
+
description=description,
|
|
1978
|
+
node=node,
|
|
1979
|
+
),
|
|
1980
|
+
None,
|
|
1981
|
+
)
|
|
1982
|
+
|
|
1983
|
+
|
|
1984
|
+
# noinspection PyTypeChecker
|
|
1985
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
1986
|
+
def _classdef_to_our_type(
|
|
1987
|
+
node: ast.ClassDef, atok: asttokens.ASTTokens
|
|
1988
|
+
) -> Tuple[Optional[OurType], Optional[Error]]:
|
|
1989
|
+
"""Interpret the class definition as our type."""
|
|
1990
|
+
underlying_errors = [] # type: List[Error]
|
|
1991
|
+
|
|
1992
|
+
base_names = [] # type: List[str]
|
|
1993
|
+
for base in node.bases:
|
|
1994
|
+
if not isinstance(base, ast.Name):
|
|
1995
|
+
underlying_errors.append(
|
|
1996
|
+
Error(
|
|
1997
|
+
base, f"Expected a base as a name, but got: {atok.get_text(base)}"
|
|
1998
|
+
)
|
|
1999
|
+
)
|
|
2000
|
+
else:
|
|
2001
|
+
base_names.append(base.id)
|
|
2002
|
+
|
|
2003
|
+
if len(underlying_errors) > 0:
|
|
2004
|
+
return (
|
|
2005
|
+
None,
|
|
2006
|
+
Error(
|
|
2007
|
+
node,
|
|
2008
|
+
f"Failed to parse the class definition: {node.name}",
|
|
2009
|
+
underlying=underlying_errors,
|
|
2010
|
+
),
|
|
2011
|
+
)
|
|
2012
|
+
|
|
2013
|
+
if "Enum" in base_names and len(base_names) > 1:
|
|
2014
|
+
return (
|
|
2015
|
+
None,
|
|
2016
|
+
Error(
|
|
2017
|
+
node,
|
|
2018
|
+
f"Expected an enumeration to only inherit from ``Enum``, "
|
|
2019
|
+
f"but it inherits from: {base_names}",
|
|
2020
|
+
),
|
|
2021
|
+
)
|
|
2022
|
+
|
|
2023
|
+
if "Enum" in base_names:
|
|
2024
|
+
return _classdef_to_enumeration(node=node, atok=atok)
|
|
2025
|
+
|
|
2026
|
+
# We have to parse the class definition from here on.
|
|
2027
|
+
|
|
2028
|
+
# DBC is only used for inheritance of the contracts in the meta-model
|
|
2029
|
+
# so that the developers tinkering with the meta-model can play with it
|
|
2030
|
+
# at runtime. We can safely ignore it as we are not looking into any
|
|
2031
|
+
# runtime code.
|
|
2032
|
+
inheritances = [
|
|
2033
|
+
Identifier(base_name) for base_name in base_names if base_name != "DBC"
|
|
2034
|
+
]
|
|
2035
|
+
|
|
2036
|
+
# region Decorators
|
|
2037
|
+
|
|
2038
|
+
invariants = [] # type: List[Invariant]
|
|
2039
|
+
|
|
2040
|
+
is_abstract = False
|
|
2041
|
+
is_implementation_specific = False
|
|
2042
|
+
|
|
2043
|
+
serialization = None # type: Optional[Serialization]
|
|
2044
|
+
|
|
2045
|
+
for decorator_node in node.decorator_list:
|
|
2046
|
+
decorator, error = _parse_class_decorator(decorator=decorator_node, atok=atok)
|
|
2047
|
+
if error is not None:
|
|
2048
|
+
underlying_errors.append(error)
|
|
2049
|
+
continue
|
|
2050
|
+
|
|
2051
|
+
assert decorator is not None
|
|
2052
|
+
if isinstance(decorator, _ClassMarker):
|
|
2053
|
+
if decorator is _ClassMarker.ABSTRACT:
|
|
2054
|
+
is_abstract = True
|
|
2055
|
+
elif decorator is _ClassMarker.IMPLEMENTATION_SPECIFIC:
|
|
2056
|
+
is_implementation_specific = True
|
|
2057
|
+
elif decorator is _ClassMarker.TEMPLATE:
|
|
2058
|
+
# NOTE (mristin, 2021-11-28):
|
|
2059
|
+
# We ignore the template marker at this moment. However, we will most
|
|
2060
|
+
# probably have to consider them in the future, so we leave them in the
|
|
2061
|
+
# meta-model, but ignore them in the code generation.
|
|
2062
|
+
pass
|
|
2063
|
+
|
|
2064
|
+
else:
|
|
2065
|
+
assert_never(decorator)
|
|
2066
|
+
|
|
2067
|
+
elif isinstance(decorator, Serialization):
|
|
2068
|
+
if serialization is not None:
|
|
2069
|
+
underlying_errors.append(
|
|
2070
|
+
Error(decorator_node, "Unexpected double serialization markers")
|
|
2071
|
+
)
|
|
2072
|
+
continue
|
|
2073
|
+
|
|
2074
|
+
serialization = decorator
|
|
2075
|
+
|
|
2076
|
+
elif isinstance(decorator, Invariant):
|
|
2077
|
+
invariants.append(decorator)
|
|
2078
|
+
|
|
2079
|
+
else:
|
|
2080
|
+
assert_never(decorator)
|
|
2081
|
+
|
|
2082
|
+
if len(underlying_errors) > 0:
|
|
2083
|
+
return (
|
|
2084
|
+
None,
|
|
2085
|
+
Error(
|
|
2086
|
+
node,
|
|
2087
|
+
f"Failed to parse the class definition: {node.name}",
|
|
2088
|
+
underlying=underlying_errors,
|
|
2089
|
+
),
|
|
2090
|
+
)
|
|
2091
|
+
|
|
2092
|
+
# NOTE (mristin, 20222-01-02):
|
|
2093
|
+
# We need to inverse the invariants as we collect them top-down, while
|
|
2094
|
+
# the decorators are applied bottom-up.
|
|
2095
|
+
invariants = list(reversed(invariants))
|
|
2096
|
+
|
|
2097
|
+
# endregion
|
|
2098
|
+
|
|
2099
|
+
if is_abstract and is_implementation_specific:
|
|
2100
|
+
return (
|
|
2101
|
+
None,
|
|
2102
|
+
Error(
|
|
2103
|
+
node,
|
|
2104
|
+
"Abstract classes can not be implementation-specific "
|
|
2105
|
+
"at the same time "
|
|
2106
|
+
"(otherwise we can not convert them to interfaces etc.)",
|
|
2107
|
+
),
|
|
2108
|
+
)
|
|
2109
|
+
|
|
2110
|
+
description = None # type: Optional[Description]
|
|
2111
|
+
|
|
2112
|
+
properties = [] # type: List[Property]
|
|
2113
|
+
methods = [] # type: List[MethodUnion]
|
|
2114
|
+
|
|
2115
|
+
cursor = 0
|
|
2116
|
+
while cursor < len(node.body):
|
|
2117
|
+
old_cursor = cursor
|
|
2118
|
+
|
|
2119
|
+
expr = node.body[cursor]
|
|
2120
|
+
|
|
2121
|
+
if cursor == 0 and is_string_expr(expr):
|
|
2122
|
+
assert isinstance(expr, ast.Expr)
|
|
2123
|
+
assert isinstance(expr.value, ast.Constant)
|
|
2124
|
+
description, error = _ast_constant_string_to_description(expr.value)
|
|
2125
|
+
if error is not None:
|
|
2126
|
+
return None, error
|
|
2127
|
+
|
|
2128
|
+
assert description is not None
|
|
2129
|
+
|
|
2130
|
+
cursor += 1
|
|
2131
|
+
continue
|
|
2132
|
+
|
|
2133
|
+
if isinstance(expr, ast.Pass):
|
|
2134
|
+
cursor += 1
|
|
2135
|
+
continue
|
|
2136
|
+
|
|
2137
|
+
if isinstance(expr, ast.AnnAssign):
|
|
2138
|
+
description_of_property = None # type: Optional[Description]
|
|
2139
|
+
|
|
2140
|
+
next_expr = node.body[cursor + 1] if cursor < len(node.body) - 1 else None
|
|
2141
|
+
if next_expr is not None and is_string_expr(next_expr):
|
|
2142
|
+
assert isinstance(next_expr, ast.Expr)
|
|
2143
|
+
assert isinstance(next_expr.value, ast.Constant)
|
|
2144
|
+
description_of_property, error = _ast_constant_string_to_description(
|
|
2145
|
+
next_expr.value
|
|
2146
|
+
)
|
|
2147
|
+
|
|
2148
|
+
if error is not None:
|
|
2149
|
+
return None, error
|
|
2150
|
+
|
|
2151
|
+
assert description_of_property is not None
|
|
2152
|
+
|
|
2153
|
+
cursor += 1
|
|
2154
|
+
|
|
2155
|
+
prop, error = _ann_assign_to_property(
|
|
2156
|
+
node=expr, description=description_of_property, atok=atok
|
|
2157
|
+
)
|
|
2158
|
+
cursor += 1
|
|
2159
|
+
|
|
2160
|
+
if error is not None:
|
|
2161
|
+
return (
|
|
2162
|
+
None,
|
|
2163
|
+
Error(expr, "Failed to parse a property", underlying=[error]),
|
|
2164
|
+
)
|
|
2165
|
+
|
|
2166
|
+
assert prop is not None
|
|
2167
|
+
|
|
2168
|
+
properties.append(prop)
|
|
2169
|
+
|
|
2170
|
+
elif isinstance(expr, ast.FunctionDef):
|
|
2171
|
+
method, error = _function_def_to_method(
|
|
2172
|
+
node=expr, expect_self=True, atok=atok
|
|
2173
|
+
)
|
|
2174
|
+
|
|
2175
|
+
if error is not None:
|
|
2176
|
+
return (
|
|
2177
|
+
None,
|
|
2178
|
+
Error(
|
|
2179
|
+
expr,
|
|
2180
|
+
f"Failed to parse the method: {expr.name}",
|
|
2181
|
+
underlying=[error],
|
|
2182
|
+
),
|
|
2183
|
+
)
|
|
2184
|
+
|
|
2185
|
+
assert method is not None
|
|
2186
|
+
methods.append(method)
|
|
2187
|
+
|
|
2188
|
+
cursor += 1
|
|
2189
|
+
|
|
2190
|
+
else:
|
|
2191
|
+
return (
|
|
2192
|
+
None,
|
|
2193
|
+
Error(
|
|
2194
|
+
expr,
|
|
2195
|
+
f"Expected only either "
|
|
2196
|
+
f"properties explicitly annotated with types or "
|
|
2197
|
+
f"instance methods, but got: {atok.get_text(expr)}",
|
|
2198
|
+
),
|
|
2199
|
+
)
|
|
2200
|
+
|
|
2201
|
+
assert old_cursor < cursor, f"Loop invariant: {old_cursor=}, {cursor=}"
|
|
2202
|
+
|
|
2203
|
+
if is_abstract:
|
|
2204
|
+
factory_for_class = (
|
|
2205
|
+
AbstractClass
|
|
2206
|
+
) # type: Union[Type[AbstractClass], Type[ConcreteClass]]
|
|
2207
|
+
else:
|
|
2208
|
+
factory_for_class = ConcreteClass
|
|
2209
|
+
|
|
2210
|
+
return (
|
|
2211
|
+
factory_for_class(
|
|
2212
|
+
name=Identifier(node.name),
|
|
2213
|
+
is_implementation_specific=is_implementation_specific,
|
|
2214
|
+
inheritances=inheritances,
|
|
2215
|
+
properties=properties,
|
|
2216
|
+
methods=methods,
|
|
2217
|
+
invariants=invariants,
|
|
2218
|
+
serialization=serialization,
|
|
2219
|
+
description=description,
|
|
2220
|
+
node=node,
|
|
2221
|
+
),
|
|
2222
|
+
None,
|
|
2223
|
+
)
|
|
2224
|
+
|
|
2225
|
+
|
|
2226
|
+
def _verify_arity_of_type_annotation_subscript(
|
|
2227
|
+
type_annotation: SubscriptedTypeAnnotation,
|
|
2228
|
+
) -> Optional[Error]:
|
|
2229
|
+
"""
|
|
2230
|
+
Check that the subscripted type annotation has the expected number of arguments.
|
|
2231
|
+
|
|
2232
|
+
:return: error message, if any
|
|
2233
|
+
"""
|
|
2234
|
+
expected_arity_map = {"List": 1, "Optional": 1}
|
|
2235
|
+
expected_arity = expected_arity_map.get(type_annotation.identifier, None)
|
|
2236
|
+
if expected_arity is None:
|
|
2237
|
+
raise AssertionError(
|
|
2238
|
+
f"Unexpected subscripted type annotation: {type_annotation}"
|
|
2239
|
+
)
|
|
2240
|
+
|
|
2241
|
+
assert expected_arity >= 0
|
|
2242
|
+
if len(type_annotation.subscripts) != expected_arity:
|
|
2243
|
+
return Error(
|
|
2244
|
+
type_annotation.node,
|
|
2245
|
+
f"Expected {expected_arity} arguments of "
|
|
2246
|
+
f"a subscripted type annotation {type_annotation.identifier!r}, "
|
|
2247
|
+
f"but got {len(type_annotation.subscripts)}: {type_annotation}",
|
|
2248
|
+
)
|
|
2249
|
+
|
|
2250
|
+
return None
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
# fmt: off
|
|
2254
|
+
@ensure(
|
|
2255
|
+
lambda result:
|
|
2256
|
+
result[1] is None or len(result[1]) > 0,
|
|
2257
|
+
"If errors are not None, there must be at least one error"
|
|
2258
|
+
)
|
|
2259
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
2260
|
+
# fmt: on
|
|
2261
|
+
def _verify_symbol_table(
|
|
2262
|
+
symbol_table: UnverifiedSymbolTable,
|
|
2263
|
+
) -> Tuple[Optional[SymbolTable], Optional[List[Error]]]:
|
|
2264
|
+
"""
|
|
2265
|
+
Check that the symbol table is consistent.
|
|
2266
|
+
|
|
2267
|
+
For example, check that there are no dangling references in type annotations or
|
|
2268
|
+
inheritances.
|
|
2269
|
+
"""
|
|
2270
|
+
errors = [] # type: List[Error]
|
|
2271
|
+
|
|
2272
|
+
# region Check reserved names
|
|
2273
|
+
|
|
2274
|
+
builtin_types_in_many_implementations = {
|
|
2275
|
+
"str",
|
|
2276
|
+
"string",
|
|
2277
|
+
"int",
|
|
2278
|
+
"integer",
|
|
2279
|
+
"float",
|
|
2280
|
+
"real",
|
|
2281
|
+
"decimal",
|
|
2282
|
+
"number",
|
|
2283
|
+
"bool",
|
|
2284
|
+
"boolean",
|
|
2285
|
+
"bytes",
|
|
2286
|
+
"bytearray",
|
|
2287
|
+
"object",
|
|
2288
|
+
"read_only",
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
# noinspection SpellCheckingInspection
|
|
2292
|
+
keywords_in_many_implementations = (
|
|
2293
|
+
builtin_types_in_many_implementations.union(
|
|
2294
|
+
# Ada, see: https://en.wikibooks.org/wiki/Ada_Programming/Keywords
|
|
2295
|
+
#
|
|
2296
|
+
# We exclude ``type`` and ``range`` from the keywords as its usage in
|
|
2297
|
+
# the meta-model predates the generation of the Ada SDK, so it has been
|
|
2298
|
+
# missed, unfortunately.
|
|
2299
|
+
{
|
|
2300
|
+
"abort",
|
|
2301
|
+
"abs",
|
|
2302
|
+
"abstract",
|
|
2303
|
+
"accept",
|
|
2304
|
+
"access",
|
|
2305
|
+
"aliased",
|
|
2306
|
+
"all",
|
|
2307
|
+
"and",
|
|
2308
|
+
"array",
|
|
2309
|
+
"at",
|
|
2310
|
+
"begin",
|
|
2311
|
+
"body",
|
|
2312
|
+
"case",
|
|
2313
|
+
"constant",
|
|
2314
|
+
"declare",
|
|
2315
|
+
"delay",
|
|
2316
|
+
"delta",
|
|
2317
|
+
"digits",
|
|
2318
|
+
"do",
|
|
2319
|
+
"else",
|
|
2320
|
+
"elsif",
|
|
2321
|
+
"end",
|
|
2322
|
+
"entry",
|
|
2323
|
+
"exception",
|
|
2324
|
+
"exit",
|
|
2325
|
+
"for",
|
|
2326
|
+
"function",
|
|
2327
|
+
"generic",
|
|
2328
|
+
"goto",
|
|
2329
|
+
"if",
|
|
2330
|
+
"in",
|
|
2331
|
+
"interface",
|
|
2332
|
+
"is",
|
|
2333
|
+
"limited",
|
|
2334
|
+
"loop",
|
|
2335
|
+
"mod",
|
|
2336
|
+
"new",
|
|
2337
|
+
"not",
|
|
2338
|
+
"null",
|
|
2339
|
+
"of",
|
|
2340
|
+
"or",
|
|
2341
|
+
"others",
|
|
2342
|
+
"out",
|
|
2343
|
+
"overriding",
|
|
2344
|
+
"package",
|
|
2345
|
+
"pragma",
|
|
2346
|
+
"private",
|
|
2347
|
+
"procedure",
|
|
2348
|
+
"protected",
|
|
2349
|
+
"raise",
|
|
2350
|
+
"record",
|
|
2351
|
+
"rem",
|
|
2352
|
+
"renames",
|
|
2353
|
+
"requeue",
|
|
2354
|
+
"return",
|
|
2355
|
+
"reverse",
|
|
2356
|
+
"select",
|
|
2357
|
+
"separate",
|
|
2358
|
+
"some",
|
|
2359
|
+
"subtype",
|
|
2360
|
+
"synchronized",
|
|
2361
|
+
"tagged",
|
|
2362
|
+
"task",
|
|
2363
|
+
"terminate",
|
|
2364
|
+
"then",
|
|
2365
|
+
"until",
|
|
2366
|
+
"use",
|
|
2367
|
+
"when",
|
|
2368
|
+
"while",
|
|
2369
|
+
"with",
|
|
2370
|
+
"xor",
|
|
2371
|
+
}
|
|
2372
|
+
)
|
|
2373
|
+
.union(
|
|
2374
|
+
# C, see: https://en.cppreference.com/w/c/keyword
|
|
2375
|
+
{
|
|
2376
|
+
"auto",
|
|
2377
|
+
"break",
|
|
2378
|
+
"case",
|
|
2379
|
+
"char",
|
|
2380
|
+
"const",
|
|
2381
|
+
"continue",
|
|
2382
|
+
"default",
|
|
2383
|
+
"do",
|
|
2384
|
+
"double",
|
|
2385
|
+
"else",
|
|
2386
|
+
"enum",
|
|
2387
|
+
"extern",
|
|
2388
|
+
"float",
|
|
2389
|
+
"for",
|
|
2390
|
+
"goto",
|
|
2391
|
+
"if",
|
|
2392
|
+
"int",
|
|
2393
|
+
"long",
|
|
2394
|
+
"register",
|
|
2395
|
+
"return",
|
|
2396
|
+
"short",
|
|
2397
|
+
"signed",
|
|
2398
|
+
"sizeof",
|
|
2399
|
+
"static",
|
|
2400
|
+
"struct",
|
|
2401
|
+
"switch",
|
|
2402
|
+
"typedef",
|
|
2403
|
+
"union",
|
|
2404
|
+
"unsigned",
|
|
2405
|
+
"void",
|
|
2406
|
+
"volatile",
|
|
2407
|
+
"while",
|
|
2408
|
+
}
|
|
2409
|
+
)
|
|
2410
|
+
.union(
|
|
2411
|
+
# C++, see: https://en.cppreference.com/w/cpp/keyword
|
|
2412
|
+
{
|
|
2413
|
+
"alignas",
|
|
2414
|
+
"alignof",
|
|
2415
|
+
"and",
|
|
2416
|
+
"and_eq",
|
|
2417
|
+
"asm",
|
|
2418
|
+
"atomic_cancel",
|
|
2419
|
+
"atomic_commit",
|
|
2420
|
+
"atomic_noexcept",
|
|
2421
|
+
"auto",
|
|
2422
|
+
"bitand",
|
|
2423
|
+
"bitor",
|
|
2424
|
+
"bool",
|
|
2425
|
+
"break",
|
|
2426
|
+
"case",
|
|
2427
|
+
"catch",
|
|
2428
|
+
"char",
|
|
2429
|
+
"char16_t",
|
|
2430
|
+
"char32_t",
|
|
2431
|
+
"char8_t",
|
|
2432
|
+
"class",
|
|
2433
|
+
"co_await",
|
|
2434
|
+
"co_return",
|
|
2435
|
+
"co_yield",
|
|
2436
|
+
"compl",
|
|
2437
|
+
"concept",
|
|
2438
|
+
"const",
|
|
2439
|
+
"const_cast",
|
|
2440
|
+
"consteval",
|
|
2441
|
+
"constexpr",
|
|
2442
|
+
"constinit",
|
|
2443
|
+
"continue",
|
|
2444
|
+
"decltype",
|
|
2445
|
+
"default",
|
|
2446
|
+
"delete",
|
|
2447
|
+
"do",
|
|
2448
|
+
"double",
|
|
2449
|
+
"dynamic_cast",
|
|
2450
|
+
"else",
|
|
2451
|
+
"enum",
|
|
2452
|
+
"explicit",
|
|
2453
|
+
"export",
|
|
2454
|
+
"extern",
|
|
2455
|
+
"false",
|
|
2456
|
+
"float",
|
|
2457
|
+
"for",
|
|
2458
|
+
"friend",
|
|
2459
|
+
"goto",
|
|
2460
|
+
"if",
|
|
2461
|
+
"inline",
|
|
2462
|
+
"int",
|
|
2463
|
+
"long",
|
|
2464
|
+
"mutable",
|
|
2465
|
+
"namespace",
|
|
2466
|
+
"new",
|
|
2467
|
+
"noexcept",
|
|
2468
|
+
"not",
|
|
2469
|
+
"not_eq",
|
|
2470
|
+
"nullptr",
|
|
2471
|
+
"operator",
|
|
2472
|
+
"or",
|
|
2473
|
+
"or_eq",
|
|
2474
|
+
"private",
|
|
2475
|
+
"protected",
|
|
2476
|
+
"public",
|
|
2477
|
+
"reflexpr",
|
|
2478
|
+
"register",
|
|
2479
|
+
"reinterpret_cast",
|
|
2480
|
+
"requires",
|
|
2481
|
+
"return",
|
|
2482
|
+
"short",
|
|
2483
|
+
"signed",
|
|
2484
|
+
"sizeof",
|
|
2485
|
+
"static",
|
|
2486
|
+
"static_assert",
|
|
2487
|
+
"static_cast",
|
|
2488
|
+
"struct",
|
|
2489
|
+
"switch",
|
|
2490
|
+
"synchronized",
|
|
2491
|
+
"template",
|
|
2492
|
+
"this",
|
|
2493
|
+
"thread_local",
|
|
2494
|
+
"throw",
|
|
2495
|
+
"true",
|
|
2496
|
+
"try",
|
|
2497
|
+
"typedef",
|
|
2498
|
+
"typeid",
|
|
2499
|
+
"typename",
|
|
2500
|
+
"union",
|
|
2501
|
+
"unsigned",
|
|
2502
|
+
"using",
|
|
2503
|
+
"virtual",
|
|
2504
|
+
"void",
|
|
2505
|
+
"volatile",
|
|
2506
|
+
"wchar_t",
|
|
2507
|
+
"while",
|
|
2508
|
+
"xor",
|
|
2509
|
+
"xor_eq",
|
|
2510
|
+
}
|
|
2511
|
+
)
|
|
2512
|
+
.union(
|
|
2513
|
+
# C#, see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
|
|
2514
|
+
{
|
|
2515
|
+
"abstract",
|
|
2516
|
+
"as",
|
|
2517
|
+
"base",
|
|
2518
|
+
"bool",
|
|
2519
|
+
"break",
|
|
2520
|
+
"byte",
|
|
2521
|
+
"case",
|
|
2522
|
+
"catch",
|
|
2523
|
+
"char",
|
|
2524
|
+
"checked",
|
|
2525
|
+
"class",
|
|
2526
|
+
"const",
|
|
2527
|
+
"continue",
|
|
2528
|
+
"decimal",
|
|
2529
|
+
"default",
|
|
2530
|
+
"delegate",
|
|
2531
|
+
"do",
|
|
2532
|
+
"double",
|
|
2533
|
+
"else",
|
|
2534
|
+
"enum",
|
|
2535
|
+
"event",
|
|
2536
|
+
"explicit",
|
|
2537
|
+
"extern",
|
|
2538
|
+
"false",
|
|
2539
|
+
"finally",
|
|
2540
|
+
"fixed",
|
|
2541
|
+
"float",
|
|
2542
|
+
"for",
|
|
2543
|
+
"foreach",
|
|
2544
|
+
"goto",
|
|
2545
|
+
"if",
|
|
2546
|
+
"implicit",
|
|
2547
|
+
"in",
|
|
2548
|
+
"int",
|
|
2549
|
+
"interface",
|
|
2550
|
+
"internal",
|
|
2551
|
+
"is",
|
|
2552
|
+
"lock",
|
|
2553
|
+
"long",
|
|
2554
|
+
"namespace",
|
|
2555
|
+
"new",
|
|
2556
|
+
"null",
|
|
2557
|
+
"object",
|
|
2558
|
+
"operator",
|
|
2559
|
+
"out",
|
|
2560
|
+
"override",
|
|
2561
|
+
"params",
|
|
2562
|
+
"private",
|
|
2563
|
+
"protected",
|
|
2564
|
+
"public",
|
|
2565
|
+
"readonly",
|
|
2566
|
+
"ref",
|
|
2567
|
+
"return",
|
|
2568
|
+
"sbyte",
|
|
2569
|
+
"sealed",
|
|
2570
|
+
"short",
|
|
2571
|
+
"sizeof",
|
|
2572
|
+
"stackalloc",
|
|
2573
|
+
"static",
|
|
2574
|
+
"string",
|
|
2575
|
+
"struct",
|
|
2576
|
+
"switch",
|
|
2577
|
+
"this",
|
|
2578
|
+
"throw",
|
|
2579
|
+
"true",
|
|
2580
|
+
"try",
|
|
2581
|
+
"typeof",
|
|
2582
|
+
"uint",
|
|
2583
|
+
"ulong",
|
|
2584
|
+
"unchecked",
|
|
2585
|
+
"unsafe",
|
|
2586
|
+
"ushort",
|
|
2587
|
+
"using",
|
|
2588
|
+
"virtual",
|
|
2589
|
+
"void",
|
|
2590
|
+
"volatile",
|
|
2591
|
+
"while",
|
|
2592
|
+
}
|
|
2593
|
+
)
|
|
2594
|
+
.union(
|
|
2595
|
+
# D, see: https://dlang.org/spec/lex.html#keywords
|
|
2596
|
+
#
|
|
2597
|
+
# We exclude ``version`` from the keywords as its usage in
|
|
2598
|
+
# the meta-model predates the generation of the D SDK, so it has been
|
|
2599
|
+
# missed, unfortunately.
|
|
2600
|
+
{
|
|
2601
|
+
"abstract",
|
|
2602
|
+
"alias",
|
|
2603
|
+
"align",
|
|
2604
|
+
"asm",
|
|
2605
|
+
"assert",
|
|
2606
|
+
"auto",
|
|
2607
|
+
"body",
|
|
2608
|
+
"bool",
|
|
2609
|
+
"break",
|
|
2610
|
+
"byte",
|
|
2611
|
+
"case",
|
|
2612
|
+
"cast",
|
|
2613
|
+
"catch",
|
|
2614
|
+
"cdouble",
|
|
2615
|
+
"cent",
|
|
2616
|
+
"cfloat",
|
|
2617
|
+
"char",
|
|
2618
|
+
"class",
|
|
2619
|
+
"const",
|
|
2620
|
+
"continue",
|
|
2621
|
+
"creal",
|
|
2622
|
+
"dchar",
|
|
2623
|
+
"debug",
|
|
2624
|
+
"default",
|
|
2625
|
+
"delegate",
|
|
2626
|
+
"delete",
|
|
2627
|
+
"deprecated",
|
|
2628
|
+
"do",
|
|
2629
|
+
"double",
|
|
2630
|
+
"else",
|
|
2631
|
+
"enum",
|
|
2632
|
+
"export",
|
|
2633
|
+
"extern",
|
|
2634
|
+
"false",
|
|
2635
|
+
"final",
|
|
2636
|
+
"finally",
|
|
2637
|
+
"float",
|
|
2638
|
+
"for",
|
|
2639
|
+
"foreach",
|
|
2640
|
+
"foreach_reverse",
|
|
2641
|
+
"function",
|
|
2642
|
+
"goto",
|
|
2643
|
+
"idouble",
|
|
2644
|
+
"if",
|
|
2645
|
+
"ifloat",
|
|
2646
|
+
"immutable",
|
|
2647
|
+
"import",
|
|
2648
|
+
"in",
|
|
2649
|
+
"inout",
|
|
2650
|
+
"int",
|
|
2651
|
+
"interface",
|
|
2652
|
+
"invariant",
|
|
2653
|
+
"ireal",
|
|
2654
|
+
"is",
|
|
2655
|
+
"lazy",
|
|
2656
|
+
"long",
|
|
2657
|
+
"macro",
|
|
2658
|
+
"mixin",
|
|
2659
|
+
"module",
|
|
2660
|
+
"new",
|
|
2661
|
+
"nothrow",
|
|
2662
|
+
"null",
|
|
2663
|
+
"out",
|
|
2664
|
+
"override",
|
|
2665
|
+
"package",
|
|
2666
|
+
"pragma",
|
|
2667
|
+
"private",
|
|
2668
|
+
"protected",
|
|
2669
|
+
"public",
|
|
2670
|
+
"pure",
|
|
2671
|
+
"real",
|
|
2672
|
+
"ref",
|
|
2673
|
+
"return",
|
|
2674
|
+
"scope",
|
|
2675
|
+
"shared",
|
|
2676
|
+
"short",
|
|
2677
|
+
"static",
|
|
2678
|
+
"struct",
|
|
2679
|
+
"super",
|
|
2680
|
+
"switch",
|
|
2681
|
+
"synchronized",
|
|
2682
|
+
"template",
|
|
2683
|
+
"this",
|
|
2684
|
+
"throw",
|
|
2685
|
+
"true",
|
|
2686
|
+
"try",
|
|
2687
|
+
"typeid",
|
|
2688
|
+
"typeof",
|
|
2689
|
+
"ubyte",
|
|
2690
|
+
"ucent",
|
|
2691
|
+
"uint",
|
|
2692
|
+
"ulong",
|
|
2693
|
+
"union",
|
|
2694
|
+
"unittest",
|
|
2695
|
+
"ushort",
|
|
2696
|
+
"void",
|
|
2697
|
+
"wchar",
|
|
2698
|
+
"while",
|
|
2699
|
+
"with",
|
|
2700
|
+
}
|
|
2701
|
+
)
|
|
2702
|
+
.union(
|
|
2703
|
+
# Eiffel, see: https://www.eiffel.org/doc/eiffel/Eiffel_programming_language_reserved_words
|
|
2704
|
+
{
|
|
2705
|
+
"across",
|
|
2706
|
+
"agent",
|
|
2707
|
+
"alias",
|
|
2708
|
+
"all",
|
|
2709
|
+
"and",
|
|
2710
|
+
"as",
|
|
2711
|
+
"assign",
|
|
2712
|
+
"attribute",
|
|
2713
|
+
"check",
|
|
2714
|
+
"class",
|
|
2715
|
+
"convert",
|
|
2716
|
+
"create",
|
|
2717
|
+
"current",
|
|
2718
|
+
"debug",
|
|
2719
|
+
"deferred",
|
|
2720
|
+
"do",
|
|
2721
|
+
"else",
|
|
2722
|
+
"elseif",
|
|
2723
|
+
"end",
|
|
2724
|
+
"ensure",
|
|
2725
|
+
"expanded",
|
|
2726
|
+
"export",
|
|
2727
|
+
"external",
|
|
2728
|
+
"false",
|
|
2729
|
+
"feature",
|
|
2730
|
+
"from",
|
|
2731
|
+
"frozen",
|
|
2732
|
+
"if",
|
|
2733
|
+
"implies",
|
|
2734
|
+
"inherit",
|
|
2735
|
+
"inspect",
|
|
2736
|
+
"invariant",
|
|
2737
|
+
"like",
|
|
2738
|
+
"local",
|
|
2739
|
+
"loop",
|
|
2740
|
+
"not",
|
|
2741
|
+
"note",
|
|
2742
|
+
"obsolete",
|
|
2743
|
+
"old",
|
|
2744
|
+
"once",
|
|
2745
|
+
"only",
|
|
2746
|
+
"or",
|
|
2747
|
+
"precursor",
|
|
2748
|
+
"redefine",
|
|
2749
|
+
"rename",
|
|
2750
|
+
"require",
|
|
2751
|
+
"rescue",
|
|
2752
|
+
# NOTE (mristin, 2024-06-19):
|
|
2753
|
+
# We exclude the keyword ``result`` from the checks as the AAS server
|
|
2754
|
+
# API at version 3.0 already uses it for one of the object properties.
|
|
2755
|
+
# We have to work around it in the code generator for Eiffel SDK.
|
|
2756
|
+
# "result",
|
|
2757
|
+
"retry",
|
|
2758
|
+
"select",
|
|
2759
|
+
"separate",
|
|
2760
|
+
"then",
|
|
2761
|
+
"true",
|
|
2762
|
+
"tuple",
|
|
2763
|
+
"undefine",
|
|
2764
|
+
"until",
|
|
2765
|
+
"variant",
|
|
2766
|
+
"void",
|
|
2767
|
+
"when",
|
|
2768
|
+
"xor",
|
|
2769
|
+
}
|
|
2770
|
+
)
|
|
2771
|
+
.union(
|
|
2772
|
+
# Elixir, see: https://hexdocs.pm/elixir/1.12.3/syntax-reference.html#reserved-words
|
|
2773
|
+
{
|
|
2774
|
+
"after",
|
|
2775
|
+
"and",
|
|
2776
|
+
"catch",
|
|
2777
|
+
"do",
|
|
2778
|
+
"else",
|
|
2779
|
+
"end",
|
|
2780
|
+
"false",
|
|
2781
|
+
"fn",
|
|
2782
|
+
"in",
|
|
2783
|
+
"nil",
|
|
2784
|
+
"not",
|
|
2785
|
+
"or",
|
|
2786
|
+
"rescue",
|
|
2787
|
+
"true",
|
|
2788
|
+
"when",
|
|
2789
|
+
}
|
|
2790
|
+
)
|
|
2791
|
+
.union(
|
|
2792
|
+
# Erlang, see: https://www.erlang.org/doc/reference_manual/introduction.html#reserved-words
|
|
2793
|
+
{
|
|
2794
|
+
"after",
|
|
2795
|
+
"and",
|
|
2796
|
+
"andalso",
|
|
2797
|
+
"band",
|
|
2798
|
+
"begin",
|
|
2799
|
+
"bnot",
|
|
2800
|
+
"bor",
|
|
2801
|
+
"bsl",
|
|
2802
|
+
"bsr",
|
|
2803
|
+
"bxor",
|
|
2804
|
+
"case",
|
|
2805
|
+
"catch",
|
|
2806
|
+
"cond",
|
|
2807
|
+
"div",
|
|
2808
|
+
"end",
|
|
2809
|
+
"fun",
|
|
2810
|
+
"if",
|
|
2811
|
+
"let",
|
|
2812
|
+
"maybe",
|
|
2813
|
+
"not",
|
|
2814
|
+
"of",
|
|
2815
|
+
"or",
|
|
2816
|
+
"orelse",
|
|
2817
|
+
"receive",
|
|
2818
|
+
"rem",
|
|
2819
|
+
"try",
|
|
2820
|
+
"when",
|
|
2821
|
+
"xor",
|
|
2822
|
+
}
|
|
2823
|
+
)
|
|
2824
|
+
.union(
|
|
2825
|
+
# Go, see: https://go.dev/ref/spec#Keywords.
|
|
2826
|
+
#
|
|
2827
|
+
# We exclude ``type`` and ``range`` from the keywords as its usage in
|
|
2828
|
+
# the meta-model predates the generation of the Go SDK, so it has been
|
|
2829
|
+
# missed, unfortunately.
|
|
2830
|
+
{
|
|
2831
|
+
"break",
|
|
2832
|
+
"case",
|
|
2833
|
+
"chan",
|
|
2834
|
+
"const",
|
|
2835
|
+
"continue",
|
|
2836
|
+
"default",
|
|
2837
|
+
"defer",
|
|
2838
|
+
"else",
|
|
2839
|
+
"fallthrough",
|
|
2840
|
+
"for",
|
|
2841
|
+
"func",
|
|
2842
|
+
"go",
|
|
2843
|
+
"goto",
|
|
2844
|
+
"if",
|
|
2845
|
+
"import",
|
|
2846
|
+
"interface",
|
|
2847
|
+
"map",
|
|
2848
|
+
"package",
|
|
2849
|
+
"return",
|
|
2850
|
+
"select",
|
|
2851
|
+
"struct",
|
|
2852
|
+
"switch",
|
|
2853
|
+
"var",
|
|
2854
|
+
}
|
|
2855
|
+
)
|
|
2856
|
+
.union(
|
|
2857
|
+
# Java, see: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html
|
|
2858
|
+
{
|
|
2859
|
+
"abstract",
|
|
2860
|
+
"assert",
|
|
2861
|
+
"boolean",
|
|
2862
|
+
"break",
|
|
2863
|
+
"byte",
|
|
2864
|
+
"case",
|
|
2865
|
+
"catch",
|
|
2866
|
+
"char",
|
|
2867
|
+
"class",
|
|
2868
|
+
"const",
|
|
2869
|
+
"continue",
|
|
2870
|
+
"default",
|
|
2871
|
+
"do",
|
|
2872
|
+
"double",
|
|
2873
|
+
"else",
|
|
2874
|
+
"enum",
|
|
2875
|
+
"extends",
|
|
2876
|
+
"final",
|
|
2877
|
+
"finally",
|
|
2878
|
+
"float",
|
|
2879
|
+
"for",
|
|
2880
|
+
"goto",
|
|
2881
|
+
"if",
|
|
2882
|
+
"implements",
|
|
2883
|
+
"import",
|
|
2884
|
+
"instanceof",
|
|
2885
|
+
"int",
|
|
2886
|
+
"interface",
|
|
2887
|
+
"long",
|
|
2888
|
+
"native",
|
|
2889
|
+
"new",
|
|
2890
|
+
"package",
|
|
2891
|
+
"private",
|
|
2892
|
+
"protected",
|
|
2893
|
+
"public",
|
|
2894
|
+
"return",
|
|
2895
|
+
"short",
|
|
2896
|
+
"static",
|
|
2897
|
+
"strictfp",
|
|
2898
|
+
"super",
|
|
2899
|
+
"switch",
|
|
2900
|
+
"synchronized",
|
|
2901
|
+
"this",
|
|
2902
|
+
"throw",
|
|
2903
|
+
"throws",
|
|
2904
|
+
"transient",
|
|
2905
|
+
"try",
|
|
2906
|
+
"void",
|
|
2907
|
+
"volatile",
|
|
2908
|
+
"while",
|
|
2909
|
+
}
|
|
2910
|
+
)
|
|
2911
|
+
.union(
|
|
2912
|
+
# Python, see: https://docs.python.org/3/reference/lexical_analysis.html#keywords
|
|
2913
|
+
{
|
|
2914
|
+
"and",
|
|
2915
|
+
"as",
|
|
2916
|
+
"assert",
|
|
2917
|
+
"async",
|
|
2918
|
+
"await",
|
|
2919
|
+
"break",
|
|
2920
|
+
"class",
|
|
2921
|
+
"continue",
|
|
2922
|
+
"def",
|
|
2923
|
+
"del",
|
|
2924
|
+
"elif",
|
|
2925
|
+
"else",
|
|
2926
|
+
"except",
|
|
2927
|
+
"false",
|
|
2928
|
+
"finally",
|
|
2929
|
+
"for",
|
|
2930
|
+
"from",
|
|
2931
|
+
"global",
|
|
2932
|
+
"if",
|
|
2933
|
+
"import",
|
|
2934
|
+
"in",
|
|
2935
|
+
"is",
|
|
2936
|
+
"lambda",
|
|
2937
|
+
"none",
|
|
2938
|
+
"nonlocal",
|
|
2939
|
+
"not",
|
|
2940
|
+
"or",
|
|
2941
|
+
"pass",
|
|
2942
|
+
"raise",
|
|
2943
|
+
"return",
|
|
2944
|
+
"true",
|
|
2945
|
+
"try",
|
|
2946
|
+
"while",
|
|
2947
|
+
"with",
|
|
2948
|
+
"yield",
|
|
2949
|
+
}
|
|
2950
|
+
)
|
|
2951
|
+
.union(
|
|
2952
|
+
# Rust, see: https://doc.rust-lang.org/reference/keywords.html
|
|
2953
|
+
#
|
|
2954
|
+
# We exclude ``type`` from the keywords as its usage in the meta-model
|
|
2955
|
+
# predates the generation of the Rust SDK, so it has been missed,
|
|
2956
|
+
# unfortunately.
|
|
2957
|
+
{
|
|
2958
|
+
"abstract",
|
|
2959
|
+
"as",
|
|
2960
|
+
"async",
|
|
2961
|
+
"await",
|
|
2962
|
+
"become",
|
|
2963
|
+
"box",
|
|
2964
|
+
"break",
|
|
2965
|
+
"const",
|
|
2966
|
+
"continue",
|
|
2967
|
+
"crate",
|
|
2968
|
+
"do",
|
|
2969
|
+
"dyn",
|
|
2970
|
+
"else",
|
|
2971
|
+
"enum",
|
|
2972
|
+
"extern",
|
|
2973
|
+
"false",
|
|
2974
|
+
"final",
|
|
2975
|
+
"fn",
|
|
2976
|
+
"for",
|
|
2977
|
+
"if",
|
|
2978
|
+
"impl",
|
|
2979
|
+
"in",
|
|
2980
|
+
"let",
|
|
2981
|
+
"loop",
|
|
2982
|
+
"macro",
|
|
2983
|
+
"match",
|
|
2984
|
+
"mod",
|
|
2985
|
+
"move",
|
|
2986
|
+
"mut",
|
|
2987
|
+
"override",
|
|
2988
|
+
"priv",
|
|
2989
|
+
"pub",
|
|
2990
|
+
"ref",
|
|
2991
|
+
"return",
|
|
2992
|
+
"self",
|
|
2993
|
+
"static",
|
|
2994
|
+
"struct",
|
|
2995
|
+
"super",
|
|
2996
|
+
"trait",
|
|
2997
|
+
"true",
|
|
2998
|
+
"try",
|
|
2999
|
+
"typeof",
|
|
3000
|
+
"union",
|
|
3001
|
+
"unsafe",
|
|
3002
|
+
"unsized",
|
|
3003
|
+
"use",
|
|
3004
|
+
"virtual",
|
|
3005
|
+
"where",
|
|
3006
|
+
"while",
|
|
3007
|
+
"yield",
|
|
3008
|
+
}
|
|
3009
|
+
)
|
|
3010
|
+
.union(
|
|
3011
|
+
# TypeScript, see: https://github.com/microsoft/TypeScript/issues/2536#issuecomment-87194347
|
|
3012
|
+
#
|
|
3013
|
+
# We exclude ``type`` and ``symbol`` from the keywords as its usage in
|
|
3014
|
+
# the meta-model predates the generation of the TypeScript SDK, so it has
|
|
3015
|
+
# been missed, unfortunately.
|
|
3016
|
+
{
|
|
3017
|
+
"any",
|
|
3018
|
+
"as",
|
|
3019
|
+
"boolean",
|
|
3020
|
+
"break",
|
|
3021
|
+
"case",
|
|
3022
|
+
"catch",
|
|
3023
|
+
"class",
|
|
3024
|
+
"const",
|
|
3025
|
+
"constructor",
|
|
3026
|
+
"continue",
|
|
3027
|
+
"debugger",
|
|
3028
|
+
"declare",
|
|
3029
|
+
"default",
|
|
3030
|
+
"delete",
|
|
3031
|
+
"do",
|
|
3032
|
+
"else",
|
|
3033
|
+
"enum",
|
|
3034
|
+
"export",
|
|
3035
|
+
"extends",
|
|
3036
|
+
"false",
|
|
3037
|
+
"finally",
|
|
3038
|
+
"for",
|
|
3039
|
+
"from",
|
|
3040
|
+
"function",
|
|
3041
|
+
"get",
|
|
3042
|
+
"if",
|
|
3043
|
+
"implements",
|
|
3044
|
+
"import",
|
|
3045
|
+
"in",
|
|
3046
|
+
"instanceof",
|
|
3047
|
+
"interface",
|
|
3048
|
+
"let",
|
|
3049
|
+
"module",
|
|
3050
|
+
"new",
|
|
3051
|
+
"null",
|
|
3052
|
+
"number",
|
|
3053
|
+
"of",
|
|
3054
|
+
"package",
|
|
3055
|
+
"private",
|
|
3056
|
+
"protected",
|
|
3057
|
+
"public",
|
|
3058
|
+
"require",
|
|
3059
|
+
"return",
|
|
3060
|
+
"set",
|
|
3061
|
+
"static",
|
|
3062
|
+
"string",
|
|
3063
|
+
"super",
|
|
3064
|
+
"switch",
|
|
3065
|
+
"this",
|
|
3066
|
+
"throw",
|
|
3067
|
+
"true",
|
|
3068
|
+
"try",
|
|
3069
|
+
"typeof",
|
|
3070
|
+
"var",
|
|
3071
|
+
"void",
|
|
3072
|
+
"while",
|
|
3073
|
+
"with",
|
|
3074
|
+
"yield",
|
|
3075
|
+
}
|
|
3076
|
+
)
|
|
3077
|
+
)
|
|
3078
|
+
|
|
3079
|
+
for keyword in keywords_in_many_implementations:
|
|
3080
|
+
assert keyword == keyword.lower(), f"{keyword=}, {keyword.lower()=}"
|
|
3081
|
+
|
|
3082
|
+
# noinspection SpellCheckingInspection
|
|
3083
|
+
reserved_type_names = keywords_in_many_implementations.union(
|
|
3084
|
+
# Types used in aas-core implementations
|
|
3085
|
+
{
|
|
3086
|
+
"aas",
|
|
3087
|
+
"accept",
|
|
3088
|
+
"context",
|
|
3089
|
+
"class",
|
|
3090
|
+
"error",
|
|
3091
|
+
"errors",
|
|
3092
|
+
"iclass",
|
|
3093
|
+
"itransformer_with_context",
|
|
3094
|
+
"ivisitor",
|
|
3095
|
+
"ivisitor_with_context",
|
|
3096
|
+
"jsonization",
|
|
3097
|
+
"path",
|
|
3098
|
+
"stringification",
|
|
3099
|
+
"transform",
|
|
3100
|
+
"transformer",
|
|
3101
|
+
"transformer_with_context",
|
|
3102
|
+
"verification",
|
|
3103
|
+
"visit",
|
|
3104
|
+
"visitation",
|
|
3105
|
+
"visitor",
|
|
3106
|
+
"visitor_with_context",
|
|
3107
|
+
"match",
|
|
3108
|
+
"constants",
|
|
3109
|
+
"model_type",
|
|
3110
|
+
"enhancement",
|
|
3111
|
+
"enhanced",
|
|
3112
|
+
"descent",
|
|
3113
|
+
"iterator",
|
|
3114
|
+
"serialization_error",
|
|
3115
|
+
"deserialization_error",
|
|
3116
|
+
"verification_error",
|
|
3117
|
+
}
|
|
3118
|
+
).union(
|
|
3119
|
+
# Utility types in Typescript,
|
|
3120
|
+
# see: https://www.typescriptlang.org/docs/handbook/utility-types.html
|
|
3121
|
+
{
|
|
3122
|
+
"awaited",
|
|
3123
|
+
"partial",
|
|
3124
|
+
"required",
|
|
3125
|
+
"readonly",
|
|
3126
|
+
"record",
|
|
3127
|
+
"pick",
|
|
3128
|
+
"omit",
|
|
3129
|
+
"exclude",
|
|
3130
|
+
"extract",
|
|
3131
|
+
"non_nullable",
|
|
3132
|
+
"parameters",
|
|
3133
|
+
"constructor_parameters",
|
|
3134
|
+
"return_type",
|
|
3135
|
+
"instance_type",
|
|
3136
|
+
"this_parameter_type",
|
|
3137
|
+
"omit_this_parameter",
|
|
3138
|
+
"this_type",
|
|
3139
|
+
"uppercase",
|
|
3140
|
+
"lowercase",
|
|
3141
|
+
"capitalize",
|
|
3142
|
+
"uncapitalize",
|
|
3143
|
+
}
|
|
3144
|
+
)
|
|
3145
|
+
|
|
3146
|
+
# NOTE (mristin, 2023-06-30):
|
|
3147
|
+
# We check against the lower-case to report even if the case is different, so
|
|
3148
|
+
# the type names are lower-cased in this set as well.
|
|
3149
|
+
for type_name in reserved_type_names:
|
|
3150
|
+
assert type_name == type_name.lower(), f"{type_name=}, {type_name.lower()=}"
|
|
3151
|
+
|
|
3152
|
+
reserved_member_names = keywords_in_many_implementations.union(
|
|
3153
|
+
{
|
|
3154
|
+
"descend",
|
|
3155
|
+
"descend_once",
|
|
3156
|
+
"accept",
|
|
3157
|
+
"transform",
|
|
3158
|
+
"type_name",
|
|
3159
|
+
"property_name",
|
|
3160
|
+
"match",
|
|
3161
|
+
"model_type",
|
|
3162
|
+
"get_model_type",
|
|
3163
|
+
"set_model_type",
|
|
3164
|
+
"set_enhancement",
|
|
3165
|
+
"get_enhancement",
|
|
3166
|
+
"enhancement",
|
|
3167
|
+
}
|
|
3168
|
+
)
|
|
3169
|
+
|
|
3170
|
+
for our_type in symbol_table.our_types:
|
|
3171
|
+
if our_type.name.startswith("I_"):
|
|
3172
|
+
errors.append(
|
|
3173
|
+
Error(
|
|
3174
|
+
our_type.node,
|
|
3175
|
+
f"The prefix ``I_`` in the name of the type is reserved "
|
|
3176
|
+
f"for the code generation: {our_type.name!r}",
|
|
3177
|
+
)
|
|
3178
|
+
)
|
|
3179
|
+
|
|
3180
|
+
if our_type.name.startswith("Must_"):
|
|
3181
|
+
errors.append(
|
|
3182
|
+
Error(
|
|
3183
|
+
our_type.node,
|
|
3184
|
+
f"The prefix ``Must_`` in the name of the type is reserved "
|
|
3185
|
+
f"for the code generation: {our_type.name!r}",
|
|
3186
|
+
)
|
|
3187
|
+
)
|
|
3188
|
+
|
|
3189
|
+
if our_type.name.lower() in reserved_type_names:
|
|
3190
|
+
errors.append(
|
|
3191
|
+
Error(
|
|
3192
|
+
our_type.node,
|
|
3193
|
+
f"The name of the type is reserved "
|
|
3194
|
+
f"for the code generation: {our_type.name!r}",
|
|
3195
|
+
)
|
|
3196
|
+
)
|
|
3197
|
+
|
|
3198
|
+
if isinstance(our_type, Class):
|
|
3199
|
+
for method in our_type.methods:
|
|
3200
|
+
if method.name.lower() in reserved_member_names:
|
|
3201
|
+
errors.append(
|
|
3202
|
+
Error(
|
|
3203
|
+
method.node,
|
|
3204
|
+
f"The name of the method is reserved "
|
|
3205
|
+
f"for the code generation: {method.name!r}",
|
|
3206
|
+
)
|
|
3207
|
+
)
|
|
3208
|
+
|
|
3209
|
+
# noinspection SpellCheckingInspection
|
|
3210
|
+
if method.name.lower().startswith("over") and (
|
|
3211
|
+
method.name.lower().endswith("or_empty")
|
|
3212
|
+
or method.name.lower().endswith("orempty")
|
|
3213
|
+
):
|
|
3214
|
+
errors.append(
|
|
3215
|
+
Error(
|
|
3216
|
+
method.node,
|
|
3217
|
+
f"The name of the method is reserved "
|
|
3218
|
+
f"for the code generation of "
|
|
3219
|
+
f'enumerable "Over_X_or_Empty" getters: {method.name!r}',
|
|
3220
|
+
)
|
|
3221
|
+
)
|
|
3222
|
+
|
|
3223
|
+
if method.name.lower().startswith("mutable"):
|
|
3224
|
+
errors.append(
|
|
3225
|
+
Error(
|
|
3226
|
+
method.node,
|
|
3227
|
+
f"The prefix 'mutable' in the name of the method "
|
|
3228
|
+
f"is reserved for the code generation in languages "
|
|
3229
|
+
f"such as C++ which distinguish between "
|
|
3230
|
+
f"mutating and const getters: {method.name!r}",
|
|
3231
|
+
)
|
|
3232
|
+
)
|
|
3233
|
+
|
|
3234
|
+
for prop in our_type.properties:
|
|
3235
|
+
if prop.name.lower() in reserved_member_names:
|
|
3236
|
+
errors.append(
|
|
3237
|
+
Error(
|
|
3238
|
+
prop.node,
|
|
3239
|
+
f"The name of the property is reserved "
|
|
3240
|
+
f"for the code generation: {prop.name!r}",
|
|
3241
|
+
)
|
|
3242
|
+
)
|
|
3243
|
+
|
|
3244
|
+
if prop.name.lower().startswith("mutable"):
|
|
3245
|
+
errors.append(
|
|
3246
|
+
Error(
|
|
3247
|
+
prop.node,
|
|
3248
|
+
f"The prefix 'mutable' in the name of the property is "
|
|
3249
|
+
f"reserved for the code generation in languages "
|
|
3250
|
+
f"such as C++ which distinguish between "
|
|
3251
|
+
f"mutating and const getters: {prop.name!r}",
|
|
3252
|
+
)
|
|
3253
|
+
)
|
|
3254
|
+
|
|
3255
|
+
for constant in symbol_table.constants:
|
|
3256
|
+
constant_name_lower = constant.name.lower()
|
|
3257
|
+
if (
|
|
3258
|
+
constant_name_lower in reserved_member_names
|
|
3259
|
+
or constant_name_lower in reserved_type_names
|
|
3260
|
+
):
|
|
3261
|
+
errors.append(
|
|
3262
|
+
Error(
|
|
3263
|
+
constant.node,
|
|
3264
|
+
f"The name of the constant is reserved "
|
|
3265
|
+
f"for the code generation: {constant.name!r}",
|
|
3266
|
+
)
|
|
3267
|
+
)
|
|
3268
|
+
|
|
3269
|
+
for func in symbol_table.verification_functions:
|
|
3270
|
+
func_name_lower = func.name.lower()
|
|
3271
|
+
if (
|
|
3272
|
+
func_name_lower in reserved_member_names
|
|
3273
|
+
or func_name_lower in reserved_type_names
|
|
3274
|
+
):
|
|
3275
|
+
errors.append(
|
|
3276
|
+
Error(
|
|
3277
|
+
func.node,
|
|
3278
|
+
f"The name of the verification function is reserved "
|
|
3279
|
+
f"for the code generation: {func.name!r}",
|
|
3280
|
+
)
|
|
3281
|
+
)
|
|
3282
|
+
|
|
3283
|
+
# endregion
|
|
3284
|
+
|
|
3285
|
+
# region Check that there are no duplicate type names
|
|
3286
|
+
|
|
3287
|
+
observed_type_names = dict() # type: MutableMapping[Identifier, OurType]
|
|
3288
|
+
for our_type in symbol_table.our_types:
|
|
3289
|
+
another_our_type = observed_type_names.get(our_type.name, None)
|
|
3290
|
+
if another_our_type is None:
|
|
3291
|
+
observed_type_names[our_type.name] = our_type
|
|
3292
|
+
else:
|
|
3293
|
+
errors.append(
|
|
3294
|
+
Error(
|
|
3295
|
+
our_type.node,
|
|
3296
|
+
f"Our type with the name {our_type.name!r} conflicts with "
|
|
3297
|
+
f"other type with the same name.",
|
|
3298
|
+
)
|
|
3299
|
+
)
|
|
3300
|
+
|
|
3301
|
+
# endregion
|
|
3302
|
+
|
|
3303
|
+
# region Check that there are no duplicate names of the constants
|
|
3304
|
+
|
|
3305
|
+
observed_constant_names = dict() # type: MutableMapping[Identifier, Constant]
|
|
3306
|
+
for constant in symbol_table.constants:
|
|
3307
|
+
another_constant = observed_constant_names.get(constant.name, None)
|
|
3308
|
+
if another_constant is None:
|
|
3309
|
+
observed_constant_names[constant.name] = constant
|
|
3310
|
+
else:
|
|
3311
|
+
errors.append(
|
|
3312
|
+
Error(
|
|
3313
|
+
constant.node,
|
|
3314
|
+
f"The constant with the name {constant.name!r} conflicts with "
|
|
3315
|
+
f"another constant with the same name.",
|
|
3316
|
+
)
|
|
3317
|
+
)
|
|
3318
|
+
|
|
3319
|
+
# endregion
|
|
3320
|
+
|
|
3321
|
+
# region Check that there are no duplicate names of the verification functions
|
|
3322
|
+
|
|
3323
|
+
observed_verification_func_names = (
|
|
3324
|
+
dict()
|
|
3325
|
+
) # type: MutableMapping[Identifier, FunctionUnion]
|
|
3326
|
+
|
|
3327
|
+
for func in symbol_table.verification_functions:
|
|
3328
|
+
another_func = observed_verification_func_names.get(func.name, None)
|
|
3329
|
+
if another_func is None:
|
|
3330
|
+
observed_verification_func_names[func.name] = func
|
|
3331
|
+
else:
|
|
3332
|
+
errors.append(
|
|
3333
|
+
Error(
|
|
3334
|
+
func.node,
|
|
3335
|
+
f"The verification function with the name {func.name!r} conflicts "
|
|
3336
|
+
f"with another verification function with the same name.",
|
|
3337
|
+
)
|
|
3338
|
+
)
|
|
3339
|
+
|
|
3340
|
+
# endregion
|
|
3341
|
+
|
|
3342
|
+
if len(errors) > 0:
|
|
3343
|
+
return None, errors
|
|
3344
|
+
|
|
3345
|
+
# region Check that imported symbols are not re-assigned in an understood method
|
|
3346
|
+
|
|
3347
|
+
for understood_method in itertools.chain(
|
|
3348
|
+
(
|
|
3349
|
+
method
|
|
3350
|
+
for method in symbol_table.verification_functions
|
|
3351
|
+
if isinstance(method, UnderstoodMethod)
|
|
3352
|
+
),
|
|
3353
|
+
(
|
|
3354
|
+
method
|
|
3355
|
+
for our_type in symbol_table.our_types
|
|
3356
|
+
if isinstance(our_type, Class)
|
|
3357
|
+
for method in our_type.methods
|
|
3358
|
+
if isinstance(method, UnderstoodMethod)
|
|
3359
|
+
),
|
|
3360
|
+
):
|
|
3361
|
+
for stmt in understood_method.body:
|
|
3362
|
+
if (
|
|
3363
|
+
isinstance(stmt, tree.Assignment)
|
|
3364
|
+
and isinstance(stmt.target, tree.Name)
|
|
3365
|
+
and stmt.target.identifier == "match"
|
|
3366
|
+
):
|
|
3367
|
+
errors.append(
|
|
3368
|
+
Error(
|
|
3369
|
+
stmt.original_node,
|
|
3370
|
+
"The name ``match`` is reserved "
|
|
3371
|
+
"for the function ``match`` "
|
|
3372
|
+
"imported from the ``re`` module of "
|
|
3373
|
+
"the standard Python library",
|
|
3374
|
+
)
|
|
3375
|
+
)
|
|
3376
|
+
|
|
3377
|
+
# endregion
|
|
3378
|
+
|
|
3379
|
+
# region Check that no class methods are used in verification
|
|
3380
|
+
|
|
3381
|
+
for our_type in symbol_table.our_types:
|
|
3382
|
+
if not isinstance(our_type, Class):
|
|
3383
|
+
continue
|
|
3384
|
+
|
|
3385
|
+
for method in our_type.methods:
|
|
3386
|
+
if method.verification:
|
|
3387
|
+
errors.append(
|
|
3388
|
+
Error(
|
|
3389
|
+
method.node,
|
|
3390
|
+
f"Unexpected verification function "
|
|
3391
|
+
f"in a class {our_type.name!r}: {method.name!r}",
|
|
3392
|
+
)
|
|
3393
|
+
)
|
|
3394
|
+
|
|
3395
|
+
# endregion
|
|
3396
|
+
|
|
3397
|
+
# region Check that only instance methods are non-mutating
|
|
3398
|
+
|
|
3399
|
+
for verification_func in symbol_table.verification_functions:
|
|
3400
|
+
if verification_func.non_mutating:
|
|
3401
|
+
errors.append(
|
|
3402
|
+
Error(
|
|
3403
|
+
verification_func.node,
|
|
3404
|
+
f"Functions can not be non-mutating: {verification_func.name!r}",
|
|
3405
|
+
)
|
|
3406
|
+
)
|
|
3407
|
+
|
|
3408
|
+
# endregion
|
|
3409
|
+
|
|
3410
|
+
# region Check dangling inheritances
|
|
3411
|
+
|
|
3412
|
+
for our_type in symbol_table.our_types:
|
|
3413
|
+
if not isinstance(our_type, Class):
|
|
3414
|
+
continue
|
|
3415
|
+
|
|
3416
|
+
for inheritance in our_type.inheritances:
|
|
3417
|
+
# NOTE (mristin, 2021-12-22):
|
|
3418
|
+
# Inheritance from primitive types allows us to constrain a primitive type.
|
|
3419
|
+
if inheritance in PRIMITIVE_TYPES:
|
|
3420
|
+
continue
|
|
3421
|
+
|
|
3422
|
+
parent_type = symbol_table.find_our_type(name=inheritance)
|
|
3423
|
+
|
|
3424
|
+
if parent_type is None:
|
|
3425
|
+
errors.append(
|
|
3426
|
+
Error(
|
|
3427
|
+
our_type.node,
|
|
3428
|
+
f"A parent of the class {our_type.name!r} "
|
|
3429
|
+
f"is dangling: {inheritance!r}",
|
|
3430
|
+
)
|
|
3431
|
+
)
|
|
3432
|
+
|
|
3433
|
+
elif isinstance(parent_type, Class):
|
|
3434
|
+
# A class can inherit from a class.
|
|
3435
|
+
pass
|
|
3436
|
+
else:
|
|
3437
|
+
errors.append(
|
|
3438
|
+
Error(
|
|
3439
|
+
our_type.node,
|
|
3440
|
+
f"Expected the class {our_type.name!r} to inherit "
|
|
3441
|
+
f"from another class, "
|
|
3442
|
+
f"but it inherits from our type {parent_type.name!r} which is "
|
|
3443
|
+
f"a {parent_type.__class__.__name__!r}",
|
|
3444
|
+
)
|
|
3445
|
+
)
|
|
3446
|
+
|
|
3447
|
+
if len(errors) > 0:
|
|
3448
|
+
return None, errors
|
|
3449
|
+
|
|
3450
|
+
# endregion
|
|
3451
|
+
|
|
3452
|
+
# region Check dangling subsets in constant sets
|
|
3453
|
+
|
|
3454
|
+
for constant in symbol_table.constants:
|
|
3455
|
+
if not isinstance(constant, ConstantSet):
|
|
3456
|
+
continue
|
|
3457
|
+
|
|
3458
|
+
for subset_name in constant.subsets:
|
|
3459
|
+
subset = symbol_table.find_constant(name=subset_name)
|
|
3460
|
+
|
|
3461
|
+
if subset is None:
|
|
3462
|
+
errors.append(
|
|
3463
|
+
Error(
|
|
3464
|
+
constant.node,
|
|
3465
|
+
f"The subset {subset_name!r} "
|
|
3466
|
+
f"of the constant set {constant.name!r} is dangling",
|
|
3467
|
+
)
|
|
3468
|
+
)
|
|
3469
|
+
elif not isinstance(subset, ConstantSet):
|
|
3470
|
+
errors.append(
|
|
3471
|
+
Error(
|
|
3472
|
+
constant.node,
|
|
3473
|
+
f"The subset {subset_name!r} "
|
|
3474
|
+
f"of the constant set {constant.name!r} is not a constant set, "
|
|
3475
|
+
f"but: {constant.__class__.__name__}",
|
|
3476
|
+
)
|
|
3477
|
+
)
|
|
3478
|
+
else:
|
|
3479
|
+
pass
|
|
3480
|
+
|
|
3481
|
+
# endregion
|
|
3482
|
+
|
|
3483
|
+
# region Check type annotations
|
|
3484
|
+
|
|
3485
|
+
expected_subscripted_types = GENERIC_TYPES
|
|
3486
|
+
|
|
3487
|
+
# NOTE (mristin, 2021-11-19):
|
|
3488
|
+
# If you expect type qualifiers such as ``Final``, make a copy of
|
|
3489
|
+
# the ``GENERIC_TYPES`` and add them to the copy.
|
|
3490
|
+
|
|
3491
|
+
def verify_no_dangling_references_in_type_annotation(
|
|
3492
|
+
type_annotation: TypeAnnotation,
|
|
3493
|
+
) -> Optional[Error]:
|
|
3494
|
+
"""
|
|
3495
|
+
Check that the type annotation contains no dangling references.
|
|
3496
|
+
|
|
3497
|
+
:return: error message, if any
|
|
3498
|
+
"""
|
|
3499
|
+
if isinstance(type_annotation, AtomicTypeAnnotation):
|
|
3500
|
+
if type_annotation.identifier in PRIMITIVE_TYPES:
|
|
3501
|
+
return None
|
|
3502
|
+
|
|
3503
|
+
if type_annotation.identifier in expected_subscripted_types:
|
|
3504
|
+
return Error(
|
|
3505
|
+
type_annotation.node,
|
|
3506
|
+
f"The type annotation is expected with subscript(s), "
|
|
3507
|
+
f"but got none: {type_annotation.identifier}",
|
|
3508
|
+
)
|
|
3509
|
+
|
|
3510
|
+
if symbol_table.find_our_type(type_annotation.identifier) is not None:
|
|
3511
|
+
return None
|
|
3512
|
+
|
|
3513
|
+
return Error(
|
|
3514
|
+
type_annotation.node,
|
|
3515
|
+
f"Our type could not be found "
|
|
3516
|
+
f"in the symbol table: {type_annotation.identifier}",
|
|
3517
|
+
)
|
|
3518
|
+
|
|
3519
|
+
elif isinstance(type_annotation, SubscriptedTypeAnnotation):
|
|
3520
|
+
if type_annotation.identifier not in expected_subscripted_types:
|
|
3521
|
+
return Error(
|
|
3522
|
+
type_annotation.node,
|
|
3523
|
+
f"Unexpected subscripted type: {type_annotation.identifier}",
|
|
3524
|
+
)
|
|
3525
|
+
|
|
3526
|
+
for subscript in type_annotation.subscripts:
|
|
3527
|
+
error_in_subscript = verify_no_dangling_references_in_type_annotation(
|
|
3528
|
+
type_annotation=subscript
|
|
3529
|
+
)
|
|
3530
|
+
if error_in_subscript is not None:
|
|
3531
|
+
return error_in_subscript
|
|
3532
|
+
|
|
3533
|
+
return None
|
|
3534
|
+
|
|
3535
|
+
elif isinstance(type_annotation, SelfTypeAnnotation):
|
|
3536
|
+
return None
|
|
3537
|
+
|
|
3538
|
+
else:
|
|
3539
|
+
assert_never(type_annotation)
|
|
3540
|
+
|
|
3541
|
+
for our_type in symbol_table.our_types:
|
|
3542
|
+
if not isinstance(our_type, Class):
|
|
3543
|
+
continue
|
|
3544
|
+
|
|
3545
|
+
for prop in our_type.properties:
|
|
3546
|
+
error = verify_no_dangling_references_in_type_annotation(
|
|
3547
|
+
type_annotation=prop.type_annotation
|
|
3548
|
+
)
|
|
3549
|
+
|
|
3550
|
+
if error is not None:
|
|
3551
|
+
errors.append(error)
|
|
3552
|
+
else:
|
|
3553
|
+
if isinstance(prop.type_annotation, SubscriptedTypeAnnotation):
|
|
3554
|
+
error = _verify_arity_of_type_annotation_subscript(
|
|
3555
|
+
prop.type_annotation
|
|
3556
|
+
)
|
|
3557
|
+
|
|
3558
|
+
if error is not None:
|
|
3559
|
+
errors.append(error)
|
|
3560
|
+
|
|
3561
|
+
for method in our_type.methods:
|
|
3562
|
+
for arg in method.arguments:
|
|
3563
|
+
error = verify_no_dangling_references_in_type_annotation(
|
|
3564
|
+
type_annotation=arg.type_annotation
|
|
3565
|
+
)
|
|
3566
|
+
|
|
3567
|
+
if error is not None:
|
|
3568
|
+
errors.append(error)
|
|
3569
|
+
else:
|
|
3570
|
+
if isinstance(arg.type_annotation, SubscriptedTypeAnnotation):
|
|
3571
|
+
error = _verify_arity_of_type_annotation_subscript(
|
|
3572
|
+
arg.type_annotation
|
|
3573
|
+
)
|
|
3574
|
+
|
|
3575
|
+
if error is not None:
|
|
3576
|
+
errors.append(error)
|
|
3577
|
+
|
|
3578
|
+
if method.returns is not None:
|
|
3579
|
+
error = verify_no_dangling_references_in_type_annotation(
|
|
3580
|
+
type_annotation=method.returns
|
|
3581
|
+
)
|
|
3582
|
+
if error is not None:
|
|
3583
|
+
errors.append(error)
|
|
3584
|
+
else:
|
|
3585
|
+
if isinstance(method.returns, SubscriptedTypeAnnotation):
|
|
3586
|
+
error = _verify_arity_of_type_annotation_subscript(
|
|
3587
|
+
method.returns
|
|
3588
|
+
)
|
|
3589
|
+
|
|
3590
|
+
if error is not None:
|
|
3591
|
+
errors.append(error)
|
|
3592
|
+
|
|
3593
|
+
for constant in symbol_table.constants:
|
|
3594
|
+
if isinstance(constant, ConstantPrimitive):
|
|
3595
|
+
pass
|
|
3596
|
+
|
|
3597
|
+
elif isinstance(constant, ConstantSet):
|
|
3598
|
+
error = verify_no_dangling_references_in_type_annotation(
|
|
3599
|
+
type_annotation=constant.items_type_annotation
|
|
3600
|
+
)
|
|
3601
|
+
|
|
3602
|
+
if error is not None:
|
|
3603
|
+
errors.append(error)
|
|
3604
|
+
|
|
3605
|
+
else:
|
|
3606
|
+
assert_never(constant)
|
|
3607
|
+
|
|
3608
|
+
if len(errors) > 0:
|
|
3609
|
+
return None, errors
|
|
3610
|
+
|
|
3611
|
+
# endregion
|
|
3612
|
+
|
|
3613
|
+
if len(errors) > 0:
|
|
3614
|
+
return None, errors
|
|
3615
|
+
|
|
3616
|
+
return SymbolTable(symbol_table), None
|
|
3617
|
+
|
|
3618
|
+
|
|
3619
|
+
Symbol = Union[
|
|
3620
|
+
AbstractClass,
|
|
3621
|
+
ConcreteClass,
|
|
3622
|
+
Enumeration,
|
|
3623
|
+
ConstantPrimitive,
|
|
3624
|
+
ConstantSet,
|
|
3625
|
+
UnderstoodMethod,
|
|
3626
|
+
ImplementationSpecificMethod,
|
|
3627
|
+
]
|
|
3628
|
+
|
|
3629
|
+
|
|
3630
|
+
# fmt: off
|
|
3631
|
+
@ensure(
|
|
3632
|
+
lambda result: not (result is not None) or (len(result) >= 1),
|
|
3633
|
+
"Either null errors or at least one error"
|
|
3634
|
+
)
|
|
3635
|
+
# fmt: on
|
|
3636
|
+
def _verify_duplicate_names(
|
|
3637
|
+
symbols: Iterable[Symbol],
|
|
3638
|
+
) -> Optional[List[Error]]:
|
|
3639
|
+
"""Determine the duplicate names in the given sequence of nameable instances."""
|
|
3640
|
+
name_set = set() # type: Set[Identifier]
|
|
3641
|
+
errors = [] # type: List[Error]
|
|
3642
|
+
for symbol in symbols:
|
|
3643
|
+
if symbol.name in name_set:
|
|
3644
|
+
errors.append(Error(symbol.node, f"Duplicate for {symbol.name!r}"))
|
|
3645
|
+
else:
|
|
3646
|
+
name_set.add(symbol.name)
|
|
3647
|
+
|
|
3648
|
+
if len(errors) > 0:
|
|
3649
|
+
return errors
|
|
3650
|
+
|
|
3651
|
+
return None
|
|
3652
|
+
|
|
3653
|
+
|
|
3654
|
+
# noinspection PyTypeChecker,PyUnresolvedReferences
|
|
3655
|
+
@require(lambda atok: isinstance(atok.tree, ast.Module))
|
|
3656
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
3657
|
+
def _atok_to_symbol_table(
|
|
3658
|
+
atok: asttokens.ASTTokens,
|
|
3659
|
+
) -> Tuple[Optional[SymbolTable], Optional[Error]]:
|
|
3660
|
+
our_types = [] # type: List[OurType]
|
|
3661
|
+
underlying_errors = [] # type: List[Error]
|
|
3662
|
+
|
|
3663
|
+
description = None # type: Optional[Description]
|
|
3664
|
+
version = None # type: Optional[str]
|
|
3665
|
+
xml_namespace = None # type: Optional[Stripped]
|
|
3666
|
+
|
|
3667
|
+
verification_functions = [] # type: List[FunctionUnion]
|
|
3668
|
+
constants = [] # type: List[ConstantUnion]
|
|
3669
|
+
|
|
3670
|
+
# region Parse
|
|
3671
|
+
|
|
3672
|
+
assert atok.tree is not None
|
|
3673
|
+
|
|
3674
|
+
for node in atok.tree.body:
|
|
3675
|
+
# NOTE (mristin, 2021-12-27):
|
|
3676
|
+
# Pass statement makes no sense in our multi-language setting.
|
|
3677
|
+
if isinstance(node, ast.Pass):
|
|
3678
|
+
continue
|
|
3679
|
+
|
|
3680
|
+
# NOTE (mristin, 2022-07-10):
|
|
3681
|
+
# We currently decided to ignore assertions though we leave them in
|
|
3682
|
+
# the meta-model as documentation
|
|
3683
|
+
if isinstance(node, ast.Assert):
|
|
3684
|
+
continue
|
|
3685
|
+
|
|
3686
|
+
# noinspection PyUnusedLocal
|
|
3687
|
+
matched = False
|
|
3688
|
+
|
|
3689
|
+
if isinstance(node, ast.ClassDef):
|
|
3690
|
+
matched = True
|
|
3691
|
+
|
|
3692
|
+
our_type, our_type_error = _classdef_to_our_type(node=node, atok=atok)
|
|
3693
|
+
if our_type_error:
|
|
3694
|
+
underlying_errors.append(
|
|
3695
|
+
Error(
|
|
3696
|
+
node,
|
|
3697
|
+
f"Failed to parse the class definition: {node.name}",
|
|
3698
|
+
[our_type_error],
|
|
3699
|
+
)
|
|
3700
|
+
)
|
|
3701
|
+
else:
|
|
3702
|
+
assert our_type is not None
|
|
3703
|
+
our_types.append(our_type)
|
|
3704
|
+
|
|
3705
|
+
elif (
|
|
3706
|
+
isinstance(node, ast.Expr)
|
|
3707
|
+
and isinstance(node.value, ast.Constant)
|
|
3708
|
+
and isinstance(node.value.value, str)
|
|
3709
|
+
):
|
|
3710
|
+
matched = True
|
|
3711
|
+
|
|
3712
|
+
# The first string literal is assumed to be the docstring of the meta-model.
|
|
3713
|
+
description, description_error = _ast_constant_string_to_description(
|
|
3714
|
+
constant=node.value
|
|
3715
|
+
)
|
|
3716
|
+
|
|
3717
|
+
if description_error is not None:
|
|
3718
|
+
assert description is None
|
|
3719
|
+
underlying_errors.append(description_error)
|
|
3720
|
+
continue
|
|
3721
|
+
|
|
3722
|
+
elif isinstance(node, ast.FunctionDef):
|
|
3723
|
+
matched = True
|
|
3724
|
+
|
|
3725
|
+
method, error = _function_def_to_method(
|
|
3726
|
+
node=node, expect_self=False, atok=atok
|
|
3727
|
+
)
|
|
3728
|
+
|
|
3729
|
+
if error is not None:
|
|
3730
|
+
underlying_errors.append(error)
|
|
3731
|
+
continue
|
|
3732
|
+
|
|
3733
|
+
assert method is not None
|
|
3734
|
+
|
|
3735
|
+
if not method.verification:
|
|
3736
|
+
underlying_errors.append(
|
|
3737
|
+
Error(
|
|
3738
|
+
node,
|
|
3739
|
+
f"We do not know how to interpret a non-verification function "
|
|
3740
|
+
f"in the meta-model: {method.name!r}",
|
|
3741
|
+
)
|
|
3742
|
+
)
|
|
3743
|
+
|
|
3744
|
+
assert isinstance(method, (UnderstoodMethod, ImplementationSpecificMethod))
|
|
3745
|
+
verification_functions.append(method)
|
|
3746
|
+
|
|
3747
|
+
elif isinstance(node, ast.ImportFrom):
|
|
3748
|
+
matched = True
|
|
3749
|
+
|
|
3750
|
+
# Ignore import statements
|
|
3751
|
+
pass
|
|
3752
|
+
|
|
3753
|
+
elif isinstance(node, ast.Assign):
|
|
3754
|
+
if len(node.targets) == 1:
|
|
3755
|
+
if (
|
|
3756
|
+
isinstance(node.targets[0], ast.Name)
|
|
3757
|
+
and isinstance(node.value, ast.Constant)
|
|
3758
|
+
and isinstance(node.value.value, str)
|
|
3759
|
+
):
|
|
3760
|
+
matched = True
|
|
3761
|
+
|
|
3762
|
+
if node.targets[0].id == "__version__":
|
|
3763
|
+
version = node.value.value
|
|
3764
|
+
elif node.targets[0].id == "__xml_namespace__":
|
|
3765
|
+
if not is_stripped(node.value.value):
|
|
3766
|
+
underlying_errors.append(
|
|
3767
|
+
Error(
|
|
3768
|
+
node.value,
|
|
3769
|
+
f"Expected the XML namespace to have no leading "
|
|
3770
|
+
f"or trailing whitespace: {node.value.value!r}",
|
|
3771
|
+
)
|
|
3772
|
+
)
|
|
3773
|
+
continue
|
|
3774
|
+
|
|
3775
|
+
if node.value.value.endswith("/"):
|
|
3776
|
+
underlying_errors.append(
|
|
3777
|
+
Error(
|
|
3778
|
+
node.value,
|
|
3779
|
+
f"Expected the XML namespace to have no trailing "
|
|
3780
|
+
f"slash ('/'), but got: {node.value.value!r}",
|
|
3781
|
+
)
|
|
3782
|
+
)
|
|
3783
|
+
continue
|
|
3784
|
+
|
|
3785
|
+
contains_unexpected_char = False
|
|
3786
|
+
for unexpected_char, unexpected_char_name in [
|
|
3787
|
+
('"', "double-quote"),
|
|
3788
|
+
("'", "single-quote"),
|
|
3789
|
+
]:
|
|
3790
|
+
if unexpected_char in node.value.value:
|
|
3791
|
+
underlying_errors.append(
|
|
3792
|
+
Error(
|
|
3793
|
+
node.value,
|
|
3794
|
+
f"Expected the XML namespace to have no "
|
|
3795
|
+
f"{unexpected_char_name} ({unexpected_char!r}),"
|
|
3796
|
+
f"but got: {node.value.value!r}",
|
|
3797
|
+
)
|
|
3798
|
+
)
|
|
3799
|
+
contains_unexpected_char = True
|
|
3800
|
+
|
|
3801
|
+
if contains_unexpected_char:
|
|
3802
|
+
continue
|
|
3803
|
+
|
|
3804
|
+
xml_namespace = Stripped(node.value.value)
|
|
3805
|
+
else:
|
|
3806
|
+
underlying_errors.append(
|
|
3807
|
+
Error(
|
|
3808
|
+
node,
|
|
3809
|
+
f"We do not know how to interpret "
|
|
3810
|
+
f"the assignment node: {ast.dump(node)}",
|
|
3811
|
+
)
|
|
3812
|
+
)
|
|
3813
|
+
continue
|
|
3814
|
+
else:
|
|
3815
|
+
if (
|
|
3816
|
+
isinstance(node.value, ast.Call)
|
|
3817
|
+
and isinstance(node.value.func, ast.Name)
|
|
3818
|
+
and node.value.func.id.startswith("constant_")
|
|
3819
|
+
):
|
|
3820
|
+
underlying_errors.append(
|
|
3821
|
+
Error(
|
|
3822
|
+
node,
|
|
3823
|
+
f"We do not know how to interpret "
|
|
3824
|
+
f"the assignment: {atok.get_text(node)}. "
|
|
3825
|
+
f"You probably forgot to specify the type annotation "
|
|
3826
|
+
f"for the constant?",
|
|
3827
|
+
)
|
|
3828
|
+
)
|
|
3829
|
+
else:
|
|
3830
|
+
underlying_errors.append(
|
|
3831
|
+
Error(
|
|
3832
|
+
node,
|
|
3833
|
+
f"We do not know how to interpret "
|
|
3834
|
+
f"the assignment: {atok.get_text(node)}",
|
|
3835
|
+
)
|
|
3836
|
+
)
|
|
3837
|
+
|
|
3838
|
+
elif len(node.targets) > 1:
|
|
3839
|
+
underlying_errors.append(
|
|
3840
|
+
Error(
|
|
3841
|
+
node,
|
|
3842
|
+
f"We do not know how to parse a multi-target assignment: "
|
|
3843
|
+
f"{atok.get_text(node)}; in AST: {ast.dump(node)}",
|
|
3844
|
+
)
|
|
3845
|
+
)
|
|
3846
|
+
continue
|
|
3847
|
+
|
|
3848
|
+
elif isinstance(node, ast.AnnAssign):
|
|
3849
|
+
matched = True
|
|
3850
|
+
|
|
3851
|
+
constant, error = _ann_assign_to_constant(node=node, atok=atok)
|
|
3852
|
+
if error is not None:
|
|
3853
|
+
underlying_errors.append(error)
|
|
3854
|
+
continue
|
|
3855
|
+
|
|
3856
|
+
assert constant is not None
|
|
3857
|
+
constants.append(constant)
|
|
3858
|
+
else:
|
|
3859
|
+
matched = False
|
|
3860
|
+
|
|
3861
|
+
if not matched:
|
|
3862
|
+
underlying_errors.append(
|
|
3863
|
+
Error(
|
|
3864
|
+
node, f"We do not know how to parse the AST node: {ast.dump(node)}"
|
|
3865
|
+
)
|
|
3866
|
+
)
|
|
3867
|
+
|
|
3868
|
+
if version is None:
|
|
3869
|
+
underlying_errors.append(
|
|
3870
|
+
Error(
|
|
3871
|
+
None,
|
|
3872
|
+
"The version (given as assignment to ``__version__``) is missing",
|
|
3873
|
+
)
|
|
3874
|
+
)
|
|
3875
|
+
|
|
3876
|
+
if xml_namespace is None:
|
|
3877
|
+
underlying_errors.append(
|
|
3878
|
+
Error(
|
|
3879
|
+
None,
|
|
3880
|
+
"The XML namespace (given as assignment to ``__xml_namespace__``) "
|
|
3881
|
+
"is missing",
|
|
3882
|
+
)
|
|
3883
|
+
)
|
|
3884
|
+
|
|
3885
|
+
duplicate_name_errors = _verify_duplicate_names(
|
|
3886
|
+
itertools.chain(our_types, constants, verification_functions)
|
|
3887
|
+
)
|
|
3888
|
+
if duplicate_name_errors is not None:
|
|
3889
|
+
underlying_errors.append(
|
|
3890
|
+
Error(
|
|
3891
|
+
None,
|
|
3892
|
+
"One or more symbols in the meta-model have duplicate names",
|
|
3893
|
+
duplicate_name_errors,
|
|
3894
|
+
)
|
|
3895
|
+
)
|
|
3896
|
+
|
|
3897
|
+
if len(underlying_errors) > 0:
|
|
3898
|
+
return None, Error(None, "Failed to parse the meta-model", underlying_errors)
|
|
3899
|
+
|
|
3900
|
+
# endregion
|
|
3901
|
+
|
|
3902
|
+
assert version is not None
|
|
3903
|
+
assert xml_namespace is not None
|
|
3904
|
+
|
|
3905
|
+
unverified_symbol_table = UnverifiedSymbolTable(
|
|
3906
|
+
our_types=our_types,
|
|
3907
|
+
constants=constants,
|
|
3908
|
+
verification_functions=verification_functions,
|
|
3909
|
+
meta_model=MetaModel(
|
|
3910
|
+
version=version,
|
|
3911
|
+
xml_namespace=xml_namespace,
|
|
3912
|
+
description=description,
|
|
3913
|
+
),
|
|
3914
|
+
)
|
|
3915
|
+
|
|
3916
|
+
symbol_table, verification_errors = _verify_symbol_table(unverified_symbol_table)
|
|
3917
|
+
|
|
3918
|
+
if verification_errors is not None:
|
|
3919
|
+
return (
|
|
3920
|
+
None,
|
|
3921
|
+
Error(
|
|
3922
|
+
atok.tree, "Verification of the meta-model failed", verification_errors
|
|
3923
|
+
),
|
|
3924
|
+
)
|
|
3925
|
+
|
|
3926
|
+
assert symbol_table is not None
|
|
3927
|
+
return symbol_table, None
|
|
3928
|
+
|
|
3929
|
+
|
|
3930
|
+
@require(lambda atok: isinstance(atok.tree, ast.Module))
|
|
3931
|
+
@ensure(lambda result: (result[0] is None) ^ (result[1] is None))
|
|
3932
|
+
def atok_to_symbol_table(
|
|
3933
|
+
atok: asttokens.ASTTokens,
|
|
3934
|
+
) -> Tuple[Optional[SymbolTable], Optional[Error]]:
|
|
3935
|
+
"""Construct the symbol table based on the parsed AST."""
|
|
3936
|
+
table, error = _atok_to_symbol_table(atok=atok)
|
|
3937
|
+
if error is not None:
|
|
3938
|
+
return None, error
|
|
3939
|
+
|
|
3940
|
+
return table, None
|