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.
Files changed (333) hide show
  1. tpy_lang-0.3.0.dev0.dist-info/METADATA +151 -0
  2. tpy_lang-0.3.0.dev0.dist-info/RECORD +333 -0
  3. tpy_lang-0.3.0.dev0.dist-info/WHEEL +4 -0
  4. tpy_lang-0.3.0.dev0.dist-info/entry_points.txt +3 -0
  5. tpyc/__init__.py +104 -0
  6. tpyc/__main__.py +6 -0
  7. tpyc/_buildinfo.py +1 -0
  8. tpyc/_data/docs/LANGUAGE_FEATURES.md +6278 -0
  9. tpyc/_data/docs/STDLIB_ROADMAP.md +1258 -0
  10. tpyc/_data/docs/TPY_FOR_AGENTS.md +556 -0
  11. tpyc/_data/lib/tpy/_bindings/__init__.py +6 -0
  12. tpyc/_data/lib/tpy/_bindings/pcre2.py +173 -0
  13. tpyc/_data/lib/tpy/_bindings/posix_socket.py +161 -0
  14. tpyc/_data/lib/tpy/_functools_macros.py +80 -0
  15. tpyc/_data/lib/tpy/_macro_helpers.py +161 -0
  16. tpyc/_data/lib/tpy/argparse.py +2062 -0
  17. tpyc/_data/lib/tpy/asyncio/__init__.py +744 -0
  18. tpyc/_data/lib/tpy/asyncio/_executor.py +515 -0
  19. tpyc/_data/lib/tpy/base64.py +410 -0
  20. tpyc/_data/lib/tpy/bisect.py +39 -0
  21. tpyc/_data/lib/tpy/builtins.py +38 -0
  22. tpyc/_data/lib/tpy/dataclasses.py +354 -0
  23. tpyc/_data/lib/tpy/enum.py +23 -0
  24. tpyc/_data/lib/tpy/functools.py +33 -0
  25. tpyc/_data/lib/tpy/hashlib.py +206 -0
  26. tpyc/_data/lib/tpy/heapq.py +118 -0
  27. tpyc/_data/lib/tpy/io.py +395 -0
  28. tpyc/_data/lib/tpy/json.py +221 -0
  29. tpyc/_data/lib/tpy/math.py +406 -0
  30. tpyc/_data/lib/tpy/random.py +597 -0
  31. tpyc/_data/lib/tpy/re.py +467 -0
  32. tpyc/_data/lib/tpy/socket.py +379 -0
  33. tpyc/_data/lib/tpy/struct.py +178 -0
  34. tpyc/_data/lib/tpy/sys.py +40 -0
  35. tpyc/_data/lib/tpy/time.py +39 -0
  36. tpyc/_data/lib/tpy/tpy/__init__.py +78 -0
  37. tpyc/_data/lib/tpy/tpy/_bootstrap/__init__.py +10 -0
  38. tpyc/_data/lib/tpy/tpy/_bootstrap/_decorators.py +37 -0
  39. tpyc/_data/lib/tpy/tpy/_bootstrap/_extern.py +64 -0
  40. tpyc/_data/lib/tpy/tpy/_builtins/__init__.py +11 -0
  41. tpyc/_data/lib/tpy/tpy/_builtins/_bytes.py +378 -0
  42. tpyc/_data/lib/tpy/tpy/_builtins/_dict.py +151 -0
  43. tpyc/_data/lib/tpy/tpy/_builtins/_exceptions.py +125 -0
  44. tpyc/_data/lib/tpy/tpy/_builtins/_funcs.py +681 -0
  45. tpyc/_data/lib/tpy/tpy/_builtins/_io.py +97 -0
  46. tpyc/_data/lib/tpy/tpy/_builtins/_list.py +127 -0
  47. tpyc/_data/lib/tpy/tpy/_builtins/_range.py +52 -0
  48. tpyc/_data/lib/tpy/tpy/_builtins/_set.py +139 -0
  49. tpyc/_data/lib/tpy/tpy/_builtins/_super.py +11 -0
  50. tpyc/_data/lib/tpy/tpy/_builtins/_types.py +661 -0
  51. tpyc/_data/lib/tpy/tpy/_core/__init__.py +23 -0
  52. tpyc/_data/lib/tpy/tpy/_core/_bytes_view.py +129 -0
  53. tpyc/_data/lib/tpy/tpy/_core/_containers.py +137 -0
  54. tpyc/_data/lib/tpy/tpy/_core/_functions.py +40 -0
  55. tpyc/_data/lib/tpy/tpy/_core/_types.py +2061 -0
  56. tpyc/_data/lib/tpy/tpy/_typing/__init__.py +77 -0
  57. tpyc/_data/lib/tpy/tpy/_version.py +29 -0
  58. tpyc/_data/lib/tpy/tpy/bits.py +28 -0
  59. tpyc/_data/lib/tpy/tpy/coro/__init__.py +127 -0
  60. tpyc/_data/lib/tpy/tpy/extern.py +8 -0
  61. tpyc/_data/lib/tpy/tpy/mem.py +49 -0
  62. tpyc/_data/lib/tpy/tpy/unsafe.py +195 -0
  63. tpyc/_data/lib/tpy/tpy/version.py +21 -0
  64. tpyc/_data/lib/tpy/typing.py +13 -0
  65. tpyc/_data/runtime/cpp/include/tpy/any.hpp +461 -0
  66. tpyc/_data/runtime/cpp/include/tpy/as_ostream.hpp +117 -0
  67. tpyc/_data/runtime/cpp/include/tpy/async.hpp +76 -0
  68. tpyc/_data/runtime/cpp/include/tpy/bigint.hpp +1343 -0
  69. tpyc/_data/runtime/cpp/include/tpy/builtins.hpp +400 -0
  70. tpyc/_data/runtime/cpp/include/tpy/bytes_ops.hpp +469 -0
  71. tpyc/_data/runtime/cpp/include/tpy/container_ops.hpp +487 -0
  72. tpyc/_data/runtime/cpp/include/tpy/copy_iter.hpp +82 -0
  73. tpyc/_data/runtime/cpp/include/tpy/core.hpp +558 -0
  74. tpyc/_data/runtime/cpp/include/tpy/dict_ops.hpp +289 -0
  75. tpyc/_data/runtime/cpp/include/tpy/dunder.hpp +750 -0
  76. tpyc/_data/runtime/cpp/include/tpy/dynamic.hpp +44 -0
  77. tpyc/_data/runtime/cpp/include/tpy/enum.hpp +40 -0
  78. tpyc/_data/runtime/cpp/include/tpy/file.hpp +245 -0
  79. tpyc/_data/runtime/cpp/include/tpy/fixed_int.hpp +317 -0
  80. tpyc/_data/runtime/cpp/include/tpy/format.hpp +954 -0
  81. tpyc/_data/runtime/cpp/include/tpy/frame_slot.hpp +120 -0
  82. tpyc/_data/runtime/cpp/include/tpy/generator.hpp +47 -0
  83. tpyc/_data/runtime/cpp/include/tpy/iterable_ops.hpp +122 -0
  84. tpyc/_data/runtime/cpp/include/tpy/itertools.hpp +749 -0
  85. tpyc/_data/runtime/cpp/include/tpy/next_iter.hpp +82 -0
  86. tpyc/_data/runtime/cpp/include/tpy/ordered_map.hpp +518 -0
  87. tpyc/_data/runtime/cpp/include/tpy/ordered_set.hpp +337 -0
  88. tpyc/_data/runtime/cpp/include/tpy/own_iter.hpp +54 -0
  89. tpyc/_data/runtime/cpp/include/tpy/pascal_graph_sdl.hpp +192 -0
  90. tpyc/_data/runtime/cpp/include/tpy/printing.hpp +302 -0
  91. tpyc/_data/runtime/cpp/include/tpy/protocols.hpp +61 -0
  92. tpyc/_data/runtime/cpp/include/tpy/range.hpp +115 -0
  93. tpyc/_data/runtime/cpp/include/tpy/ranges.hpp +212 -0
  94. tpyc/_data/runtime/cpp/include/tpy/set_ops.hpp +265 -0
  95. tpyc/_data/runtime/cpp/include/tpy/slice.hpp +47 -0
  96. tpyc/_data/runtime/cpp/include/tpy/span_iter.hpp +42 -0
  97. tpyc/_data/runtime/cpp/include/tpy/stdlib/math.hpp +41 -0
  98. tpyc/_data/runtime/cpp/include/tpy/stdlib/pcre2_h.hpp +96 -0
  99. tpyc/_data/runtime/cpp/include/tpy/stdlib/random.hpp +25 -0
  100. tpyc/_data/runtime/cpp/include/tpy/stdlib/socket_h.hpp +145 -0
  101. tpyc/_data/runtime/cpp/include/tpy/stdlib/time.hpp +62 -0
  102. tpyc/_data/runtime/cpp/include/tpy/system.hpp +121 -0
  103. tpyc/_data/runtime/cpp/include/tpy/throwable.hpp +55 -0
  104. tpyc/_data/runtime/cpp/include/tpy/tpy.hpp +156 -0
  105. tpyc/_data/runtime/cpp/include/tpy/type_name.hpp +77 -0
  106. tpyc/_data/runtime/cpp/include/tpy/type_traits.hpp +240 -0
  107. tpyc/_data/runtime/cpp/include/tpy/uninit_array_storage.hpp +250 -0
  108. tpyc/_data/runtime/cpp/include/tpy/uninit_heap_storage.hpp +277 -0
  109. tpyc/_data/runtime/cpp/include/tpy/varargs.hpp +174 -0
  110. tpyc/_data/runtime/cpp/include/tpy/variant_ref.hpp +118 -0
  111. tpyc/_data/runtime/cpp/src/stdlib/socket_impl.cpp +104 -0
  112. tpyc/_data/runtime/cpp/third_party/README.md +58 -0
  113. tpyc/_data/runtime/cpp/third_party/pcre2/AUTHORS +36 -0
  114. tpyc/_data/runtime/cpp/third_party/pcre2/CMakeLists.txt +1233 -0
  115. tpyc/_data/runtime/cpp/third_party/pcre2/COPYING +5 -0
  116. tpyc/_data/runtime/cpp/third_party/pcre2/ChangeLog +3097 -0
  117. tpyc/_data/runtime/cpp/third_party/pcre2/HACKING +853 -0
  118. tpyc/_data/runtime/cpp/third_party/pcre2/INSTALL +368 -0
  119. tpyc/_data/runtime/cpp/third_party/pcre2/LICENCE +94 -0
  120. tpyc/_data/runtime/cpp/third_party/pcre2/NEWS +492 -0
  121. tpyc/_data/runtime/cpp/third_party/pcre2/NON-AUTOTOOLS-BUILD +430 -0
  122. tpyc/_data/runtime/cpp/third_party/pcre2/README +956 -0
  123. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/COPYING-CMAKE-SCRIPTS +22 -0
  124. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindEditline.cmake +16 -0
  125. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindPackageHandleStandardArgs.cmake +58 -0
  126. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/FindReadline.cmake +29 -0
  127. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config-version.cmake.in +15 -0
  128. tpyc/_data/runtime/cpp/third_party/pcre2/cmake/pcre2-config.cmake.in +148 -0
  129. tpyc/_data/runtime/cpp/third_party/pcre2/config-cmake.h.in +56 -0
  130. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-16.pc.in +13 -0
  131. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-32.pc.in +13 -0
  132. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-8.pc.in +13 -0
  133. tpyc/_data/runtime/cpp/third_party/pcre2/libpcre2-posix.pc.in +13 -0
  134. tpyc/_data/runtime/cpp/third_party/pcre2/pcre2-config.in +121 -0
  135. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h +483 -0
  136. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.generic +483 -0
  137. tpyc/_data/runtime/cpp/third_party/pcre2/src/config.h.in +460 -0
  138. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h +1010 -0
  139. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.generic +1010 -0
  140. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2.h.in +1010 -0
  141. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_auto_possess.c +1371 -0
  142. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c +196 -0
  143. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chartables.c.dist +196 -0
  144. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_chkdint.c +96 -0
  145. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_compile.c +11001 -0
  146. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_config.c +252 -0
  147. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_context.c +510 -0
  148. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_convert.c +1189 -0
  149. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dfa_match.c +4119 -0
  150. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_dftables.c +297 -0
  151. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_error.c +345 -0
  152. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_extuni.c +162 -0
  153. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_find_bracket.c +219 -0
  154. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_fuzzsupport.c +792 -0
  155. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_internal.h +2084 -0
  156. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_intmodedep.h +940 -0
  157. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_compile.c +14972 -0
  158. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_match.c +200 -0
  159. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_misc.c +234 -0
  160. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_neon_inc.h +354 -0
  161. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_simd_inc.h +2355 -0
  162. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_jit_test.c +2528 -0
  163. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_maketables.c +165 -0
  164. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match.c +7777 -0
  165. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_match_data.c +185 -0
  166. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_newline.c +243 -0
  167. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ord2utf.c +120 -0
  168. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_pattern_info.c +432 -0
  169. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_printint.c +886 -0
  170. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_script_run.c +344 -0
  171. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_serialize.c +286 -0
  172. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_string_utils.c +237 -0
  173. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_study.c +1915 -0
  174. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substitute.c +1009 -0
  175. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_substring.c +550 -0
  176. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_tables.c +234 -0
  177. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucd.c +5460 -0
  178. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucp.h +396 -0
  179. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_ucptables.c +1533 -0
  180. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_valid_utf.c +398 -0
  181. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2_xclass.c +308 -0
  182. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2demo.c +497 -0
  183. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2grep.c +4606 -0
  184. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.c +425 -0
  185. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix.h +187 -0
  186. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2posix_test.c +209 -0
  187. tpyc/_data/runtime/cpp/third_party/pcre2/src/pcre2test.c +9708 -0
  188. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorApple.c +137 -0
  189. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorCore.c +327 -0
  190. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorFreeBSD.c +89 -0
  191. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorPosix.c +62 -0
  192. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitExecAllocatorWindows.c +40 -0
  193. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorNetBSD.c +72 -0
  194. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitProtExecAllocatorPosix.c +172 -0
  195. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorPosix.c +141 -0
  196. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/allocator_src/sljitWXExecAllocatorWindows.c +102 -0
  197. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfig.h +142 -0
  198. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigCPU.h +188 -0
  199. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitConfigInternal.h +907 -0
  200. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.c +3561 -0
  201. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitLir.h +2466 -0
  202. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_32.c +4636 -0
  203. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_64.c +3491 -0
  204. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeARM_T2_32.c +4302 -0
  205. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeLOONGARCH_64.c +3765 -0
  206. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_32.c +472 -0
  207. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_64.c +387 -0
  208. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeMIPS_common.c +4259 -0
  209. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_32.c +485 -0
  210. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_64.c +719 -0
  211. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativePPC_common.c +3161 -0
  212. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_32.c +142 -0
  213. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_64.c +222 -0
  214. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeRISCV_common.c +3121 -0
  215. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeS390X.c +4526 -0
  216. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_32.c +1685 -0
  217. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_64.c +1398 -0
  218. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitNativeX86_common.c +5001 -0
  219. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitSerialize.c +516 -0
  220. tpyc/_data/runtime/cpp/third_party/pcre2/src/sljit/sljitUtils.c +344 -0
  221. tpyc/_data/runtime/cpp/third_party/pcre2.sources.txt +54 -0
  222. tpyc/_data/runtime/cpp/third_party/pcre2.vendor.json +7 -0
  223. tpyc/build/__init__.py +7 -0
  224. tpyc/build/pcre2.py +122 -0
  225. tpyc/build/third_party.py +413 -0
  226. tpyc/cli.py +822 -0
  227. tpyc/codegen_cpp/__init__.py +18 -0
  228. tpyc/codegen_cpp/builtins.py +484 -0
  229. tpyc/codegen_cpp/context.py +2064 -0
  230. tpyc/codegen_cpp/expressions.py +5940 -0
  231. tpyc/codegen_cpp/functions.py +1913 -0
  232. tpyc/codegen_cpp/gen_async.py +3258 -0
  233. tpyc/codegen_cpp/gen_generators.py +657 -0
  234. tpyc/codegen_cpp/generator.py +2258 -0
  235. tpyc/codegen_cpp/match.py +1997 -0
  236. tpyc/codegen_cpp/param_const.py +172 -0
  237. tpyc/codegen_cpp/protocols.py +907 -0
  238. tpyc/codegen_cpp/records.py +1654 -0
  239. tpyc/codegen_cpp/resumable_cfg.py +1651 -0
  240. tpyc/codegen_cpp/statements.py +4963 -0
  241. tpyc/codegen_cpp/string_dispatch.py +76 -0
  242. tpyc/codegen_cpp/test_context.py +46 -0
  243. tpyc/codegen_cpp/test_param_const.py +113 -0
  244. tpyc/codegen_cpp/test_resumable_cfg.py +182 -0
  245. tpyc/codegen_cpp/type_resolution.py +53 -0
  246. tpyc/codegen_cpp/types.py +436 -0
  247. tpyc/codegen_cpp/variant_access.py +135 -0
  248. tpyc/coercions.py +749 -0
  249. tpyc/compilation_context.py +57 -0
  250. tpyc/compiler.py +3945 -0
  251. tpyc/cycle_detection.py +358 -0
  252. tpyc/diagnostics.py +135 -0
  253. tpyc/dump_types.py +353 -0
  254. tpyc/frontend_diagnostics.py +47 -0
  255. tpyc/frontend_ir/__init__.py +140 -0
  256. tpyc/frontend_ir/lower.py +1098 -0
  257. tpyc/frontend_ir/nodes.py +718 -0
  258. tpyc/frontend_ir/resolver_adapter.py +151 -0
  259. tpyc/frontend_plugin.py +209 -0
  260. tpyc/install_docs.py +81 -0
  261. tpyc/liveness.py +756 -0
  262. tpyc/macro_api.py +1724 -0
  263. tpyc/macro_loader.py +497 -0
  264. tpyc/module_names.py +64 -0
  265. tpyc/modules/__init__.py +31 -0
  266. tpyc/modules/defs.py +89 -0
  267. tpyc/modules/registry.py +36 -0
  268. tpyc/modules/resolver.py +192 -0
  269. tpyc/modules/type_resolution.py +629 -0
  270. tpyc/namespace.py +172 -0
  271. tpyc/parse/__init__.py +84 -0
  272. tpyc/parse/imports.py +490 -0
  273. tpyc/parse/nodes.py +1732 -0
  274. tpyc/parse/parser.py +4043 -0
  275. tpyc/parse/resolve_refs.py +466 -0
  276. tpyc/parse/type_resolver.py +1060 -0
  277. tpyc/prescan.py +254 -0
  278. tpyc/qnames.py +149 -0
  279. tpyc/repl.py +529 -0
  280. tpyc/repl_backends.py +848 -0
  281. tpyc/sema/__init__.py +21 -0
  282. tpyc/sema/analyzer.py +3625 -0
  283. tpyc/sema/bound_check.py +72 -0
  284. tpyc/sema/builder_trace.py +684 -0
  285. tpyc/sema/calls.py +5406 -0
  286. tpyc/sema/compatibility.py +2107 -0
  287. tpyc/sema/context.py +1243 -0
  288. tpyc/sema/expressions.py +3737 -0
  289. tpyc/sema/flow_facts.py +199 -0
  290. tpyc/sema/init_tracker.py +150 -0
  291. tpyc/sema/list_literals.py +69 -0
  292. tpyc/sema/literal_utils.py +27 -0
  293. tpyc/sema/local_deduction.py +1088 -0
  294. tpyc/sema/macros.py +179 -0
  295. tpyc/sema/match.py +1177 -0
  296. tpyc/sema/method_expansion.py +347 -0
  297. tpyc/sema/methods.py +2197 -0
  298. tpyc/sema/mutation_propagation.py +268 -0
  299. tpyc/sema/narrowing.py +857 -0
  300. tpyc/sema/numeric_lattice.py +160 -0
  301. tpyc/sema/operators.py +402 -0
  302. tpyc/sema/overloads.py +841 -0
  303. tpyc/sema/protocols.py +1209 -0
  304. tpyc/sema/reach_analysis.py +202 -0
  305. tpyc/sema/registration.py +3156 -0
  306. tpyc/sema/scope_tracker.py +193 -0
  307. tpyc/sema/statements.py +4426 -0
  308. tpyc/sema/type_ops.py +1879 -0
  309. tpyc/sema/value_range.py +181 -0
  310. tpyc/symbol_binding.py +259 -0
  311. tpyc/test_c3_mro.py +208 -0
  312. tpyc/test_cli_argv.py +52 -0
  313. tpyc/test_compiler.py +559 -0
  314. tpyc/test_contains_type_param.py +101 -0
  315. tpyc/test_cycle_detection.py +221 -0
  316. tpyc/test_dump_types.py +225 -0
  317. tpyc/test_install_docs.py +65 -0
  318. tpyc/test_local_cpp_form.py +135 -0
  319. tpyc/test_macro_loader.py +76 -0
  320. tpyc/test_method_expansion.py +254 -0
  321. tpyc/test_nominal_identity.py +182 -0
  322. tpyc/test_overloads.py +410 -0
  323. tpyc/test_parse.py +303 -0
  324. tpyc/test_parse_type_ref.py +506 -0
  325. tpyc/test_parse_version_info.py +58 -0
  326. tpyc/test_reach_analysis.py +72 -0
  327. tpyc/test_ref_type.py +216 -0
  328. tpyc/test_send_sync_substitution.py +276 -0
  329. tpyc/test_tuple_mutation_propagation.py +206 -0
  330. tpyc/test_type_def_registry.py +1729 -0
  331. tpyc/test_union_types.py +195 -0
  332. tpyc/type_def_registry.py +975 -0
  333. tpyc/typesys.py +5104 -0
