tpy-lang 0.3.0.dev0__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.
- tpy_lang-0.3.0.dev0.dist-info/METADATA +151 -0
- tpy_lang-0.3.0.dev0.dist-info/RECORD +333 -0
- tpy_lang-0.3.0.dev0.dist-info/WHEEL +4 -0
- tpy_lang-0.3.0.dev0.dist-info/entry_points.txt +3 -0
- tpyc/__init__.py +104 -0
- tpyc/__main__.py +6 -0
- tpyc/_buildinfo.py +1 -0
- tpyc/_data/docs/LANGUAGE_FEATURES.md +6278 -0
- tpyc/_data/docs/STDLIB_ROADMAP.md +1258 -0
- tpyc/_data/docs/TPY_FOR_AGENTS.md +556 -0
- tpyc/_data/lib/tpy/_bindings/__init__.py +6 -0
- tpyc/_data/lib/tpy/_bindings/pcre2.py +173 -0
- tpyc/_data/lib/tpy/_bindings/posix_socket.py +161 -0
- tpyc/_data/lib/tpy/_functools_macros.py +80 -0
- tpyc/_data/lib/tpy/_macro_helpers.py +161 -0
- tpyc/_data/lib/tpy/argparse.py +2062 -0
- tpyc/_data/lib/tpy/asyncio/__init__.py +744 -0
- tpyc/_data/lib/tpy/asyncio/_executor.py +515 -0
- tpyc/_data/lib/tpy/base64.py +410 -0
- tpyc/_data/lib/tpy/bisect.py +39 -0
- tpyc/_data/lib/tpy/builtins.py +38 -0
- tpyc/_data/lib/tpy/dataclasses.py +354 -0
- tpyc/_data/lib/tpy/enum.py +23 -0
- tpyc/_data/lib/tpy/functools.py +33 -0
- tpyc/_data/lib/tpy/hashlib.py +206 -0
- tpyc/_data/lib/tpy/heapq.py +118 -0
- tpyc/_data/lib/tpy/io.py +395 -0
- tpyc/_data/lib/tpy/json.py +221 -0
- tpyc/_data/lib/tpy/math.py +406 -0
- tpyc/_data/lib/tpy/random.py +597 -0
- tpyc/_data/lib/tpy/re.py +467 -0
- tpyc/_data/lib/tpy/socket.py +379 -0
- tpyc/_data/lib/tpy/struct.py +178 -0
- tpyc/_data/lib/tpy/sys.py +40 -0
- tpyc/_data/lib/tpy/time.py +39 -0
- tpyc/_data/lib/tpy/tpy/__init__.py +78 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/__init__.py +10 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/_decorators.py +37 -0
- tpyc/_data/lib/tpy/tpy/_bootstrap/_extern.py +64 -0
- tpyc/_data/lib/tpy/tpy/_builtins/__init__.py +11 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_bytes.py +378 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_dict.py +151 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_exceptions.py +125 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_funcs.py +681 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_io.py +97 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_list.py +127 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_range.py +52 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_set.py +139 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_super.py +11 -0
- tpyc/_data/lib/tpy/tpy/_builtins/_types.py +661 -0
- tpyc/_data/lib/tpy/tpy/_core/__init__.py +23 -0
- tpyc/_data/lib/tpy/tpy/_core/_bytes_view.py +129 -0
- tpyc/_data/lib/tpy/tpy/_core/_containers.py +137 -0
- tpyc/_data/lib/tpy/tpy/_core/_functions.py +40 -0
- tpyc/_data/lib/tpy/tpy/_core/_types.py +2061 -0
- tpyc/_data/lib/tpy/tpy/_typing/__init__.py +77 -0
- tpyc/_data/lib/tpy/tpy/_version.py +29 -0
- tpyc/_data/lib/tpy/tpy/bits.py +28 -0
- tpyc/_data/lib/tpy/tpy/coro/__init__.py +127 -0
- tpyc/_data/lib/tpy/tpy/extern.py +8 -0
- tpyc/_data/lib/tpy/tpy/mem.py +49 -0
- tpyc/_data/lib/tpy/tpy/unsafe.py +195 -0
- tpyc/_data/lib/tpy/tpy/version.py +21 -0
- tpyc/_data/lib/tpy/typing.py +13 -0
- tpyc/_data/runtime/cpp/include/tpy/any.hpp +461 -0
- tpyc/_data/runtime/cpp/include/tpy/as_ostream.hpp +117 -0
- tpyc/_data/runtime/cpp/include/tpy/async.hpp +76 -0
- tpyc/_data/runtime/cpp/include/tpy/bigint.hpp +1343 -0
- tpyc/_data/runtime/cpp/include/tpy/builtins.hpp +400 -0
- tpyc/_data/runtime/cpp/include/tpy/bytes_ops.hpp +469 -0
- tpyc/_data/runtime/cpp/include/tpy/container_ops.hpp +487 -0
- tpyc/_data/runtime/cpp/include/tpy/copy_iter.hpp +82 -0
- tpyc/_data/runtime/cpp/include/tpy/core.hpp +558 -0
- tpyc/_data/runtime/cpp/include/tpy/dict_ops.hpp +289 -0
- tpyc/_data/runtime/cpp/include/tpy/dunder.hpp +750 -0
- tpyc/_data/runtime/cpp/include/tpy/dynamic.hpp +44 -0
- tpyc/_data/runtime/cpp/include/tpy/enum.hpp +40 -0
- tpyc/_data/runtime/cpp/include/tpy/file.hpp +245 -0
- tpyc/_data/runtime/cpp/include/tpy/fixed_int.hpp +317 -0
- tpyc/_data/runtime/cpp/include/tpy/format.hpp +954 -0
- tpyc/_data/runtime/cpp/include/tpy/frame_slot.hpp +120 -0
- tpyc/_data/runtime/cpp/include/tpy/generator.hpp +47 -0
- tpyc/_data/runtime/cpp/include/tpy/iterable_ops.hpp +122 -0
- tpyc/_data/runtime/cpp/include/tpy/itertools.hpp +749 -0
- tpyc/_data/runtime/cpp/include/tpy/next_iter.hpp +82 -0
- tpyc/_data/runtime/cpp/include/tpy/ordered_map.hpp +518 -0
- tpyc/_data/runtime/cpp/include/tpy/ordered_set.hpp +337 -0
- tpyc/_data/runtime/cpp/include/tpy/own_iter.hpp +54 -0
- tpyc/_data/runtime/cpp/include/tpy/pascal_graph_sdl.hpp +192 -0
- tpyc/_data/runtime/cpp/include/tpy/printing.hpp +302 -0
- tpyc/_data/runtime/cpp/include/tpy/protocols.hpp +61 -0
- tpyc/_data/runtime/cpp/include/tpy/range.hpp +115 -0
- tpyc/_data/runtime/cpp/include/tpy/ranges.hpp +212 -0
- tpyc/_data/runtime/cpp/include/tpy/set_ops.hpp +265 -0
- tpyc/_data/runtime/cpp/include/tpy/slice.hpp +47 -0
- tpyc/_data/runtime/cpp/include/tpy/span_iter.hpp +42 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/math.hpp +41 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/pcre2_h.hpp +96 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/random.hpp +25 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/socket_h.hpp +145 -0
- tpyc/_data/runtime/cpp/include/tpy/stdlib/time.hpp +62 -0
- tpyc/_data/runtime/cpp/include/tpy/system.hpp +121 -0
- tpyc/_data/runtime/cpp/include/tpy/throwable.hpp +55 -0
- tpyc/_data/runtime/cpp/include/tpy/tpy.hpp +156 -0
- tpyc/_data/runtime/cpp/include/tpy/type_name.hpp +77 -0
- tpyc/_data/runtime/cpp/include/tpy/type_traits.hpp +240 -0
- tpyc/_data/runtime/cpp/include/tpy/uninit_array_storage.hpp +250 -0
- tpyc/_data/runtime/cpp/include/tpy/uninit_heap_storage.hpp +277 -0
- tpyc/_data/runtime/cpp/include/tpy/varargs.hpp +174 -0
- tpyc/_data/runtime/cpp/include/tpy/variant_ref.hpp +118 -0
- tpyc/_data/runtime/cpp/src/stdlib/socket_impl.cpp +104 -0
- tpyc/_data/runtime/cpp/third_party/README.md +58 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/AUTHORS +36 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/CMakeLists.txt +1233 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/COPYING +5 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/ChangeLog +3097 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/HACKING +853 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/INSTALL +368 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/LICENCE +94 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/NEWS +492 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/NON-AUTOTOOLS-BUILD +430 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/README +956 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/COPYING-CMAKE-SCRIPTS +22 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindEditline.cmake +16 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindPackageHandleStandardArgs.cmake +58 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindReadline.cmake +29 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config-version.cmake.in +15 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config.cmake.in +148 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/config-cmake.h.in +56 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-16.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-32.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-8.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-posix.pc.in +13 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/pcre2-config.in +121 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h +483 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.generic +483 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.in +460 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.generic +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.in +1010 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_auto_possess.c +1371 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c +196 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c.dist +196 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chkdint.c +96 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_compile.c +11001 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_config.c +252 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_context.c +510 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_convert.c +1189 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dfa_match.c +4119 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dftables.c +297 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_error.c +345 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_extuni.c +162 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_find_bracket.c +219 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_fuzzsupport.c +792 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_internal.h +2084 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_intmodedep.h +940 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_compile.c +14972 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_match.c +200 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_misc.c +234 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_neon_inc.h +354 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_simd_inc.h +2355 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_test.c +2528 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_maketables.c +165 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match.c +7777 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match_data.c +185 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_newline.c +243 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ord2utf.c +120 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_pattern_info.c +432 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_printint.c +886 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_script_run.c +344 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_serialize.c +286 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_string_utils.c +237 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_study.c +1915 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substitute.c +1009 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substring.c +550 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_tables.c +234 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucd.c +5460 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucp.h +396 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucptables.c +1533 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_valid_utf.c +398 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_xclass.c +308 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2demo.c +497 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2grep.c +4606 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.c +425 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.h +187 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix_test.c +209 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2test.c +9708 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorApple.c +137 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorCore.c +327 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorFreeBSD.c +89 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorPosix.c +62 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorWindows.c +40 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorNetBSD.c +72 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorPosix.c +172 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorPosix.c +141 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorWindows.c +102 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfig.h +142 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigCPU.h +188 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigInternal.h +907 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.c +3561 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.h +2466 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_32.c +4636 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_64.c +3491 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_T2_32.c +4302 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeLOONGARCH_64.c +3765 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_32.c +472 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_64.c +387 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_common.c +4259 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_32.c +485 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_64.c +719 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_common.c +3161 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_32.c +142 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_64.c +222 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_common.c +3121 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeS390X.c +4526 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_32.c +1685 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_64.c +1398 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_common.c +5001 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitSerialize.c +516 -0
- tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitUtils.c +344 -0
- tpyc/_data/runtime/cpp/third_party/pcre2.sources.txt +54 -0
- tpyc/_data/runtime/cpp/third_party/pcre2.vendor.json +7 -0
- tpyc/build/__init__.py +7 -0
- tpyc/build/pcre2.py +122 -0
- tpyc/build/third_party.py +413 -0
- tpyc/cli.py +822 -0
- tpyc/codegen_cpp/__init__.py +18 -0
- tpyc/codegen_cpp/builtins.py +484 -0
- tpyc/codegen_cpp/context.py +2064 -0
- tpyc/codegen_cpp/expressions.py +5940 -0
- tpyc/codegen_cpp/functions.py +1913 -0
- tpyc/codegen_cpp/gen_async.py +3258 -0
- tpyc/codegen_cpp/gen_generators.py +657 -0
- tpyc/codegen_cpp/generator.py +2258 -0
- tpyc/codegen_cpp/match.py +1997 -0
- tpyc/codegen_cpp/param_const.py +172 -0
- tpyc/codegen_cpp/protocols.py +907 -0
- tpyc/codegen_cpp/records.py +1654 -0
- tpyc/codegen_cpp/resumable_cfg.py +1651 -0
- tpyc/codegen_cpp/statements.py +4963 -0
- tpyc/codegen_cpp/string_dispatch.py +76 -0
- tpyc/codegen_cpp/test_context.py +46 -0
- tpyc/codegen_cpp/test_param_const.py +113 -0
- tpyc/codegen_cpp/test_resumable_cfg.py +182 -0
- tpyc/codegen_cpp/type_resolution.py +53 -0
- tpyc/codegen_cpp/types.py +436 -0
- tpyc/codegen_cpp/variant_access.py +135 -0
- tpyc/coercions.py +749 -0
- tpyc/compilation_context.py +57 -0
- tpyc/compiler.py +3945 -0
- tpyc/cycle_detection.py +358 -0
- tpyc/diagnostics.py +135 -0
- tpyc/dump_types.py +353 -0
- tpyc/frontend_diagnostics.py +47 -0
- tpyc/frontend_ir/__init__.py +140 -0
- tpyc/frontend_ir/lower.py +1098 -0
- tpyc/frontend_ir/nodes.py +718 -0
- tpyc/frontend_ir/resolver_adapter.py +151 -0
- tpyc/frontend_plugin.py +209 -0
- tpyc/install_docs.py +81 -0
- tpyc/liveness.py +756 -0
- tpyc/macro_api.py +1724 -0
- tpyc/macro_loader.py +497 -0
- tpyc/module_names.py +64 -0
- tpyc/modules/__init__.py +31 -0
- tpyc/modules/defs.py +89 -0
- tpyc/modules/registry.py +36 -0
- tpyc/modules/resolver.py +192 -0
- tpyc/modules/type_resolution.py +629 -0
- tpyc/namespace.py +172 -0
- tpyc/parse/__init__.py +84 -0
- tpyc/parse/imports.py +490 -0
- tpyc/parse/nodes.py +1732 -0
- tpyc/parse/parser.py +4043 -0
- tpyc/parse/resolve_refs.py +466 -0
- tpyc/parse/type_resolver.py +1060 -0
- tpyc/prescan.py +254 -0
- tpyc/qnames.py +149 -0
- tpyc/repl.py +529 -0
- tpyc/repl_backends.py +848 -0
- tpyc/sema/__init__.py +21 -0
- tpyc/sema/analyzer.py +3625 -0
- tpyc/sema/bound_check.py +72 -0
- tpyc/sema/builder_trace.py +684 -0
- tpyc/sema/calls.py +5406 -0
- tpyc/sema/compatibility.py +2107 -0
- tpyc/sema/context.py +1243 -0
- tpyc/sema/expressions.py +3737 -0
- tpyc/sema/flow_facts.py +199 -0
- tpyc/sema/init_tracker.py +150 -0
- tpyc/sema/list_literals.py +69 -0
- tpyc/sema/literal_utils.py +27 -0
- tpyc/sema/local_deduction.py +1088 -0
- tpyc/sema/macros.py +179 -0
- tpyc/sema/match.py +1177 -0
- tpyc/sema/method_expansion.py +347 -0
- tpyc/sema/methods.py +2197 -0
- tpyc/sema/mutation_propagation.py +268 -0
- tpyc/sema/narrowing.py +857 -0
- tpyc/sema/numeric_lattice.py +160 -0
- tpyc/sema/operators.py +402 -0
- tpyc/sema/overloads.py +841 -0
- tpyc/sema/protocols.py +1209 -0
- tpyc/sema/reach_analysis.py +202 -0
- tpyc/sema/registration.py +3156 -0
- tpyc/sema/scope_tracker.py +193 -0
- tpyc/sema/statements.py +4426 -0
- tpyc/sema/type_ops.py +1879 -0
- tpyc/sema/value_range.py +181 -0
- tpyc/symbol_binding.py +259 -0
- tpyc/test_c3_mro.py +208 -0
- tpyc/test_cli_argv.py +52 -0
- tpyc/test_compiler.py +559 -0
- tpyc/test_contains_type_param.py +101 -0
- tpyc/test_cycle_detection.py +221 -0
- tpyc/test_dump_types.py +225 -0
- tpyc/test_install_docs.py +65 -0
- tpyc/test_local_cpp_form.py +135 -0
- tpyc/test_macro_loader.py +76 -0
- tpyc/test_method_expansion.py +254 -0
- tpyc/test_nominal_identity.py +182 -0
- tpyc/test_overloads.py +410 -0
- tpyc/test_parse.py +303 -0
- tpyc/test_parse_type_ref.py +506 -0
- tpyc/test_parse_version_info.py +58 -0
- tpyc/test_reach_analysis.py +72 -0
- tpyc/test_ref_type.py +216 -0
- tpyc/test_send_sync_substitution.py +276 -0
- tpyc/test_tuple_mutation_propagation.py +206 -0
- tpyc/test_type_def_registry.py +1729 -0
- tpyc/test_union_types.py +195 -0
- tpyc/type_def_registry.py +975 -0
- tpyc/typesys.py +5104 -0
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
"""Lowering pass: FrontendModule (IR) -> TpyModule (parser AST).
|
|
2
|
+
|
|
3
|
+
Lowering owns the IR -> TpyModule adapter and the parser-internal field
|
|
4
|
+
synthesis that a Python parse would normally perform (import dicts,
|
|
5
|
+
TpyImport emission, call-site `resolved_import` tagging).
|
|
6
|
+
|
|
7
|
+
It does NOT resolve names or types -- that is sema's job. For M1 the IR
|
|
8
|
+
has no type expressions, so symbolic-type lowering is stubbed.
|
|
9
|
+
|
|
10
|
+
Diagnostics emitted here use the `FrontendDiagnostic` envelope with
|
|
11
|
+
category `PLUGIN_IR_INVALID` (structural problem in plugin output) or
|
|
12
|
+
`LOWERING_INTERNAL` (compiler bug after validation passed).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Iterable
|
|
20
|
+
|
|
21
|
+
from ..diagnostics import Diagnostic, DiagnosticLevel
|
|
22
|
+
from ..frontend_diagnostics import FrontendDiagnostic, FrontendDiagnosticCategory
|
|
23
|
+
from ..parse.nodes import (
|
|
24
|
+
ModuleDirectives,
|
|
25
|
+
SourceLocation,
|
|
26
|
+
TpyArrayLiteral,
|
|
27
|
+
TpyAsPattern,
|
|
28
|
+
TpyAssign,
|
|
29
|
+
TpyBinOp,
|
|
30
|
+
TpyCallableRef,
|
|
31
|
+
TpyClassPattern,
|
|
32
|
+
TpyBoolLiteral,
|
|
33
|
+
TpyBreak,
|
|
34
|
+
TpyCall,
|
|
35
|
+
TpyContinue,
|
|
36
|
+
TpyEnum,
|
|
37
|
+
TpyExprStmt,
|
|
38
|
+
TpyFieldAccess,
|
|
39
|
+
TpyFloatLiteral,
|
|
40
|
+
TpyForEach,
|
|
41
|
+
TpyFunction,
|
|
42
|
+
TpyIf,
|
|
43
|
+
TpyImport,
|
|
44
|
+
TpyIntLiteral,
|
|
45
|
+
TpyLiteralPattern,
|
|
46
|
+
TpyMatch,
|
|
47
|
+
TpyMatchCase,
|
|
48
|
+
TpyMethodCall,
|
|
49
|
+
TpyModule,
|
|
50
|
+
TpyName,
|
|
51
|
+
TpyNoneLiteral,
|
|
52
|
+
TpyRaise,
|
|
53
|
+
TpyRecord,
|
|
54
|
+
TpyReturn,
|
|
55
|
+
TpySetLiteral,
|
|
56
|
+
TpyStrLiteral,
|
|
57
|
+
TpySubscript,
|
|
58
|
+
TpyTypeRef,
|
|
59
|
+
TpyUnaryOp,
|
|
60
|
+
TpyUnionRef,
|
|
61
|
+
TpyValuePattern,
|
|
62
|
+
TpyVarDecl,
|
|
63
|
+
TpyWhile,
|
|
64
|
+
TpyWildcardPattern,
|
|
65
|
+
)
|
|
66
|
+
from ..typesys import FieldInfo, RecordInfo
|
|
67
|
+
from .resolver_adapter import make_plugin_resolver
|
|
68
|
+
from .nodes import (
|
|
69
|
+
API_VERSION,
|
|
70
|
+
Assign,
|
|
71
|
+
Attr,
|
|
72
|
+
BinOp,
|
|
73
|
+
BinOpKind,
|
|
74
|
+
BoolLit,
|
|
75
|
+
Break,
|
|
76
|
+
Call,
|
|
77
|
+
CallableType,
|
|
78
|
+
CmpOpKind,
|
|
79
|
+
Compare,
|
|
80
|
+
Continue,
|
|
81
|
+
Enum,
|
|
82
|
+
EnumValue,
|
|
83
|
+
ExprStmt,
|
|
84
|
+
Field,
|
|
85
|
+
FloatLit,
|
|
86
|
+
ForEach,
|
|
87
|
+
ForRange,
|
|
88
|
+
FrontendModule,
|
|
89
|
+
FromImport,
|
|
90
|
+
Function,
|
|
91
|
+
If,
|
|
92
|
+
Import,
|
|
93
|
+
IntLit,
|
|
94
|
+
IntTypeArg,
|
|
95
|
+
ListLit,
|
|
96
|
+
Loc,
|
|
97
|
+
Match,
|
|
98
|
+
MatchCase,
|
|
99
|
+
MatchClass,
|
|
100
|
+
MatchValue,
|
|
101
|
+
MatchWildcard,
|
|
102
|
+
Name,
|
|
103
|
+
NamedType,
|
|
104
|
+
NoneLit,
|
|
105
|
+
Param,
|
|
106
|
+
PointerType,
|
|
107
|
+
Raise,
|
|
108
|
+
RangeDir,
|
|
109
|
+
Record,
|
|
110
|
+
RepeatUntil,
|
|
111
|
+
Return,
|
|
112
|
+
SetLit,
|
|
113
|
+
StarImport,
|
|
114
|
+
StrLit,
|
|
115
|
+
Subscript,
|
|
116
|
+
TypeArg,
|
|
117
|
+
TypeExpr,
|
|
118
|
+
TypeTypeArg,
|
|
119
|
+
UnaryOp,
|
|
120
|
+
UnaryOpKind,
|
|
121
|
+
UnionType,
|
|
122
|
+
VarDecl,
|
|
123
|
+
While,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Map IR operator enums onto the string opcodes TPy's parser AST uses
|
|
128
|
+
# (see `tpyc/parse/parser.py` -- `_BINOP_TO_STR` / `_UNARYOP_TO_STR`).
|
|
129
|
+
_BINOP_OP_STR: dict[BinOpKind, str] = {
|
|
130
|
+
BinOpKind.ADD: "+",
|
|
131
|
+
BinOpKind.SUB: "-",
|
|
132
|
+
BinOpKind.MUL: "*",
|
|
133
|
+
BinOpKind.TRUE_DIV: "div",
|
|
134
|
+
BinOpKind.FLOOR_DIV: "//",
|
|
135
|
+
BinOpKind.MOD: "%",
|
|
136
|
+
BinOpKind.POW: "**",
|
|
137
|
+
BinOpKind.BIT_OR: "|",
|
|
138
|
+
BinOpKind.BIT_XOR: "^",
|
|
139
|
+
BinOpKind.BIT_AND: "&",
|
|
140
|
+
BinOpKind.LSHIFT: "<<",
|
|
141
|
+
BinOpKind.RSHIFT: ">>",
|
|
142
|
+
BinOpKind.LOGICAL_AND: "&&",
|
|
143
|
+
BinOpKind.LOGICAL_OR: "||",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_UNARYOP_OP_STR: dict[UnaryOpKind, str] = {
|
|
147
|
+
UnaryOpKind.POS: "+",
|
|
148
|
+
UnaryOpKind.NEG: "-",
|
|
149
|
+
UnaryOpKind.NOT: "!",
|
|
150
|
+
UnaryOpKind.INVERT: "~",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# IR comparison enum -> parser string opcode (matches the parser's
|
|
154
|
+
# `_CMPOP_TO_STR` table). Used for both the single-op case (lowers to
|
|
155
|
+
# `TpyBinOp`) and the chained-op case (lowers to `TpyChainedCompare`).
|
|
156
|
+
_CMPOP_OP_STR: dict[CmpOpKind, str] = {
|
|
157
|
+
CmpOpKind.EQ: "==",
|
|
158
|
+
CmpOpKind.NE: "!=",
|
|
159
|
+
CmpOpKind.LT: "<",
|
|
160
|
+
CmpOpKind.LE: "<=",
|
|
161
|
+
CmpOpKind.GT: ">",
|
|
162
|
+
CmpOpKind.GE: ">=",
|
|
163
|
+
CmpOpKind.IS: "is",
|
|
164
|
+
CmpOpKind.IS_NOT: "is not",
|
|
165
|
+
CmpOpKind.IN: "in",
|
|
166
|
+
CmpOpKind.NOT_IN: "not in",
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@dataclass
|
|
171
|
+
class LoweredFrontend:
|
|
172
|
+
"""Result of lowering one FrontendModule.
|
|
173
|
+
|
|
174
|
+
`module` is `None` when validation failed before a partial TpyModule
|
|
175
|
+
could be constructed; callers should bail on the affected module.
|
|
176
|
+
`diagnostics` holds wrapped FrontendDiagnostics produced by lowering
|
|
177
|
+
plus any diagnostics the plugin returned in its FrontendOutput.
|
|
178
|
+
"""
|
|
179
|
+
module: TpyModule | None
|
|
180
|
+
diagnostics: list[FrontendDiagnostic] = field(default_factory=list)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def lower_module(
|
|
184
|
+
fm: FrontendModule,
|
|
185
|
+
plugin_name: str,
|
|
186
|
+
plugin_diagnostics: Iterable[Diagnostic] = (),
|
|
187
|
+
*,
|
|
188
|
+
is_entry_point: bool = False,
|
|
189
|
+
) -> LoweredFrontend:
|
|
190
|
+
"""Lower a FrontendModule produced by a plugin to a TpyModule.
|
|
191
|
+
|
|
192
|
+
`plugin_name` is used to tag wrapped diagnostics from the plugin.
|
|
193
|
+
`plugin_diagnostics` come from `FrontendOutput.diagnostics`; they
|
|
194
|
+
are wrapped with category `PLUGIN_REPORTED` and propagated.
|
|
195
|
+
`is_entry_point` mirrors TPy's parser-side convention: the
|
|
196
|
+
entry-point module's records and enums are minted with qname prefix
|
|
197
|
+
`__main__` (matching sema's `ctx.module_name = "__main__"` rename)
|
|
198
|
+
so resolver-side placeholders agree with sema's
|
|
199
|
+
`attach_dynamic_type_def` registrations downstream.
|
|
200
|
+
"""
|
|
201
|
+
diags: list[FrontendDiagnostic] = []
|
|
202
|
+
|
|
203
|
+
for d in plugin_diagnostics:
|
|
204
|
+
diags.append(FrontendDiagnostic(
|
|
205
|
+
diagnostic=d,
|
|
206
|
+
category=FrontendDiagnosticCategory.PLUGIN_REPORTED,
|
|
207
|
+
plugin_name=plugin_name,
|
|
208
|
+
source_language=fm.source_language or None,
|
|
209
|
+
))
|
|
210
|
+
plugin_had_error = any(
|
|
211
|
+
fd.diagnostic.level == DiagnosticLevel.ERROR
|
|
212
|
+
and fd.category == FrontendDiagnosticCategory.PLUGIN_REPORTED
|
|
213
|
+
for fd in diags
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Structural validation. Failures here surface as PLUGIN_IR_INVALID.
|
|
217
|
+
if fm.api_version != API_VERSION:
|
|
218
|
+
diags.append(_ir_invalid(
|
|
219
|
+
plugin_name, fm,
|
|
220
|
+
f"FrontendModule.api_version={fm.api_version}; "
|
|
221
|
+
f"compiler supports api_version={API_VERSION}",
|
|
222
|
+
))
|
|
223
|
+
return LoweredFrontend(module=None, diagnostics=diags)
|
|
224
|
+
|
|
225
|
+
if not fm.qname:
|
|
226
|
+
diags.append(_ir_invalid(
|
|
227
|
+
plugin_name, fm,
|
|
228
|
+
"FrontendModule.qname is empty",
|
|
229
|
+
))
|
|
230
|
+
return LoweredFrontend(module=None, diagnostics=diags)
|
|
231
|
+
|
|
232
|
+
# Imports: build the four dicts the parser/sema expect and emit
|
|
233
|
+
# corresponding TpyImport statements at the head of top_level_stmts.
|
|
234
|
+
# `module_aliases` follows the parser convention -- canonical name
|
|
235
|
+
# keys the local alias -- not the reverse map used at lookup time.
|
|
236
|
+
module_imports: dict[str, set[tuple[str, str]] | None] = {}
|
|
237
|
+
user_module_imports: dict[str, int] = {}
|
|
238
|
+
bare_module_imports: set[str] = set()
|
|
239
|
+
module_aliases: dict[str, str] = {}
|
|
240
|
+
name_to_origin: dict[str, tuple[str, str]] = {}
|
|
241
|
+
leading_imports: list[TpyImport] = []
|
|
242
|
+
star_imports: set[str] = set()
|
|
243
|
+
|
|
244
|
+
for imp in fm.imports:
|
|
245
|
+
loc = _to_source_loc(imp.loc)
|
|
246
|
+
lineno = loc.line if loc else 0
|
|
247
|
+
if isinstance(imp, Import):
|
|
248
|
+
if not imp.module:
|
|
249
|
+
diags.append(_ir_invalid(
|
|
250
|
+
plugin_name, fm, "Import.module is empty"))
|
|
251
|
+
continue
|
|
252
|
+
if imp.module not in module_imports:
|
|
253
|
+
module_imports[imp.module] = set()
|
|
254
|
+
user_module_imports[imp.module] = lineno
|
|
255
|
+
bare_module_imports.add(imp.module)
|
|
256
|
+
if imp.alias is not None:
|
|
257
|
+
module_aliases[imp.module] = imp.alias
|
|
258
|
+
leading_imports.append(TpyImport(
|
|
259
|
+
module_name=imp.module, alias=imp.alias, loc=loc))
|
|
260
|
+
elif isinstance(imp, FromImport):
|
|
261
|
+
if not imp.module:
|
|
262
|
+
diags.append(_ir_invalid(
|
|
263
|
+
plugin_name, fm, "FromImport.module is empty"))
|
|
264
|
+
continue
|
|
265
|
+
bucket = module_imports.setdefault(imp.module, set())
|
|
266
|
+
assert isinstance(bucket, set) # never None for FromImport
|
|
267
|
+
for nm in imp.names:
|
|
268
|
+
bucket.add((nm.original, nm.local))
|
|
269
|
+
name_to_origin[nm.local] = (imp.module, nm.original)
|
|
270
|
+
user_module_imports[imp.module] = lineno
|
|
271
|
+
leading_imports.append(TpyImport(
|
|
272
|
+
module_name=imp.module, loc=loc))
|
|
273
|
+
elif isinstance(imp, StarImport):
|
|
274
|
+
if not imp.module:
|
|
275
|
+
diags.append(_ir_invalid(
|
|
276
|
+
plugin_name, fm, "StarImport.module is empty"))
|
|
277
|
+
continue
|
|
278
|
+
# The compiler's `_expand_star_imports_for_module` consumes
|
|
279
|
+
# `star_imports` + `module_imports[mod] = set()` and fans
|
|
280
|
+
# the imported module's public surface into the importer.
|
|
281
|
+
# Implicit-stdlib star imports (`tpy` / `builtins` /
|
|
282
|
+
# `typing`) are handled at parse time by the .py parser
|
|
283
|
+
# itself; plugin lowering doesn't take that path because
|
|
284
|
+
# source-language plugins would name a non-implicit
|
|
285
|
+
# module here.
|
|
286
|
+
module_imports.setdefault(imp.module, set())
|
|
287
|
+
user_module_imports[imp.module] = lineno
|
|
288
|
+
star_imports.add(imp.module)
|
|
289
|
+
leading_imports.append(TpyImport(
|
|
290
|
+
module_name=imp.module, loc=loc))
|
|
291
|
+
else:
|
|
292
|
+
diags.append(_ir_invalid(
|
|
293
|
+
plugin_name, fm,
|
|
294
|
+
f"unknown import node kind: {type(imp).__name__}",
|
|
295
|
+
))
|
|
296
|
+
|
|
297
|
+
# Build the parser-adapter / TypeResolver early so we can register
|
|
298
|
+
# user records into the adapter's registry before lowering any
|
|
299
|
+
# bodies that might reference them. The same TypeResolver the .py
|
|
300
|
+
# parser uses runs over our adapter -- it reads parser-internal
|
|
301
|
+
# attributes/methods exposed on the adapter object.
|
|
302
|
+
resolver = make_plugin_resolver(
|
|
303
|
+
module_name=fm.qname,
|
|
304
|
+
imports=dict(module_imports),
|
|
305
|
+
name_index=tuple(
|
|
306
|
+
(local, mod, original)
|
|
307
|
+
for local, (mod, original) in name_to_origin.items()
|
|
308
|
+
),
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Records: lower each and register the resulting RecordInfo into
|
|
312
|
+
# the resolver's registry so type references downstream (var decls,
|
|
313
|
+
# function params, other records) can resolve user record names.
|
|
314
|
+
# For the entry point we mint qnames against `__main__`; sema
|
|
315
|
+
# follows the same rule for the entry module, so this keeps
|
|
316
|
+
# parser-side and sema-side qnames in lockstep.
|
|
317
|
+
qname_prefix = "__main__" if is_entry_point else fm.qname
|
|
318
|
+
tpy_records: list[TpyRecord] = []
|
|
319
|
+
record_class_names: set[str] = set()
|
|
320
|
+
for rec in fm.records:
|
|
321
|
+
lowered_rec, rinfo = _lower_record(
|
|
322
|
+
rec, plugin_name, fm, diags, qname_prefix)
|
|
323
|
+
if lowered_rec is None or rinfo is None:
|
|
324
|
+
continue
|
|
325
|
+
tpy_records.append(lowered_rec)
|
|
326
|
+
resolver.registry.register_record(rinfo)
|
|
327
|
+
record_class_names.add(rec.name)
|
|
328
|
+
if record_class_names:
|
|
329
|
+
# Adapter exposes a `frozenset` here; merge with whatever the
|
|
330
|
+
# resolver-adapter constructor pre-populated (currently empty).
|
|
331
|
+
resolver._parser._module_class_names = frozenset(
|
|
332
|
+
resolver._parser._module_class_names | record_class_names
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Enums: lower each and register a placeholder NominalType so the
|
|
336
|
+
# resolver can route `EnumName` and `EnumName.Member` references.
|
|
337
|
+
# Sema's `register_enum` re-registers later with the fully-populated
|
|
338
|
+
# NominalType + TypeDef.enum payload (members, underlying type, ...).
|
|
339
|
+
tpy_enums: list[TpyEnum] = []
|
|
340
|
+
for en in fm.enums:
|
|
341
|
+
lowered_en = _lower_enum(en, plugin_name, fm, diags)
|
|
342
|
+
if lowered_en is None:
|
|
343
|
+
continue
|
|
344
|
+
tpy_enums.append(lowered_en)
|
|
345
|
+
resolver.registry.register_enum_placeholder(
|
|
346
|
+
en.name, module=qname_prefix,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Functions: lower each to a TpyFunction. Function bodies do not
|
|
350
|
+
# share the module-level statement list, so lowering them before
|
|
351
|
+
# walking top_level_stmts keeps things tidy.
|
|
352
|
+
tpy_functions: list[TpyFunction] = []
|
|
353
|
+
for fn in fm.functions:
|
|
354
|
+
lowered_fn = _lower_function(fn, name_to_origin, plugin_name, fm, diags)
|
|
355
|
+
if lowered_fn is not None:
|
|
356
|
+
tpy_functions.append(lowered_fn)
|
|
357
|
+
|
|
358
|
+
# Statements
|
|
359
|
+
top_level_stmts = list(leading_imports)
|
|
360
|
+
for stmt in fm.top_level_stmts:
|
|
361
|
+
lowered = _lower_stmt(stmt, name_to_origin, plugin_name, fm, diags)
|
|
362
|
+
if lowered is not None:
|
|
363
|
+
top_level_stmts.append(lowered)
|
|
364
|
+
|
|
365
|
+
# Directives: map FrontendDirectives onto ModuleDirectives.
|
|
366
|
+
directives = ModuleDirectives(
|
|
367
|
+
includes=list(fm.directives.cpp_includes),
|
|
368
|
+
link_libs=list(fm.directives.link_libs),
|
|
369
|
+
third_party_deps=list(fm.directives.third_party_deps),
|
|
370
|
+
native_module=fm.directives.native_module,
|
|
371
|
+
cpp_namespace=fm.directives.cpp_namespace,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
has_ir_error = any(
|
|
375
|
+
fd.category == FrontendDiagnosticCategory.PLUGIN_IR_INVALID
|
|
376
|
+
for fd in diags
|
|
377
|
+
)
|
|
378
|
+
if has_ir_error or plugin_had_error:
|
|
379
|
+
return LoweredFrontend(module=None, diagnostics=diags)
|
|
380
|
+
|
|
381
|
+
module = TpyModule(
|
|
382
|
+
records=tpy_records,
|
|
383
|
+
functions=tpy_functions,
|
|
384
|
+
protocols=[],
|
|
385
|
+
enums=tpy_enums,
|
|
386
|
+
top_level_stmts=top_level_stmts,
|
|
387
|
+
source_lines=list(fm.source_lines),
|
|
388
|
+
imports=module_imports,
|
|
389
|
+
user_module_imports=user_module_imports,
|
|
390
|
+
module_aliases=module_aliases,
|
|
391
|
+
bare_module_imports=bare_module_imports,
|
|
392
|
+
star_imports=star_imports,
|
|
393
|
+
directives=directives,
|
|
394
|
+
resolver=resolver,
|
|
395
|
+
)
|
|
396
|
+
return LoweredFrontend(module=module, diagnostics=diags)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _lower_stmt(
|
|
400
|
+
stmt,
|
|
401
|
+
name_to_origin: dict[str, tuple[str, str]],
|
|
402
|
+
plugin_name: str,
|
|
403
|
+
fm: FrontendModule,
|
|
404
|
+
diags: list[FrontendDiagnostic],
|
|
405
|
+
):
|
|
406
|
+
loc = _to_source_loc(stmt.loc) if getattr(stmt, "loc", None) else None
|
|
407
|
+
if isinstance(stmt, ExprStmt):
|
|
408
|
+
expr = _lower_expr(stmt.value, name_to_origin, plugin_name, fm, diags)
|
|
409
|
+
if expr is None:
|
|
410
|
+
return None
|
|
411
|
+
out = TpyExprStmt(expr=expr)
|
|
412
|
+
out.loc = loc
|
|
413
|
+
return out
|
|
414
|
+
if isinstance(stmt, VarDecl):
|
|
415
|
+
type_ref = (_lower_type(stmt.type, plugin_name, fm, diags)
|
|
416
|
+
if stmt.type is not None else None)
|
|
417
|
+
init = (_lower_expr(stmt.init, name_to_origin, plugin_name, fm, diags)
|
|
418
|
+
if stmt.init is not None else None)
|
|
419
|
+
if stmt.init is not None and init is None:
|
|
420
|
+
return None
|
|
421
|
+
return TpyVarDecl(name=stmt.name, type=type_ref, init=init, loc=loc)
|
|
422
|
+
if isinstance(stmt, Assign):
|
|
423
|
+
if len(stmt.targets) != 1:
|
|
424
|
+
diags.append(_ir_invalid(
|
|
425
|
+
plugin_name, fm,
|
|
426
|
+
"Assign requires exactly one target in M2 "
|
|
427
|
+
"(chained assignment lands later)",
|
|
428
|
+
))
|
|
429
|
+
return None
|
|
430
|
+
target = _lower_expr(stmt.targets[0], name_to_origin, plugin_name, fm, diags)
|
|
431
|
+
value = _lower_expr(stmt.value, name_to_origin, plugin_name, fm, diags)
|
|
432
|
+
if target is None or value is None:
|
|
433
|
+
return None
|
|
434
|
+
return TpyAssign(target=target, value=value, loc=loc)
|
|
435
|
+
if isinstance(stmt, If):
|
|
436
|
+
cond = _lower_expr(stmt.cond, name_to_origin, plugin_name, fm, diags)
|
|
437
|
+
if cond is None:
|
|
438
|
+
return None
|
|
439
|
+
then_body = _lower_stmt_list(
|
|
440
|
+
stmt.then_body, name_to_origin, plugin_name, fm, diags)
|
|
441
|
+
else_body = _lower_stmt_list(
|
|
442
|
+
stmt.else_body, name_to_origin, plugin_name, fm, diags)
|
|
443
|
+
return TpyIf(condition=cond, then_body=then_body, else_body=else_body, loc=loc)
|
|
444
|
+
if isinstance(stmt, While):
|
|
445
|
+
cond = _lower_expr(stmt.cond, name_to_origin, plugin_name, fm, diags)
|
|
446
|
+
if cond is None:
|
|
447
|
+
return None
|
|
448
|
+
body = _lower_stmt_list(
|
|
449
|
+
stmt.body, name_to_origin, plugin_name, fm, diags)
|
|
450
|
+
return TpyWhile(condition=cond, body=body, loc=loc)
|
|
451
|
+
if isinstance(stmt, RepeatUntil):
|
|
452
|
+
# Desugar to `while True: body; if cond: break`. Pascal's
|
|
453
|
+
# `repeat...until` runs the body at least once and exits when
|
|
454
|
+
# cond becomes true; the `while True` shape preserves that.
|
|
455
|
+
cond = _lower_expr(stmt.cond, name_to_origin, plugin_name, fm, diags)
|
|
456
|
+
if cond is None:
|
|
457
|
+
return None
|
|
458
|
+
body = _lower_stmt_list(
|
|
459
|
+
stmt.body, name_to_origin, plugin_name, fm, diags)
|
|
460
|
+
body.append(TpyIf(
|
|
461
|
+
condition=cond,
|
|
462
|
+
then_body=[TpyBreak(loc=loc)],
|
|
463
|
+
else_body=[],
|
|
464
|
+
loc=loc,
|
|
465
|
+
))
|
|
466
|
+
return TpyWhile(
|
|
467
|
+
condition=TpyBoolLiteral(value=True, loc=loc),
|
|
468
|
+
body=body, loc=loc,
|
|
469
|
+
)
|
|
470
|
+
if isinstance(stmt, ForRange):
|
|
471
|
+
# Desugar to `for var in range(start, end[+1], [-1]): body`.
|
|
472
|
+
# `inclusive=True` (Pascal default) bumps the endpoint by one
|
|
473
|
+
# so the upper bound is visited; descending loops use a step
|
|
474
|
+
# of -1 and bump the endpoint by -1 instead.
|
|
475
|
+
start = _lower_expr(stmt.start, name_to_origin, plugin_name, fm, diags)
|
|
476
|
+
end = _lower_expr(stmt.end, name_to_origin, plugin_name, fm, diags)
|
|
477
|
+
if start is None or end is None:
|
|
478
|
+
return None
|
|
479
|
+
bump = 1 if stmt.direction == RangeDir.ASC else -1
|
|
480
|
+
if stmt.inclusive:
|
|
481
|
+
end = TpyBinOp(
|
|
482
|
+
left=end,
|
|
483
|
+
op="+" if bump > 0 else "-",
|
|
484
|
+
right=TpyIntLiteral(value=1, loc=loc),
|
|
485
|
+
loc=loc,
|
|
486
|
+
)
|
|
487
|
+
range_args = [start, end]
|
|
488
|
+
if bump != 1:
|
|
489
|
+
range_args.append(TpyIntLiteral(value=bump, loc=loc))
|
|
490
|
+
range_call = TpyCall(
|
|
491
|
+
func=TpyName(name="range", loc=loc),
|
|
492
|
+
args=range_args,
|
|
493
|
+
loc=loc,
|
|
494
|
+
)
|
|
495
|
+
body = _lower_stmt_list(
|
|
496
|
+
stmt.body, name_to_origin, plugin_name, fm, diags)
|
|
497
|
+
return TpyForEach(var=stmt.var, iterable=range_call, body=body, loc=loc)
|
|
498
|
+
if isinstance(stmt, ForEach):
|
|
499
|
+
it = _lower_expr(stmt.iter, name_to_origin, plugin_name, fm, diags)
|
|
500
|
+
if it is None:
|
|
501
|
+
return None
|
|
502
|
+
body = _lower_stmt_list(
|
|
503
|
+
stmt.body, name_to_origin, plugin_name, fm, diags)
|
|
504
|
+
return TpyForEach(var=stmt.var, iterable=it, body=body, loc=loc)
|
|
505
|
+
if isinstance(stmt, Return):
|
|
506
|
+
value = None
|
|
507
|
+
if stmt.value is not None:
|
|
508
|
+
value = _lower_expr(stmt.value, name_to_origin,
|
|
509
|
+
plugin_name, fm, diags)
|
|
510
|
+
if value is None:
|
|
511
|
+
return None
|
|
512
|
+
return TpyReturn(value=value, loc=loc)
|
|
513
|
+
if isinstance(stmt, Break):
|
|
514
|
+
return TpyBreak(loc=loc)
|
|
515
|
+
if isinstance(stmt, Continue):
|
|
516
|
+
return TpyContinue(loc=loc)
|
|
517
|
+
if isinstance(stmt, Raise):
|
|
518
|
+
value = None
|
|
519
|
+
if stmt.value is not None:
|
|
520
|
+
value = _lower_expr(stmt.value, name_to_origin,
|
|
521
|
+
plugin_name, fm, diags)
|
|
522
|
+
if value is None:
|
|
523
|
+
return None
|
|
524
|
+
out = TpyRaise(raise_expr=value)
|
|
525
|
+
out.loc = loc
|
|
526
|
+
return out
|
|
527
|
+
if isinstance(stmt, Match):
|
|
528
|
+
subject = _lower_expr(stmt.subject, name_to_origin, plugin_name, fm, diags)
|
|
529
|
+
if subject is None:
|
|
530
|
+
return None
|
|
531
|
+
tpy_cases: list[TpyMatchCase] = []
|
|
532
|
+
for case in stmt.cases:
|
|
533
|
+
pattern = _lower_pattern(case.pattern, name_to_origin,
|
|
534
|
+
plugin_name, fm, diags)
|
|
535
|
+
if pattern is None:
|
|
536
|
+
return None
|
|
537
|
+
guard = None
|
|
538
|
+
if case.guard is not None:
|
|
539
|
+
guard = _lower_expr(case.guard, name_to_origin,
|
|
540
|
+
plugin_name, fm, diags)
|
|
541
|
+
if guard is None:
|
|
542
|
+
return None
|
|
543
|
+
body = _lower_stmt_list(
|
|
544
|
+
case.body, name_to_origin, plugin_name, fm, diags)
|
|
545
|
+
tpy_cases.append(TpyMatchCase(
|
|
546
|
+
pattern=pattern, guard=guard, body=body,
|
|
547
|
+
loc=_to_source_loc(case.loc),
|
|
548
|
+
))
|
|
549
|
+
return TpyMatch(subject=subject, cases=tpy_cases, loc=loc)
|
|
550
|
+
diags.append(_ir_invalid(
|
|
551
|
+
plugin_name, fm,
|
|
552
|
+
f"unsupported top-level stmt kind: {type(stmt).__name__}",
|
|
553
|
+
))
|
|
554
|
+
return None
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def _lower_stmt_list(stmts, name_to_origin, plugin_name, fm, diags):
|
|
558
|
+
out: list = []
|
|
559
|
+
for s in stmts:
|
|
560
|
+
lowered = _lower_stmt(s, name_to_origin, plugin_name, fm, diags)
|
|
561
|
+
if lowered is not None:
|
|
562
|
+
out.append(lowered)
|
|
563
|
+
return out
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def _lower_enum(
|
|
567
|
+
en: Enum,
|
|
568
|
+
plugin_name: str,
|
|
569
|
+
fm: FrontendModule,
|
|
570
|
+
diags: list[FrontendDiagnostic],
|
|
571
|
+
) -> TpyEnum | None:
|
|
572
|
+
"""Lower an IR `Enum` to a `TpyEnum`.
|
|
573
|
+
|
|
574
|
+
M7 emits Pascal-style auto-numbered enums (members get values 0, 1,
|
|
575
|
+
2, ...). Explicit member values from `EnumValue.value` are accepted
|
|
576
|
+
only when they are `IntLit`; richer expressions wait on a later
|
|
577
|
+
milestone since TpyEnum's members slot is `(name, int, loc)` --
|
|
578
|
+
pre-resolved at parser/lowering time.
|
|
579
|
+
"""
|
|
580
|
+
members: list = []
|
|
581
|
+
auto_index = 0
|
|
582
|
+
for v in en.values:
|
|
583
|
+
if v.value is None:
|
|
584
|
+
int_value = auto_index
|
|
585
|
+
elif isinstance(v.value, IntLit):
|
|
586
|
+
int_value = v.value.value
|
|
587
|
+
else:
|
|
588
|
+
diags.append(_ir_invalid(
|
|
589
|
+
plugin_name, fm,
|
|
590
|
+
"EnumValue.value must be IntLit or None in M7 "
|
|
591
|
+
"(auto-numbering / explicit int literal)",
|
|
592
|
+
))
|
|
593
|
+
return None
|
|
594
|
+
members.append((v.name, int_value, _to_source_loc(v.loc)))
|
|
595
|
+
auto_index = int_value + 1
|
|
596
|
+
return TpyEnum(
|
|
597
|
+
name=en.name,
|
|
598
|
+
members=members,
|
|
599
|
+
is_int_enum=False,
|
|
600
|
+
loc=_to_source_loc(en.loc),
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def _is_known_record_name(name: str, fm: FrontendModule) -> bool:
|
|
605
|
+
"""True iff `name` matches the name of a record declared in this
|
|
606
|
+
FrontendModule. Used during expression lowering to recognise
|
|
607
|
+
constructor calls (`Point()`) so the resulting TpyCall carries a
|
|
608
|
+
`call_type` -- the same shape the Python parser emits for
|
|
609
|
+
type-instantiation calls.
|
|
610
|
+
"""
|
|
611
|
+
return any(rec.name == name for rec in fm.records)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _lower_record(
|
|
615
|
+
rec: Record,
|
|
616
|
+
plugin_name: str,
|
|
617
|
+
fm: FrontendModule,
|
|
618
|
+
diags: list[FrontendDiagnostic],
|
|
619
|
+
qname_prefix: str,
|
|
620
|
+
) -> tuple[TpyRecord | None, RecordInfo | None]:
|
|
621
|
+
"""Lower an IR `Record` to a `TpyRecord` plus the parallel
|
|
622
|
+
`RecordInfo` the resolver registry consumes.
|
|
623
|
+
|
|
624
|
+
Field types lower to `TpyTypeRef`; sema's `resolve_refs` pass turns
|
|
625
|
+
them into real `TpyType`s using the resolver. The TpyRecord and
|
|
626
|
+
RecordInfo share the same FieldInfo objects (parser convention),
|
|
627
|
+
so mutation by either downstream consumer is visible to the other.
|
|
628
|
+
"""
|
|
629
|
+
field_infos: list[FieldInfo] = []
|
|
630
|
+
for f in rec.fields:
|
|
631
|
+
t = _lower_type(f.type, plugin_name, fm, diags)
|
|
632
|
+
if t is None:
|
|
633
|
+
return None, None
|
|
634
|
+
default_expr = None
|
|
635
|
+
if f.default is not None:
|
|
636
|
+
default_expr = _lower_expr(
|
|
637
|
+
f.default, {}, plugin_name, fm, diags)
|
|
638
|
+
if default_expr is None:
|
|
639
|
+
return None, None
|
|
640
|
+
field_infos.append(FieldInfo(
|
|
641
|
+
name=f.name, type=t,
|
|
642
|
+
default_expr=default_expr,
|
|
643
|
+
loc=_to_source_loc(f.loc),
|
|
644
|
+
))
|
|
645
|
+
if rec.nested_records or rec.nested_enums:
|
|
646
|
+
diags.append(_ir_invalid(
|
|
647
|
+
plugin_name, fm,
|
|
648
|
+
"nested records / enums are reserved for later milestones",
|
|
649
|
+
))
|
|
650
|
+
return None, None
|
|
651
|
+
methods: list[TpyFunction] = []
|
|
652
|
+
for m in rec.methods:
|
|
653
|
+
lowered = _lower_function(m, {}, plugin_name, fm, diags)
|
|
654
|
+
if lowered is None:
|
|
655
|
+
return None, None
|
|
656
|
+
lowered.is_method = True
|
|
657
|
+
# Strip the leading `self` parameter from `params`: TPy's
|
|
658
|
+
# method-arg accounting (sema's `init_params`, arity checks,
|
|
659
|
+
# etc.) excludes self. The IR carries it because plugins
|
|
660
|
+
# build method bodies that reference `self` by name; for the
|
|
661
|
+
# TpyFunction the receiver is implicit.
|
|
662
|
+
if lowered.params and lowered.params[0][0] == "self":
|
|
663
|
+
lowered.params = lowered.params[1:]
|
|
664
|
+
if m.is_property_getter:
|
|
665
|
+
lowered.is_property_getter = True
|
|
666
|
+
if m.is_property_setter:
|
|
667
|
+
lowered.is_property_setter = True
|
|
668
|
+
if m.property_name is not None:
|
|
669
|
+
lowered.property_name = m.property_name
|
|
670
|
+
methods.append(lowered)
|
|
671
|
+
tpy_rec = TpyRecord(
|
|
672
|
+
name=rec.name,
|
|
673
|
+
fields=field_infos,
|
|
674
|
+
methods=methods,
|
|
675
|
+
)
|
|
676
|
+
# RecordInfo's `module` is the public module qname; entry-point
|
|
677
|
+
# modules use `__main__` (matches sema's `ctx.module_name` rename
|
|
678
|
+
# for the entry point), other modules use their dotted name.
|
|
679
|
+
# Builtin-type-key stays unset because plugin records aren't
|
|
680
|
+
# `@builtin_type` decorated.
|
|
681
|
+
record_info = RecordInfo(
|
|
682
|
+
name=rec.name,
|
|
683
|
+
fields=field_infos,
|
|
684
|
+
has_init=False,
|
|
685
|
+
module=qname_prefix,
|
|
686
|
+
)
|
|
687
|
+
return tpy_rec, record_info
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def _lower_function(
|
|
691
|
+
fn: Function,
|
|
692
|
+
name_to_origin: dict[str, tuple[str, str]],
|
|
693
|
+
plugin_name: str,
|
|
694
|
+
fm: FrontendModule,
|
|
695
|
+
diags: list[FrontendDiagnostic],
|
|
696
|
+
) -> TpyFunction | None:
|
|
697
|
+
"""Lower a frontend `Function` to a `TpyFunction`. Plugins emit
|
|
698
|
+
functions whose params already carry resolved types (NamedType /
|
|
699
|
+
PointerType); the body uses the M4 expression / statement set.
|
|
700
|
+
"""
|
|
701
|
+
params: list = []
|
|
702
|
+
for p in fn.params:
|
|
703
|
+
# An un-annotated param (`type is None`) is permitted for
|
|
704
|
+
# `self` on record methods, mirroring the no-annotation form
|
|
705
|
+
# in ordinary TPy source. The resolver-attached pre-pass
|
|
706
|
+
# treats a None-typed first-param of a method as `Self`.
|
|
707
|
+
if p.type is None:
|
|
708
|
+
t = None
|
|
709
|
+
else:
|
|
710
|
+
t = _lower_type(p.type, plugin_name, fm, diags)
|
|
711
|
+
if t is None:
|
|
712
|
+
return None
|
|
713
|
+
if p.default is not None:
|
|
714
|
+
diags.append(_ir_invalid(
|
|
715
|
+
plugin_name, fm,
|
|
716
|
+
"parameter defaults are not lowered in M4",
|
|
717
|
+
))
|
|
718
|
+
return None
|
|
719
|
+
params.append((p.name, t))
|
|
720
|
+
return_type = None
|
|
721
|
+
if fn.return_type is not None:
|
|
722
|
+
return_type = _lower_type(fn.return_type, plugin_name, fm, diags)
|
|
723
|
+
if return_type is None:
|
|
724
|
+
return None
|
|
725
|
+
body = _lower_stmt_list(
|
|
726
|
+
fn.body, name_to_origin, plugin_name, fm, diags)
|
|
727
|
+
return TpyFunction(
|
|
728
|
+
name=fn.name,
|
|
729
|
+
params=params,
|
|
730
|
+
return_type=return_type,
|
|
731
|
+
body=body,
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def _lower_pattern(p, name_to_origin, plugin_name, fm, diags):
|
|
736
|
+
loc = _to_source_loc(p.loc) if getattr(p, "loc", None) else None
|
|
737
|
+
if isinstance(p, MatchWildcard):
|
|
738
|
+
return TpyWildcardPattern(loc=loc)
|
|
739
|
+
if isinstance(p, MatchValue):
|
|
740
|
+
# Literal patterns (int / str / bool) route through TpyLiteralPattern.
|
|
741
|
+
# Named-constant patterns (`Color.Red`) keep their expression
|
|
742
|
+
# form via TpyValuePattern, which sema resolves to the enum
|
|
743
|
+
# member value at match-analysis time.
|
|
744
|
+
v = p.value
|
|
745
|
+
if isinstance(v, IntLit):
|
|
746
|
+
return TpyLiteralPattern(value=v.value, loc=loc)
|
|
747
|
+
if isinstance(v, StrLit):
|
|
748
|
+
return TpyLiteralPattern(value=v.value, loc=loc)
|
|
749
|
+
if isinstance(v, BoolLit):
|
|
750
|
+
return TpyLiteralPattern(value=v.value, loc=loc)
|
|
751
|
+
if isinstance(v, Attr):
|
|
752
|
+
lowered = _lower_expr(v, {}, plugin_name, fm, diags)
|
|
753
|
+
if lowered is None:
|
|
754
|
+
return None
|
|
755
|
+
return TpyValuePattern(expr=lowered, loc=loc)
|
|
756
|
+
diags.append(_ir_invalid(
|
|
757
|
+
plugin_name, fm,
|
|
758
|
+
f"MatchValue payload must be a literal or attribute "
|
|
759
|
+
f"reference, got {type(v).__name__}",
|
|
760
|
+
))
|
|
761
|
+
return None
|
|
762
|
+
if isinstance(p, MatchClass):
|
|
763
|
+
cls_ref = TpyName(name=p.class_name)
|
|
764
|
+
cls_ref.loc = loc
|
|
765
|
+
class_pat = TpyClassPattern(
|
|
766
|
+
cls=cls_ref, positional=[], keywords=[], loc=loc,
|
|
767
|
+
)
|
|
768
|
+
if p.bind is not None:
|
|
769
|
+
return TpyAsPattern(pattern=class_pat, name=p.bind, loc=loc)
|
|
770
|
+
return class_pat
|
|
771
|
+
diags.append(_ir_invalid(
|
|
772
|
+
plugin_name, fm,
|
|
773
|
+
f"unsupported MatchPattern kind: {type(p).__name__}",
|
|
774
|
+
))
|
|
775
|
+
return None
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def _lower_expr(
|
|
779
|
+
expr,
|
|
780
|
+
name_to_origin: dict[str, tuple[str, str]],
|
|
781
|
+
plugin_name: str,
|
|
782
|
+
fm: FrontendModule,
|
|
783
|
+
diags: list[FrontendDiagnostic],
|
|
784
|
+
):
|
|
785
|
+
loc = _to_source_loc(expr.loc) if getattr(expr, "loc", None) else None
|
|
786
|
+
if isinstance(expr, StrLit):
|
|
787
|
+
out = TpyStrLiteral(value=expr.value)
|
|
788
|
+
out.loc = loc
|
|
789
|
+
return out
|
|
790
|
+
if isinstance(expr, BoolLit):
|
|
791
|
+
out = TpyBoolLiteral(value=expr.value)
|
|
792
|
+
out.loc = loc
|
|
793
|
+
return out
|
|
794
|
+
if isinstance(expr, IntLit):
|
|
795
|
+
out = TpyIntLiteral(value=expr.value)
|
|
796
|
+
out.loc = loc
|
|
797
|
+
return out
|
|
798
|
+
if isinstance(expr, FloatLit):
|
|
799
|
+
out = TpyFloatLiteral(value=expr.value)
|
|
800
|
+
out.loc = loc
|
|
801
|
+
return out
|
|
802
|
+
if isinstance(expr, Name):
|
|
803
|
+
out = TpyName(name=expr.ident)
|
|
804
|
+
out.loc = loc
|
|
805
|
+
return out
|
|
806
|
+
if isinstance(expr, BinOp):
|
|
807
|
+
op_str = _BINOP_OP_STR.get(expr.op)
|
|
808
|
+
if op_str is None:
|
|
809
|
+
diags.append(_ir_invalid(
|
|
810
|
+
plugin_name, fm,
|
|
811
|
+
f"unsupported BinOpKind: {expr.op}",
|
|
812
|
+
))
|
|
813
|
+
return None
|
|
814
|
+
lhs = _lower_expr(expr.lhs, name_to_origin, plugin_name, fm, diags)
|
|
815
|
+
rhs = _lower_expr(expr.rhs, name_to_origin, plugin_name, fm, diags)
|
|
816
|
+
if lhs is None or rhs is None:
|
|
817
|
+
return None
|
|
818
|
+
out = TpyBinOp(left=lhs, op=op_str, right=rhs)
|
|
819
|
+
out.loc = loc
|
|
820
|
+
return out
|
|
821
|
+
if isinstance(expr, UnaryOp):
|
|
822
|
+
op_str = _UNARYOP_OP_STR.get(expr.op)
|
|
823
|
+
if op_str is None:
|
|
824
|
+
diags.append(_ir_invalid(
|
|
825
|
+
plugin_name, fm,
|
|
826
|
+
f"unsupported UnaryOpKind: {expr.op}",
|
|
827
|
+
))
|
|
828
|
+
return None
|
|
829
|
+
operand = _lower_expr(expr.operand, name_to_origin, plugin_name, fm, diags)
|
|
830
|
+
if operand is None:
|
|
831
|
+
return None
|
|
832
|
+
out = TpyUnaryOp(op=op_str, operand=operand)
|
|
833
|
+
out.loc = loc
|
|
834
|
+
return out
|
|
835
|
+
if isinstance(expr, SetLit):
|
|
836
|
+
lowered_elems: list = []
|
|
837
|
+
for elem in expr.elements:
|
|
838
|
+
le = _lower_expr(elem, name_to_origin, plugin_name, fm, diags)
|
|
839
|
+
if le is None:
|
|
840
|
+
return None
|
|
841
|
+
lowered_elems.append(le)
|
|
842
|
+
out = TpySetLiteral(elements=lowered_elems)
|
|
843
|
+
out.loc = loc
|
|
844
|
+
return out
|
|
845
|
+
if isinstance(expr, ListLit):
|
|
846
|
+
lowered_elems = []
|
|
847
|
+
for elem in expr.elements:
|
|
848
|
+
le = _lower_expr(elem, name_to_origin, plugin_name, fm, diags)
|
|
849
|
+
if le is None:
|
|
850
|
+
return None
|
|
851
|
+
lowered_elems.append(le)
|
|
852
|
+
out = TpyArrayLiteral(elements=lowered_elems)
|
|
853
|
+
out.loc = loc
|
|
854
|
+
return out
|
|
855
|
+
if isinstance(expr, NoneLit):
|
|
856
|
+
out = TpyNoneLiteral()
|
|
857
|
+
out.loc = loc
|
|
858
|
+
return out
|
|
859
|
+
if isinstance(expr, Compare):
|
|
860
|
+
if not expr.ops or len(expr.ops) != len(expr.comparators):
|
|
861
|
+
diags.append(_ir_invalid(
|
|
862
|
+
plugin_name, fm,
|
|
863
|
+
"Compare requires ops and comparators of equal, "
|
|
864
|
+
"non-zero length",
|
|
865
|
+
))
|
|
866
|
+
return None
|
|
867
|
+
# Single-op comparison lowers to `TpyBinOp` (matches what the
|
|
868
|
+
# parser produces for a non-chained Python compare). Chained
|
|
869
|
+
# form (a < b < c) would lower to `TpyChainedCompare`; no
|
|
870
|
+
# M3-target source language uses chains, so it's left for a
|
|
871
|
+
# future plugin to opt into.
|
|
872
|
+
if len(expr.ops) != 1:
|
|
873
|
+
diags.append(_ir_invalid(
|
|
874
|
+
plugin_name, fm,
|
|
875
|
+
"chained comparisons are not lowered in M3",
|
|
876
|
+
))
|
|
877
|
+
return None
|
|
878
|
+
op_str = _CMPOP_OP_STR.get(expr.ops[0])
|
|
879
|
+
if op_str is None:
|
|
880
|
+
diags.append(_ir_invalid(
|
|
881
|
+
plugin_name, fm,
|
|
882
|
+
f"unsupported CmpOpKind: {expr.ops[0]}",
|
|
883
|
+
))
|
|
884
|
+
return None
|
|
885
|
+
lhs = _lower_expr(expr.lhs, name_to_origin, plugin_name, fm, diags)
|
|
886
|
+
rhs = _lower_expr(expr.comparators[0], name_to_origin,
|
|
887
|
+
plugin_name, fm, diags)
|
|
888
|
+
if lhs is None or rhs is None:
|
|
889
|
+
return None
|
|
890
|
+
out = TpyBinOp(left=lhs, op=op_str, right=rhs)
|
|
891
|
+
out.loc = loc
|
|
892
|
+
return out
|
|
893
|
+
if isinstance(expr, Attr):
|
|
894
|
+
target = _lower_expr(expr.target, name_to_origin, plugin_name, fm, diags)
|
|
895
|
+
if target is None:
|
|
896
|
+
return None
|
|
897
|
+
out = TpyFieldAccess(obj=target, field=expr.ident)
|
|
898
|
+
out.loc = loc
|
|
899
|
+
return out
|
|
900
|
+
if isinstance(expr, Subscript):
|
|
901
|
+
target = _lower_expr(expr.target, name_to_origin, plugin_name, fm, diags)
|
|
902
|
+
index = _lower_expr(expr.index, name_to_origin, plugin_name, fm, diags)
|
|
903
|
+
if target is None or index is None:
|
|
904
|
+
return None
|
|
905
|
+
out = TpySubscript(obj=target, index=index)
|
|
906
|
+
out.loc = loc
|
|
907
|
+
return out
|
|
908
|
+
if isinstance(expr, Call):
|
|
909
|
+
# Method-call shape: callee is `obj.method` -- emit TpyMethodCall
|
|
910
|
+
# directly so sema's method-dispatch path fires. TpyCall with a
|
|
911
|
+
# field-access function would otherwise have to be re-routed by
|
|
912
|
+
# sema, and not every method-resolution path handles that.
|
|
913
|
+
if isinstance(expr.callee, Attr):
|
|
914
|
+
obj = _lower_expr(expr.callee.target, name_to_origin,
|
|
915
|
+
plugin_name, fm, diags)
|
|
916
|
+
if obj is None:
|
|
917
|
+
return None
|
|
918
|
+
args: list = []
|
|
919
|
+
for a in expr.args:
|
|
920
|
+
la = _lower_expr(a, name_to_origin, plugin_name, fm, diags)
|
|
921
|
+
if la is None:
|
|
922
|
+
return None
|
|
923
|
+
args.append(la)
|
|
924
|
+
mcall = TpyMethodCall(
|
|
925
|
+
obj=obj, method=expr.callee.ident, args=args,
|
|
926
|
+
)
|
|
927
|
+
mcall.loc = loc
|
|
928
|
+
return mcall
|
|
929
|
+
callee = _lower_expr(expr.callee, name_to_origin, plugin_name, fm, diags)
|
|
930
|
+
if callee is None:
|
|
931
|
+
return None
|
|
932
|
+
args = []
|
|
933
|
+
for a in expr.args:
|
|
934
|
+
la = _lower_expr(a, name_to_origin, plugin_name, fm, diags)
|
|
935
|
+
if la is None:
|
|
936
|
+
return None
|
|
937
|
+
args.append(la)
|
|
938
|
+
kwargs: dict = {}
|
|
939
|
+
for kw_name, kw_value in expr.kwargs:
|
|
940
|
+
la = _lower_expr(kw_value, name_to_origin, plugin_name, fm, diags)
|
|
941
|
+
if la is None:
|
|
942
|
+
return None
|
|
943
|
+
kwargs[kw_name] = la
|
|
944
|
+
out = TpyCall(func=callee, args=args, kwargs=kwargs)
|
|
945
|
+
out.loc = loc
|
|
946
|
+
# `type_args` on the IR maps to the parser's `call_type`: a
|
|
947
|
+
# TpyTypeRef whose `args` are the explicit type/int args. This
|
|
948
|
+
# is what powers generic-constructor calls like
|
|
949
|
+
# `Array[Int32, 8]()` and `Container[T]()`.
|
|
950
|
+
if expr.type_args:
|
|
951
|
+
if not isinstance(expr.callee, Name):
|
|
952
|
+
diags.append(_ir_invalid(
|
|
953
|
+
plugin_name, fm,
|
|
954
|
+
"type_args only allowed on calls with a bare-name callee",
|
|
955
|
+
))
|
|
956
|
+
return None
|
|
957
|
+
type_ref_args: list = []
|
|
958
|
+
for a in expr.type_args:
|
|
959
|
+
if isinstance(a, IntTypeArg):
|
|
960
|
+
type_ref_args.append(a.value)
|
|
961
|
+
elif isinstance(a, TypeTypeArg):
|
|
962
|
+
lowered = _lower_type(a.value, plugin_name, fm, diags)
|
|
963
|
+
if lowered is None:
|
|
964
|
+
return None
|
|
965
|
+
type_ref_args.append(lowered)
|
|
966
|
+
else:
|
|
967
|
+
diags.append(_ir_invalid(
|
|
968
|
+
plugin_name, fm,
|
|
969
|
+
f"unsupported TypeArg on Call: {type(a).__name__}",
|
|
970
|
+
))
|
|
971
|
+
return None
|
|
972
|
+
out.call_type = TpyTypeRef(
|
|
973
|
+
name=expr.callee.ident, args=tuple(type_ref_args), loc=loc,
|
|
974
|
+
)
|
|
975
|
+
# If the callee is a bare Name that we imported via FromImport,
|
|
976
|
+
# tag the call so sema knows which module exports it (parallel
|
|
977
|
+
# to what Parser._resolve_call_import does for .py sources).
|
|
978
|
+
if isinstance(expr.callee, Name):
|
|
979
|
+
origin = name_to_origin.get(expr.callee.ident)
|
|
980
|
+
if origin is not None:
|
|
981
|
+
out.resolved_import = origin
|
|
982
|
+
# Constructor call (no explicit type_args): if the callee
|
|
983
|
+
# is the name of a user-declared record in this module,
|
|
984
|
+
# tag the call as a constructor so sema's record-instantiation
|
|
985
|
+
# path fires with the right module-qname.
|
|
986
|
+
elif (out.call_type is None
|
|
987
|
+
and _is_known_record_name(expr.callee.ident, fm)):
|
|
988
|
+
out.call_type = TpyTypeRef(
|
|
989
|
+
name=expr.callee.ident, args=(), loc=loc,
|
|
990
|
+
)
|
|
991
|
+
return out
|
|
992
|
+
diags.append(_ir_invalid(
|
|
993
|
+
plugin_name, fm,
|
|
994
|
+
f"unsupported expr kind: {type(expr).__name__}",
|
|
995
|
+
))
|
|
996
|
+
return None
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
def _lower_type(
|
|
1000
|
+
t: TypeExpr,
|
|
1001
|
+
plugin_name: str,
|
|
1002
|
+
fm: FrontendModule,
|
|
1003
|
+
diags: list[FrontendDiagnostic],
|
|
1004
|
+
) -> TpyTypeRef | None:
|
|
1005
|
+
"""Lower a symbolic IR type to a parser `TpyTypeRef`.
|
|
1006
|
+
|
|
1007
|
+
M2: `NamedType` (with generic args). M4: `PointerType` for Pascal
|
|
1008
|
+
`var` parameters; lowers to `Ptr[T]` via the canonical resolver
|
|
1009
|
+
wrapper name. Other wrappers (Optional/Own/Readonly/...) land
|
|
1010
|
+
milestone-by-milestone.
|
|
1011
|
+
"""
|
|
1012
|
+
if isinstance(t, NamedType):
|
|
1013
|
+
args: list = []
|
|
1014
|
+
for a in t.args:
|
|
1015
|
+
if isinstance(a, IntTypeArg):
|
|
1016
|
+
args.append(a.value)
|
|
1017
|
+
elif isinstance(a, TypeTypeArg):
|
|
1018
|
+
lowered = _lower_type(a.value, plugin_name, fm, diags)
|
|
1019
|
+
if lowered is None:
|
|
1020
|
+
return None
|
|
1021
|
+
args.append(lowered)
|
|
1022
|
+
else:
|
|
1023
|
+
diags.append(_ir_invalid(
|
|
1024
|
+
plugin_name, fm,
|
|
1025
|
+
f"unsupported TypeArg kind: {type(a).__name__}",
|
|
1026
|
+
))
|
|
1027
|
+
return None
|
|
1028
|
+
return TpyTypeRef(name=t.name, args=tuple(args),
|
|
1029
|
+
loc=_to_source_loc(t.loc))
|
|
1030
|
+
if isinstance(t, PointerType):
|
|
1031
|
+
inner = _lower_type(t.inner, plugin_name, fm, diags)
|
|
1032
|
+
if inner is None:
|
|
1033
|
+
return None
|
|
1034
|
+
# `tpy:Ptr` is the canonical-name spelling TypeResolver
|
|
1035
|
+
# recognises for the Ptr wrapper (see type_resolver.py's
|
|
1036
|
+
# structural-wrapper table). The walker that parses the
|
|
1037
|
+
# surface form `Ptr[T]` produces this name only when the
|
|
1038
|
+
# source identifier truly resolved to `tpy.Ptr`; for plugin
|
|
1039
|
+
# lowering we know the binding by construction.
|
|
1040
|
+
return TpyTypeRef(name="tpy:Ptr", args=(inner,),
|
|
1041
|
+
loc=_to_source_loc(t.loc))
|
|
1042
|
+
if isinstance(t, UnionType):
|
|
1043
|
+
members: list = []
|
|
1044
|
+
for m in t.members:
|
|
1045
|
+
lm = _lower_type(m, plugin_name, fm, diags)
|
|
1046
|
+
if lm is None:
|
|
1047
|
+
return None
|
|
1048
|
+
members.append(lm)
|
|
1049
|
+
return TpyUnionRef(members=tuple(members),
|
|
1050
|
+
loc=_to_source_loc(t.loc))
|
|
1051
|
+
if isinstance(t, CallableType):
|
|
1052
|
+
params_lowered: list = []
|
|
1053
|
+
for p in t.params:
|
|
1054
|
+
lp = _lower_type(p, plugin_name, fm, diags)
|
|
1055
|
+
if lp is None:
|
|
1056
|
+
return None
|
|
1057
|
+
params_lowered.append(lp)
|
|
1058
|
+
if t.return_type is None:
|
|
1059
|
+
# `procedure(...)` -- the void-returning form. TPy
|
|
1060
|
+
# spells this as `Callable[[...], None]`; the resolver
|
|
1061
|
+
# accepts `NoneType` (`TpyTypeRef("None")`) as the
|
|
1062
|
+
# return type.
|
|
1063
|
+
ret = TpyTypeRef(name="None", args=(),
|
|
1064
|
+
loc=_to_source_loc(t.loc))
|
|
1065
|
+
else:
|
|
1066
|
+
ret = _lower_type(t.return_type, plugin_name, fm, diags)
|
|
1067
|
+
if ret is None:
|
|
1068
|
+
return None
|
|
1069
|
+
return TpyCallableRef(
|
|
1070
|
+
kind="Callable", params=tuple(params_lowered),
|
|
1071
|
+
return_type=ret, loc=_to_source_loc(t.loc),
|
|
1072
|
+
)
|
|
1073
|
+
diags.append(_ir_invalid(
|
|
1074
|
+
plugin_name, fm,
|
|
1075
|
+
f"unsupported TypeExpr kind: {type(t).__name__}",
|
|
1076
|
+
))
|
|
1077
|
+
return None
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
def _to_source_loc(loc: Loc | None) -> SourceLocation | None:
|
|
1081
|
+
if loc is None:
|
|
1082
|
+
return None
|
|
1083
|
+
return SourceLocation(
|
|
1084
|
+
line=loc.line,
|
|
1085
|
+
column=max(0, loc.col - 1), # parser uses 0-based columns
|
|
1086
|
+
file=str(loc.file),
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
def _ir_invalid(
|
|
1091
|
+
plugin_name: str, fm: FrontendModule, message: str,
|
|
1092
|
+
) -> FrontendDiagnostic:
|
|
1093
|
+
return FrontendDiagnostic(
|
|
1094
|
+
diagnostic=Diagnostic(level=DiagnosticLevel.ERROR, message=message),
|
|
1095
|
+
category=FrontendDiagnosticCategory.PLUGIN_IR_INVALID,
|
|
1096
|
+
plugin_name=plugin_name,
|
|
1097
|
+
source_language=fm.source_language or None,
|
|
1098
|
+
)
|