oxython 0.1.0__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 (321) hide show
  1. Cython/Build/BuildExecutable.py +156 -0
  2. Cython/Build/Cache.py +198 -0
  3. Cython/Build/Cythonize.py +349 -0
  4. Cython/Build/Dependencies.py +1320 -0
  5. Cython/Build/Distutils.py +1 -0
  6. Cython/Build/Inline.py +467 -0
  7. Cython/Build/IpythonMagic.py +559 -0
  8. Cython/Build/SharedModule.py +165 -0
  9. Cython/Build/Tests/TestCyCache.py +222 -0
  10. Cython/Build/Tests/TestCythonizeArgsParser.py +480 -0
  11. Cython/Build/Tests/TestDependencies.py +133 -0
  12. Cython/Build/Tests/TestInline.py +177 -0
  13. Cython/Build/Tests/TestIpythonMagic.py +303 -0
  14. Cython/Build/Tests/TestRecythonize.py +212 -0
  15. Cython/Build/Tests/TestStripLiterals.py +155 -0
  16. Cython/Build/Tests/__init__.py +1 -0
  17. Cython/Build/__init__.py +11 -0
  18. Cython/CodeWriter.py +825 -0
  19. Cython/Compiler/AnalysedTreeTransforms.py +97 -0
  20. Cython/Compiler/Annotate.py +328 -0
  21. Cython/Compiler/AutoDocTransforms.py +320 -0
  22. Cython/Compiler/Buffer.py +679 -0
  23. Cython/Compiler/Builtin.py +1102 -0
  24. Cython/Compiler/CmdLine.py +373 -0
  25. Cython/Compiler/Code.pxd +154 -0
  26. Cython/Compiler/Code.py +3760 -0
  27. Cython/Compiler/CodeGeneration.py +33 -0
  28. Cython/Compiler/CythonScope.py +208 -0
  29. Cython/Compiler/Dataclass.py +890 -0
  30. Cython/Compiler/DebugFlags.py +24 -0
  31. Cython/Compiler/Errors.py +310 -0
  32. Cython/Compiler/ExprNodes.py +16319 -0
  33. Cython/Compiler/FlowControl.pxd +112 -0
  34. Cython/Compiler/FlowControl.py +1575 -0
  35. Cython/Compiler/FusedNode.py +978 -0
  36. Cython/Compiler/Future.py +16 -0
  37. Cython/Compiler/Interpreter.py +57 -0
  38. Cython/Compiler/Lexicon.py +442 -0
  39. Cython/Compiler/LineTable.py +114 -0
  40. Cython/Compiler/Main.py +879 -0
  41. Cython/Compiler/MatchCaseNodes.py +2200 -0
  42. Cython/Compiler/MemoryView.py +930 -0
  43. Cython/Compiler/ModuleNode.py +4558 -0
  44. Cython/Compiler/Naming.py +370 -0
  45. Cython/Compiler/Nodes.py +11334 -0
  46. Cython/Compiler/Optimize.py +5563 -0
  47. Cython/Compiler/Options.py +841 -0
  48. Cython/Compiler/ParseTreeTransforms.pxd +80 -0
  49. Cython/Compiler/ParseTreeTransforms.py +4808 -0
  50. Cython/Compiler/Parsing.pxd +9 -0
  51. Cython/Compiler/Parsing.py +5442 -0
  52. Cython/Compiler/Pipeline.py +439 -0
  53. Cython/Compiler/PyrexTypes.py +6588 -0
  54. Cython/Compiler/Pythran.py +232 -0
  55. Cython/Compiler/Scanning.pxd +74 -0
  56. Cython/Compiler/Scanning.py +771 -0
  57. Cython/Compiler/StringEncoding.py +354 -0
  58. Cython/Compiler/Symtab.py +3122 -0
  59. Cython/Compiler/Tests/TestBuffer.py +105 -0
  60. Cython/Compiler/Tests/TestBuiltin.py +196 -0
  61. Cython/Compiler/Tests/TestCmdLine.py +652 -0
  62. Cython/Compiler/Tests/TestCode.py +145 -0
  63. Cython/Compiler/Tests/TestFlowControl.py +65 -0
  64. Cython/Compiler/Tests/TestGrammar.py +202 -0
  65. Cython/Compiler/Tests/TestMemView.py +71 -0
  66. Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
  67. Cython/Compiler/Tests/TestScanning.py +132 -0
  68. Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
  69. Cython/Compiler/Tests/TestStringEncoding.py +20 -0
  70. Cython/Compiler/Tests/TestTreeFragment.py +63 -0
  71. Cython/Compiler/Tests/TestTreePath.py +103 -0
  72. Cython/Compiler/Tests/TestTypes.py +119 -0
  73. Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
  74. Cython/Compiler/Tests/TestVisitor.py +119 -0
  75. Cython/Compiler/Tests/Utils.py +36 -0
  76. Cython/Compiler/Tests/__init__.py +1 -0
  77. Cython/Compiler/TreeFragment.py +279 -0
  78. Cython/Compiler/TreePath.py +303 -0
  79. Cython/Compiler/TypeInference.py +626 -0
  80. Cython/Compiler/TypeSlots.py +1330 -0
  81. Cython/Compiler/UFuncs.py +317 -0
  82. Cython/Compiler/UtilNodes.py +389 -0
  83. Cython/Compiler/UtilityCode.py +354 -0
  84. Cython/Compiler/Version.py +8 -0
  85. Cython/Compiler/Visitor.pxd +52 -0
  86. Cython/Compiler/Visitor.py +914 -0
  87. Cython/Compiler/__init__.py +1 -0
  88. Cython/Coverage.py +448 -0
  89. Cython/Debugger/Cygdb.py +214 -0
  90. Cython/Debugger/DebugWriter.py +82 -0
  91. Cython/Debugger/Tests/TestLibCython.py +280 -0
  92. Cython/Debugger/Tests/__init__.py +1 -0
  93. Cython/Debugger/Tests/cfuncs.c +8 -0
  94. Cython/Debugger/Tests/codefile +49 -0
  95. Cython/Debugger/Tests/test_libcython_in_gdb.py +580 -0
  96. Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
  97. Cython/Debugger/__init__.py +1 -0
  98. Cython/Debugger/libcython.py +1548 -0
  99. Cython/Debugger/libpython.py +2821 -0
  100. Cython/Debugging.py +20 -0
  101. Cython/Distutils/__init__.py +2 -0
  102. Cython/Distutils/build_ext.py +143 -0
  103. Cython/Distutils/extension.py +96 -0
  104. Cython/Distutils/old_build_ext.py +351 -0
  105. Cython/Includes/cpython/__init__.pxd +173 -0
  106. Cython/Includes/cpython/array.pxd +159 -0
  107. Cython/Includes/cpython/bool.pxd +37 -0
  108. Cython/Includes/cpython/buffer.pxd +112 -0
  109. Cython/Includes/cpython/bytearray.pxd +33 -0
  110. Cython/Includes/cpython/bytes.pxd +200 -0
  111. Cython/Includes/cpython/cellobject.pxd +35 -0
  112. Cython/Includes/cpython/ceval.pxd +8 -0
  113. Cython/Includes/cpython/codecs.pxd +121 -0
  114. Cython/Includes/cpython/complex.pxd +60 -0
  115. Cython/Includes/cpython/contextvars.pxd +145 -0
  116. Cython/Includes/cpython/conversion.pxd +36 -0
  117. Cython/Includes/cpython/datetime.pxd +395 -0
  118. Cython/Includes/cpython/descr.pxd +26 -0
  119. Cython/Includes/cpython/dict.pxd +268 -0
  120. Cython/Includes/cpython/exc.pxd +263 -0
  121. Cython/Includes/cpython/fileobject.pxd +57 -0
  122. Cython/Includes/cpython/float.pxd +56 -0
  123. Cython/Includes/cpython/frozendict.pxd +37 -0
  124. Cython/Includes/cpython/function.pxd +65 -0
  125. Cython/Includes/cpython/genobject.pxd +25 -0
  126. Cython/Includes/cpython/getargs.pxd +12 -0
  127. Cython/Includes/cpython/instance.pxd +25 -0
  128. Cython/Includes/cpython/iterator.pxd +36 -0
  129. Cython/Includes/cpython/iterobject.pxd +24 -0
  130. Cython/Includes/cpython/list.pxd +144 -0
  131. Cython/Includes/cpython/long.pxd +180 -0
  132. Cython/Includes/cpython/longintrepr.pxd +14 -0
  133. Cython/Includes/cpython/mapping.pxd +63 -0
  134. Cython/Includes/cpython/marshal.pxd +66 -0
  135. Cython/Includes/cpython/mem.pxd +120 -0
  136. Cython/Includes/cpython/memoryview.pxd +50 -0
  137. Cython/Includes/cpython/method.pxd +49 -0
  138. Cython/Includes/cpython/module.pxd +208 -0
  139. Cython/Includes/cpython/number.pxd +258 -0
  140. Cython/Includes/cpython/object.pxd +430 -0
  141. Cython/Includes/cpython/pycapsule.pxd +143 -0
  142. Cython/Includes/cpython/pylifecycle.pxd +68 -0
  143. Cython/Includes/cpython/pyport.pxd +8 -0
  144. Cython/Includes/cpython/pystate.pxd +95 -0
  145. Cython/Includes/cpython/pythread.pxd +53 -0
  146. Cython/Includes/cpython/ref.pxd +141 -0
  147. Cython/Includes/cpython/sentinel.pxd +17 -0
  148. Cython/Includes/cpython/sequence.pxd +134 -0
  149. Cython/Includes/cpython/set.pxd +119 -0
  150. Cython/Includes/cpython/slice.pxd +70 -0
  151. Cython/Includes/cpython/time.pxd +129 -0
  152. Cython/Includes/cpython/tuple.pxd +72 -0
  153. Cython/Includes/cpython/type.pxd +146 -0
  154. Cython/Includes/cpython/unicode.pxd +639 -0
  155. Cython/Includes/cpython/version.pxd +32 -0
  156. Cython/Includes/cpython/weakref.pxd +78 -0
  157. Cython/Includes/libc/__init__.pxd +1 -0
  158. Cython/Includes/libc/complex.pxd +35 -0
  159. Cython/Includes/libc/errno.pxd +127 -0
  160. Cython/Includes/libc/float.pxd +43 -0
  161. Cython/Includes/libc/limits.pxd +28 -0
  162. Cython/Includes/libc/locale.pxd +46 -0
  163. Cython/Includes/libc/math.pxd +209 -0
  164. Cython/Includes/libc/setjmp.pxd +10 -0
  165. Cython/Includes/libc/signal.pxd +64 -0
  166. Cython/Includes/libc/stdbool.pxd +5 -0
  167. Cython/Includes/libc/stddef.pxd +9 -0
  168. Cython/Includes/libc/stdint.pxd +105 -0
  169. Cython/Includes/libc/stdio.pxd +80 -0
  170. Cython/Includes/libc/stdlib.pxd +72 -0
  171. Cython/Includes/libc/string.pxd +50 -0
  172. Cython/Includes/libc/threads.pxd +234 -0
  173. Cython/Includes/libc/time.pxd +52 -0
  174. Cython/Includes/libcpp/__init__.pxd +4 -0
  175. Cython/Includes/libcpp/algorithm.pxd +320 -0
  176. Cython/Includes/libcpp/any.pxd +16 -0
  177. Cython/Includes/libcpp/atomic.pxd +59 -0
  178. Cython/Includes/libcpp/barrier.pxd +22 -0
  179. Cython/Includes/libcpp/bit.pxd +29 -0
  180. Cython/Includes/libcpp/cast.pxd +12 -0
  181. Cython/Includes/libcpp/cmath.pxd +518 -0
  182. Cython/Includes/libcpp/complex.pxd +106 -0
  183. Cython/Includes/libcpp/condition_variable.pxd +322 -0
  184. Cython/Includes/libcpp/deque.pxd +165 -0
  185. Cython/Includes/libcpp/exception.pxd +216 -0
  186. Cython/Includes/libcpp/execution.pxd +15 -0
  187. Cython/Includes/libcpp/forward_list.pxd +63 -0
  188. Cython/Includes/libcpp/functional.pxd +26 -0
  189. Cython/Includes/libcpp/future.pxd +103 -0
  190. Cython/Includes/libcpp/iterator.pxd +34 -0
  191. Cython/Includes/libcpp/latch.pxd +17 -0
  192. Cython/Includes/libcpp/limits.pxd +61 -0
  193. Cython/Includes/libcpp/list.pxd +117 -0
  194. Cython/Includes/libcpp/map.pxd +252 -0
  195. Cython/Includes/libcpp/memory.pxd +115 -0
  196. Cython/Includes/libcpp/mutex.pxd +387 -0
  197. Cython/Includes/libcpp/numbers.pxd +15 -0
  198. Cython/Includes/libcpp/numeric.pxd +131 -0
  199. Cython/Includes/libcpp/optional.pxd +34 -0
  200. Cython/Includes/libcpp/pair.pxd +1 -0
  201. Cython/Includes/libcpp/queue.pxd +25 -0
  202. Cython/Includes/libcpp/random.pxd +166 -0
  203. Cython/Includes/libcpp/semaphore.pxd +43 -0
  204. Cython/Includes/libcpp/set.pxd +228 -0
  205. Cython/Includes/libcpp/shared_mutex.pxd +96 -0
  206. Cython/Includes/libcpp/span.pxd +87 -0
  207. Cython/Includes/libcpp/stack.pxd +11 -0
  208. Cython/Includes/libcpp/stop_token.pxd +117 -0
  209. Cython/Includes/libcpp/string.pxd +355 -0
  210. Cython/Includes/libcpp/string_view.pxd +183 -0
  211. Cython/Includes/libcpp/typeindex.pxd +15 -0
  212. Cython/Includes/libcpp/typeinfo.pxd +10 -0
  213. Cython/Includes/libcpp/unordered_map.pxd +193 -0
  214. Cython/Includes/libcpp/unordered_set.pxd +152 -0
  215. Cython/Includes/libcpp/utility.pxd +30 -0
  216. Cython/Includes/libcpp/vector.pxd +186 -0
  217. Cython/Includes/numpy/math.pxd +150 -0
  218. Cython/Includes/openmp.pxd +50 -0
  219. Cython/Includes/posix/__init__.pxd +1 -0
  220. Cython/Includes/posix/dlfcn.pxd +14 -0
  221. Cython/Includes/posix/fcntl.pxd +86 -0
  222. Cython/Includes/posix/ioctl.pxd +4 -0
  223. Cython/Includes/posix/mman.pxd +101 -0
  224. Cython/Includes/posix/resource.pxd +57 -0
  225. Cython/Includes/posix/select.pxd +21 -0
  226. Cython/Includes/posix/signal.pxd +73 -0
  227. Cython/Includes/posix/stat.pxd +98 -0
  228. Cython/Includes/posix/stdio.pxd +37 -0
  229. Cython/Includes/posix/stdlib.pxd +29 -0
  230. Cython/Includes/posix/strings.pxd +9 -0
  231. Cython/Includes/posix/time.pxd +71 -0
  232. Cython/Includes/posix/types.pxd +30 -0
  233. Cython/Includes/posix/uio.pxd +26 -0
  234. Cython/Includes/posix/unistd.pxd +271 -0
  235. Cython/Includes/posix/wait.pxd +38 -0
  236. Cython/LZSS.py +184 -0
  237. Cython/Plex/Actions.pxd +24 -0
  238. Cython/Plex/Actions.py +119 -0
  239. Cython/Plex/DFA.pxd +14 -0
  240. Cython/Plex/DFA.py +164 -0
  241. Cython/Plex/Errors.py +48 -0
  242. Cython/Plex/Lexicons.py +178 -0
  243. Cython/Plex/Machines.pxd +36 -0
  244. Cython/Plex/Machines.py +238 -0
  245. Cython/Plex/Regexps.py +535 -0
  246. Cython/Plex/Scanners.pxd +45 -0
  247. Cython/Plex/Scanners.py +328 -0
  248. Cython/Plex/Transitions.pxd +14 -0
  249. Cython/Plex/Transitions.py +239 -0
  250. Cython/Plex/__init__.py +34 -0
  251. Cython/Runtime/__init__.py +1 -0
  252. Cython/Runtime/refnanny.pyx +237 -0
  253. Cython/Shadow.py +1172 -0
  254. Cython/StringIOTree.py +169 -0
  255. Cython/Tempita/__init__.py +4 -0
  256. Cython/Tempita/_looper.py +154 -0
  257. Cython/Tempita/_tempita.py +1087 -0
  258. Cython/TestUtils.py +472 -0
  259. Cython/Tests/TestCodeWriter.py +128 -0
  260. Cython/Tests/TestCythonUtils.py +237 -0
  261. Cython/Tests/TestJediTyper.py +223 -0
  262. Cython/Tests/TestShadow.py +125 -0
  263. Cython/Tests/TestStringIOTree.py +68 -0
  264. Cython/Tests/TestTestUtils.py +89 -0
  265. Cython/Tests/__init__.py +1 -0
  266. Cython/Tests/xmlrunner.py +390 -0
  267. Cython/Utility/AsyncGen.c +1152 -0
  268. Cython/Utility/Buffer.c +866 -0
  269. Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
  270. Cython/Utility/Builtins.c +1068 -0
  271. Cython/Utility/CConvert.pyx +153 -0
  272. Cython/Utility/CMath.c +104 -0
  273. Cython/Utility/CommonStructures.c +244 -0
  274. Cython/Utility/Complex.c +378 -0
  275. Cython/Utility/Coroutine.c +2344 -0
  276. Cython/Utility/CpdefEnums.pyx +119 -0
  277. Cython/Utility/CppConvert.pyx +284 -0
  278. Cython/Utility/CppSupport.cpp +151 -0
  279. Cython/Utility/CythonFunction.c +2143 -0
  280. Cython/Utility/Dataclasses.c +101 -0
  281. Cython/Utility/Embed.c +129 -0
  282. Cython/Utility/Exceptions.c +1331 -0
  283. Cython/Utility/Exceptions_Cy.pyx +109 -0
  284. Cython/Utility/ExtensionTypes.c +1199 -0
  285. Cython/Utility/FunctionArguments.c +1052 -0
  286. Cython/Utility/FusedFunction.pyx +44 -0
  287. Cython/Utility/ImportExport.c +972 -0
  288. Cython/Utility/MatchCase.c +985 -0
  289. Cython/Utility/MatchCase_Cy.pyx +12 -0
  290. Cython/Utility/MemoryView.pxd +108 -0
  291. Cython/Utility/MemoryView.pyx +1499 -0
  292. Cython/Utility/MemoryView_C.c +1056 -0
  293. Cython/Utility/ModuleSetupCode.c +3349 -0
  294. Cython/Utility/NumpyImportArray.c +46 -0
  295. Cython/Utility/ObjectHandling.c +3404 -0
  296. Cython/Utility/Optimize.c +2575 -0
  297. Cython/Utility/Overflow.c +378 -0
  298. Cython/Utility/Profile.c +736 -0
  299. Cython/Utility/StringTools.c +1534 -0
  300. Cython/Utility/Synchronization.c +438 -0
  301. Cython/Utility/TString.c +369 -0
  302. Cython/Utility/TestCyUtilityLoader.pyx +8 -0
  303. Cython/Utility/TestCythonScope.pyx +75 -0
  304. Cython/Utility/TestUtilityLoader.c +12 -0
  305. Cython/Utility/TypeConversion.c +1588 -0
  306. Cython/Utility/UFuncs.pyx +50 -0
  307. Cython/Utility/UFuncs_C.c +89 -0
  308. Cython/Utility/__init__.py +28 -0
  309. Cython/Utility/arrayarray.h +190 -0
  310. Cython/Utils.py +682 -0
  311. Cython/__init__.py +12 -0
  312. Cython/py.typed +0 -0
  313. cython.py +29 -0
  314. oxython-0.1.0.dist-info/METADATA +138 -0
  315. oxython-0.1.0.dist-info/RECORD +321 -0
  316. oxython-0.1.0.dist-info/WHEEL +5 -0
  317. oxython-0.1.0.dist-info/entry_points.txt +4 -0
  318. oxython-0.1.0.dist-info/top_level.txt +3 -0
  319. pyximport/__init__.py +4 -0
  320. pyximport/pyxbuild.py +160 -0
  321. pyximport/pyximport.py +482 -0