tpyc/cli.py ADDED
@@ -0,0 +1,822 @@
1
+ """
2
+ TurboPython CLI
3
+
4
+ Two entry points share this module:
5
+
6
+ tpy -- user-facing runner (default: run script, or REPL with no args)
7
+ tpyc -- compiler (default: emit .hpp/.cpp)
8
+
9
+ Both accept the same flags; only the default action differs.
10
+
11
+ Usage (tpy -- runner):
12
+ tpy # Start interactive REPL (auto-detect backend)
13
+ tpy input.py # Run the program
14
+ tpy -c "print(1 + 2)" # Run an inline snippet
15
+ tpy -b input.py # Compile and build binary (no run)
16
+ tpy --dump-code input.py # Print generated C++ to stdout
17
+
18
+ Usage (tpyc -- compiler):
19
+ tpyc input.py # Compile to C++ in __tpyc__/
20
+ tpyc input.py -o out/ # Compile to C++ in out/
21
+ tpyc input.py --build # Compile to C++ and build binary
22
+ tpyc input.py --exec # Compile, build, and run
23
+ tpyc input.py -x -- a b # Run with program args (sys.argv[1:] = ["a","b"])
24
+ tpyc --exec <<EOF # Read from stdin, build, and run
25
+ tpyc --dump-code <<EOF # Print generated C++ to stdout
26
+ tpyc --repl # Start interactive REPL (auto-detect backend)
27
+ tpyc --repl --cxx gcc # Force gcc backend
28
+ tpyc --repl file.py # Load file then start REPL
29
+ tpyc --repl -v # REPL with timing
30
+ tpyc --repl -vv # REPL with timing + generated C++
31
+
32
+ For tpyc, program arguments for --exec must be passed after `--` so tpyc's
33
+ own options can appear in any position (e.g. `tpyc foo.py -o out/ -x`).
34
+ """
35
+
36
+ from __future__ import annotations
37
+ import argparse
38
+ from concurrent.futures import ThreadPoolExecutor, as_completed
39
+ import os
40
+ import subprocess
41
+ import sys
42
+ import tempfile
43
+ import time
44
+ from pathlib import Path
45
+
46
+ from .parse import ParseError
47
+ from .sema import SemanticError, DiagnosticLevel
48
+ from .codegen_cpp import CodeGenOptions, CodeGenError
49
+ from .compiler import (
50
+ Compiler, CompileError, CompilerNotFoundError, BuildLayout, CppCompilerConfig,
51
+ DEFAULT_INT_CHOICES, list_compilers, get_or_build_pch,
52
+ )
53
+ from . import __version__, get_git_commit, get_runtime_dir, get_lib_dir, get_docs_dir
54
+ from .frontend_plugin import (
55
+ FrontendPluginError, FrontendRegistry, resolve_plugin_class,
56
+ route_dsl_opts,
57
+ )
58
+
59
+
60
+ def _fmt_ms(seconds: float) -> str:
61
+ """Format seconds as a human-readable duration."""
62
+ ms = seconds * 1000
63
+ if ms < 1000:
64
+ return f"{ms:.0f}ms"
65
+ return f"{ms / 1000:.1f}s"
66
+
67
+
68
+ def _print_info(prog_name: str) -> None:
69
+ """Print compiler version, paths, and environment info."""
70
+ commit = get_git_commit()
71
+ print(f"{prog_name} {__version__} ({commit})")
72
+ print()
73
+
74
+ # Paths
75
+ pkg_dir = Path(__file__).parent
76
+ lib_dir = get_lib_dir()
77
+ runtime_dir = get_runtime_dir()
78
+ docs_dir = get_docs_dir()
79
+ print(f"compiler: {pkg_dir}")
80
+ print(f"lib: {lib_dir / 'tpy'}")
81
+ print(f"runtime: {runtime_dir}")
82
+ print(f"docs: {docs_dir}")
83
+ print()
84
+
85
+ # C++ compiler
86
+ try:
87
+ config = CppCompilerConfig.from_env(cxx="auto")
88
+ cxx_desc = config.compiler_name
89
+ if config.ccache:
90
+ cxx_desc += " + ccache"
91
+ print(f"cxx: {cxx_desc} ({' '.join(config.compiler)})")
92
+ except CompilerNotFoundError:
93
+ print("cxx: not found")
94
+ print()
95
+
96
+ # Python
97
+ print(f"python: {sys.version.split()[0]} ({sys.executable})")
98
+
99
+
100
+ class ProgressPrinter:
101
+ """Prints build progress lines to stderr."""
102
+
103
+ def __init__(self, enabled: bool = True):
104
+ self.enabled = enabled
105
+
106
+ def _write(self, msg: str) -> None:
107
+ if not self.enabled:
108
+ return
109
+ sys.stderr.write(msg)
110
+ sys.stderr.flush()
111
+
112
+ @staticmethod
113
+ def _module_path(name: str) -> str:
114
+ """Convert dot-separated module name to path format."""
115
+ return name.replace('.', '/')
116
+
117
+ def header(self, config: CppCompilerConfig | None = None,
118
+ release: bool = False, n_jobs: int = 1) -> None:
119
+ if not self.enabled:
120
+ return
121
+ if config is not None:
122
+ variant = "release" if release else "debug"
123
+ cxx = config.compiler_name
124
+ if config.ccache:
125
+ cxx += " + ccache"
126
+ job_s = "job" if n_jobs == 1 else "jobs"
127
+ sys.stderr.write(f"TurboPython v{__version__} ({cxx}, {variant}, {n_jobs} {job_s})\n")
128
+ else:
129
+ sys.stderr.write(f"TurboPython v{__version__}\n")
130
+ sys.stderr.flush()
131
+
132
+ def analyzed(self, user_modules: list[str], n_stdlib: int,
133
+ n_warnings: int, elapsed: float) -> None:
134
+ n_total = len(user_modules) + n_stdlib
135
+ self._write(f" analyzed {n_total} modules ({_fmt_ms(elapsed)})\n")
136
+ for i, name in enumerate(user_modules):
137
+ is_last = i == len(user_modules) - 1
138
+ suffix = f" (+ {n_stdlib} stdlib)\n" if is_last and n_stdlib else "\n"
139
+ self._write(f" {self._module_path(name)}.py{suffix}")
140
+ if n_warnings:
141
+ w = "warning" if n_warnings == 1 else "warnings"
142
+ self._write(f" {n_warnings} {w}\n")
143
+
144
+ def pch(self, elapsed: float) -> None:
145
+ if elapsed >= 0.1:
146
+ self._write(f" precompiled tpy.hpp ({_fmt_ms(elapsed)})\n")
147
+
148
+ def translated(self, name: str, elapsed: float) -> None:
149
+ self._write(f" translated {self._module_path(name)}.py ({_fmt_ms(elapsed)})\n")
150
+
151
+ def compiled(self, name: str, elapsed: float) -> None:
152
+ self._write(f" compiled {name} ({_fmt_ms(elapsed)})\n")
153
+
154
+ def linked(self, name: str, elapsed: float) -> None:
155
+ self._write(f" linked {name} ({_fmt_ms(elapsed)})\n")
156
+
157
+ def separator(self) -> None:
158
+ self._write("-- \n")
159
+
160
+ def summary(self, n_modules: int,
161
+ t_compile: float, t_codegen: float, t_build: float) -> None:
162
+ if not self.enabled:
163
+ return
164
+ total = t_compile + t_codegen + t_build
165
+ sys.stderr.write(
166
+ f"{n_modules} modules compiled in {_fmt_ms(total)}"
167
+ f" (py {_fmt_ms(t_compile)}, codegen {_fmt_ms(t_codegen)},"
168
+ f" build {_fmt_ms(t_build)})\n"
169
+ )
170
+ sys.stderr.flush()
171
+
172
+
173
+ def _timed_run(cmd: list[str]) -> tuple[subprocess.CompletedProcess[str], float]:
174
+ """Run a command and return (result, elapsed_seconds)."""
175
+ t = time.monotonic()
176
+ r = subprocess.run(cmd, capture_output=True, text=True)
177
+ return r, time.monotonic() - t
178
+
179
+
180
+ def get_module_name(input_path: Path,
181
+ extra_extensions: frozenset[str] = frozenset()) -> str:
182
+ """Get module name from source file (e.g., hello.py -> hello).
183
+
184
+ `extra_extensions` lets frontend-plugin source files (e.g. `.pas`)
185
+ contribute additional strippable suffixes.
186
+ """
187
+ name = input_path.name
188
+ if name.endswith(".py"):
189
+ return name[:-3]
190
+ for ext in extra_extensions:
191
+ if name.endswith(ext):
192
+ return name[: -len(ext)]
193
+ return name
194
+
195
+
196
+ def _build_frontend_registry(plugin_specs: list[str],
197
+ dsl_opts: list[str]) -> FrontendRegistry | None:
198
+ """Load plugins listed on the command line and return a registry.
199
+
200
+ Two-phase: resolve each plugin's class (so we learn its `name`
201
+ ClassVar without running `__init__`), route `--dsl-opt` values to
202
+ each plugin's options dict, *then* instantiate with the routed
203
+ dict. Plugins that validate required options in `__init__` see
204
+ the real values on the first call; reassigning `self.options`
205
+ after construction wouldn't trigger that path. Returns None when
206
+ no plugins were requested.
207
+ """
208
+ if not plugin_specs:
209
+ if dsl_opts:
210
+ from .diagnostics import Diagnostic, DiagnosticLevel
211
+ raise FrontendPluginError(Diagnostic(
212
+ level=DiagnosticLevel.ERROR,
213
+ message=("--dsl-opt requires at least one --dsl-plugin"),
214
+ ))
215
+ return None
216
+ plugin_classes = [resolve_plugin_class(spec) for spec in plugin_specs]
217
+ routed = route_dsl_opts(dsl_opts, [cls.name for cls in plugin_classes])
218
+ registry = FrontendRegistry()
219
+ for cls in plugin_classes:
220
+ registry.register(cls(routed.get(cls.name, {})))
221
+ return registry
222
+
223
+
224
+ def _split_tpyc_argv(argv: list[str]) -> tuple[list[str], list[str]]:
225
+ """Split tpyc argv on the first `--` separator.
226
+
227
+ Returns (compiler_argv, script_args). Only the first `--` is honored; any
228
+ later `--` tokens are preserved as literal program args.
229
+
230
+ Raises ValueError if `--` is the first token (nothing for tpyc to parse).
231
+ """
232
+ if "--" not in argv:
233
+ return argv, []
234
+ i = argv.index("--")
235
+ if i == 0 and argv[1:]:
236
+ raise ValueError("input file must appear before '--'")
237
+ return argv[:i], argv[i + 1:]
238
+
239
+
240
+ def _run_cli(is_runner: bool) -> int:
241
+ prog_name = "tpy" if is_runner else "tpyc"
242
+ parser = argparse.ArgumentParser(
243
+ prog=prog_name,
244
+ description=(
245
+ "TurboPython - run programs or start an interactive REPL"
246
+ if is_runner else
247
+ "TurboPython Compiler - compiles TurboPython to C++"
248
+ ),
249
+ )
250
+ parser.add_argument("--version", action="version",
251
+ version=f"%(prog)s {__version__} ({get_git_commit()})")
252
+ parser.add_argument("input", nargs="?", help="Input TurboPython source file (.py)")
253
+ if is_runner:
254
+ # tpy: REMAINDER captures everything after the input positional, including
255
+ # flags like -O, matching `python script.py -O`. Limitation: flags that
256
+ # also exist as tpy options (e.g. -O) AND appear *before* the input
257
+ # positional are still consumed by tpy -- with `-c CMD`, there is no
258
+ # input positional to separate them. Use `--` to force forwarding:
259
+ # `tpy -c CMD -- -O arg`.
260
+ parser.add_argument("script_args", nargs=argparse.REMAINDER,
261
+ help="Arguments forwarded to the running program as sys.argv[1:]")
262
+ # tpyc: no REMAINDER positional -- tpyc's own options must be parseable in
263
+ # any position (including after the input file). Program args for --exec
264
+ # are pre-split off from argv on `--` before parse_args runs below.
265
+ parser.add_argument("-c", dest="cmd", metavar="CMD", help="Execute CMD as a TurboPython program string")
266
+ parser.add_argument("-o", "--output", help="Output directory (default: __tpyc__/ next to source)")
267
+ parser.add_argument("-v", "--verbose", action="count", default=0, help="Verbose output (-v commands+timing, -vv +generated C++)")
268
+ parser.add_argument("-b", "--build", action="store_true", help="Compile C++ to binary after generating")
269
+ parser.add_argument("-x", "--exec", action="store_true", help="Build and run the program")
270
+ parser.add_argument("-O", "--release", action="store_true", help="Build with optimizations (default: debug)")
271
+ parser.add_argument("--emit-source", action="store_true", help="Embed Python source as comments in generated C++")
272
+ parser.add_argument("-i", "--repl", action="store_true", help="Start interactive REPL")
273
+ parser.add_argument("--print-types", action="store_true", help="Print API reference (builtins, tplib, bundled stdlib) as markdown")
274
+ parser.add_argument("--install-agent-docs", metavar="DIR",
275
+ help="Install TPy agent docs (TPY_FOR_AGENTS.md, TPY_LANGUAGE_FEATURES.md, "
276
+ "TPY_STDLIB_ROADMAP.md, TPY_API_REFERENCE.md) into DIR and print "
277
+ "an AGENTS.md snippet to stdout")
278
+ parser.add_argument("--dump-code", action="store_true", help="Print generated C++ to stdout")
279
+ parser.add_argument(
280
+ "--default-int",
281
+ choices=DEFAULT_INT_CHOICES,
282
+ default="Int32",
283
+ help="Default type for unannotated integer literals (default: Int32)",
284
+ )
285
+ parser.add_argument(
286
+ "-L", "--lib", action="append", default=None,
287
+ help="Extra library search path (can be repeated)",
288
+ )
289
+ parser.add_argument(
290
+ "--no-stdlib", action="store_true",
291
+ help="Disable standard library (tplib, stdlib modules, tpy protocols)",
292
+ )
293
+ parser.add_argument(
294
+ "--cxx", default="auto",
295
+ help="C++ compiler: auto, list, gcc, gcc-14, clang, clang-18, zig, ... (default: auto)",
296
+ )
297
+ ccache_group = parser.add_mutually_exclusive_group()
298
+ ccache_group.add_argument("--ccache", action="store_true", default=None,
299
+ help="Force ccache usage")
300
+ ccache_group.add_argument("--no-ccache", dest="ccache", action="store_false",
301
+ help="Disable ccache")
302
+ parser.add_argument("--no-pch", dest="pch", action="store_false", default=True,
303
+ help="Disable precompiled header caching")
304
+ parser.add_argument("--no-bundle-runtime", dest="bundle_runtime",
305
+ action="store_false", default=True,
306
+ help="Don't copy runtime headers into the output directory")
307
+ parser.add_argument(
308
+ "--pcre2", choices=["bundled", "system", "auto", "none"], default="bundled",
309
+ help="PCRE2 source for the `re` module: bundled (vendored, default), "
310
+ "system (find_package / -lpcre2-8), auto (system, fall back to "
311
+ "bundled), or none (disabled -- any module that imports `re` "
312
+ "becomes a compile error, useful for embedded targets that want "
313
+ "to strip out regex)",
314
+ )
315
+ parser.add_argument(
316
+ "--dsl-plugin", action="append", default=None, metavar="SPEC",
317
+ help="Load a frontend plugin (path to .py file or importable module). "
318
+ "Can be repeated.",
319
+ )
320
+ parser.add_argument(
321
+ "--dsl-opt", action="append", default=None, metavar="NAME.KEY=VALUE",
322
+ help="Pass an option to a frontend plugin. Can be repeated.",
323
+ )
324
+ parser.add_argument("-j", "--jobs", type=int, default=None,
325
+ help="Parallel compile jobs (default: number of CPUs)")
326
+ parser.add_argument("--no-main", dest="no_main", action="store_true",
327
+ help="Skip main() generation (emit __tpy_main instead, for linking with external C++)")
328
+ parser.add_argument("-q", "--quiet", action="store_true",
329
+ help="Suppress progress lines (show only errors and program output)")
330
+ parser.add_argument("--info", action="store_true",
331
+ help="Print compiler version, paths, and environment info")
332
+
333
+ if is_runner:
334
+ args = parser.parse_args()
335
+ else:
336
+ # tpyc: program args (for --exec) must follow `--`, so tpyc's own
337
+ # options can appear anywhere -- including after the input file.
338
+ try:
339
+ tpyc_argv, script_args = _split_tpyc_argv(sys.argv[1:])
340
+ except ValueError as e:
341
+ parser.error(str(e))
342
+ args = parser.parse_args(tpyc_argv)
343
+ args.script_args = script_args
344
+
345
+ # tpy (runner) default action: bare `tpy` -> REPL; `tpy foo.py` -> run.
346
+ # Explicit actions (-b, -x, --dump-code, -i) and informational flags
347
+ # take precedence -- we only set a default when nothing else was requested.
348
+ # Piped stdin counts as input (matches `python < script.py`).
349
+ if is_runner:
350
+ has_input = bool(args.input or args.cmd) or not sys.stdin.isatty()
351
+ has_action = (
352
+ args.build or args.exec or args.dump_code or args.repl
353
+ or args.info or args.print_types
354
+ or args.install_agent_docs is not None
355
+ or args.cxx == "list"
356
+ )
357
+ if not has_action:
358
+ if has_input:
359
+ args.exec = True
360
+ else:
361
+ args.repl = True
362
+
363
+ # Build library search paths
364
+ lib_dir = get_lib_dir()
365
+ lib_dirs: list[Path] = []
366
+ for extra in (args.lib or []):
367
+ lib_dirs.append(Path(extra).resolve())
368
+ if not args.no_stdlib:
369
+ lib_dirs.append(lib_dir / "tpy")
370
+
371
+ # Handle --info
372
+ if args.info:
373
+ _print_info(prog_name)
374
+ return 0
375
+
376
+ # Handle --cxx list
377
+ if args.cxx == "list":
378
+ list_compilers()
379
+ return 0
380
+
381
+ # Handle REPL mode
382
+ if args.repl:
383
+ from .repl import REPLSession
384
+ preload_files = []
385
+ if args.input:
386
+ # Support multiple files separated by the input arg
387
+ preload_files = [Path(args.input).resolve()]
388
+ return REPLSession(verbose=args.verbose, preload_files=preload_files,
389
+ lib_dirs=lib_dirs, cxx=args.cxx).run()
390
+
391
+ # Handle --install-agent-docs (before --print-types so it's not silently dropped)
392
+ if args.install_agent_docs is not None:
393
+ from .install_docs import install_agent_docs, agents_md_snippet
394
+ target = Path(args.install_agent_docs).resolve()
395
+ try:
396
+ written = install_agent_docs(target)
397
+ except (NotADirectoryError, OSError) as e:
398
+ print(f"error: {e}", file=sys.stderr)
399
+ return 1
400
+ for p in written:
401
+ print(f"Wrote {p}", file=sys.stderr)
402
+ print(agents_md_snippet(Path(args.install_agent_docs)))
403
+ return 0
404
+
405
+ # Handle --print-types
406
+ if args.print_types:
407
+ from .dump_types import dump_builtin_types
408
+ dump_builtin_types()
409
+ return 0
410
+
411
+ # Handle -c: implies -x unless --dump-code or -b is set.
412
+ # With -c, a positional ends up in args.input due to nargs="?" -- prepend
413
+ # it to forwarded script args (matches `python -c CMD a b`). For tpyc,
414
+ # args.script_args may already contain tokens from a post-`--` separator;
415
+ # the input positional precedes them in sys.argv[1:] order.
416
+ if args.cmd is not None:
417
+ if args.input:
418
+ args.script_args = [args.input, *args.script_args]
419
+ args.input = None
420
+ if not args.dump_code and not args.build:
421
+ args.exec = True
422
+
423
+ # Auto-detect stdin when no input file given and stdin is piped/heredoc
424
+ if not args.cmd and not args.input and not sys.stdin.isatty():
425
+ args.input = "-"
426
+
427
+ # Require input file for non-REPL modes
428
+ if not args.cmd and not args.input:
429
+ parser.error("the following arguments are required: input (or -c CMD)")
430
+
431
+ # script_args only makes sense when the program is actually run.
432
+ # For tpy (runner), these come from REMAINDER after the input positional.
433
+ # For tpyc, they come either from after a `--` separator or from the -c
434
+ # shuffle above. In compile-only / --build / --dump-code modes, reject
435
+ # them with argparse's native "unrecognized arguments" phrasing.
436
+ if args.script_args and not args.exec:
437
+ parser.error(f"unrecognized arguments: {' '.join(args.script_args)}")
438
+
439
+ # Handle inline/stdin source
440
+ reading_from_stdin = args.cmd is not None or args.input == "-"
441
+ temp_dir = None
442
+
443
+ # Load frontend plugins, if any, before deriving the module name so
444
+ # that plugin-claimed extensions (e.g. `.pas`) are stripped.
445
+ try:
446
+ frontend_registry = _build_frontend_registry(
447
+ args.dsl_plugin or [], args.dsl_opt or [])
448
+ except FrontendPluginError as e:
449
+ print(f"error: {e.diagnostic.message}", file=sys.stderr)
450
+ return 1
451
+ plugin_extensions = (frontend_registry.all_extensions()
452
+ if frontend_registry is not None else frozenset())
453
+ # Plugins may contribute extra library search dirs (e.g. the
454
+ # Pascal frontend ships its stdlib at `pascal/lib/`). Append them
455
+ # before the implicit TPy stdlib so the user doesn't need a `-L`
456
+ # for each plugin-owned directory.
457
+ if frontend_registry is not None:
458
+ plugin_libs: list[Path] = []
459
+ for p in frontend_registry.plugins:
460
+ for d in p.library_paths():
461
+ plugin_libs.append(Path(d).resolve())
462
+ stdlib_pos = len(lib_dirs)
463
+ if not args.no_stdlib:
464
+ # `lib_dirs` ends with the TPy stdlib (`lib/tpy/`) when
465
+ # not --no-stdlib; keep plugin libs ahead of it so they
466
+ # don't shadow stdlib lookups but still come after user
467
+ # `-L` dirs.
468
+ stdlib_pos = len(lib_dirs) - 1
469
+ lib_dirs[stdlib_pos:stdlib_pos] = plugin_libs
470
+
471
+ if reading_from_stdin:
472
+ source = args.cmd if args.cmd is not None else sys.stdin.read()
473
+ module_name = "main"
474
+ temp_dir = tempfile.mkdtemp(prefix="tpyc_")
475
+ if args.output:
476
+ output_dir = Path(args.output)
477
+ else:
478
+ output_dir = Path(temp_dir)
479
+ input_path = None
480
+ else:
481
+ input_path = Path(args.input).resolve()
482
+
483
+ if not input_path.exists():
484
+ print(f"Error: Input file not found: {input_path}", file=sys.stderr)
485
+ return 1
486
+
487
+ # Determine output directory
488
+ if args.output:
489
+ output_dir = Path(args.output)
490
+ else:
491
+ # Default: __tpyc__/ next to source file
492
+ output_dir = input_path.parent / "__tpyc__"
493
+
494
+ # Get module name for output paths
495
+ module_name = get_module_name(input_path, plugin_extensions)
496
+
497
+ if args.dump_code and (args.build or args.exec):
498
+ parser.error("--dump-code cannot be combined with --build or --exec")
499
+ if args.jobs is not None and args.jobs < 1:
500
+ parser.error("-j/--jobs must be a positive integer")
501
+ building = args.build or args.exec
502
+ quiet = args.dump_code or args.quiet
503
+ explicit_output = bool(args.output)
504
+ n_jobs = args.jobs or os.cpu_count() or 1
505
+ progress = ProgressPrinter(enabled=not quiet)
506
+
507
+ try:
508
+ options = CodeGenOptions(emit_source_comments=args.emit_source,
509
+ no_main=args.no_main)
510
+ all_cpp_paths = []
511
+
512
+ cpp_config: CppCompilerConfig | None = None
513
+ if building:
514
+ cpp_config = CppCompilerConfig.from_env(cxx=args.cxx)
515
+ if args.ccache is not None:
516
+ cpp_config.ccache = args.ccache
517
+ progress.header(cpp_config, args.release, n_jobs)
518
+ else:
519
+ progress.header()
520
+
521
+ # Create compiler (unified for both stdin and file input)
522
+ t_compile_start = time.monotonic()
523
+ if reading_from_stdin:
524
+ compiler = Compiler.from_source(source, module_name, default_int=args.default_int,
525
+ lib_dirs=lib_dirs,
526
+ frontend_registry=frontend_registry)
527
+ else:
528
+ compiler = Compiler(input_path, default_int=args.default_int, lib_dirs=lib_dirs,
529
+ frontend_registry=frontend_registry)
530
+
531
+ compiled_modules = compiler.compile()
532
+ t_compile = time.monotonic() - t_compile_start
533
+
534
+ n_py = len(compiled_modules)
535
+ user_modules = [m.name for m in compiled_modules if compiler.is_user_module(m)]
536
+ n_stdlib = n_py - len(user_modules)
537
+
538
+ # Collect diagnostics: errors abort immediately, warnings are deferred
539
+ has_errors = False
540
+ warning_messages: list[str] = []
541
+ n_warnings = 0
542
+ for diag in compiler.diagnostics:
543
+ # Errors from the compiler driver itself (frontend plugin
544
+ # parse failures, module-resolution failures, etc.) must
545
+ # halt the build the same way analyzer-level errors do.
546
+ # Without this check a plugin's parse error surfaces as a
547
+ # downstream C++ compile failure because the empty-module
548
+ # fallback gets fed into the build pipeline.
549
+ if diag.level == DiagnosticLevel.ERROR:
550
+ has_errors = True
551
+ print(diag.format(prog_name), file=sys.stderr)
552
+ else:
553
+ n_warnings += 1
554
+ warning_messages.append(diag.format(prog_name))
555
+ for compiled in compiled_modules:
556
+ source_name = "<stdin>" if reading_from_stdin else os.path.relpath(compiled.path)
557
+ if compiled.analyzer:
558
+ for diag in compiled.analyzer.diagnostics:
559
+ if diag.level == DiagnosticLevel.ERROR:
560
+ has_errors = True
561
+ print(diag.format(source_name), file=sys.stderr)
562
+ elif diag.level == DiagnosticLevel.WARNING:
563
+ n_warnings += 1
564
+ warning_messages.append(diag.format(source_name))
565
+
566
+ progress.analyzed(user_modules, n_stdlib, n_warnings, t_compile)
567
+
568
+ if has_errors:
569
+ return 1
570
+
571
+ # Print warnings immediately when not building (no summary to defer to)
572
+ if not building:
573
+ for msg in warning_messages:
574
+ print(msg, file=sys.stderr)
575
+
576
+ t_codegen_start = time.monotonic()
577
+ for i, compiled in enumerate(compiled_modules, 1):
578
+ source_name = "<stdin>" if reading_from_stdin else os.path.relpath(compiled.path)
579
+
580
+ if args.dump_code:
581
+ try:
582
+ hpp_code, cpp_code = compiler.generate_code_to_strings(compiled, options=options)
583
+ except CodeGenError as e:
584
+ if e.filename is None and not compiled.is_entry_point:
585
+ e.filename = source_name
586
+ raise
587
+ if hpp_code:
588
+ print(f"// === include/{compiled.name}.hpp ===")
589
+ print(hpp_code)
590
+ if cpp_code:
591
+ print(f"// === src/{compiled.name}.cpp ===")
592
+ print(cpp_code)
593
+ continue
594
+
595
+ # -vv: show generated C++ inline
596
+ if args.verbose >= 2:
597
+ try:
598
+ hpp_code, cpp_code = compiler.generate_code_to_strings(compiled, options=options)
599
+ except CodeGenError as e:
600
+ if e.filename is None and not compiled.is_entry_point:
601
+ e.filename = source_name
602
+ raise
603
+ print(f"// === include/{compiled.name}.hpp ===")
604
+ print(hpp_code)
605
+ if cpp_code:
606
+ print(f"// === src/{compiled.name}.cpp ===")
607
+ print(cpp_code)
608
+
609
+ t_file_start = time.monotonic()
610
+ try:
611
+ hpp_path, cpp_path = compiler.generate_code(compiled, output_dir, options=options,
612
+ flat=explicit_output)
613
+ except CodeGenError as e:
614
+ if e.filename is None and not compiled.is_entry_point:
615
+ e.filename = source_name
616
+ raise
617
+ t_file = time.monotonic() - t_file_start
618
+ if cpp_path is not None:
619
+ all_cpp_paths.append(cpp_path)
620
+ progress.translated(compiled.name, t_file)
621
+
622
+ t_codegen = time.monotonic() - t_codegen_start
623
+
624
+ if args.dump_code:
625
+ return 0
626
+
627
+ n_cpp = len(all_cpp_paths)
628
+
629
+ # Generate sources.cmake for CMake integration
630
+ runtime_dir = get_runtime_dir()
631
+ link_flags = compiler.collect_link_flags()
632
+
633
+ # Resolve third-party deps (e.g. PCRE2 declared via
634
+ # `# tpy: link("pcre2", managed=True)`) into concrete build inputs.
635
+ # DisabledLibError is raised when the user passed `--<lib>=none` but
636
+ # a module in the compile graph needs that lib -- surface it as a
637
+ # clean compile error.
638
+ from .build.third_party import resolve_build_plan, DisabledLibError
639
+ third_party_modes = {"pcre2": args.pcre2}
640
+ try:
641
+ third_party_plan = resolve_build_plan(
642
+ dep_names=compiler.collect_third_party_deps(),
643
+ runtime_cpp_dir=runtime_dir / "cpp",
644
+ modes=third_party_modes,
645
+ )
646
+ except DisabledLibError as exc:
647
+ print(f"error: {exc}", file=sys.stderr)
648
+ return 1
649
+
650
+ from .compiler import discover_runtime_cpp_sources
651
+ runtime_cpp_sources = discover_runtime_cpp_sources(runtime_dir / "cpp")
652
+
653
+ cmake_layout = BuildLayout(output_dir, module_name, flat=explicit_output)
654
+ cmake_layout.generate_cmake(
655
+ runtime_include_dir=runtime_dir / "cpp" / "include",
656
+ cpp_files=all_cpp_paths,
657
+ link_flags=link_flags,
658
+ bundle_runtime=args.bundle_runtime and not building and explicit_output,
659
+ third_party_libs=third_party_plan.libs,
660
+ runtime_cpp_sources=runtime_cpp_sources or None,
661
+ )
662
+
663
+ # Build if requested
664
+ if building:
665
+ assert cpp_config is not None
666
+ build_variant = "release" if args.release else "debug"
667
+ layout = BuildLayout(output_dir, module_name, build_variant=build_variant,
668
+ flat=explicit_output)
669
+ binary_path = layout.binary_path()
670
+
671
+ opt_flags = ["-O3", "-DNDEBUG"] if args.release else ["-g", "-O0"]
672
+ cpp_config.link_flags = link_flags
673
+
674
+ # Build or reuse precompiled header
675
+ pch_includes: list[Path] = []
676
+ if args.pch:
677
+ t_pch_start = time.monotonic()
678
+ pch_path = get_or_build_pch(
679
+ cpp_config, runtime_dir / "cpp" / "include", opt_flags,
680
+ pch_dir=layout.build_dir / "pch",
681
+ )
682
+ t_pch = time.monotonic() - t_pch_start
683
+ if pch_path:
684
+ pch_includes.append(pch_path)
685
+ progress.pch(t_pch)
686
+ # ccache needs these sloppiness flags for PCH support
687
+ if cpp_config.ccache:
688
+ slop = os.environ.get("CCACHE_SLOPPINESS", "")
689
+ parts = {s.strip() for s in slop.split(",") if s.strip()}
690
+ parts.update(("pch_defines", "time_macros"))
691
+ os.environ["CCACHE_SLOPPINESS"] = ",".join(sorted(parts))
692
+
693
+ compile_cmds = layout.build_cpp_commands(
694
+ runtime_include_dir=runtime_dir / "cpp" / "include",
695
+ cpp_files=all_cpp_paths,
696
+ opt_flags=opt_flags,
697
+ config=cpp_config,
698
+ force_includes=pch_includes or None,
699
+ extra_include_dirs=third_party_plan.extra_include_dirs or None,
700
+ extra_link_flags=third_party_plan.extra_link_flags or None,
701
+ c_sources=third_party_plan.c_sources or None,
702
+ runtime_cpp_sources=runtime_cpp_sources or None,
703
+ )
704
+
705
+ compile_steps = compile_cmds[:-1]
706
+ link_step = compile_cmds[-1]
707
+
708
+ t_build_start = time.monotonic()
709
+
710
+ def _cpp_name(cmd: list[str]) -> str:
711
+ """Extract .cpp filename from a compile command."""
712
+ src = cmd[-1]
713
+ return os.path.basename(src)
714
+
715
+ # Compile steps in parallel (or serial for single file / -j1)
716
+ if len(compile_steps) <= 1 or n_jobs <= 1:
717
+ for cmd in compile_steps:
718
+ if args.verbose >= 1:
719
+ print(f" $ {' '.join(cmd)}", file=sys.stderr)
720
+ t_step = time.monotonic()
721
+ result = subprocess.run(cmd, capture_output=True, text=True)
722
+ if result.returncode != 0:
723
+ print(f"C++ compilation failed:", file=sys.stderr)
724
+ print(result.stderr, file=sys.stderr)
725
+ return 1
726
+ progress.compiled(_cpp_name(cmd), time.monotonic() - t_step)
727
+ else:
728
+ if args.verbose >= 1:
729
+ for cmd in compile_steps:
730
+ print(f" $ {' '.join(cmd)}", file=sys.stderr)
731
+ failed_stderr = ""
732
+ with ThreadPoolExecutor(max_workers=n_jobs) as pool:
733
+ futures = {
734
+ pool.submit(_timed_run, cmd): cmd
735
+ for cmd in compile_steps
736
+ }
737
+ for future in as_completed(futures):
738
+ r, elapsed = future.result()
739
+ if r.returncode != 0 and not failed_stderr:
740
+ failed_stderr = r.stderr
741
+ else:
742
+ progress.compiled(_cpp_name(futures[future]), elapsed)
743
+ if failed_stderr:
744
+ print(f"C++ compilation failed:", file=sys.stderr)
745
+ print(failed_stderr, file=sys.stderr)
746
+ return 1
747
+
748
+ # Link step
749
+ if args.verbose >= 1:
750
+ print(f" $ {' '.join(link_step)}", file=sys.stderr)
751
+ t_link = time.monotonic()
752
+ result = subprocess.run(link_step, capture_output=True, text=True)
753
+ if result.returncode != 0:
754
+ print(f"C++ link failed:", file=sys.stderr)
755
+ print(result.stderr, file=sys.stderr)
756
+ return 1
757
+ progress.linked(module_name, time.monotonic() - t_link)
758
+ t_build = time.monotonic() - t_build_start
759
+
760
+ progress.summary(n_py, t_compile, t_codegen, t_build)
761
+
762
+ for msg in warning_messages:
763
+ print(msg, file=sys.stderr)
764
+
765
+ if not args.exec:
766
+ print(f"Built: {binary_path}")
767
+
768
+ # Run if requested
769
+ if args.exec:
770
+ progress.separator()
771
+ t_run_start = time.monotonic()
772
+ result = subprocess.run([str(binary_path), *args.script_args])
773
+ t_run = time.monotonic() - t_run_start
774
+
775
+ if args.verbose >= 1:
776
+ print(f" run: {t_run*1000:.0f}ms total: {(t_compile+t_codegen+t_build+t_run)*1000:.0f}ms",
777
+ file=sys.stderr)
778
+
779
+ return result.returncode
780
+
781
+ return 0
782
+
783
+ except CompilerNotFoundError as e:
784
+ print(f"Error: {e}", file=sys.stderr)
785
+ return 1
786
+ except CompileError as e:
787
+ print(e.format(), file=sys.stderr)
788
+ return 1
789
+ except ParseError as e:
790
+ print(f"Parse error: {e}", file=sys.stderr)
791
+ return 1
792
+ except SemanticError as e:
793
+ error_filename = "<stdin>" if reading_from_stdin else input_path.name
794
+ print(e.format(error_filename), file=sys.stderr)
795
+ return 1
796
+ except CodeGenError as e:
797
+ error_filename = "<stdin>" if reading_from_stdin else input_path.name
798
+ print(e.format(error_filename), file=sys.stderr)
799
+ return 1
800
+ except FileNotFoundError as e:
801
+ print(f"Error: {e}", file=sys.stderr)
802
+ return 1
803
+ except Exception as e:
804
+ print(f"Internal error: {e}", file=sys.stderr)
805
+ if args.verbose:
806
+ import traceback
807
+ traceback.print_exc()
808
+ return 1
809
+
810
+
811
+ def main_tpyc() -> int:
812
+ """Entry point for the `tpyc` command (compiler mode)."""
813
+ return _run_cli(is_runner=False)
814
+
815
+
816
+ def main_tpy() -> int:
817
+ """Entry point for the `tpy` command (runner mode)."""
818
+ return _run_cli(is_runner=True)
819
+
820
+
821
+ if __name__ == "__main__":
822
+ sys.exit(main_tpyc())