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/repl.py ADDED
@@ -0,0 +1,529 @@
1
+ """
2
+ TurboPython Interactive REPL
3
+
4
+ Provides an interactive Python-like experience with:
5
+ - Multi-line input support (for function/class definitions)
6
+ - Expression auto-printing (bare `5+3` prints the result)
7
+ - Error/panic recovery (failed statements don't accumulate)
8
+ - C++ compiler selection via --cxx: clang-repl (default), clang++, g++, zig
9
+ - Verbose mode: -v for timing, -vv for generated C++
10
+ - Trailing backslash continues to next line
11
+ """
12
+
13
+ from __future__ import annotations
14
+ import ast
15
+ import atexit
16
+ import difflib
17
+ import readline # For history support
18
+ import shutil
19
+ import sys
20
+ import tempfile
21
+ import time
22
+ from pathlib import Path
23
+
24
+ from .parse import ParseError, TpyExprStmt, TpyCoerce
25
+ from .sema import SemanticError, DiagnosticLevel
26
+ from .typesys import VoidType, is_any_str_type
27
+ from .type_def_registry import is_char_type
28
+ from .compiler import Compiler
29
+ from .codegen_cpp.context import get_include_path
30
+ from .repl_backends import (
31
+ REPLBackend, BackendResult, detect_backend, _fmt_ms,
32
+ )
33
+
34
+
35
+ class REPLSession:
36
+ """Interactive TurboPython REPL session."""
37
+
38
+ def __init__(self, verbose: int = 0, preload_files: list[Path] | None = None,
39
+ lib_dirs: list[Path] | None = None,
40
+ cxx: str = "auto"):
41
+ self.accumulated_lines: list[str] = []
42
+ self.verbose = verbose
43
+ self.preload_files = preload_files or []
44
+ self.lib_dirs = lib_dirs
45
+ self.temp_dir = Path(tempfile.mkdtemp(prefix="tpyc_repl_"))
46
+ self.prev_cpp_lines: list[str] = [] # For verbose diff
47
+ self._module_name = "repl"
48
+ self._cxx = cxx
49
+ self._backend: REPLBackend | None = None
50
+ atexit.register(self.cleanup)
51
+
52
+ def _preload_file(self, filepath: Path) -> bool:
53
+ """Load and execute a file, accumulating its definitions.
54
+
55
+ Returns True on success, False on error.
56
+ """
57
+ try:
58
+ source = filepath.read_text()
59
+ except FileNotFoundError:
60
+ print(f"Error: File not found: {filepath}", file=sys.stderr)
61
+ return False
62
+ except IOError as e:
63
+ print(f"Error reading {filepath}: {e}", file=sys.stderr)
64
+ return False
65
+
66
+ # Try to compile and run the file
67
+ success, output = self._try_compile_and_run(source, maybe_auto_print=False)
68
+
69
+ if success:
70
+ # Accumulate all definitions from the file
71
+ if self._should_accumulate(source):
72
+ self.accumulated_lines.append(source)
73
+ else:
74
+ # For files with mixed content, extract just the definitions
75
+ try:
76
+ tree = ast.parse(source)
77
+ definitions = []
78
+ for stmt in tree.body:
79
+ if isinstance(stmt, (
80
+ ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef,
81
+ ast.Assign, ast.AnnAssign, ast.AugAssign,
82
+ ast.Import, ast.ImportFrom, ast.TypeAlias,
83
+ )):
84
+ # Get the source lines for this statement
85
+ start = stmt.lineno - 1
86
+ end = stmt.end_lineno if stmt.end_lineno else stmt.lineno
87
+ lines = source.split('\n')[start:end]
88
+ definitions.append('\n'.join(lines))
89
+ if definitions:
90
+ self.accumulated_lines.extend(definitions)
91
+ except SyntaxError:
92
+ pass
93
+ if output:
94
+ print(output, end="")
95
+ return True
96
+ else:
97
+ print(output, end="", file=sys.stderr)
98
+ return False
99
+
100
+ def _setup_readline(self) -> None:
101
+ """Configure readline for history."""
102
+ history_path = Path.home() / ".tpyc_history"
103
+ try:
104
+ readline.read_history_file(history_path)
105
+ except FileNotFoundError:
106
+ pass
107
+ readline.set_history_length(1000)
108
+ atexit.register(readline.write_history_file, history_path)
109
+
110
+ def _read_paste_mode(self) -> str | None:
111
+ """Read multiline input in paste mode. Returns None if cancelled."""
112
+ print("Paste mode: paste your code, then Ctrl+D to execute (Ctrl+C to cancel)")
113
+ lines = []
114
+ try:
115
+ while True:
116
+ try:
117
+ line = input("... ")
118
+ lines.append(line)
119
+ except EOFError:
120
+ break
121
+ except KeyboardInterrupt:
122
+ print("\nPaste cancelled.")
123
+ return None
124
+
125
+ print() # Newline after Ctrl+D
126
+ return "\n".join(lines)
127
+
128
+ def run(self) -> int:
129
+ """Main REPL loop. Returns exit code."""
130
+ # Detect and start backend
131
+ self._backend = detect_backend(
132
+ self._cxx, self.temp_dir, self._module_name, verbose=self.verbose,
133
+ )
134
+ from . import __version__
135
+ print(f"TurboPython REPL v{__version__} (backend: {self._backend.name})")
136
+ print("Variables, functions, and classes are remembered between inputs.")
137
+ print("Empty line unindents (or ends block at col 0). Trailing \\ continues input. Ctrl+D to exit.")
138
+ print("Type .paste (or .p) for multiline input mode.")
139
+
140
+ self._backend.startup()
141
+ if self.verbose >= 1:
142
+ print(f"Build dir: {self.temp_dir}/")
143
+ if hasattr(self._backend, 'pch_cache_dir'):
144
+ print(f"PCH cache: {self._backend.pch_cache_dir}/")
145
+
146
+ # Preload files if specified
147
+ for filepath in self.preload_files:
148
+ print(f"Loading {filepath}...")
149
+ if not self._preload_file(filepath):
150
+ return 1
151
+
152
+ # Auto-import tpy types if no files were preloaded
153
+ if not self.preload_files:
154
+ self.accumulated_lines.append("from tpy import *")
155
+ print("(imported tpy types: Int32, Char, Array, Span, Ptr, ...)")
156
+
157
+ # Setup readline
158
+ self._setup_readline()
159
+
160
+ while True:
161
+ try:
162
+ line = input(">>> ")
163
+
164
+ # Handle special commands
165
+ stripped = line.strip()
166
+ if stripped in ("exit", "quit"):
167
+ break
168
+ if not stripped:
169
+ continue
170
+
171
+ # Dot commands
172
+ if stripped.startswith("."):
173
+ if stripped in (".paste", ".p"):
174
+ pasted = self._read_paste_mode()
175
+ if pasted and pasted.strip():
176
+ self._process_input(pasted)
177
+ else:
178
+ print(f"Unknown command: {stripped.split()[0]}")
179
+ continue
180
+
181
+ # Alt+Enter inserts literal newlines into readline buffer,
182
+ # so input() may return multiline strings directly
183
+ full_input = line
184
+
185
+ # Handle backslash continuation (fallback for terminals
186
+ # where Alt+Enter doesn't work)
187
+ while full_input.rstrip().endswith("\\"):
188
+ full_input = full_input.rstrip()[:-1] # Remove trailing backslash
189
+ try:
190
+ cont_line = input("... ")
191
+ full_input += "\n" + cont_line
192
+ except EOFError:
193
+ break
194
+
195
+ # Check for multi-line continuation (incomplete syntax)
196
+ if self._needs_continuation(full_input):
197
+ in_block = full_input.rstrip().endswith(":")
198
+ indent_level = 1 # Start with one indent level
199
+ cont_stripped = ""
200
+ while True:
201
+ try:
202
+ indent = "\t" * indent_level
203
+ cont_line = input("... " + indent)
204
+ # Empty line: reduce indent or end block
205
+ if not cont_line.strip():
206
+ if indent_level > 1:
207
+ indent_level -= 1
208
+ continue # Don't add empty line, just reduce indent
209
+ else:
210
+ break # At first block level (or zero), end block
211
+ # else/elif/except/finally belong at the outer
212
+ # block level (no extra indent)
213
+ cont_stripped = cont_line.strip()
214
+ if cont_stripped.split()[0].rstrip(":") in (
215
+ "else", "elif", "except", "finally",
216
+ ):
217
+ full_input += "\n" + cont_line.strip()
218
+ else:
219
+ full_input += "\n" + indent + cont_line
220
+ except EOFError:
221
+ break
222
+ # For blocks (def/class/if/etc), require empty line to end
223
+ # For other continuations (unclosed parens), end when complete
224
+ if not in_block and not self._needs_continuation(full_input):
225
+ break
226
+ # Adjust indent based on what user typed
227
+ if cont_stripped.endswith(":"):
228
+ indent_level += 1
229
+
230
+ self._process_input(full_input)
231
+
232
+ except EOFError:
233
+ print()
234
+ break
235
+ except KeyboardInterrupt:
236
+ print()
237
+ continue
238
+
239
+ return 0
240
+
241
+ def _needs_continuation(self, source: str) -> bool:
242
+ """Check if input needs continuation lines."""
243
+ stripped = source.rstrip()
244
+
245
+ # Ends with colon (def, class, if, for, while, etc.)
246
+ if stripped.endswith(":"):
247
+ return True
248
+
249
+ # Check for unclosed brackets/parens
250
+ opens = source.count("(") + source.count("[") + source.count("{")
251
+ closes = source.count(")") + source.count("]") + source.count("}")
252
+ if opens > closes:
253
+ return True
254
+
255
+ # Check for incomplete block (has def/class/if/etc but only header)
256
+ try:
257
+ ast.parse(source)
258
+ return False
259
+ except SyntaxError:
260
+ return True
261
+
262
+ def _get_indent_for_continuation(self, source: str) -> str:
263
+ """Calculate indentation for the next continuation line."""
264
+ lines = source.split("\n")
265
+ last_line = lines[-1]
266
+
267
+ # Get current indentation of last line
268
+ current_indent = len(last_line) - len(last_line.lstrip())
269
+
270
+ # If last line ends with ':', increase indent
271
+ if last_line.rstrip().endswith(":"):
272
+ return " " * (current_indent + 4)
273
+
274
+ # Otherwise, maintain the same indentation as the last non-empty line
275
+ for line in reversed(lines):
276
+ if line.strip():
277
+ return " " * (len(line) - len(line.lstrip()))
278
+
279
+ return ""
280
+
281
+ def _is_bare_expression(self, source: str) -> bool:
282
+ """Check if input is a single bare expression (not a print call)."""
283
+ try:
284
+ tree = ast.parse(source)
285
+ if len(tree.body) != 1:
286
+ return False
287
+ stmt = tree.body[0]
288
+ if not isinstance(stmt, ast.Expr):
289
+ return False
290
+ # print() calls are already printing, don't wrap
291
+ if isinstance(stmt.value, ast.Call):
292
+ if isinstance(stmt.value.func, ast.Name):
293
+ if stmt.value.func.id == "print":
294
+ return False
295
+ return True
296
+ except SyntaxError:
297
+ return False
298
+
299
+ def _should_accumulate(self, source: str) -> bool:
300
+ """Check if input should be accumulated for future compilations.
301
+
302
+ Accumulates definitions/assignments. Skips inputs containing any
303
+ expression-statement call (`print(x)`, `sys.stderr.write(...)`,
304
+ `f.write(...)`, etc.) -- their side effects shouldn't be replayed.
305
+ """
306
+ try:
307
+ tree = ast.parse(source)
308
+ if not tree.body:
309
+ return False
310
+
311
+ for stmt in tree.body:
312
+ if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
313
+ return False
314
+
315
+ return True
316
+ except SyntaxError:
317
+ return False
318
+
319
+ def _process_input(self, source: str) -> None:
320
+ """Process a single input and optionally accumulate if successful."""
321
+ # Check if this might be an expression that should auto-print
322
+ # (actual type checking happens in _try_compile_and_run)
323
+ maybe_auto_print = self._is_bare_expression(source)
324
+
325
+ success, output = self._try_compile_and_run(source, maybe_auto_print)
326
+
327
+ if success:
328
+ # Only accumulate definitions/assignments, not side-effect statements
329
+ if self._should_accumulate(source):
330
+ self.accumulated_lines.append(source)
331
+ if output:
332
+ print(output, end="")
333
+ else:
334
+ # Show error, don't accumulate
335
+ print(output, end="", file=sys.stderr)
336
+
337
+ def _try_compile_and_run(
338
+ self, new_source: str, maybe_auto_print: bool = False
339
+ ) -> tuple[bool, str]:
340
+ """Attempt to compile accumulated + new source, run it.
341
+
342
+ Returns (success, output) where output is either the program output
343
+ or the error message.
344
+ """
345
+ t_start = time.monotonic()
346
+
347
+ all_lines = self.accumulated_lines + [new_source]
348
+ combined = "\n".join(all_lines)
349
+
350
+ # Ensure main() is always generated
351
+ # (typed variable declarations become globals, not main() body)
352
+ combined += "\n0 # repl-noop"
353
+
354
+ # -v: show accumulated tpy source being compiled
355
+ source_output = ""
356
+ if self.verbose >= 1:
357
+ for line in all_lines:
358
+ for sub in line.split("\n"):
359
+ source_output += f" [tpy] {sub}\n"
360
+
361
+ # Compile via Compiler.from_source (supports multi-module imports)
362
+ try:
363
+ compiler = Compiler.from_source(
364
+ combined, self._module_name, lib_dirs=self.lib_dirs
365
+ )
366
+ compiler.allow_top_level_error_unwrap = True
367
+ compiled_modules = compiler.compile()
368
+ except ParseError as e:
369
+ return False, source_output + f"Parse error: {e}\n"
370
+ except SyntaxError as e:
371
+ return False, source_output + f"Syntax error: {e.msg} at line {e.lineno}\n"
372
+ except SemanticError as e:
373
+ return False, source_output + f"{e.format('repl')}\n"
374
+ except Exception as e:
375
+ return False, source_output + f"Compile error: {e}\n"
376
+
377
+ t_compile = time.monotonic() - t_start
378
+
379
+ # Find the entry module
380
+ entry_module = next(m for m in compiled_modules if m.is_entry_point)
381
+ module = entry_module.ast
382
+ analyzer = entry_module.analyzer
383
+
384
+ # Check if we should auto-print the expression
385
+ # The second-to-last statement is the user's input (last is the noop)
386
+ is_str_or_char = False
387
+ if maybe_auto_print and len(module.top_level_stmts) >= 2:
388
+ user_stmt = module.top_level_stmts[-2]
389
+ if isinstance(user_stmt, TpyExprStmt):
390
+ expr_type = analyzer.get_expr_type(user_stmt.expr)
391
+ # Only wrap if the expression has a non-void type
392
+ if expr_type is not None and not isinstance(expr_type, VoidType):
393
+ # Track if we're printing a string/char for post-processing
394
+ is_str_or_char = is_any_str_type(expr_type) or is_char_type(expr_type)
395
+
396
+ # For @error_return calls, unwrap into a temp first
397
+ # so the error check happens at statement level
398
+ expr = user_stmt.expr
399
+ while isinstance(expr, TpyCoerce):
400
+ expr = expr.expr
401
+ fi = getattr(expr, 'resolved_function_info', None)
402
+ is_error_return = fi is not None and fi.error_return_type is not None
403
+ if is_error_return:
404
+ wrapped_source = f"__repl_val = {new_source.strip()}\nprint(__repl_val)"
405
+ else:
406
+ wrapped_source = f"print({new_source.strip()})"
407
+ combined = "\n".join(self.accumulated_lines + [wrapped_source])
408
+ combined += "\n0 # repl-noop"
409
+ try:
410
+ compiler = Compiler.from_source(
411
+ combined, self._module_name, lib_dirs=self.lib_dirs
412
+ )
413
+ compiler.allow_top_level_error_unwrap = True
414
+ compiled_modules = compiler.compile()
415
+ entry_module = next(m for m in compiled_modules if m.is_entry_point)
416
+ module = entry_module.ast
417
+ analyzer = entry_module.analyzer
418
+ except (ParseError, SyntaxError, SemanticError):
419
+ is_str_or_char = False # Fall back to original if wrapping fails
420
+
421
+ # Show diagnostics and check for errors
422
+ diag_output = ""
423
+ has_errors = False
424
+ for diag in analyzer.diagnostics:
425
+ if diag.level in (DiagnosticLevel.WARNING, DiagnosticLevel.ERROR):
426
+ diag_output += f"{diag.format('repl')}\n"
427
+ if diag.level == DiagnosticLevel.ERROR:
428
+ has_errors = True
429
+ if has_errors:
430
+ return diag_output.rstrip()
431
+
432
+ # Generate code for all modules
433
+ t_codegen_start = time.monotonic()
434
+ all_hpp_paths: list[Path] = []
435
+ all_cpp_paths: list[Path] = []
436
+ all_cpp_code: list[str] = []
437
+ all_hpp_code: list[str] = []
438
+ for mod in compiled_modules:
439
+ hpp_code, cpp_code = compiler.generate_code_to_strings(mod)
440
+
441
+ # Write files matching the include path the codegen emits
442
+ include_path = get_include_path(mod.name)
443
+ if include_path is not None:
444
+ # e.g. "tpystd/tpy.hpp" -> write to {temp}/tpystd/tpy.{hpp,cpp}
445
+ rel = Path(include_path)
446
+ hpp_path = self.temp_dir / rel
447
+ cpp_path = self.temp_dir / rel.with_suffix('.cpp')
448
+ hpp_path.parent.mkdir(parents=True, exist_ok=True)
449
+ else:
450
+ mod_parts = mod.name.split('.')
451
+ if len(mod_parts) > 1:
452
+ hpp_subdir = self.temp_dir / Path(*mod_parts[:-1])
453
+ hpp_subdir.mkdir(parents=True, exist_ok=True)
454
+ hpp_path = hpp_subdir / f"{mod_parts[-1]}.hpp"
455
+ cpp_path = hpp_subdir / f"{mod_parts[-1]}.cpp"
456
+ else:
457
+ hpp_path = self.temp_dir / f"{mod.name}.hpp"
458
+ cpp_path = self.temp_dir / f"{mod.name}.cpp"
459
+
460
+ hpp_path.write_text(hpp_code)
461
+ cpp_path.write_text(cpp_code)
462
+ all_hpp_paths.append(hpp_path)
463
+ all_cpp_paths.append(cpp_path)
464
+ all_hpp_code.append(hpp_code)
465
+ all_cpp_code.append(cpp_code)
466
+
467
+ t_codegen = time.monotonic() - t_codegen_start
468
+
469
+ # -vv: show diff of generated C++ (entry module only)
470
+ verbose_output = ""
471
+ if self.verbose >= 2:
472
+ entry_cpp_code = all_cpp_code[-1]
473
+ current_lines = []
474
+ lines = [line.strip() for line in entry_cpp_code.split("\n") if line.strip()]
475
+ for i, stripped in enumerate(lines):
476
+ if stripped.startswith("//") or stripped.startswith("#"):
477
+ continue
478
+ if stripped in ("return 0;", "0;", "int main() {"):
479
+ continue
480
+ if stripped == "}" and i == len(lines) - 1:
481
+ continue
482
+ current_lines.append(stripped)
483
+
484
+ diff = difflib.unified_diff(self.prev_cpp_lines, current_lines, lineterm="", n=0)
485
+ new_lines = [line[1:] for line in diff if line.startswith("+") and not line.startswith("+++")]
486
+
487
+ if new_lines:
488
+ verbose_output = " [C++] " + "\n [C++] ".join(new_lines) + "\n"
489
+
490
+ self.prev_cpp_lines = current_lines
491
+
492
+ # Execute via backend
493
+ result = self._backend.execute(all_hpp_code, all_cpp_code, all_hpp_paths, all_cpp_paths)
494
+
495
+ if not result.success:
496
+ error_msg = result.stderr or "Unknown error\n"
497
+ return False, source_output + error_msg
498
+
499
+ # Surface anything the program wrote to stderr (sys.stderr.write, ...)
500
+ # to the REPL host's stderr; Python's interactive interpreter does the
501
+ # same. Compiler / runtime errors take the not-success path above.
502
+ if result.stderr:
503
+ sys.stderr.write(result.stderr)
504
+ sys.stderr.flush()
505
+
506
+ # For strings/chars, wrap output in quotes like Python's REPL
507
+ program_output = result.stdout
508
+ if is_str_or_char and program_output:
509
+ program_output = "'" + program_output.rstrip("\n") + "'\n"
510
+
511
+ # -v: show timing
512
+ timing_output = ""
513
+ if self.verbose >= 1:
514
+ t_total = time.monotonic() - t_start
515
+ build_str = _fmt_ms(result.t_build) if not result.build_cached else "cached"
516
+ timing_output = (
517
+ f" [{_fmt_ms(t_total)}] compile: {_fmt_ms(t_compile)} "
518
+ f"build: {build_str} "
519
+ f"run: {_fmt_ms(result.t_run)}\n"
520
+ )
521
+
522
+ return True, source_output + verbose_output + timing_output + diag_output + program_output
523
+
524
+ def cleanup(self) -> None:
525
+ """Remove temp directory and stop backend on exit."""
526
+ if self._backend:
527
+ self._backend.cleanup()
528
+ if self.temp_dir.exists():
529
+ shutil.rmtree(self.temp_dir, ignore_errors=True)