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,2266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Infer the types of the tree nodes.
|
|
3
|
+
|
|
4
|
+
Note that these types roughly follow the type annotations in
|
|
5
|
+
:py:mod:`aas_core_codegen.intermediate._types`, but are not identical. For example,
|
|
6
|
+
the ``LENGTH`` primitive exists only in type inference. Another example, we do not
|
|
7
|
+
track ``parsed`` as the types are inferred in the intermediate stage, but can not
|
|
8
|
+
be traced back to the parse stage.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import abc
|
|
12
|
+
import contextlib
|
|
13
|
+
import enum
|
|
14
|
+
from typing import (
|
|
15
|
+
Mapping,
|
|
16
|
+
MutableMapping,
|
|
17
|
+
Optional,
|
|
18
|
+
List,
|
|
19
|
+
Final,
|
|
20
|
+
Union,
|
|
21
|
+
get_args,
|
|
22
|
+
Tuple,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from icontract import DBC, ensure, require
|
|
26
|
+
|
|
27
|
+
from aas_core_codegen.common import (
|
|
28
|
+
Identifier,
|
|
29
|
+
Error,
|
|
30
|
+
assert_never,
|
|
31
|
+
assert_union_of_descendants_exhaustive,
|
|
32
|
+
assert_union_without_excluded,
|
|
33
|
+
)
|
|
34
|
+
from aas_core_codegen.intermediate import _types
|
|
35
|
+
from aas_core_codegen.parse import tree as parse_tree
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PrimitiveType(enum.Enum):
|
|
39
|
+
"""List primitive types."""
|
|
40
|
+
|
|
41
|
+
BOOL = "bool"
|
|
42
|
+
INT = "int"
|
|
43
|
+
FLOAT = "float"
|
|
44
|
+
STR = "str"
|
|
45
|
+
BYTEARRAY = "bytearray"
|
|
46
|
+
|
|
47
|
+
#: Denote the language-agnostic type returned from ``len(.)``.
|
|
48
|
+
#: Depending on the language, this is not the same as ``INT``.
|
|
49
|
+
LENGTH = "length"
|
|
50
|
+
|
|
51
|
+
#: Denote that the node is a statement and that there is no type
|
|
52
|
+
NONE = "None"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TypeAnnotation(DBC):
|
|
56
|
+
"""Represent an inferred type annotation."""
|
|
57
|
+
|
|
58
|
+
@abc.abstractmethod
|
|
59
|
+
def __str__(self) -> str:
|
|
60
|
+
# Signal that this is a purely abstract class
|
|
61
|
+
raise NotImplementedError()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AtomicTypeAnnotation(TypeAnnotation):
|
|
65
|
+
"""
|
|
66
|
+
Represent an atomic type annotation.
|
|
67
|
+
|
|
68
|
+
Atomic, in this context, means a non-generic type annotation.
|
|
69
|
+
|
|
70
|
+
For example, ``int``.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
@abc.abstractmethod
|
|
74
|
+
def __str__(self) -> str:
|
|
75
|
+
# Signal that this is a purely abstract class
|
|
76
|
+
raise NotImplementedError()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class PrimitiveTypeAnnotation(AtomicTypeAnnotation):
|
|
80
|
+
"""Represent a primitive type such as ``int``."""
|
|
81
|
+
|
|
82
|
+
def __init__(self, a_type: PrimitiveType) -> None:
|
|
83
|
+
"""Initialize with the given values."""
|
|
84
|
+
self.a_type = a_type
|
|
85
|
+
|
|
86
|
+
def __str__(self) -> str:
|
|
87
|
+
return str(self.a_type.value)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class OurTypeAnnotation(AtomicTypeAnnotation):
|
|
91
|
+
"""
|
|
92
|
+
Represent an atomic annotation defined by our type in the meta-model.
|
|
93
|
+
|
|
94
|
+
For example, ``Asset``.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(self, our_type: _types.OurType) -> None:
|
|
98
|
+
"""Initialize with the given values."""
|
|
99
|
+
self.our_type = our_type
|
|
100
|
+
|
|
101
|
+
def __str__(self) -> str:
|
|
102
|
+
return self.our_type.name
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class FunctionTypeAnnotation(AtomicTypeAnnotation):
|
|
106
|
+
"""Represent a function as a type."""
|
|
107
|
+
|
|
108
|
+
@abc.abstractmethod
|
|
109
|
+
def __str__(self) -> str:
|
|
110
|
+
# Signal that this is a purely abstract class
|
|
111
|
+
raise NotImplementedError()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class VerificationTypeAnnotation(FunctionTypeAnnotation):
|
|
115
|
+
"""Represent a type of verification function."""
|
|
116
|
+
|
|
117
|
+
def __init__(self, func: _types.Verification):
|
|
118
|
+
"""Initialize with the given values."""
|
|
119
|
+
self.func = func
|
|
120
|
+
|
|
121
|
+
def __str__(self) -> str:
|
|
122
|
+
return self.func.name
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class BuiltinFunction:
|
|
126
|
+
"""Represent a built-in function."""
|
|
127
|
+
|
|
128
|
+
def __init__(self, name: Identifier, returns: Optional["TypeAnnotationUnion"]):
|
|
129
|
+
"""Initialize with the given values."""
|
|
130
|
+
self.name = name
|
|
131
|
+
self.returns = returns
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class BuiltinFunctionTypeAnnotation(FunctionTypeAnnotation):
|
|
135
|
+
"""Represent a type of built-in function."""
|
|
136
|
+
|
|
137
|
+
def __init__(self, func: BuiltinFunction):
|
|
138
|
+
"""Initialize with the given values."""
|
|
139
|
+
self.func = func
|
|
140
|
+
|
|
141
|
+
def __str__(self) -> str:
|
|
142
|
+
return self.func.name
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class MethodTypeAnnotation(AtomicTypeAnnotation):
|
|
146
|
+
"""Represent a type of class method."""
|
|
147
|
+
|
|
148
|
+
def __init__(self, method: _types.Method):
|
|
149
|
+
"""Initialize with the given values."""
|
|
150
|
+
self.method = method
|
|
151
|
+
|
|
152
|
+
def __str__(self) -> str:
|
|
153
|
+
return self.method.name
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class SubscriptedTypeAnnotation(TypeAnnotation):
|
|
157
|
+
"""Represent a subscripted (i.e. generic) type annotation.
|
|
158
|
+
|
|
159
|
+
The subscripted type annotations are, for example, ``List[...]`` (or
|
|
160
|
+
``Mapping[..., ...]``, *etc.*).
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
@abc.abstractmethod
|
|
164
|
+
def __str__(self) -> str:
|
|
165
|
+
# Signal that this is a purely abstract class
|
|
166
|
+
raise NotImplementedError()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class ListTypeAnnotation(SubscriptedTypeAnnotation):
|
|
170
|
+
"""Represent a type annotation involving a ``List[...]``."""
|
|
171
|
+
|
|
172
|
+
def __init__(self, items: "TypeAnnotationUnion"):
|
|
173
|
+
self.items = items
|
|
174
|
+
|
|
175
|
+
def __str__(self) -> str:
|
|
176
|
+
return f"List[{self.items}]"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class SetTypeAnnotation(SubscriptedTypeAnnotation):
|
|
180
|
+
"""Represent a type annotation involving a ``Set[...]``."""
|
|
181
|
+
|
|
182
|
+
def __init__(self, items: "TypeAnnotationUnion"):
|
|
183
|
+
self.items = items
|
|
184
|
+
|
|
185
|
+
def __str__(self) -> str:
|
|
186
|
+
return f"Set[{self.items}]"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class OptionalTypeAnnotation(SubscriptedTypeAnnotation):
|
|
190
|
+
"""Represent a type annotation involving an ``Optional[...]``."""
|
|
191
|
+
|
|
192
|
+
def __init__(self, value: "TypeAnnotationUnion"):
|
|
193
|
+
self.value = value
|
|
194
|
+
|
|
195
|
+
def __str__(self) -> str:
|
|
196
|
+
return f"Optional[{self.value}]"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class EnumerationAsTypeTypeAnnotation(TypeAnnotation):
|
|
200
|
+
"""
|
|
201
|
+
Represent an enum class as a type.
|
|
202
|
+
|
|
203
|
+
Note that this is not the enum as a type of that enum class, but
|
|
204
|
+
rather the type-as-a-type. We write``Type[T]`` in Python to describe this.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
# NOTE (mristin, 2022-02-04):
|
|
208
|
+
# The name of this class is admittedly clumsy. Please feel free to change if you
|
|
209
|
+
# come up with a better idea.
|
|
210
|
+
|
|
211
|
+
def __init__(self, enumeration: _types.Enumeration) -> None:
|
|
212
|
+
"""Initialize with the given values."""
|
|
213
|
+
self.enumeration = enumeration
|
|
214
|
+
|
|
215
|
+
def __str__(self) -> str:
|
|
216
|
+
return f"{self.__class__.__name__}[{self.enumeration}]"
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def beneath_optional(
|
|
220
|
+
type_annotation: "TypeAnnotationUnion",
|
|
221
|
+
) -> "TypeAnnotationExceptOptional":
|
|
222
|
+
"""Recurse over optionals until we reach a non-optional."""
|
|
223
|
+
while isinstance(type_annotation, OptionalTypeAnnotation):
|
|
224
|
+
type_annotation = type_annotation.value
|
|
225
|
+
|
|
226
|
+
return type_annotation
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _type_annotations_equal(
|
|
230
|
+
that: "TypeAnnotationUnion", other: "TypeAnnotationUnion"
|
|
231
|
+
) -> bool:
|
|
232
|
+
"""Check whether the ``that`` and ``other`` type annotations are identical."""
|
|
233
|
+
if isinstance(that, PrimitiveTypeAnnotation):
|
|
234
|
+
if not isinstance(other, PrimitiveTypeAnnotation):
|
|
235
|
+
return False
|
|
236
|
+
else:
|
|
237
|
+
return that.a_type == other.a_type
|
|
238
|
+
|
|
239
|
+
elif isinstance(that, OurTypeAnnotation):
|
|
240
|
+
if not isinstance(other, OurTypeAnnotation):
|
|
241
|
+
return False
|
|
242
|
+
else:
|
|
243
|
+
return that.our_type is other.our_type
|
|
244
|
+
|
|
245
|
+
elif isinstance(that, VerificationTypeAnnotation):
|
|
246
|
+
if not isinstance(other, VerificationTypeAnnotation):
|
|
247
|
+
return False
|
|
248
|
+
else:
|
|
249
|
+
return that.func is other.func
|
|
250
|
+
|
|
251
|
+
elif isinstance(that, BuiltinFunctionTypeAnnotation):
|
|
252
|
+
if not isinstance(other, BuiltinFunctionTypeAnnotation):
|
|
253
|
+
return False
|
|
254
|
+
else:
|
|
255
|
+
return that.func is other.func
|
|
256
|
+
|
|
257
|
+
elif isinstance(that, MethodTypeAnnotation):
|
|
258
|
+
if not isinstance(other, MethodTypeAnnotation):
|
|
259
|
+
return False
|
|
260
|
+
else:
|
|
261
|
+
return that.method is other.method
|
|
262
|
+
|
|
263
|
+
elif isinstance(that, ListTypeAnnotation):
|
|
264
|
+
if not isinstance(other, ListTypeAnnotation):
|
|
265
|
+
return False
|
|
266
|
+
else:
|
|
267
|
+
return _type_annotations_equal(that.items, other.items)
|
|
268
|
+
|
|
269
|
+
elif isinstance(that, SetTypeAnnotation):
|
|
270
|
+
if not isinstance(other, SetTypeAnnotation):
|
|
271
|
+
return False
|
|
272
|
+
else:
|
|
273
|
+
return _type_annotations_equal(that.items, other.items)
|
|
274
|
+
|
|
275
|
+
elif isinstance(that, OptionalTypeAnnotation):
|
|
276
|
+
if not isinstance(other, OptionalTypeAnnotation):
|
|
277
|
+
return False
|
|
278
|
+
else:
|
|
279
|
+
return _type_annotations_equal(that.value, other.value)
|
|
280
|
+
|
|
281
|
+
elif isinstance(that, EnumerationAsTypeTypeAnnotation):
|
|
282
|
+
if not isinstance(other, EnumerationAsTypeTypeAnnotation):
|
|
283
|
+
return False
|
|
284
|
+
else:
|
|
285
|
+
return that.enumeration is other.enumeration
|
|
286
|
+
|
|
287
|
+
else:
|
|
288
|
+
assert_never(that)
|
|
289
|
+
|
|
290
|
+
raise AssertionError("Should not have gotten here")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
PRIMITIVE_TYPE_MAP = {
|
|
294
|
+
_types.PrimitiveType.BOOL: PrimitiveType.BOOL,
|
|
295
|
+
_types.PrimitiveType.INT: PrimitiveType.INT,
|
|
296
|
+
_types.PrimitiveType.FLOAT: PrimitiveType.FLOAT,
|
|
297
|
+
_types.PrimitiveType.STR: PrimitiveType.STR,
|
|
298
|
+
_types.PrimitiveType.BYTEARRAY: PrimitiveType.BYTEARRAY,
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _assignable(
|
|
303
|
+
target_type: "TypeAnnotationUnion", value_type: "TypeAnnotationUnion"
|
|
304
|
+
) -> bool:
|
|
305
|
+
"""Check whether the value can be assigned to the target."""
|
|
306
|
+
if isinstance(target_type, PrimitiveTypeAnnotation):
|
|
307
|
+
if isinstance(value_type, PrimitiveTypeAnnotation):
|
|
308
|
+
return target_type.a_type == value_type.a_type
|
|
309
|
+
|
|
310
|
+
# NOTE (mristin, 2021-12-25):
|
|
311
|
+
# We have to be careful about the constrained primitives,
|
|
312
|
+
# since we can always assign a constrained primitive to a primitive, if they
|
|
313
|
+
# primitive types match.
|
|
314
|
+
elif isinstance(value_type, OurTypeAnnotation) and isinstance(
|
|
315
|
+
value_type.our_type, _types.ConstrainedPrimitive
|
|
316
|
+
):
|
|
317
|
+
return (
|
|
318
|
+
target_type.a_type
|
|
319
|
+
== PRIMITIVE_TYPE_MAP[value_type.our_type.constrainee]
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
else:
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
elif isinstance(target_type, OurTypeAnnotation):
|
|
326
|
+
if isinstance(target_type.our_type, _types.Enumeration):
|
|
327
|
+
# NOTE (mristin, 2021-12-25):
|
|
328
|
+
# The enumerations are invariant.
|
|
329
|
+
return (
|
|
330
|
+
isinstance(value_type, OurTypeAnnotation)
|
|
331
|
+
and isinstance(value_type.our_type, _types.Enumeration)
|
|
332
|
+
and target_type.our_type is value_type.our_type
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
elif isinstance(target_type.our_type, _types.ConstrainedPrimitive):
|
|
336
|
+
# NOTE (mristin, 2021-12-25):
|
|
337
|
+
# If it is a constrained primitive with no constraints, allow the assignment
|
|
338
|
+
# if the target and the value match on the primitive type.
|
|
339
|
+
if len(target_type.our_type.invariants) == 0 and isinstance(
|
|
340
|
+
value_type, PrimitiveTypeAnnotation
|
|
341
|
+
):
|
|
342
|
+
return (
|
|
343
|
+
PRIMITIVE_TYPE_MAP[target_type.our_type.constrainee]
|
|
344
|
+
== value_type.a_type
|
|
345
|
+
)
|
|
346
|
+
else:
|
|
347
|
+
# NOTE (mristin, 2021-12-25):
|
|
348
|
+
# We assume the assignments of constrained primitives to be co-variant.
|
|
349
|
+
if (
|
|
350
|
+
isinstance(value_type, OurTypeAnnotation)
|
|
351
|
+
and isinstance(value_type.our_type, _types.ConstrainedPrimitive)
|
|
352
|
+
and target_type.our_type.constrainee
|
|
353
|
+
== value_type.our_type.constrainee
|
|
354
|
+
):
|
|
355
|
+
return (
|
|
356
|
+
target_type.our_type is value_type.our_type
|
|
357
|
+
or id(value_type.our_type)
|
|
358
|
+
in target_type.our_type.descendant_id_set
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
elif isinstance(target_type.our_type, _types.Class):
|
|
364
|
+
if not (
|
|
365
|
+
isinstance(value_type, OurTypeAnnotation)
|
|
366
|
+
and isinstance(value_type.our_type, _types.Class)
|
|
367
|
+
):
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
# NOTE (mristin, 2021-12-25):
|
|
371
|
+
# We assume the assignment to be co-variant. Either the target type and
|
|
372
|
+
# the value type are equal *or* the value type is a descendant of the
|
|
373
|
+
# target type.
|
|
374
|
+
|
|
375
|
+
return target_type.our_type is value_type.our_type or (
|
|
376
|
+
id(value_type.our_type) in target_type.our_type.descendant_id_set
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
elif isinstance(target_type, VerificationTypeAnnotation):
|
|
380
|
+
if not isinstance(value_type, VerificationTypeAnnotation):
|
|
381
|
+
return False
|
|
382
|
+
else:
|
|
383
|
+
return target_type.func is value_type.func
|
|
384
|
+
|
|
385
|
+
elif isinstance(target_type, BuiltinFunctionTypeAnnotation):
|
|
386
|
+
if not isinstance(value_type, BuiltinFunctionTypeAnnotation):
|
|
387
|
+
return False
|
|
388
|
+
else:
|
|
389
|
+
return target_type.func is value_type.func
|
|
390
|
+
|
|
391
|
+
elif isinstance(target_type, MethodTypeAnnotation):
|
|
392
|
+
if not isinstance(value_type, MethodTypeAnnotation):
|
|
393
|
+
return False
|
|
394
|
+
else:
|
|
395
|
+
return target_type.method is value_type.method
|
|
396
|
+
|
|
397
|
+
elif isinstance(target_type, ListTypeAnnotation):
|
|
398
|
+
if not isinstance(value_type, ListTypeAnnotation):
|
|
399
|
+
return False
|
|
400
|
+
else:
|
|
401
|
+
# NOTE (mristin, 2021-12-25):
|
|
402
|
+
# We assume the lists to be invariant. This is necessary for code generation
|
|
403
|
+
# in implementation targets such as C++ and Golang.
|
|
404
|
+
return _type_annotations_equal(target_type.items, value_type.items)
|
|
405
|
+
|
|
406
|
+
elif isinstance(target_type, SetTypeAnnotation):
|
|
407
|
+
if not isinstance(value_type, SetTypeAnnotation):
|
|
408
|
+
return False
|
|
409
|
+
else:
|
|
410
|
+
# NOTE (mristin, 2021-12-25):
|
|
411
|
+
# We assume the sets to be invariant. This is necessary for code generation
|
|
412
|
+
# in implementation targets such as C++ and Golang.
|
|
413
|
+
return _type_annotations_equal(target_type.items, value_type.items)
|
|
414
|
+
|
|
415
|
+
elif isinstance(target_type, OptionalTypeAnnotation):
|
|
416
|
+
# NOTE (mristin, 2021-12-25):
|
|
417
|
+
# We can always assign a non-optional to an optional.
|
|
418
|
+
if not isinstance(value_type, OptionalTypeAnnotation):
|
|
419
|
+
return _assignable(target_type=target_type.value, value_type=value_type)
|
|
420
|
+
else:
|
|
421
|
+
# NOTE (mristin, 2021-12-25):
|
|
422
|
+
# We assume the optionals to be co-variant.
|
|
423
|
+
return _assignable(
|
|
424
|
+
target_type=target_type.value, value_type=value_type.value
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
elif isinstance(target_type, EnumerationAsTypeTypeAnnotation):
|
|
428
|
+
raise NotImplementedError(
|
|
429
|
+
"(mristin, 2022-02-04): Assigning enumeration-as-type to another "
|
|
430
|
+
"enumeration-as-type is a very niche program logic. As we do not have "
|
|
431
|
+
"a concrete example of such an assignment, we currently ignore this case "
|
|
432
|
+
"in determining whether the assignment makes sense. When you have "
|
|
433
|
+
"a concrete example, please revisit this part of the code."
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
else:
|
|
437
|
+
assert_never(target_type)
|
|
438
|
+
|
|
439
|
+
return False
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
for _types_primitive_type in _types.PrimitiveType:
|
|
443
|
+
assert (
|
|
444
|
+
_types_primitive_type in PRIMITIVE_TYPE_MAP
|
|
445
|
+
), f"All primitive types from _types covered, but: {_types_primitive_type=}"
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def convert_type_annotation(
|
|
449
|
+
type_annotation: _types.TypeAnnotationUnion,
|
|
450
|
+
) -> "TypeAnnotationUnion":
|
|
451
|
+
"""
|
|
452
|
+
Convert from the :py:mod:`aas_core_codegen.intermediate._types`.
|
|
453
|
+
|
|
454
|
+
We can not use the same type annotations as type inference uses a wider set of
|
|
455
|
+
type annotations such as ``LENGTH`` in primitives or enumeration-as-type.
|
|
456
|
+
"""
|
|
457
|
+
if isinstance(type_annotation, _types.PrimitiveTypeAnnotation):
|
|
458
|
+
return PrimitiveTypeAnnotation(
|
|
459
|
+
a_type=PRIMITIVE_TYPE_MAP[type_annotation.a_type]
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
elif isinstance(type_annotation, _types.OurTypeAnnotation):
|
|
463
|
+
return OurTypeAnnotation(our_type=type_annotation.our_type)
|
|
464
|
+
|
|
465
|
+
elif isinstance(type_annotation, _types.ListTypeAnnotation):
|
|
466
|
+
return ListTypeAnnotation(items=convert_type_annotation(type_annotation.items))
|
|
467
|
+
|
|
468
|
+
elif isinstance(type_annotation, _types.OptionalTypeAnnotation):
|
|
469
|
+
return OptionalTypeAnnotation(
|
|
470
|
+
value=convert_type_annotation(type_annotation.value)
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
else:
|
|
474
|
+
assert_never(type_annotation)
|
|
475
|
+
|
|
476
|
+
raise AssertionError("Should not have gotten here")
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
class Environment(DBC):
|
|
480
|
+
"""
|
|
481
|
+
Map names to type annotations for a given scope.
|
|
482
|
+
|
|
483
|
+
We first search in the given scope and then iterate over the ancestor scopes.
|
|
484
|
+
See, for example: https://craftinginterpreters.com/resolving-and-binding.html.
|
|
485
|
+
|
|
486
|
+
The most outer, global, scope is parentless.
|
|
487
|
+
"""
|
|
488
|
+
|
|
489
|
+
def __init__(self, parent: Optional["Environment"]) -> None:
|
|
490
|
+
"""Initialize with the given values."""
|
|
491
|
+
self.parent = parent
|
|
492
|
+
|
|
493
|
+
@property
|
|
494
|
+
@abc.abstractmethod
|
|
495
|
+
def mapping(self) -> Mapping[Identifier, "TypeAnnotationUnion"]:
|
|
496
|
+
"""Retrieve the underlying mapping."""
|
|
497
|
+
raise NotImplementedError()
|
|
498
|
+
|
|
499
|
+
def find(self, identifier: Identifier) -> Optional["TypeAnnotationUnion"]:
|
|
500
|
+
"""
|
|
501
|
+
Search for the type annotation of the given ``identifier``.
|
|
502
|
+
|
|
503
|
+
We search all the way to the most outer scope.
|
|
504
|
+
"""
|
|
505
|
+
type_anno = self.mapping.get(identifier, None)
|
|
506
|
+
if type_anno is not None:
|
|
507
|
+
return type_anno
|
|
508
|
+
|
|
509
|
+
if self.parent is not None:
|
|
510
|
+
return self.parent.find(identifier)
|
|
511
|
+
|
|
512
|
+
return None
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
class ImmutableEnvironment(Environment):
|
|
516
|
+
"""
|
|
517
|
+
Map immutably names to type annotations for a given scope.
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
def __init__(
|
|
521
|
+
self,
|
|
522
|
+
mapping: Mapping[Identifier, "TypeAnnotationUnion"],
|
|
523
|
+
parent: Optional["Environment"] = None,
|
|
524
|
+
) -> None:
|
|
525
|
+
self._mapping = mapping
|
|
526
|
+
|
|
527
|
+
Environment.__init__(self, parent)
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def mapping(self) -> Mapping[Identifier, "TypeAnnotationUnion"]:
|
|
531
|
+
"""Retrieve the underlying mapping."""
|
|
532
|
+
return self._mapping
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class MutableEnvironment(Environment):
|
|
536
|
+
"""
|
|
537
|
+
Map names to type annotations for a given scope and allow mutations.
|
|
538
|
+
"""
|
|
539
|
+
|
|
540
|
+
def __init__(self, parent: Optional["Environment"] = None) -> None:
|
|
541
|
+
self._mapping = (
|
|
542
|
+
dict()
|
|
543
|
+
) # type: MutableMapping[Identifier, "TypeAnnotationUnion"]
|
|
544
|
+
|
|
545
|
+
Environment.__init__(self, parent)
|
|
546
|
+
|
|
547
|
+
@property
|
|
548
|
+
def mapping(self) -> Mapping[Identifier, "TypeAnnotationUnion"]:
|
|
549
|
+
"""Retrieve the underlying mapping."""
|
|
550
|
+
return self._mapping
|
|
551
|
+
|
|
552
|
+
def set(
|
|
553
|
+
self, identifier: Identifier, type_annotation: "TypeAnnotationUnion"
|
|
554
|
+
) -> None:
|
|
555
|
+
"""Set the ``type_annotation`` for the given ``identifier``."""
|
|
556
|
+
self._mapping[identifier] = type_annotation
|
|
557
|
+
|
|
558
|
+
def remove(self, identifier: Identifier) -> None:
|
|
559
|
+
"""
|
|
560
|
+
Remove the entry in the environment for the ``identifier``.
|
|
561
|
+
|
|
562
|
+
For example, you need to do this if you have temporary scopes such as generator
|
|
563
|
+
expressions where a long linked list of environments would be neither
|
|
564
|
+
performant nor readable during the debugging.
|
|
565
|
+
"""
|
|
566
|
+
del self._mapping[identifier]
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
class _Canonicalizer(parse_tree.RestrictedTransformer[str]):
|
|
570
|
+
"""Represent the nodes as canonical strings so that they can be used in look-ups."""
|
|
571
|
+
|
|
572
|
+
#: Track of the canonical representations
|
|
573
|
+
representation_map: Final[MutableMapping[parse_tree.Node, str]]
|
|
574
|
+
|
|
575
|
+
def __init__(self) -> None:
|
|
576
|
+
"""Initialize with the given values."""
|
|
577
|
+
self.representation_map = dict()
|
|
578
|
+
|
|
579
|
+
@ensure(lambda self, node: node in self.representation_map)
|
|
580
|
+
def transform(self, node: parse_tree.Node) -> str:
|
|
581
|
+
return super().transform(node)
|
|
582
|
+
|
|
583
|
+
@staticmethod
|
|
584
|
+
def _needs_no_brackets(node: parse_tree.Node) -> bool:
|
|
585
|
+
"""
|
|
586
|
+
Check if the representation needs brackets for unambiguity.
|
|
587
|
+
|
|
588
|
+
While we could always put brackets, they harm readability in later debugging, so
|
|
589
|
+
we try to make the representation as readable as possible.
|
|
590
|
+
"""
|
|
591
|
+
return isinstance(
|
|
592
|
+
node,
|
|
593
|
+
(
|
|
594
|
+
parse_tree.Member,
|
|
595
|
+
parse_tree.MethodCall,
|
|
596
|
+
parse_tree.Name,
|
|
597
|
+
parse_tree.FunctionCall,
|
|
598
|
+
parse_tree.Constant,
|
|
599
|
+
parse_tree.JoinedStr,
|
|
600
|
+
parse_tree.Any,
|
|
601
|
+
parse_tree.All,
|
|
602
|
+
),
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
def transform_member(self, node: parse_tree.Member) -> str:
|
|
606
|
+
instance_repr = self.transform(node.instance)
|
|
607
|
+
|
|
608
|
+
if _Canonicalizer._needs_no_brackets(node.instance):
|
|
609
|
+
result = f"{instance_repr}.{node.name}"
|
|
610
|
+
else:
|
|
611
|
+
result = f"({instance_repr}).{node.name}"
|
|
612
|
+
|
|
613
|
+
self.representation_map[node] = result
|
|
614
|
+
return result
|
|
615
|
+
|
|
616
|
+
def transform_index(self, node: parse_tree.Index) -> str:
|
|
617
|
+
collection_repr = self.transform(node.collection)
|
|
618
|
+
index_repr = self.transform(node.index)
|
|
619
|
+
|
|
620
|
+
if _Canonicalizer._needs_no_brackets(node.collection):
|
|
621
|
+
result = f"{collection_repr}[{index_repr}]"
|
|
622
|
+
else:
|
|
623
|
+
result = f"({collection_repr})[{index_repr}]"
|
|
624
|
+
|
|
625
|
+
self.representation_map[node] = result
|
|
626
|
+
return result
|
|
627
|
+
|
|
628
|
+
def transform_comparison(self, node: parse_tree.Comparison) -> str:
|
|
629
|
+
left = self.transform(node.left)
|
|
630
|
+
if not _Canonicalizer._needs_no_brackets(node.left):
|
|
631
|
+
left = f"({left})"
|
|
632
|
+
|
|
633
|
+
right = self.transform(node.right)
|
|
634
|
+
if not _Canonicalizer._needs_no_brackets(node.right):
|
|
635
|
+
right = f"({right})"
|
|
636
|
+
|
|
637
|
+
result = f"{left} {node.op.value} {right}"
|
|
638
|
+
self.representation_map[node] = result
|
|
639
|
+
return result
|
|
640
|
+
|
|
641
|
+
def transform_is_in(self, node: parse_tree.IsIn) -> str:
|
|
642
|
+
member = self.transform(node.member)
|
|
643
|
+
if not _Canonicalizer._needs_no_brackets(node.member):
|
|
644
|
+
member = f"({member})"
|
|
645
|
+
|
|
646
|
+
container = self.transform(node.container)
|
|
647
|
+
if not _Canonicalizer._needs_no_brackets(node.container):
|
|
648
|
+
container = f"({container})"
|
|
649
|
+
|
|
650
|
+
result = f"{member} in {container}"
|
|
651
|
+
self.representation_map[node] = result
|
|
652
|
+
return result
|
|
653
|
+
|
|
654
|
+
def transform_implication(self, node: parse_tree.Implication) -> str:
|
|
655
|
+
antecedent = self.transform(node.antecedent)
|
|
656
|
+
if not _Canonicalizer._needs_no_brackets(node.antecedent):
|
|
657
|
+
antecedent = f"({antecedent})"
|
|
658
|
+
|
|
659
|
+
consequent = self.transform(node.consequent)
|
|
660
|
+
if not _Canonicalizer._needs_no_brackets(node.consequent):
|
|
661
|
+
consequent = f"({consequent})"
|
|
662
|
+
|
|
663
|
+
result = f"{antecedent} ⇒ {consequent}"
|
|
664
|
+
self.representation_map[node] = result
|
|
665
|
+
return result
|
|
666
|
+
|
|
667
|
+
def transform_method_call(self, node: parse_tree.MethodCall) -> str:
|
|
668
|
+
member = self.transform(node.member)
|
|
669
|
+
|
|
670
|
+
args = [self.transform(arg) for arg in node.args]
|
|
671
|
+
|
|
672
|
+
args_joined = ", ".join(args)
|
|
673
|
+
result = f"{member}({args_joined})"
|
|
674
|
+
self.representation_map[node] = result
|
|
675
|
+
return result
|
|
676
|
+
|
|
677
|
+
def transform_function_call(self, node: parse_tree.FunctionCall) -> str:
|
|
678
|
+
name = self.transform(node.name)
|
|
679
|
+
|
|
680
|
+
args = [self.transform(arg) for arg in node.args]
|
|
681
|
+
|
|
682
|
+
args_joined = ", ".join(args)
|
|
683
|
+
result = f"{name}({args_joined})"
|
|
684
|
+
self.representation_map[node] = result
|
|
685
|
+
return result
|
|
686
|
+
|
|
687
|
+
def transform_constant(self, node: parse_tree.Constant) -> str:
|
|
688
|
+
result = repr(node.value)
|
|
689
|
+
self.representation_map[node] = result
|
|
690
|
+
return result
|
|
691
|
+
|
|
692
|
+
def transform_is_none(self, node: parse_tree.IsNone) -> str:
|
|
693
|
+
value = self.transform(node.value)
|
|
694
|
+
if not _Canonicalizer._needs_no_brackets(node.value):
|
|
695
|
+
value = f"({value})"
|
|
696
|
+
|
|
697
|
+
result = f"{value} is None"
|
|
698
|
+
self.representation_map[node] = result
|
|
699
|
+
return result
|
|
700
|
+
|
|
701
|
+
def transform_is_not_none(self, node: parse_tree.IsNotNone) -> str:
|
|
702
|
+
value = self.transform(node.value)
|
|
703
|
+
if not _Canonicalizer._needs_no_brackets(node.value):
|
|
704
|
+
value = f"({value})"
|
|
705
|
+
|
|
706
|
+
result = f"{value} is not None"
|
|
707
|
+
self.representation_map[node] = result
|
|
708
|
+
return result
|
|
709
|
+
|
|
710
|
+
def transform_name(self, node: parse_tree.Name) -> str:
|
|
711
|
+
result = node.identifier
|
|
712
|
+
self.representation_map[node] = result
|
|
713
|
+
return result
|
|
714
|
+
|
|
715
|
+
def transform_not(self, node: parse_tree.Not) -> str:
|
|
716
|
+
operand_repr = self.transform(node.operand)
|
|
717
|
+
|
|
718
|
+
if not _Canonicalizer._needs_no_brackets(node):
|
|
719
|
+
operand_repr = f"({operand_repr})"
|
|
720
|
+
|
|
721
|
+
result = f"not {operand_repr}"
|
|
722
|
+
self.representation_map[node] = result
|
|
723
|
+
return result
|
|
724
|
+
|
|
725
|
+
def transform_and(self, node: parse_tree.And) -> str:
|
|
726
|
+
values = [] # type: List[str]
|
|
727
|
+
|
|
728
|
+
for value_node in node.values:
|
|
729
|
+
value = self.transform(value_node)
|
|
730
|
+
if not _Canonicalizer._needs_no_brackets(value_node):
|
|
731
|
+
value = f"({value})"
|
|
732
|
+
|
|
733
|
+
values.append(value)
|
|
734
|
+
|
|
735
|
+
result = " and ".join(values)
|
|
736
|
+
self.representation_map[node] = result
|
|
737
|
+
return result
|
|
738
|
+
|
|
739
|
+
def transform_or(self, node: parse_tree.Or) -> str:
|
|
740
|
+
values = [] # type: List[str]
|
|
741
|
+
for value_node in node.values:
|
|
742
|
+
value = self.transform(value_node)
|
|
743
|
+
if not _Canonicalizer._needs_no_brackets(value_node):
|
|
744
|
+
value = f"({value})"
|
|
745
|
+
|
|
746
|
+
values.append(value)
|
|
747
|
+
|
|
748
|
+
result = " or ".join(values)
|
|
749
|
+
self.representation_map[node] = result
|
|
750
|
+
return result
|
|
751
|
+
|
|
752
|
+
def _transform_add_or_sub(self, node: Union[parse_tree.Add, parse_tree.Sub]) -> str:
|
|
753
|
+
left_repr = self.transform(node.left)
|
|
754
|
+
if not _Canonicalizer._needs_no_brackets(node.left):
|
|
755
|
+
left_repr = f"({left_repr})"
|
|
756
|
+
|
|
757
|
+
right_repr = self.transform(node.right)
|
|
758
|
+
if not _Canonicalizer._needs_no_brackets(node.right):
|
|
759
|
+
right_repr = f"({right_repr})"
|
|
760
|
+
|
|
761
|
+
result: str
|
|
762
|
+
if isinstance(node, parse_tree.Add):
|
|
763
|
+
result = f"{left_repr} + {right_repr}"
|
|
764
|
+
elif isinstance(node, parse_tree.Sub):
|
|
765
|
+
result = f"{left_repr} - {right_repr}"
|
|
766
|
+
else:
|
|
767
|
+
assert_never(node)
|
|
768
|
+
|
|
769
|
+
self.representation_map[node] = result
|
|
770
|
+
return result
|
|
771
|
+
|
|
772
|
+
def transform_add(self, node: parse_tree.Add) -> str:
|
|
773
|
+
return self._transform_add_or_sub(node)
|
|
774
|
+
|
|
775
|
+
def transform_sub(self, node: parse_tree.Sub) -> str:
|
|
776
|
+
return self._transform_add_or_sub(node)
|
|
777
|
+
|
|
778
|
+
def transform_formatted_value(self, node: parse_tree.FormattedValue) -> str:
|
|
779
|
+
result = self.transform(node.value)
|
|
780
|
+
self.representation_map[node] = result
|
|
781
|
+
return result
|
|
782
|
+
|
|
783
|
+
def transform_joined_str(self, node: parse_tree.JoinedStr) -> str:
|
|
784
|
+
parts = [] # type: List[str]
|
|
785
|
+
for value in node.values:
|
|
786
|
+
if isinstance(value, str):
|
|
787
|
+
parts.append(repr(value))
|
|
788
|
+
elif isinstance(value, parse_tree.FormattedValue):
|
|
789
|
+
transformed_value = self.transform(value)
|
|
790
|
+
parts.append(f"{{{transformed_value}}}")
|
|
791
|
+
else:
|
|
792
|
+
assert_never(value)
|
|
793
|
+
|
|
794
|
+
result = "".join(parts)
|
|
795
|
+
self.representation_map[node] = result
|
|
796
|
+
return result
|
|
797
|
+
|
|
798
|
+
def transform_for_each(self, node: parse_tree.ForEach) -> str:
|
|
799
|
+
variable = self.transform(node.variable)
|
|
800
|
+
iteration = self.transform(node.iteration)
|
|
801
|
+
if not _Canonicalizer._needs_no_brackets(node.iteration):
|
|
802
|
+
iteration = f"({iteration})"
|
|
803
|
+
|
|
804
|
+
result = f"for {variable} in {iteration}"
|
|
805
|
+
self.representation_map[node] = result
|
|
806
|
+
return result
|
|
807
|
+
|
|
808
|
+
def transform_for_range(self, node: parse_tree.ForRange) -> str:
|
|
809
|
+
variable = self.transform(node.variable)
|
|
810
|
+
start = self.transform(node.start)
|
|
811
|
+
end = self.transform(node.end)
|
|
812
|
+
|
|
813
|
+
result = f"for {variable} in range({start}, {end})"
|
|
814
|
+
self.representation_map[node] = result
|
|
815
|
+
return result
|
|
816
|
+
|
|
817
|
+
def _transform_any_or_all(self, node: Union[parse_tree.Any, parse_tree.All]) -> str:
|
|
818
|
+
generator = self.transform(node.generator)
|
|
819
|
+
|
|
820
|
+
condition = self.transform(node.condition)
|
|
821
|
+
if not _Canonicalizer._needs_no_brackets(node.condition):
|
|
822
|
+
condition = f"({condition})"
|
|
823
|
+
|
|
824
|
+
result: str
|
|
825
|
+
|
|
826
|
+
if isinstance(node, parse_tree.Any):
|
|
827
|
+
result = f"any({condition} {generator})"
|
|
828
|
+
elif isinstance(node, parse_tree.All):
|
|
829
|
+
result = f"all({condition} {generator})"
|
|
830
|
+
else:
|
|
831
|
+
assert_never(node)
|
|
832
|
+
|
|
833
|
+
self.representation_map[node] = result
|
|
834
|
+
return result
|
|
835
|
+
|
|
836
|
+
def transform_any(self, node: parse_tree.Any) -> str:
|
|
837
|
+
return self._transform_any_or_all(node)
|
|
838
|
+
|
|
839
|
+
def transform_all(self, node: parse_tree.All) -> str:
|
|
840
|
+
return self._transform_any_or_all(node)
|
|
841
|
+
|
|
842
|
+
def transform_assignment(self, node: parse_tree.Assignment) -> str:
|
|
843
|
+
target = self.transform(node.target)
|
|
844
|
+
if not _Canonicalizer._needs_no_brackets(node.target):
|
|
845
|
+
target = f"({target})"
|
|
846
|
+
|
|
847
|
+
# NOTE (mristin, 2022-06-17):
|
|
848
|
+
# Nested assignments are not possible in Python, but who knows where our
|
|
849
|
+
# intermediate representation will take us. Therefore, we handle this edge case
|
|
850
|
+
# even though it seems nonsensical at the moment.
|
|
851
|
+
value = self.transform(node.value)
|
|
852
|
+
if isinstance(node.value, parse_tree.Assignment):
|
|
853
|
+
value = f"({value})"
|
|
854
|
+
|
|
855
|
+
result = f"{target} = {value}"
|
|
856
|
+
|
|
857
|
+
self.representation_map[node] = result
|
|
858
|
+
return result
|
|
859
|
+
|
|
860
|
+
def transform_return(self, node: parse_tree.Return) -> str:
|
|
861
|
+
if node.value is not None:
|
|
862
|
+
value = self.transform(node.value)
|
|
863
|
+
|
|
864
|
+
# NOTE (mristin, 2022-06-17):
|
|
865
|
+
# Nested returns are not possible in Python, but who knows where our
|
|
866
|
+
# intermediate representation will take us. Therefore, we handle
|
|
867
|
+
# this edge case even though it seems nonsensical at the moment.
|
|
868
|
+
if isinstance(node.value, parse_tree.Return):
|
|
869
|
+
value = f"({value})"
|
|
870
|
+
|
|
871
|
+
result = f"return {value}"
|
|
872
|
+
else:
|
|
873
|
+
result = "return"
|
|
874
|
+
|
|
875
|
+
self.representation_map[node] = result
|
|
876
|
+
return result
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
class _CountingMap:
|
|
880
|
+
"""Provide a map to track multiple counters."""
|
|
881
|
+
|
|
882
|
+
def __init__(self) -> None:
|
|
883
|
+
self._counts = dict() # type: MutableMapping[str, int]
|
|
884
|
+
|
|
885
|
+
def increment(self, key: str) -> None:
|
|
886
|
+
"""Increment the counter for the ``key``."""
|
|
887
|
+
count = self._counts.get(key, None)
|
|
888
|
+
if count is None:
|
|
889
|
+
self._counts[key] = 1
|
|
890
|
+
else:
|
|
891
|
+
self._counts[key] = count + 1
|
|
892
|
+
|
|
893
|
+
@ensure(lambda self, key, result: not result or self.count(key) > 0)
|
|
894
|
+
@ensure(lambda self, key, result: result or self.count(key) == 0)
|
|
895
|
+
def at_least_once(self, key: str) -> bool:
|
|
896
|
+
"""Return ``True`` if the ``key`` is tracked and the count is at least 1."""
|
|
897
|
+
count = self.count(key)
|
|
898
|
+
return count >= 1
|
|
899
|
+
|
|
900
|
+
@ensure(lambda result: result >= 0)
|
|
901
|
+
def count(self, key: str) -> int:
|
|
902
|
+
"""Return the number of stacked ``key``'s."""
|
|
903
|
+
result = self._counts.get(key, None)
|
|
904
|
+
return 0 if result is None else result
|
|
905
|
+
|
|
906
|
+
# fmt: off
|
|
907
|
+
@require(
|
|
908
|
+
lambda self, key:
|
|
909
|
+
self.at_least_once(key),
|
|
910
|
+
"Can not decrement past 1"
|
|
911
|
+
)
|
|
912
|
+
# fmt: on
|
|
913
|
+
def decrement(self, key: str) -> None:
|
|
914
|
+
"""Decrement the counter for the ``key``."""
|
|
915
|
+
count = self._counts.get(key, None)
|
|
916
|
+
|
|
917
|
+
if count is None or count == 0:
|
|
918
|
+
raise AssertionError(f"Unexpected count == 0 for key {key!r}")
|
|
919
|
+
elif count < 0:
|
|
920
|
+
raise AssertionError(f"Unexpected count < 0 for key {key!r}")
|
|
921
|
+
elif count == 1:
|
|
922
|
+
del self._counts[key]
|
|
923
|
+
else:
|
|
924
|
+
self._counts[key] = count - 1
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
TypeAnnotationUnion = Union[
|
|
928
|
+
PrimitiveTypeAnnotation,
|
|
929
|
+
OurTypeAnnotation,
|
|
930
|
+
VerificationTypeAnnotation,
|
|
931
|
+
BuiltinFunctionTypeAnnotation,
|
|
932
|
+
MethodTypeAnnotation,
|
|
933
|
+
ListTypeAnnotation,
|
|
934
|
+
SetTypeAnnotation,
|
|
935
|
+
OptionalTypeAnnotation,
|
|
936
|
+
EnumerationAsTypeTypeAnnotation,
|
|
937
|
+
]
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
class _Inferrer(parse_tree.RestrictedTransformer[Optional["TypeAnnotationUnion"]]):
|
|
941
|
+
"""
|
|
942
|
+
Infer the types of the given parse tree.
|
|
943
|
+
|
|
944
|
+
Since we also handle non-nullness, you need to pre-compute the canonical
|
|
945
|
+
representation of the nodes that you want to infer the types for. To that end,
|
|
946
|
+
use :class:`~CanonicalRepresenter`.
|
|
947
|
+
"""
|
|
948
|
+
|
|
949
|
+
#: Track of the inferred types
|
|
950
|
+
type_map: Final[MutableMapping[parse_tree.Node, "TypeAnnotationUnion"]]
|
|
951
|
+
|
|
952
|
+
#: Errors encountered during the inference
|
|
953
|
+
errors: Final[List[Error]]
|
|
954
|
+
|
|
955
|
+
def __init__(
|
|
956
|
+
self,
|
|
957
|
+
environment: "Environment",
|
|
958
|
+
representation_map: Mapping[parse_tree.Node, str],
|
|
959
|
+
) -> None:
|
|
960
|
+
"""Initialize with the given values."""
|
|
961
|
+
# We need to create our own child environment so that we can introduce new
|
|
962
|
+
# entries without affecting the variables from the outer scopes.
|
|
963
|
+
self._environment = MutableEnvironment(parent=environment)
|
|
964
|
+
|
|
965
|
+
self._representation_map = representation_map
|
|
966
|
+
|
|
967
|
+
# NOTE (mristin, 2022-06-17):
|
|
968
|
+
# We need to keep track of the expressions that can be assumed to be non-null.
|
|
969
|
+
# This member is stateful! It will constantly change, depending on the position
|
|
970
|
+
# of the iteration through the tree.
|
|
971
|
+
#
|
|
972
|
+
# We use canonical representation from ``representation_map`` to associate
|
|
973
|
+
# non-null assumptions with the expressions.
|
|
974
|
+
self._non_null = _CountingMap()
|
|
975
|
+
|
|
976
|
+
self.type_map = dict()
|
|
977
|
+
self.errors = []
|
|
978
|
+
|
|
979
|
+
def _strip_optional_if_non_null(
|
|
980
|
+
self, node: parse_tree.Node, type_annotation: "TypeAnnotationUnion"
|
|
981
|
+
) -> "TypeAnnotationUnion":
|
|
982
|
+
"""
|
|
983
|
+
Remove ``Optional`` from the ``type_annotation`` if the ``node`` is non-null.
|
|
984
|
+
|
|
985
|
+
The ``type_annotation`` refers to the type inferred for the ``node``.
|
|
986
|
+
|
|
987
|
+
We keep track of the non-nullness over the iteration in :attr:`._non_null`.
|
|
988
|
+
Using the canonical representation of the ``node``, we can check whether
|
|
989
|
+
the type of the ``node`` is non-null.
|
|
990
|
+
"""
|
|
991
|
+
if not isinstance(type_annotation, OptionalTypeAnnotation):
|
|
992
|
+
return type_annotation
|
|
993
|
+
|
|
994
|
+
canonical_repr = self._representation_map[node]
|
|
995
|
+
if self._non_null.at_least_once(canonical_repr):
|
|
996
|
+
return type_annotation.value
|
|
997
|
+
|
|
998
|
+
return type_annotation
|
|
999
|
+
|
|
1000
|
+
@ensure(lambda self, result: not (result is None) or len(self.errors) > 0)
|
|
1001
|
+
def transform(self, node: parse_tree.Node) -> Optional["TypeAnnotationUnion"]:
|
|
1002
|
+
# NOTE (mristin, 2022-06-17):
|
|
1003
|
+
# We can not write the following as the pre-condition as it would break
|
|
1004
|
+
# behavioral subtyping since the parent class expects no pre-conditions.
|
|
1005
|
+
#
|
|
1006
|
+
# However, in this case, we check that the supplied dependency,
|
|
1007
|
+
# ``representation_map``, is correct.
|
|
1008
|
+
assert node in self._representation_map, (
|
|
1009
|
+
f"The node {parse_tree.dump(node)} at 0x{id(node):x} could not be found "
|
|
1010
|
+
f"in the supplied representation_map."
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
return super().transform(node)
|
|
1014
|
+
|
|
1015
|
+
def transform_member(
|
|
1016
|
+
self, node: parse_tree.Member
|
|
1017
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1018
|
+
instance_type = self.transform(node.instance)
|
|
1019
|
+
|
|
1020
|
+
if instance_type is None:
|
|
1021
|
+
return None
|
|
1022
|
+
|
|
1023
|
+
if isinstance(instance_type, OurTypeAnnotation):
|
|
1024
|
+
if not isinstance(instance_type.our_type, _types.Class):
|
|
1025
|
+
self.errors.append(
|
|
1026
|
+
Error(
|
|
1027
|
+
node.instance.original_node,
|
|
1028
|
+
f"Expected an instance type as our type to be a class, "
|
|
1029
|
+
f"but got: {instance_type.our_type}",
|
|
1030
|
+
)
|
|
1031
|
+
)
|
|
1032
|
+
return None
|
|
1033
|
+
|
|
1034
|
+
cls = instance_type.our_type
|
|
1035
|
+
assert isinstance(cls, _types.Class)
|
|
1036
|
+
|
|
1037
|
+
prop = cls.properties_by_name.get(node.name, None)
|
|
1038
|
+
if prop is not None:
|
|
1039
|
+
result = convert_type_annotation(prop.type_annotation)
|
|
1040
|
+
|
|
1041
|
+
result = self._strip_optional_if_non_null(
|
|
1042
|
+
node=node, type_annotation=result
|
|
1043
|
+
)
|
|
1044
|
+
self.type_map[node] = result
|
|
1045
|
+
return result
|
|
1046
|
+
|
|
1047
|
+
method = cls.methods_by_name.get(node.name, None)
|
|
1048
|
+
if method is not None:
|
|
1049
|
+
result = MethodTypeAnnotation(method=method)
|
|
1050
|
+
|
|
1051
|
+
result = self._strip_optional_if_non_null(
|
|
1052
|
+
node=node, type_annotation=result
|
|
1053
|
+
)
|
|
1054
|
+
self.type_map[node] = result
|
|
1055
|
+
return result
|
|
1056
|
+
|
|
1057
|
+
self.errors.append(
|
|
1058
|
+
Error(
|
|
1059
|
+
node.original_node,
|
|
1060
|
+
f"The member {node.name!r} could not be found "
|
|
1061
|
+
f"in the class {cls.name!r}",
|
|
1062
|
+
)
|
|
1063
|
+
)
|
|
1064
|
+
|
|
1065
|
+
elif isinstance(instance_type, EnumerationAsTypeTypeAnnotation):
|
|
1066
|
+
enumeration = instance_type.enumeration
|
|
1067
|
+
literal = enumeration.literals_by_name.get(node.name, None)
|
|
1068
|
+
if literal is not None:
|
|
1069
|
+
result = OurTypeAnnotation(our_type=enumeration)
|
|
1070
|
+
|
|
1071
|
+
result = self._strip_optional_if_non_null(
|
|
1072
|
+
node=node, type_annotation=result
|
|
1073
|
+
)
|
|
1074
|
+
self.type_map[node] = result
|
|
1075
|
+
return result
|
|
1076
|
+
|
|
1077
|
+
self.errors.append(
|
|
1078
|
+
Error(
|
|
1079
|
+
node.original_node,
|
|
1080
|
+
f"The literal {node.name!r} could not be found "
|
|
1081
|
+
f"in the enumeration {enumeration.name!r}",
|
|
1082
|
+
)
|
|
1083
|
+
)
|
|
1084
|
+
else:
|
|
1085
|
+
if isinstance(instance_type, OptionalTypeAnnotation):
|
|
1086
|
+
self.errors.append(
|
|
1087
|
+
Error(
|
|
1088
|
+
node.instance.original_node,
|
|
1089
|
+
f"Expected an instance type to be a non-None, either "
|
|
1090
|
+
f"an enumeration-as-type or our type, "
|
|
1091
|
+
f"but inferred an Optional: {instance_type}",
|
|
1092
|
+
)
|
|
1093
|
+
)
|
|
1094
|
+
else:
|
|
1095
|
+
self.errors.append(
|
|
1096
|
+
Error(
|
|
1097
|
+
node.instance.original_node,
|
|
1098
|
+
f"Expected an instance type to be either "
|
|
1099
|
+
f"an enumeration-as-type or our type, "
|
|
1100
|
+
f"but inferred: {instance_type}",
|
|
1101
|
+
)
|
|
1102
|
+
)
|
|
1103
|
+
return None
|
|
1104
|
+
|
|
1105
|
+
return None
|
|
1106
|
+
|
|
1107
|
+
def transform_index(
|
|
1108
|
+
self, node: parse_tree.Index
|
|
1109
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1110
|
+
collection_type = self.transform(node.collection)
|
|
1111
|
+
if collection_type is None:
|
|
1112
|
+
return None
|
|
1113
|
+
|
|
1114
|
+
index_type = self.transform(node.index)
|
|
1115
|
+
if index_type is None:
|
|
1116
|
+
return None
|
|
1117
|
+
|
|
1118
|
+
success = True
|
|
1119
|
+
|
|
1120
|
+
if isinstance(collection_type, OptionalTypeAnnotation):
|
|
1121
|
+
self.errors.append(
|
|
1122
|
+
Error(
|
|
1123
|
+
node.collection.original_node,
|
|
1124
|
+
f"Expected the collection to be a non-None, "
|
|
1125
|
+
f"but got: {collection_type}",
|
|
1126
|
+
)
|
|
1127
|
+
)
|
|
1128
|
+
success = False
|
|
1129
|
+
|
|
1130
|
+
if isinstance(index_type, OptionalTypeAnnotation):
|
|
1131
|
+
self.errors.append(
|
|
1132
|
+
Error(
|
|
1133
|
+
node.index.original_node,
|
|
1134
|
+
f"Expected the index to be a non-None, " f"but got: {index_type}",
|
|
1135
|
+
)
|
|
1136
|
+
)
|
|
1137
|
+
success = False
|
|
1138
|
+
|
|
1139
|
+
if not success:
|
|
1140
|
+
return None
|
|
1141
|
+
|
|
1142
|
+
if not isinstance(collection_type, ListTypeAnnotation):
|
|
1143
|
+
self.errors.append(
|
|
1144
|
+
Error(
|
|
1145
|
+
node.collection.original_node,
|
|
1146
|
+
f"Expected an index access on a list, but got: {collection_type}",
|
|
1147
|
+
)
|
|
1148
|
+
)
|
|
1149
|
+
return None
|
|
1150
|
+
|
|
1151
|
+
if not (
|
|
1152
|
+
isinstance(index_type, PrimitiveTypeAnnotation)
|
|
1153
|
+
and index_type.a_type in (PrimitiveType.INT, PrimitiveType.LENGTH)
|
|
1154
|
+
):
|
|
1155
|
+
self.errors.append(
|
|
1156
|
+
Error(
|
|
1157
|
+
node.collection.original_node,
|
|
1158
|
+
f"Expected the index to be an integer, but got: {index_type}",
|
|
1159
|
+
)
|
|
1160
|
+
)
|
|
1161
|
+
return None
|
|
1162
|
+
|
|
1163
|
+
result = collection_type.items
|
|
1164
|
+
self.type_map[node] = result
|
|
1165
|
+
return result
|
|
1166
|
+
|
|
1167
|
+
def transform_comparison(
|
|
1168
|
+
self, node: parse_tree.Comparison
|
|
1169
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1170
|
+
# Just recurse to fill ``type_map`` on ``left`` and ``right`` even though we
|
|
1171
|
+
# know the type in advance
|
|
1172
|
+
|
|
1173
|
+
left_type = self.transform(node.left)
|
|
1174
|
+
if left_type is None:
|
|
1175
|
+
return None
|
|
1176
|
+
|
|
1177
|
+
right_type = self.transform(node.right)
|
|
1178
|
+
if right_type is None:
|
|
1179
|
+
return None
|
|
1180
|
+
|
|
1181
|
+
success = True
|
|
1182
|
+
|
|
1183
|
+
if isinstance(left_type, OptionalTypeAnnotation):
|
|
1184
|
+
self.errors.append(
|
|
1185
|
+
Error(
|
|
1186
|
+
node.left.original_node,
|
|
1187
|
+
f"Expected the left operand to be a non-None, "
|
|
1188
|
+
f"but got: {left_type}",
|
|
1189
|
+
)
|
|
1190
|
+
)
|
|
1191
|
+
success = False
|
|
1192
|
+
|
|
1193
|
+
if isinstance(right_type, OptionalTypeAnnotation):
|
|
1194
|
+
self.errors.append(
|
|
1195
|
+
Error(
|
|
1196
|
+
node.right.original_node,
|
|
1197
|
+
f"Expected the right operand to be a non-None, "
|
|
1198
|
+
f"but got: {right_type}",
|
|
1199
|
+
)
|
|
1200
|
+
)
|
|
1201
|
+
success = False
|
|
1202
|
+
|
|
1203
|
+
if not success:
|
|
1204
|
+
return None
|
|
1205
|
+
|
|
1206
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1207
|
+
self.type_map[node] = result
|
|
1208
|
+
return result
|
|
1209
|
+
|
|
1210
|
+
def transform_is_in(self, node: parse_tree.IsIn) -> Optional["TypeAnnotationUnion"]:
|
|
1211
|
+
# Just recurse to fill ``type_map`` on ``member`` and ``container`` even though
|
|
1212
|
+
# we know the type of the expression in advance.
|
|
1213
|
+
|
|
1214
|
+
member_type = self.transform(node.member)
|
|
1215
|
+
container_type = self.transform(node.container)
|
|
1216
|
+
|
|
1217
|
+
if member_type is None or container_type is None:
|
|
1218
|
+
return None
|
|
1219
|
+
|
|
1220
|
+
# NOTE (mristin, 2023-06-09):
|
|
1221
|
+
# Check that both the member and the container are non-nullables. We already
|
|
1222
|
+
# had bugs related to this, see:
|
|
1223
|
+
# https://github.com/aas-core-works/aas-core-meta/pull/272
|
|
1224
|
+
success = True
|
|
1225
|
+
|
|
1226
|
+
if isinstance(member_type, OptionalTypeAnnotation):
|
|
1227
|
+
self.errors.append(
|
|
1228
|
+
Error(
|
|
1229
|
+
node.member.original_node,
|
|
1230
|
+
f"Expected the member to be a non-None, but got: {member_type}",
|
|
1231
|
+
)
|
|
1232
|
+
)
|
|
1233
|
+
success = False
|
|
1234
|
+
|
|
1235
|
+
if isinstance(container_type, OptionalTypeAnnotation):
|
|
1236
|
+
self.errors.append(
|
|
1237
|
+
Error(
|
|
1238
|
+
node.container.original_node,
|
|
1239
|
+
f"Expected the container to be a non-None, "
|
|
1240
|
+
f"but got: {container_type}",
|
|
1241
|
+
)
|
|
1242
|
+
)
|
|
1243
|
+
success = False
|
|
1244
|
+
|
|
1245
|
+
if not success:
|
|
1246
|
+
return None
|
|
1247
|
+
|
|
1248
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1249
|
+
self.type_map[node] = result
|
|
1250
|
+
return result
|
|
1251
|
+
|
|
1252
|
+
def transform_implication(
|
|
1253
|
+
self, node: parse_tree.Implication
|
|
1254
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1255
|
+
# NOTE (mristin, 2022-06-17):
|
|
1256
|
+
# Just recurse to fill ``type_map`` on ``antecedent`` even though we know the
|
|
1257
|
+
# type in advance
|
|
1258
|
+
|
|
1259
|
+
antecedent_type = self.transform(node.antecedent)
|
|
1260
|
+
if antecedent_type is None:
|
|
1261
|
+
return None
|
|
1262
|
+
|
|
1263
|
+
success = True
|
|
1264
|
+
|
|
1265
|
+
if isinstance(antecedent_type, OptionalTypeAnnotation):
|
|
1266
|
+
self.errors.append(
|
|
1267
|
+
Error(
|
|
1268
|
+
node.antecedent.original_node,
|
|
1269
|
+
f"Expected the antecedent to be a non-None, "
|
|
1270
|
+
f"but got: {antecedent_type}",
|
|
1271
|
+
)
|
|
1272
|
+
)
|
|
1273
|
+
success = False
|
|
1274
|
+
|
|
1275
|
+
# region Recurse into consequent while considering any non-nullness
|
|
1276
|
+
|
|
1277
|
+
# NOTE (mristin, 2022-06-17):
|
|
1278
|
+
# We are very lax here and ignore the fact that calls to methods and functions
|
|
1279
|
+
# can actually alter the value assumed to be non-null, and actually violate
|
|
1280
|
+
# its non-nullness by setting it to null.
|
|
1281
|
+
#
|
|
1282
|
+
# This lack of conservatism works for now. If the bugs related to nullness
|
|
1283
|
+
# start to surface, we should re-think our approach here.
|
|
1284
|
+
|
|
1285
|
+
with contextlib.ExitStack() as exit_stack:
|
|
1286
|
+
if isinstance(node.antecedent, parse_tree.IsNotNone):
|
|
1287
|
+
canonical_repr = self._representation_map[node.antecedent.value]
|
|
1288
|
+
self._non_null.increment(canonical_repr)
|
|
1289
|
+
|
|
1290
|
+
exit_stack.callback(
|
|
1291
|
+
lambda a_canonical_repr=canonical_repr: self._non_null.decrement( # type: ignore
|
|
1292
|
+
a_canonical_repr
|
|
1293
|
+
)
|
|
1294
|
+
)
|
|
1295
|
+
|
|
1296
|
+
elif isinstance(node.antecedent, parse_tree.And):
|
|
1297
|
+
for value in node.antecedent.values:
|
|
1298
|
+
if isinstance(value, parse_tree.IsNotNone):
|
|
1299
|
+
canonical_repr = self._representation_map[value.value]
|
|
1300
|
+
self._non_null.increment(canonical_repr)
|
|
1301
|
+
|
|
1302
|
+
# fmt: off
|
|
1303
|
+
exit_stack.callback(
|
|
1304
|
+
lambda a_canonical_repr=canonical_repr: # type: ignore
|
|
1305
|
+
self._non_null.decrement(a_canonical_repr)
|
|
1306
|
+
)
|
|
1307
|
+
# fmt: on
|
|
1308
|
+
else:
|
|
1309
|
+
# NOTE (mristin, 2022-06-17):
|
|
1310
|
+
# We do not know how to infer any non-nullness in this case.
|
|
1311
|
+
pass
|
|
1312
|
+
|
|
1313
|
+
success = (self.transform(node.consequent) is not None) and success
|
|
1314
|
+
|
|
1315
|
+
if not success:
|
|
1316
|
+
return None
|
|
1317
|
+
|
|
1318
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1319
|
+
self.type_map[node] = result
|
|
1320
|
+
return result
|
|
1321
|
+
|
|
1322
|
+
def transform_method_call(
|
|
1323
|
+
self, node: parse_tree.MethodCall
|
|
1324
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1325
|
+
# Simply recurse to track the type, but we don't care about the arguments
|
|
1326
|
+
failed = False
|
|
1327
|
+
for arg in node.args:
|
|
1328
|
+
arg_type = self.transform(arg)
|
|
1329
|
+
if arg_type is None:
|
|
1330
|
+
failed = True
|
|
1331
|
+
|
|
1332
|
+
member_type = self.transform(node.member)
|
|
1333
|
+
|
|
1334
|
+
if member_type is None:
|
|
1335
|
+
failed = True
|
|
1336
|
+
|
|
1337
|
+
elif isinstance(member_type, OptionalTypeAnnotation):
|
|
1338
|
+
self.errors.append(
|
|
1339
|
+
Error(
|
|
1340
|
+
node.member.original_node,
|
|
1341
|
+
f"Expected the member to be a non-None, " f"but got: {member_type}",
|
|
1342
|
+
)
|
|
1343
|
+
)
|
|
1344
|
+
failed = True
|
|
1345
|
+
else:
|
|
1346
|
+
pass
|
|
1347
|
+
|
|
1348
|
+
if failed:
|
|
1349
|
+
return None
|
|
1350
|
+
|
|
1351
|
+
if not isinstance(member_type, MethodTypeAnnotation):
|
|
1352
|
+
self.errors.append(
|
|
1353
|
+
Error(
|
|
1354
|
+
node.original_node,
|
|
1355
|
+
f"Expected the member in a method call to be a method, "
|
|
1356
|
+
f"but got: {member_type}",
|
|
1357
|
+
)
|
|
1358
|
+
)
|
|
1359
|
+
return None
|
|
1360
|
+
|
|
1361
|
+
result: TypeAnnotationUnion
|
|
1362
|
+
|
|
1363
|
+
if member_type.method.returns is None:
|
|
1364
|
+
result = PrimitiveTypeAnnotation(a_type=PrimitiveType.NONE)
|
|
1365
|
+
else:
|
|
1366
|
+
result = convert_type_annotation(member_type.method.returns)
|
|
1367
|
+
|
|
1368
|
+
result = self._strip_optional_if_non_null(node=node, type_annotation=result)
|
|
1369
|
+
self.type_map[node] = result
|
|
1370
|
+
return result
|
|
1371
|
+
|
|
1372
|
+
def transform_function_call(
|
|
1373
|
+
self, node: parse_tree.FunctionCall
|
|
1374
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1375
|
+
result = None # type: Optional[TypeAnnotationUnion]
|
|
1376
|
+
failed = False
|
|
1377
|
+
|
|
1378
|
+
func_type = self.transform(node.name)
|
|
1379
|
+
if func_type is None:
|
|
1380
|
+
failed = True
|
|
1381
|
+
else:
|
|
1382
|
+
# NOTE (mristin, 2021-12-26):
|
|
1383
|
+
# The verification functions use
|
|
1384
|
+
# :py:mod:`aas_core_codegen.intermediate._types` while the built-in
|
|
1385
|
+
# functions are a construct of
|
|
1386
|
+
# :py:mod:`aas_core_codegen.intermediate.type_inference`.
|
|
1387
|
+
|
|
1388
|
+
if isinstance(func_type, VerificationTypeAnnotation):
|
|
1389
|
+
if func_type.func.returns is not None:
|
|
1390
|
+
result = convert_type_annotation(func_type.func.returns)
|
|
1391
|
+
else:
|
|
1392
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.NONE)
|
|
1393
|
+
|
|
1394
|
+
elif isinstance(func_type, BuiltinFunctionTypeAnnotation):
|
|
1395
|
+
if func_type.func.returns is not None:
|
|
1396
|
+
result = func_type.func.returns
|
|
1397
|
+
else:
|
|
1398
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.NONE)
|
|
1399
|
+
|
|
1400
|
+
elif isinstance(
|
|
1401
|
+
func_type,
|
|
1402
|
+
(
|
|
1403
|
+
PrimitiveTypeAnnotation,
|
|
1404
|
+
OurTypeAnnotation,
|
|
1405
|
+
MethodTypeAnnotation,
|
|
1406
|
+
ListTypeAnnotation,
|
|
1407
|
+
SetTypeAnnotation,
|
|
1408
|
+
OptionalTypeAnnotation,
|
|
1409
|
+
EnumerationAsTypeTypeAnnotation,
|
|
1410
|
+
),
|
|
1411
|
+
):
|
|
1412
|
+
self.errors.append(
|
|
1413
|
+
Error(
|
|
1414
|
+
node.name.original_node,
|
|
1415
|
+
f"Expected the variable {node.name.identifier!r} to be "
|
|
1416
|
+
f"a function, but got {func_type}",
|
|
1417
|
+
)
|
|
1418
|
+
)
|
|
1419
|
+
|
|
1420
|
+
else:
|
|
1421
|
+
assert_never(func_type)
|
|
1422
|
+
|
|
1423
|
+
# NOTE (mristin, 2021-12-26):
|
|
1424
|
+
# Recurse to track the type of arguments. Even if we failed before, we want to
|
|
1425
|
+
# catch the errors in the arguments for better developer experience.
|
|
1426
|
+
#
|
|
1427
|
+
# Mind that we are sloppy here. Theoretically, we could check that arguments in
|
|
1428
|
+
# the call are assignable to the arguments in the function definition and catch
|
|
1429
|
+
# errors in the meta-model at this point. However, we prioritize other features
|
|
1430
|
+
# and leave this check unimplemented for now.
|
|
1431
|
+
|
|
1432
|
+
for arg in node.args:
|
|
1433
|
+
arg_type = self.transform(arg)
|
|
1434
|
+
if arg_type is None:
|
|
1435
|
+
failed = True
|
|
1436
|
+
|
|
1437
|
+
if failed:
|
|
1438
|
+
return None
|
|
1439
|
+
|
|
1440
|
+
assert result is not None
|
|
1441
|
+
|
|
1442
|
+
result = self._strip_optional_if_non_null(node=node, type_annotation=result)
|
|
1443
|
+
self.type_map[node] = result
|
|
1444
|
+
return result
|
|
1445
|
+
|
|
1446
|
+
def transform_constant(
|
|
1447
|
+
self, node: parse_tree.Constant
|
|
1448
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1449
|
+
result: TypeAnnotationUnion
|
|
1450
|
+
|
|
1451
|
+
if isinstance(node.value, bool):
|
|
1452
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1453
|
+
elif isinstance(node.value, int):
|
|
1454
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.INT)
|
|
1455
|
+
elif isinstance(node.value, float):
|
|
1456
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.FLOAT)
|
|
1457
|
+
elif isinstance(node.value, str):
|
|
1458
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.STR)
|
|
1459
|
+
else:
|
|
1460
|
+
assert_never(node.value)
|
|
1461
|
+
|
|
1462
|
+
self.type_map[node] = result
|
|
1463
|
+
return result
|
|
1464
|
+
|
|
1465
|
+
def transform_is_none(
|
|
1466
|
+
self, node: parse_tree.IsNone
|
|
1467
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1468
|
+
value_type = self.transform(node.value)
|
|
1469
|
+
|
|
1470
|
+
# NOTE (mristin):
|
|
1471
|
+
# Something went wrong if we could not infer the type of the ``value``.
|
|
1472
|
+
if value_type is None:
|
|
1473
|
+
return None
|
|
1474
|
+
|
|
1475
|
+
if not isinstance(value_type, OptionalTypeAnnotation):
|
|
1476
|
+
self.errors.append(
|
|
1477
|
+
Error(
|
|
1478
|
+
node.value.original_node,
|
|
1479
|
+
f"Expected the value to be of an optional type for "
|
|
1480
|
+
f"a nullness check (``is None``), "
|
|
1481
|
+
f"but got {value_type}",
|
|
1482
|
+
)
|
|
1483
|
+
)
|
|
1484
|
+
return None
|
|
1485
|
+
|
|
1486
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1487
|
+
self.type_map[node] = result
|
|
1488
|
+
return result
|
|
1489
|
+
|
|
1490
|
+
def transform_is_not_none(
|
|
1491
|
+
self, node: parse_tree.IsNotNone
|
|
1492
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1493
|
+
value_type = self.transform(node.value)
|
|
1494
|
+
|
|
1495
|
+
# NOTE (mristin):
|
|
1496
|
+
# Something went wrong if we could not infer the type of the ``value``.
|
|
1497
|
+
if value_type is None:
|
|
1498
|
+
return None
|
|
1499
|
+
|
|
1500
|
+
if not isinstance(value_type, OptionalTypeAnnotation):
|
|
1501
|
+
self.errors.append(
|
|
1502
|
+
Error(
|
|
1503
|
+
node.value.original_node,
|
|
1504
|
+
f"Expected the value to be of an optional type "
|
|
1505
|
+
f"for a non-nullness check (``is not None``), "
|
|
1506
|
+
f"but got {value_type}",
|
|
1507
|
+
)
|
|
1508
|
+
)
|
|
1509
|
+
return None
|
|
1510
|
+
|
|
1511
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1512
|
+
self.type_map[node] = result
|
|
1513
|
+
return result
|
|
1514
|
+
|
|
1515
|
+
def transform_not(self, node: parse_tree.Not) -> Optional["TypeAnnotationUnion"]:
|
|
1516
|
+
# Just recurse to fill ``type_map`` on ``operand`` even though we know the type
|
|
1517
|
+
# in advance
|
|
1518
|
+
|
|
1519
|
+
operand_type = self.transform(node.operand)
|
|
1520
|
+
|
|
1521
|
+
if operand_type is None:
|
|
1522
|
+
return None
|
|
1523
|
+
|
|
1524
|
+
if isinstance(operand_type, OptionalTypeAnnotation):
|
|
1525
|
+
self.errors.append(
|
|
1526
|
+
Error(
|
|
1527
|
+
node.operand.original_node,
|
|
1528
|
+
f"Expected the operand to be a non-None, "
|
|
1529
|
+
f"but got: {operand_type}",
|
|
1530
|
+
)
|
|
1531
|
+
)
|
|
1532
|
+
return None
|
|
1533
|
+
|
|
1534
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1535
|
+
self.type_map[node] = result
|
|
1536
|
+
return result
|
|
1537
|
+
|
|
1538
|
+
def transform_name(self, node: parse_tree.Name) -> Optional["TypeAnnotationUnion"]:
|
|
1539
|
+
type_in_env = self._environment.find(node.identifier)
|
|
1540
|
+
if type_in_env is None:
|
|
1541
|
+
self.errors.append(
|
|
1542
|
+
Error(
|
|
1543
|
+
node.original_node,
|
|
1544
|
+
f"We do not know how to infer the type of "
|
|
1545
|
+
f"the variable with the identifier {node.identifier!r} from the "
|
|
1546
|
+
f"given environment. Mind that we do not consider the module "
|
|
1547
|
+
f"scope nor handle all built-in functions due to simplicity! If "
|
|
1548
|
+
f"you believe this needs to work, please notify the developers.",
|
|
1549
|
+
)
|
|
1550
|
+
)
|
|
1551
|
+
return None
|
|
1552
|
+
|
|
1553
|
+
result = type_in_env
|
|
1554
|
+
|
|
1555
|
+
result = self._strip_optional_if_non_null(node=node, type_annotation=result)
|
|
1556
|
+
self.type_map[node] = result
|
|
1557
|
+
return result
|
|
1558
|
+
|
|
1559
|
+
def transform_and(self, node: parse_tree.And) -> Optional["TypeAnnotationUnion"]:
|
|
1560
|
+
# NOTE (mristin, 2022-06-17):
|
|
1561
|
+
# We need to iterate and recurse into ``values`` to fill the ``type_map``.
|
|
1562
|
+
# In the process, we have to consider the non-nullness and how it applies
|
|
1563
|
+
# to the remainder of the conjunction.
|
|
1564
|
+
|
|
1565
|
+
# NOTE (mristin, 2022-06-17):
|
|
1566
|
+
# We are very lax here and ignore the fact that calls to methods and functions
|
|
1567
|
+
# can actually alter the value assumed to be non-null, and actually violate
|
|
1568
|
+
# its non-nullness by setting it to null.
|
|
1569
|
+
#
|
|
1570
|
+
# This lack of conservatism works for now. If the bugs related to nullness
|
|
1571
|
+
# start to surface, we should re-think our approach here.
|
|
1572
|
+
|
|
1573
|
+
success = True
|
|
1574
|
+
|
|
1575
|
+
with contextlib.ExitStack() as exit_stack:
|
|
1576
|
+
for value_node in node.values:
|
|
1577
|
+
value_type = self.transform(value_node)
|
|
1578
|
+
if value_type is None:
|
|
1579
|
+
return None
|
|
1580
|
+
|
|
1581
|
+
if isinstance(value_type, OptionalTypeAnnotation):
|
|
1582
|
+
self.errors.append(
|
|
1583
|
+
Error(
|
|
1584
|
+
value_node.original_node,
|
|
1585
|
+
f"Expected the value to be a non-None, "
|
|
1586
|
+
f"but got: {value_type}",
|
|
1587
|
+
)
|
|
1588
|
+
)
|
|
1589
|
+
success = False
|
|
1590
|
+
|
|
1591
|
+
if isinstance(value_node, parse_tree.IsNotNone):
|
|
1592
|
+
canonical_repr = self._representation_map[value_node.value]
|
|
1593
|
+
self._non_null.increment(canonical_repr)
|
|
1594
|
+
|
|
1595
|
+
# fmt: off
|
|
1596
|
+
exit_stack.callback(
|
|
1597
|
+
lambda a_canonical_repr=canonical_repr: # type: ignore
|
|
1598
|
+
self._non_null.decrement(a_canonical_repr)
|
|
1599
|
+
)
|
|
1600
|
+
# fmt: on
|
|
1601
|
+
|
|
1602
|
+
if not success:
|
|
1603
|
+
return None
|
|
1604
|
+
|
|
1605
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1606
|
+
self.type_map[node] = result
|
|
1607
|
+
return result
|
|
1608
|
+
|
|
1609
|
+
def transform_or(self, node: parse_tree.Or) -> Optional["TypeAnnotationUnion"]:
|
|
1610
|
+
# Just recurse to fill ``type_map`` on ``values`` even though we know the type
|
|
1611
|
+
# in advance
|
|
1612
|
+
|
|
1613
|
+
success = True
|
|
1614
|
+
|
|
1615
|
+
with contextlib.ExitStack() as exit_stack:
|
|
1616
|
+
for value_node in node.values:
|
|
1617
|
+
value_type = self.transform(value_node)
|
|
1618
|
+
if value_type is None:
|
|
1619
|
+
return None
|
|
1620
|
+
|
|
1621
|
+
if isinstance(value_type, OptionalTypeAnnotation):
|
|
1622
|
+
self.errors.append(
|
|
1623
|
+
Error(
|
|
1624
|
+
value_node.original_node,
|
|
1625
|
+
f"Expected the value to be a non-None, "
|
|
1626
|
+
f"but got: {value_type}",
|
|
1627
|
+
)
|
|
1628
|
+
)
|
|
1629
|
+
success = False
|
|
1630
|
+
|
|
1631
|
+
if isinstance(value_node, parse_tree.IsNone):
|
|
1632
|
+
canonical_repr = self._representation_map[value_node.value]
|
|
1633
|
+
self._non_null.increment(canonical_repr)
|
|
1634
|
+
|
|
1635
|
+
# fmt: off
|
|
1636
|
+
exit_stack.callback(
|
|
1637
|
+
lambda a_canonical_repr=canonical_repr: # type: ignore
|
|
1638
|
+
self._non_null.decrement(
|
|
1639
|
+
a_canonical_repr
|
|
1640
|
+
)
|
|
1641
|
+
)
|
|
1642
|
+
# fmt: on
|
|
1643
|
+
|
|
1644
|
+
if not success:
|
|
1645
|
+
return None
|
|
1646
|
+
|
|
1647
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
1648
|
+
self.type_map[node] = result
|
|
1649
|
+
return result
|
|
1650
|
+
|
|
1651
|
+
@staticmethod
|
|
1652
|
+
def _binary_operation_name_with_capital_the(
|
|
1653
|
+
node: Union[parse_tree.Add, parse_tree.Sub]
|
|
1654
|
+
) -> str:
|
|
1655
|
+
if isinstance(node, parse_tree.Add):
|
|
1656
|
+
return "The addition"
|
|
1657
|
+
|
|
1658
|
+
elif isinstance(node, parse_tree.Sub):
|
|
1659
|
+
return "The subtraction"
|
|
1660
|
+
|
|
1661
|
+
else:
|
|
1662
|
+
assert_never(node)
|
|
1663
|
+
|
|
1664
|
+
def _transform_add_or_sub(
|
|
1665
|
+
self, node: Union[parse_tree.Add, parse_tree.Sub]
|
|
1666
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1667
|
+
left_type = self.transform(node.left)
|
|
1668
|
+
if left_type is None:
|
|
1669
|
+
return None
|
|
1670
|
+
|
|
1671
|
+
right_type = self.transform(node.right)
|
|
1672
|
+
if right_type is None:
|
|
1673
|
+
return None
|
|
1674
|
+
|
|
1675
|
+
success = True
|
|
1676
|
+
|
|
1677
|
+
if isinstance(left_type, OptionalTypeAnnotation):
|
|
1678
|
+
self.errors.append(
|
|
1679
|
+
Error(
|
|
1680
|
+
node.left.original_node,
|
|
1681
|
+
f"Expected the left operand to be a non-None, "
|
|
1682
|
+
f"but got: {left_type}",
|
|
1683
|
+
)
|
|
1684
|
+
)
|
|
1685
|
+
success = False
|
|
1686
|
+
|
|
1687
|
+
if isinstance(right_type, OptionalTypeAnnotation):
|
|
1688
|
+
self.errors.append(
|
|
1689
|
+
Error(
|
|
1690
|
+
node.right.original_node,
|
|
1691
|
+
f"Expected the right operand to be a non-None, "
|
|
1692
|
+
f"but got: {right_type}",
|
|
1693
|
+
)
|
|
1694
|
+
)
|
|
1695
|
+
success = False
|
|
1696
|
+
|
|
1697
|
+
if not success:
|
|
1698
|
+
return None
|
|
1699
|
+
|
|
1700
|
+
if not (
|
|
1701
|
+
isinstance(left_type, PrimitiveTypeAnnotation)
|
|
1702
|
+
and left_type.a_type
|
|
1703
|
+
in (
|
|
1704
|
+
PrimitiveType.INT,
|
|
1705
|
+
PrimitiveType.FLOAT,
|
|
1706
|
+
PrimitiveType.LENGTH,
|
|
1707
|
+
)
|
|
1708
|
+
):
|
|
1709
|
+
self.errors.append(
|
|
1710
|
+
Error(
|
|
1711
|
+
node.left.original_node,
|
|
1712
|
+
f"{_Inferrer._binary_operation_name_with_capital_the(node)} is "
|
|
1713
|
+
f"only defined on integer and floating-point numbers, "
|
|
1714
|
+
f"but got as a left operand: {left_type}",
|
|
1715
|
+
)
|
|
1716
|
+
)
|
|
1717
|
+
success = False
|
|
1718
|
+
|
|
1719
|
+
if not (
|
|
1720
|
+
isinstance(right_type, PrimitiveTypeAnnotation)
|
|
1721
|
+
and right_type.a_type
|
|
1722
|
+
in (
|
|
1723
|
+
PrimitiveType.INT,
|
|
1724
|
+
PrimitiveType.FLOAT,
|
|
1725
|
+
PrimitiveType.LENGTH,
|
|
1726
|
+
)
|
|
1727
|
+
):
|
|
1728
|
+
self.errors.append(
|
|
1729
|
+
Error(
|
|
1730
|
+
node.right.original_node,
|
|
1731
|
+
f"{_Inferrer._binary_operation_name_with_capital_the(node)} is "
|
|
1732
|
+
f"only defined on integer and floating-point numbers, "
|
|
1733
|
+
f"but got as a right operand: {right_type}",
|
|
1734
|
+
)
|
|
1735
|
+
)
|
|
1736
|
+
success = False
|
|
1737
|
+
|
|
1738
|
+
if not success:
|
|
1739
|
+
return None
|
|
1740
|
+
|
|
1741
|
+
assert isinstance(left_type, PrimitiveTypeAnnotation)
|
|
1742
|
+
assert isinstance(right_type, PrimitiveTypeAnnotation)
|
|
1743
|
+
|
|
1744
|
+
# fmt: off
|
|
1745
|
+
if (
|
|
1746
|
+
(
|
|
1747
|
+
left_type.a_type is PrimitiveType.FLOAT
|
|
1748
|
+
and right_type.a_type is not PrimitiveType.FLOAT
|
|
1749
|
+
) or (
|
|
1750
|
+
right_type.a_type is PrimitiveType.FLOAT
|
|
1751
|
+
and left_type.a_type is not PrimitiveType.FLOAT
|
|
1752
|
+
)
|
|
1753
|
+
):
|
|
1754
|
+
# fmt: on
|
|
1755
|
+
self.errors.append(
|
|
1756
|
+
Error(
|
|
1757
|
+
node.original_node,
|
|
1758
|
+
f"You can not mix floating-point and integer numbers, "
|
|
1759
|
+
f"but the left operand was: {left_type}; "
|
|
1760
|
+
f"and the right operand was: {right_type}"
|
|
1761
|
+
)
|
|
1762
|
+
)
|
|
1763
|
+
success = False
|
|
1764
|
+
|
|
1765
|
+
if not success:
|
|
1766
|
+
return None
|
|
1767
|
+
|
|
1768
|
+
# fmt: off
|
|
1769
|
+
result_type: PrimitiveType
|
|
1770
|
+
if (
|
|
1771
|
+
(
|
|
1772
|
+
left_type.a_type is PrimitiveType.LENGTH
|
|
1773
|
+
and right_type.a_type in (PrimitiveType.INT, PrimitiveType.LENGTH)
|
|
1774
|
+
) or (
|
|
1775
|
+
right_type.a_type is PrimitiveType.LENGTH
|
|
1776
|
+
and left_type.a_type in (PrimitiveType.INT, PrimitiveType.LENGTH)
|
|
1777
|
+
)
|
|
1778
|
+
):
|
|
1779
|
+
result_type = PrimitiveType.LENGTH
|
|
1780
|
+
|
|
1781
|
+
elif (
|
|
1782
|
+
left_type.a_type is PrimitiveType.INT
|
|
1783
|
+
and right_type.a_type is PrimitiveType.INT
|
|
1784
|
+
):
|
|
1785
|
+
result_type = PrimitiveType.INT
|
|
1786
|
+
|
|
1787
|
+
elif (
|
|
1788
|
+
left_type.a_type is PrimitiveType.FLOAT
|
|
1789
|
+
and right_type.a_type is PrimitiveType.FLOAT
|
|
1790
|
+
):
|
|
1791
|
+
result_type = PrimitiveType.FLOAT
|
|
1792
|
+
else:
|
|
1793
|
+
raise AssertionError(
|
|
1794
|
+
f"Unhandled execution path: {left_type=}, {right_type=}"
|
|
1795
|
+
)
|
|
1796
|
+
# fmt: on
|
|
1797
|
+
|
|
1798
|
+
result = PrimitiveTypeAnnotation(a_type=result_type)
|
|
1799
|
+
self.type_map[node] = result
|
|
1800
|
+
return result
|
|
1801
|
+
|
|
1802
|
+
def transform_add(self, node: parse_tree.Add) -> Optional["TypeAnnotationUnion"]:
|
|
1803
|
+
return self._transform_add_or_sub(node)
|
|
1804
|
+
|
|
1805
|
+
def transform_sub(self, node: parse_tree.Sub) -> Optional["TypeAnnotationUnion"]:
|
|
1806
|
+
return self._transform_add_or_sub(node)
|
|
1807
|
+
|
|
1808
|
+
def transform_formatted_value(
|
|
1809
|
+
self, node: parse_tree.FormattedValue
|
|
1810
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1811
|
+
value_type = self.transform(node.value)
|
|
1812
|
+
if value_type is None:
|
|
1813
|
+
return None
|
|
1814
|
+
|
|
1815
|
+
if isinstance(value_type, OptionalTypeAnnotation):
|
|
1816
|
+
self.errors.append(
|
|
1817
|
+
Error(
|
|
1818
|
+
node.value.original_node,
|
|
1819
|
+
f"Expected the value to be a non-None, " f"but got: {value_type}",
|
|
1820
|
+
)
|
|
1821
|
+
)
|
|
1822
|
+
return None
|
|
1823
|
+
|
|
1824
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.STR)
|
|
1825
|
+
self.type_map[node] = result
|
|
1826
|
+
return result
|
|
1827
|
+
|
|
1828
|
+
def transform_joined_str(
|
|
1829
|
+
self, node: parse_tree.JoinedStr
|
|
1830
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1831
|
+
# Just recurse to fill ``type_map`` on ``values`` even though we know the type
|
|
1832
|
+
# in advance
|
|
1833
|
+
success = True
|
|
1834
|
+
for value in node.values:
|
|
1835
|
+
if isinstance(value, str):
|
|
1836
|
+
continue
|
|
1837
|
+
elif isinstance(value, parse_tree.FormattedValue):
|
|
1838
|
+
formatted_value_type = self.transform(value)
|
|
1839
|
+
if formatted_value_type is None:
|
|
1840
|
+
success = False
|
|
1841
|
+
else:
|
|
1842
|
+
assert_never(value)
|
|
1843
|
+
|
|
1844
|
+
if not success:
|
|
1845
|
+
return None
|
|
1846
|
+
|
|
1847
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.STR)
|
|
1848
|
+
self.type_map[node] = result
|
|
1849
|
+
return result
|
|
1850
|
+
|
|
1851
|
+
def transform_for_each(
|
|
1852
|
+
self, node: parse_tree.ForEach
|
|
1853
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1854
|
+
variable_type_in_env = self._environment.find(node.variable.identifier)
|
|
1855
|
+
if variable_type_in_env is not None:
|
|
1856
|
+
self.errors.append(
|
|
1857
|
+
Error(
|
|
1858
|
+
node.variable.original_node,
|
|
1859
|
+
f"The variable {node.variable.identifier} "
|
|
1860
|
+
f"has been already defined before",
|
|
1861
|
+
)
|
|
1862
|
+
)
|
|
1863
|
+
return None
|
|
1864
|
+
|
|
1865
|
+
iter_type = self.transform(node.iteration)
|
|
1866
|
+
if iter_type is None:
|
|
1867
|
+
return None
|
|
1868
|
+
|
|
1869
|
+
if isinstance(iter_type, OptionalTypeAnnotation):
|
|
1870
|
+
self.errors.append(
|
|
1871
|
+
Error(
|
|
1872
|
+
node.iteration.original_node,
|
|
1873
|
+
f"Expected the collection which we iterate over to be a non-None, "
|
|
1874
|
+
f"but got: {iter_type}",
|
|
1875
|
+
)
|
|
1876
|
+
)
|
|
1877
|
+
return None
|
|
1878
|
+
|
|
1879
|
+
if not isinstance(iter_type, ListTypeAnnotation):
|
|
1880
|
+
self.errors.append(
|
|
1881
|
+
Error(
|
|
1882
|
+
node.iteration.original_node,
|
|
1883
|
+
f"Expected an iteration over a list, but got: {iter_type}",
|
|
1884
|
+
)
|
|
1885
|
+
)
|
|
1886
|
+
return None
|
|
1887
|
+
|
|
1888
|
+
loop_variable_type = iter_type.items
|
|
1889
|
+
|
|
1890
|
+
self.type_map[node.variable] = loop_variable_type
|
|
1891
|
+
|
|
1892
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.NONE)
|
|
1893
|
+
self.type_map[node] = result
|
|
1894
|
+
return result
|
|
1895
|
+
|
|
1896
|
+
def transform_for_range(
|
|
1897
|
+
self, node: parse_tree.ForRange
|
|
1898
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
1899
|
+
variable_type_in_env = self._environment.find(node.variable.identifier)
|
|
1900
|
+
if variable_type_in_env is not None:
|
|
1901
|
+
self.errors.append(
|
|
1902
|
+
Error(
|
|
1903
|
+
node.variable.original_node,
|
|
1904
|
+
f"The variable {node.variable.identifier} "
|
|
1905
|
+
f"has been already defined before",
|
|
1906
|
+
)
|
|
1907
|
+
)
|
|
1908
|
+
return None
|
|
1909
|
+
|
|
1910
|
+
start_type = self.transform(node.start)
|
|
1911
|
+
if start_type is None:
|
|
1912
|
+
return None
|
|
1913
|
+
|
|
1914
|
+
end_type = self.transform(node.end)
|
|
1915
|
+
if end_type is None:
|
|
1916
|
+
return None
|
|
1917
|
+
|
|
1918
|
+
success = True
|
|
1919
|
+
|
|
1920
|
+
if isinstance(start_type, OptionalTypeAnnotation):
|
|
1921
|
+
self.errors.append(
|
|
1922
|
+
Error(
|
|
1923
|
+
node.start.original_node,
|
|
1924
|
+
f"Expected the start to be a non-None, " f"but got: {start_type}",
|
|
1925
|
+
)
|
|
1926
|
+
)
|
|
1927
|
+
success = False
|
|
1928
|
+
|
|
1929
|
+
if isinstance(end_type, OptionalTypeAnnotation):
|
|
1930
|
+
self.errors.append(
|
|
1931
|
+
Error(
|
|
1932
|
+
node.end.original_node,
|
|
1933
|
+
f"Expected the end to be a non-None, " f"but got: {end_type}",
|
|
1934
|
+
)
|
|
1935
|
+
)
|
|
1936
|
+
success = False
|
|
1937
|
+
|
|
1938
|
+
if not success:
|
|
1939
|
+
return None
|
|
1940
|
+
|
|
1941
|
+
if not (
|
|
1942
|
+
isinstance(start_type, PrimitiveTypeAnnotation)
|
|
1943
|
+
and start_type.a_type in (PrimitiveType.INT, PrimitiveType.LENGTH)
|
|
1944
|
+
):
|
|
1945
|
+
self.errors.append(
|
|
1946
|
+
Error(
|
|
1947
|
+
node.start.original_node,
|
|
1948
|
+
f"Expected the start of a range to be an integer, "
|
|
1949
|
+
f"but got: {start_type}",
|
|
1950
|
+
)
|
|
1951
|
+
)
|
|
1952
|
+
return None
|
|
1953
|
+
|
|
1954
|
+
if not (
|
|
1955
|
+
isinstance(end_type, PrimitiveTypeAnnotation)
|
|
1956
|
+
and end_type.a_type in (PrimitiveType.INT, PrimitiveType.LENGTH)
|
|
1957
|
+
):
|
|
1958
|
+
self.errors.append(
|
|
1959
|
+
Error(
|
|
1960
|
+
node.end.original_node,
|
|
1961
|
+
f"Expected the end of a range to be an integer, "
|
|
1962
|
+
f"but got: {end_type}",
|
|
1963
|
+
)
|
|
1964
|
+
)
|
|
1965
|
+
return None
|
|
1966
|
+
|
|
1967
|
+
# region Pick the larger integer type for the type of the loop variable
|
|
1968
|
+
assert isinstance(
|
|
1969
|
+
start_type, PrimitiveTypeAnnotation
|
|
1970
|
+
) and start_type.a_type in (PrimitiveType.INT, PrimitiveType.LENGTH)
|
|
1971
|
+
assert isinstance(end_type, PrimitiveTypeAnnotation) and end_type.a_type in (
|
|
1972
|
+
PrimitiveType.INT,
|
|
1973
|
+
PrimitiveType.LENGTH,
|
|
1974
|
+
)
|
|
1975
|
+
|
|
1976
|
+
loop_variable_type: PrimitiveTypeAnnotation
|
|
1977
|
+
if (
|
|
1978
|
+
start_type.a_type is PrimitiveType.LENGTH
|
|
1979
|
+
or end_type.a_type is PrimitiveType.LENGTH
|
|
1980
|
+
):
|
|
1981
|
+
loop_variable_type = PrimitiveTypeAnnotation(a_type=PrimitiveType.LENGTH)
|
|
1982
|
+
else:
|
|
1983
|
+
assert (
|
|
1984
|
+
start_type.a_type is PrimitiveType.INT
|
|
1985
|
+
and end_type.a_type is PrimitiveType.INT
|
|
1986
|
+
)
|
|
1987
|
+
loop_variable_type = PrimitiveTypeAnnotation(a_type=PrimitiveType.INT)
|
|
1988
|
+
|
|
1989
|
+
# endregion
|
|
1990
|
+
|
|
1991
|
+
self.type_map[node.variable] = loop_variable_type
|
|
1992
|
+
|
|
1993
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.NONE)
|
|
1994
|
+
self.type_map[node] = result
|
|
1995
|
+
return result
|
|
1996
|
+
|
|
1997
|
+
def _transform_any_or_all(
|
|
1998
|
+
self, node: Union[parse_tree.Any, parse_tree.All]
|
|
1999
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
2000
|
+
a_type = self.transform(node.generator)
|
|
2001
|
+
if a_type is None:
|
|
2002
|
+
return None
|
|
2003
|
+
|
|
2004
|
+
loop_variable_type = self.type_map[node.generator.variable]
|
|
2005
|
+
try:
|
|
2006
|
+
self._environment.set(
|
|
2007
|
+
identifier=node.generator.variable.identifier,
|
|
2008
|
+
type_annotation=loop_variable_type,
|
|
2009
|
+
)
|
|
2010
|
+
|
|
2011
|
+
a_type = self.transform(node.condition)
|
|
2012
|
+
if a_type is None:
|
|
2013
|
+
return None
|
|
2014
|
+
|
|
2015
|
+
if (
|
|
2016
|
+
not isinstance(a_type, PrimitiveTypeAnnotation)
|
|
2017
|
+
or a_type.a_type is not PrimitiveType.BOOL
|
|
2018
|
+
):
|
|
2019
|
+
self.errors.append(
|
|
2020
|
+
Error(
|
|
2021
|
+
node.condition.original_node,
|
|
2022
|
+
f"Expected the condition to be a boolean, "
|
|
2023
|
+
f"but got: {a_type}",
|
|
2024
|
+
)
|
|
2025
|
+
)
|
|
2026
|
+
return None
|
|
2027
|
+
|
|
2028
|
+
finally:
|
|
2029
|
+
self._environment.remove(identifier=node.generator.variable.identifier)
|
|
2030
|
+
|
|
2031
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.BOOL)
|
|
2032
|
+
self.type_map[node] = result
|
|
2033
|
+
return result
|
|
2034
|
+
|
|
2035
|
+
def transform_any(self, node: parse_tree.Any) -> Optional["TypeAnnotationUnion"]:
|
|
2036
|
+
return self._transform_any_or_all(node)
|
|
2037
|
+
|
|
2038
|
+
def transform_all(self, node: parse_tree.All) -> Optional["TypeAnnotationUnion"]:
|
|
2039
|
+
return self._transform_any_or_all(node)
|
|
2040
|
+
|
|
2041
|
+
def transform_assignment(
|
|
2042
|
+
self, node: parse_tree.Assignment
|
|
2043
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
2044
|
+
is_new_variable = False
|
|
2045
|
+
|
|
2046
|
+
target_type: Optional[TypeAnnotationUnion]
|
|
2047
|
+
|
|
2048
|
+
if isinstance(node.target, parse_tree.Name):
|
|
2049
|
+
target_type = self._environment.find(node.target.identifier)
|
|
2050
|
+
if target_type is None:
|
|
2051
|
+
is_new_variable = True
|
|
2052
|
+
else:
|
|
2053
|
+
target_type = self.transform(node.target)
|
|
2054
|
+
|
|
2055
|
+
value_type = self.transform(node.value)
|
|
2056
|
+
|
|
2057
|
+
if (not is_new_variable and target_type is None) or (value_type is None):
|
|
2058
|
+
return None
|
|
2059
|
+
|
|
2060
|
+
if target_type is not None and not _assignable(
|
|
2061
|
+
target_type=target_type, value_type=value_type
|
|
2062
|
+
):
|
|
2063
|
+
self.errors.append(
|
|
2064
|
+
Error(
|
|
2065
|
+
node.original_node,
|
|
2066
|
+
f"We inferred the target type of the assignment to "
|
|
2067
|
+
f"be {target_type}, while the value type is inferred to "
|
|
2068
|
+
f"be {value_type}. We do not know how to model this assignment.",
|
|
2069
|
+
)
|
|
2070
|
+
)
|
|
2071
|
+
return None
|
|
2072
|
+
|
|
2073
|
+
if is_new_variable:
|
|
2074
|
+
assert isinstance(node.target, parse_tree.Name)
|
|
2075
|
+
|
|
2076
|
+
self._environment.set(
|
|
2077
|
+
identifier=node.target.identifier, type_annotation=value_type
|
|
2078
|
+
)
|
|
2079
|
+
|
|
2080
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.NONE)
|
|
2081
|
+
self.type_map[node] = result
|
|
2082
|
+
return result
|
|
2083
|
+
|
|
2084
|
+
def transform_return(
|
|
2085
|
+
self, node: parse_tree.Return
|
|
2086
|
+
) -> Optional["TypeAnnotationUnion"]:
|
|
2087
|
+
# Just recurse to fill ``type_map`` on ``value`` even though we know the type
|
|
2088
|
+
# in advance
|
|
2089
|
+
if node.value is not None:
|
|
2090
|
+
success = self.transform(node.value) is not None
|
|
2091
|
+
if not success:
|
|
2092
|
+
return None
|
|
2093
|
+
|
|
2094
|
+
# Treat ``return`` as a statement
|
|
2095
|
+
result = PrimitiveTypeAnnotation(PrimitiveType.NONE)
|
|
2096
|
+
self.type_map[node] = result
|
|
2097
|
+
return result
|
|
2098
|
+
|
|
2099
|
+
|
|
2100
|
+
def populate_base_environment(symbol_table: _types.SymbolTable) -> Environment:
|
|
2101
|
+
"""Create a basic mapping name 🠒 type annotation from the global scope.
|
|
2102
|
+
|
|
2103
|
+
The global scope, in this context, refers to the level of symbol table.
|
|
2104
|
+
"""
|
|
2105
|
+
# Build up the environment;
|
|
2106
|
+
# see https://craftinginterpreters.com/resolving-and-binding.html
|
|
2107
|
+
mapping: MutableMapping[Identifier, "TypeAnnotationUnion"] = {
|
|
2108
|
+
Identifier("len"): BuiltinFunctionTypeAnnotation(
|
|
2109
|
+
func=BuiltinFunction(
|
|
2110
|
+
name=Identifier("len"),
|
|
2111
|
+
returns=PrimitiveTypeAnnotation(PrimitiveType.LENGTH),
|
|
2112
|
+
)
|
|
2113
|
+
)
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
for constant in symbol_table.constants:
|
|
2117
|
+
if isinstance(constant, _types.ConstantPrimitive):
|
|
2118
|
+
mapping[constant.name] = PrimitiveTypeAnnotation(
|
|
2119
|
+
a_type=PRIMITIVE_TYPE_MAP[constant.a_type]
|
|
2120
|
+
)
|
|
2121
|
+
elif isinstance(constant, _types.ConstantSetOfPrimitives):
|
|
2122
|
+
mapping[constant.name] = SetTypeAnnotation(
|
|
2123
|
+
items=PrimitiveTypeAnnotation(PRIMITIVE_TYPE_MAP[constant.a_type])
|
|
2124
|
+
)
|
|
2125
|
+
elif isinstance(constant, _types.ConstantSetOfEnumerationLiterals):
|
|
2126
|
+
mapping[constant.name] = SetTypeAnnotation(
|
|
2127
|
+
items=OurTypeAnnotation(our_type=constant.enumeration)
|
|
2128
|
+
)
|
|
2129
|
+
else:
|
|
2130
|
+
assert_never(constant)
|
|
2131
|
+
|
|
2132
|
+
for verification in symbol_table.verification_functions:
|
|
2133
|
+
assert verification.name not in mapping
|
|
2134
|
+
mapping[verification.name] = VerificationTypeAnnotation(func=verification)
|
|
2135
|
+
|
|
2136
|
+
for our_type in symbol_table.our_types:
|
|
2137
|
+
if isinstance(our_type, _types.Enumeration):
|
|
2138
|
+
assert our_type.name not in mapping
|
|
2139
|
+
mapping[our_type.name] = EnumerationAsTypeTypeAnnotation(
|
|
2140
|
+
enumeration=our_type
|
|
2141
|
+
)
|
|
2142
|
+
|
|
2143
|
+
return ImmutableEnvironment(mapping=mapping, parent=None)
|
|
2144
|
+
|
|
2145
|
+
|
|
2146
|
+
class InferenceOfFunction:
|
|
2147
|
+
"""Represent the result of type inference on a function body and arguments."""
|
|
2148
|
+
|
|
2149
|
+
#: Environment inferred after processing a body of statements including
|
|
2150
|
+
#: the function arguments
|
|
2151
|
+
environment_with_args: Final[Environment]
|
|
2152
|
+
|
|
2153
|
+
#: Map of body nodes to types
|
|
2154
|
+
type_map: Final[Mapping[parse_tree.Node, "TypeAnnotationUnion"]]
|
|
2155
|
+
|
|
2156
|
+
def __init__(
|
|
2157
|
+
self,
|
|
2158
|
+
environment_with_args: Environment,
|
|
2159
|
+
type_map: Mapping[parse_tree.Node, "TypeAnnotationUnion"],
|
|
2160
|
+
) -> None:
|
|
2161
|
+
"""Initialize with the given values."""
|
|
2162
|
+
self.environment_with_args = environment_with_args
|
|
2163
|
+
self.type_map = type_map
|
|
2164
|
+
|
|
2165
|
+
|
|
2166
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
2167
|
+
def infer_for_verification(
|
|
2168
|
+
verification: _types.TranspilableVerification, base_environment: Environment
|
|
2169
|
+
) -> Tuple[Optional[InferenceOfFunction], Optional[Error]]:
|
|
2170
|
+
"""Infer the types for the given function and map the body nodes to the types."""
|
|
2171
|
+
canonicalizer = _Canonicalizer()
|
|
2172
|
+
for node in verification.parsed.body:
|
|
2173
|
+
_ = canonicalizer.transform(node)
|
|
2174
|
+
|
|
2175
|
+
environment = MutableEnvironment(parent=base_environment)
|
|
2176
|
+
|
|
2177
|
+
for arg in verification.arguments:
|
|
2178
|
+
environment.set(
|
|
2179
|
+
identifier=arg.name,
|
|
2180
|
+
type_annotation=convert_type_annotation(arg.type_annotation),
|
|
2181
|
+
)
|
|
2182
|
+
|
|
2183
|
+
type_inferrer = _Inferrer(
|
|
2184
|
+
environment=environment,
|
|
2185
|
+
representation_map=canonicalizer.representation_map,
|
|
2186
|
+
)
|
|
2187
|
+
|
|
2188
|
+
for node in verification.parsed.body:
|
|
2189
|
+
_ = type_inferrer.transform(node)
|
|
2190
|
+
|
|
2191
|
+
if len(type_inferrer.errors):
|
|
2192
|
+
return None, Error(
|
|
2193
|
+
verification.parsed.node,
|
|
2194
|
+
f"Failed to infer the types "
|
|
2195
|
+
f"in the verification function {verification.name!r}",
|
|
2196
|
+
type_inferrer.errors,
|
|
2197
|
+
)
|
|
2198
|
+
|
|
2199
|
+
return (
|
|
2200
|
+
InferenceOfFunction(
|
|
2201
|
+
environment_with_args=environment, type_map=type_inferrer.type_map
|
|
2202
|
+
),
|
|
2203
|
+
None,
|
|
2204
|
+
)
|
|
2205
|
+
|
|
2206
|
+
|
|
2207
|
+
@ensure(lambda result: (result[0] is not None) ^ (result[1] is not None))
|
|
2208
|
+
def infer_for_invariant(
|
|
2209
|
+
invariant: _types.Invariant, environment: Environment
|
|
2210
|
+
) -> Tuple[Optional[Mapping[parse_tree.Node, "TypeAnnotationUnion"]], Optional[Error]]:
|
|
2211
|
+
"""Infer the types of the nodes corresponding to the body of an invariant."""
|
|
2212
|
+
canonicalizer = _Canonicalizer()
|
|
2213
|
+
_ = canonicalizer.transform(invariant.body)
|
|
2214
|
+
|
|
2215
|
+
type_inferrer = _Inferrer(
|
|
2216
|
+
environment=environment,
|
|
2217
|
+
representation_map=canonicalizer.representation_map,
|
|
2218
|
+
)
|
|
2219
|
+
|
|
2220
|
+
_ = type_inferrer.transform(invariant.body)
|
|
2221
|
+
|
|
2222
|
+
if len(type_inferrer.errors):
|
|
2223
|
+
return None, Error(
|
|
2224
|
+
invariant.parsed.node,
|
|
2225
|
+
"Failed to infer the types in the invariant",
|
|
2226
|
+
type_inferrer.errors,
|
|
2227
|
+
)
|
|
2228
|
+
|
|
2229
|
+
return type_inferrer.type_map, None
|
|
2230
|
+
|
|
2231
|
+
|
|
2232
|
+
assert_union_of_descendants_exhaustive(
|
|
2233
|
+
union=TypeAnnotationUnion, base_class=TypeAnnotation
|
|
2234
|
+
)
|
|
2235
|
+
|
|
2236
|
+
TypeAnnotationExceptOptional = Union[
|
|
2237
|
+
PrimitiveTypeAnnotation,
|
|
2238
|
+
OurTypeAnnotation,
|
|
2239
|
+
VerificationTypeAnnotation,
|
|
2240
|
+
BuiltinFunctionTypeAnnotation,
|
|
2241
|
+
MethodTypeAnnotation,
|
|
2242
|
+
ListTypeAnnotation,
|
|
2243
|
+
SetTypeAnnotation,
|
|
2244
|
+
EnumerationAsTypeTypeAnnotation,
|
|
2245
|
+
]
|
|
2246
|
+
assert_union_without_excluded(
|
|
2247
|
+
original_union=TypeAnnotationUnion,
|
|
2248
|
+
subset_union=TypeAnnotationExceptOptional,
|
|
2249
|
+
excluded=[OptionalTypeAnnotation],
|
|
2250
|
+
)
|
|
2251
|
+
|
|
2252
|
+
FunctionTypeAnnotationUnion = Union[
|
|
2253
|
+
VerificationTypeAnnotation, BuiltinFunctionTypeAnnotation
|
|
2254
|
+
]
|
|
2255
|
+
assert_union_of_descendants_exhaustive(
|
|
2256
|
+
union=FunctionTypeAnnotationUnion, base_class=FunctionTypeAnnotation
|
|
2257
|
+
)
|
|
2258
|
+
|
|
2259
|
+
# NOTE (mristin, 2021-12-27):
|
|
2260
|
+
# Mypy is not smart enough to work with ``get_args``, so we have to manually write it
|
|
2261
|
+
# out.
|
|
2262
|
+
FunctionTypeAnnotationUnionAsTuple = (
|
|
2263
|
+
VerificationTypeAnnotation,
|
|
2264
|
+
BuiltinFunctionTypeAnnotation,
|
|
2265
|
+
)
|
|
2266
|
+
assert FunctionTypeAnnotationUnionAsTuple == get_args(FunctionTypeAnnotationUnion)
|