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.
- Cython/Build/BuildExecutable.py +156 -0
- Cython/Build/Cache.py +198 -0
- Cython/Build/Cythonize.py +349 -0
- Cython/Build/Dependencies.py +1320 -0
- Cython/Build/Distutils.py +1 -0
- Cython/Build/Inline.py +467 -0
- Cython/Build/IpythonMagic.py +559 -0
- Cython/Build/SharedModule.py +165 -0
- Cython/Build/Tests/TestCyCache.py +222 -0
- Cython/Build/Tests/TestCythonizeArgsParser.py +480 -0
- Cython/Build/Tests/TestDependencies.py +133 -0
- Cython/Build/Tests/TestInline.py +177 -0
- Cython/Build/Tests/TestIpythonMagic.py +303 -0
- Cython/Build/Tests/TestRecythonize.py +212 -0
- Cython/Build/Tests/TestStripLiterals.py +155 -0
- Cython/Build/Tests/__init__.py +1 -0
- Cython/Build/__init__.py +11 -0
- Cython/CodeWriter.py +825 -0
- Cython/Compiler/AnalysedTreeTransforms.py +97 -0
- Cython/Compiler/Annotate.py +328 -0
- Cython/Compiler/AutoDocTransforms.py +320 -0
- Cython/Compiler/Buffer.py +679 -0
- Cython/Compiler/Builtin.py +1102 -0
- Cython/Compiler/CmdLine.py +373 -0
- Cython/Compiler/Code.pxd +154 -0
- Cython/Compiler/Code.py +3760 -0
- Cython/Compiler/CodeGeneration.py +33 -0
- Cython/Compiler/CythonScope.py +208 -0
- Cython/Compiler/Dataclass.py +890 -0
- Cython/Compiler/DebugFlags.py +24 -0
- Cython/Compiler/Errors.py +310 -0
- Cython/Compiler/ExprNodes.py +16319 -0
- Cython/Compiler/FlowControl.pxd +112 -0
- Cython/Compiler/FlowControl.py +1575 -0
- Cython/Compiler/FusedNode.py +978 -0
- Cython/Compiler/Future.py +16 -0
- Cython/Compiler/Interpreter.py +57 -0
- Cython/Compiler/Lexicon.py +442 -0
- Cython/Compiler/LineTable.py +114 -0
- Cython/Compiler/Main.py +879 -0
- Cython/Compiler/MatchCaseNodes.py +2200 -0
- Cython/Compiler/MemoryView.py +930 -0
- Cython/Compiler/ModuleNode.py +4558 -0
- Cython/Compiler/Naming.py +370 -0
- Cython/Compiler/Nodes.py +11334 -0
- Cython/Compiler/Optimize.py +5563 -0
- Cython/Compiler/Options.py +841 -0
- Cython/Compiler/ParseTreeTransforms.pxd +80 -0
- Cython/Compiler/ParseTreeTransforms.py +4808 -0
- Cython/Compiler/Parsing.pxd +9 -0
- Cython/Compiler/Parsing.py +5442 -0
- Cython/Compiler/Pipeline.py +439 -0
- Cython/Compiler/PyrexTypes.py +6588 -0
- Cython/Compiler/Pythran.py +232 -0
- Cython/Compiler/Scanning.pxd +74 -0
- Cython/Compiler/Scanning.py +771 -0
- Cython/Compiler/StringEncoding.py +354 -0
- Cython/Compiler/Symtab.py +3122 -0
- Cython/Compiler/Tests/TestBuffer.py +105 -0
- Cython/Compiler/Tests/TestBuiltin.py +196 -0
- Cython/Compiler/Tests/TestCmdLine.py +652 -0
- Cython/Compiler/Tests/TestCode.py +145 -0
- Cython/Compiler/Tests/TestFlowControl.py +65 -0
- Cython/Compiler/Tests/TestGrammar.py +202 -0
- Cython/Compiler/Tests/TestMemView.py +71 -0
- Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
- Cython/Compiler/Tests/TestScanning.py +132 -0
- Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
- Cython/Compiler/Tests/TestStringEncoding.py +20 -0
- Cython/Compiler/Tests/TestTreeFragment.py +63 -0
- Cython/Compiler/Tests/TestTreePath.py +103 -0
- Cython/Compiler/Tests/TestTypes.py +119 -0
- Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
- Cython/Compiler/Tests/TestVisitor.py +119 -0
- Cython/Compiler/Tests/Utils.py +36 -0
- Cython/Compiler/Tests/__init__.py +1 -0
- Cython/Compiler/TreeFragment.py +279 -0
- Cython/Compiler/TreePath.py +303 -0
- Cython/Compiler/TypeInference.py +626 -0
- Cython/Compiler/TypeSlots.py +1330 -0
- Cython/Compiler/UFuncs.py +317 -0
- Cython/Compiler/UtilNodes.py +389 -0
- Cython/Compiler/UtilityCode.py +354 -0
- Cython/Compiler/Version.py +8 -0
- Cython/Compiler/Visitor.pxd +52 -0
- Cython/Compiler/Visitor.py +914 -0
- Cython/Compiler/__init__.py +1 -0
- Cython/Coverage.py +448 -0
- Cython/Debugger/Cygdb.py +214 -0
- Cython/Debugger/DebugWriter.py +82 -0
- Cython/Debugger/Tests/TestLibCython.py +280 -0
- Cython/Debugger/Tests/__init__.py +1 -0
- Cython/Debugger/Tests/cfuncs.c +8 -0
- Cython/Debugger/Tests/codefile +49 -0
- Cython/Debugger/Tests/test_libcython_in_gdb.py +580 -0
- Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
- Cython/Debugger/__init__.py +1 -0
- Cython/Debugger/libcython.py +1548 -0
- Cython/Debugger/libpython.py +2821 -0
- Cython/Debugging.py +20 -0
- Cython/Distutils/__init__.py +2 -0
- Cython/Distutils/build_ext.py +143 -0
- Cython/Distutils/extension.py +96 -0
- Cython/Distutils/old_build_ext.py +351 -0
- Cython/Includes/cpython/__init__.pxd +173 -0
- Cython/Includes/cpython/array.pxd +159 -0
- Cython/Includes/cpython/bool.pxd +37 -0
- Cython/Includes/cpython/buffer.pxd +112 -0
- Cython/Includes/cpython/bytearray.pxd +33 -0
- Cython/Includes/cpython/bytes.pxd +200 -0
- Cython/Includes/cpython/cellobject.pxd +35 -0
- Cython/Includes/cpython/ceval.pxd +8 -0
- Cython/Includes/cpython/codecs.pxd +121 -0
- Cython/Includes/cpython/complex.pxd +60 -0
- Cython/Includes/cpython/contextvars.pxd +145 -0
- Cython/Includes/cpython/conversion.pxd +36 -0
- Cython/Includes/cpython/datetime.pxd +395 -0
- Cython/Includes/cpython/descr.pxd +26 -0
- Cython/Includes/cpython/dict.pxd +268 -0
- Cython/Includes/cpython/exc.pxd +263 -0
- Cython/Includes/cpython/fileobject.pxd +57 -0
- Cython/Includes/cpython/float.pxd +56 -0
- Cython/Includes/cpython/frozendict.pxd +37 -0
- Cython/Includes/cpython/function.pxd +65 -0
- Cython/Includes/cpython/genobject.pxd +25 -0
- Cython/Includes/cpython/getargs.pxd +12 -0
- Cython/Includes/cpython/instance.pxd +25 -0
- Cython/Includes/cpython/iterator.pxd +36 -0
- Cython/Includes/cpython/iterobject.pxd +24 -0
- Cython/Includes/cpython/list.pxd +144 -0
- Cython/Includes/cpython/long.pxd +180 -0
- Cython/Includes/cpython/longintrepr.pxd +14 -0
- Cython/Includes/cpython/mapping.pxd +63 -0
- Cython/Includes/cpython/marshal.pxd +66 -0
- Cython/Includes/cpython/mem.pxd +120 -0
- Cython/Includes/cpython/memoryview.pxd +50 -0
- Cython/Includes/cpython/method.pxd +49 -0
- Cython/Includes/cpython/module.pxd +208 -0
- Cython/Includes/cpython/number.pxd +258 -0
- Cython/Includes/cpython/object.pxd +430 -0
- Cython/Includes/cpython/pycapsule.pxd +143 -0
- Cython/Includes/cpython/pylifecycle.pxd +68 -0
- Cython/Includes/cpython/pyport.pxd +8 -0
- Cython/Includes/cpython/pystate.pxd +95 -0
- Cython/Includes/cpython/pythread.pxd +53 -0
- Cython/Includes/cpython/ref.pxd +141 -0
- Cython/Includes/cpython/sentinel.pxd +17 -0
- Cython/Includes/cpython/sequence.pxd +134 -0
- Cython/Includes/cpython/set.pxd +119 -0
- Cython/Includes/cpython/slice.pxd +70 -0
- Cython/Includes/cpython/time.pxd +129 -0
- Cython/Includes/cpython/tuple.pxd +72 -0
- Cython/Includes/cpython/type.pxd +146 -0
- Cython/Includes/cpython/unicode.pxd +639 -0
- Cython/Includes/cpython/version.pxd +32 -0
- Cython/Includes/cpython/weakref.pxd +78 -0
- Cython/Includes/libc/__init__.pxd +1 -0
- Cython/Includes/libc/complex.pxd +35 -0
- Cython/Includes/libc/errno.pxd +127 -0
- Cython/Includes/libc/float.pxd +43 -0
- Cython/Includes/libc/limits.pxd +28 -0
- Cython/Includes/libc/locale.pxd +46 -0
- Cython/Includes/libc/math.pxd +209 -0
- Cython/Includes/libc/setjmp.pxd +10 -0
- Cython/Includes/libc/signal.pxd +64 -0
- Cython/Includes/libc/stdbool.pxd +5 -0
- Cython/Includes/libc/stddef.pxd +9 -0
- Cython/Includes/libc/stdint.pxd +105 -0
- Cython/Includes/libc/stdio.pxd +80 -0
- Cython/Includes/libc/stdlib.pxd +72 -0
- Cython/Includes/libc/string.pxd +50 -0
- Cython/Includes/libc/threads.pxd +234 -0
- Cython/Includes/libc/time.pxd +52 -0
- Cython/Includes/libcpp/__init__.pxd +4 -0
- Cython/Includes/libcpp/algorithm.pxd +320 -0
- Cython/Includes/libcpp/any.pxd +16 -0
- Cython/Includes/libcpp/atomic.pxd +59 -0
- Cython/Includes/libcpp/barrier.pxd +22 -0
- Cython/Includes/libcpp/bit.pxd +29 -0
- Cython/Includes/libcpp/cast.pxd +12 -0
- Cython/Includes/libcpp/cmath.pxd +518 -0
- Cython/Includes/libcpp/complex.pxd +106 -0
- Cython/Includes/libcpp/condition_variable.pxd +322 -0
- Cython/Includes/libcpp/deque.pxd +165 -0
- Cython/Includes/libcpp/exception.pxd +216 -0
- Cython/Includes/libcpp/execution.pxd +15 -0
- Cython/Includes/libcpp/forward_list.pxd +63 -0
- Cython/Includes/libcpp/functional.pxd +26 -0
- Cython/Includes/libcpp/future.pxd +103 -0
- Cython/Includes/libcpp/iterator.pxd +34 -0
- Cython/Includes/libcpp/latch.pxd +17 -0
- Cython/Includes/libcpp/limits.pxd +61 -0
- Cython/Includes/libcpp/list.pxd +117 -0
- Cython/Includes/libcpp/map.pxd +252 -0
- Cython/Includes/libcpp/memory.pxd +115 -0
- Cython/Includes/libcpp/mutex.pxd +387 -0
- Cython/Includes/libcpp/numbers.pxd +15 -0
- Cython/Includes/libcpp/numeric.pxd +131 -0
- Cython/Includes/libcpp/optional.pxd +34 -0
- Cython/Includes/libcpp/pair.pxd +1 -0
- Cython/Includes/libcpp/queue.pxd +25 -0
- Cython/Includes/libcpp/random.pxd +166 -0
- Cython/Includes/libcpp/semaphore.pxd +43 -0
- Cython/Includes/libcpp/set.pxd +228 -0
- Cython/Includes/libcpp/shared_mutex.pxd +96 -0
- Cython/Includes/libcpp/span.pxd +87 -0
- Cython/Includes/libcpp/stack.pxd +11 -0
- Cython/Includes/libcpp/stop_token.pxd +117 -0
- Cython/Includes/libcpp/string.pxd +355 -0
- Cython/Includes/libcpp/string_view.pxd +183 -0
- Cython/Includes/libcpp/typeindex.pxd +15 -0
- Cython/Includes/libcpp/typeinfo.pxd +10 -0
- Cython/Includes/libcpp/unordered_map.pxd +193 -0
- Cython/Includes/libcpp/unordered_set.pxd +152 -0
- Cython/Includes/libcpp/utility.pxd +30 -0
- Cython/Includes/libcpp/vector.pxd +186 -0
- Cython/Includes/numpy/math.pxd +150 -0
- Cython/Includes/openmp.pxd +50 -0
- Cython/Includes/posix/__init__.pxd +1 -0
- Cython/Includes/posix/dlfcn.pxd +14 -0
- Cython/Includes/posix/fcntl.pxd +86 -0
- Cython/Includes/posix/ioctl.pxd +4 -0
- Cython/Includes/posix/mman.pxd +101 -0
- Cython/Includes/posix/resource.pxd +57 -0
- Cython/Includes/posix/select.pxd +21 -0
- Cython/Includes/posix/signal.pxd +73 -0
- Cython/Includes/posix/stat.pxd +98 -0
- Cython/Includes/posix/stdio.pxd +37 -0
- Cython/Includes/posix/stdlib.pxd +29 -0
- Cython/Includes/posix/strings.pxd +9 -0
- Cython/Includes/posix/time.pxd +71 -0
- Cython/Includes/posix/types.pxd +30 -0
- Cython/Includes/posix/uio.pxd +26 -0
- Cython/Includes/posix/unistd.pxd +271 -0
- Cython/Includes/posix/wait.pxd +38 -0
- Cython/LZSS.py +184 -0
- Cython/Plex/Actions.pxd +24 -0
- Cython/Plex/Actions.py +119 -0
- Cython/Plex/DFA.pxd +14 -0
- Cython/Plex/DFA.py +164 -0
- Cython/Plex/Errors.py +48 -0
- Cython/Plex/Lexicons.py +178 -0
- Cython/Plex/Machines.pxd +36 -0
- Cython/Plex/Machines.py +238 -0
- Cython/Plex/Regexps.py +535 -0
- Cython/Plex/Scanners.pxd +45 -0
- Cython/Plex/Scanners.py +328 -0
- Cython/Plex/Transitions.pxd +14 -0
- Cython/Plex/Transitions.py +239 -0
- Cython/Plex/__init__.py +34 -0
- Cython/Runtime/__init__.py +1 -0
- Cython/Runtime/refnanny.pyx +237 -0
- Cython/Shadow.py +1172 -0
- Cython/StringIOTree.py +169 -0
- Cython/Tempita/__init__.py +4 -0
- Cython/Tempita/_looper.py +154 -0
- Cython/Tempita/_tempita.py +1087 -0
- Cython/TestUtils.py +472 -0
- Cython/Tests/TestCodeWriter.py +128 -0
- Cython/Tests/TestCythonUtils.py +237 -0
- Cython/Tests/TestJediTyper.py +223 -0
- Cython/Tests/TestShadow.py +125 -0
- Cython/Tests/TestStringIOTree.py +68 -0
- Cython/Tests/TestTestUtils.py +89 -0
- Cython/Tests/__init__.py +1 -0
- Cython/Tests/xmlrunner.py +390 -0
- Cython/Utility/AsyncGen.c +1152 -0
- Cython/Utility/Buffer.c +866 -0
- Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
- Cython/Utility/Builtins.c +1068 -0
- Cython/Utility/CConvert.pyx +153 -0
- Cython/Utility/CMath.c +104 -0
- Cython/Utility/CommonStructures.c +244 -0
- Cython/Utility/Complex.c +378 -0
- Cython/Utility/Coroutine.c +2344 -0
- Cython/Utility/CpdefEnums.pyx +119 -0
- Cython/Utility/CppConvert.pyx +284 -0
- Cython/Utility/CppSupport.cpp +151 -0
- Cython/Utility/CythonFunction.c +2143 -0
- Cython/Utility/Dataclasses.c +101 -0
- Cython/Utility/Embed.c +129 -0
- Cython/Utility/Exceptions.c +1331 -0
- Cython/Utility/Exceptions_Cy.pyx +109 -0
- Cython/Utility/ExtensionTypes.c +1199 -0
- Cython/Utility/FunctionArguments.c +1052 -0
- Cython/Utility/FusedFunction.pyx +44 -0
- Cython/Utility/ImportExport.c +972 -0
- Cython/Utility/MatchCase.c +985 -0
- Cython/Utility/MatchCase_Cy.pyx +12 -0
- Cython/Utility/MemoryView.pxd +108 -0
- Cython/Utility/MemoryView.pyx +1499 -0
- Cython/Utility/MemoryView_C.c +1056 -0
- Cython/Utility/ModuleSetupCode.c +3349 -0
- Cython/Utility/NumpyImportArray.c +46 -0
- Cython/Utility/ObjectHandling.c +3404 -0
- Cython/Utility/Optimize.c +2575 -0
- Cython/Utility/Overflow.c +378 -0
- Cython/Utility/Profile.c +736 -0
- Cython/Utility/StringTools.c +1534 -0
- Cython/Utility/Synchronization.c +438 -0
- Cython/Utility/TString.c +369 -0
- Cython/Utility/TestCyUtilityLoader.pyx +8 -0
- Cython/Utility/TestCythonScope.pyx +75 -0
- Cython/Utility/TestUtilityLoader.c +12 -0
- Cython/Utility/TypeConversion.c +1588 -0
- Cython/Utility/UFuncs.pyx +50 -0
- Cython/Utility/UFuncs_C.c +89 -0
- Cython/Utility/__init__.py +28 -0
- Cython/Utility/arrayarray.h +190 -0
- Cython/Utils.py +682 -0
- Cython/__init__.py +12 -0
- Cython/py.typed +0 -0
- cython.py +29 -0
- oxython-0.1.0.dist-info/METADATA +138 -0
- oxython-0.1.0.dist-info/RECORD +321 -0
- oxython-0.1.0.dist-info/WHEEL +5 -0
- oxython-0.1.0.dist-info/entry_points.txt +4 -0
- oxython-0.1.0.dist-info/top_level.txt +3 -0
- pyximport/__init__.py +4 -0
- pyximport/pyxbuild.py +160 -0
- 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)
|