@@ -0,0 +1,2200 @@
1
+ # Nodes for structural pattern matching.
2
+ #
3
+ # In a separate file because they're unlikely to be useful for much else.
4
+
5
+ import enum
6
+
7
+ from .Nodes import Node, StatNode, ErrorNode
8
+ from .Errors import error, local_errors, report_error
9
+ from . import Nodes, ExprNodes, PyrexTypes, Builtin
10
+ from .Code import UtilityCode, TempitaUtilityCode
11
+ from .Options import copy_inherited_directives
12
+
13
+
14
+ class MatchNode(StatNode):
15
+ """
16
+ subject ExprNode The expression to be matched
17
+ cases [MatchCaseBaseNode] list of cases
18
+
19
+ sequence_mapping_temp None or AssignableTempNode an int temp to store result of sequence/mapping tests
20
+ sequence_mapping_temp is an optimization because determining whether something is a sequence or mapping
21
+ is slow on Python <3.10, the Limited API (in principle, although in practice we're able to retrieve
22
+ the flags at runtime), and PyPy. It should be deleted once no longer required.
23
+ """
24
+
25
+ child_attrs = ["subject", "cases"]
26
+
27
+ sequence_mapping_temp = None
28
+
29
+ def validate_irrefutable(self):
30
+ found_irrefutable_case = None
31
+ for case in self.cases:
32
+ if isinstance(case, ErrorNode):
33
+ # This validation happens before error nodes have been
34
+ # transformed into actual errors, so we need to ignore them
35
+ continue
36
+ if found_irrefutable_case:
37
+ error(
38
+ found_irrefutable_case.pos,
39
+ f"{found_irrefutable_case.pattern.irrefutable_message()} makes remaining patterns unreachable"
40
+ )
41
+ break
42
+ if case.is_irrefutable():
43
+ found_irrefutable_case = case
44
+ case.validate_irrefutable()
45
+
46
+ def refactor_cases(self):
47
+ # An early transform - changes cases that can be represented as
48
+ # a simple if/else statement into them (giving them maximum chance
49
+ # to be optimized by the existing mechanisms). Leaves other cases
50
+ # unchanged
51
+ from .ExprNodes import CloneNode, ProxyNode, NameNode
52
+
53
+ self.subject = ProxyNode(self.subject)
54
+ subject = self.subject_clonenode = CloneNode(self.subject)
55
+ current_if_statement = None
56
+ new_cases = []
57
+ for c in self.cases:
58
+ if c is not None and c.is_simple_value_comparison():
59
+ body = SubstitutedIfStatListNode(
60
+ c.body.pos,
61
+ stats=c.body.stats,
62
+ match_node=self
63
+ )
64
+ if_clause = Nodes.IfClauseNode(
65
+ c.pos,
66
+ condition=c.pattern.get_simple_comparison_node(subject),
67
+ body=body,
68
+ )
69
+ # Passing None for env is safe only because we know it's a simple value comparison.
70
+ assignments = c.pattern.create_target_assignments(subject, env=None)
71
+ if assignments:
72
+ if_clause.body.stats.insert(0, assignments)
73
+ if current_if_statement is None:
74
+ current_if_statement = Nodes.IfStatNode(
75
+ c.pos, if_clauses=[], else_clause=None
76
+ )
77
+ current_if_statement.if_clauses.append(if_clause)
78
+ else:
79
+ if current_if_statement:
80
+ # this cannot be simplified, but previous case(s) were
81
+ new_cases.append(SubstitutedMatchCaseNode(
82
+ current_if_statement.pos, body=current_if_statement
83
+ ))
84
+ current_if_statement = None
85
+ if c is not None:
86
+ new_cases.append(c)
87
+ if current_if_statement:
88
+ # this cannot be simplified, but previous case(s) were
89
+ new_cases.append(SubstitutedMatchCaseNode(
90
+ current_if_statement.pos, body=current_if_statement
91
+ ))
92
+ self.cases = new_cases
93
+
94
+ def analyse_declarations(self, env):
95
+ self.subject.analyse_declarations(env)
96
+ for c in self.cases:
97
+ c.analyse_case_declarations(self.subject_clonenode, env)
98
+
99
+ def analyse_expressions(self, env):
100
+ sequence_mapping_count = 0
101
+ for c in self.cases:
102
+ if c.is_sequence_or_mapping():
103
+ sequence_mapping_count += 1
104
+ if sequence_mapping_count >= 2:
105
+ self.sequence_mapping_temp = AssignableTempNode(
106
+ self.pos, PyrexTypes.c_uint_type,
107
+ is_addressable=True
108
+ )
109
+
110
+ self.subject = self.subject.analyse_expressions(env)
111
+ assert isinstance(self.subject, ExprNodes.ProxyNode)
112
+ if not self.subject.arg.is_literal:
113
+ self.subject.arg = self.subject.arg.coerce_to_temp(env)
114
+ subject = self.subject_clonenode.analyse_expressions(env)
115
+ analysed_cases = []
116
+ for c in self.cases:
117
+ c = c.analyse_case_expressions(subject, env, self.sequence_mapping_temp)
118
+ if c is not None:
119
+ analysed_cases.append(c)
120
+ self.cases = analysed_cases
121
+ return self
122
+
123
+ def generate_execution_code(self, code):
124
+ if self.sequence_mapping_temp:
125
+ self.sequence_mapping_temp.allocate(code)
126
+ code.putln(f"{self.sequence_mapping_temp.result()} = 0; /* sequence/mapping test temp */")
127
+ # For things that are a sequence at compile-time it's difficult
128
+ # to avoid generating the sequence mapping temp. Therefore, silence
129
+ # an "unused error".
130
+ code.putln(f"(void){self.sequence_mapping_temp.result()};")
131
+ end_label = self.end_label = code.new_label()
132
+ self.subject.generate_evaluation_code(code)
133
+ for c in self.cases:
134
+ c.generate_execution_code(code, end_label)
135
+ if self.sequence_mapping_temp:
136
+ self.sequence_mapping_temp.release(code)
137
+ if code.label_used(end_label):
138
+ code.put_label(end_label)
139
+ self.subject.generate_disposal_code(code)
140
+ self.subject.free_temps(code)
141
+
142
+
143
+ class MatchCaseBaseNode(Node):
144
+ """
145
+ Common base for a MatchCaseNode and a
146
+ substituted node
147
+ """
148
+
149
+ pass
150
+
151
+
152
+ class MatchCaseNode(Node):
153
+ """
154
+ pattern PatternNode
155
+ body StatListNode
156
+ guard ExprNode or None
157
+
158
+ generated:
159
+ target_assignments [ SingleAssignmentNodes ]
160
+ comp_node ExprNode that evaluates to bool
161
+ """
162
+
163
+ target_assignments = None
164
+ comp_node = None
165
+ child_attrs = ["pattern", "target_assignments", "comp_node", "guard", "body"]
166
+
167
+ def is_irrefutable(self):
168
+ if isinstance(self.pattern, ErrorNode):
169
+ return True # value doesn't really matter
170
+ return self.pattern.is_irrefutable() and not self.guard
171
+
172
+ def is_simple_value_comparison(self):
173
+ if self.guard:
174
+ return False
175
+ return self.pattern.is_simple_value_comparison()
176
+
177
+ def validate_targets(self):
178
+ if isinstance(self.pattern, ErrorNode):
179
+ return
180
+ self.pattern.get_targets()
181
+
182
+ def validate_irrefutable(self):
183
+ if isinstance(self.pattern, ErrorNode):
184
+ return
185
+ self.pattern.validate_irrefutable()
186
+
187
+ def is_sequence_or_mapping(self):
188
+ return self.pattern.is_sequence_or_mapping()
189
+
190
+ def analyse_case_declarations(self, subject_node, env):
191
+ self.pattern.analyse_declarations(env)
192
+ self.target_assignments = self.pattern.create_target_assignments(subject_node, env)
193
+ if self.target_assignments:
194
+ self.target_assignments.analyse_declarations(env)
195
+ if self.guard:
196
+ self.guard.analyse_declarations(env)
197
+ self.body.analyse_declarations(env)
198
+
199
+ def analyse_case_expressions(self, subject_node, env, sequence_mapping_temp):
200
+ with local_errors(True) as errors:
201
+ self.pattern = self.pattern.analyse_pattern_expressions(env, sequence_mapping_temp)
202
+ self.comp_node = self.pattern.get_comparison_node(subject_node, sequence_mapping_temp)
203
+ self.comp_node = self.comp_node.analyse_types(env)
204
+
205
+ if self.comp_node and isinstance(self.comp_node, ExprNodes.BoolNode):
206
+ if not self.comp_node.value:
207
+ # We know this pattern can't succeed. Ignore any errors and return None.
208
+ return None
209
+ for error in errors:
210
+ report_error(error)
211
+
212
+ # coerce_to_boolean.coerce_to_simple is taken from analyse_temp_boolean_expression
213
+ # and ensures that self.comp_node.generate_disposal_code is trivial and so
214
+ # it doesn't matter if it's skipped in one branch. IfClauseNode relies on the same mechanism.
215
+ self.comp_node = self.comp_node.coerce_to_boolean(env).coerce_to_simple(env)
216
+
217
+ if self.target_assignments:
218
+ self.target_assignments = self.target_assignments.analyse_expressions(env)
219
+ if self.guard:
220
+ # analyse_temp_boolean_expression ensures that self.guard.generate_disposal_code is trivial
221
+ # and so it doesn't matter if it's skipped in one branch.
222
+ self.guard = self.guard.analyse_temp_boolean_expression(env)
223
+ self.body = self.body.analyse_expressions(env)
224
+ return self
225
+
226
+ def generate_execution_code(self, code, end_label):
227
+ self.pattern.allocate_subject_temps(code)
228
+ self.comp_node.generate_evaluation_code(code)
229
+
230
+ end_of_case_label = code.new_label()
231
+
232
+ code.putln(f"if (!{self.comp_node.result()}) ""{ /* !pattern */")
233
+ self.pattern.dispose_of_subject_temps(code) # failed, don't need the subjects
234
+ code.put_goto(end_of_case_label)
235
+
236
+ code.putln("} else { /* pattern */")
237
+ self.comp_node.generate_disposal_code(code)
238
+ self.comp_node.free_temps(code)
239
+ if self.target_assignments:
240
+ self.target_assignments.generate_execution_code(code)
241
+ self.pattern.dispose_of_subject_temps(code)
242
+ self.pattern.release_subject_temps(code) # we're done with the subjects here
243
+ if self.guard:
244
+ self.guard.generate_evaluation_code(code)
245
+ code.putln("if (%s) { /* guard */" % self.guard.result())
246
+ self.guard.generate_disposal_code(code)
247
+ self.guard.free_temps(code)
248
+ self.body.generate_execution_code(code)
249
+ if not self.body.is_terminator:
250
+ code.put_goto(end_label)
251
+ if self.guard:
252
+ code.putln("} /* guard */")
253
+ code.putln("} /* pattern */")
254
+ code.put_label(end_of_case_label)
255
+
256
+
257
+ class SubstitutedMatchCaseNode(MatchCaseBaseNode):
258
+ # body - Node - The (probably) if statement that it's replaced with
259
+ child_attrs = ["body"]
260
+
261
+ def is_sequence_or_mapping(self):
262
+ return False
263
+
264
+ def analyse_case_declarations(self, subject_node, env):
265
+ self.analyse_declarations(env)
266
+
267
+ def analyse_declarations(self, env):
268
+ self.body.analyse_declarations(env)
269
+
270
+ def analyse_case_expressions(self, subject_node, env, sequence_mapping_temp):
271
+ self.body = self.body.analyse_expressions(env)
272
+ return self
273
+
274
+ def generate_execution_code(self, code, end_label):
275
+ self.body.generate_execution_code(code)
276
+
277
+
278
+ class PatternNode(Node):
279
+ """
280
+ PatternNode is not an expression because
281
+ it does several things (evaluating a boolean expression,
282
+ assignment of targets), and they need to be done at different
283
+ times.
284
+
285
+ as_targets [NameNode] any target assign by "as"
286
+
287
+ Generated in analysis:
288
+ comp_node ExprNode node to evaluate for the pattern
289
+
290
+ ----------------------------------------
291
+ How these nodes are processed:
292
+ 1. During "analyse_declarations", "PatternNode.create_target_assignments()"
293
+ is called on the main "PatternNode" of the case. This calls its
294
+ sub-patterns ".create_target_assignments()" recursively.
295
+ This creates a "StatListNode" that is held by the "MatchCaseNode".
296
+ 2. In the "analyse_expressions" phases, the "MatchCaseNode" calls
297
+ "PatternNode.analyse_pattern_expressions", which calls its sub-pattern recursively.
298
+ 3. At the end of the "analyse_expressions" stage, the "MatchCaseNode"
299
+ class "PatternNode.get_comparison_node()" (which calls
300
+ "PatternNode.get_comparison_node()" for its sub-patterns).
301
+ This returns an ExprNode which can be evaluated to determine if the
302
+ pattern has matched.
303
+ While generating the comparison we try quite hard not to
304
+ analyse it until right at the end, because otherwise it'll lead
305
+ to a lot of repeated work for deeply nested patterns.
306
+ 4. In the code generation stage, "PatternNodes" hardly generate any
307
+ code themselves. However, they do set up whatever temps they need
308
+ (mainly for sub-pattern subjects), with "allocate_subject_temps",
309
+ "release_subject_temps", and "dispose_of_subject_temps" (which
310
+ they also call recursively on their sub-patterns).
311
+ """
312
+
313
+ # useful for type tests
314
+ is_match_value_pattern = False
315
+ is_match_and_assign_pattern = False
316
+
317
+ child_attrs = ["as_targets"]
318
+
319
+ def __init__(self, pos, **kwds):
320
+ if "as_targets" not in kwds:
321
+ kwds["as_targets"] = []
322
+ super(PatternNode, self).__init__(pos, **kwds)
323
+
324
+ def is_irrefutable(self):
325
+ return False
326
+
327
+ def is_sequence_or_mapping(self):
328
+ """
329
+ Used for determining whether to allocate a sequence_mapping_temp.
330
+
331
+ An OrPattern containing at least one also returns True
332
+ """
333
+ return False
334
+
335
+ def get_targets(self):
336
+ targets = self.get_main_pattern_targets()
337
+ for target in self.as_targets:
338
+ self.add_target_to_targets(targets, target.name)
339
+ return targets
340
+
341
+ def update_targets_with_targets(self, targets, other_targets):
342
+ for name in targets.intersection(other_targets):
343
+ error(self.pos, f"multiple assignments to name '{name}' in pattern")
344
+ targets.update(other_targets)
345
+
346
+ def add_target_to_targets(self, targets, target):
347
+ if target in targets:
348
+ error(self.pos, f"multiple assignments to name '{target}' in pattern")
349
+ targets.add(target)
350
+
351
+ def get_main_pattern_targets(self):
352
+ # exclude "as" target
353
+ raise NotImplementedError
354
+
355
+ def is_simple_value_comparison(self):
356
+ # Can this be converted to an "if ... elif: ..." statement?
357
+ # Only worth doing to take advantage of things like SwitchTransform
358
+ # so there's little benefit on doing it too widely
359
+ return False
360
+
361
+ def get_simple_comparison_node(self):
362
+ """
363
+ Returns an ExprNode that can be used as the case in an if-statement
364
+
365
+ Should only be called if is_simple_value_comparison() is True
366
+ """
367
+ raise NotImplementedError
368
+
369
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
370
+ error(self.pos, f"This type of pattern is not currently supported: {self}")
371
+ raise NotImplementedError
372
+
373
+ def validate_irrefutable(self):
374
+ for attr in self.child_attrs:
375
+ child = getattr(self, attr)
376
+ if child is not None and isinstance(child, PatternNode):
377
+ child.validate_irrefutable()
378
+
379
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
380
+ error(self.pos, f"This type of pattern is not currently supported {self}")
381
+ raise NotImplementedError
382
+
383
+ def generate_result_code(self, code):
384
+ pass
385
+
386
+ def create_target_assignments(self, subject_node, env):
387
+ # Generates the assignment code needed to initialize all the targets.
388
+ # Returns either a StatListNode or None.
389
+ #
390
+ # env may be None only if self.is_simple_value_comparison().
391
+ # In which case there is no main_pattern_assignment_list.
392
+ assignments = []
393
+ for target in self.as_targets:
394
+ if (
395
+ self.is_match_value_pattern and
396
+ self.value and
397
+ self.value.is_simple()
398
+ ):
399
+ # in this case we can optimize slightly and just take the value
400
+ subject_node = self.value.clone_node()
401
+ assignments.append(
402
+ Nodes.SingleAssignmentNode(
403
+ target.pos, lhs=target.clone_node(), rhs=subject_node
404
+ )
405
+ )
406
+ assert env or self.is_simple_value_comparison()
407
+ assignments.extend(self.create_main_pattern_assignment_list(subject_node, env))
408
+ if assignments:
409
+ return Nodes.StatListNode(self.pos, stats=assignments)
410
+ else:
411
+ return None
412
+
413
+ def create_main_pattern_assignment_list(self, subject_node, env):
414
+ # Generates assignments for everything except the "as_targets".
415
+ # Override in subclasses.
416
+ # Returns a list of Nodes.
417
+ return []
418
+
419
+ def allocate_subject_temps(self, code):
420
+ pass # Implement in nodes that need it
421
+
422
+ def release_subject_temps(self, code):
423
+ pass # Implement in nodes that need it
424
+
425
+ def dispose_of_subject_temps(self, code):
426
+ pass # Implement in nodes that need it
427
+
428
+
429
+ class MatchValuePatternNode(PatternNode):
430
+ """
431
+ value ExprNode
432
+ is_is_check bool Picks "is" or equality check
433
+ """
434
+
435
+ is_match_value_pattern = True
436
+
437
+ child_attrs = PatternNode.child_attrs + ["value"]
438
+
439
+ is_is_check = False
440
+
441
+ def get_main_pattern_targets(self):
442
+ return set()
443
+
444
+ def is_simple_value_comparison(self):
445
+ return True
446
+
447
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
448
+ # for this node the comparison and "simple" comparison are the same
449
+ return LazyCoerceToBool(self.pos,
450
+ arg=self.get_simple_comparison_node(subject_node)
451
+ )
452
+
453
+ def get_simple_comparison_node(self, subject_node):
454
+ op = "is" if self.is_is_check else "=="
455
+ return MatchValuePrimaryCmpNode(
456
+ self.pos, operator=op, operand1=subject_node, operand2=self.value
457
+ )
458
+
459
+ def analyse_declarations(self, env):
460
+ super(MatchValuePatternNode, self).analyse_declarations(env)
461
+ if self.value:
462
+ self.value.analyse_declarations(env)
463
+
464
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
465
+ if self.value:
466
+ self.value = self.value.analyse_expressions(env)
467
+ return self
468
+
469
+
470
+ class MatchAndAssignPatternNode(PatternNode):
471
+ """
472
+ target NameNode or None the target to assign to (None = wildcard)
473
+ is_star bool
474
+ """
475
+
476
+ target = None
477
+ is_star = False
478
+ is_match_and_assign_pattern = True
479
+
480
+ child_attrs = PatternNode.child_attrs + ["target"]
481
+
482
+ def is_irrefutable(self):
483
+ return True
484
+
485
+ def irrefutable_message(self):
486
+ if self.target:
487
+ return f"name capture '{self.target.name}'"
488
+ else:
489
+ return "wildcard"
490
+
491
+ def get_main_pattern_targets(self):
492
+ if self.target:
493
+ return {self.target.name}
494
+ else:
495
+ return set()
496
+
497
+ def is_simple_value_comparison(self):
498
+ return self.is_irrefutable() # the comparison is to "True"
499
+
500
+ def get_simple_comparison_node(self, subject_node):
501
+ assert self.is_simple_value_comparison()
502
+ return self.get_comparison_node(subject_node)
503
+
504
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
505
+ return ExprNodes.BoolNode(self.pos, value=True)
506
+
507
+ def create_main_pattern_assignment_list(self, subject_node, env):
508
+ if not self.target:
509
+ return []
510
+ return [Nodes.SingleAssignmentNode(self.pos, lhs=self.target.clone_node(), rhs=subject_node)]
511
+
512
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
513
+ return self # nothing to analyse
514
+
515
+
516
+ class OrPatternNode(PatternNode):
517
+ """
518
+ alternatives list of PatternNodes
519
+
520
+ generated:
521
+ which_alternative_temp - an integer temp node. 0 for failed; 1, 2...
522
+ identify the alternative that succeeded
523
+ """
524
+ which_alternative_temp = None
525
+ sequence_mapping_temp = None # used in a similar way to MatchCaseNode,
526
+ # to avoid recalculating if we're a sequence or mapping.
527
+
528
+ child_attrs = PatternNode.child_attrs + ["alternatives"]
529
+
530
+ def get_first_irrefutable(self):
531
+ for alternative in self.alternatives:
532
+ if alternative.is_irrefutable():
533
+ return alternative
534
+ return None
535
+
536
+ def is_irrefutable(self):
537
+ return self.get_first_irrefutable() is not None
538
+
539
+ def irrefutable_message(self):
540
+ return self.get_first_irrefutable().irrefutable_message()
541
+
542
+ def is_sequence_or_mapping(self):
543
+ # This affects if the caller generates a temp for it. If so then
544
+ # this node can forward the temp to the relevant alternative.
545
+ for a in self.alternatives:
546
+ if a.is_sequence_or_mapping():
547
+ return True
548
+ return False
549
+
550
+ def get_main_pattern_targets(self):
551
+ child_targets = None
552
+ for alternative in self.alternatives:
553
+ alternative_targets = alternative.get_targets()
554
+ if child_targets is not None and child_targets != alternative_targets:
555
+ error(self.pos, "alternative patterns bind different names")
556
+ child_targets = alternative_targets
557
+ return child_targets
558
+
559
+ def validate_irrefutable(self):
560
+ super(OrPatternNode, self).validate_irrefutable()
561
+ found_irrefutable_case = None
562
+ for alternative in self.alternatives:
563
+ if found_irrefutable_case:
564
+ error(
565
+ found_irrefutable_case.pos,
566
+ f"{found_irrefutable_case.irrefutable_message()} makes remaining patterns unreachable"
567
+ )
568
+ break
569
+ if alternative.is_irrefutable():
570
+ found_irrefutable_case = alternative
571
+ alternative.validate_irrefutable()
572
+
573
+ def is_simple_value_comparison(self):
574
+ # it turns out to be hard to generate correct assignment code
575
+ # for or patterns with targets
576
+ return self.is_really_simple_value_comparison()
577
+
578
+ def is_really_simple_value_comparison(self):
579
+ # like is_simple_value_comparison but also doesn't have any targets
580
+ return all(
581
+ a.is_simple_value_comparison() and not a.get_targets()
582
+ for a in self.alternatives
583
+ )
584
+
585
+ def get_simple_comparison_node(self, subject_node):
586
+ assert self.is_simple_value_comparison()
587
+ assert len(self.alternatives) >= 2, self.alternatives
588
+ checks = []
589
+ for a in self.alternatives:
590
+ check = a.get_simple_comparison_node(subject_node)
591
+ checks.append(check)
592
+ if isinstance(check, ExprNodes.BoolNode) and check.value:
593
+ break # no point in going further
594
+ return generate_binop_tree_from_list(self.pos, "or", checks)
595
+
596
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
597
+ if self.is_really_simple_value_comparison():
598
+ return self.get_simple_comparison_node(subject_node)
599
+
600
+ cond_exprs = []
601
+ for n, a in enumerate(self.alternatives, start=1):
602
+ a_test = a.get_comparison_node(subject_node, sequence_mapping_temp)
603
+ a_value = ExprNodes.IntNode.for_size(a.pos, n)
604
+ if isinstance(a_test, ExprNodes.BoolNode):
605
+ if a_test.value:
606
+ cond_exprs.append(a_value)
607
+ break
608
+ else:
609
+ cond_exprs.append(ExprNodes.IntNode.for_size(self.pos, 0))
610
+ else:
611
+ cond_exprs.append(
612
+ ExprNodes.CondExprNode(
613
+ self.pos,
614
+ condition = a_test,
615
+ true_val = a_value,
616
+ false_val = ExprNodes.IntNode.for_size(self.pos, 0)
617
+ )
618
+ )
619
+
620
+ expr = generate_binop_tree_from_list(self.pos, "or", cond_exprs)
621
+
622
+ if self.which_alternative_temp:
623
+ expr = ExprNodes.AssignmentExpressionNode(
624
+ self.pos,
625
+ lhs = self.which_alternative_temp,
626
+ rhs = expr
627
+ )
628
+ return LazyCoerceToBool(expr.pos, arg=expr)
629
+
630
+ def analyse_declarations(self, env):
631
+ super(OrPatternNode, self).analyse_declarations(env)
632
+ for a in self.alternatives:
633
+ a.analyse_declarations(env)
634
+
635
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
636
+ self.alternatives = [
637
+ a.analyse_pattern_expressions(env, sequence_mapping_temp)
638
+ for a in self.alternatives
639
+ ]
640
+ if not sequence_mapping_temp:
641
+ sequence_mapping_condition_usage = sum(1 for a in self.alternatives if a.is_sequence_or_mapping())
642
+ if sequence_mapping_condition_usage >= 2:
643
+ self.sequence_mapping_temp = AssignableTempNode(
644
+ self.pos, PyrexTypes.c_uint_type,
645
+ is_addressable=True
646
+ )
647
+ return self
648
+
649
+ def create_main_pattern_assignment_list(self, subject_node, env):
650
+ ifclauses = []
651
+ for n, a in enumerate(self.alternatives, start=1):
652
+ a_assignment = a.create_target_assignments(subject_node, env)
653
+ if a_assignment:
654
+ if not self.which_alternative_temp:
655
+ self.which_alternative_temp = AssignableTempNode(self.pos, PyrexTypes.c_int_type)
656
+ # Switch code paths depending on which node gets assigned
657
+ ifclause = Nodes.IfClauseNode(
658
+ a.pos,
659
+ condition=ExprNodes.PrimaryCmpNode(
660
+ a.pos,
661
+ operator="==",
662
+ operand1=self.which_alternative_temp,
663
+ operand2=ExprNodes.IntNode.for_size(a.pos, n)
664
+ ),
665
+ body = a_assignment
666
+ )
667
+ ifclauses.append(ifclause)
668
+ if ifclauses:
669
+ return [
670
+ Nodes.IfStatNode(
671
+ self.pos,
672
+ if_clauses=ifclauses,
673
+ else_clause=None
674
+ )
675
+ ]
676
+ return []
677
+
678
+ def allocate_subject_temps(self, code):
679
+ if self.sequence_mapping_temp:
680
+ self.sequence_mapping_temp.allocate(code)
681
+ tempvar = self.sequence_mapping_temp.result()
682
+ code.putln(f"{tempvar} = 0; /* sequence/mapping test temp */")
683
+ # For things that are a sequence at compile-time it's difficult
684
+ # to avoid generating the sequence mapping temp. Therefore, silence
685
+ # an "unused error".
686
+ code.putln(f"(void){tempvar};")
687
+ if self.which_alternative_temp:
688
+ self.which_alternative_temp.allocate(code)
689
+ for a in self.alternatives:
690
+ a.allocate_subject_temps(code)
691
+
692
+ def release_subject_temps(self, code):
693
+ if self.sequence_mapping_temp:
694
+ self.sequence_mapping_temp.release(code)
695
+ if self.which_alternative_temp:
696
+ self.which_alternative_temp.release(code)
697
+ for a in self.alternatives:
698
+ a.release_subject_temps(code)
699
+
700
+ def dispose_of_subject_temps(self, code):
701
+ if self.which_alternative_temp:
702
+ self.which_alternative_temp.generate_disposal_code(code)
703
+ if self.sequence_mapping_temp:
704
+ self.sequence_mapping_temp.generate_disposal_code(code)
705
+ for a in self.alternatives:
706
+ a.dispose_of_subject_temps(code)
707
+
708
+
709
+ class MatchSequencePatternNode(PatternNode):
710
+ """
711
+ patterns list of PatternNodes
712
+
713
+ generated:
714
+ subjects [TrackTypeTempNode] individual subsubjects can be assigned to these
715
+ """
716
+
717
+ subjects = None
718
+ needs_length_temp = False
719
+
720
+ child_attrs = PatternNode.child_attrs + ["patterns"]
721
+
722
+ Pyx_sequence_check_type = PyrexTypes.CFuncType(
723
+ PyrexTypes.c_bint_type,
724
+ [
725
+ PyrexTypes.CFuncTypeArg("o", PyrexTypes.py_object_type, None),
726
+ PyrexTypes.CFuncTypeArg(
727
+ "sequence_mapping_temp",
728
+ PyrexTypes.c_ptr_type(PyrexTypes.c_uint_type),
729
+ None,
730
+ ),
731
+ ],
732
+ exception_value="-1",
733
+ )
734
+
735
+ def is_sequence_or_mapping(self):
736
+ return True
737
+
738
+ def __init__(self, pos, **kwds):
739
+ super(MatchSequencePatternNode, self).__init__(pos, **kwds)
740
+ self.length_temp = AssignableTempNode(self.pos, PyrexTypes.c_py_ssize_t_type)
741
+
742
+ def get_main_pattern_targets(self):
743
+ targets = set()
744
+ star_count = 0
745
+ for pattern in self.patterns:
746
+ if pattern.is_match_and_assign_pattern and pattern.is_star:
747
+ star_count += 1
748
+ self.update_targets_with_targets(targets, pattern.get_targets())
749
+ if star_count > 1:
750
+ error(self.pos, "multiple starred names in sequence pattern")
751
+ return targets
752
+
753
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
754
+ from .UtilNodes import TempResultFromStatNode, ResultRefNode
755
+
756
+ test = None
757
+ assert getattr(self, "subject_temps", None) is not None
758
+
759
+ seq_test = self.make_sequence_check(subject_node, sequence_mapping_temp)
760
+ if isinstance(seq_test, ExprNodes.BoolNode) and not seq_test.value:
761
+ return seq_test # no point in proceeding further!
762
+
763
+ has_star = False
764
+ all_tests = [seq_test]
765
+ pattern_tests = []
766
+ for n, pattern in enumerate(self.patterns):
767
+ if isinstance(pattern, MatchAndAssignPatternNode) and pattern.is_star:
768
+ has_star = True
769
+ self.needs_length_temp = True
770
+
771
+ if self.subject_temps[n] is None:
772
+ # The subject has been identified as unneeded, so don't evaluate it
773
+ continue
774
+ p_test = pattern.get_comparison_node(self.subject_temps[n])
775
+
776
+ result_ref = ResultRefNode(pos=self.pos, type=PyrexTypes.c_bint_type)
777
+ subject_assignment = Nodes.SingleAssignmentNode(
778
+ self.pos,
779
+ lhs=self.subject_temps[n], # the temp node
780
+ rhs=self.subjects[n], # the regular node
781
+ )
782
+ test_assignment = Nodes.SingleAssignmentNode(
783
+ self.pos, lhs=result_ref, rhs=p_test
784
+ )
785
+ stats = Nodes.StatListNode(
786
+ self.pos, stats=[subject_assignment, test_assignment]
787
+ )
788
+ pattern_tests.append(TempResultFromStatNode(result_ref, stats))
789
+
790
+ min_length = len(self.patterns)
791
+ if has_star:
792
+ min_length -= 1
793
+ # check whether we need a length call...
794
+ if not (self.patterns and len(self.patterns) == 1 and has_star):
795
+ length_call = self.make_length_call_node(subject_node)
796
+
797
+ if length_call.is_literal and (
798
+ (has_star and min_length < length_call.constant_result)
799
+ or (not has_star and min_length != length_call.constant_result)
800
+ ):
801
+ # definitely failed!
802
+ return ExprNodes.BoolNode(self.pos, value=False)
803
+ seq_len_test = ExprNodes.PrimaryCmpNode(
804
+ self.pos,
805
+ operator=">=" if has_star else "==",
806
+ operand1=length_call,
807
+ operand2=ExprNodes.IntNode.for_size(self.pos, min_length),
808
+ )
809
+ all_tests.append(seq_len_test)
810
+ else:
811
+ self.needs_length_temp = False
812
+ all_tests.extend(pattern_tests)
813
+ test = generate_binop_tree_from_list(self.pos, "and", all_tests)
814
+ return LazyCoerceToBool(test.pos, arg=test)
815
+
816
+ def generate_subjects(self, subject_node, env):
817
+ assert self.subjects is None # not called twice
818
+
819
+ star_idx = None
820
+ for n, pattern in enumerate(self.patterns):
821
+ if pattern.is_match_and_assign_pattern and pattern.is_star:
822
+ star_idx = n
823
+ if star_idx is None:
824
+ idxs = list(range(len(self.patterns)))
825
+ else:
826
+ fwd_idxs = list(range(star_idx))
827
+ backward_idxs = list(range(star_idx - len(self.patterns) + 1, 0))
828
+ star_idx = (
829
+ fwd_idxs[-1] + 1 if fwd_idxs else None,
830
+ backward_idxs[0] if backward_idxs else None,
831
+ )
832
+ idxs = fwd_idxs + [star_idx] + backward_idxs
833
+
834
+ subjects = []
835
+ for pattern, idx in zip(self.patterns, idxs):
836
+ indexer = self.make_indexing_node(pattern, subject_node, idx, env)
837
+ subjects.append(ExprNodes.ProxyNode(indexer) if indexer else None)
838
+ self.subjects = subjects
839
+ self.subject_temps = [
840
+ None if p.is_irrefutable() else TrackTypeTempNode(self.pos, s)
841
+ for s, p in zip(self.subjects, self.patterns)
842
+ ]
843
+
844
+ def create_main_pattern_assignment_list(self, subject_node, env):
845
+ assignments = []
846
+ self.generate_subjects(subject_node, env)
847
+ for subject_temp, subject, pattern in zip(
848
+ self.subject_temps, self.subjects, self.patterns
849
+ ):
850
+ needs_result_ref = False
851
+ if subject_temp is not None:
852
+ subject = subject_temp
853
+ else:
854
+ if subject is None:
855
+ assert not pattern.get_targets()
856
+ continue
857
+ elif not subject.is_literal or subject.is_temp:
858
+ from .UtilNodes import ResultRefNode, LetNode
859
+
860
+ subject = ResultRefNode(subject)
861
+ needs_result_ref = True
862
+ p_assignments = pattern.create_target_assignments(subject, env)
863
+ if needs_result_ref:
864
+ p_assignments = LetNode(subject, p_assignments)
865
+ else:
866
+ p_assignments = p_assignments
867
+ if p_assignments:
868
+ assignments.append(p_assignments)
869
+ return assignments
870
+
871
+ def make_sequence_check(self, subject_node, sequence_mapping_temp):
872
+ # Note: the sequence check code is very quick on Python 3.10+
873
+ # but potentially quite slow on lower versions (although should
874
+ # be medium quick for common types). It'd be nice to cache the
875
+ # results of it where it's been called on the same object
876
+ # multiple times.
877
+ # DW has decided that that's too complicated to implement
878
+ # for now.
879
+ utility_code = UtilityCode.load_cached("IsSequence", "MatchCase.c")
880
+ if sequence_mapping_temp is not None:
881
+ sequence_mapping_temp = ExprNodes.AmpersandNode(
882
+ self.pos, operand=sequence_mapping_temp
883
+ )
884
+ else:
885
+ sequence_mapping_temp = ExprNodes.NullNode(self.pos)
886
+ call = ExprNodes.PythonCapiCallNode(
887
+ self.pos,
888
+ "__Pyx_MatchCase_IsSequence",
889
+ self.Pyx_sequence_check_type,
890
+ utility_code=utility_code,
891
+ args=[subject_node, sequence_mapping_temp],
892
+ )
893
+
894
+ def type_check(type):
895
+ # type-check need not be perfect, it's an optimization
896
+ if type.is_pylist_type or type.is_pytuple_type:
897
+ return True
898
+ if type.is_memoryviewslice or type.is_ctuple:
899
+ return True
900
+ if (
901
+ type.is_bytes_or_str_or_bytearray or
902
+ type.is_pyanydict_type or
903
+ type.is_pyanyset_type
904
+ ):
905
+ # non-exhaustive list at this stage, but returning "False" is
906
+ # an optimization so it's allowed to be non-exchaustive
907
+ return False
908
+ if type.is_numeric or type.is_struct or type.is_enum:
909
+ # again, not exhaustive
910
+ return False
911
+ return None
912
+
913
+ return StaticTypeCheckNode(
914
+ self.pos, arg=subject_node, fallback=call, check=type_check
915
+ )
916
+
917
+ def make_length_call_node(self, subject_node):
918
+ len_entry = Builtin.builtin_scope.lookup("len")
919
+ if subject_node.type.is_memoryviewslice:
920
+ len_call = ExprNodes.IndexNode(
921
+ self.pos,
922
+ base=ExprNodes.AttributeNode(
923
+ self.pos, obj=subject_node, attribute="shape"
924
+ ),
925
+ index=ExprNodes.IntNode.for_size(self.pos, 0),
926
+ )
927
+ elif subject_node.type.is_ctuple:
928
+ len_call = ExprNodes.IntNode.for_size(
929
+ self.pos, len(subject_node.type.components))
930
+ else:
931
+ len_call = ExprNodes.SimpleCallNode(
932
+ self.pos,
933
+ function=ExprNodes.NameNode(self.pos, name="len", entry=len_entry),
934
+ args=[subject_node],
935
+ )
936
+ if self.needs_length_temp:
937
+ return ExprNodes.AssignmentExpressionNode(
938
+ self.pos, lhs=self.length_temp, rhs=len_call
939
+ )
940
+ else:
941
+ return len_call
942
+
943
+ def make_indexing_node(self, pattern, subject_node, idx, env):
944
+ if pattern.is_irrefutable() and not pattern.get_targets():
945
+ # Nothing to do - index isn't used
946
+ return None
947
+
948
+ def get_index_from_int(i):
949
+ if i is None:
950
+ return None
951
+ else:
952
+ int_node = ExprNodes.IntNode.for_size(pattern.pos, i)
953
+ if i >= 0:
954
+ return int_node
955
+ else:
956
+ self.needs_length_temp = True
957
+ return ExprNodes.binop_node(
958
+ pattern.pos,
959
+ operator="+",
960
+ operand1=self.length_temp,
961
+ operand2=int_node,
962
+ )
963
+
964
+ if isinstance(idx, tuple):
965
+ start = get_index_from_int(idx[0])
966
+ stop = get_index_from_int(idx[1])
967
+ indexer = SliceToListNode(
968
+ pattern.pos,
969
+ base=subject_node,
970
+ start=start,
971
+ stop=stop,
972
+ length_node=self.length_temp if self.needs_length_temp else None,
973
+ )
974
+ else:
975
+ indexer = ExprNodes.CompilerDirectivesExprNode(
976
+ arg=ExprNodes.IndexNode(
977
+ pattern.pos, base=subject_node, index=get_index_from_int(idx)
978
+ ),
979
+ directives=copy_inherited_directives(
980
+ env.directives, boundscheck=False, wraparound=False
981
+ ),
982
+ )
983
+ return indexer
984
+
985
+ def analyse_declarations(self, env):
986
+ for p in self.patterns:
987
+ p.analyse_declarations(env)
988
+ return super(MatchSequencePatternNode, self).analyse_declarations(env)
989
+
990
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
991
+ for n in range(len(self.subjects)):
992
+ if self.subjects[n]:
993
+ self.subjects[n] = self.subjects[n].analyse_types(env)
994
+ for n in range(len(self.patterns)):
995
+ self.patterns[n] = self.patterns[n].analyse_pattern_expressions(env, None)
996
+ return self
997
+
998
+ def allocate_subject_temps(self, code):
999
+ if self.needs_length_temp:
1000
+ self.length_temp.allocate(code)
1001
+ for temp in self.subject_temps:
1002
+ if temp is not None:
1003
+ temp.allocate(code)
1004
+ for pattern in self.patterns:
1005
+ pattern.allocate_subject_temps(code)
1006
+
1007
+ def release_subject_temps(self, code):
1008
+ if self.needs_length_temp:
1009
+ self.length_temp.release(code)
1010
+ for temp in self.subject_temps:
1011
+ if temp is not None:
1012
+ temp.release(code)
1013
+ for pattern in self.patterns:
1014
+ pattern.release_subject_temps(code)
1015
+
1016
+ def dispose_of_subject_temps(self, code):
1017
+ if self.needs_length_temp:
1018
+ code.put_xdecref_clear(self.length_temp.result(), self.length_temp.type)
1019
+ for temp in self.subject_temps:
1020
+ if temp is not None:
1021
+ code.put_xdecref_clear(temp.result(), temp.type)
1022
+ for pattern in self.patterns:
1023
+ pattern.dispose_of_subject_temps(code)
1024
+
1025
+
1026
+ class MatchMappingPatternNode(PatternNode):
1027
+ """
1028
+ keys list of Literals or AttributeNodes
1029
+ value_patterns list of PatternNodes of equal length to keys
1030
+ double_star_capture_target NameNode or None
1031
+
1032
+ needs_runtime_keycheck - bool - are there any keys which can only be resolved at runtime
1033
+ subjects [temp nodes or None] individual subsubjects can be assigned to these
1034
+ """
1035
+
1036
+ keys = []
1037
+ value_patterns = []
1038
+ double_star_capture_target = None
1039
+ subject_temps = None
1040
+ double_star_temp = None
1041
+
1042
+ needs_runtime_keycheck = False
1043
+
1044
+ child_attrs = PatternNode.child_attrs + [
1045
+ "keys",
1046
+ "value_patterns",
1047
+ "double_star_capture_target",
1048
+ ]
1049
+
1050
+ Pyx_mapping_check_type = PyrexTypes.CFuncType(
1051
+ PyrexTypes.c_bint_type,
1052
+ [
1053
+ PyrexTypes.CFuncTypeArg("o", PyrexTypes.py_object_type, None),
1054
+ PyrexTypes.CFuncTypeArg(
1055
+ "sequence_mapping_temp",
1056
+ PyrexTypes.c_ptr_type(PyrexTypes.c_uint_type),
1057
+ None,
1058
+ ),
1059
+ ],
1060
+ exception_value="-1",
1061
+ )
1062
+ # lie about the types of keys for simplicity
1063
+ Pyx_mapping_check_duplicates_type = PyrexTypes.CFuncType(
1064
+ PyrexTypes.c_int_type,
1065
+ [
1066
+ PyrexTypes.CFuncTypeArg("keys", PyrexTypes.c_void_ptr_type, None),
1067
+ PyrexTypes.CFuncTypeArg("nKeys", PyrexTypes.c_py_ssize_t_type, None),
1068
+ ],
1069
+ exception_value="-1",
1070
+ )
1071
+ # lie about the types of keys and subjects for simplicity
1072
+ Pyx_mapping_extract_subjects_type = PyrexTypes.CFuncType(
1073
+ PyrexTypes.c_bint_type,
1074
+ [
1075
+ PyrexTypes.CFuncTypeArg("mapping", PyrexTypes.py_object_type, None),
1076
+ PyrexTypes.CFuncTypeArg("keys", PyrexTypes.c_void_ptr_type, None),
1077
+ PyrexTypes.CFuncTypeArg("nKeys", PyrexTypes.c_py_ssize_t_type, None),
1078
+ PyrexTypes.CFuncTypeArg("subjects", PyrexTypes.c_void_ptr_ptr_type, None),
1079
+ ],
1080
+ exception_value="-1",
1081
+ )
1082
+ Pyx_mapping_doublestar_type = PyrexTypes.CFuncType(
1083
+ Builtin.dict_type,
1084
+ [
1085
+ PyrexTypes.CFuncTypeArg("mapping", PyrexTypes.py_object_type, None),
1086
+ PyrexTypes.CFuncTypeArg("keys", PyrexTypes.c_void_ptr_type, None),
1087
+ PyrexTypes.CFuncTypeArg("nKeys", PyrexTypes.c_py_ssize_t_type, None),
1088
+ ],
1089
+ )
1090
+
1091
+ def is_sequence_or_mapping(self):
1092
+ return True
1093
+
1094
+ def get_main_pattern_targets(self):
1095
+ targets = set()
1096
+ for pattern in self.value_patterns:
1097
+ self.update_targets_with_targets(targets, pattern.get_targets())
1098
+ if self.double_star_capture_target:
1099
+ self.add_target_to_targets(targets, self.double_star_capture_target.name)
1100
+ return targets
1101
+
1102
+ def validate_keys(self):
1103
+ # called after constant folding
1104
+ literal_keys = set()
1105
+ for k in self.keys:
1106
+ if k.has_constant_result():
1107
+ value = k.constant_result
1108
+ # Note that "set" equality behaviour is intentional. e.g. 0.0 and 0 are duplicates.
1109
+ if value in literal_keys:
1110
+ error(k.pos, "mapping pattern checks duplicate key (%s)" % value)
1111
+ literal_keys.add(value)
1112
+ else:
1113
+ self.needs_runtime_keycheck = True
1114
+
1115
+ if literal_keys and self.keys:
1116
+ # it's very useful to sort keys early so the literal keys
1117
+ # come first
1118
+ sorted_keys = sorted(
1119
+ zip(self.keys, self.value_patterns),
1120
+ key=lambda kvp: (not kvp[0].is_literal),
1121
+ )
1122
+ self.keys, self.value_patterns = [list(l) for l in zip(*sorted_keys)]
1123
+
1124
+ def analyse_declarations(self, env):
1125
+ super(MatchMappingPatternNode, self).analyse_declarations(env)
1126
+ self.validate_keys()
1127
+ for k in self.keys:
1128
+ k.analyse_declarations(env)
1129
+ for vp in self.value_patterns:
1130
+ vp.analyse_declarations(env)
1131
+ if self.double_star_capture_target:
1132
+ self.double_star_capture_target.analyse_declarations(env)
1133
+
1134
+ def generate_subjects(self, subject_node, env):
1135
+ assert self.subject_temps is None # already calculated
1136
+ subject_temps = []
1137
+ for pattern in self.value_patterns:
1138
+ if pattern.is_match_and_assign_pattern and not pattern.target:
1139
+ subject_temps.append(None)
1140
+ else:
1141
+ subject_temps.append(
1142
+ AssignableTempNode(pattern.pos, PyrexTypes.py_object_type)
1143
+ )
1144
+ self.subject_temps = subject_temps
1145
+
1146
+ def create_main_pattern_assignment_list(self, subject_node, env):
1147
+ self.generate_subjects(subject_node, env)
1148
+ assignments = []
1149
+ for subject, pattern in zip(self.subject_temps, self.value_patterns):
1150
+ p_assignments = pattern.create_target_assignments(subject, env)
1151
+ if p_assignments:
1152
+ assignments.extend(p_assignments.stats)
1153
+ if self.double_star_capture_target:
1154
+ self.double_star_temp = AssignableTempNode(self.pos, Builtin.dict_type)
1155
+ assignments.append(
1156
+ Nodes.SingleAssignmentNode(
1157
+ self.double_star_temp.pos,
1158
+ lhs=self.double_star_capture_target,
1159
+ rhs=self.double_star_temp,
1160
+ )
1161
+ )
1162
+ return assignments
1163
+
1164
+ class DictCheckResult(enum.IntEnum):
1165
+ NotAMapping = enum.auto()
1166
+ NotADict = enum.auto() # but might be another mapping
1167
+ MightBeAnAnyDict = enum.auto()
1168
+ ExactDict = enum.auto()
1169
+ ExactFrozenDict = enum.auto()
1170
+
1171
+ def is_dict_type_check(self, type):
1172
+ # Returns a DictCheckResult to summarize what we know about types dictness.
1173
+ # We need to get ExactDict, ExactFrozenDict, and NotAMapping right. The other
1174
+ # two cases are optimizations so can be wrong.
1175
+ if type.is_pydict_type:
1176
+ return self.DictCheckResult.ExactDict
1177
+ if type.is_pyfrozendict_type:
1178
+ return self.DictCheckResult.ExactFrozenDict
1179
+ if type.is_builtin_type:
1180
+ # All other builtin types that Cython knows of are not mappings
1181
+ # (DictProxyType is, but Cython doesn't know about that).
1182
+ return self.DictCheckResult.NotAMapping
1183
+ if not type.is_pyobject:
1184
+ # for now any non-pyobject type isn't treated as a mapping
1185
+ return self.DictCheckResult.NotAMapping
1186
+ if type.is_extension_type:
1187
+ # An external extension type might actually be a dict, but that's fine:
1188
+ # it just ends up on a slightly slower code path.
1189
+ return self.DictCheckResult.NotADict
1190
+ return self.DictCheckResult.MightBeAnAnyDict
1191
+
1192
+ def make_mapping_check(self, subject_node, sequence_mapping_temp):
1193
+ # Note: the mapping check code is very quick on Python 3.10+
1194
+ # but potentially quite slow on lower versions (although should
1195
+ # be medium quick for common types). It'd be nice to cache the
1196
+ # results of it where it's been called on the same object
1197
+ # multiple times.
1198
+ # DW has decided that that's too complicated to implement
1199
+ # for now.
1200
+ utility_code = UtilityCode.load_cached("IsMapping", "MatchCase.c")
1201
+ if sequence_mapping_temp is not None:
1202
+ sequence_mapping_temp = ExprNodes.AmpersandNode(
1203
+ self.pos, operand=sequence_mapping_temp
1204
+ )
1205
+ else:
1206
+ sequence_mapping_temp = ExprNodes.NullNode(self.pos)
1207
+ call = ExprNodes.PythonCapiCallNode(
1208
+ self.pos,
1209
+ "__Pyx_MatchCase_IsMapping",
1210
+ self.Pyx_mapping_check_type,
1211
+ utility_code=utility_code,
1212
+ args=[subject_node, sequence_mapping_temp],
1213
+ )
1214
+
1215
+ def type_check(type):
1216
+ res = self.is_dict_type_check(type)
1217
+ if res is self.DictCheckResult.ExactDict or res is self.DictCheckResult.ExactFrozenDict:
1218
+ return True
1219
+ if res is self.DictCheckResult.NotAMapping:
1220
+ return False
1221
+ return None
1222
+
1223
+ return StaticTypeCheckNode(
1224
+ self.pos, arg=subject_node, fallback=call, check=type_check
1225
+ )
1226
+
1227
+ def make_duplicate_keys_check(self, n_fixed_keys):
1228
+ utility_code = UtilityCode.load_cached("MappingKeyCheck", "MatchCase.c")
1229
+ if n_fixed_keys == len(self.keys):
1230
+ return None # nothing to check
1231
+ if n_fixed_keys == 0 and len(self.keys) <= 1:
1232
+ return None # nothing to check
1233
+
1234
+ return Nodes.ExprStatNode(
1235
+ self.pos,
1236
+ expr=ExprNodes.PythonCapiCallNode(
1237
+ self.pos,
1238
+ "__Pyx_MatchCase_CheckMappingDuplicateKeys",
1239
+ self.Pyx_mapping_check_duplicates_type,
1240
+ utility_code=utility_code,
1241
+ args=[
1242
+ EvaluateWithKeysAndSubjectsArrays.make_keys_node(self.pos),
1243
+ ExprNodes.IntNode.for_size(self.pos, n_fixed_keys),
1244
+ ExprNodes.IntNode.for_size(self.pos, len(self.keys)),
1245
+ ],
1246
+ ),
1247
+ )
1248
+
1249
+ def check_all_keys(self, subject_node):
1250
+ # It's debatable here whether to go for individual unpacking or a function.
1251
+ # Current implementation is a function that's loosely copied from CPython.
1252
+ # For small numbers of keys it might be better to generate the code instead.
1253
+ # There's three versions depending on if we know that the type is exactly
1254
+ # a dict, definitely not a dict, or unknown.
1255
+ # The advantages of generating a function are:
1256
+ # * more compact code
1257
+ # * can potentially be extracted into the shared utility module
1258
+ # * easier to check the type once then branch the implementation
1259
+ # * faster in the cases that are more likely to fail due to wrong keys being
1260
+ # present than due to the values not matching the patterns
1261
+ if not self.keys:
1262
+ return ExprNodes.BoolNode(self.pos, value=True)
1263
+
1264
+ is_dict = self.is_dict_type_check(subject_node.type)
1265
+ if is_dict is self.DictCheckResult.ExactDict or is_dict is self.DictCheckResult.ExactFrozenDict:
1266
+ util_code = UtilityCode.load_cached("ExtractExactDict", "MatchCase.c")
1267
+ func_name = "__Pyx_MatchCase_Mapping_ExtractDict"
1268
+ elif is_dict is self.DictCheckResult.MightBeAnAnyDict:
1269
+ util_code = UtilityCode.load_cached("ExtractGeneric", "MatchCase.c")
1270
+ func_name = "__Pyx_MatchCase_Mapping_Extract"
1271
+ else:
1272
+ util_code = UtilityCode.load_cached("ExtractNonDict", "MatchCase.c")
1273
+ func_name = "__Pyx_MatchCase_Mapping_ExtractNonDict"
1274
+
1275
+ return ExprNodes.PythonCapiCallNode(
1276
+ self.pos,
1277
+ func_name,
1278
+ self.Pyx_mapping_extract_subjects_type,
1279
+ utility_code=util_code,
1280
+ args=[
1281
+ subject_node,
1282
+ EvaluateWithKeysAndSubjectsArrays.make_keys_node(self.pos),
1283
+ ExprNodes.IntNode.for_size(self.pos, len(self.keys)),
1284
+ EvaluateWithKeysAndSubjectsArrays.make_subjects_node(self.pos),
1285
+ ],
1286
+ )
1287
+
1288
+ def make_double_star_capture(self, subject_node, test_result):
1289
+ # test_result being the variable that holds "case check passed until now"
1290
+ is_dict = self.is_dict_type_check(subject_node.type)
1291
+ if is_dict is self.DictCheckResult.ExactDict:
1292
+ tag = "ExactDict"
1293
+ elif is_dict is self.DictCheckResult.ExactFrozenDict:
1294
+ tag = "ExactFrozenDict"
1295
+ elif is_dict is self.DictCheckResult.MightBeAnAnyDict:
1296
+ tag = ""
1297
+ else:
1298
+ tag = "NotDict"
1299
+ utility_code = TempitaUtilityCode.load_cached(
1300
+ "DoubleStarCapture", "MatchCase.c", context={"tag": tag}
1301
+ )
1302
+ func = ExprNodes.PythonCapiCallNode(
1303
+ self.double_star_capture_target.pos,
1304
+ "__Pyx_MatchCase_DoubleStarCapture" + tag,
1305
+ self.Pyx_mapping_doublestar_type,
1306
+ utility_code=utility_code,
1307
+ args=[
1308
+ subject_node,
1309
+ EvaluateWithKeysAndSubjectsArrays.make_keys_node(self.pos),
1310
+ ExprNodes.IntNode.for_size(self.pos, len(self.keys)),
1311
+ ],
1312
+ )
1313
+ assignment = Nodes.SingleAssignmentNode(
1314
+ self.double_star_capture_target.pos, lhs=self.double_star_temp, rhs=func
1315
+ )
1316
+ if_clause = Nodes.IfClauseNode(
1317
+ self.double_star_capture_target.pos, condition=test_result, body=assignment
1318
+ )
1319
+ return Nodes.IfStatNode(
1320
+ self.double_star_capture_target.pos,
1321
+ if_clauses=[if_clause],
1322
+ else_clause=None,
1323
+ )
1324
+
1325
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
1326
+ from . import UtilNodes
1327
+
1328
+ var_keys = []
1329
+ n_literal_keys = 0
1330
+ for k in self.keys:
1331
+ if not k.is_literal:
1332
+ var_keys.append(k)
1333
+ else:
1334
+ n_literal_keys += 1
1335
+
1336
+ all_tests = []
1337
+ all_tests.append(self.make_mapping_check(subject_node, sequence_mapping_temp))
1338
+ all_tests.append(self.check_all_keys(subject_node))
1339
+
1340
+ if any(isinstance(test, ExprNodes.BoolNode) and not test.value for test in all_tests):
1341
+ # identify automatic-failure
1342
+ return ExprNodes.BoolNode(self.pos, value=False)
1343
+
1344
+ for pattern, subject in zip(self.value_patterns, self.subject_temps):
1345
+ if pattern.is_irrefutable():
1346
+ continue
1347
+ assert subject
1348
+ all_tests.append(pattern.get_comparison_node(subject))
1349
+
1350
+ body = all_tests_node = generate_binop_tree_from_list(self.pos, "and", all_tests)
1351
+ duplicate_check = self.make_duplicate_keys_check(n_literal_keys)
1352
+
1353
+ if duplicate_check or self.double_star_capture_target:
1354
+ stats = []
1355
+ if duplicate_check:
1356
+ stats.append(duplicate_check)
1357
+ test_result = UtilNodes.ResultRefNode(pos=self.pos, type=PyrexTypes.c_bint_type)
1358
+ stats.append(
1359
+ Nodes.SingleAssignmentNode(self.pos, lhs=test_result, rhs=all_tests_node))
1360
+ if self.double_star_capture_target:
1361
+ assert self.double_star_temp
1362
+ stats.append(
1363
+ # make_double_star_capture wraps itself in an if
1364
+ self.make_double_star_capture(subject_node, test_result)
1365
+ )
1366
+ body = UtilNodes.TempResultFromStatNode(test_result, Nodes.StatListNode(self.pos, stats=stats))
1367
+ if self.keys or self.double_star_capture_target:
1368
+ body = EvaluateWithKeysAndSubjectsArrays(
1369
+ body.pos,
1370
+ arg=LazyCoerceToBool(body.pos, arg=body),
1371
+ keys_array=self.keys,
1372
+ subjects_array=self.subject_temps
1373
+ )
1374
+ return LazyCoerceToBool(body.pos, arg=body)
1375
+
1376
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
1377
+ def to_temp_or_literal(node):
1378
+ return node if node.is_literal else node.coerce_to_temp(env)
1379
+
1380
+ self.keys = [
1381
+ to_temp_or_literal(k.analyse_expressions(env))
1382
+ for k in self.keys
1383
+ ]
1384
+
1385
+ self.value_patterns = [ p.analyse_pattern_expressions(env, None) for p in self.value_patterns ]
1386
+ return self
1387
+
1388
+ def allocate_subject_temps(self, code):
1389
+ for temp in self.subject_temps:
1390
+ if temp is not None:
1391
+ temp.allocate(code)
1392
+ for pattern in self.value_patterns:
1393
+ pattern.allocate_subject_temps(code)
1394
+ if self.double_star_temp:
1395
+ self.double_star_temp.allocate(code)
1396
+
1397
+ def release_subject_temps(self, code):
1398
+ for temp in self.subject_temps:
1399
+ if temp is not None:
1400
+ temp.release(code)
1401
+ for pattern in self.value_patterns:
1402
+ pattern.release_subject_temps(code)
1403
+ if self.double_star_temp:
1404
+ self.double_star_temp.release(code)
1405
+
1406
+ def dispose_of_subject_temps(self, code):
1407
+ for temp in self.subject_temps:
1408
+ if temp is not None:
1409
+ code.put_xdecref_clear(temp.result(), temp.type)
1410
+ for pattern in self.value_patterns:
1411
+ pattern.dispose_of_subject_temps(code)
1412
+ if self.double_star_temp:
1413
+ code.put_xdecref_clear(
1414
+ self.double_star_temp.result(), self.double_star_temp.type
1415
+ )
1416
+
1417
+
1418
+ class ClassPatternNode(PatternNode):
1419
+ """
1420
+ class_ NameNode or AttributeNode
1421
+ positional_patterns list of PatternNodes
1422
+ keyword_pattern_names list of NameNodes
1423
+ keyword_pattern_patterns list of PatternNodes
1424
+ (same length as keyword_pattern_names)
1425
+ """
1426
+
1427
+ class_ = None
1428
+ positional_patterns = []
1429
+ keyword_pattern_names = []
1430
+ keyword_pattern_patterns = []
1431
+
1432
+ # as with the mapping functions, lie a little about some of the types for
1433
+ # ease of declaration
1434
+ Pyx_positional_type = PyrexTypes.CFuncType(
1435
+ PyrexTypes.c_bint_type,
1436
+ [
1437
+ PyrexTypes.CFuncTypeArg("subject", PyrexTypes.py_object_type, None),
1438
+ PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None),
1439
+ PyrexTypes.CFuncTypeArg("fixed_names", PyrexTypes.c_void_ptr_type, None),
1440
+ PyrexTypes.CFuncTypeArg("n_fixed", PyrexTypes.c_py_ssize_t_type, None),
1441
+ PyrexTypes.CFuncTypeArg("match_self", PyrexTypes.c_int_type, None),
1442
+ PyrexTypes.CFuncTypeArg("subjects", PyrexTypes.c_void_ptr_ptr_type, None),
1443
+ PyrexTypes.CFuncTypeArg("n_subjects", PyrexTypes.c_int_type, None),
1444
+ ],
1445
+ exception_value="-1",
1446
+ )
1447
+
1448
+ Pyx_typeguard_type = PyrexTypes.CFuncType(
1449
+ PyrexTypes.py_object_type,
1450
+ [
1451
+ PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None),
1452
+ ],
1453
+ )
1454
+
1455
+ child_attrs = PatternNode.child_attrs + [
1456
+ "class_",
1457
+ "positional_patterns",
1458
+ "keyword_pattern_patterns",
1459
+ # keyword_pattern_names are deliberately excluded. They're only NameNodes as a
1460
+ # convenient way of storing a name and a pos. There's nothing to be gained from
1461
+ # processing them
1462
+ ]
1463
+
1464
+ def generate_subjects(self, subject_node):
1465
+ assert not hasattr(self, "keyword_subject_temps")
1466
+
1467
+ if self.class_known_type:
1468
+ # maximizes type inference
1469
+ subject_node = ExprNodes.TypecastNode(
1470
+ subject_node.pos,
1471
+ operand=subject_node,
1472
+ type=self.class_known_type,
1473
+ typecheck=False,
1474
+ )
1475
+
1476
+ self.keyword_subject_temps = []
1477
+ self.keyword_subject_attrs = []
1478
+ for p, p_name in zip(self.keyword_pattern_patterns, self.keyword_pattern_names):
1479
+ # The attribute lookups are calculated here to maximize chance of type interference
1480
+ attr_lookup = ExprNodes.AttributeNode(
1481
+ p_name.pos, obj=subject_node, attribute=p_name.name, mangle_private_names=False
1482
+ )
1483
+ self.keyword_subject_attrs.append(attr_lookup)
1484
+ if not p.get_targets() and p.is_irrefutable():
1485
+ self.keyword_subject_temps.append(None)
1486
+ else:
1487
+ # Hopefully the type can be assigned later
1488
+ self.keyword_subject_temps.append(TrackTypeTempNode(p.pos, attr_lookup))
1489
+
1490
+ self.positional_subject_temps = []
1491
+ for p in self.positional_patterns:
1492
+ if not p.get_targets() and p.is_irrefutable():
1493
+ self.positional_subject_temps.append(None)
1494
+ else:
1495
+ self.positional_subject_temps.append(
1496
+ AssignableTempNode(p.pos, PyrexTypes.py_object_type)
1497
+ )
1498
+
1499
+ def get_main_pattern_targets(self):
1500
+ targets = set()
1501
+ for pattern in self.positional_patterns + self.keyword_pattern_patterns:
1502
+ self.update_targets_with_targets(targets, pattern.get_targets())
1503
+ return targets
1504
+
1505
+ def create_main_pattern_assignment_list(self, subject_node, env):
1506
+ self.generate_subjects(subject_node)
1507
+ assignments = []
1508
+ patterns = self.keyword_pattern_patterns + self.positional_patterns
1509
+ temps = self.keyword_subject_temps + self.positional_subject_temps
1510
+ for pattern, temp in zip(patterns, temps):
1511
+ pattern_assignments = pattern.create_target_assignments(temp, env)
1512
+ if pattern_assignments:
1513
+ assignments.extend(pattern_assignments.stats)
1514
+ return assignments
1515
+
1516
+ def make_typecheck_call(self, subject_node, class_node):
1517
+ if not subject_node.type.is_pyobject:
1518
+ with local_errors(True) as errors:
1519
+ # TODO - it'd be nice to be able to match up simple c types
1520
+ # e.g. "int" to "int", "double" to "double"
1521
+ # without having to go through this
1522
+ subject_node = LazyCoerceToPyObject(subject_node.pos, arg=subject_node)
1523
+ if errors:
1524
+ return ExprNodes.BoolNode(self.pos, value=False)
1525
+ if self.class_known_type:
1526
+ if not self.class_known_type.is_pyobject:
1527
+ error(self.pos, "class must be a Python object")
1528
+ return ExprNodes.BoolNode(self.pos, value=False)
1529
+
1530
+ if subject_node.type.subtype_of_resolved_type(self.class_known_type):
1531
+ if subject_node.may_be_none():
1532
+ return ExprNodes.PrimaryCmpNode(
1533
+ self.pos,
1534
+ operator="is_not",
1535
+ operand1=subject_node,
1536
+ operand2=ExprNodes.NoneNode(self.pos),
1537
+ )
1538
+ else:
1539
+ return ExprNodes.BoolNode(self.pos, value=True)
1540
+ # if subject_node.type is not PyrexTypes.py_object_type
1541
+ # I suspect the value is false, but possibly can't prove it
1542
+
1543
+ return ExprNodes.SimpleCallNode(
1544
+ self.pos,
1545
+ function=ExprNodes.NameNode(
1546
+ self.pos,
1547
+ name="isinstance",
1548
+ entry=Builtin.builtin_scope.lookup("isinstance"),
1549
+ ),
1550
+ args=[subject_node, class_node],
1551
+ )
1552
+
1553
+ def make_keyword_pattern_lookups(self):
1554
+ # These are always looking up fixed names.
1555
+ # Therefore, get best efficiency by letting Cython do the lookup
1556
+ # and so infer the types
1557
+ assert self.keyword_pattern_names
1558
+
1559
+ from .UtilNodes import ResultRefNode, TempResultFromStatNode
1560
+
1561
+ passed_rr = ResultRefNode(pos=self.pos, type=PyrexTypes.c_bint_type)
1562
+ stats = []
1563
+ for pattern_name, subject_temp, lookup in zip(
1564
+ self.keyword_pattern_names,
1565
+ self.keyword_subject_temps,
1566
+ self.keyword_subject_attrs,
1567
+ ):
1568
+ if subject_temp:
1569
+ subject_temp.arg = lookup # it should now know the type
1570
+ stat = Nodes.SingleAssignmentNode(
1571
+ pattern_name.pos, lhs=subject_temp, rhs=lookup
1572
+ )
1573
+ else:
1574
+ stat = Nodes.ExprStatNode(pattern_name.pos, expr=lookup)
1575
+ stats.append(stat)
1576
+ except_clause = Nodes.ExceptClauseNode(
1577
+ self.pos,
1578
+ pattern=[
1579
+ ExprNodes.NameNode(
1580
+ self.pos,
1581
+ name="AttributeError",
1582
+ entry=Builtin.builtin_scope.lookup("AttributeError"),
1583
+ )
1584
+ ],
1585
+ body=Nodes.StatListNode(
1586
+ self.pos,
1587
+ stats=[
1588
+ Nodes.SingleAssignmentNode(
1589
+ self.pos,
1590
+ lhs=passed_rr,
1591
+ rhs=ExprNodes.BoolNode(self.pos, value=False),
1592
+ )
1593
+ ],
1594
+ ),
1595
+ target=None,
1596
+ )
1597
+ else_clause = Nodes.SingleAssignmentNode(
1598
+ self.pos, lhs=passed_rr, rhs=ExprNodes.BoolNode(self.pos, value=True)
1599
+ )
1600
+ try_except = Nodes.TryExceptStatNode(
1601
+ self.pos,
1602
+ body=Nodes.StatListNode(self.pos, stats=stats),
1603
+ except_clauses=[except_clause],
1604
+ else_clause=else_clause,
1605
+ )
1606
+ return TempResultFromStatNode(passed_rr, try_except)
1607
+
1608
+ def _calculate_match_self(self):
1609
+ # -1 is "unknown", 0 is false, 1 is true
1610
+ if len(self.positional_patterns) != 1 or self.keyword_pattern_names:
1611
+ return 0
1612
+ if self.class_known_type:
1613
+ for type_attr in [
1614
+ "is_pytuple_type",
1615
+ "is_pylist_type",
1616
+ "is_pyanydict_type",
1617
+ "is_pyanyset_type",
1618
+ "is_bytes_or_str_or_bytearray",
1619
+ ]:
1620
+ if getattr(self.class_known_type, type_attr):
1621
+ return 1
1622
+ for t in [
1623
+ Builtin.bool_type,
1624
+ Builtin.float_type,
1625
+ Builtin.int_type,
1626
+ ]:
1627
+ if self.class_known_type.subtype_of_resolved_type(t):
1628
+ return 1
1629
+ if self.class_known_type.is_extension_type:
1630
+ tp = self.class_known_type
1631
+ while tp:
1632
+ if (not tp.is_extension_type or
1633
+ tp.is_external
1634
+ or not self.class_known_type.scope.method_table_cname
1635
+ ): # effectively extern visibility:
1636
+ return -1
1637
+ tp = tp.base_type
1638
+ # We know enough about the type's provinence to know it can't match a self argument
1639
+ return 0
1640
+ return -1
1641
+
1642
+
1643
+ def make_positional_args_call(self, subject_node, class_node):
1644
+ assert self.positional_patterns
1645
+ util_code = UtilityCode.load_cached("ClassPositionalPatterns", "MatchCase.c")
1646
+ keynames = [
1647
+ ExprNodes.UnicodeNode(n.pos, value=n.name)
1648
+ for n in self.keyword_pattern_names
1649
+ ]
1650
+
1651
+ match_self = ExprNodes.IntNode.for_size(self.pos, self._calculate_match_self())
1652
+ n_subjects = ExprNodes.IntNode.for_size(self.pos, len(self.positional_patterns))
1653
+ return EvaluateWithKeysAndSubjectsArrays(
1654
+ self.pos,
1655
+ arg=ExprNodes.PythonCapiCallNode(
1656
+ self.pos,
1657
+ "__Pyx_MatchCase_ClassPositional",
1658
+ self.Pyx_positional_type,
1659
+ utility_code=util_code,
1660
+ args=[
1661
+ subject_node,
1662
+ class_node,
1663
+ EvaluateWithKeysAndSubjectsArrays.make_keys_node(self.pos),
1664
+ ExprNodes.IntNode.for_size(self.pos, len(keynames)),
1665
+ match_self,
1666
+ EvaluateWithKeysAndSubjectsArrays.make_subjects_node(self.pos),
1667
+ n_subjects,
1668
+ ]
1669
+ ),
1670
+ subjects_array=self.positional_subject_temps,
1671
+ keys_array=keynames,
1672
+ )
1673
+
1674
+ def make_subpattern_checks(self):
1675
+ patterns = self.keyword_pattern_patterns + self.positional_patterns
1676
+ temps = self.keyword_subject_temps + self.positional_subject_temps
1677
+ checks = []
1678
+ for temp, pattern in zip(temps, patterns):
1679
+ if temp:
1680
+ checks.append(pattern.get_comparison_node(temp))
1681
+ return checks
1682
+
1683
+ def get_comparison_node(self, subject_node, sequence_mapping_temp=None):
1684
+ from .UtilNodes import ResultRefNode, EvalWithTempExprNode
1685
+
1686
+ if self.class_known_type:
1687
+ class_node = self.class_.clone_node()
1688
+ class_node.entry = self.class_known_type.entry
1689
+ else:
1690
+ if self.class_.type is not Builtin.type_type:
1691
+ util_code = UtilityCode.load_cached("MatchClassTypeGuard", "MatchCase.c")
1692
+ class_node = ExprNodes.PythonCapiCallNode(
1693
+ self.pos,
1694
+ "__Pyx_MatchCase_TypeGuard",
1695
+ self.Pyx_typeguard_type,
1696
+ utility_code=util_code,
1697
+ args=[self.class_],
1698
+ )
1699
+ class_node = ResultRefNode(class_node)
1700
+
1701
+ all_checks = []
1702
+ all_checks.append(self.make_typecheck_call(subject_node, class_node))
1703
+
1704
+ if self.class_known_type:
1705
+ # From this point on we know the type of the subject
1706
+ subject_node = ExprNodes.TypecastNode(
1707
+ self.class_.pos,
1708
+ operand=subject_node,
1709
+ type=self.class_known_type,
1710
+ typecheck=False,
1711
+ )
1712
+ if self.positional_patterns:
1713
+ all_checks.append(self.make_positional_args_call(subject_node, class_node))
1714
+ if self.keyword_pattern_names:
1715
+ all_checks.append(self.make_keyword_pattern_lookups())
1716
+
1717
+ all_checks.extend(self.make_subpattern_checks())
1718
+
1719
+ if any(isinstance(ch, ExprNodes.BoolNode) and not ch.value for ch in all_checks):
1720
+ # handle any obvious failures
1721
+ return ExprNodes.BoolNode(self.pos, value=False)
1722
+
1723
+ all_checks = generate_binop_tree_from_list(self.pos, "and", all_checks)
1724
+
1725
+ if isinstance(class_node, ResultRefNode) and not all_checks.is_literal:
1726
+ return LazyCoerceToBool(class_node.pos, arg=EvalWithTempExprNode(class_node, all_checks))
1727
+ else:
1728
+ return LazyCoerceToBool(all_checks.pos, arg=all_checks)
1729
+
1730
+ def analyse_declarations(self, env):
1731
+ self.validate_keywords()
1732
+ # Try to work out the type early
1733
+ self.class_.analyse_declarations(env)
1734
+ self.class_known_type = self.class_.analyse_as_extension_type(env)
1735
+ for p in self.positional_patterns:
1736
+ p.analyse_declarations(env)
1737
+ for p_name, p in zip(self.keyword_pattern_names, self.keyword_pattern_patterns):
1738
+ p_name.analyse_declarations(env)
1739
+ p.analyse_declarations(env)
1740
+ super(ClassPatternNode, self).analyse_declarations(env)
1741
+
1742
+ def analyse_pattern_expressions(self, env, sequence_mapping_temp):
1743
+ self.class_ = self.class_.analyse_types(env)
1744
+
1745
+ self.keyword_subject_attrs = [ a.analyse_types(env) for a in self.keyword_subject_attrs ]
1746
+ self.keyword_pattern_patterns = [ p.analyse_pattern_expressions(env, None) for p in self.keyword_pattern_patterns ]
1747
+ self.positional_patterns = [ p.analyse_pattern_expressions(env, None) for p in self.positional_patterns ]
1748
+
1749
+ return self
1750
+
1751
+ def allocate_subject_temps(self, code):
1752
+ for temp in self.keyword_subject_temps + self.positional_subject_temps:
1753
+ if temp is not None:
1754
+ temp.allocate(code)
1755
+ for pattern in self.keyword_pattern_patterns + self.positional_patterns:
1756
+ pattern.allocate_subject_temps(code)
1757
+
1758
+ def release_subject_temps(self, code):
1759
+ for temp in self.keyword_subject_temps + self.positional_subject_temps:
1760
+ if temp is not None:
1761
+ temp.release(code)
1762
+ for pattern in self.keyword_pattern_patterns + self.positional_patterns:
1763
+ pattern.release_subject_temps(code)
1764
+
1765
+ def dispose_of_subject_temps(self, code):
1766
+ for temp in self.keyword_subject_temps + self.positional_subject_temps:
1767
+ if temp is not None:
1768
+ code.put_xdecref_clear(temp.result(), temp.type)
1769
+ for pattern in self.keyword_pattern_patterns + self.positional_patterns:
1770
+ pattern.dispose_of_subject_temps(code)
1771
+
1772
+ def validate_keywords(self):
1773
+ seen = set()
1774
+ for kw in self.keyword_pattern_names:
1775
+ if kw.name in seen:
1776
+ error(
1777
+ self.pos,
1778
+ f"attribute name repeated in class pattern: '{kw.name}'"
1779
+ )
1780
+ seen.add(kw.name)
1781
+
1782
+
1783
+ class MatchValuePrimaryCmpNode(ExprNodes.PrimaryCmpNode):
1784
+ """
1785
+ Overrides PrimaryCmpNode to be a little more restrictive
1786
+ than normal. Specifically, Cython normally allows:
1787
+ int(1) is True
1788
+ Here, True should only match an exact Python object, or
1789
+ a bint(True).
1790
+ """
1791
+ def __init__(self, pos, **kwds):
1792
+ super().__init__(pos, **kwds)
1793
+ # operand1 should be the match subject
1794
+ assert isinstance(self.operand1, (ExprNodes.CloneNode, TrackTypeTempNode, AssignableTempNode)), type(self.operand1)
1795
+ assert self.operator in ["==", "is"]
1796
+
1797
+ def analyse_types(self, env):
1798
+ if (self.operator == "is" and
1799
+ isinstance(self.operand2, ExprNodes.BoolNode)):
1800
+ # operand1's type should already be known
1801
+ op1_type = self.operand1.type
1802
+ if not (op1_type.is_pyobject or op1_type is PyrexTypes.c_bint_type):
1803
+ return ExprNodes.BoolNode(self.pos, value=False).analyse_expressions(env)
1804
+
1805
+ return super().analyse_types(env)
1806
+
1807
+
1808
+ class SubstitutedIfStatListNode(Nodes.StatListNode):
1809
+ """
1810
+ Like StatListNode but with a "goto end of match" at the
1811
+ end of it
1812
+
1813
+ match_node - the enclosing match statement
1814
+ """
1815
+
1816
+ def generate_execution_code(self, code):
1817
+ super(SubstitutedIfStatListNode, self).generate_execution_code(code)
1818
+ if not self.is_terminator:
1819
+ code.put_goto(self.match_node.end_label)
1820
+
1821
+
1822
+ class StaticTypeCheckNode(ExprNodes.ExprNode):
1823
+ """
1824
+ Useful for structural pattern matching, where we
1825
+ can skip the "is_seqeunce/is_mapping" checks if
1826
+ we know the type in advance (or reduce it to a
1827
+ None check).
1828
+
1829
+ This should optimize itself out at the analyse_expressions
1830
+ stage
1831
+
1832
+ arg ExprNode
1833
+ fallback ExprNode Function to be called if the static
1834
+ typecheck isn't optimized out
1835
+ check callable Returns True, False, or None (for "can't tell")
1836
+ """
1837
+
1838
+ child_attrs = ["fallback"] # arg in not included since it's in "fallback"
1839
+
1840
+ def analyse_types(self, env):
1841
+ check = self.check(self.arg.type)
1842
+ if check:
1843
+ if self.arg.may_be_none():
1844
+ return ExprNodes.PrimaryCmpNode(
1845
+ self.pos,
1846
+ operand1=self.arg,
1847
+ operand2=ExprNodes.NoneNode(self.pos),
1848
+ operator="is_not",
1849
+ ).analyse_expressions(env)
1850
+ else:
1851
+ return ExprNodes.BoolNode(pos=self.pos, value=True).analyse_expressions(
1852
+ env
1853
+ )
1854
+ elif check is None:
1855
+ return self.fallback.analyse_expressions(env)
1856
+ else:
1857
+ return ExprNodes.BoolNode(pos=self.pos, value=False).analyse_expressions(
1858
+ env
1859
+ )
1860
+
1861
+
1862
+ class AssignableTempNode(ExprNodes.TempNode):
1863
+ lhs_of_first_assignment = True # assume it can be assigned to once
1864
+ _assigned_twice = False
1865
+
1866
+ def __init__(self, pos, *args, is_addressable=False, **kwds):
1867
+ self._is_addressable = is_addressable
1868
+ super().__init__(pos, *args, **kwds)
1869
+
1870
+ def is_addressable(self):
1871
+ return self._is_addressable
1872
+
1873
+ def infer_type(self, env):
1874
+ return self.type
1875
+
1876
+ def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
1877
+ assert (
1878
+ not self._assigned_twice
1879
+ ) # if this happens it's not a disaster but it needs a refactor
1880
+ self._assigned_twice = True
1881
+ if self.type.is_pyobject:
1882
+ rhs.make_owned_reference(code)
1883
+ if not self.lhs_of_first_assignment:
1884
+ code.put_decref(self.result(), self.ctype())
1885
+ code.putln(
1886
+ "%s = %s;"
1887
+ % (
1888
+ self.result(),
1889
+ rhs.result() if overloaded_assignment else rhs.result_as(self.ctype()),
1890
+ )
1891
+ )
1892
+ rhs.generate_post_assignment_code(code)
1893
+ rhs.free_temps(code)
1894
+
1895
+ def generate_post_assignment_code(self, code):
1896
+ code.put_incref(self.result(), self.type)
1897
+
1898
+ def generate_disposal_code(self, code):
1899
+ pass # handled elsewhere - we expect to use this temp multiple times
1900
+
1901
+ def clone_node(self):
1902
+ return self # temps break if you make a copy!
1903
+
1904
+
1905
+ class TrackTypeTempNode(AssignableTempNode):
1906
+ # Like a temp node, but type is set from arg
1907
+
1908
+ lhs_of_first_assignment = True # assume it can be assigned to once
1909
+ _assigned_twice = False
1910
+
1911
+ @property
1912
+ def type(self):
1913
+ return getattr(self.arg, "type", None)
1914
+
1915
+ def __init__(self, pos, arg):
1916
+ ExprNodes.ExprNode.__init__(self, pos) # skip a level
1917
+ self.arg = arg
1918
+
1919
+ def infer_type(self, env):
1920
+ return self.arg.infer_type(env)
1921
+
1922
+
1923
+ class SliceToListNode(ExprNodes.ExprNode):
1924
+ """
1925
+ Used as a brief temporary node to optimize
1926
+ case [..., *_, ...].
1927
+ Always reduces to something else after analyse_types
1928
+ """
1929
+
1930
+ subexprs = ["base", "start", "stop", "length_node"]
1931
+
1932
+ type = Builtin.list_type
1933
+
1934
+ Pyx_iterable_to_list_type = PyrexTypes.CFuncType(
1935
+ Builtin.list_type,
1936
+ [
1937
+ PyrexTypes.CFuncTypeArg("iterable", PyrexTypes.py_object_type, None),
1938
+ PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
1939
+ PyrexTypes.CFuncTypeArg("stop", PyrexTypes.c_py_ssize_t_type, None),
1940
+ ],
1941
+ )
1942
+
1943
+ def generate_via_slicing(self, env):
1944
+ # for any more complicated type that doesn't have a specialized path
1945
+ # we can simply slice it and copy it to list
1946
+ res = ExprNodes.CompilerDirectivesExprNode(
1947
+ arg=ExprNodes.SliceIndexNode(
1948
+ self.pos, base=self.base, start=self.start, stop=self.stop
1949
+ ),
1950
+ directives=copy_inherited_directives(
1951
+ env.directives, boundcheck=False, wraparound=False
1952
+ ),
1953
+ )
1954
+ res = ExprNodes.SimpleCallNode(
1955
+ self.pos,
1956
+ function=ExprNodes.NameNode(
1957
+ self.pos,
1958
+ name="list",
1959
+ entry=Builtin.builtin_scope.lookup("list"),
1960
+ ),
1961
+ args=[res],
1962
+ )
1963
+ return res
1964
+
1965
+ def get_stop(self):
1966
+ if not self.stop:
1967
+ if self.length_node:
1968
+ return self.length_node
1969
+ else:
1970
+ return ExprNodes.SimpleCallNode(
1971
+ self.pos,
1972
+ function=ExprNodes.NameNode(
1973
+ self.pos, name="len", entry=Builtin.builtin_scope.lookup("len")
1974
+ ),
1975
+ args=[self.base],
1976
+ )
1977
+ else:
1978
+ return self.stop
1979
+
1980
+ def generate_for_memoryview(self, env):
1981
+ # Requires Cython code generation...
1982
+ # A list comprehension with indexing turns out to be a good option
1983
+ from .UtilityCode import CythonUtilityCode
1984
+
1985
+ suffix = self.base.type.specialization_suffix()
1986
+ util_code = CythonUtilityCode.load(
1987
+ "MemoryviewSliceToList",
1988
+ "MatchCase_Cy.pyx",
1989
+ context={
1990
+ "decl_code": self.base.type.empty_declaration_code(pyrex=True),
1991
+ "suffix": suffix,
1992
+ },
1993
+ )
1994
+ func_type = PyrexTypes.CFuncType(
1995
+ Builtin.list_type,
1996
+ [
1997
+ PyrexTypes.CFuncTypeArg("x", self.base.type, None),
1998
+ PyrexTypes.CFuncTypeArg("start", PyrexTypes.c_py_ssize_t_type, None),
1999
+ PyrexTypes.CFuncTypeArg("stop", PyrexTypes.c_py_ssize_t_type, None),
2000
+ ],
2001
+ )
2002
+ env.use_utility_code(
2003
+ util_code
2004
+ ) # attaching it to the call node doesn't seem enough
2005
+ return ExprNodes.PythonCapiCallNode(
2006
+ self.pos,
2007
+ "__Pyx_MatchCase_SliceMemoryview_%s" % suffix,
2008
+ func_type,
2009
+ utility_code=util_code,
2010
+ args=[
2011
+ self.base,
2012
+ self.start if self.start else ExprNodes.IntNode.for_size(self.pos, 0),
2013
+ self.get_stop(),
2014
+ ],
2015
+ )
2016
+
2017
+ def generate_for_pyobject(self):
2018
+ util_code_name = None
2019
+ func_name = None
2020
+ if self.base.type.is_pytuple_type:
2021
+ util_code_name = "TupleSliceToList"
2022
+ elif self.base.type.is_pylist_type:
2023
+ func_name = "PyList_GetSlice"
2024
+ elif (
2025
+ self.base.type.is_pyobject
2026
+ and self.base.type is not PyrexTypes.py_object_type
2027
+ ):
2028
+ # some specialized type that almost certainly isn't a list. Just go straight
2029
+ # to the "other" version of it
2030
+ util_code_name = "OtherSequenceSliceToList"
2031
+ else:
2032
+ util_code_name = "UnknownTypeSliceToList"
2033
+ if not func_name:
2034
+ func_name = "__Pyx_MatchCase_%s" % util_code_name
2035
+ if util_code_name:
2036
+ util_code = UtilityCode.load_cached(
2037
+ util_code_name,
2038
+ "MatchCase.c"
2039
+ )
2040
+ else:
2041
+ util_code = None
2042
+ start = self.start if self.start else ExprNodes.IntNode.for_size(self.pos, 0)
2043
+ stop = self.get_stop()
2044
+ return ExprNodes.PythonCapiCallNode(
2045
+ self.pos,
2046
+ func_name,
2047
+ self.Pyx_iterable_to_list_type,
2048
+ utility_code=util_code,
2049
+ args=[self.base, start, stop],
2050
+ )
2051
+
2052
+ def analyse_types(self, env):
2053
+ self.base = self.base.analyse_types(env)
2054
+ if self.base.type.is_memoryviewslice:
2055
+ result = self.generate_for_memoryview(env)
2056
+ elif self.base.type.is_pyobject:
2057
+ result = self.generate_for_pyobject()
2058
+ else:
2059
+ # Some other type (probably a ctuple).
2060
+ # Just slice it, copy it to a list and hope it works
2061
+ result = self.generate_via_slicing(env)
2062
+ return result.analyse_types(env)
2063
+
2064
+
2065
+ class LazyCoerceToPyObject(ExprNodes.ExprNode):
2066
+ """
2067
+ Just calls "self.arg.coerce_to_pyobject" when it's analysed,
2068
+ so doesn't need 'env' when it's created
2069
+ arg - ExprNode
2070
+ """
2071
+ subexprs = ["arg"]
2072
+ type = PyrexTypes.py_object_type
2073
+
2074
+ def analyse_types(self, env):
2075
+ return self.arg.analyse_types(env).coerce_to_pyobject(env)
2076
+
2077
+
2078
+ class LazyCoerceToBool(ExprNodes.ExprNode):
2079
+ """
2080
+ Just calls "self.arg.analyse_boolean_expression"
2081
+ when it's analysed, so doesn't need 'env' when it's created
2082
+ arg - ExprNode
2083
+ """
2084
+ subexprs = ["arg"]
2085
+ type = PyrexTypes.c_bint_type
2086
+
2087
+ def analyse_types(self, env):
2088
+ return self.arg.analyse_boolean_expression(env)
2089
+
2090
+ def generate_binop_tree_from_list(pos, operator, list_of_tests):
2091
+ """
2092
+ Given a list of operands generates a roughly balanced tree:
2093
+ (test1 op test2) op (test3 op test4)
2094
+ This is better than (((test1 op test2) op test3) op test4)
2095
+ because it generates a shallower tree of nodes so is
2096
+ less likely to overflow the compiler
2097
+ """
2098
+ len_tests = len(list_of_tests)
2099
+ if len_tests == 1:
2100
+ return list_of_tests[0]
2101
+ else:
2102
+ split_idx = len_tests // 2
2103
+ operand1 = generate_binop_tree_from_list(
2104
+ pos, operator, list_of_tests[:split_idx]
2105
+ )
2106
+ operand2 = generate_binop_tree_from_list(
2107
+ pos, operator, list_of_tests[split_idx:]
2108
+ )
2109
+ return ExprNodes.binop_node(
2110
+ pos,
2111
+ operator=operator,
2112
+ operand1=operand1,
2113
+ operand2=operand2
2114
+ )
2115
+
2116
+
2117
+ class EvaluateWithKeysAndSubjectsArrays(ExprNodes.ExprNode):
2118
+ """
2119
+ This is responsible for setting up up the arrays of
2120
+ subjects and keys that are used in
2121
+ the function calls that handle these types of patterns
2122
+ (mapping and class patterns).
2123
+ Essentially 'arg' is evaluated with the keys and subjects
2124
+ arrays.
2125
+ """
2126
+ subexprs = ["keys_array", "arg"]
2127
+
2128
+ keys_array_cname = "__pyx_match_mapping_keys"
2129
+ subjects_array_cname = "__pyx_match_mapping_subjects"
2130
+
2131
+ is_temp = True
2132
+
2133
+ @property
2134
+ def type(self):
2135
+ return self.arg.type
2136
+
2137
+ @classmethod
2138
+ def make_keys_node(cls, pos):
2139
+ return ExprNodes.RawCNameExprNode(
2140
+ pos,
2141
+ type=PyrexTypes.c_void_ptr_type,
2142
+ cname=cls.keys_array_cname
2143
+ )
2144
+
2145
+ @classmethod
2146
+ def make_subjects_node(cls, pos):
2147
+ return ExprNodes.RawCNameExprNode(
2148
+ pos,
2149
+ type=PyrexTypes.c_void_ptr_ptr_type,
2150
+ cname=cls.subjects_array_cname
2151
+ )
2152
+
2153
+ def analyse_types(self, env):
2154
+ self.arg = self.arg.analyse_types(env)
2155
+ assert self.arg.type is PyrexTypes.c_bint_type
2156
+ self.keys_array = [
2157
+ key.analyse_types(env).coerce_to_pyobject(env).coerce_to_simple(env)
2158
+ for key in self.keys_array
2159
+ ]
2160
+ return self
2161
+
2162
+ def generate_evaluation_code(self, code):
2163
+ for k in self.keys_array:
2164
+ k.generate_evaluation_code(code)
2165
+
2166
+ code.mark_pos(self.pos)
2167
+ if self.is_temp:
2168
+ self.allocate_temp_result(code)
2169
+
2170
+ code.putln("{")
2171
+ keys_str = ", ".join(k.result() for k in self.keys_array)
2172
+ if not keys_str:
2173
+ # GCC gets worried about overflow if we pass
2174
+ # a genuinely empty array
2175
+ keys_str = "NULL"
2176
+ code.putln("PyObject *%s[] = {%s};" % (
2177
+ EvaluateWithKeysAndSubjectsArrays.keys_array_cname,
2178
+ keys_str,
2179
+ ))
2180
+
2181
+ if self.subjects_array:
2182
+ subjects_str = ", ".join(
2183
+ "&"+subject.result() if subject is not None else "NULL"
2184
+ for subject in self.subjects_array
2185
+ )
2186
+ if not subjects_str:
2187
+ # GCC gets worried about overflow if we pass
2188
+ # a genuinely empty array
2189
+ subjects_str = "NULL"
2190
+ code.putln("PyObject **%s[] = {%s};" % (
2191
+ EvaluateWithKeysAndSubjectsArrays.subjects_array_cname,
2192
+ subjects_str
2193
+ ))
2194
+
2195
+ self.arg.generate_evaluation_code(code)
2196
+ code.putln(f"{self.result()} = {self.arg.result()};")
2197
+
2198
+ code.putln("}")
2199
+ self.generate_subexpr_disposal_code(code)
2200
+ self.free_subexpr_temps(code)