Cython 3.2.0__cp39-abi3-win32.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 +169 -0
- Cython/Build/Cache.py +199 -0
- Cython/Build/Cythonize.py +350 -0
- Cython/Build/Dependencies.py +1314 -0
- Cython/Build/Distutils.py +1 -0
- Cython/Build/Inline.py +463 -0
- Cython/Build/IpythonMagic.py +560 -0
- Cython/Build/SharedModule.py +94 -0
- Cython/Build/Tests/TestCyCache.py +194 -0
- Cython/Build/Tests/TestCythonizeArgsParser.py +481 -0
- Cython/Build/Tests/TestDependencies.py +133 -0
- Cython/Build/Tests/TestInline.py +177 -0
- Cython/Build/Tests/TestIpythonMagic.py +287 -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 +815 -0
- Cython/Compiler/AnalysedTreeTransforms.py +97 -0
- Cython/Compiler/Annotate.py +328 -0
- Cython/Compiler/AutoDocTransforms.py +320 -0
- Cython/Compiler/Buffer.py +680 -0
- Cython/Compiler/Builtin.py +984 -0
- Cython/Compiler/CmdLine.py +263 -0
- Cython/Compiler/Code.pxd +149 -0
- Cython/Compiler/Code.py +3746 -0
- Cython/Compiler/Code.pyd +0 -0
- Cython/Compiler/CodeGeneration.py +33 -0
- Cython/Compiler/CythonScope.py +191 -0
- Cython/Compiler/Dataclass.py +864 -0
- Cython/Compiler/DebugFlags.py +24 -0
- Cython/Compiler/Errors.py +297 -0
- Cython/Compiler/ExprNodes.py +15562 -0
- Cython/Compiler/FlowControl.pxd +97 -0
- Cython/Compiler/FlowControl.py +1451 -0
- Cython/Compiler/FlowControl.pyd +0 -0
- Cython/Compiler/FusedNode.py +971 -0
- Cython/Compiler/FusedNode.pyd +0 -0
- Cython/Compiler/Future.py +16 -0
- Cython/Compiler/Interpreter.py +57 -0
- Cython/Compiler/Lexicon.py +421 -0
- Cython/Compiler/LineTable.py +114 -0
- Cython/Compiler/LineTable.pyd +0 -0
- Cython/Compiler/Main.py +857 -0
- Cython/Compiler/MatchCaseNodes.py +259 -0
- Cython/Compiler/MemoryView.py +905 -0
- Cython/Compiler/ModuleNode.py +4235 -0
- Cython/Compiler/Naming.py +363 -0
- Cython/Compiler/Nodes.py +10831 -0
- Cython/Compiler/Optimize.py +5288 -0
- Cython/Compiler/Options.py +843 -0
- Cython/Compiler/ParseTreeTransforms.pxd +78 -0
- Cython/Compiler/ParseTreeTransforms.py +4638 -0
- Cython/Compiler/Parsing.pxd +9 -0
- Cython/Compiler/Parsing.py +4775 -0
- Cython/Compiler/Parsing.pyd +0 -0
- Cython/Compiler/Pipeline.py +439 -0
- Cython/Compiler/PyrexTypes.py +5870 -0
- Cython/Compiler/Pythran.py +232 -0
- Cython/Compiler/Scanning.pxd +48 -0
- Cython/Compiler/Scanning.py +701 -0
- Cython/Compiler/Scanning.pyd +0 -0
- Cython/Compiler/StringEncoding.py +298 -0
- Cython/Compiler/Symtab.py +3073 -0
- Cython/Compiler/Tests/TestBuffer.py +105 -0
- Cython/Compiler/Tests/TestBuiltin.py +72 -0
- Cython/Compiler/Tests/TestCmdLine.py +586 -0
- Cython/Compiler/Tests/TestCode.py +144 -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 +134 -0
- Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
- Cython/Compiler/Tests/TestStringEncoding.py +21 -0
- Cython/Compiler/Tests/TestTreeFragment.py +63 -0
- Cython/Compiler/Tests/TestTreePath.py +103 -0
- Cython/Compiler/Tests/TestTypes.py +75 -0
- Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
- Cython/Compiler/Tests/TestVisitor.py +61 -0
- Cython/Compiler/Tests/Utils.py +36 -0
- Cython/Compiler/Tests/__init__.py +1 -0
- Cython/Compiler/TreeFragment.py +278 -0
- Cython/Compiler/TreePath.py +303 -0
- Cython/Compiler/TypeInference.py +591 -0
- Cython/Compiler/TypeSlots.py +1174 -0
- Cython/Compiler/UFuncs.py +311 -0
- Cython/Compiler/UtilNodes.py +389 -0
- Cython/Compiler/UtilityCode.py +344 -0
- Cython/Compiler/Version.py +8 -0
- Cython/Compiler/Visitor.pxd +53 -0
- Cython/Compiler/Visitor.py +861 -0
- Cython/Compiler/Visitor.pyd +0 -0
- Cython/Compiler/__init__.py +1 -0
- Cython/Coverage.py +448 -0
- Cython/Debugger/Cygdb.py +177 -0
- Cython/Debugger/DebugWriter.py +82 -0
- Cython/Debugger/Tests/TestLibCython.py +275 -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 +578 -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 +139 -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 +178 -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 +187 -0
- Cython/Includes/cpython/exc.pxd +263 -0
- Cython/Includes/cpython/fileobject.pxd +57 -0
- Cython/Includes/cpython/float.pxd +47 -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 +92 -0
- Cython/Includes/cpython/long.pxd +149 -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 +433 -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/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 +53 -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/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 +86 -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/Plex/Actions.pxd +24 -0
- Cython/Plex/Actions.py +119 -0
- Cython/Plex/Actions.pyd +0 -0
- Cython/Plex/DFA.pxd +14 -0
- Cython/Plex/DFA.py +164 -0
- Cython/Plex/DFA.pyd +0 -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/Machines.pyd +0 -0
- Cython/Plex/Regexps.py +535 -0
- Cython/Plex/Scanners.pxd +45 -0
- Cython/Plex/Scanners.py +328 -0
- Cython/Plex/Scanners.pyd +0 -0
- Cython/Plex/Transitions.pxd +14 -0
- Cython/Plex/Transitions.py +239 -0
- Cython/Plex/Transitions.pyd +0 -0
- Cython/Plex/__init__.py +34 -0
- Cython/Runtime/__init__.py +1 -0
- Cython/Runtime/refnanny.pyd +0 -0
- Cython/Runtime/refnanny.pyx +237 -0
- Cython/Shadow.py +690 -0
- Cython/Shadow.pyi +521 -0
- Cython/StringIOTree.py +170 -0
- Cython/StringIOTree.pyd +0 -0
- Cython/Tempita/__init__.py +4 -0
- Cython/Tempita/_looper.py +154 -0
- Cython/Tempita/_tempita.py +1091 -0
- Cython/Tempita/_tempita.pyd +0 -0
- Cython/TestUtils.py +422 -0
- Cython/Tests/TestCodeWriter.py +128 -0
- Cython/Tests/TestCythonUtils.py +202 -0
- Cython/Tests/TestJediTyper.py +223 -0
- Cython/Tests/TestShadow.py +114 -0
- Cython/Tests/TestStringIOTree.py +67 -0
- Cython/Tests/TestTestUtils.py +90 -0
- Cython/Tests/__init__.py +1 -0
- Cython/Tests/xmlrunner.py +390 -0
- Cython/Utility/AsyncGen.c +1031 -0
- Cython/Utility/Buffer.c +865 -0
- Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
- Cython/Utility/Builtins.c +810 -0
- Cython/Utility/CConvert.pyx +134 -0
- Cython/Utility/CMath.c +104 -0
- Cython/Utility/CommonStructures.c +226 -0
- Cython/Utility/Complex.c +378 -0
- Cython/Utility/Coroutine.c +2300 -0
- Cython/Utility/CpdefEnums.pyx +103 -0
- Cython/Utility/CppConvert.pyx +282 -0
- Cython/Utility/CppSupport.cpp +151 -0
- Cython/Utility/CythonFunction.c +1832 -0
- Cython/Utility/Dataclasses.c +101 -0
- Cython/Utility/Embed.c +121 -0
- Cython/Utility/Exceptions.c +1016 -0
- Cython/Utility/ExtensionTypes.c +996 -0
- Cython/Utility/FunctionArguments.c +1043 -0
- Cython/Utility/FusedFunction.pyx +44 -0
- Cython/Utility/ImportExport.c +907 -0
- Cython/Utility/MemoryView.pxd +188 -0
- Cython/Utility/MemoryView.pyx +1482 -0
- Cython/Utility/MemoryView_C.c +927 -0
- Cython/Utility/ModuleSetupCode.c +3203 -0
- Cython/Utility/NumpyImportArray.c +46 -0
- Cython/Utility/ObjectHandling.c +3273 -0
- Cython/Utility/Optimize.c +1603 -0
- Cython/Utility/Overflow.c +384 -0
- Cython/Utility/Printing.c +86 -0
- Cython/Utility/Profile.c +732 -0
- Cython/Utility/StringTools.c +1379 -0
- Cython/Utility/Synchronization.c +399 -0
- Cython/Utility/TString.c +356 -0
- Cython/Utility/TestCyUtilityLoader.pyx +8 -0
- Cython/Utility/TestCythonScope.pyx +75 -0
- Cython/Utility/TestUtilityLoader.c +12 -0
- Cython/Utility/TypeConversion.c +1385 -0
- Cython/Utility/UFuncs.pyx +50 -0
- Cython/Utility/UFuncs_C.c +89 -0
- Cython/Utility/__init__.py +28 -0
- Cython/Utility/arrayarray.h +167 -0
- Cython/Utils.py +687 -0
- Cython/Utils.pyd +0 -0
- Cython/__init__.py +10 -0
- Cython/__init__.pyi +7 -0
- Cython/py.typed +0 -0
- cython-3.2.0.dist-info/METADATA +85 -0
- cython-3.2.0.dist-info/RECORD +333 -0
- cython-3.2.0.dist-info/WHEEL +5 -0
- cython-3.2.0.dist-info/entry_points.txt +4 -0
- cython-3.2.0.dist-info/top_level.txt +3 -0
- cython.py +29 -0
- pyximport/__init__.py +4 -0
- pyximport/pyxbuild.py +160 -0
- pyximport/pyximport.py +482 -0
|
@@ -0,0 +1,4775 @@
|
|
|
1
|
+
# cython: auto_cpdef=True, infer_types=True, py2_import=True
|
|
2
|
+
#
|
|
3
|
+
# Parser
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# This should be done automatically
|
|
8
|
+
import cython
|
|
9
|
+
cython.declare(Nodes=object, ExprNodes=object, EncodedString=object,
|
|
10
|
+
bytes_literal=object, StringEncoding=object,
|
|
11
|
+
FileSourceDescriptor=object, lookup_unicodechar=object,
|
|
12
|
+
Future=object, Options=object, error=object, warning=object,
|
|
13
|
+
Builtin=object, ModuleNode=object, Utils=object, _unicode=object, _bytes=object,
|
|
14
|
+
re=object, _parse_escape_sequences=object, _parse_escape_sequences_raw=object,
|
|
15
|
+
partial=object, reduce=object,
|
|
16
|
+
_CDEF_MODIFIERS=tuple, COMMON_BINOP_MISTAKES=dict)
|
|
17
|
+
|
|
18
|
+
from io import StringIO
|
|
19
|
+
import re
|
|
20
|
+
from unicodedata import lookup as lookup_unicodechar
|
|
21
|
+
from functools import partial, reduce
|
|
22
|
+
|
|
23
|
+
from .Scanning import PyrexScanner, FileSourceDescriptor, tentatively_scan
|
|
24
|
+
from . import Nodes
|
|
25
|
+
from . import ExprNodes
|
|
26
|
+
from . import MatchCaseNodes
|
|
27
|
+
from . import Builtin
|
|
28
|
+
from . import StringEncoding
|
|
29
|
+
from .StringEncoding import EncodedString, bytes_literal
|
|
30
|
+
from .ModuleNode import ModuleNode
|
|
31
|
+
from .Errors import error, warning, CompileError
|
|
32
|
+
from .. import Utils
|
|
33
|
+
from . import Future
|
|
34
|
+
from . import Options
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_CDEF_MODIFIERS = ('inline', 'nogil', 'api')
|
|
38
|
+
statement_terminators = cython.declare(frozenset, frozenset((
|
|
39
|
+
';', 'NEWLINE', 'EOF')))
|
|
40
|
+
|
|
41
|
+
class Ctx:
|
|
42
|
+
# Parsing context
|
|
43
|
+
level = 'other'
|
|
44
|
+
visibility = 'private'
|
|
45
|
+
cdef_flag = False
|
|
46
|
+
typedef_flag = False
|
|
47
|
+
api = False
|
|
48
|
+
overridable = False
|
|
49
|
+
nogil = False
|
|
50
|
+
namespace = None
|
|
51
|
+
templates = None
|
|
52
|
+
allow_struct_enum_decorator = False
|
|
53
|
+
|
|
54
|
+
def __init__(self, **kwds):
|
|
55
|
+
self.__dict__.update(kwds)
|
|
56
|
+
|
|
57
|
+
def __call__(self, **kwds):
|
|
58
|
+
ctx = Ctx()
|
|
59
|
+
d = ctx.__dict__
|
|
60
|
+
d.update(self.__dict__)
|
|
61
|
+
d.update(kwds)
|
|
62
|
+
return ctx
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@cython.cfunc
|
|
66
|
+
def p_ident(s: PyrexScanner, message="Expected an identifier"):
|
|
67
|
+
if s.sy == 'IDENT':
|
|
68
|
+
name = s.context.intern_ustring(s.systring)
|
|
69
|
+
s.next()
|
|
70
|
+
return name
|
|
71
|
+
else:
|
|
72
|
+
s.error(message)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@cython.cfunc
|
|
76
|
+
def p_ident_list(s: PyrexScanner):
|
|
77
|
+
names = []
|
|
78
|
+
while s.sy == 'IDENT':
|
|
79
|
+
names.append(s.context.intern_ustring(s.systring))
|
|
80
|
+
s.next()
|
|
81
|
+
if s.sy != ',':
|
|
82
|
+
break
|
|
83
|
+
s.next()
|
|
84
|
+
return names
|
|
85
|
+
|
|
86
|
+
#------------------------------------------
|
|
87
|
+
#
|
|
88
|
+
# Expressions
|
|
89
|
+
#
|
|
90
|
+
#------------------------------------------
|
|
91
|
+
|
|
92
|
+
@cython.cfunc
|
|
93
|
+
def p_binop_operator(s: PyrexScanner) -> tuple:
|
|
94
|
+
pos = s.position()
|
|
95
|
+
op = s.sy
|
|
96
|
+
s.next()
|
|
97
|
+
return op, pos
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# signature is currently overridden in pxd file
|
|
101
|
+
def p_binop_expr(s: PyrexScanner, ops, p_sub_expr):
|
|
102
|
+
n1 = p_sub_expr(s)
|
|
103
|
+
while s.sy in ops:
|
|
104
|
+
op, pos = p_binop_operator(s)
|
|
105
|
+
n2 = p_sub_expr(s)
|
|
106
|
+
n1 = ExprNodes.binop_node(pos, op, n1, n2)
|
|
107
|
+
if op == '/':
|
|
108
|
+
if Future.division in s.context.future_directives:
|
|
109
|
+
n1.truedivision = True
|
|
110
|
+
else:
|
|
111
|
+
n1.truedivision = None # unknown
|
|
112
|
+
return n1
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
#lambdef: 'lambda' [varargslist] ':' test
|
|
116
|
+
|
|
117
|
+
@cython.cfunc
|
|
118
|
+
def p_lambdef(s: PyrexScanner):
|
|
119
|
+
# s.sy == 'lambda'
|
|
120
|
+
pos = s.position()
|
|
121
|
+
s.next()
|
|
122
|
+
if s.sy == ':':
|
|
123
|
+
args = []
|
|
124
|
+
star_arg = starstar_arg = None
|
|
125
|
+
else:
|
|
126
|
+
args, star_arg, starstar_arg = p_varargslist(
|
|
127
|
+
s, terminator=':', annotated=False)
|
|
128
|
+
s.expect(':')
|
|
129
|
+
expr = p_test(s)
|
|
130
|
+
return ExprNodes.LambdaNode(
|
|
131
|
+
pos, args = args,
|
|
132
|
+
star_arg = star_arg, starstar_arg = starstar_arg,
|
|
133
|
+
result_expr = expr)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
#test: or_test ['if' or_test 'else' test] | lambdef
|
|
137
|
+
|
|
138
|
+
@cython.cfunc
|
|
139
|
+
def p_test(s: PyrexScanner):
|
|
140
|
+
# The check for a following ':=' is only for error reporting purposes.
|
|
141
|
+
# It simply changes a
|
|
142
|
+
# expected ')', found ':='
|
|
143
|
+
# message into something a bit more descriptive.
|
|
144
|
+
# It is close to what the PEG parser does in CPython, where an expression has
|
|
145
|
+
# a lookahead assertion that it isn't followed by ':='
|
|
146
|
+
expr = p_test_allow_walrus_after(s)
|
|
147
|
+
if s.sy == ':=':
|
|
148
|
+
s.error("invalid syntax: assignment expression not allowed in this context")
|
|
149
|
+
return expr
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@cython.cfunc
|
|
153
|
+
def p_test_allow_walrus_after(s: PyrexScanner):
|
|
154
|
+
if s.sy == 'lambda':
|
|
155
|
+
return p_lambdef(s)
|
|
156
|
+
pos = s.position()
|
|
157
|
+
expr = p_or_test(s)
|
|
158
|
+
if s.sy == 'if':
|
|
159
|
+
s.next()
|
|
160
|
+
test = p_or_test(s)
|
|
161
|
+
s.expect('else')
|
|
162
|
+
other = p_test(s)
|
|
163
|
+
return ExprNodes.CondExprNode(pos, test=test, true_val=expr, false_val=other)
|
|
164
|
+
else:
|
|
165
|
+
return expr
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@cython.cfunc
|
|
169
|
+
def p_namedexpr_test(s: PyrexScanner):
|
|
170
|
+
# defined in the LL parser as
|
|
171
|
+
# namedexpr_test: test [':=' test]
|
|
172
|
+
# The requirement that the LHS is a name is not enforced in the grammar.
|
|
173
|
+
# For comparison the PEG parser does:
|
|
174
|
+
# 1. look for "name :=", if found it's definitely a named expression
|
|
175
|
+
# so look for expression
|
|
176
|
+
# 2. Otherwise, look for expression
|
|
177
|
+
lhs = p_test_allow_walrus_after(s)
|
|
178
|
+
if s.sy == ':=':
|
|
179
|
+
position = s.position()
|
|
180
|
+
if not lhs.is_name:
|
|
181
|
+
s.error("Left-hand side of assignment expression must be an identifier", fatal=False)
|
|
182
|
+
s.next()
|
|
183
|
+
rhs = p_test(s)
|
|
184
|
+
return ExprNodes.AssignmentExpressionNode(position, lhs=lhs, rhs=rhs)
|
|
185
|
+
return lhs
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
#or_test: and_test ('or' and_test)*
|
|
189
|
+
|
|
190
|
+
COMMON_BINOP_MISTAKES = {'||': 'or', '&&': 'and'}
|
|
191
|
+
|
|
192
|
+
@cython.cfunc
|
|
193
|
+
def p_or_test(s: PyrexScanner):
|
|
194
|
+
return p_rassoc_binop_expr(s, 'or', p_and_test)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# signature is currently overridden in pxd file
|
|
198
|
+
def p_rassoc_binop_expr(s: PyrexScanner, op, p_subexpr):
|
|
199
|
+
n1 = p_subexpr(s)
|
|
200
|
+
if s.sy == op:
|
|
201
|
+
pos = s.position()
|
|
202
|
+
op = s.sy
|
|
203
|
+
s.next()
|
|
204
|
+
n2 = p_rassoc_binop_expr(s, op, p_subexpr)
|
|
205
|
+
n1 = ExprNodes.binop_node(pos, op, n1, n2)
|
|
206
|
+
elif s.sy in COMMON_BINOP_MISTAKES and COMMON_BINOP_MISTAKES[s.sy] == op:
|
|
207
|
+
# Only report this for the current operator since we pass through here twice for 'and' and 'or'.
|
|
208
|
+
warning(s.position(),
|
|
209
|
+
"Found the C operator '%s', did you mean the Python operator '%s'?" % (s.sy, op),
|
|
210
|
+
level=1)
|
|
211
|
+
return n1
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
#and_test: not_test ('and' not_test)*
|
|
215
|
+
|
|
216
|
+
@cython.cfunc
|
|
217
|
+
def p_and_test(s: PyrexScanner):
|
|
218
|
+
#return p_binop_expr(s, ('and',), p_not_test)
|
|
219
|
+
return p_rassoc_binop_expr(s, 'and', p_not_test)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
#not_test: 'not' not_test | comparison
|
|
223
|
+
|
|
224
|
+
@cython.cfunc
|
|
225
|
+
def p_not_test(s: PyrexScanner):
|
|
226
|
+
if s.sy == 'not':
|
|
227
|
+
pos = s.position()
|
|
228
|
+
s.next()
|
|
229
|
+
return ExprNodes.NotNode(pos, operand = p_not_test(s))
|
|
230
|
+
else:
|
|
231
|
+
return p_comparison(s)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
#comparison: expr (comp_op expr)*
|
|
235
|
+
#comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
|
|
236
|
+
|
|
237
|
+
@cython.cfunc
|
|
238
|
+
def p_comparison(s: PyrexScanner):
|
|
239
|
+
n1 = p_starred_expr(s)
|
|
240
|
+
if s.sy in comparison_ops:
|
|
241
|
+
pos = s.position()
|
|
242
|
+
op = p_cmp_op(s)
|
|
243
|
+
n2 = p_starred_expr(s)
|
|
244
|
+
n1 = ExprNodes.PrimaryCmpNode(pos,
|
|
245
|
+
operator = op, operand1 = n1, operand2 = n2)
|
|
246
|
+
if s.sy in comparison_ops:
|
|
247
|
+
n1.cascade = p_cascaded_cmp(s)
|
|
248
|
+
return n1
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@cython.cfunc
|
|
252
|
+
def p_test_or_starred_expr(s: PyrexScanner):
|
|
253
|
+
if s.sy == '*':
|
|
254
|
+
return p_starred_expr(s)
|
|
255
|
+
else:
|
|
256
|
+
return p_test(s)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@cython.cfunc
|
|
260
|
+
def p_namedexpr_test_or_starred_expr(s: PyrexScanner):
|
|
261
|
+
if s.sy == '*':
|
|
262
|
+
return p_starred_expr(s)
|
|
263
|
+
else:
|
|
264
|
+
return p_namedexpr_test(s)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@cython.cfunc
|
|
268
|
+
def p_starred_expr(s: PyrexScanner):
|
|
269
|
+
pos = s.position()
|
|
270
|
+
if s.sy == '*':
|
|
271
|
+
starred = True
|
|
272
|
+
s.next()
|
|
273
|
+
else:
|
|
274
|
+
starred = False
|
|
275
|
+
expr = p_bit_expr(s)
|
|
276
|
+
if starred:
|
|
277
|
+
expr = ExprNodes.StarredUnpackingNode(pos, expr)
|
|
278
|
+
return expr
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@cython.cfunc
|
|
282
|
+
def p_cascaded_cmp(s: PyrexScanner):
|
|
283
|
+
pos = s.position()
|
|
284
|
+
op = p_cmp_op(s)
|
|
285
|
+
n2 = p_starred_expr(s)
|
|
286
|
+
result = ExprNodes.CascadedCmpNode(pos,
|
|
287
|
+
operator = op, operand2 = n2)
|
|
288
|
+
if s.sy in comparison_ops:
|
|
289
|
+
result.cascade = p_cascaded_cmp(s)
|
|
290
|
+
return result
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@cython.cfunc
|
|
294
|
+
def p_cmp_op(s: PyrexScanner):
|
|
295
|
+
if s.sy == 'not':
|
|
296
|
+
s.next()
|
|
297
|
+
s.expect('in')
|
|
298
|
+
op = 'not_in'
|
|
299
|
+
elif s.sy == 'is':
|
|
300
|
+
s.next()
|
|
301
|
+
if s.sy == 'not':
|
|
302
|
+
s.next()
|
|
303
|
+
op = 'is_not'
|
|
304
|
+
else:
|
|
305
|
+
op = 'is'
|
|
306
|
+
else:
|
|
307
|
+
op = s.sy
|
|
308
|
+
s.next()
|
|
309
|
+
if op == '<>':
|
|
310
|
+
op = '!='
|
|
311
|
+
return op
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
comparison_ops = cython.declare(frozenset, frozenset((
|
|
315
|
+
'<', '>', '==', '>=', '<=', '<>', '!=',
|
|
316
|
+
'in', 'is', 'not'
|
|
317
|
+
)))
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
#expr: xor_expr ('|' xor_expr)*
|
|
321
|
+
|
|
322
|
+
@cython.cfunc
|
|
323
|
+
def p_bit_expr(s: PyrexScanner):
|
|
324
|
+
return p_binop_expr(s, ('|',), p_xor_expr)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
#xor_expr: and_expr ('^' and_expr)*
|
|
328
|
+
|
|
329
|
+
@cython.cfunc
|
|
330
|
+
def p_xor_expr(s: PyrexScanner):
|
|
331
|
+
return p_binop_expr(s, ('^',), p_and_expr)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
#and_expr: shift_expr ('&' shift_expr)*
|
|
335
|
+
|
|
336
|
+
@cython.cfunc
|
|
337
|
+
def p_and_expr(s: PyrexScanner):
|
|
338
|
+
return p_binop_expr(s, ('&',), p_shift_expr)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
#shift_expr: arith_expr (('<<'|'>>') arith_expr)*
|
|
342
|
+
|
|
343
|
+
@cython.cfunc
|
|
344
|
+
def p_shift_expr(s: PyrexScanner):
|
|
345
|
+
return p_binop_expr(s, ('<<', '>>'), p_arith_expr)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
#arith_expr: term (('+'|'-') term)*
|
|
349
|
+
|
|
350
|
+
@cython.cfunc
|
|
351
|
+
def p_arith_expr(s: PyrexScanner):
|
|
352
|
+
return p_binop_expr(s, ('+', '-'), p_term)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
#term: factor (('*'|'@'|'/'|'%'|'//') factor)*
|
|
356
|
+
|
|
357
|
+
@cython.cfunc
|
|
358
|
+
def p_term(s: PyrexScanner):
|
|
359
|
+
return p_binop_expr(s, ('*', '@', '/', '%', '//'), p_factor)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
#factor: ('+'|'-'|'~'|'&'|typecast|sizeof) factor | power
|
|
363
|
+
|
|
364
|
+
@cython.cfunc
|
|
365
|
+
def p_factor(s: PyrexScanner):
|
|
366
|
+
# little indirection for C-ification purposes
|
|
367
|
+
return _p_factor(s)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@cython.cfunc
|
|
371
|
+
def _p_factor(s: PyrexScanner):
|
|
372
|
+
sy = s.sy
|
|
373
|
+
if sy in ('+', '-', '~'):
|
|
374
|
+
op = s.sy
|
|
375
|
+
pos = s.position()
|
|
376
|
+
s.next()
|
|
377
|
+
return ExprNodes.unop_node(pos, op, p_factor(s))
|
|
378
|
+
elif not s.in_python_file:
|
|
379
|
+
if sy == '&':
|
|
380
|
+
pos = s.position()
|
|
381
|
+
s.next()
|
|
382
|
+
arg = p_factor(s)
|
|
383
|
+
return ExprNodes.AmpersandNode(pos, operand = arg)
|
|
384
|
+
elif sy == "<":
|
|
385
|
+
return p_typecast(s)
|
|
386
|
+
elif sy == 'IDENT' and s.systring == "sizeof":
|
|
387
|
+
return p_sizeof(s)
|
|
388
|
+
return p_power(s)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@cython.cfunc
|
|
392
|
+
def p_typecast(s: PyrexScanner):
|
|
393
|
+
# s.sy == "<"
|
|
394
|
+
pos = s.position()
|
|
395
|
+
s.next()
|
|
396
|
+
base_type = p_c_base_type(s)
|
|
397
|
+
is_memslice = isinstance(base_type, Nodes.MemoryViewSliceTypeNode)
|
|
398
|
+
is_other_unnamed_type = isinstance(base_type, (
|
|
399
|
+
Nodes.TemplatedTypeNode,
|
|
400
|
+
Nodes.CConstOrVolatileTypeNode,
|
|
401
|
+
Nodes.CTupleBaseTypeNode,
|
|
402
|
+
))
|
|
403
|
+
if not (is_memslice or is_other_unnamed_type) and base_type.name is None:
|
|
404
|
+
s.error("Unknown type")
|
|
405
|
+
declarator = p_c_declarator(s, empty=True)
|
|
406
|
+
if s.sy == '?':
|
|
407
|
+
s.next()
|
|
408
|
+
typecheck = True
|
|
409
|
+
else:
|
|
410
|
+
typecheck = False
|
|
411
|
+
s.expect(">")
|
|
412
|
+
operand = p_factor(s)
|
|
413
|
+
if is_memslice:
|
|
414
|
+
return ExprNodes.CythonArrayNode(pos, base_type_node=base_type, operand=operand)
|
|
415
|
+
|
|
416
|
+
return ExprNodes.TypecastNode(pos,
|
|
417
|
+
base_type = base_type,
|
|
418
|
+
declarator = declarator,
|
|
419
|
+
operand = operand,
|
|
420
|
+
typecheck = typecheck)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
@cython.cfunc
|
|
424
|
+
def p_sizeof(s: PyrexScanner):
|
|
425
|
+
# s.sy == ident "sizeof"
|
|
426
|
+
pos = s.position()
|
|
427
|
+
s.next()
|
|
428
|
+
s.expect('(')
|
|
429
|
+
# Here we decide if we are looking at an expression or type
|
|
430
|
+
# If it is actually a type, but parsable as an expression,
|
|
431
|
+
# we treat it as an expression here.
|
|
432
|
+
if looking_at_expr(s):
|
|
433
|
+
operand = p_test(s)
|
|
434
|
+
node = ExprNodes.SizeofVarNode(pos, operand = operand)
|
|
435
|
+
else:
|
|
436
|
+
base_type = p_c_base_type(s)
|
|
437
|
+
declarator = p_c_declarator(s, empty=True)
|
|
438
|
+
node = ExprNodes.SizeofTypeNode(pos,
|
|
439
|
+
base_type = base_type, declarator = declarator)
|
|
440
|
+
s.expect(')')
|
|
441
|
+
return node
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@cython.cfunc
|
|
445
|
+
def p_yield_expression(s: PyrexScanner, statement_terminators: frozenset = statement_terminators):
|
|
446
|
+
# s.sy == "yield"
|
|
447
|
+
pos = s.position()
|
|
448
|
+
s.next()
|
|
449
|
+
is_yield_from = False
|
|
450
|
+
if s.sy == 'from':
|
|
451
|
+
is_yield_from = True
|
|
452
|
+
s.next()
|
|
453
|
+
if s.sy != ')' and s.sy not in statement_terminators:
|
|
454
|
+
# "yield from" does not support implicit tuples, but "yield" does ("yield 1,2")
|
|
455
|
+
arg = p_test(s) if is_yield_from else p_testlist(s)
|
|
456
|
+
else:
|
|
457
|
+
if is_yield_from:
|
|
458
|
+
s.error("'yield from' requires a source argument",
|
|
459
|
+
pos=pos, fatal=False)
|
|
460
|
+
arg = None
|
|
461
|
+
if is_yield_from:
|
|
462
|
+
return ExprNodes.YieldFromExprNode(pos, arg=arg)
|
|
463
|
+
else:
|
|
464
|
+
return ExprNodes.YieldExprNode(pos, arg=arg)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
@cython.cfunc
|
|
468
|
+
def p_yield_statement(s: PyrexScanner):
|
|
469
|
+
# s.sy == "yield"
|
|
470
|
+
yield_expr = p_yield_expression(s)
|
|
471
|
+
return Nodes.ExprStatNode(yield_expr.pos, expr=yield_expr)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
@cython.cfunc
|
|
475
|
+
def p_async_statement(s: PyrexScanner, ctx, decorators):
|
|
476
|
+
# s.sy >> 'async' ...
|
|
477
|
+
if s.sy == 'def':
|
|
478
|
+
# 'async def' statements aren't allowed in pxd files
|
|
479
|
+
if 'pxd' in ctx.level:
|
|
480
|
+
s.error('def statement not allowed here')
|
|
481
|
+
s.level = ctx.level
|
|
482
|
+
return p_def_statement(s, decorators, is_async_def=True)
|
|
483
|
+
elif decorators:
|
|
484
|
+
s.error("Decorators can only be followed by functions or classes")
|
|
485
|
+
elif s.sy == 'for':
|
|
486
|
+
return p_for_statement(s, is_async=True)
|
|
487
|
+
elif s.sy == 'with':
|
|
488
|
+
s.next()
|
|
489
|
+
return p_with_items(s, is_async=True)
|
|
490
|
+
else:
|
|
491
|
+
s.error("expected one of 'def', 'for', 'with' after 'async'")
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
#power: atom_expr ('**' factor)*
|
|
495
|
+
#atom_expr: ['await'] atom trailer*
|
|
496
|
+
|
|
497
|
+
@cython.cfunc
|
|
498
|
+
def p_power(s: PyrexScanner):
|
|
499
|
+
if s.systring == 'new' and s.peek()[0] == 'IDENT':
|
|
500
|
+
return p_new_expr(s)
|
|
501
|
+
await_pos = None
|
|
502
|
+
if s.sy == 'await':
|
|
503
|
+
await_pos = s.position()
|
|
504
|
+
s.next()
|
|
505
|
+
n1 = p_atom(s)
|
|
506
|
+
while s.sy in ('(', '[', '.'):
|
|
507
|
+
n1 = p_trailer(s, n1)
|
|
508
|
+
if await_pos:
|
|
509
|
+
n1 = ExprNodes.AwaitExprNode(await_pos, arg=n1)
|
|
510
|
+
if s.sy == '**':
|
|
511
|
+
pos = s.position()
|
|
512
|
+
s.next()
|
|
513
|
+
n2 = p_factor(s)
|
|
514
|
+
n1 = ExprNodes.binop_node(pos, '**', n1, n2)
|
|
515
|
+
return n1
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
@cython.cfunc
|
|
519
|
+
def p_new_expr(s: PyrexScanner):
|
|
520
|
+
# s.systring == 'new'.
|
|
521
|
+
pos = s.position()
|
|
522
|
+
s.next()
|
|
523
|
+
cppclass = p_c_base_type(s)
|
|
524
|
+
return p_call(s, ExprNodes.NewExprNode(pos, cppclass = cppclass))
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
#trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
|
528
|
+
|
|
529
|
+
@cython.cfunc
|
|
530
|
+
def p_trailer(s: PyrexScanner, node1):
|
|
531
|
+
pos = s.position()
|
|
532
|
+
if s.sy == '(':
|
|
533
|
+
return p_call(s, node1)
|
|
534
|
+
elif s.sy == '[':
|
|
535
|
+
return p_index(s, node1)
|
|
536
|
+
else: # s.sy == '.'
|
|
537
|
+
s.next()
|
|
538
|
+
name = p_ident(s)
|
|
539
|
+
return ExprNodes.AttributeNode(pos,
|
|
540
|
+
obj=node1, attribute=name)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
# arglist: argument (',' argument)* [',']
|
|
544
|
+
# argument: [test '='] test # Really [keyword '='] test
|
|
545
|
+
|
|
546
|
+
# since PEP 448:
|
|
547
|
+
# argument: ( test [comp_for] |
|
|
548
|
+
# test '=' test |
|
|
549
|
+
# '**' expr |
|
|
550
|
+
# star_expr )
|
|
551
|
+
|
|
552
|
+
@cython.cfunc
|
|
553
|
+
def p_call_parse_args(s: PyrexScanner, allow_genexp: cython.bint = True):
|
|
554
|
+
# s.sy == '('
|
|
555
|
+
s.next()
|
|
556
|
+
positional_args = []
|
|
557
|
+
keyword_args = []
|
|
558
|
+
starstar_seen = False
|
|
559
|
+
last_was_tuple_unpack = False
|
|
560
|
+
while s.sy != ')':
|
|
561
|
+
if s.sy == '*':
|
|
562
|
+
if starstar_seen:
|
|
563
|
+
s.error("Non-keyword arg following keyword arg", pos=s.position())
|
|
564
|
+
s.next()
|
|
565
|
+
positional_args.append(p_test(s))
|
|
566
|
+
last_was_tuple_unpack = True
|
|
567
|
+
elif s.sy == '**':
|
|
568
|
+
s.next()
|
|
569
|
+
keyword_args.append(p_test(s))
|
|
570
|
+
starstar_seen = True
|
|
571
|
+
else:
|
|
572
|
+
arg = p_namedexpr_test(s)
|
|
573
|
+
if s.sy == '=':
|
|
574
|
+
s.next()
|
|
575
|
+
if not arg.is_name:
|
|
576
|
+
s.error("Expected an identifier before '='",
|
|
577
|
+
pos=arg.pos)
|
|
578
|
+
encoded_name = s.context.intern_ustring(arg.name)
|
|
579
|
+
keyword = ExprNodes.IdentifierStringNode(
|
|
580
|
+
arg.pos, value=encoded_name)
|
|
581
|
+
arg = p_test(s)
|
|
582
|
+
keyword_args.append((keyword, arg))
|
|
583
|
+
else:
|
|
584
|
+
if keyword_args:
|
|
585
|
+
s.error("Non-keyword arg following keyword arg", pos=arg.pos)
|
|
586
|
+
if positional_args and not last_was_tuple_unpack:
|
|
587
|
+
positional_args[-1].append(arg)
|
|
588
|
+
else:
|
|
589
|
+
positional_args.append([arg])
|
|
590
|
+
last_was_tuple_unpack = False
|
|
591
|
+
if s.sy != ',':
|
|
592
|
+
break
|
|
593
|
+
s.next()
|
|
594
|
+
|
|
595
|
+
if s.sy in ('for', 'async') and allow_genexp:
|
|
596
|
+
if not keyword_args and not last_was_tuple_unpack:
|
|
597
|
+
if len(positional_args) == 1 and len(positional_args[0]) == 1:
|
|
598
|
+
positional_args = [[p_genexp(s, positional_args[0][0])]]
|
|
599
|
+
s.expect(')')
|
|
600
|
+
return positional_args or [[]], keyword_args
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
@cython.cfunc
|
|
604
|
+
def p_call_build_packed_args(pos, positional_args, keyword_args) -> tuple:
|
|
605
|
+
keyword_dict = None
|
|
606
|
+
|
|
607
|
+
subtuples = [
|
|
608
|
+
ExprNodes.TupleNode(pos, args=arg) if isinstance(arg, list) else ExprNodes.AsTupleNode(pos, arg=arg)
|
|
609
|
+
for arg in positional_args
|
|
610
|
+
]
|
|
611
|
+
# TODO: implement a faster way to join tuples than creating each one and adding them
|
|
612
|
+
arg_tuple = reduce(partial(ExprNodes.binop_node, pos, '+'), subtuples)
|
|
613
|
+
|
|
614
|
+
if keyword_args:
|
|
615
|
+
kwargs = []
|
|
616
|
+
dict_items = []
|
|
617
|
+
for item in keyword_args:
|
|
618
|
+
if isinstance(item, tuple):
|
|
619
|
+
key, value = item
|
|
620
|
+
dict_items.append(ExprNodes.DictItemNode(pos=key.pos, key=key, value=value))
|
|
621
|
+
elif item.is_dict_literal:
|
|
622
|
+
# unpack "**{a:b}" directly
|
|
623
|
+
dict_items.extend(item.key_value_pairs)
|
|
624
|
+
else:
|
|
625
|
+
if dict_items:
|
|
626
|
+
kwargs.append(ExprNodes.DictNode(
|
|
627
|
+
dict_items[0].pos, key_value_pairs=dict_items, reject_duplicates=True))
|
|
628
|
+
dict_items = []
|
|
629
|
+
kwargs.append(item)
|
|
630
|
+
|
|
631
|
+
if dict_items:
|
|
632
|
+
kwargs.append(ExprNodes.DictNode(
|
|
633
|
+
dict_items[0].pos, key_value_pairs=dict_items, reject_duplicates=True))
|
|
634
|
+
|
|
635
|
+
if kwargs:
|
|
636
|
+
if len(kwargs) == 1 and kwargs[0].is_dict_literal:
|
|
637
|
+
# only simple keyword arguments found -> one dict
|
|
638
|
+
keyword_dict = kwargs[0]
|
|
639
|
+
else:
|
|
640
|
+
# at least one **kwargs
|
|
641
|
+
keyword_dict = ExprNodes.MergedDictNode(pos, keyword_args=kwargs)
|
|
642
|
+
|
|
643
|
+
return arg_tuple, keyword_dict
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
@cython.cfunc
|
|
647
|
+
def p_call(s: PyrexScanner, function):
|
|
648
|
+
# s.sy == '('
|
|
649
|
+
pos = s.position()
|
|
650
|
+
positional_args, keyword_args = p_call_parse_args(s)
|
|
651
|
+
|
|
652
|
+
if not keyword_args and len(positional_args) == 1 and isinstance(positional_args[0], list):
|
|
653
|
+
return ExprNodes.SimpleCallNode(pos, function=function, args=positional_args[0])
|
|
654
|
+
else:
|
|
655
|
+
arg_tuple, keyword_dict = p_call_build_packed_args(pos, positional_args, keyword_args)
|
|
656
|
+
return ExprNodes.GeneralCallNode(
|
|
657
|
+
pos, function=function, positional_args=arg_tuple, keyword_args=keyword_dict)
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
#lambdef: 'lambda' [varargslist] ':' test
|
|
661
|
+
|
|
662
|
+
#subscriptlist: subscript (',' subscript)* [',']
|
|
663
|
+
|
|
664
|
+
@cython.cfunc
|
|
665
|
+
def p_index(s: PyrexScanner, base):
|
|
666
|
+
# s.sy == '['
|
|
667
|
+
pos = s.position()
|
|
668
|
+
s.next()
|
|
669
|
+
subscripts, is_single_value = p_subscript_list(s)
|
|
670
|
+
if is_single_value and len(subscripts[0]) == 2:
|
|
671
|
+
start, stop = subscripts[0]
|
|
672
|
+
result = ExprNodes.SliceIndexNode(pos,
|
|
673
|
+
base = base, start = start, stop = stop)
|
|
674
|
+
else:
|
|
675
|
+
indexes = make_slice_nodes(pos, subscripts)
|
|
676
|
+
if is_single_value:
|
|
677
|
+
index = indexes[0]
|
|
678
|
+
else:
|
|
679
|
+
index = ExprNodes.TupleNode(pos, args = indexes)
|
|
680
|
+
result = ExprNodes.IndexNode(pos,
|
|
681
|
+
base = base, index = index)
|
|
682
|
+
s.expect(']')
|
|
683
|
+
return result
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
@cython.cfunc
|
|
687
|
+
def p_subscript_list(s: PyrexScanner) -> tuple:
|
|
688
|
+
is_single_value = True
|
|
689
|
+
items = [p_subscript(s)]
|
|
690
|
+
while s.sy == ',':
|
|
691
|
+
is_single_value = False
|
|
692
|
+
s.next()
|
|
693
|
+
if s.sy == ']':
|
|
694
|
+
break
|
|
695
|
+
items.append(p_subscript(s))
|
|
696
|
+
return items, is_single_value
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
#subscript: '.' '.' '.' | test | [test] ':' [test] [':' [test]]
|
|
700
|
+
|
|
701
|
+
@cython.cfunc
|
|
702
|
+
def p_subscript(s: PyrexScanner):
|
|
703
|
+
# Parse a subscript and return a list of
|
|
704
|
+
# 1, 2 or 3 ExprNodes, depending on how
|
|
705
|
+
# many slice elements were encountered.
|
|
706
|
+
start = p_slice_element(s, (':',))
|
|
707
|
+
if s.sy != ':':
|
|
708
|
+
return [start]
|
|
709
|
+
s.next()
|
|
710
|
+
stop = p_slice_element(s, (':', ',', ']'))
|
|
711
|
+
if s.sy != ':':
|
|
712
|
+
return [start, stop]
|
|
713
|
+
s.next()
|
|
714
|
+
step = p_slice_element(s, (':', ',', ']'))
|
|
715
|
+
return [start, stop, step]
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
@cython.cfunc
|
|
719
|
+
def p_slice_element(s: PyrexScanner, follow_set):
|
|
720
|
+
# Simple expression which may be missing iff
|
|
721
|
+
# it is followed by something in follow_set.
|
|
722
|
+
if s.sy not in follow_set:
|
|
723
|
+
return p_test(s)
|
|
724
|
+
else:
|
|
725
|
+
return None
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
@cython.cfunc
|
|
729
|
+
def expect_ellipsis(s: PyrexScanner):
|
|
730
|
+
s.expect('...')
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
@cython.cfunc
|
|
734
|
+
def make_slice_nodes(pos, subscripts):
|
|
735
|
+
# Convert a list of subscripts as returned
|
|
736
|
+
# by p_subscript_list into a list of ExprNodes,
|
|
737
|
+
# creating SliceNodes for elements with 2 or
|
|
738
|
+
# more components.
|
|
739
|
+
result = []
|
|
740
|
+
for subscript in subscripts:
|
|
741
|
+
if len(subscript) == 1:
|
|
742
|
+
result.append(subscript[0])
|
|
743
|
+
else:
|
|
744
|
+
result.append(make_slice_node(pos, *subscript))
|
|
745
|
+
return result
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
@cython.ccall
|
|
749
|
+
def make_slice_node(pos, start, stop = None, step = None):
|
|
750
|
+
if not start:
|
|
751
|
+
start = ExprNodes.NoneNode(pos)
|
|
752
|
+
if not stop:
|
|
753
|
+
stop = ExprNodes.NoneNode(pos)
|
|
754
|
+
if not step:
|
|
755
|
+
step = ExprNodes.NoneNode(pos)
|
|
756
|
+
return ExprNodes.SliceNode(pos,
|
|
757
|
+
start = start, stop = stop, step = step)
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
#atom: '(' [yield_expr|testlist_comp] ')' | '[' [listmaker] ']' | '{' [dict_or_set_maker] '}' | '`' testlist '`' | NAME | NUMBER | STRING+
|
|
761
|
+
|
|
762
|
+
@cython.cfunc
|
|
763
|
+
def p_atom(s: PyrexScanner):
|
|
764
|
+
pos = s.position()
|
|
765
|
+
sy = s.sy
|
|
766
|
+
if sy == '(':
|
|
767
|
+
s.next()
|
|
768
|
+
if s.sy == ')':
|
|
769
|
+
result = ExprNodes.TupleNode(pos, args = [])
|
|
770
|
+
elif s.sy == 'yield':
|
|
771
|
+
result = p_yield_expression(s)
|
|
772
|
+
else:
|
|
773
|
+
result = p_testlist_comp(s)
|
|
774
|
+
s.expect(')')
|
|
775
|
+
return result
|
|
776
|
+
elif sy == '[':
|
|
777
|
+
return p_list_maker(s)
|
|
778
|
+
elif sy == '{':
|
|
779
|
+
return p_dict_or_set_maker(s)
|
|
780
|
+
elif sy == '`':
|
|
781
|
+
return p_backquote_expr(s)
|
|
782
|
+
elif sy == '...':
|
|
783
|
+
expect_ellipsis(s)
|
|
784
|
+
return ExprNodes.EllipsisNode(pos)
|
|
785
|
+
elif sy == 'INT':
|
|
786
|
+
return p_int_literal(s)
|
|
787
|
+
elif sy == 'FLOAT':
|
|
788
|
+
value = s.systring
|
|
789
|
+
s.next()
|
|
790
|
+
return ExprNodes.FloatNode(pos, value = value)
|
|
791
|
+
elif sy == 'IMAG':
|
|
792
|
+
value = s.systring[:-1]
|
|
793
|
+
s.next()
|
|
794
|
+
return ExprNodes.ImagNode(pos, value = value)
|
|
795
|
+
elif sy == 'BEGIN_STRING' or sy == 'BEGIN_FT_STRING':
|
|
796
|
+
return p_atom_string(s)
|
|
797
|
+
elif sy == 'IDENT':
|
|
798
|
+
result = p_atom_ident_constants(s)
|
|
799
|
+
if result is None:
|
|
800
|
+
result = p_name(s, s.systring)
|
|
801
|
+
s.next()
|
|
802
|
+
return result
|
|
803
|
+
else:
|
|
804
|
+
s.error("Expected an identifier or literal")
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
@cython.cfunc
|
|
808
|
+
def p_atom_string(s: PyrexScanner):
|
|
809
|
+
# s.sy == 'BEGIN_STRING' or s.sy == 'BEGIN_FT_STRING'
|
|
810
|
+
pos = s.position()
|
|
811
|
+
kind, bytes_value, unicode_value = p_cat_string_literal(s)
|
|
812
|
+
if not kind:
|
|
813
|
+
return ExprNodes.UnicodeNode(pos, value=unicode_value, bytes_value=bytes_value)
|
|
814
|
+
kind_char: cython.Py_UCS4 = kind
|
|
815
|
+
if kind_char == 'c':
|
|
816
|
+
return ExprNodes.CharNode(pos, value=bytes_value)
|
|
817
|
+
elif kind_char == 'u':
|
|
818
|
+
return ExprNodes.UnicodeNode(pos, value=unicode_value, bytes_value=bytes_value)
|
|
819
|
+
elif kind_char == 'b':
|
|
820
|
+
return ExprNodes.BytesNode(pos, value=bytes_value)
|
|
821
|
+
elif kind_char == 'f':
|
|
822
|
+
return ExprNodes.JoinedStrNode(pos, values=unicode_value)
|
|
823
|
+
elif kind_char == 't':
|
|
824
|
+
# TODO
|
|
825
|
+
return ExprNodes.TemplateStringNode(pos, values=unicode_value)
|
|
826
|
+
else:
|
|
827
|
+
# This is actually prevented by the scanner (Lexicon.py).
|
|
828
|
+
s.error(f"invalid string kind '{kind}'")
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
@cython.cfunc
|
|
832
|
+
def p_atom_ident_constants(s: PyrexScanner):
|
|
833
|
+
"""
|
|
834
|
+
Returns None if it isn't a special-cased named constant.
|
|
835
|
+
Only calls s.next() if it successfully matches a named constant.
|
|
836
|
+
"""
|
|
837
|
+
# s.sy == 'IDENT'
|
|
838
|
+
pos = s.position()
|
|
839
|
+
name = s.systring
|
|
840
|
+
if name == "None":
|
|
841
|
+
result = ExprNodes.NoneNode(pos)
|
|
842
|
+
elif name == "True":
|
|
843
|
+
result = ExprNodes.BoolNode(pos, value=True)
|
|
844
|
+
elif name == "False":
|
|
845
|
+
result = ExprNodes.BoolNode(pos, value=False)
|
|
846
|
+
elif name == "NULL" and not s.in_python_file:
|
|
847
|
+
result = ExprNodes.NullNode(pos)
|
|
848
|
+
else:
|
|
849
|
+
return None
|
|
850
|
+
s.next()
|
|
851
|
+
return result
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
@cython.cfunc
|
|
855
|
+
def p_int_literal(s: PyrexScanner):
|
|
856
|
+
pos = s.position()
|
|
857
|
+
value: str = cython.cast(str, s.systring)
|
|
858
|
+
s.next()
|
|
859
|
+
unsigned = ""
|
|
860
|
+
longness = ""
|
|
861
|
+
while value[-1] in "UuLl":
|
|
862
|
+
if value[-1] in "Ll":
|
|
863
|
+
longness += "L"
|
|
864
|
+
else:
|
|
865
|
+
unsigned += "U"
|
|
866
|
+
value = value[:-1]
|
|
867
|
+
# '3L' is ambiguous in Py2 but not in Py3. '3U' and '3LL' are
|
|
868
|
+
# illegal in Py2 Python files. All suffixes are illegal in Py3
|
|
869
|
+
# Python files.
|
|
870
|
+
is_c_literal = None
|
|
871
|
+
if unsigned:
|
|
872
|
+
is_c_literal = True
|
|
873
|
+
elif longness:
|
|
874
|
+
if longness == 'LL' or s.context.language_level >= 3:
|
|
875
|
+
is_c_literal = True
|
|
876
|
+
if s.in_python_file:
|
|
877
|
+
if is_c_literal:
|
|
878
|
+
error(pos, "illegal integer literal syntax in Python source file")
|
|
879
|
+
is_c_literal = False
|
|
880
|
+
return ExprNodes.IntNode(pos,
|
|
881
|
+
is_c_literal = is_c_literal,
|
|
882
|
+
value = value,
|
|
883
|
+
unsigned = unsigned,
|
|
884
|
+
longness = longness)
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
@cython.cfunc
|
|
888
|
+
def p_name(s: PyrexScanner, name):
|
|
889
|
+
pos = s.position()
|
|
890
|
+
if not s.compile_time_expr and name in s.compile_time_env:
|
|
891
|
+
value = s.compile_time_env.lookup_here(name)
|
|
892
|
+
node = wrap_compile_time_constant(pos, value)
|
|
893
|
+
if node is not None:
|
|
894
|
+
return node
|
|
895
|
+
return ExprNodes.NameNode(pos, name=name)
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
@cython.cfunc
|
|
899
|
+
def wrap_compile_time_constant(pos, value):
|
|
900
|
+
if value is None:
|
|
901
|
+
return ExprNodes.NoneNode(pos)
|
|
902
|
+
elif value is Ellipsis:
|
|
903
|
+
return ExprNodes.EllipsisNode(pos)
|
|
904
|
+
elif isinstance(value, bool):
|
|
905
|
+
return ExprNodes.BoolNode(pos, value=value)
|
|
906
|
+
elif isinstance(value, int):
|
|
907
|
+
return ExprNodes.IntNode(pos, value=repr(value), constant_result=value)
|
|
908
|
+
elif isinstance(value, float):
|
|
909
|
+
return ExprNodes.FloatNode(pos, value=repr(value), constant_result=value)
|
|
910
|
+
elif isinstance(value, complex):
|
|
911
|
+
node = ExprNodes.ImagNode(pos, value=repr(value.imag), constant_result=complex(0.0, value.imag))
|
|
912
|
+
if value.real:
|
|
913
|
+
# FIXME: should we care about -0.0 ?
|
|
914
|
+
# probably not worth using the '-' operator for negative imag values
|
|
915
|
+
node = ExprNodes.binop_node(
|
|
916
|
+
pos, '+', ExprNodes.FloatNode(pos, value=repr(value.real), constant_result=value.real), node,
|
|
917
|
+
constant_result=value)
|
|
918
|
+
return node
|
|
919
|
+
elif isinstance(value, str):
|
|
920
|
+
return ExprNodes.UnicodeNode(pos, value=EncodedString(value))
|
|
921
|
+
elif isinstance(value, bytes):
|
|
922
|
+
bvalue = bytes_literal(value, 'ascii') # actually: unknown encoding, but BytesLiteral requires one
|
|
923
|
+
return ExprNodes.BytesNode(pos, value=bvalue, constant_result=value)
|
|
924
|
+
elif isinstance(value, tuple):
|
|
925
|
+
args = [wrap_compile_time_constant(pos, arg) for arg in value]
|
|
926
|
+
if None in args:
|
|
927
|
+
# error already reported
|
|
928
|
+
return None
|
|
929
|
+
return ExprNodes.TupleNode(pos, args=args)
|
|
930
|
+
|
|
931
|
+
error(pos, "Invalid type for compile-time constant: %r (type %s)"
|
|
932
|
+
% (value, value.__class__.__name__))
|
|
933
|
+
return None
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
@cython.cfunc
|
|
937
|
+
def p_cat_string_literal(s: PyrexScanner) -> tuple:
|
|
938
|
+
# A sequence of one or more adjacent string literals.
|
|
939
|
+
# Returns (kind, bytes_value, unicode_value)
|
|
940
|
+
# where kind in ('b', 'c', 'u', 'f', 't', '')
|
|
941
|
+
pos = s.position()
|
|
942
|
+
kind, bytes_value, unicode_value = p_string_literal(s)
|
|
943
|
+
if kind == 'c' or (s.sy != 'BEGIN_STRING' and s.sy != 'BEGIN_FT_STRING'):
|
|
944
|
+
return kind, bytes_value, unicode_value
|
|
945
|
+
bstrings, ustrings, positions = [bytes_value], [unicode_value], [pos]
|
|
946
|
+
bytes_value = unicode_value = None
|
|
947
|
+
while s.sy == 'BEGIN_STRING' or s.sy == 'BEGIN_FT_STRING':
|
|
948
|
+
pos = s.position()
|
|
949
|
+
next_kind, next_bytes_value, next_unicode_value = p_string_literal(s)
|
|
950
|
+
if next_kind == 'c':
|
|
951
|
+
error(pos, "Cannot concatenate char literal with another string or char literal")
|
|
952
|
+
continue
|
|
953
|
+
elif next_kind != kind:
|
|
954
|
+
# concatenating f strings and normal strings is allowed and leads to an f string
|
|
955
|
+
if {kind, next_kind} in ({'f', 'u'}, {'f', ''}):
|
|
956
|
+
kind = 'f'
|
|
957
|
+
elif kind == 't' or next_kind == 't':
|
|
958
|
+
error(pos, "cannot mix t-string literals with string or bytes literals")
|
|
959
|
+
continue
|
|
960
|
+
else:
|
|
961
|
+
error(pos, "Cannot mix string literals of different types, expected %s'', got %s''" % (
|
|
962
|
+
kind, next_kind))
|
|
963
|
+
continue
|
|
964
|
+
bstrings.append(next_bytes_value)
|
|
965
|
+
ustrings.append(next_unicode_value)
|
|
966
|
+
positions.append(pos)
|
|
967
|
+
# join and rewrap the partial literals
|
|
968
|
+
if kind in ('b', 'c', '') or kind == 'u' and None not in bstrings:
|
|
969
|
+
# Py3 enforced unicode literals are parsed as bytes/unicode combination
|
|
970
|
+
bytes_value = bytes_literal(b''.join(bstrings), s.source_encoding)
|
|
971
|
+
if kind in ('u', ''):
|
|
972
|
+
unicode_value = EncodedString(''.join([u for u in ustrings if u is not None]))
|
|
973
|
+
if kind == 'f':
|
|
974
|
+
unicode_value = []
|
|
975
|
+
for u, pos in zip(ustrings, positions):
|
|
976
|
+
if isinstance(u, list):
|
|
977
|
+
unicode_value += u
|
|
978
|
+
else:
|
|
979
|
+
# non-f-string concatenated into the f-string
|
|
980
|
+
unicode_value.append(ExprNodes.UnicodeNode(pos, value=EncodedString(u)))
|
|
981
|
+
if kind == 't':
|
|
982
|
+
unicode_value = []
|
|
983
|
+
for u in ustrings:
|
|
984
|
+
unicode_value.extend(u)
|
|
985
|
+
return kind, bytes_value, unicode_value
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
@cython.cfunc
|
|
989
|
+
def p_opt_string_literal(s: PyrexScanner, required_type: str = 'u'):
|
|
990
|
+
if s.sy != 'BEGIN_STRING':
|
|
991
|
+
return None
|
|
992
|
+
pos = s.position()
|
|
993
|
+
kind, bytes_value, unicode_value = p_string_literal(s, required_type)
|
|
994
|
+
if required_type == 'u':
|
|
995
|
+
if kind == 'f':
|
|
996
|
+
s.error("f-string not allowed here", pos)
|
|
997
|
+
return unicode_value
|
|
998
|
+
elif required_type == 'b':
|
|
999
|
+
return bytes_value
|
|
1000
|
+
else:
|
|
1001
|
+
s.error("internal parser configuration error")
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
@cython.cfunc
|
|
1005
|
+
def check_for_non_ascii_characters(string) -> cython.bint:
|
|
1006
|
+
s = cython.cast(str, string) # EncodedString
|
|
1007
|
+
for c in s:
|
|
1008
|
+
if c >= '\x80':
|
|
1009
|
+
return True
|
|
1010
|
+
return False
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
@cython.cfunc
|
|
1014
|
+
def p_string_literal_shared_read(
|
|
1015
|
+
s: PyrexScanner, pos, chars, kind,
|
|
1016
|
+
is_raw: cython.bint):
|
|
1017
|
+
"""
|
|
1018
|
+
Returns a string of non-escaped characters (if handled) or none.
|
|
1019
|
+
If passed an escape sequence returns an empty string.
|
|
1020
|
+
"""
|
|
1021
|
+
sy = s.sy
|
|
1022
|
+
systr = s.systring
|
|
1023
|
+
result = systr
|
|
1024
|
+
is_python3_source: cython.bint = s.context.language_level >= 3
|
|
1025
|
+
# print "p_string_literal: sy =", sy, repr(s.systring) ###
|
|
1026
|
+
if sy == 'CHARS':
|
|
1027
|
+
chars.append(systr)
|
|
1028
|
+
elif sy == 'ESCAPE':
|
|
1029
|
+
# in Py2, 'ur' raw unicode strings resolve unicode escapes but nothing else
|
|
1030
|
+
if is_raw and (is_python3_source or kind != 'u' or len(systr) < 2 or systr[1] not in 'Uu'):
|
|
1031
|
+
chars.append(systr)
|
|
1032
|
+
else:
|
|
1033
|
+
result = ""
|
|
1034
|
+
_append_escape_sequence(kind, chars, systr, s)
|
|
1035
|
+
elif sy == 'NEWLINE':
|
|
1036
|
+
chars.append('\n')
|
|
1037
|
+
elif sy == 'EOF':
|
|
1038
|
+
s.error("Unclosed string literal", pos=pos)
|
|
1039
|
+
else:
|
|
1040
|
+
return None
|
|
1041
|
+
return result
|
|
1042
|
+
|
|
1043
|
+
@cython.cfunc
|
|
1044
|
+
def _validate_kind_string(pos, systring: str):
|
|
1045
|
+
kind_string = systring.rstrip('"\'').lower()
|
|
1046
|
+
if len(kind_string) <= 1 or (len(kind_string) == 2 and kind_string in "rbrurfrtr"):
|
|
1047
|
+
return kind_string
|
|
1048
|
+
# Otherwise an error of some sort
|
|
1049
|
+
unique_string_prefixes = set(kind_string)
|
|
1050
|
+
if len(unique_string_prefixes) != len(kind_string):
|
|
1051
|
+
error(pos, 'Duplicate string prefix character')
|
|
1052
|
+
unique_string_prefixes.discard('r')
|
|
1053
|
+
unique_string_prefixes = sorted(unique_string_prefixes)
|
|
1054
|
+
if len(unique_string_prefixes) >= 2:
|
|
1055
|
+
error(pos, f'String prefixes {unique_string_prefixes[0]} and {unique_string_prefixes[1]} cannot be combined')
|
|
1056
|
+
else:
|
|
1057
|
+
error(pos, f'Invalid string prefix {kind_string}')
|
|
1058
|
+
return ''
|
|
1059
|
+
|
|
1060
|
+
@cython.cfunc
|
|
1061
|
+
def p_string_literal(s: PyrexScanner, kind_override=None) -> tuple:
|
|
1062
|
+
# A single string or char literal. Returns (kind, bvalue, uvalue)
|
|
1063
|
+
# where kind in ('b', 'c', 'u', 'f', ''). The 'bvalue' is the source
|
|
1064
|
+
# code byte sequence of the string literal, 'uvalue' is the
|
|
1065
|
+
# decoded Unicode string. Either of the two may be None depending
|
|
1066
|
+
# on the 'kind' of string, only unprefixed strings have both
|
|
1067
|
+
# representations. In f-strings, the uvalue is a list of the Unicode
|
|
1068
|
+
# strings and f-string expressions that make up the f-string.
|
|
1069
|
+
# s.sy == 'BEGIN_STRING' or s.sy == 'BEGIN_FT_STRING'
|
|
1070
|
+
if s.sy == 'BEGIN_FT_STRING':
|
|
1071
|
+
assert kind_override is None
|
|
1072
|
+
return p_ft_string_literal(s)
|
|
1073
|
+
pos = s.position()
|
|
1074
|
+
is_python3_source: cython.bint = s.context.language_level >= 3
|
|
1075
|
+
has_non_ascii_literal_characters = False
|
|
1076
|
+
kind_string = _validate_kind_string(pos, s.systring)
|
|
1077
|
+
|
|
1078
|
+
is_raw: cython.bint = 'r' in kind_string
|
|
1079
|
+
|
|
1080
|
+
if 'c' in kind_string:
|
|
1081
|
+
# this should never happen, since the lexer does not allow combining c
|
|
1082
|
+
# with other prefix characters
|
|
1083
|
+
if len(kind_string) != 1:
|
|
1084
|
+
error(pos, 'Invalid string prefix for character literal')
|
|
1085
|
+
kind = 'c'
|
|
1086
|
+
elif 'b' in kind_string:
|
|
1087
|
+
kind = 'b'
|
|
1088
|
+
elif 'u' in kind_string:
|
|
1089
|
+
kind = 'u'
|
|
1090
|
+
else:
|
|
1091
|
+
kind = ''
|
|
1092
|
+
|
|
1093
|
+
if kind == '' and kind_override is None and Future.unicode_literals in s.context.future_directives:
|
|
1094
|
+
chars = StringEncoding.StrLiteralBuilder(s.source_encoding)
|
|
1095
|
+
kind = 'u'
|
|
1096
|
+
else:
|
|
1097
|
+
if kind_override is not None and kind_override in 'ub':
|
|
1098
|
+
kind = kind_override
|
|
1099
|
+
if kind in ('u', 'f'): # f-strings are scanned exactly like Unicode literals, but are parsed further later
|
|
1100
|
+
chars = StringEncoding.UnicodeLiteralBuilder()
|
|
1101
|
+
elif kind == '':
|
|
1102
|
+
chars = StringEncoding.StrLiteralBuilder(s.source_encoding)
|
|
1103
|
+
else:
|
|
1104
|
+
chars = StringEncoding.BytesLiteralBuilder(s.source_encoding)
|
|
1105
|
+
|
|
1106
|
+
while 1:
|
|
1107
|
+
s.next()
|
|
1108
|
+
handled_chars = p_string_literal_shared_read(
|
|
1109
|
+
s, pos, chars, kind,
|
|
1110
|
+
is_raw=is_raw)
|
|
1111
|
+
if handled_chars is not None:
|
|
1112
|
+
if (not has_non_ascii_literal_characters and
|
|
1113
|
+
is_python3_source and Future.unicode_literals in s.context.future_directives):
|
|
1114
|
+
has_non_ascii_literal_characters = check_for_non_ascii_characters(handled_chars)
|
|
1115
|
+
continue
|
|
1116
|
+
if s.sy == 'END_STRING':
|
|
1117
|
+
break
|
|
1118
|
+
else:
|
|
1119
|
+
s.error("Unexpected token %r:%r in string literal" % (
|
|
1120
|
+
s.sy, s.systring))
|
|
1121
|
+
|
|
1122
|
+
if kind == 'c':
|
|
1123
|
+
unicode_value = None
|
|
1124
|
+
bytes_value = chars.getchar()
|
|
1125
|
+
if len(bytes_value) != 1:
|
|
1126
|
+
error(pos, "invalid character literal: %r" % bytes_value)
|
|
1127
|
+
else:
|
|
1128
|
+
bytes_value, unicode_value = chars.getstrings()
|
|
1129
|
+
if (has_non_ascii_literal_characters
|
|
1130
|
+
and is_python3_source and Future.unicode_literals in s.context.future_directives):
|
|
1131
|
+
# Python 3 forbids literal non-ASCII characters in byte strings
|
|
1132
|
+
if kind == 'b':
|
|
1133
|
+
s.error("bytes can only contain ASCII literal characters.", pos=pos)
|
|
1134
|
+
bytes_value = None
|
|
1135
|
+
s.next()
|
|
1136
|
+
return (kind, bytes_value, unicode_value)
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
@cython.cfunc
|
|
1140
|
+
def p_read_ft_string_expression(s: PyrexScanner):
|
|
1141
|
+
strings = []
|
|
1142
|
+
while True:
|
|
1143
|
+
s.next()
|
|
1144
|
+
sy = s.sy
|
|
1145
|
+
if sy in ["END_FT_STRING_EXPR",
|
|
1146
|
+
# probably an error, but handle it elsewhere
|
|
1147
|
+
"EOF", None]:
|
|
1148
|
+
if sy == "END_FT_STRING_EXPR":
|
|
1149
|
+
s.next()
|
|
1150
|
+
return ''.join(strings)
|
|
1151
|
+
strings.append(s.systring)
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
@cython.cfunc
|
|
1155
|
+
def p_ft_string_replacement_field(s: PyrexScanner,
|
|
1156
|
+
is_raw: cython.bint, is_single_quoted: cython.bint,
|
|
1157
|
+
tf_string_kind: cython.Py_UCS4):
|
|
1158
|
+
result = []
|
|
1159
|
+
conversion_char = format_spec = expr = None
|
|
1160
|
+
t_string_expression = None
|
|
1161
|
+
self_documenting = False
|
|
1162
|
+
|
|
1163
|
+
bracket_pos = s.position()
|
|
1164
|
+
expr_pos = (bracket_pos[0], bracket_pos[1], bracket_pos[2]+1)
|
|
1165
|
+
expr_string = p_read_ft_string_expression(s)
|
|
1166
|
+
if not expr_string.strip():
|
|
1167
|
+
error(bracket_pos,
|
|
1168
|
+
f"empty expression not allowed in {tf_string_kind}-string")
|
|
1169
|
+
result = []
|
|
1170
|
+
else:
|
|
1171
|
+
original_scanner = s
|
|
1172
|
+
s = PyrexScanner(
|
|
1173
|
+
StringIO(expr_string),
|
|
1174
|
+
bracket_pos[0],
|
|
1175
|
+
parent_scanner=s,
|
|
1176
|
+
source_encoding=s.source_encoding,
|
|
1177
|
+
initial_pos=expr_pos
|
|
1178
|
+
)
|
|
1179
|
+
s.bracket_nesting_level += 1
|
|
1180
|
+
if s.sy == "INDENT":
|
|
1181
|
+
s.next()
|
|
1182
|
+
if s.sy == 'yield':
|
|
1183
|
+
expr = p_yield_expression(
|
|
1184
|
+
s,
|
|
1185
|
+
statement_terminators=statement_terminators | {':', '}', '!'})
|
|
1186
|
+
else:
|
|
1187
|
+
expr = p_testlist_star_expr(s)
|
|
1188
|
+
|
|
1189
|
+
if s.sy == "=":
|
|
1190
|
+
self_documenting = True
|
|
1191
|
+
s.next()
|
|
1192
|
+
|
|
1193
|
+
if s.sy == "!":
|
|
1194
|
+
# format conversion
|
|
1195
|
+
previous_pos = s.position()
|
|
1196
|
+
s.next()
|
|
1197
|
+
conversion_char = s.systring
|
|
1198
|
+
# validate the conversion char
|
|
1199
|
+
if conversion_char in ['}', ':', '']:
|
|
1200
|
+
error(s.position(), "missing conversion character")
|
|
1201
|
+
elif not ExprNodes.FormattedValueNode.find_conversion_func(conversion_char):
|
|
1202
|
+
error(s.position(), "invalid conversion character '%s'" % conversion_char)
|
|
1203
|
+
s.next()
|
|
1204
|
+
elif s.position()[2] != (previous_pos[2] + 1):
|
|
1205
|
+
error(s.position(), "f-string: conversion type must come right after the exclamation mark")
|
|
1206
|
+
s.next()
|
|
1207
|
+
else:
|
|
1208
|
+
s.next()
|
|
1209
|
+
|
|
1210
|
+
if self_documenting or tf_string_kind == 't':
|
|
1211
|
+
if conversion_char is not None:
|
|
1212
|
+
expr_string, _ = expr_string.rsplit('!', 1)
|
|
1213
|
+
if tf_string_kind == 't':
|
|
1214
|
+
t_string_expression = ExprNodes.UnicodeNode(
|
|
1215
|
+
pos=expr_pos,
|
|
1216
|
+
value=StringEncoding.EncodedString(expr_string.rstrip().rstrip('=').rstrip())
|
|
1217
|
+
)
|
|
1218
|
+
if self_documenting:
|
|
1219
|
+
result.append(
|
|
1220
|
+
ExprNodes.UnicodeNode(
|
|
1221
|
+
pos=expr_pos,
|
|
1222
|
+
value=StringEncoding.EncodedString(expr_string)
|
|
1223
|
+
)
|
|
1224
|
+
)
|
|
1225
|
+
|
|
1226
|
+
# Validate that the expression string has actually ended
|
|
1227
|
+
while s.sy == "NEWLINE" or s.sy == "DEDENT":
|
|
1228
|
+
s.next()
|
|
1229
|
+
if s.sy != "EOF":
|
|
1230
|
+
error(
|
|
1231
|
+
s.position(),
|
|
1232
|
+
f"Unexpected characters after {tf_string_kind}-string expression: {s.systring}")
|
|
1233
|
+
|
|
1234
|
+
s = original_scanner
|
|
1235
|
+
|
|
1236
|
+
if s.sy == ":":
|
|
1237
|
+
# full format spec
|
|
1238
|
+
pos = s.position()
|
|
1239
|
+
# Contents of format spec are handled closer to an f-string than a t-string
|
|
1240
|
+
# (even for t-strings).
|
|
1241
|
+
format_spec_contents = p_ft_string_middles(s, is_raw, is_single_quoted, is_format_string=True, tf_string_kind='f')
|
|
1242
|
+
format_spec = ExprNodes.JoinedStrNode(
|
|
1243
|
+
pos,
|
|
1244
|
+
values=format_spec_contents
|
|
1245
|
+
)
|
|
1246
|
+
if self_documenting and conversion_char is None and format_spec is None:
|
|
1247
|
+
conversion_char = 'r'
|
|
1248
|
+
|
|
1249
|
+
if conversion_char is not None:
|
|
1250
|
+
conversion_char = StringEncoding.EncodedString(conversion_char)
|
|
1251
|
+
if tf_string_kind == 't':
|
|
1252
|
+
result.append(ExprNodes.TStringInterpolationNode(
|
|
1253
|
+
bracket_pos, value=expr, conversion_char=conversion_char,
|
|
1254
|
+
format_spec=format_spec, expression_str=t_string_expression
|
|
1255
|
+
))
|
|
1256
|
+
else:
|
|
1257
|
+
result.append(ExprNodes.FormattedValueNode(
|
|
1258
|
+
bracket_pos, value=expr, conversion_char=conversion_char,
|
|
1259
|
+
format_spec=format_spec
|
|
1260
|
+
))
|
|
1261
|
+
return result
|
|
1262
|
+
|
|
1263
|
+
@cython.cfunc
|
|
1264
|
+
def p_ft_string_middles(s: PyrexScanner,
|
|
1265
|
+
is_raw: cython.bint, is_single_quoted: cython.bint,
|
|
1266
|
+
is_format_string: cython.bint,
|
|
1267
|
+
tf_string_kind: cython.Py_UCS4):
|
|
1268
|
+
middles: list = []
|
|
1269
|
+
builder = StringEncoding.UnicodeLiteralBuilder()
|
|
1270
|
+
pos = s.position()
|
|
1271
|
+
while True:
|
|
1272
|
+
s.next()
|
|
1273
|
+
sy = s.sy
|
|
1274
|
+
|
|
1275
|
+
handled_chars = p_string_literal_shared_read(
|
|
1276
|
+
s, pos, builder, "u",
|
|
1277
|
+
is_raw=is_raw)
|
|
1278
|
+
if handled_chars is not None:
|
|
1279
|
+
continue
|
|
1280
|
+
|
|
1281
|
+
if builder.chars:
|
|
1282
|
+
middles.append(ExprNodes.UnicodeNode(pos, value=builder.getstring()))
|
|
1283
|
+
builder = StringEncoding.UnicodeLiteralBuilder()
|
|
1284
|
+
if sy == "{":
|
|
1285
|
+
fields = p_ft_string_replacement_field(
|
|
1286
|
+
s, is_raw, is_single_quoted, tf_string_kind=tf_string_kind)
|
|
1287
|
+
middles.extend(fields)
|
|
1288
|
+
if not s.sy == '}':
|
|
1289
|
+
s.expected('}')
|
|
1290
|
+
continue
|
|
1291
|
+
elif sy == "END_FT_STRING":
|
|
1292
|
+
break
|
|
1293
|
+
elif s.sy == '}':
|
|
1294
|
+
if is_format_string:
|
|
1295
|
+
break
|
|
1296
|
+
# otherwise it's an error, but the scanner has reported it
|
|
1297
|
+
else:
|
|
1298
|
+
error(
|
|
1299
|
+
s.position(),
|
|
1300
|
+
"Unexpected token %r:%r in %s-string literal" % (
|
|
1301
|
+
s.sy, s.systring, tf_string_kind))
|
|
1302
|
+
return middles
|
|
1303
|
+
|
|
1304
|
+
@cython.cfunc
|
|
1305
|
+
def p_ft_string_literal(s: PyrexScanner):
|
|
1306
|
+
# s.sy == BEGIN_FT_STRING
|
|
1307
|
+
kind_string = _validate_kind_string(s.position(), s.systring)
|
|
1308
|
+
tf_string_kind: cython.Py_UCS4 = 't' if 't' in kind_string else 'f'
|
|
1309
|
+
is_raw: cython.bint = 'r' in kind_string
|
|
1310
|
+
quotes = s.systring.lstrip("rRbBuUfFtT")
|
|
1311
|
+
is_single_quoted: cython.bint = len(quotes) != 3
|
|
1312
|
+
middles = p_ft_string_middles(s, is_raw, is_single_quoted, is_format_string=False, tf_string_kind=tf_string_kind)
|
|
1313
|
+
if s.sy != "END_FT_STRING":
|
|
1314
|
+
s.expected(quotes)
|
|
1315
|
+
s.next()
|
|
1316
|
+
return tf_string_kind, None, middles
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
@cython.cfunc
|
|
1320
|
+
def _append_escape_sequence(kind, builder, escape_sequence: str, s: PyrexScanner):
|
|
1321
|
+
if len(escape_sequence) < 2:
|
|
1322
|
+
builder.append("\\") # invalid escape sequence, warned earlier
|
|
1323
|
+
return
|
|
1324
|
+
c = escape_sequence[1]
|
|
1325
|
+
if c in "01234567":
|
|
1326
|
+
builder.append_charval(int(escape_sequence[1:], 8))
|
|
1327
|
+
elif c in "'\"\\":
|
|
1328
|
+
builder.append(c)
|
|
1329
|
+
elif c in "abfnrtv":
|
|
1330
|
+
builder.append(StringEncoding.char_from_escape_sequence(escape_sequence))
|
|
1331
|
+
elif c == '\n':
|
|
1332
|
+
pass # line continuation
|
|
1333
|
+
elif c == 'x': # \xXX
|
|
1334
|
+
if len(escape_sequence) == 4:
|
|
1335
|
+
builder.append_charval(int(escape_sequence[2:], 16))
|
|
1336
|
+
else:
|
|
1337
|
+
s.error("Invalid hex escape '%s'" % escape_sequence, fatal=False)
|
|
1338
|
+
elif c in 'NUu' and kind in ('u', 'f', ''): # \uxxxx, \Uxxxxxxxx, \N{...}
|
|
1339
|
+
chrval = -1
|
|
1340
|
+
if c == 'N':
|
|
1341
|
+
uchar = None
|
|
1342
|
+
try:
|
|
1343
|
+
uchar = lookup_unicodechar(escape_sequence[3:-1])
|
|
1344
|
+
chrval = ord(uchar)
|
|
1345
|
+
except KeyError:
|
|
1346
|
+
s.error("Unknown Unicode character name %s" %
|
|
1347
|
+
repr(escape_sequence[3:-1]).lstrip('u'), fatal=False)
|
|
1348
|
+
elif len(escape_sequence) in (6, 10):
|
|
1349
|
+
chrval = int(escape_sequence[2:], 16)
|
|
1350
|
+
if chrval > 1114111: # sys.maxunicode:
|
|
1351
|
+
s.error("Invalid unicode escape '%s'" % escape_sequence)
|
|
1352
|
+
chrval = -1
|
|
1353
|
+
else:
|
|
1354
|
+
s.error("Invalid unicode escape '%s'" % escape_sequence, fatal=False)
|
|
1355
|
+
if chrval >= 0:
|
|
1356
|
+
builder.append_uescape(chrval, escape_sequence)
|
|
1357
|
+
else:
|
|
1358
|
+
builder.append(escape_sequence)
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
# since PEP 448:
|
|
1362
|
+
# list_display ::= "[" [listmaker] "]"
|
|
1363
|
+
# listmaker ::= (named_test|star_expr) ( comp_for | (',' (named_test|star_expr))* [','] )
|
|
1364
|
+
# comp_iter ::= comp_for | comp_if
|
|
1365
|
+
# comp_for ::= ["async"] "for" expression_list "in" testlist [comp_iter]
|
|
1366
|
+
# comp_if ::= "if" test [comp_iter]
|
|
1367
|
+
|
|
1368
|
+
@cython.cfunc
|
|
1369
|
+
def p_list_maker(s: PyrexScanner):
|
|
1370
|
+
# s.sy == '['
|
|
1371
|
+
pos = s.position()
|
|
1372
|
+
s.next()
|
|
1373
|
+
if s.sy == ']':
|
|
1374
|
+
s.expect(']')
|
|
1375
|
+
return ExprNodes.ListNode(pos, args=[])
|
|
1376
|
+
|
|
1377
|
+
expr = p_namedexpr_test_or_starred_expr(s)
|
|
1378
|
+
if s.sy in ('for', 'async'):
|
|
1379
|
+
if expr.is_starred:
|
|
1380
|
+
s.error("iterable unpacking cannot be used in comprehension")
|
|
1381
|
+
append = ExprNodes.ComprehensionAppendNode(pos, expr=expr)
|
|
1382
|
+
loop = p_comp_for(s, append)
|
|
1383
|
+
s.expect(']')
|
|
1384
|
+
return ExprNodes.ComprehensionNode(
|
|
1385
|
+
pos, loop=loop, append=append, type=Builtin.list_type,
|
|
1386
|
+
# list comprehensions leak their loop variable in Py2
|
|
1387
|
+
has_local_scope=s.context.language_level >= 3)
|
|
1388
|
+
|
|
1389
|
+
# (merged) list literal
|
|
1390
|
+
if s.sy == ',':
|
|
1391
|
+
s.next()
|
|
1392
|
+
exprs = p_namedexpr_test_or_starred_expr_list(s, expr)
|
|
1393
|
+
else:
|
|
1394
|
+
exprs = [expr]
|
|
1395
|
+
s.expect(']')
|
|
1396
|
+
return ExprNodes.ListNode(pos, args=exprs)
|
|
1397
|
+
|
|
1398
|
+
|
|
1399
|
+
@cython.cfunc
|
|
1400
|
+
def p_comp_iter(s: PyrexScanner, body):
|
|
1401
|
+
if s.sy in ('for', 'async'):
|
|
1402
|
+
return p_comp_for(s, body)
|
|
1403
|
+
elif s.sy == 'if':
|
|
1404
|
+
return p_comp_if(s, body)
|
|
1405
|
+
else:
|
|
1406
|
+
# insert the 'append' operation into the loop
|
|
1407
|
+
return body
|
|
1408
|
+
|
|
1409
|
+
|
|
1410
|
+
@cython.cfunc
|
|
1411
|
+
def p_comp_for(s: PyrexScanner, body):
|
|
1412
|
+
pos = s.position()
|
|
1413
|
+
# [async] for ...
|
|
1414
|
+
is_async = False
|
|
1415
|
+
if s.sy == 'async':
|
|
1416
|
+
is_async = True
|
|
1417
|
+
s.next()
|
|
1418
|
+
|
|
1419
|
+
# s.sy == 'for'
|
|
1420
|
+
s.expect('for')
|
|
1421
|
+
kw = p_for_bounds(s, allow_testlist=False, is_async=is_async)
|
|
1422
|
+
kw.update(else_clause=None, body=p_comp_iter(s, body), is_async=is_async)
|
|
1423
|
+
return Nodes.ForStatNode(pos, **kw)
|
|
1424
|
+
|
|
1425
|
+
|
|
1426
|
+
@cython.cfunc
|
|
1427
|
+
def p_comp_if(s: PyrexScanner, body):
|
|
1428
|
+
# s.sy == 'if'
|
|
1429
|
+
pos = s.position()
|
|
1430
|
+
s.next()
|
|
1431
|
+
# Note that Python 3.9+ is actually more restrictive here and Cython now follows
|
|
1432
|
+
# the Python 3.9+ behaviour: https://github.com/python/cpython/issues/86014
|
|
1433
|
+
# On Python <3.9 `[i for i in range(10) if lambda: i if True else 1]` was disallowed
|
|
1434
|
+
# but `[i for i in range(10) if lambda: i]` was allowed.
|
|
1435
|
+
# On Python >=3.9 they're both disallowed.
|
|
1436
|
+
test = p_or_test(s)
|
|
1437
|
+
return Nodes.IfStatNode(pos,
|
|
1438
|
+
if_clauses = [Nodes.IfClauseNode(pos, condition = test,
|
|
1439
|
+
body = p_comp_iter(s, body))],
|
|
1440
|
+
else_clause = None )
|
|
1441
|
+
|
|
1442
|
+
|
|
1443
|
+
# since PEP 448:
|
|
1444
|
+
#dictorsetmaker: ( ((test ':' test | '**' expr)
|
|
1445
|
+
# (comp_for | (',' (test ':' test | '**' expr))* [','])) |
|
|
1446
|
+
# ((test | star_expr)
|
|
1447
|
+
# (comp_for | (',' (test | star_expr))* [','])) )
|
|
1448
|
+
|
|
1449
|
+
@cython.cfunc
|
|
1450
|
+
def p_dict_or_set_maker(s: PyrexScanner):
|
|
1451
|
+
# s.sy == '{'
|
|
1452
|
+
pos = s.position()
|
|
1453
|
+
s.next()
|
|
1454
|
+
if s.sy == '}':
|
|
1455
|
+
s.next()
|
|
1456
|
+
return ExprNodes.DictNode(pos, key_value_pairs=[])
|
|
1457
|
+
|
|
1458
|
+
parts = []
|
|
1459
|
+
target_type: cython.int = 0
|
|
1460
|
+
last_was_simple_item = False
|
|
1461
|
+
while True:
|
|
1462
|
+
if s.sy in ('*', '**'):
|
|
1463
|
+
# merged set/dict literal
|
|
1464
|
+
if target_type == 0:
|
|
1465
|
+
target_type = 1 if s.sy == '*' else 2 # 'stars'
|
|
1466
|
+
elif target_type != len(s.sy):
|
|
1467
|
+
s.error("unexpected %sitem found in %s literal" % (
|
|
1468
|
+
s.sy, 'set' if target_type == 1 else 'dict'))
|
|
1469
|
+
s.next()
|
|
1470
|
+
if s.sy == '*':
|
|
1471
|
+
s.error("expected expression, found '*'")
|
|
1472
|
+
item = p_starred_expr(s)
|
|
1473
|
+
parts.append(item)
|
|
1474
|
+
last_was_simple_item = False
|
|
1475
|
+
else:
|
|
1476
|
+
item = p_test(s)
|
|
1477
|
+
if target_type == 0:
|
|
1478
|
+
target_type = 2 if s.sy == ':' else 1 # dict vs. set
|
|
1479
|
+
if target_type == 2:
|
|
1480
|
+
# dict literal
|
|
1481
|
+
s.expect(':')
|
|
1482
|
+
key = item
|
|
1483
|
+
value = p_test(s)
|
|
1484
|
+
item = ExprNodes.DictItemNode(key.pos, key=key, value=value)
|
|
1485
|
+
if last_was_simple_item:
|
|
1486
|
+
parts[-1].append(item)
|
|
1487
|
+
else:
|
|
1488
|
+
parts.append([item])
|
|
1489
|
+
last_was_simple_item = True
|
|
1490
|
+
|
|
1491
|
+
if s.sy == ',':
|
|
1492
|
+
s.next()
|
|
1493
|
+
if s.sy == '}':
|
|
1494
|
+
break
|
|
1495
|
+
else:
|
|
1496
|
+
break
|
|
1497
|
+
|
|
1498
|
+
if s.sy in ('for', 'async'):
|
|
1499
|
+
# dict/set comprehension
|
|
1500
|
+
if len(parts) == 1 and isinstance(parts[0], list) and len(parts[0]) == 1:
|
|
1501
|
+
item = parts[0][0]
|
|
1502
|
+
if target_type == 2:
|
|
1503
|
+
assert isinstance(item, ExprNodes.DictItemNode), type(item)
|
|
1504
|
+
comprehension_type = Builtin.dict_type
|
|
1505
|
+
append = ExprNodes.DictComprehensionAppendNode(
|
|
1506
|
+
item.pos, key_expr=item.key, value_expr=item.value)
|
|
1507
|
+
else:
|
|
1508
|
+
comprehension_type = Builtin.set_type
|
|
1509
|
+
append = ExprNodes.ComprehensionAppendNode(item.pos, expr=item)
|
|
1510
|
+
loop = p_comp_for(s, append)
|
|
1511
|
+
s.expect('}')
|
|
1512
|
+
return ExprNodes.ComprehensionNode(pos, loop=loop, append=append, type=comprehension_type)
|
|
1513
|
+
else:
|
|
1514
|
+
# syntax error, try to find a good error message
|
|
1515
|
+
if len(parts) == 1 and not isinstance(parts[0], list):
|
|
1516
|
+
s.error("iterable unpacking cannot be used in comprehension")
|
|
1517
|
+
else:
|
|
1518
|
+
# e.g. "{1,2,3 for ..."
|
|
1519
|
+
s.expect('}')
|
|
1520
|
+
return ExprNodes.DictNode(pos, key_value_pairs=[])
|
|
1521
|
+
|
|
1522
|
+
s.expect('}')
|
|
1523
|
+
if target_type == 1:
|
|
1524
|
+
# (merged) set literal
|
|
1525
|
+
items = []
|
|
1526
|
+
set_items = []
|
|
1527
|
+
for part in parts:
|
|
1528
|
+
if isinstance(part, list):
|
|
1529
|
+
set_items.extend(part)
|
|
1530
|
+
else:
|
|
1531
|
+
if set_items:
|
|
1532
|
+
items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items))
|
|
1533
|
+
set_items = []
|
|
1534
|
+
items.append(part)
|
|
1535
|
+
if set_items:
|
|
1536
|
+
items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items))
|
|
1537
|
+
if len(items) == 1 and items[0].is_set_literal:
|
|
1538
|
+
return items[0]
|
|
1539
|
+
return ExprNodes.MergedSequenceNode(pos, args=items, type=Builtin.set_type)
|
|
1540
|
+
else:
|
|
1541
|
+
# (merged) dict literal
|
|
1542
|
+
items = []
|
|
1543
|
+
dict_items = []
|
|
1544
|
+
for part in parts:
|
|
1545
|
+
if isinstance(part, list):
|
|
1546
|
+
dict_items.extend(part)
|
|
1547
|
+
else:
|
|
1548
|
+
if dict_items:
|
|
1549
|
+
items.append(ExprNodes.DictNode(dict_items[0].pos, key_value_pairs=dict_items))
|
|
1550
|
+
dict_items = []
|
|
1551
|
+
items.append(part)
|
|
1552
|
+
if dict_items:
|
|
1553
|
+
items.append(ExprNodes.DictNode(dict_items[0].pos, key_value_pairs=dict_items))
|
|
1554
|
+
if len(items) == 1 and items[0].is_dict_literal:
|
|
1555
|
+
return items[0]
|
|
1556
|
+
return ExprNodes.MergedDictNode(pos, keyword_args=items, reject_duplicates=False)
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
# NOTE: no longer in Py3 :)
|
|
1560
|
+
@cython.cfunc
|
|
1561
|
+
def p_backquote_expr(s: PyrexScanner):
|
|
1562
|
+
# s.sy == '`'
|
|
1563
|
+
pos = s.position()
|
|
1564
|
+
s.next()
|
|
1565
|
+
args = [p_test(s)]
|
|
1566
|
+
while s.sy == ',':
|
|
1567
|
+
s.next()
|
|
1568
|
+
args.append(p_test(s))
|
|
1569
|
+
s.expect('`')
|
|
1570
|
+
if len(args) == 1:
|
|
1571
|
+
arg = args[0]
|
|
1572
|
+
else:
|
|
1573
|
+
arg = ExprNodes.TupleNode(pos, args = args)
|
|
1574
|
+
return ExprNodes.BackquoteNode(pos, arg = arg)
|
|
1575
|
+
|
|
1576
|
+
|
|
1577
|
+
@cython.cfunc
|
|
1578
|
+
def p_simple_expr_list(s: PyrexScanner, expr=None) -> list:
|
|
1579
|
+
exprs: list = [expr] if expr is not None else []
|
|
1580
|
+
while s.sy not in expr_terminators:
|
|
1581
|
+
exprs.append( p_test(s) )
|
|
1582
|
+
if s.sy != ',':
|
|
1583
|
+
break
|
|
1584
|
+
s.next()
|
|
1585
|
+
return exprs
|
|
1586
|
+
|
|
1587
|
+
|
|
1588
|
+
@cython.cfunc
|
|
1589
|
+
def p_test_or_starred_expr_list(s: PyrexScanner, expr=None) -> list:
|
|
1590
|
+
exprs: list = [expr] if expr is not None else []
|
|
1591
|
+
while s.sy not in expr_terminators:
|
|
1592
|
+
exprs.append(p_test_or_starred_expr(s))
|
|
1593
|
+
if s.sy != ',':
|
|
1594
|
+
break
|
|
1595
|
+
s.next()
|
|
1596
|
+
return exprs
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
@cython.cfunc
|
|
1600
|
+
def p_namedexpr_test_or_starred_expr_list(s: PyrexScanner, expr=None) -> list:
|
|
1601
|
+
exprs: list = [expr] if expr is not None else []
|
|
1602
|
+
while s.sy not in expr_terminators:
|
|
1603
|
+
exprs.append(p_namedexpr_test_or_starred_expr(s))
|
|
1604
|
+
if s.sy != ',':
|
|
1605
|
+
break
|
|
1606
|
+
s.next()
|
|
1607
|
+
return exprs
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
#testlist: test (',' test)* [',']
|
|
1611
|
+
|
|
1612
|
+
@cython.cfunc
|
|
1613
|
+
def p_testlist(s: PyrexScanner):
|
|
1614
|
+
pos = s.position()
|
|
1615
|
+
expr = p_test(s)
|
|
1616
|
+
if s.sy == ',':
|
|
1617
|
+
s.next()
|
|
1618
|
+
exprs = p_simple_expr_list(s, expr)
|
|
1619
|
+
return ExprNodes.TupleNode(pos, args = exprs)
|
|
1620
|
+
else:
|
|
1621
|
+
return expr
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
# testlist_star_expr: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
|
1625
|
+
|
|
1626
|
+
@cython.cfunc
|
|
1627
|
+
def p_testlist_star_expr(s: PyrexScanner):
|
|
1628
|
+
pos = s.position()
|
|
1629
|
+
expr = p_test_or_starred_expr(s)
|
|
1630
|
+
if s.sy == ',':
|
|
1631
|
+
s.next()
|
|
1632
|
+
exprs = p_test_or_starred_expr_list(s, expr)
|
|
1633
|
+
return ExprNodes.TupleNode(pos, args = exprs)
|
|
1634
|
+
else:
|
|
1635
|
+
return expr
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
# testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
|
1639
|
+
|
|
1640
|
+
@cython.cfunc
|
|
1641
|
+
def p_testlist_comp(s: PyrexScanner):
|
|
1642
|
+
pos = s.position()
|
|
1643
|
+
expr = p_namedexpr_test_or_starred_expr(s)
|
|
1644
|
+
if s.sy == ',':
|
|
1645
|
+
s.next()
|
|
1646
|
+
exprs = p_namedexpr_test_or_starred_expr_list(s, expr)
|
|
1647
|
+
return ExprNodes.TupleNode(pos, args = exprs)
|
|
1648
|
+
elif s.sy in ('for', 'async'):
|
|
1649
|
+
return p_genexp(s, expr)
|
|
1650
|
+
else:
|
|
1651
|
+
return expr
|
|
1652
|
+
|
|
1653
|
+
|
|
1654
|
+
@cython.cfunc
|
|
1655
|
+
def p_genexp(s: PyrexScanner, expr):
|
|
1656
|
+
# s.sy == 'async' | 'for'
|
|
1657
|
+
loop = p_comp_for(s, Nodes.ExprStatNode(
|
|
1658
|
+
expr.pos, expr = ExprNodes.YieldExprNode(expr.pos, arg=expr)))
|
|
1659
|
+
return ExprNodes.GeneratorExpressionNode(expr.pos, loop=loop)
|
|
1660
|
+
|
|
1661
|
+
|
|
1662
|
+
expr_terminators = cython.declare(frozenset, frozenset((
|
|
1663
|
+
')', ']', '}', ':', '=', 'NEWLINE', 'EOF')))
|
|
1664
|
+
|
|
1665
|
+
|
|
1666
|
+
#-------------------------------------------------------
|
|
1667
|
+
#
|
|
1668
|
+
# Statements
|
|
1669
|
+
#
|
|
1670
|
+
#-------------------------------------------------------
|
|
1671
|
+
|
|
1672
|
+
@cython.cfunc
|
|
1673
|
+
def p_global_statement(s: PyrexScanner):
|
|
1674
|
+
# assume s.sy == 'global'
|
|
1675
|
+
pos = s.position()
|
|
1676
|
+
s.next()
|
|
1677
|
+
names = p_ident_list(s)
|
|
1678
|
+
return Nodes.GlobalNode(pos, names = names)
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
@cython.cfunc
|
|
1682
|
+
def p_nonlocal_statement(s: PyrexScanner):
|
|
1683
|
+
pos = s.position()
|
|
1684
|
+
s.next()
|
|
1685
|
+
names = p_ident_list(s)
|
|
1686
|
+
return Nodes.NonlocalNode(pos, names = names)
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
@cython.cfunc
|
|
1690
|
+
def p_expression_or_assignment(s: PyrexScanner):
|
|
1691
|
+
expr = p_testlist_star_expr(s)
|
|
1692
|
+
has_annotation = False
|
|
1693
|
+
if s.sy == ':' and (expr.is_name or expr.is_subscript or expr.is_attribute):
|
|
1694
|
+
has_annotation = True
|
|
1695
|
+
s.next()
|
|
1696
|
+
expr.annotation = p_annotation(s)
|
|
1697
|
+
|
|
1698
|
+
if s.sy == '=' and expr.is_starred:
|
|
1699
|
+
# This is a common enough error to make when learning Cython to let
|
|
1700
|
+
# it fail as early as possible and give a very clear error message.
|
|
1701
|
+
s.error("a starred assignment target must be in a list or tuple"
|
|
1702
|
+
" - maybe you meant to use an index assignment: var[0] = ...",
|
|
1703
|
+
pos=expr.pos)
|
|
1704
|
+
|
|
1705
|
+
expr_list = [expr]
|
|
1706
|
+
while s.sy == '=':
|
|
1707
|
+
s.next()
|
|
1708
|
+
if s.sy == 'yield':
|
|
1709
|
+
expr = p_yield_expression(s)
|
|
1710
|
+
else:
|
|
1711
|
+
expr = p_testlist_star_expr(s)
|
|
1712
|
+
expr_list.append(expr)
|
|
1713
|
+
if len(expr_list) == 1:
|
|
1714
|
+
if re.match(r"([-+*/%^&|]|<<|>>|\*\*|//|@)=", s.sy):
|
|
1715
|
+
lhs = expr_list[0]
|
|
1716
|
+
if isinstance(lhs, ExprNodes.SliceIndexNode):
|
|
1717
|
+
# implementation requires IndexNode
|
|
1718
|
+
lhs = ExprNodes.IndexNode(
|
|
1719
|
+
lhs.pos,
|
|
1720
|
+
base=lhs.base,
|
|
1721
|
+
index=make_slice_node(lhs.pos, lhs.start, lhs.stop))
|
|
1722
|
+
elif not isinstance(lhs, (ExprNodes.AttributeNode, ExprNodes.IndexNode, ExprNodes.NameNode)):
|
|
1723
|
+
error(lhs.pos, "Illegal operand for inplace operation.")
|
|
1724
|
+
operator = s.sy[:-1]
|
|
1725
|
+
s.next()
|
|
1726
|
+
if s.sy == 'yield':
|
|
1727
|
+
rhs = p_yield_expression(s)
|
|
1728
|
+
else:
|
|
1729
|
+
rhs = p_testlist(s)
|
|
1730
|
+
return Nodes.InPlaceAssignmentNode(lhs.pos, operator=operator, lhs=lhs, rhs=rhs)
|
|
1731
|
+
expr = expr_list[0]
|
|
1732
|
+
return Nodes.ExprStatNode(expr.pos, expr=expr)
|
|
1733
|
+
|
|
1734
|
+
rhs = expr_list[-1]
|
|
1735
|
+
if len(expr_list) == 2:
|
|
1736
|
+
return Nodes.SingleAssignmentNode(rhs.pos, lhs=expr_list[0], rhs=rhs, first=has_annotation)
|
|
1737
|
+
else:
|
|
1738
|
+
return Nodes.CascadedAssignmentNode(rhs.pos, lhs_list=expr_list[:-1], rhs=rhs)
|
|
1739
|
+
|
|
1740
|
+
|
|
1741
|
+
@cython.cfunc
|
|
1742
|
+
def p_print_statement(s: PyrexScanner):
|
|
1743
|
+
# s.sy == 'print'
|
|
1744
|
+
pos = s.position()
|
|
1745
|
+
ends_with_comma: cython.bint = False
|
|
1746
|
+
s.next()
|
|
1747
|
+
if s.sy == '>>':
|
|
1748
|
+
s.next()
|
|
1749
|
+
stream = p_test(s)
|
|
1750
|
+
if s.sy == ',':
|
|
1751
|
+
s.next()
|
|
1752
|
+
ends_with_comma = s.sy in ('NEWLINE', 'EOF')
|
|
1753
|
+
else:
|
|
1754
|
+
stream = None
|
|
1755
|
+
args = []
|
|
1756
|
+
if s.sy not in ('NEWLINE', 'EOF'):
|
|
1757
|
+
args.append(p_test(s))
|
|
1758
|
+
while s.sy == ',':
|
|
1759
|
+
s.next()
|
|
1760
|
+
if s.sy in ('NEWLINE', 'EOF'):
|
|
1761
|
+
ends_with_comma = True
|
|
1762
|
+
break
|
|
1763
|
+
args.append(p_test(s))
|
|
1764
|
+
arg_tuple = ExprNodes.TupleNode(pos, args=args)
|
|
1765
|
+
return Nodes.PrintStatNode(pos,
|
|
1766
|
+
arg_tuple=arg_tuple, stream=stream,
|
|
1767
|
+
append_newline=not ends_with_comma)
|
|
1768
|
+
|
|
1769
|
+
|
|
1770
|
+
@cython.cfunc
|
|
1771
|
+
def p_exec_statement(s: PyrexScanner):
|
|
1772
|
+
# s.sy == 'exec'
|
|
1773
|
+
pos = s.position()
|
|
1774
|
+
s.next()
|
|
1775
|
+
code = p_bit_expr(s)
|
|
1776
|
+
if isinstance(code, ExprNodes.TupleNode):
|
|
1777
|
+
# Py3 compatibility syntax
|
|
1778
|
+
tuple_variant = True
|
|
1779
|
+
args = code.args
|
|
1780
|
+
if len(args) not in (2, 3):
|
|
1781
|
+
s.error("expected tuple of length 2 or 3, got length %d" % len(args),
|
|
1782
|
+
pos=pos, fatal=False)
|
|
1783
|
+
args = [code]
|
|
1784
|
+
else:
|
|
1785
|
+
tuple_variant = False
|
|
1786
|
+
args = [code]
|
|
1787
|
+
if s.sy == 'in':
|
|
1788
|
+
if tuple_variant:
|
|
1789
|
+
s.error("tuple variant of exec does not support additional 'in' arguments",
|
|
1790
|
+
fatal=False)
|
|
1791
|
+
s.next()
|
|
1792
|
+
args.append(p_test(s))
|
|
1793
|
+
if s.sy == ',':
|
|
1794
|
+
s.next()
|
|
1795
|
+
args.append(p_test(s))
|
|
1796
|
+
return Nodes.ExecStatNode(pos, args=args)
|
|
1797
|
+
|
|
1798
|
+
|
|
1799
|
+
@cython.cfunc
|
|
1800
|
+
def p_del_statement(s: PyrexScanner):
|
|
1801
|
+
# s.sy == 'del'
|
|
1802
|
+
pos = s.position()
|
|
1803
|
+
s.next()
|
|
1804
|
+
# FIXME: 'exprlist' in Python
|
|
1805
|
+
args = p_simple_expr_list(s)
|
|
1806
|
+
return Nodes.DelStatNode(pos, args = args)
|
|
1807
|
+
|
|
1808
|
+
|
|
1809
|
+
@cython.cfunc
|
|
1810
|
+
def p_pass_statement(s: PyrexScanner, with_newline: cython.bint = False):
|
|
1811
|
+
pos = s.position()
|
|
1812
|
+
s.expect('pass')
|
|
1813
|
+
if with_newline:
|
|
1814
|
+
s.expect_newline("Expected a newline", ignore_semicolon=True)
|
|
1815
|
+
return Nodes.PassStatNode(pos)
|
|
1816
|
+
|
|
1817
|
+
|
|
1818
|
+
@cython.cfunc
|
|
1819
|
+
def p_break_statement(s: PyrexScanner):
|
|
1820
|
+
# s.sy == 'break'
|
|
1821
|
+
pos = s.position()
|
|
1822
|
+
s.next()
|
|
1823
|
+
return Nodes.BreakStatNode(pos)
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
@cython.cfunc
|
|
1827
|
+
def p_continue_statement(s: PyrexScanner):
|
|
1828
|
+
# s.sy == 'continue'
|
|
1829
|
+
pos = s.position()
|
|
1830
|
+
s.next()
|
|
1831
|
+
return Nodes.ContinueStatNode(pos)
|
|
1832
|
+
|
|
1833
|
+
|
|
1834
|
+
@cython.cfunc
|
|
1835
|
+
def p_return_statement(s: PyrexScanner):
|
|
1836
|
+
# s.sy == 'return'
|
|
1837
|
+
pos = s.position()
|
|
1838
|
+
s.next()
|
|
1839
|
+
if s.sy not in statement_terminators:
|
|
1840
|
+
value = p_testlist(s)
|
|
1841
|
+
else:
|
|
1842
|
+
value = None
|
|
1843
|
+
return Nodes.ReturnStatNode(pos, value = value)
|
|
1844
|
+
|
|
1845
|
+
|
|
1846
|
+
@cython.cfunc
|
|
1847
|
+
def p_raise_statement(s: PyrexScanner):
|
|
1848
|
+
# s.sy == 'raise'
|
|
1849
|
+
pos = s.position()
|
|
1850
|
+
s.next()
|
|
1851
|
+
exc_type = None
|
|
1852
|
+
exc_value = None
|
|
1853
|
+
exc_tb = None
|
|
1854
|
+
cause = None
|
|
1855
|
+
if s.sy not in statement_terminators:
|
|
1856
|
+
exc_type = p_test(s)
|
|
1857
|
+
if s.sy == ',':
|
|
1858
|
+
s.next()
|
|
1859
|
+
exc_value = p_test(s)
|
|
1860
|
+
if s.sy == ',':
|
|
1861
|
+
s.next()
|
|
1862
|
+
exc_tb = p_test(s)
|
|
1863
|
+
elif s.sy == 'from':
|
|
1864
|
+
s.next()
|
|
1865
|
+
cause = p_test(s)
|
|
1866
|
+
if exc_type or exc_value or exc_tb:
|
|
1867
|
+
return Nodes.RaiseStatNode(pos,
|
|
1868
|
+
exc_type = exc_type,
|
|
1869
|
+
exc_value = exc_value,
|
|
1870
|
+
exc_tb = exc_tb,
|
|
1871
|
+
cause = cause)
|
|
1872
|
+
else:
|
|
1873
|
+
return Nodes.ReraiseStatNode(pos)
|
|
1874
|
+
|
|
1875
|
+
|
|
1876
|
+
@cython.cfunc
|
|
1877
|
+
def p_import_statement(s: PyrexScanner):
|
|
1878
|
+
# s.sy in ('import', 'cimport')
|
|
1879
|
+
pos = s.position()
|
|
1880
|
+
kind = s.sy
|
|
1881
|
+
s.next()
|
|
1882
|
+
items = [p_dotted_name(s, as_allowed=True)]
|
|
1883
|
+
while s.sy == ',':
|
|
1884
|
+
s.next()
|
|
1885
|
+
items.append(p_dotted_name(s, as_allowed=True))
|
|
1886
|
+
stats = []
|
|
1887
|
+
is_absolute = Future.absolute_import in s.context.future_directives
|
|
1888
|
+
for pos, target_name, dotted_name, as_name in items:
|
|
1889
|
+
if kind == 'cimport':
|
|
1890
|
+
stat = Nodes.CImportStatNode(
|
|
1891
|
+
pos,
|
|
1892
|
+
module_name=dotted_name,
|
|
1893
|
+
as_name=as_name,
|
|
1894
|
+
is_absolute=is_absolute)
|
|
1895
|
+
else:
|
|
1896
|
+
stat = Nodes.SingleAssignmentNode(
|
|
1897
|
+
pos,
|
|
1898
|
+
lhs=ExprNodes.NameNode(pos, name=as_name or target_name),
|
|
1899
|
+
rhs=ExprNodes.ImportNode(
|
|
1900
|
+
pos,
|
|
1901
|
+
module_name=ExprNodes.IdentifierStringNode(pos, value=dotted_name),
|
|
1902
|
+
is_import_as_name=bool(as_name),
|
|
1903
|
+
level=0 if is_absolute else None,
|
|
1904
|
+
imported_names=None))
|
|
1905
|
+
stats.append(stat)
|
|
1906
|
+
return Nodes.StatListNode(pos, stats=stats)
|
|
1907
|
+
|
|
1908
|
+
|
|
1909
|
+
@cython.cfunc
|
|
1910
|
+
def p_from_import_statement(s: PyrexScanner, first_statement: cython.bint = 0):
|
|
1911
|
+
# s.sy == 'from'
|
|
1912
|
+
pos = s.position()
|
|
1913
|
+
s.next()
|
|
1914
|
+
if s.sy in ('.', '...'):
|
|
1915
|
+
# count relative import level
|
|
1916
|
+
level = 0
|
|
1917
|
+
while s.sy in ('.', '...'):
|
|
1918
|
+
level += len(s.sy)
|
|
1919
|
+
s.next()
|
|
1920
|
+
else:
|
|
1921
|
+
level = None
|
|
1922
|
+
if level is not None and s.sy in ('import', 'cimport'):
|
|
1923
|
+
# we are dealing with "from .. import foo, bar"
|
|
1924
|
+
dotted_name_pos, dotted_name = s.position(), s.context.intern_ustring('')
|
|
1925
|
+
else:
|
|
1926
|
+
if level is None and Future.absolute_import in s.context.future_directives:
|
|
1927
|
+
level = 0
|
|
1928
|
+
(dotted_name_pos, _, dotted_name, _) = p_dotted_name(s, as_allowed=False)
|
|
1929
|
+
if s.sy not in ('import', 'cimport'):
|
|
1930
|
+
s.error("Expected 'import' or 'cimport'")
|
|
1931
|
+
kind = s.sy
|
|
1932
|
+
s.next()
|
|
1933
|
+
|
|
1934
|
+
is_cimport = kind == 'cimport'
|
|
1935
|
+
is_parenthesized = False
|
|
1936
|
+
if s.sy == '*':
|
|
1937
|
+
imported_names = [(s.position(), s.context.intern_ustring("*"), None)]
|
|
1938
|
+
s.next()
|
|
1939
|
+
else:
|
|
1940
|
+
if s.sy == '(':
|
|
1941
|
+
is_parenthesized = True
|
|
1942
|
+
s.next()
|
|
1943
|
+
imported_names = [p_imported_name(s)]
|
|
1944
|
+
while s.sy == ',':
|
|
1945
|
+
s.next()
|
|
1946
|
+
if is_parenthesized and s.sy == ')':
|
|
1947
|
+
break
|
|
1948
|
+
imported_names.append(p_imported_name(s))
|
|
1949
|
+
if is_parenthesized:
|
|
1950
|
+
s.expect(')')
|
|
1951
|
+
if dotted_name == '__future__':
|
|
1952
|
+
if not first_statement:
|
|
1953
|
+
s.error("from __future__ imports must occur at the beginning of the file")
|
|
1954
|
+
elif level:
|
|
1955
|
+
s.error("invalid syntax")
|
|
1956
|
+
else:
|
|
1957
|
+
for (name_pos, name, as_name) in imported_names:
|
|
1958
|
+
if name == "braces":
|
|
1959
|
+
s.error("not a chance", name_pos)
|
|
1960
|
+
break
|
|
1961
|
+
try:
|
|
1962
|
+
directive = getattr(Future, name)
|
|
1963
|
+
except AttributeError:
|
|
1964
|
+
s.error("future feature %s is not defined" % name, name_pos)
|
|
1965
|
+
break
|
|
1966
|
+
s.context.future_directives.add(directive)
|
|
1967
|
+
return Nodes.PassStatNode(pos)
|
|
1968
|
+
elif is_cimport:
|
|
1969
|
+
return Nodes.FromCImportStatNode(
|
|
1970
|
+
pos, module_name=dotted_name,
|
|
1971
|
+
relative_level=level,
|
|
1972
|
+
imported_names=imported_names)
|
|
1973
|
+
else:
|
|
1974
|
+
imported_name_strings = []
|
|
1975
|
+
items = []
|
|
1976
|
+
for (name_pos, name, as_name) in imported_names:
|
|
1977
|
+
imported_name_strings.append(
|
|
1978
|
+
ExprNodes.IdentifierStringNode(name_pos, value=name))
|
|
1979
|
+
items.append(
|
|
1980
|
+
(name, ExprNodes.NameNode(name_pos, name=as_name or name)))
|
|
1981
|
+
return Nodes.FromImportStatNode(pos,
|
|
1982
|
+
module = ExprNodes.ImportNode(dotted_name_pos,
|
|
1983
|
+
module_name = ExprNodes.IdentifierStringNode(pos, value = dotted_name),
|
|
1984
|
+
is_import_as_name = False,
|
|
1985
|
+
level = level,
|
|
1986
|
+
imported_names = imported_name_strings),
|
|
1987
|
+
items = items)
|
|
1988
|
+
|
|
1989
|
+
|
|
1990
|
+
@cython.cfunc
|
|
1991
|
+
def p_imported_name(s: PyrexScanner):
|
|
1992
|
+
pos = s.position()
|
|
1993
|
+
name = p_ident(s)
|
|
1994
|
+
as_name = p_as_name(s)
|
|
1995
|
+
return (pos, name, as_name)
|
|
1996
|
+
|
|
1997
|
+
|
|
1998
|
+
@cython.cfunc
|
|
1999
|
+
def p_dotted_name(s: PyrexScanner, as_allowed: cython.bint) -> tuple:
|
|
2000
|
+
pos = s.position()
|
|
2001
|
+
target_name = p_ident(s)
|
|
2002
|
+
as_name = None
|
|
2003
|
+
names = [target_name]
|
|
2004
|
+
while s.sy == '.':
|
|
2005
|
+
s.next()
|
|
2006
|
+
names.append(p_ident(s))
|
|
2007
|
+
if as_allowed:
|
|
2008
|
+
as_name = p_as_name(s)
|
|
2009
|
+
return (pos, target_name, s.context.intern_ustring('.'.join(names)), as_name)
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
@cython.cfunc
|
|
2013
|
+
def p_as_name(s: PyrexScanner):
|
|
2014
|
+
if s.sy == 'IDENT' and s.systring == 'as':
|
|
2015
|
+
s.next()
|
|
2016
|
+
return p_ident(s)
|
|
2017
|
+
else:
|
|
2018
|
+
return None
|
|
2019
|
+
|
|
2020
|
+
|
|
2021
|
+
@cython.cfunc
|
|
2022
|
+
def p_assert_statement(s: PyrexScanner):
|
|
2023
|
+
# s.sy == 'assert'
|
|
2024
|
+
pos = s.position()
|
|
2025
|
+
s.next()
|
|
2026
|
+
cond = p_test(s)
|
|
2027
|
+
if s.sy == ',':
|
|
2028
|
+
s.next()
|
|
2029
|
+
value = p_test(s)
|
|
2030
|
+
else:
|
|
2031
|
+
value = None
|
|
2032
|
+
return Nodes.AssertStatNode(pos, condition=cond, value=value)
|
|
2033
|
+
|
|
2034
|
+
|
|
2035
|
+
@cython.cfunc
|
|
2036
|
+
def p_if_statement(s: PyrexScanner):
|
|
2037
|
+
# s.sy == 'if'
|
|
2038
|
+
pos = s.position()
|
|
2039
|
+
s.next()
|
|
2040
|
+
if_clauses = [p_if_clause(s)]
|
|
2041
|
+
while s.sy == 'elif':
|
|
2042
|
+
s.next()
|
|
2043
|
+
if_clauses.append(p_if_clause(s))
|
|
2044
|
+
else_clause = p_else_clause(s)
|
|
2045
|
+
return Nodes.IfStatNode(pos,
|
|
2046
|
+
if_clauses = if_clauses, else_clause = else_clause)
|
|
2047
|
+
|
|
2048
|
+
|
|
2049
|
+
@cython.cfunc
|
|
2050
|
+
def p_if_clause(s: PyrexScanner):
|
|
2051
|
+
pos = s.position()
|
|
2052
|
+
test = p_namedexpr_test(s)
|
|
2053
|
+
body = p_suite(s)
|
|
2054
|
+
return Nodes.IfClauseNode(pos,
|
|
2055
|
+
condition = test, body = body)
|
|
2056
|
+
|
|
2057
|
+
|
|
2058
|
+
@cython.cfunc
|
|
2059
|
+
def p_else_clause(s: PyrexScanner):
|
|
2060
|
+
if s.sy == 'else':
|
|
2061
|
+
s.next()
|
|
2062
|
+
return p_suite(s)
|
|
2063
|
+
else:
|
|
2064
|
+
return None
|
|
2065
|
+
|
|
2066
|
+
|
|
2067
|
+
@cython.cfunc
|
|
2068
|
+
def p_while_statement(s: PyrexScanner):
|
|
2069
|
+
# s.sy == 'while'
|
|
2070
|
+
pos = s.position()
|
|
2071
|
+
s.next()
|
|
2072
|
+
test = p_namedexpr_test(s)
|
|
2073
|
+
body = p_suite(s)
|
|
2074
|
+
else_clause = p_else_clause(s)
|
|
2075
|
+
return Nodes.WhileStatNode(pos,
|
|
2076
|
+
condition = test, body = body,
|
|
2077
|
+
else_clause = else_clause)
|
|
2078
|
+
|
|
2079
|
+
|
|
2080
|
+
@cython.cfunc
|
|
2081
|
+
def p_for_statement(s: PyrexScanner, is_async: cython.bint = False):
|
|
2082
|
+
# s.sy == 'for'
|
|
2083
|
+
pos = s.position()
|
|
2084
|
+
s.next()
|
|
2085
|
+
kw = p_for_bounds(s, allow_testlist=True, is_async=is_async)
|
|
2086
|
+
body = p_suite(s)
|
|
2087
|
+
else_clause = p_else_clause(s)
|
|
2088
|
+
kw.update(body=body, else_clause=else_clause, is_async=is_async)
|
|
2089
|
+
return Nodes.ForStatNode(pos, **kw)
|
|
2090
|
+
|
|
2091
|
+
|
|
2092
|
+
@cython.cfunc
|
|
2093
|
+
def p_for_bounds(s: PyrexScanner, allow_testlist: cython.bint = True, is_async: cython.bint = False) -> dict:
|
|
2094
|
+
target = p_for_target(s)
|
|
2095
|
+
if s.sy == 'in':
|
|
2096
|
+
s.next()
|
|
2097
|
+
iterator = p_for_iterator(s, allow_testlist, is_async=is_async)
|
|
2098
|
+
return dict(target=target, iterator=iterator)
|
|
2099
|
+
elif not s.in_python_file and not is_async:
|
|
2100
|
+
if s.sy == 'from':
|
|
2101
|
+
s.next()
|
|
2102
|
+
bound1 = p_bit_expr(s)
|
|
2103
|
+
else:
|
|
2104
|
+
# Support shorter "for a <= x < b" syntax
|
|
2105
|
+
bound1, target = target, None
|
|
2106
|
+
rel1 = p_for_from_relation(s)
|
|
2107
|
+
name2_pos = s.position()
|
|
2108
|
+
name2 = p_ident(s)
|
|
2109
|
+
rel2_pos = s.position()
|
|
2110
|
+
rel2 = p_for_from_relation(s)
|
|
2111
|
+
bound2 = p_bit_expr(s)
|
|
2112
|
+
step = p_for_from_step(s)
|
|
2113
|
+
if target is None:
|
|
2114
|
+
target = ExprNodes.NameNode(name2_pos, name = name2)
|
|
2115
|
+
else:
|
|
2116
|
+
if not target.is_name:
|
|
2117
|
+
error(target.pos,
|
|
2118
|
+
"Target of for-from statement must be a variable name")
|
|
2119
|
+
elif name2 != target.name:
|
|
2120
|
+
error(name2_pos,
|
|
2121
|
+
"Variable name in for-from range does not match target")
|
|
2122
|
+
if rel1[0] != rel2[0]:
|
|
2123
|
+
error(rel2_pos,
|
|
2124
|
+
"Relation directions in for-from do not match")
|
|
2125
|
+
return dict(target = target,
|
|
2126
|
+
bound1 = bound1,
|
|
2127
|
+
relation1 = rel1,
|
|
2128
|
+
relation2 = rel2,
|
|
2129
|
+
bound2 = bound2,
|
|
2130
|
+
step = step,
|
|
2131
|
+
)
|
|
2132
|
+
else:
|
|
2133
|
+
s.expect('in')
|
|
2134
|
+
return {}
|
|
2135
|
+
|
|
2136
|
+
|
|
2137
|
+
@cython.cfunc
|
|
2138
|
+
def p_for_from_relation(s: PyrexScanner):
|
|
2139
|
+
if s.sy in inequality_relations:
|
|
2140
|
+
op = s.sy
|
|
2141
|
+
s.next()
|
|
2142
|
+
return op
|
|
2143
|
+
else:
|
|
2144
|
+
s.error("Expected one of '<', '<=', '>' '>='")
|
|
2145
|
+
|
|
2146
|
+
|
|
2147
|
+
@cython.cfunc
|
|
2148
|
+
def p_for_from_step(s: PyrexScanner):
|
|
2149
|
+
if s.sy == 'IDENT' and s.systring == 'by':
|
|
2150
|
+
s.next()
|
|
2151
|
+
step = p_bit_expr(s)
|
|
2152
|
+
return step
|
|
2153
|
+
else:
|
|
2154
|
+
return None
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
inequality_relations = cython.declare(frozenset, frozenset((
|
|
2158
|
+
'<', '<=', '>', '>=')))
|
|
2159
|
+
|
|
2160
|
+
|
|
2161
|
+
@cython.cfunc
|
|
2162
|
+
def p_target(s: PyrexScanner, terminator: str):
|
|
2163
|
+
pos = s.position()
|
|
2164
|
+
expr = p_starred_expr(s)
|
|
2165
|
+
if s.sy == ',':
|
|
2166
|
+
s.next()
|
|
2167
|
+
exprs = [expr]
|
|
2168
|
+
while s.sy != terminator:
|
|
2169
|
+
exprs.append(p_starred_expr(s))
|
|
2170
|
+
if s.sy != ',':
|
|
2171
|
+
break
|
|
2172
|
+
s.next()
|
|
2173
|
+
return ExprNodes.TupleNode(pos, args = exprs)
|
|
2174
|
+
else:
|
|
2175
|
+
return expr
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
@cython.cfunc
|
|
2179
|
+
def p_for_target(s: PyrexScanner):
|
|
2180
|
+
return p_target(s, 'in')
|
|
2181
|
+
|
|
2182
|
+
|
|
2183
|
+
@cython.cfunc
|
|
2184
|
+
def p_for_iterator(s: PyrexScanner, allow_testlist: cython.bint = True, is_async: cython.bint = False):
|
|
2185
|
+
pos = s.position()
|
|
2186
|
+
if allow_testlist:
|
|
2187
|
+
expr = p_testlist(s)
|
|
2188
|
+
else:
|
|
2189
|
+
expr = p_or_test(s)
|
|
2190
|
+
return (ExprNodes.AsyncIteratorNode if is_async else ExprNodes.IteratorNode)(pos, sequence=expr)
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
@cython.cfunc
|
|
2194
|
+
def p_try_statement(s: PyrexScanner):
|
|
2195
|
+
# s.sy == 'try'
|
|
2196
|
+
pos = s.position()
|
|
2197
|
+
s.next()
|
|
2198
|
+
body = p_suite(s)
|
|
2199
|
+
except_clauses = []
|
|
2200
|
+
else_clause = None
|
|
2201
|
+
if s.sy in ('except', 'else'):
|
|
2202
|
+
while s.sy == 'except':
|
|
2203
|
+
except_clauses.append(p_except_clause(s))
|
|
2204
|
+
if s.sy == 'else':
|
|
2205
|
+
s.next()
|
|
2206
|
+
else_clause = p_suite(s)
|
|
2207
|
+
body = Nodes.TryExceptStatNode(pos,
|
|
2208
|
+
body = body, except_clauses = except_clauses,
|
|
2209
|
+
else_clause = else_clause)
|
|
2210
|
+
if s.sy != 'finally':
|
|
2211
|
+
return body
|
|
2212
|
+
# try-except-finally is equivalent to nested try-except/try-finally
|
|
2213
|
+
if s.sy == 'finally':
|
|
2214
|
+
s.next()
|
|
2215
|
+
finally_clause = p_suite(s)
|
|
2216
|
+
return Nodes.TryFinallyStatNode(pos,
|
|
2217
|
+
body = body, finally_clause = finally_clause)
|
|
2218
|
+
else:
|
|
2219
|
+
s.error("Expected 'except' or 'finally'")
|
|
2220
|
+
|
|
2221
|
+
|
|
2222
|
+
@cython.cfunc
|
|
2223
|
+
def p_except_clause(s: PyrexScanner):
|
|
2224
|
+
# s.sy == 'except'
|
|
2225
|
+
pos = s.position()
|
|
2226
|
+
s.next()
|
|
2227
|
+
exc_type = None
|
|
2228
|
+
exc_value = None
|
|
2229
|
+
is_except_as = False
|
|
2230
|
+
if s.sy != ':':
|
|
2231
|
+
exc_type = p_test(s)
|
|
2232
|
+
# normalise into list of single exception tests
|
|
2233
|
+
if isinstance(exc_type, ExprNodes.TupleNode):
|
|
2234
|
+
exc_type = exc_type.args
|
|
2235
|
+
else:
|
|
2236
|
+
exc_type = [exc_type]
|
|
2237
|
+
if s.sy == ',' or (s.sy == 'IDENT' and s.systring == 'as'
|
|
2238
|
+
and s.context.language_level == 2):
|
|
2239
|
+
s.next()
|
|
2240
|
+
exc_value = p_test(s)
|
|
2241
|
+
elif s.sy == 'IDENT' and s.systring == 'as':
|
|
2242
|
+
# Py3 syntax requires a name here
|
|
2243
|
+
s.next()
|
|
2244
|
+
pos2 = s.position()
|
|
2245
|
+
name = p_ident(s)
|
|
2246
|
+
exc_value = ExprNodes.NameNode(pos2, name = name)
|
|
2247
|
+
is_except_as = True
|
|
2248
|
+
body = p_suite(s)
|
|
2249
|
+
return Nodes.ExceptClauseNode(pos,
|
|
2250
|
+
pattern = exc_type, target = exc_value,
|
|
2251
|
+
body = body, is_except_as=is_except_as)
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
@cython.cfunc
|
|
2255
|
+
def p_include_statement(s: PyrexScanner, ctx):
|
|
2256
|
+
pos = s.position()
|
|
2257
|
+
s.next() # 'include'
|
|
2258
|
+
unicode_include_file_name = p_string_literal(s, 'u')[2]
|
|
2259
|
+
s.expect_newline("Syntax error in include statement")
|
|
2260
|
+
if s.compile_time_eval:
|
|
2261
|
+
include_file_name = unicode_include_file_name
|
|
2262
|
+
include_file_path = s.context.find_include_file(include_file_name, pos)
|
|
2263
|
+
if include_file_path:
|
|
2264
|
+
s.included_files.append(include_file_name)
|
|
2265
|
+
source_desc = FileSourceDescriptor(include_file_path)
|
|
2266
|
+
with source_desc.get_file_object() as f:
|
|
2267
|
+
s2 = PyrexScanner(f, source_desc, s, source_encoding=f.encoding, parse_comments=s.parse_comments)
|
|
2268
|
+
tree = p_statement_list(s2, ctx)
|
|
2269
|
+
return tree
|
|
2270
|
+
else:
|
|
2271
|
+
return None
|
|
2272
|
+
else:
|
|
2273
|
+
return Nodes.PassStatNode(pos)
|
|
2274
|
+
|
|
2275
|
+
|
|
2276
|
+
@cython.cfunc
|
|
2277
|
+
def p_with_statement(s: PyrexScanner):
|
|
2278
|
+
s.next() # 'with'
|
|
2279
|
+
if s.systring == 'template' and not s.in_python_file:
|
|
2280
|
+
node = p_with_template(s)
|
|
2281
|
+
else:
|
|
2282
|
+
node = p_with_items(s)
|
|
2283
|
+
return node
|
|
2284
|
+
|
|
2285
|
+
|
|
2286
|
+
@cython.cfunc
|
|
2287
|
+
def p_with_items(s: PyrexScanner, is_async: cython.bint = False):
|
|
2288
|
+
"""
|
|
2289
|
+
Copied from CPython:
|
|
2290
|
+
| 'with' '(' a[asdl_withitem_seq*]=','.with_item+ ','? ')' ':' b=block {
|
|
2291
|
+
_PyAST_With(a, b, NULL, EXTRA) }
|
|
2292
|
+
| 'with' a[asdl_withitem_seq*]=','.with_item+ ':' tc=[TYPE_COMMENT] b=block {
|
|
2293
|
+
_PyAST_With(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA) }
|
|
2294
|
+
Therefore the first thing to try is the bracket-enclosed
|
|
2295
|
+
version and if that fails try the regular version
|
|
2296
|
+
"""
|
|
2297
|
+
brackets_succeeded = False
|
|
2298
|
+
items = () # unused, but static analysis fails to track that below
|
|
2299
|
+
if s.sy == '(':
|
|
2300
|
+
with tentatively_scan(s) as errors:
|
|
2301
|
+
s.next()
|
|
2302
|
+
items = p_with_items_list(s, is_async)
|
|
2303
|
+
s.expect(")")
|
|
2304
|
+
if s.sy != ":":
|
|
2305
|
+
# Fail - the message doesn't matter because we'll try the
|
|
2306
|
+
# non-bracket version so it'll never be shown
|
|
2307
|
+
s.error("")
|
|
2308
|
+
brackets_succeeded = not errors
|
|
2309
|
+
if not brackets_succeeded:
|
|
2310
|
+
# try the non-bracket version
|
|
2311
|
+
items = p_with_items_list(s, is_async)
|
|
2312
|
+
body = p_suite(s)
|
|
2313
|
+
for cls, pos, kwds in reversed(items):
|
|
2314
|
+
# construct the actual nodes now that we know what the body is
|
|
2315
|
+
body = cls(pos, body=body, **kwds)
|
|
2316
|
+
return body
|
|
2317
|
+
|
|
2318
|
+
|
|
2319
|
+
@cython.cfunc
|
|
2320
|
+
def p_with_items_list(s: PyrexScanner, is_async: cython.bint) -> list:
|
|
2321
|
+
items = []
|
|
2322
|
+
while True:
|
|
2323
|
+
items.append(p_with_item(s, is_async))
|
|
2324
|
+
if s.sy != ",":
|
|
2325
|
+
break
|
|
2326
|
+
s.next()
|
|
2327
|
+
if s.sy == ")":
|
|
2328
|
+
# trailing commas allowed
|
|
2329
|
+
break
|
|
2330
|
+
return items
|
|
2331
|
+
|
|
2332
|
+
|
|
2333
|
+
@cython.cfunc
|
|
2334
|
+
def p_with_item(s: PyrexScanner, is_async: cython.bint) -> tuple:
|
|
2335
|
+
# In contrast to most parsing functions, this returns a tuple of
|
|
2336
|
+
# class, pos, kwd_dict
|
|
2337
|
+
# This is because GILStatNode does a reasonable amount of initialization in its
|
|
2338
|
+
# constructor, and requires "body" to be set, which we don't currently have
|
|
2339
|
+
pos = s.position()
|
|
2340
|
+
if not s.in_python_file and s.sy == 'IDENT' and s.systring in ('nogil', 'gil'):
|
|
2341
|
+
if is_async:
|
|
2342
|
+
s.error("with gil/nogil cannot be async")
|
|
2343
|
+
state = s.systring
|
|
2344
|
+
s.next()
|
|
2345
|
+
|
|
2346
|
+
# support conditional gil/nogil
|
|
2347
|
+
condition = None
|
|
2348
|
+
if s.sy == '(':
|
|
2349
|
+
s.next()
|
|
2350
|
+
condition = p_test(s)
|
|
2351
|
+
s.expect(')')
|
|
2352
|
+
|
|
2353
|
+
return Nodes.GILStatNode, pos, {"state": state, "condition": condition}
|
|
2354
|
+
else:
|
|
2355
|
+
manager = p_test(s)
|
|
2356
|
+
target = None
|
|
2357
|
+
if s.sy == 'IDENT' and s.systring == 'as':
|
|
2358
|
+
s.next()
|
|
2359
|
+
target = p_starred_expr(s)
|
|
2360
|
+
return Nodes.WithStatNode, pos, {"manager": manager, "target": target, "is_async": is_async}
|
|
2361
|
+
|
|
2362
|
+
|
|
2363
|
+
@cython.cfunc
|
|
2364
|
+
def p_with_template(s: PyrexScanner):
|
|
2365
|
+
pos = s.position()
|
|
2366
|
+
templates = []
|
|
2367
|
+
s.next()
|
|
2368
|
+
s.expect('[')
|
|
2369
|
+
templates.append(s.systring)
|
|
2370
|
+
s.next()
|
|
2371
|
+
while s.systring == ',':
|
|
2372
|
+
s.next()
|
|
2373
|
+
templates.append(s.systring)
|
|
2374
|
+
s.next()
|
|
2375
|
+
s.expect(']')
|
|
2376
|
+
if s.sy == ':':
|
|
2377
|
+
s.next()
|
|
2378
|
+
s.expect_newline("Syntax error in template function declaration")
|
|
2379
|
+
s.expect_indent()
|
|
2380
|
+
body_ctx = Ctx()
|
|
2381
|
+
body_ctx.templates = templates
|
|
2382
|
+
func_or_var = p_c_func_or_var_declaration(s, pos, body_ctx)
|
|
2383
|
+
s.expect_dedent()
|
|
2384
|
+
return func_or_var
|
|
2385
|
+
else:
|
|
2386
|
+
error(pos, "Syntax error in template function declaration")
|
|
2387
|
+
|
|
2388
|
+
|
|
2389
|
+
@cython.cfunc
|
|
2390
|
+
def p_simple_statement(s: PyrexScanner, first_statement: cython.bint = 0):
|
|
2391
|
+
#print "p_simple_statement:", s.sy, s.systring ###
|
|
2392
|
+
if s.sy == 'global':
|
|
2393
|
+
node = p_global_statement(s)
|
|
2394
|
+
elif s.sy == 'nonlocal':
|
|
2395
|
+
node = p_nonlocal_statement(s)
|
|
2396
|
+
elif s.sy == 'print':
|
|
2397
|
+
node = p_print_statement(s)
|
|
2398
|
+
elif s.sy == 'exec':
|
|
2399
|
+
node = p_exec_statement(s)
|
|
2400
|
+
elif s.sy == 'del':
|
|
2401
|
+
node = p_del_statement(s)
|
|
2402
|
+
elif s.sy == 'break':
|
|
2403
|
+
node = p_break_statement(s)
|
|
2404
|
+
elif s.sy == 'continue':
|
|
2405
|
+
node = p_continue_statement(s)
|
|
2406
|
+
elif s.sy == 'return':
|
|
2407
|
+
node = p_return_statement(s)
|
|
2408
|
+
elif s.sy == 'raise':
|
|
2409
|
+
node = p_raise_statement(s)
|
|
2410
|
+
elif s.sy in ('import', 'cimport'):
|
|
2411
|
+
node = p_import_statement(s)
|
|
2412
|
+
elif s.sy == 'from':
|
|
2413
|
+
node = p_from_import_statement(s, first_statement = first_statement)
|
|
2414
|
+
elif s.sy == 'yield':
|
|
2415
|
+
node = p_yield_statement(s)
|
|
2416
|
+
elif s.sy == 'assert':
|
|
2417
|
+
node = p_assert_statement(s)
|
|
2418
|
+
elif s.sy == 'pass':
|
|
2419
|
+
node = p_pass_statement(s)
|
|
2420
|
+
else:
|
|
2421
|
+
node = p_expression_or_assignment(s)
|
|
2422
|
+
return node
|
|
2423
|
+
|
|
2424
|
+
|
|
2425
|
+
@cython.cfunc
|
|
2426
|
+
def p_simple_statement_list(s: PyrexScanner, ctx, first_statement: cython.bint = 0):
|
|
2427
|
+
# Parse a series of simple statements on one line
|
|
2428
|
+
# separated by semicolons.
|
|
2429
|
+
stat = p_simple_statement(s, first_statement = first_statement)
|
|
2430
|
+
pos = stat.pos
|
|
2431
|
+
stats = []
|
|
2432
|
+
if not isinstance(stat, Nodes.PassStatNode):
|
|
2433
|
+
stats.append(stat)
|
|
2434
|
+
while s.sy == ';':
|
|
2435
|
+
#print "p_simple_statement_list: maybe more to follow" ###
|
|
2436
|
+
s.next()
|
|
2437
|
+
if s.sy in ('NEWLINE', 'EOF'):
|
|
2438
|
+
break
|
|
2439
|
+
stat = p_simple_statement(s, first_statement = first_statement)
|
|
2440
|
+
if isinstance(stat, Nodes.PassStatNode):
|
|
2441
|
+
continue
|
|
2442
|
+
stats.append(stat)
|
|
2443
|
+
first_statement = False
|
|
2444
|
+
|
|
2445
|
+
if not stats:
|
|
2446
|
+
stat = Nodes.PassStatNode(pos)
|
|
2447
|
+
elif len(stats) == 1:
|
|
2448
|
+
stat = stats[0]
|
|
2449
|
+
else:
|
|
2450
|
+
stat = Nodes.StatListNode(pos, stats = stats)
|
|
2451
|
+
|
|
2452
|
+
if s.sy not in ('NEWLINE', 'EOF'):
|
|
2453
|
+
# provide a better error message for users who accidentally write Cython code in .py files
|
|
2454
|
+
if isinstance(stat, Nodes.ExprStatNode):
|
|
2455
|
+
if stat.expr.is_name and stat.expr.name == 'cdef':
|
|
2456
|
+
s.error("The 'cdef' keyword is only allowed in Cython files (pyx/pxi/pxd)", pos)
|
|
2457
|
+
s.expect_newline("Syntax error in simple statement list")
|
|
2458
|
+
|
|
2459
|
+
return stat
|
|
2460
|
+
|
|
2461
|
+
|
|
2462
|
+
@cython.cfunc
|
|
2463
|
+
def p_compile_time_expr(s: PyrexScanner):
|
|
2464
|
+
old = s.compile_time_expr
|
|
2465
|
+
s.compile_time_expr = 1
|
|
2466
|
+
expr = p_testlist(s)
|
|
2467
|
+
s.compile_time_expr = old
|
|
2468
|
+
return expr
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
@cython.cfunc
|
|
2472
|
+
def p_DEF_statement(s: PyrexScanner):
|
|
2473
|
+
pos = s.position()
|
|
2474
|
+
denv = s.compile_time_env
|
|
2475
|
+
s.next() # 'DEF'
|
|
2476
|
+
name = p_ident(s)
|
|
2477
|
+
s.expect('=')
|
|
2478
|
+
expr = p_compile_time_expr(s)
|
|
2479
|
+
if s.compile_time_eval:
|
|
2480
|
+
value = expr.compile_time_value(denv)
|
|
2481
|
+
#print "p_DEF_statement: %s = %r" % (name, value) ###
|
|
2482
|
+
denv.declare(name, value)
|
|
2483
|
+
s.expect_newline("Expected a newline", ignore_semicolon=True)
|
|
2484
|
+
return Nodes.PassStatNode(pos)
|
|
2485
|
+
|
|
2486
|
+
|
|
2487
|
+
@cython.cfunc
|
|
2488
|
+
def p_IF_statement(s: PyrexScanner, ctx):
|
|
2489
|
+
pos = s.position()
|
|
2490
|
+
saved_eval = s.compile_time_eval
|
|
2491
|
+
current_eval = saved_eval
|
|
2492
|
+
denv = s.compile_time_env
|
|
2493
|
+
result = None
|
|
2494
|
+
while 1:
|
|
2495
|
+
s.next() # 'IF' or 'ELIF'
|
|
2496
|
+
expr = p_compile_time_expr(s)
|
|
2497
|
+
s.compile_time_eval = current_eval and bool(expr.compile_time_value(denv))
|
|
2498
|
+
body = p_suite(s, ctx)
|
|
2499
|
+
if s.compile_time_eval:
|
|
2500
|
+
result = body
|
|
2501
|
+
current_eval = 0
|
|
2502
|
+
if s.sy != 'ELIF':
|
|
2503
|
+
break
|
|
2504
|
+
if s.sy == 'ELSE':
|
|
2505
|
+
s.next()
|
|
2506
|
+
s.compile_time_eval = current_eval
|
|
2507
|
+
body = p_suite(s, ctx)
|
|
2508
|
+
if current_eval:
|
|
2509
|
+
result = body
|
|
2510
|
+
if not result:
|
|
2511
|
+
result = Nodes.PassStatNode(pos)
|
|
2512
|
+
s.compile_time_eval = saved_eval
|
|
2513
|
+
return result
|
|
2514
|
+
|
|
2515
|
+
|
|
2516
|
+
@cython.cfunc
|
|
2517
|
+
def p_statement(s: PyrexScanner, ctx, first_statement: cython.bint = False):
|
|
2518
|
+
cdef_flag: cython.bint = ctx.cdef_flag
|
|
2519
|
+
pos = s.position()
|
|
2520
|
+
decorators = None
|
|
2521
|
+
if s.sy == 'ctypedef':
|
|
2522
|
+
if ctx.level not in ('module', 'module_pxd'):
|
|
2523
|
+
s.error("ctypedef statement not allowed here")
|
|
2524
|
+
#if ctx.api:
|
|
2525
|
+
# error(pos, "'api' not allowed with 'ctypedef'")
|
|
2526
|
+
return p_ctypedef_statement(s, ctx)
|
|
2527
|
+
elif s.sy == 'DEF':
|
|
2528
|
+
# We used to dep-warn about this but removed the warning again since
|
|
2529
|
+
# we don't have a good answer yet for all use cases.
|
|
2530
|
+
if s.context.compiler_directives.get("warn.deprecated.DEF", False):
|
|
2531
|
+
warning(pos,
|
|
2532
|
+
"The 'DEF' statement will be removed in a future Cython version. "
|
|
2533
|
+
"Consider using global variables, constants, and in-place literals instead. "
|
|
2534
|
+
"See https://github.com/cython/cython/issues/4310", level=1)
|
|
2535
|
+
return p_DEF_statement(s)
|
|
2536
|
+
elif s.sy == 'IF':
|
|
2537
|
+
if s.context.compiler_directives.get("warn.deprecated.IF", True):
|
|
2538
|
+
warning(pos,
|
|
2539
|
+
"The 'IF' statement is deprecated and will be removed in a future Cython version. "
|
|
2540
|
+
"Consider using runtime conditions or C macros instead. "
|
|
2541
|
+
"See https://github.com/cython/cython/issues/4310", level=1)
|
|
2542
|
+
return p_IF_statement(s, ctx)
|
|
2543
|
+
elif s.sy == '@':
|
|
2544
|
+
if ctx.level not in ('module', 'class', 'c_class', 'function', 'property', 'module_pxd', 'c_class_pxd', 'other'):
|
|
2545
|
+
s.error('decorator not allowed here')
|
|
2546
|
+
s.level = ctx.level
|
|
2547
|
+
decorators = p_decorators(s)
|
|
2548
|
+
if not ctx.allow_struct_enum_decorator and s.sy not in ('def', 'cdef', 'cpdef', 'class', 'async'):
|
|
2549
|
+
if s.sy == 'IDENT' and s.systring == 'async':
|
|
2550
|
+
pass # handled below
|
|
2551
|
+
else:
|
|
2552
|
+
s.error("Decorators can only be followed by functions or classes")
|
|
2553
|
+
elif s.sy == 'pass' and cdef_flag:
|
|
2554
|
+
# empty cdef block
|
|
2555
|
+
return p_pass_statement(s, with_newline=True)
|
|
2556
|
+
|
|
2557
|
+
overridable = False
|
|
2558
|
+
if s.sy == 'cdef':
|
|
2559
|
+
cdef_flag = True
|
|
2560
|
+
s.next()
|
|
2561
|
+
elif s.sy == 'cpdef':
|
|
2562
|
+
cdef_flag = True
|
|
2563
|
+
overridable = True
|
|
2564
|
+
s.next()
|
|
2565
|
+
if cdef_flag:
|
|
2566
|
+
if ctx.level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
|
|
2567
|
+
s.error('cdef statement not allowed here')
|
|
2568
|
+
s.level = ctx.level
|
|
2569
|
+
node = p_cdef_statement(s, pos, ctx(overridable=overridable))
|
|
2570
|
+
if decorators is not None:
|
|
2571
|
+
tup = (Nodes.CFuncDefNode, Nodes.CVarDefNode, Nodes.CClassDefNode)
|
|
2572
|
+
if ctx.allow_struct_enum_decorator:
|
|
2573
|
+
tup += (Nodes.CStructOrUnionDefNode, Nodes.CEnumDefNode)
|
|
2574
|
+
if not isinstance(node, tup):
|
|
2575
|
+
s.error("Decorators can only be followed by functions or classes")
|
|
2576
|
+
node.decorators = decorators
|
|
2577
|
+
return node
|
|
2578
|
+
else:
|
|
2579
|
+
if ctx.api:
|
|
2580
|
+
s.error("'api' not allowed with this statement", fatal=False)
|
|
2581
|
+
elif s.sy == 'def':
|
|
2582
|
+
# def statements aren't allowed in pxd files, except
|
|
2583
|
+
# as part of a cdef class
|
|
2584
|
+
if ('pxd' in ctx.level) and (ctx.level != 'c_class_pxd'):
|
|
2585
|
+
s.error('def statement not allowed here')
|
|
2586
|
+
s.level = ctx.level
|
|
2587
|
+
return p_def_statement(s, decorators)
|
|
2588
|
+
elif s.sy == 'class':
|
|
2589
|
+
if ctx.level not in ('module', 'function', 'class', 'other'):
|
|
2590
|
+
s.error("class definition not allowed here")
|
|
2591
|
+
return p_class_statement(s, decorators)
|
|
2592
|
+
elif s.sy == 'include':
|
|
2593
|
+
if ctx.level not in ('module', 'module_pxd'):
|
|
2594
|
+
s.error("include statement not allowed here")
|
|
2595
|
+
return p_include_statement(s, ctx)
|
|
2596
|
+
elif ctx.level == 'c_class' and s.sy == 'IDENT' and s.systring == 'property':
|
|
2597
|
+
return p_property_decl(s)
|
|
2598
|
+
elif s.sy == 'pass' and ctx.level != 'property':
|
|
2599
|
+
return p_pass_statement(s, with_newline=True)
|
|
2600
|
+
else:
|
|
2601
|
+
if ctx.level in ('c_class_pxd', 'property'):
|
|
2602
|
+
node = p_ignorable_statement(s)
|
|
2603
|
+
if node is not None:
|
|
2604
|
+
return node
|
|
2605
|
+
s.error("Executable statement not allowed here")
|
|
2606
|
+
if s.sy == 'if':
|
|
2607
|
+
return p_if_statement(s)
|
|
2608
|
+
elif s.sy == 'while':
|
|
2609
|
+
return p_while_statement(s)
|
|
2610
|
+
elif s.sy == 'for':
|
|
2611
|
+
return p_for_statement(s)
|
|
2612
|
+
elif s.sy == 'try':
|
|
2613
|
+
return p_try_statement(s)
|
|
2614
|
+
elif s.sy == 'with':
|
|
2615
|
+
return p_with_statement(s)
|
|
2616
|
+
elif s.sy == 'async':
|
|
2617
|
+
s.next()
|
|
2618
|
+
return p_async_statement(s, ctx, decorators)
|
|
2619
|
+
else:
|
|
2620
|
+
if s.sy == 'IDENT' and s.systring == 'async':
|
|
2621
|
+
ident_name = s.systring
|
|
2622
|
+
ident_pos = s.position()
|
|
2623
|
+
# PEP 492 enables the async/await keywords when it spots "async def ..."
|
|
2624
|
+
s.next()
|
|
2625
|
+
if s.sy == 'def':
|
|
2626
|
+
return p_async_statement(s, ctx, decorators)
|
|
2627
|
+
elif decorators:
|
|
2628
|
+
s.error("Decorators can only be followed by functions or classes")
|
|
2629
|
+
s.put_back('IDENT', ident_name, ident_pos) # re-insert original token
|
|
2630
|
+
if s.sy == 'IDENT' and s.systring == 'match':
|
|
2631
|
+
# p_match_statement returns None on a "soft" initial failure
|
|
2632
|
+
match_statement = p_match_statement(s, ctx)
|
|
2633
|
+
if match_statement is not None:
|
|
2634
|
+
return match_statement
|
|
2635
|
+
return p_simple_statement_list(s, ctx, first_statement=first_statement)
|
|
2636
|
+
|
|
2637
|
+
|
|
2638
|
+
@cython.cfunc
|
|
2639
|
+
def p_statement_list(s: PyrexScanner, ctx, first_statement: cython.bint = 0):
|
|
2640
|
+
# Parse a series of statements separated by newlines.
|
|
2641
|
+
pos = s.position()
|
|
2642
|
+
stats = []
|
|
2643
|
+
while s.sy not in ('DEDENT', 'EOF'):
|
|
2644
|
+
stat = p_statement(s, ctx, first_statement = first_statement)
|
|
2645
|
+
if isinstance(stat, Nodes.PassStatNode):
|
|
2646
|
+
continue
|
|
2647
|
+
stats.append(stat)
|
|
2648
|
+
first_statement = False
|
|
2649
|
+
if not stats:
|
|
2650
|
+
return Nodes.PassStatNode(pos)
|
|
2651
|
+
elif len(stats) == 1:
|
|
2652
|
+
return stats[0]
|
|
2653
|
+
else:
|
|
2654
|
+
return Nodes.StatListNode(pos, stats = stats)
|
|
2655
|
+
|
|
2656
|
+
|
|
2657
|
+
@cython.cfunc
|
|
2658
|
+
def p_suite(s: PyrexScanner, ctx=Ctx()):
|
|
2659
|
+
return p_suite_with_docstring(s, ctx, with_doc_only=False)[1]
|
|
2660
|
+
|
|
2661
|
+
|
|
2662
|
+
@cython.cfunc
|
|
2663
|
+
def p_suite_with_docstring(s: PyrexScanner, ctx, with_doc_only: cython.bint = False) -> tuple:
|
|
2664
|
+
s.expect(':')
|
|
2665
|
+
doc = None
|
|
2666
|
+
if s.sy == 'NEWLINE':
|
|
2667
|
+
s.next()
|
|
2668
|
+
s.expect_indent()
|
|
2669
|
+
if with_doc_only:
|
|
2670
|
+
doc = p_doc_string(s)
|
|
2671
|
+
body = p_statement_list(s, ctx)
|
|
2672
|
+
s.expect_dedent()
|
|
2673
|
+
else:
|
|
2674
|
+
if ctx.api:
|
|
2675
|
+
s.error("'api' not allowed with this statement", fatal=False)
|
|
2676
|
+
if ctx.level in ('module', 'class', 'function', 'other'):
|
|
2677
|
+
body = p_simple_statement_list(s, ctx)
|
|
2678
|
+
else:
|
|
2679
|
+
body = p_pass_statement(s)
|
|
2680
|
+
s.expect_newline("Syntax error in declarations", ignore_semicolon=True)
|
|
2681
|
+
if not with_doc_only:
|
|
2682
|
+
doc, body = _extract_docstring(body)
|
|
2683
|
+
return doc, body
|
|
2684
|
+
|
|
2685
|
+
|
|
2686
|
+
@cython.cfunc
|
|
2687
|
+
def p_positional_and_keyword_args(s: PyrexScanner, end_sy_set, templates = None):
|
|
2688
|
+
"""
|
|
2689
|
+
Parses positional and keyword arguments. end_sy_set
|
|
2690
|
+
should contain any s.sy that terminate the argument list.
|
|
2691
|
+
Argument expansion (* and **) are not allowed.
|
|
2692
|
+
|
|
2693
|
+
Returns: (positional_args, keyword_args)
|
|
2694
|
+
"""
|
|
2695
|
+
positional_args = []
|
|
2696
|
+
keyword_args = []
|
|
2697
|
+
pos_idx = 0
|
|
2698
|
+
|
|
2699
|
+
while s.sy not in end_sy_set:
|
|
2700
|
+
if s.sy == '*' or s.sy == '**':
|
|
2701
|
+
s.error('Argument expansion not allowed here.', fatal=False)
|
|
2702
|
+
|
|
2703
|
+
parsed_type = False
|
|
2704
|
+
if s.sy == 'IDENT' and s.peek()[0] == '=':
|
|
2705
|
+
ident = s.systring
|
|
2706
|
+
s.next() # s.sy is '='
|
|
2707
|
+
s.next()
|
|
2708
|
+
if looking_at_expr(s):
|
|
2709
|
+
arg = p_test(s)
|
|
2710
|
+
else:
|
|
2711
|
+
base_type = p_c_base_type(s, templates = templates)
|
|
2712
|
+
declarator = p_c_declarator(s, empty=True)
|
|
2713
|
+
arg = Nodes.CComplexBaseTypeNode(base_type.pos,
|
|
2714
|
+
base_type = base_type, declarator = declarator)
|
|
2715
|
+
parsed_type = True
|
|
2716
|
+
keyword_node = ExprNodes.IdentifierStringNode(arg.pos, value=ident)
|
|
2717
|
+
keyword_args.append((keyword_node, arg))
|
|
2718
|
+
was_keyword = True
|
|
2719
|
+
|
|
2720
|
+
else:
|
|
2721
|
+
if looking_at_expr(s):
|
|
2722
|
+
arg = p_test(s)
|
|
2723
|
+
else:
|
|
2724
|
+
base_type = p_c_base_type(s, templates = templates)
|
|
2725
|
+
declarator = p_c_declarator(s, empty=True)
|
|
2726
|
+
arg = Nodes.CComplexBaseTypeNode(base_type.pos,
|
|
2727
|
+
base_type = base_type, declarator = declarator)
|
|
2728
|
+
parsed_type = True
|
|
2729
|
+
positional_args.append(arg)
|
|
2730
|
+
pos_idx += 1
|
|
2731
|
+
if len(keyword_args) > 0:
|
|
2732
|
+
s.error("Non-keyword arg following keyword arg",
|
|
2733
|
+
pos=arg.pos)
|
|
2734
|
+
|
|
2735
|
+
if s.sy != ',':
|
|
2736
|
+
if s.sy not in end_sy_set:
|
|
2737
|
+
if parsed_type:
|
|
2738
|
+
s.error("Unmatched %s" % " or ".join(end_sy_set))
|
|
2739
|
+
break
|
|
2740
|
+
s.next()
|
|
2741
|
+
return positional_args, keyword_args
|
|
2742
|
+
|
|
2743
|
+
|
|
2744
|
+
@cython.ccall
|
|
2745
|
+
def p_c_base_type(s: PyrexScanner, nonempty: cython.bint = False, templates=None):
|
|
2746
|
+
if s.sy == '(':
|
|
2747
|
+
return p_c_complex_base_type(s, templates = templates)
|
|
2748
|
+
else:
|
|
2749
|
+
return p_c_simple_base_type(s, nonempty=nonempty, templates=templates)
|
|
2750
|
+
|
|
2751
|
+
|
|
2752
|
+
@cython.cfunc
|
|
2753
|
+
def p_calling_convention(s: PyrexScanner):
|
|
2754
|
+
if s.sy == 'IDENT' and s.systring in calling_convention_words:
|
|
2755
|
+
result = s.systring
|
|
2756
|
+
s.next()
|
|
2757
|
+
return result
|
|
2758
|
+
else:
|
|
2759
|
+
return EncodedString("")
|
|
2760
|
+
|
|
2761
|
+
|
|
2762
|
+
calling_convention_words = cython.declare(frozenset, frozenset((
|
|
2763
|
+
"__stdcall", "__cdecl", "__fastcall")))
|
|
2764
|
+
|
|
2765
|
+
|
|
2766
|
+
@cython.cfunc
|
|
2767
|
+
def p_c_complex_base_type(s: PyrexScanner, templates = None):
|
|
2768
|
+
# s.sy == '('
|
|
2769
|
+
pos = s.position()
|
|
2770
|
+
s.next()
|
|
2771
|
+
base_type = p_c_base_type(s, templates=templates)
|
|
2772
|
+
declarator = p_c_declarator(s, empty=True)
|
|
2773
|
+
type_node = Nodes.CComplexBaseTypeNode(
|
|
2774
|
+
pos, base_type=base_type, declarator=declarator)
|
|
2775
|
+
if s.sy == ',':
|
|
2776
|
+
components = [type_node]
|
|
2777
|
+
while s.sy == ',':
|
|
2778
|
+
s.next()
|
|
2779
|
+
if s.sy == ')':
|
|
2780
|
+
break
|
|
2781
|
+
base_type = p_c_base_type(s, templates=templates)
|
|
2782
|
+
declarator = p_c_declarator(s, empty=True)
|
|
2783
|
+
components.append(Nodes.CComplexBaseTypeNode(
|
|
2784
|
+
pos, base_type=base_type, declarator=declarator))
|
|
2785
|
+
type_node = Nodes.CTupleBaseTypeNode(pos, components = components)
|
|
2786
|
+
|
|
2787
|
+
s.expect(')')
|
|
2788
|
+
if s.sy == '[':
|
|
2789
|
+
if is_memoryviewslice_access(s):
|
|
2790
|
+
type_node = p_memoryviewslice_access(s, type_node)
|
|
2791
|
+
else:
|
|
2792
|
+
type_node = p_buffer_or_template(s, type_node, templates)
|
|
2793
|
+
return type_node
|
|
2794
|
+
|
|
2795
|
+
|
|
2796
|
+
@cython.cfunc
|
|
2797
|
+
def p_c_simple_base_type(s: PyrexScanner, nonempty: cython.bint, templates=None):
|
|
2798
|
+
is_basic = False
|
|
2799
|
+
signed = 1
|
|
2800
|
+
longness = 0
|
|
2801
|
+
complex = False
|
|
2802
|
+
module_path = []
|
|
2803
|
+
pos = s.position()
|
|
2804
|
+
|
|
2805
|
+
# Handle const/volatile
|
|
2806
|
+
is_const = is_volatile = False
|
|
2807
|
+
while s.sy == 'IDENT':
|
|
2808
|
+
if s.systring == 'const':
|
|
2809
|
+
if is_const: error(pos, "Duplicate 'const'")
|
|
2810
|
+
is_const = True
|
|
2811
|
+
elif s.systring == 'volatile':
|
|
2812
|
+
if is_volatile: error(pos, "Duplicate 'volatile'")
|
|
2813
|
+
is_volatile = True
|
|
2814
|
+
else:
|
|
2815
|
+
break
|
|
2816
|
+
s.next()
|
|
2817
|
+
if is_const or is_volatile:
|
|
2818
|
+
base_type = p_c_base_type(s, nonempty=nonempty, templates=templates)
|
|
2819
|
+
if isinstance(base_type, Nodes.MemoryViewSliceTypeNode):
|
|
2820
|
+
# reverse order to avoid having to write "(const int)[:]"
|
|
2821
|
+
base_type.base_type_node = Nodes.CConstOrVolatileTypeNode(pos,
|
|
2822
|
+
base_type=base_type.base_type_node, is_const=is_const, is_volatile=is_volatile)
|
|
2823
|
+
return base_type
|
|
2824
|
+
return Nodes.CConstOrVolatileTypeNode(pos,
|
|
2825
|
+
base_type=base_type, is_const=is_const, is_volatile=is_volatile)
|
|
2826
|
+
|
|
2827
|
+
if s.sy != 'IDENT':
|
|
2828
|
+
error(pos, "Expected an identifier, found '%s'" % s.sy)
|
|
2829
|
+
if looking_at_base_type(s):
|
|
2830
|
+
#print "p_c_simple_base_type: looking_at_base_type at", s.position()
|
|
2831
|
+
is_basic = True
|
|
2832
|
+
if s.sy == 'IDENT' and s.systring in special_basic_c_types:
|
|
2833
|
+
signed, longness = special_basic_c_types[s.systring]
|
|
2834
|
+
name = s.systring
|
|
2835
|
+
s.next()
|
|
2836
|
+
else:
|
|
2837
|
+
signed, longness = p_sign_and_longness(s)
|
|
2838
|
+
if s.sy == 'IDENT' and s.systring in basic_c_type_names:
|
|
2839
|
+
name = s.systring
|
|
2840
|
+
s.next()
|
|
2841
|
+
else:
|
|
2842
|
+
name = 'int' # long [int], short [int], long [int] complex, etc.
|
|
2843
|
+
if s.sy == 'IDENT' and s.systring == 'complex':
|
|
2844
|
+
complex = True
|
|
2845
|
+
s.next()
|
|
2846
|
+
elif looking_at_dotted_name(s):
|
|
2847
|
+
#print "p_c_simple_base_type: looking_at_type_name at", s.position()
|
|
2848
|
+
name = s.systring
|
|
2849
|
+
s.next()
|
|
2850
|
+
while s.sy == '.':
|
|
2851
|
+
module_path.append(name)
|
|
2852
|
+
s.next()
|
|
2853
|
+
name = p_ident(s)
|
|
2854
|
+
else:
|
|
2855
|
+
name = s.systring
|
|
2856
|
+
name_pos = s.position()
|
|
2857
|
+
s.next()
|
|
2858
|
+
if nonempty and s.sy != 'IDENT':
|
|
2859
|
+
# Make sure this is not a declaration of a variable or function.
|
|
2860
|
+
if s.sy == '(':
|
|
2861
|
+
old_pos = s.position()
|
|
2862
|
+
s.next()
|
|
2863
|
+
if (s.sy == '*' or s.sy == '**' or s.sy == '&'
|
|
2864
|
+
or (s.sy == 'IDENT' and s.systring in calling_convention_words)):
|
|
2865
|
+
s.put_back('(', '(', old_pos)
|
|
2866
|
+
else:
|
|
2867
|
+
s.put_back('(', '(', old_pos)
|
|
2868
|
+
s.put_back('IDENT', name, name_pos)
|
|
2869
|
+
name = None
|
|
2870
|
+
elif s.sy not in ('*', '**', '[', '&'):
|
|
2871
|
+
s.put_back('IDENT', name, name_pos)
|
|
2872
|
+
name = None
|
|
2873
|
+
|
|
2874
|
+
type_node = Nodes.CSimpleBaseTypeNode(pos,
|
|
2875
|
+
name = name, module_path = module_path,
|
|
2876
|
+
is_basic_c_type = is_basic, signed = signed,
|
|
2877
|
+
complex = complex, longness = longness,
|
|
2878
|
+
templates = templates)
|
|
2879
|
+
|
|
2880
|
+
# declarations here.
|
|
2881
|
+
if s.sy == '[':
|
|
2882
|
+
if is_memoryviewslice_access(s):
|
|
2883
|
+
type_node = p_memoryviewslice_access(s, type_node)
|
|
2884
|
+
else:
|
|
2885
|
+
type_node = p_buffer_or_template(s, type_node, templates)
|
|
2886
|
+
|
|
2887
|
+
if s.sy == '.':
|
|
2888
|
+
s.next()
|
|
2889
|
+
name = p_ident(s)
|
|
2890
|
+
type_node = Nodes.CNestedBaseTypeNode(pos, base_type = type_node, name = name)
|
|
2891
|
+
|
|
2892
|
+
return type_node
|
|
2893
|
+
|
|
2894
|
+
|
|
2895
|
+
@cython.cfunc
|
|
2896
|
+
def p_buffer_or_template(s: PyrexScanner, base_type_node, templates):
|
|
2897
|
+
# s.sy == '['
|
|
2898
|
+
pos = s.position()
|
|
2899
|
+
s.next()
|
|
2900
|
+
# Note that buffer_positional_options_count=1, so the only positional argument is dtype.
|
|
2901
|
+
# For templated types, all parameters are types.
|
|
2902
|
+
positional_args, keyword_args = (
|
|
2903
|
+
p_positional_and_keyword_args(s, (']',), templates)
|
|
2904
|
+
)
|
|
2905
|
+
s.expect(']')
|
|
2906
|
+
|
|
2907
|
+
if s.sy == '[':
|
|
2908
|
+
base_type_node = p_buffer_or_template(s, base_type_node, templates)
|
|
2909
|
+
|
|
2910
|
+
keyword_dict = ExprNodes.DictNode(pos,
|
|
2911
|
+
key_value_pairs = [
|
|
2912
|
+
ExprNodes.DictItemNode(pos=key.pos, key=key, value=value)
|
|
2913
|
+
for key, value in keyword_args
|
|
2914
|
+
])
|
|
2915
|
+
result = Nodes.TemplatedTypeNode(pos,
|
|
2916
|
+
positional_args = positional_args,
|
|
2917
|
+
keyword_args = keyword_dict,
|
|
2918
|
+
base_type_node = base_type_node)
|
|
2919
|
+
return result
|
|
2920
|
+
|
|
2921
|
+
|
|
2922
|
+
@cython.cfunc
|
|
2923
|
+
def is_memoryviewslice_access(s: PyrexScanner) -> cython.bint:
|
|
2924
|
+
# s.sy == '['
|
|
2925
|
+
# a memoryview slice declaration is distinguishable from a buffer access
|
|
2926
|
+
# declaration by the first entry in the bracketed list. The buffer will
|
|
2927
|
+
# not have an unnested colon in the first entry; the memoryview slice will.
|
|
2928
|
+
saved = [(s.sy, s.systring, s.position())]
|
|
2929
|
+
s.next()
|
|
2930
|
+
retval = False
|
|
2931
|
+
if s.systring == ':':
|
|
2932
|
+
retval = True
|
|
2933
|
+
elif s.sy == 'INT':
|
|
2934
|
+
saved.append((s.sy, s.systring, s.position()))
|
|
2935
|
+
s.next()
|
|
2936
|
+
if s.sy == ':':
|
|
2937
|
+
retval = True
|
|
2938
|
+
|
|
2939
|
+
for sv in saved[::-1]:
|
|
2940
|
+
s.put_back(*sv)
|
|
2941
|
+
|
|
2942
|
+
return retval
|
|
2943
|
+
|
|
2944
|
+
|
|
2945
|
+
@cython.cfunc
|
|
2946
|
+
def p_memoryviewslice_access(s: PyrexScanner, base_type_node):
|
|
2947
|
+
# s.sy == '['
|
|
2948
|
+
pos = s.position()
|
|
2949
|
+
s.next()
|
|
2950
|
+
subscripts, _ = p_subscript_list(s)
|
|
2951
|
+
# make sure each entry in subscripts is a slice
|
|
2952
|
+
for subscript in subscripts:
|
|
2953
|
+
if len(subscript) < 2:
|
|
2954
|
+
s.error("An axis specification in memoryview declaration does not have a ':'.")
|
|
2955
|
+
s.expect(']')
|
|
2956
|
+
indexes = make_slice_nodes(pos, subscripts)
|
|
2957
|
+
result = Nodes.MemoryViewSliceTypeNode(pos,
|
|
2958
|
+
base_type_node = base_type_node,
|
|
2959
|
+
axes = indexes)
|
|
2960
|
+
return result
|
|
2961
|
+
|
|
2962
|
+
|
|
2963
|
+
@cython.cfunc
|
|
2964
|
+
def looking_at_name(s: PyrexScanner) -> cython.bint:
|
|
2965
|
+
return s.sy == 'IDENT' and s.systring not in calling_convention_words
|
|
2966
|
+
|
|
2967
|
+
|
|
2968
|
+
@cython.cfunc
|
|
2969
|
+
def looking_at_expr(s: PyrexScanner) -> cython.bint:
|
|
2970
|
+
if s.systring in base_type_start_words:
|
|
2971
|
+
return False
|
|
2972
|
+
elif s.sy == 'IDENT':
|
|
2973
|
+
is_type = False
|
|
2974
|
+
name = s.systring
|
|
2975
|
+
name_pos = s.position()
|
|
2976
|
+
dotted_path = []
|
|
2977
|
+
s.next()
|
|
2978
|
+
|
|
2979
|
+
while s.sy == '.':
|
|
2980
|
+
s.next()
|
|
2981
|
+
dotted_path.append((s.systring, s.position()))
|
|
2982
|
+
s.expect('IDENT')
|
|
2983
|
+
|
|
2984
|
+
saved = s.sy, s.systring, s.position()
|
|
2985
|
+
if s.sy == 'IDENT':
|
|
2986
|
+
is_type = True
|
|
2987
|
+
elif s.sy == '*' or s.sy == '**':
|
|
2988
|
+
s.next()
|
|
2989
|
+
is_type = s.sy in (')', ']')
|
|
2990
|
+
s.put_back(*saved)
|
|
2991
|
+
elif s.sy == '(':
|
|
2992
|
+
s.next()
|
|
2993
|
+
is_type = s.sy == '*'
|
|
2994
|
+
s.put_back(*saved)
|
|
2995
|
+
elif s.sy == '[':
|
|
2996
|
+
s.next()
|
|
2997
|
+
is_type = s.sy == ']' or not looking_at_expr(s) # could be a nested template type
|
|
2998
|
+
s.put_back(*saved)
|
|
2999
|
+
|
|
3000
|
+
dotted_path.reverse()
|
|
3001
|
+
for p in dotted_path:
|
|
3002
|
+
s.put_back('IDENT', *p)
|
|
3003
|
+
s.put_back('.', '.', p[1]) # gets the position slightly wrong
|
|
3004
|
+
|
|
3005
|
+
s.put_back('IDENT', name, name_pos)
|
|
3006
|
+
return not is_type and saved[0]
|
|
3007
|
+
else:
|
|
3008
|
+
return True
|
|
3009
|
+
|
|
3010
|
+
|
|
3011
|
+
@cython.cfunc
|
|
3012
|
+
def looking_at_base_type(s: PyrexScanner) -> cython.bint:
|
|
3013
|
+
#print "looking_at_base_type?", s.sy, s.systring, s.position()
|
|
3014
|
+
return s.sy == 'IDENT' and s.systring in base_type_start_words
|
|
3015
|
+
|
|
3016
|
+
|
|
3017
|
+
@cython.cfunc
|
|
3018
|
+
def looking_at_dotted_name(s: PyrexScanner) -> cython.bint:
|
|
3019
|
+
if s.sy == 'IDENT':
|
|
3020
|
+
name = s.systring
|
|
3021
|
+
name_pos = s.position()
|
|
3022
|
+
s.next()
|
|
3023
|
+
result: cython.bint = s.sy == '.'
|
|
3024
|
+
s.put_back('IDENT', name, name_pos)
|
|
3025
|
+
return result
|
|
3026
|
+
else:
|
|
3027
|
+
return False
|
|
3028
|
+
|
|
3029
|
+
|
|
3030
|
+
basic_c_type_names = cython.declare(frozenset, frozenset((
|
|
3031
|
+
"void", "char", "int", "float", "double", "bint")))
|
|
3032
|
+
|
|
3033
|
+
special_basic_c_types = cython.declare(dict, {
|
|
3034
|
+
# name : (signed, longness)
|
|
3035
|
+
"Py_UNICODE" : (0, 0),
|
|
3036
|
+
"Py_UCS4" : (0, 0),
|
|
3037
|
+
"Py_hash_t" : (2, 0),
|
|
3038
|
+
"Py_ssize_t" : (2, 0),
|
|
3039
|
+
"ssize_t" : (2, 0),
|
|
3040
|
+
"size_t" : (0, 0),
|
|
3041
|
+
"ptrdiff_t" : (2, 0),
|
|
3042
|
+
"Py_tss_t" : (1, 0),
|
|
3043
|
+
})
|
|
3044
|
+
|
|
3045
|
+
sign_and_longness_words = cython.declare(frozenset, frozenset((
|
|
3046
|
+
"short", "long", "signed", "unsigned")))
|
|
3047
|
+
|
|
3048
|
+
base_type_start_words = cython.declare(
|
|
3049
|
+
frozenset,
|
|
3050
|
+
basic_c_type_names
|
|
3051
|
+
| sign_and_longness_words
|
|
3052
|
+
| frozenset(special_basic_c_types))
|
|
3053
|
+
|
|
3054
|
+
struct_enum_union = cython.declare(frozenset, frozenset((
|
|
3055
|
+
"struct", "union", "enum", "packed")))
|
|
3056
|
+
|
|
3057
|
+
|
|
3058
|
+
@cython.cfunc
|
|
3059
|
+
def p_sign_and_longness(s: PyrexScanner) -> tuple:
|
|
3060
|
+
signed = 1
|
|
3061
|
+
longness = 0
|
|
3062
|
+
while s.sy == 'IDENT' and s.systring in sign_and_longness_words:
|
|
3063
|
+
if s.systring == 'unsigned':
|
|
3064
|
+
signed = 0
|
|
3065
|
+
elif s.systring == 'signed':
|
|
3066
|
+
signed = 2
|
|
3067
|
+
elif s.systring == 'short':
|
|
3068
|
+
longness = -1
|
|
3069
|
+
elif s.systring == 'long':
|
|
3070
|
+
longness += 1
|
|
3071
|
+
s.next()
|
|
3072
|
+
return signed, longness
|
|
3073
|
+
|
|
3074
|
+
|
|
3075
|
+
@cython.cfunc
|
|
3076
|
+
def p_opt_cname(s: PyrexScanner):
|
|
3077
|
+
literal = p_opt_string_literal(s, 'u')
|
|
3078
|
+
if literal is not None:
|
|
3079
|
+
cname = EncodedString(literal)
|
|
3080
|
+
cname.encoding = s.source_encoding
|
|
3081
|
+
else:
|
|
3082
|
+
cname = None
|
|
3083
|
+
return cname
|
|
3084
|
+
|
|
3085
|
+
|
|
3086
|
+
@cython.ccall
|
|
3087
|
+
def p_c_declarator(s: PyrexScanner, ctx = Ctx(),
|
|
3088
|
+
empty: cython.bint = False, is_type: cython.bint = False, cmethod_flag: cython.bint = False,
|
|
3089
|
+
assignable: cython.bint = False, nonempty: cython.bint = False,
|
|
3090
|
+
calling_convention_allowed: cython.bint = False):
|
|
3091
|
+
# If empty is true, the declarator must be empty. If nonempty is true,
|
|
3092
|
+
# the declarator must be nonempty. Otherwise we don't care.
|
|
3093
|
+
# If cmethod_flag is true, then if this declarator declares
|
|
3094
|
+
# a function, it's a C method of an extension type.
|
|
3095
|
+
pos = s.position()
|
|
3096
|
+
if s.sy == '(':
|
|
3097
|
+
s.next()
|
|
3098
|
+
if s.sy == ')' or looking_at_name(s):
|
|
3099
|
+
base = Nodes.CNameDeclaratorNode(pos, name=s.context.intern_ustring(""), cname=None)
|
|
3100
|
+
result = p_c_func_declarator(s, pos, ctx, base, cmethod_flag)
|
|
3101
|
+
else:
|
|
3102
|
+
result = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
|
|
3103
|
+
cmethod_flag = cmethod_flag,
|
|
3104
|
+
nonempty = nonempty,
|
|
3105
|
+
calling_convention_allowed = True)
|
|
3106
|
+
s.expect(')')
|
|
3107
|
+
else:
|
|
3108
|
+
result = p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag,
|
|
3109
|
+
assignable, nonempty)
|
|
3110
|
+
if not calling_convention_allowed and result.calling_convention and s.sy != '(':
|
|
3111
|
+
error(s.position(), "%s on something that is not a function"
|
|
3112
|
+
% result.calling_convention)
|
|
3113
|
+
while s.sy in ('[', '('):
|
|
3114
|
+
pos = s.position()
|
|
3115
|
+
if s.sy == '[':
|
|
3116
|
+
result = p_c_array_declarator(s, result)
|
|
3117
|
+
else: # sy == '('
|
|
3118
|
+
s.next()
|
|
3119
|
+
result = p_c_func_declarator(s, pos, ctx, result, cmethod_flag)
|
|
3120
|
+
cmethod_flag = 0
|
|
3121
|
+
return result
|
|
3122
|
+
|
|
3123
|
+
|
|
3124
|
+
@cython.cfunc
|
|
3125
|
+
def p_c_array_declarator(s: PyrexScanner, base):
|
|
3126
|
+
pos = s.position()
|
|
3127
|
+
s.next() # '['
|
|
3128
|
+
if s.sy != ']':
|
|
3129
|
+
dim = p_testlist(s)
|
|
3130
|
+
else:
|
|
3131
|
+
dim = None
|
|
3132
|
+
s.expect(']')
|
|
3133
|
+
return Nodes.CArrayDeclaratorNode(pos, base = base, dimension = dim)
|
|
3134
|
+
|
|
3135
|
+
|
|
3136
|
+
@cython.cfunc
|
|
3137
|
+
def p_c_func_declarator(s: PyrexScanner, pos, ctx, base, cmethod_flag: cython.bint):
|
|
3138
|
+
# Opening paren has already been skipped
|
|
3139
|
+
args = p_c_arg_list(s, ctx, cmethod_flag = cmethod_flag,
|
|
3140
|
+
nonempty_declarators = 0)
|
|
3141
|
+
ellipsis = p_optional_ellipsis(s)
|
|
3142
|
+
s.expect(')')
|
|
3143
|
+
nogil = p_nogil(s)
|
|
3144
|
+
exc_val, exc_check, exc_clause = p_exception_value_clause(s, ctx.visibility == 'extern')
|
|
3145
|
+
if nogil and exc_clause:
|
|
3146
|
+
warning(
|
|
3147
|
+
s.position(),
|
|
3148
|
+
"The keyword 'nogil' should appear at the end of the "
|
|
3149
|
+
"function signature line. Placing it before 'except' "
|
|
3150
|
+
"or 'noexcept' will be disallowed in a future version "
|
|
3151
|
+
"of Cython.",
|
|
3152
|
+
level=2
|
|
3153
|
+
)
|
|
3154
|
+
nogil = nogil or p_nogil(s)
|
|
3155
|
+
with_gil = p_with_gil(s)
|
|
3156
|
+
return Nodes.CFuncDeclaratorNode(pos,
|
|
3157
|
+
base = base, args = args, has_varargs = ellipsis,
|
|
3158
|
+
exception_value = exc_val, exception_check = exc_check,
|
|
3159
|
+
nogil = nogil or ctx.nogil or with_gil, with_gil = with_gil, has_explicit_exc_clause=exc_clause)
|
|
3160
|
+
|
|
3161
|
+
|
|
3162
|
+
supported_overloaded_operators = cython.declare(frozenset, frozenset((
|
|
3163
|
+
'+', '-', '*', '/', '%',
|
|
3164
|
+
'++', '--', '~', '|', '&', '^', '<<', '>>', ',',
|
|
3165
|
+
'==', '!=', '>=', '>', '<=', '<',
|
|
3166
|
+
'[]', '()', '!', '=',
|
|
3167
|
+
'bool',
|
|
3168
|
+
)))
|
|
3169
|
+
|
|
3170
|
+
|
|
3171
|
+
@cython.cfunc
|
|
3172
|
+
def p_c_simple_declarator(s: PyrexScanner, ctx,
|
|
3173
|
+
empty: cython.bint, is_type: cython.bint, cmethod_flag: cython.bint,
|
|
3174
|
+
assignable: cython.bint, nonempty: cython.bint):
|
|
3175
|
+
pos = s.position()
|
|
3176
|
+
calling_convention = p_calling_convention(s)
|
|
3177
|
+
if s.sy in ('*', '**'):
|
|
3178
|
+
# scanner returns '**' as a single token
|
|
3179
|
+
is_ptrptr = s.sy == '**'
|
|
3180
|
+
s.next()
|
|
3181
|
+
|
|
3182
|
+
const_pos = s.position()
|
|
3183
|
+
is_const = s.systring == 'const' and s.sy == 'IDENT'
|
|
3184
|
+
if is_const:
|
|
3185
|
+
s.next()
|
|
3186
|
+
|
|
3187
|
+
base = p_c_declarator(s, ctx, empty=empty, is_type=is_type,
|
|
3188
|
+
cmethod_flag=cmethod_flag,
|
|
3189
|
+
assignable=assignable, nonempty=nonempty)
|
|
3190
|
+
if is_const:
|
|
3191
|
+
base = Nodes.CConstDeclaratorNode(const_pos, base=base)
|
|
3192
|
+
if is_ptrptr:
|
|
3193
|
+
base = Nodes.CPtrDeclaratorNode(pos, base=base)
|
|
3194
|
+
result = Nodes.CPtrDeclaratorNode(pos, base=base)
|
|
3195
|
+
elif s.sy == '&' or (s.sy == '&&' and s.context.cpp):
|
|
3196
|
+
node_class = Nodes.CppRvalueReferenceDeclaratorNode if s.sy == '&&' else Nodes.CReferenceDeclaratorNode
|
|
3197
|
+
s.next()
|
|
3198
|
+
base = p_c_declarator(s, ctx, empty=empty, is_type=is_type,
|
|
3199
|
+
cmethod_flag=cmethod_flag,
|
|
3200
|
+
assignable=assignable, nonempty=nonempty)
|
|
3201
|
+
result = node_class(pos, base=base)
|
|
3202
|
+
else:
|
|
3203
|
+
rhs = None
|
|
3204
|
+
if s.sy == 'IDENT':
|
|
3205
|
+
name = s.systring
|
|
3206
|
+
if empty:
|
|
3207
|
+
error(s.position(), "Declarator should be empty")
|
|
3208
|
+
s.next()
|
|
3209
|
+
cname = p_opt_cname(s)
|
|
3210
|
+
if name != 'operator' and s.sy == '=' and assignable:
|
|
3211
|
+
s.next()
|
|
3212
|
+
rhs = p_test(s)
|
|
3213
|
+
else:
|
|
3214
|
+
if nonempty:
|
|
3215
|
+
error(s.position(), "Empty declarator")
|
|
3216
|
+
name = ""
|
|
3217
|
+
cname = None
|
|
3218
|
+
if cname is None and ctx.namespace is not None and nonempty:
|
|
3219
|
+
cname = ctx.namespace + "::" + name
|
|
3220
|
+
if name == 'operator' and ctx.visibility == 'extern' and nonempty:
|
|
3221
|
+
op = s.sy
|
|
3222
|
+
if [1 for c in op if c in '+-*/<=>!%&|([^~,']:
|
|
3223
|
+
s.next()
|
|
3224
|
+
# Handle diphthong operators.
|
|
3225
|
+
if op == '(':
|
|
3226
|
+
s.expect(')')
|
|
3227
|
+
op = '()'
|
|
3228
|
+
elif op == '[':
|
|
3229
|
+
s.expect(']')
|
|
3230
|
+
op = '[]'
|
|
3231
|
+
elif op in ('-', '+', '|', '&') and s.sy == op:
|
|
3232
|
+
op *= 2 # ++, --, ...
|
|
3233
|
+
s.next()
|
|
3234
|
+
elif s.sy == '=':
|
|
3235
|
+
op += s.sy # +=, -=, ...
|
|
3236
|
+
s.next()
|
|
3237
|
+
if op not in supported_overloaded_operators:
|
|
3238
|
+
s.error("Overloading operator '%s' not yet supported." % op,
|
|
3239
|
+
fatal=False)
|
|
3240
|
+
name += op
|
|
3241
|
+
elif op == 'IDENT':
|
|
3242
|
+
op = s.systring
|
|
3243
|
+
if op not in supported_overloaded_operators:
|
|
3244
|
+
s.error("Overloading operator '%s' not yet supported." % op,
|
|
3245
|
+
fatal=False)
|
|
3246
|
+
name = name + ' ' + op
|
|
3247
|
+
s.next()
|
|
3248
|
+
result = Nodes.CNameDeclaratorNode(pos,
|
|
3249
|
+
name = name, cname = cname, default = rhs)
|
|
3250
|
+
result.calling_convention = calling_convention
|
|
3251
|
+
return result
|
|
3252
|
+
|
|
3253
|
+
|
|
3254
|
+
@cython.cfunc
|
|
3255
|
+
def p_nogil(s: PyrexScanner) -> cython.bint:
|
|
3256
|
+
if s.sy == 'IDENT' and s.systring == 'nogil':
|
|
3257
|
+
s.next()
|
|
3258
|
+
return True
|
|
3259
|
+
else:
|
|
3260
|
+
return False
|
|
3261
|
+
|
|
3262
|
+
|
|
3263
|
+
@cython.cfunc
|
|
3264
|
+
def p_with_gil(s: PyrexScanner) -> cython.bint:
|
|
3265
|
+
if s.sy == 'with':
|
|
3266
|
+
s.next()
|
|
3267
|
+
s.expect_keyword('gil')
|
|
3268
|
+
return True
|
|
3269
|
+
else:
|
|
3270
|
+
return False
|
|
3271
|
+
|
|
3272
|
+
|
|
3273
|
+
@cython.cfunc
|
|
3274
|
+
def p_exception_value_clause(s: PyrexScanner, is_extern: cython.bint) -> tuple:
|
|
3275
|
+
"""
|
|
3276
|
+
Parse exception value clause.
|
|
3277
|
+
|
|
3278
|
+
Maps clauses to exc_check / exc_value / exc_clause as follows:
|
|
3279
|
+
______________________________________________________________________
|
|
3280
|
+
| | | | |
|
|
3281
|
+
| Clause | exc_check | exc_value | exc_clause |
|
|
3282
|
+
| ___________________________ | ___________ | ___________ | __________ |
|
|
3283
|
+
| | | | |
|
|
3284
|
+
| <nothing> (default func.) | True | None | False |
|
|
3285
|
+
| <nothing> (cdef extern) | False | None | False |
|
|
3286
|
+
| noexcept | False | None | True |
|
|
3287
|
+
| except <val> | False | <val> | True |
|
|
3288
|
+
| except? <val> | True | <val> | True |
|
|
3289
|
+
| except * | True | None | True |
|
|
3290
|
+
| except + | '+' | None | True |
|
|
3291
|
+
| except +* | '+' | '*' | True |
|
|
3292
|
+
| except +<PyErr> | '+' | <PyErr> | True |
|
|
3293
|
+
| ___________________________ | ___________ | ___________ | __________ |
|
|
3294
|
+
|
|
3295
|
+
Note that the only reason we need `exc_clause` is to raise a
|
|
3296
|
+
warning when `'except'` or `'noexcept'` is placed after the
|
|
3297
|
+
`'nogil'` keyword.
|
|
3298
|
+
"""
|
|
3299
|
+
exc_clause: cython.bint = False
|
|
3300
|
+
exc_val = None
|
|
3301
|
+
exc_check = False if is_extern else True
|
|
3302
|
+
|
|
3303
|
+
if s.sy == 'IDENT' and s.systring == 'noexcept':
|
|
3304
|
+
exc_clause = True
|
|
3305
|
+
s.next()
|
|
3306
|
+
exc_check = False
|
|
3307
|
+
elif s.sy == 'except':
|
|
3308
|
+
exc_clause = True
|
|
3309
|
+
s.next()
|
|
3310
|
+
if s.sy == '*':
|
|
3311
|
+
exc_check = True
|
|
3312
|
+
s.next()
|
|
3313
|
+
elif s.sy == '+':
|
|
3314
|
+
exc_check = '+'
|
|
3315
|
+
plus_char_pos = s.position()[2]
|
|
3316
|
+
s.next()
|
|
3317
|
+
if s.sy == 'IDENT':
|
|
3318
|
+
name = s.systring
|
|
3319
|
+
if name == 'nogil':
|
|
3320
|
+
if s.position()[2] == plus_char_pos + 1:
|
|
3321
|
+
error(s.position(),
|
|
3322
|
+
"'except +nogil' defines an exception handling function. Use 'except + nogil' for the 'nogil' modifier.")
|
|
3323
|
+
# 'except + nogil' is parsed outside
|
|
3324
|
+
else:
|
|
3325
|
+
exc_val = p_name(s, name)
|
|
3326
|
+
s.next()
|
|
3327
|
+
elif s.sy == '*':
|
|
3328
|
+
exc_val = ExprNodes.CharNode(s.position(), value='*')
|
|
3329
|
+
s.next()
|
|
3330
|
+
else:
|
|
3331
|
+
if s.sy == '?':
|
|
3332
|
+
exc_check = True
|
|
3333
|
+
s.next()
|
|
3334
|
+
else:
|
|
3335
|
+
exc_check = False
|
|
3336
|
+
# exc_val can be non-None even if exc_check is False, c.f. "except -1"
|
|
3337
|
+
exc_val = p_test(s)
|
|
3338
|
+
|
|
3339
|
+
return exc_val, exc_check, exc_clause
|
|
3340
|
+
|
|
3341
|
+
|
|
3342
|
+
c_arg_list_terminators = cython.declare(frozenset, frozenset((
|
|
3343
|
+
'*', '**', '...', ')', ':', '/')))
|
|
3344
|
+
|
|
3345
|
+
|
|
3346
|
+
@cython.ccall
|
|
3347
|
+
def p_c_arg_list(s: PyrexScanner, ctx = Ctx(),
|
|
3348
|
+
in_pyfunc: cython.bint = False, cmethod_flag: cython.bint = False,
|
|
3349
|
+
nonempty_declarators: cython.bint = False, kw_only: cython.bint = False,
|
|
3350
|
+
annotated: cython.bint = True) -> list:
|
|
3351
|
+
# Comma-separated list of C argument declarations, possibly empty.
|
|
3352
|
+
# May have a trailing comma.
|
|
3353
|
+
args = []
|
|
3354
|
+
is_self_arg = cmethod_flag
|
|
3355
|
+
while s.sy not in c_arg_list_terminators:
|
|
3356
|
+
args.append(p_c_arg_decl(s, ctx, in_pyfunc, is_self_arg,
|
|
3357
|
+
nonempty = nonempty_declarators, kw_only = kw_only,
|
|
3358
|
+
annotated = annotated))
|
|
3359
|
+
if s.sy != ',':
|
|
3360
|
+
break
|
|
3361
|
+
s.next()
|
|
3362
|
+
is_self_arg = 0
|
|
3363
|
+
return args
|
|
3364
|
+
|
|
3365
|
+
|
|
3366
|
+
@cython.cfunc
|
|
3367
|
+
def p_optional_ellipsis(s: PyrexScanner) -> cython.bint:
|
|
3368
|
+
if s.sy == '...':
|
|
3369
|
+
expect_ellipsis(s)
|
|
3370
|
+
return True
|
|
3371
|
+
else:
|
|
3372
|
+
return False
|
|
3373
|
+
|
|
3374
|
+
|
|
3375
|
+
@cython.cfunc
|
|
3376
|
+
def p_c_arg_decl(s: PyrexScanner, ctx, in_pyfunc: cython.bint, cmethod_flag: cython.bint = False,
|
|
3377
|
+
nonempty: cython.bint = False,
|
|
3378
|
+
kw_only: cython.bint = False, annotated: cython.bint = True):
|
|
3379
|
+
pos = s.position()
|
|
3380
|
+
not_none = or_none = False
|
|
3381
|
+
default = None
|
|
3382
|
+
annotation = None
|
|
3383
|
+
if s.in_python_file:
|
|
3384
|
+
# empty type declaration
|
|
3385
|
+
base_type = Nodes.CSimpleBaseTypeNode(pos,
|
|
3386
|
+
name = None, module_path = [],
|
|
3387
|
+
is_basic_c_type = False, signed = 0,
|
|
3388
|
+
complex = False, longness = 0,
|
|
3389
|
+
is_self_arg = cmethod_flag, templates = None)
|
|
3390
|
+
else:
|
|
3391
|
+
base_type = p_c_base_type(s, nonempty=nonempty)
|
|
3392
|
+
declarator = p_c_declarator(s, ctx, nonempty = nonempty)
|
|
3393
|
+
if s.sy in ('not', 'or') and not s.in_python_file:
|
|
3394
|
+
kind = s.sy
|
|
3395
|
+
s.next()
|
|
3396
|
+
if s.sy == 'IDENT' and s.systring == 'None':
|
|
3397
|
+
s.next()
|
|
3398
|
+
else:
|
|
3399
|
+
s.error("Expected 'None'")
|
|
3400
|
+
if not in_pyfunc:
|
|
3401
|
+
error(pos, "'%s None' only allowed in Python functions" % kind)
|
|
3402
|
+
or_none = kind == 'or'
|
|
3403
|
+
not_none = kind == 'not'
|
|
3404
|
+
if annotated and s.sy == ':':
|
|
3405
|
+
s.next()
|
|
3406
|
+
annotation = p_annotation(s)
|
|
3407
|
+
if s.sy == '=':
|
|
3408
|
+
s.next()
|
|
3409
|
+
if 'pxd' in ctx.level:
|
|
3410
|
+
if s.sy in ['*', '?']:
|
|
3411
|
+
# TODO(github/1736): Make this an error for inline declarations.
|
|
3412
|
+
default = ExprNodes.NoneNode(pos)
|
|
3413
|
+
s.next()
|
|
3414
|
+
elif 'inline' in ctx.modifiers:
|
|
3415
|
+
default = p_test(s)
|
|
3416
|
+
else:
|
|
3417
|
+
error(pos, "default values cannot be specified in pxd files, use ? or *")
|
|
3418
|
+
else:
|
|
3419
|
+
default = p_test(s)
|
|
3420
|
+
return Nodes.CArgDeclNode(pos,
|
|
3421
|
+
base_type = base_type,
|
|
3422
|
+
declarator = declarator,
|
|
3423
|
+
not_none = not_none,
|
|
3424
|
+
or_none = or_none,
|
|
3425
|
+
default = default,
|
|
3426
|
+
annotation = annotation,
|
|
3427
|
+
kw_only = kw_only)
|
|
3428
|
+
|
|
3429
|
+
|
|
3430
|
+
@cython.cfunc
|
|
3431
|
+
def p_annotation(s: PyrexScanner):
|
|
3432
|
+
"""An annotation just has the "test" syntax, but also stores the string it came from
|
|
3433
|
+
|
|
3434
|
+
Note that the string is *allowed* to be changed/processed (although isn't here)
|
|
3435
|
+
so may not exactly match the string generated by Python, and if it doesn't
|
|
3436
|
+
then it is not a bug.
|
|
3437
|
+
"""
|
|
3438
|
+
pos = s.position()
|
|
3439
|
+
expr = p_test(s)
|
|
3440
|
+
return ExprNodes.AnnotationNode(pos, expr=expr)
|
|
3441
|
+
|
|
3442
|
+
|
|
3443
|
+
@cython.cfunc
|
|
3444
|
+
def p_api(s: PyrexScanner) -> cython.bint:
|
|
3445
|
+
if s.sy == 'IDENT' and s.systring == 'api':
|
|
3446
|
+
s.next()
|
|
3447
|
+
return True
|
|
3448
|
+
else:
|
|
3449
|
+
return False
|
|
3450
|
+
|
|
3451
|
+
|
|
3452
|
+
@cython.cfunc
|
|
3453
|
+
def p_cdef_statement(s: PyrexScanner, pos, ctx):
|
|
3454
|
+
ctx.visibility = p_visibility(s, ctx.visibility)
|
|
3455
|
+
ctx.api = ctx.api or p_api(s)
|
|
3456
|
+
if ctx.api:
|
|
3457
|
+
if ctx.visibility not in ('private', 'public'):
|
|
3458
|
+
error(pos, "Cannot combine 'api' with '%s'" % ctx.visibility)
|
|
3459
|
+
if (ctx.visibility == 'extern') and s.sy == 'from':
|
|
3460
|
+
return p_cdef_extern_block(s, pos, ctx)
|
|
3461
|
+
elif s.sy == 'import':
|
|
3462
|
+
s.next()
|
|
3463
|
+
return p_cdef_extern_block(s, pos, ctx)
|
|
3464
|
+
elif p_nogil(s):
|
|
3465
|
+
ctx.nogil = True
|
|
3466
|
+
if ctx.overridable:
|
|
3467
|
+
error(pos, "cdef blocks cannot be declared cpdef")
|
|
3468
|
+
return p_cdef_block(s, ctx)
|
|
3469
|
+
elif s.sy == ':':
|
|
3470
|
+
if ctx.overridable:
|
|
3471
|
+
error(pos, "cdef blocks cannot be declared cpdef")
|
|
3472
|
+
return p_cdef_block(s, ctx)
|
|
3473
|
+
elif s.sy == 'class':
|
|
3474
|
+
if ctx.level not in ('module', 'module_pxd'):
|
|
3475
|
+
error(pos, "Extension type definition not allowed here")
|
|
3476
|
+
if ctx.overridable:
|
|
3477
|
+
error(pos, "Extension types cannot be declared cpdef")
|
|
3478
|
+
return p_c_class_definition(s, pos, ctx)
|
|
3479
|
+
elif s.sy == 'IDENT' and s.systring == 'cppclass':
|
|
3480
|
+
return p_cpp_class_definition(s, pos, ctx)
|
|
3481
|
+
elif s.sy == 'IDENT' and s.systring in struct_enum_union:
|
|
3482
|
+
if ctx.level not in ('module', 'module_pxd'):
|
|
3483
|
+
error(pos, "C struct/union/enum definition not allowed here")
|
|
3484
|
+
if ctx.overridable:
|
|
3485
|
+
if s.systring != 'enum':
|
|
3486
|
+
error(pos, "C struct/union cannot be declared cpdef")
|
|
3487
|
+
return p_struct_enum(s, pos, ctx)
|
|
3488
|
+
elif s.sy == 'IDENT' and s.systring == 'fused':
|
|
3489
|
+
return p_fused_definition(s, pos, ctx)
|
|
3490
|
+
else:
|
|
3491
|
+
return p_c_func_or_var_declaration(s, pos, ctx)
|
|
3492
|
+
|
|
3493
|
+
|
|
3494
|
+
@cython.cfunc
|
|
3495
|
+
def p_cdef_block(s: PyrexScanner, ctx):
|
|
3496
|
+
return p_suite(s, ctx(cdef_flag = True))
|
|
3497
|
+
|
|
3498
|
+
|
|
3499
|
+
@cython.cfunc
|
|
3500
|
+
def p_cdef_extern_block(s: PyrexScanner, pos, ctx):
|
|
3501
|
+
if ctx.overridable:
|
|
3502
|
+
error(pos, "cdef extern blocks cannot be declared cpdef")
|
|
3503
|
+
include_file = None
|
|
3504
|
+
s.expect('from')
|
|
3505
|
+
if s.sy == '*':
|
|
3506
|
+
s.next()
|
|
3507
|
+
else:
|
|
3508
|
+
include_file = p_string_literal(s, 'u')[2]
|
|
3509
|
+
ctx = ctx(cdef_flag = True, visibility = 'extern')
|
|
3510
|
+
if s.systring == "namespace":
|
|
3511
|
+
s.next()
|
|
3512
|
+
ctx.namespace = p_string_literal(s, 'u')[2]
|
|
3513
|
+
if p_nogil(s):
|
|
3514
|
+
ctx.nogil = True
|
|
3515
|
+
|
|
3516
|
+
# Use "docstring" as verbatim string to include
|
|
3517
|
+
verbatim_include, body = p_suite_with_docstring(s, ctx, True)
|
|
3518
|
+
|
|
3519
|
+
return Nodes.CDefExternNode(pos,
|
|
3520
|
+
include_file = include_file,
|
|
3521
|
+
verbatim_include = verbatim_include,
|
|
3522
|
+
body = body,
|
|
3523
|
+
namespace = ctx.namespace)
|
|
3524
|
+
|
|
3525
|
+
|
|
3526
|
+
@cython.cfunc
|
|
3527
|
+
def p_c_enum_definition(s: PyrexScanner, pos, ctx):
|
|
3528
|
+
# s.sy == ident 'enum'
|
|
3529
|
+
s.next()
|
|
3530
|
+
|
|
3531
|
+
scoped = False
|
|
3532
|
+
if s.context.cpp and (s.sy == 'class' or (s.sy == 'IDENT' and s.systring == 'struct')):
|
|
3533
|
+
scoped = True
|
|
3534
|
+
s.next()
|
|
3535
|
+
|
|
3536
|
+
if s.sy == 'IDENT':
|
|
3537
|
+
name = s.systring
|
|
3538
|
+
s.next()
|
|
3539
|
+
cname = p_opt_cname(s)
|
|
3540
|
+
if cname is None and ctx.namespace is not None:
|
|
3541
|
+
cname = ctx.namespace + "::" + name
|
|
3542
|
+
else:
|
|
3543
|
+
name = cname = None
|
|
3544
|
+
if scoped:
|
|
3545
|
+
s.error("Unnamed scoped enum not allowed")
|
|
3546
|
+
|
|
3547
|
+
if scoped and s.sy == '(':
|
|
3548
|
+
s.next()
|
|
3549
|
+
underlying_type = p_c_base_type(s)
|
|
3550
|
+
s.expect(')')
|
|
3551
|
+
else:
|
|
3552
|
+
underlying_type = Nodes.CSimpleBaseTypeNode(
|
|
3553
|
+
pos,
|
|
3554
|
+
name="int",
|
|
3555
|
+
module_path = [],
|
|
3556
|
+
is_basic_c_type = True,
|
|
3557
|
+
signed = 1,
|
|
3558
|
+
complex = False,
|
|
3559
|
+
longness = 0
|
|
3560
|
+
)
|
|
3561
|
+
|
|
3562
|
+
s.expect(':')
|
|
3563
|
+
items = []
|
|
3564
|
+
|
|
3565
|
+
doc = None
|
|
3566
|
+
if s.sy != 'NEWLINE':
|
|
3567
|
+
p_c_enum_line(s, ctx, items)
|
|
3568
|
+
else:
|
|
3569
|
+
s.next() # 'NEWLINE'
|
|
3570
|
+
s.expect_indent()
|
|
3571
|
+
doc = p_doc_string(s)
|
|
3572
|
+
|
|
3573
|
+
while s.sy not in ('DEDENT', 'EOF'):
|
|
3574
|
+
p_c_enum_line(s, ctx, items)
|
|
3575
|
+
|
|
3576
|
+
s.expect_dedent()
|
|
3577
|
+
|
|
3578
|
+
if not items and ctx.visibility != "extern":
|
|
3579
|
+
error(pos, "Empty enum definition not allowed outside a 'cdef extern from' block")
|
|
3580
|
+
|
|
3581
|
+
return Nodes.CEnumDefNode(
|
|
3582
|
+
pos, name=name, cname=cname,
|
|
3583
|
+
scoped=scoped, items=items,
|
|
3584
|
+
underlying_type=underlying_type,
|
|
3585
|
+
typedef_flag=ctx.typedef_flag, visibility=ctx.visibility,
|
|
3586
|
+
create_wrapper=ctx.overridable,
|
|
3587
|
+
api=ctx.api, in_pxd=ctx.level == 'module_pxd', doc=doc)
|
|
3588
|
+
|
|
3589
|
+
|
|
3590
|
+
@cython.cfunc
|
|
3591
|
+
def p_c_enum_line(s: PyrexScanner, ctx, items: list):
|
|
3592
|
+
if s.sy != 'pass':
|
|
3593
|
+
p_c_enum_item(s, ctx, items)
|
|
3594
|
+
while s.sy == ',':
|
|
3595
|
+
s.next()
|
|
3596
|
+
if s.sy in ('NEWLINE', 'EOF'):
|
|
3597
|
+
break
|
|
3598
|
+
p_c_enum_item(s, ctx, items)
|
|
3599
|
+
else:
|
|
3600
|
+
s.next()
|
|
3601
|
+
s.expect_newline("Syntax error in enum item list")
|
|
3602
|
+
|
|
3603
|
+
|
|
3604
|
+
@cython.cfunc
|
|
3605
|
+
def p_c_enum_item(s: PyrexScanner, ctx, items: list):
|
|
3606
|
+
pos = s.position()
|
|
3607
|
+
name = p_ident(s)
|
|
3608
|
+
cname = p_opt_cname(s)
|
|
3609
|
+
if cname is None and ctx.namespace is not None:
|
|
3610
|
+
cname = ctx.namespace + "::" + name
|
|
3611
|
+
value = None
|
|
3612
|
+
if s.sy == '=':
|
|
3613
|
+
s.next()
|
|
3614
|
+
value = p_test(s)
|
|
3615
|
+
items.append(Nodes.CEnumDefItemNode(pos,
|
|
3616
|
+
name = name, cname = cname, value = value))
|
|
3617
|
+
|
|
3618
|
+
|
|
3619
|
+
@cython.cfunc
|
|
3620
|
+
def p_c_struct_or_union_definition(s: PyrexScanner, pos, ctx):
|
|
3621
|
+
packed = False
|
|
3622
|
+
if s.systring == 'packed':
|
|
3623
|
+
packed = True
|
|
3624
|
+
s.next()
|
|
3625
|
+
if s.sy != 'IDENT' or s.systring != 'struct':
|
|
3626
|
+
s.expected('struct')
|
|
3627
|
+
# s.sy == ident 'struct' or 'union'
|
|
3628
|
+
kind = s.systring
|
|
3629
|
+
s.next()
|
|
3630
|
+
name = p_ident(s)
|
|
3631
|
+
cname = p_opt_cname(s)
|
|
3632
|
+
if cname is None and ctx.namespace is not None:
|
|
3633
|
+
cname = ctx.namespace + "::" + name
|
|
3634
|
+
attributes = None
|
|
3635
|
+
if s.sy == ':':
|
|
3636
|
+
s.next()
|
|
3637
|
+
attributes = []
|
|
3638
|
+
if s.sy == 'pass':
|
|
3639
|
+
s.next()
|
|
3640
|
+
s.expect_newline("Expected a newline", ignore_semicolon=True)
|
|
3641
|
+
else:
|
|
3642
|
+
s.expect('NEWLINE')
|
|
3643
|
+
s.expect_indent()
|
|
3644
|
+
body_ctx = Ctx(visibility=ctx.visibility)
|
|
3645
|
+
while s.sy != 'DEDENT':
|
|
3646
|
+
if s.sy != 'pass':
|
|
3647
|
+
attributes.append(
|
|
3648
|
+
p_c_func_or_var_declaration(s, s.position(), body_ctx))
|
|
3649
|
+
else:
|
|
3650
|
+
s.next()
|
|
3651
|
+
s.expect_newline("Expected a newline")
|
|
3652
|
+
s.expect_dedent()
|
|
3653
|
+
|
|
3654
|
+
if not attributes and ctx.visibility != "extern":
|
|
3655
|
+
error(pos, "Empty struct or union definition not allowed outside a 'cdef extern from' block")
|
|
3656
|
+
else:
|
|
3657
|
+
s.expect_newline("Syntax error in struct or union definition")
|
|
3658
|
+
|
|
3659
|
+
return Nodes.CStructOrUnionDefNode(pos,
|
|
3660
|
+
name = name, cname = cname, kind = kind, attributes = attributes,
|
|
3661
|
+
typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
|
|
3662
|
+
api = ctx.api, in_pxd = ctx.level == 'module_pxd', packed = packed)
|
|
3663
|
+
|
|
3664
|
+
|
|
3665
|
+
@cython.cfunc
|
|
3666
|
+
def p_fused_definition(s: PyrexScanner, pos, ctx):
|
|
3667
|
+
"""
|
|
3668
|
+
c(type)def fused my_fused_type:
|
|
3669
|
+
...
|
|
3670
|
+
"""
|
|
3671
|
+
# s.systring == 'fused'
|
|
3672
|
+
|
|
3673
|
+
if ctx.level not in ('module', 'module_pxd'):
|
|
3674
|
+
error(pos, "Fused type definition not allowed here")
|
|
3675
|
+
|
|
3676
|
+
s.next()
|
|
3677
|
+
name = p_ident(s)
|
|
3678
|
+
|
|
3679
|
+
s.expect(":")
|
|
3680
|
+
s.expect_newline()
|
|
3681
|
+
s.expect_indent()
|
|
3682
|
+
|
|
3683
|
+
types = []
|
|
3684
|
+
while s.sy != 'DEDENT':
|
|
3685
|
+
if s.sy != 'pass':
|
|
3686
|
+
#types.append(p_c_declarator(s))
|
|
3687
|
+
types.append(p_c_base_type(s)) #, nonempty=1))
|
|
3688
|
+
else:
|
|
3689
|
+
s.next()
|
|
3690
|
+
|
|
3691
|
+
s.expect_newline()
|
|
3692
|
+
|
|
3693
|
+
s.expect_dedent()
|
|
3694
|
+
|
|
3695
|
+
if not types:
|
|
3696
|
+
error(pos, "Need at least one type")
|
|
3697
|
+
|
|
3698
|
+
return Nodes.FusedTypeNode(pos, name=name, types=types)
|
|
3699
|
+
|
|
3700
|
+
|
|
3701
|
+
@cython.cfunc
|
|
3702
|
+
def p_struct_enum(s: PyrexScanner, pos, ctx):
|
|
3703
|
+
if s.systring == 'enum':
|
|
3704
|
+
return p_c_enum_definition(s, pos, ctx)
|
|
3705
|
+
else:
|
|
3706
|
+
return p_c_struct_or_union_definition(s, pos, ctx)
|
|
3707
|
+
|
|
3708
|
+
|
|
3709
|
+
@cython.cfunc
|
|
3710
|
+
def p_visibility(s: PyrexScanner, prev_visibility):
|
|
3711
|
+
visibility = prev_visibility
|
|
3712
|
+
if s.sy == 'IDENT' and s.systring in ('extern', 'public', 'readonly'):
|
|
3713
|
+
visibility = s.systring
|
|
3714
|
+
if prev_visibility != 'private' and visibility != prev_visibility:
|
|
3715
|
+
s.error("Conflicting visibility options '%s' and '%s'"
|
|
3716
|
+
% (prev_visibility, visibility), fatal=False)
|
|
3717
|
+
s.next()
|
|
3718
|
+
return visibility
|
|
3719
|
+
|
|
3720
|
+
|
|
3721
|
+
@cython.cfunc
|
|
3722
|
+
def p_c_modifiers(s: PyrexScanner) -> list:
|
|
3723
|
+
if s.sy == 'IDENT' and s.systring in ('inline',):
|
|
3724
|
+
modifier = s.systring
|
|
3725
|
+
s.next()
|
|
3726
|
+
return [modifier] + p_c_modifiers(s)
|
|
3727
|
+
return []
|
|
3728
|
+
|
|
3729
|
+
|
|
3730
|
+
@cython.cfunc
|
|
3731
|
+
def p_c_func_or_var_declaration(s: PyrexScanner, pos, ctx):
|
|
3732
|
+
cmethod_flag: cython.bint = ctx.level in ('c_class', 'c_class_pxd')
|
|
3733
|
+
modifiers = p_c_modifiers(s)
|
|
3734
|
+
base_type = p_c_base_type(s, nonempty=True, templates = ctx.templates)
|
|
3735
|
+
declarator = p_c_declarator(s, ctx(modifiers=modifiers), cmethod_flag = cmethod_flag,
|
|
3736
|
+
assignable=True, nonempty =True)
|
|
3737
|
+
declarator.overridable = ctx.overridable
|
|
3738
|
+
|
|
3739
|
+
if s.sy == 'IDENT' and s.systring == 'const' and ctx.level == 'cpp_class':
|
|
3740
|
+
s.next()
|
|
3741
|
+
is_const_method = True
|
|
3742
|
+
else:
|
|
3743
|
+
is_const_method = False
|
|
3744
|
+
|
|
3745
|
+
if s.sy == '->':
|
|
3746
|
+
# Special enough to give a better error message and keep going.
|
|
3747
|
+
s.error(
|
|
3748
|
+
"Return type annotation is not allowed in cdef/cpdef signatures. "
|
|
3749
|
+
"Please define it before the function name, as in C signatures.",
|
|
3750
|
+
fatal=False)
|
|
3751
|
+
s.next()
|
|
3752
|
+
p_test(s) # Keep going, but ignore result.
|
|
3753
|
+
|
|
3754
|
+
if s.sy == ':':
|
|
3755
|
+
if ctx.level not in ('module', 'c_class', 'module_pxd', 'c_class_pxd', 'cpp_class') and not ctx.templates:
|
|
3756
|
+
s.error("C function definition not allowed here")
|
|
3757
|
+
doc, suite = p_suite_with_docstring(s, Ctx(level='function'))
|
|
3758
|
+
result = Nodes.CFuncDefNode(pos,
|
|
3759
|
+
visibility = ctx.visibility,
|
|
3760
|
+
base_type = base_type,
|
|
3761
|
+
declarator = declarator,
|
|
3762
|
+
body = suite,
|
|
3763
|
+
doc = doc,
|
|
3764
|
+
modifiers = modifiers,
|
|
3765
|
+
api = ctx.api,
|
|
3766
|
+
overridable = ctx.overridable,
|
|
3767
|
+
is_const_method = is_const_method)
|
|
3768
|
+
else:
|
|
3769
|
+
#if api:
|
|
3770
|
+
# s.error("'api' not allowed with variable declaration")
|
|
3771
|
+
if is_const_method:
|
|
3772
|
+
declarator.is_const_method = is_const_method
|
|
3773
|
+
declarators = [declarator]
|
|
3774
|
+
while s.sy == ',':
|
|
3775
|
+
s.next()
|
|
3776
|
+
if s.sy == 'NEWLINE':
|
|
3777
|
+
break
|
|
3778
|
+
declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag,
|
|
3779
|
+
assignable=True, nonempty=True)
|
|
3780
|
+
declarators.append(declarator)
|
|
3781
|
+
doc_line = s.start_line + 1
|
|
3782
|
+
s.expect_newline("Syntax error in C variable declaration", ignore_semicolon=True)
|
|
3783
|
+
if ctx.level in ('c_class', 'c_class_pxd') and s.start_line == doc_line:
|
|
3784
|
+
doc = p_doc_string(s)
|
|
3785
|
+
else:
|
|
3786
|
+
doc = None
|
|
3787
|
+
result = Nodes.CVarDefNode(pos,
|
|
3788
|
+
visibility = ctx.visibility,
|
|
3789
|
+
base_type = base_type,
|
|
3790
|
+
declarators = declarators,
|
|
3791
|
+
in_pxd = ctx.level in ('module_pxd', 'c_class_pxd'),
|
|
3792
|
+
doc = doc,
|
|
3793
|
+
api = ctx.api,
|
|
3794
|
+
modifiers = modifiers,
|
|
3795
|
+
overridable = ctx.overridable)
|
|
3796
|
+
return result
|
|
3797
|
+
|
|
3798
|
+
|
|
3799
|
+
@cython.cfunc
|
|
3800
|
+
def p_ctypedef_statement(s: PyrexScanner, ctx):
|
|
3801
|
+
# s.sy == 'ctypedef'
|
|
3802
|
+
pos = s.position()
|
|
3803
|
+
s.next()
|
|
3804
|
+
visibility = p_visibility(s, ctx.visibility)
|
|
3805
|
+
api = p_api(s)
|
|
3806
|
+
ctx = ctx(typedef_flag=True, visibility = visibility)
|
|
3807
|
+
if api:
|
|
3808
|
+
ctx.api = True
|
|
3809
|
+
if s.sy == 'class':
|
|
3810
|
+
return p_c_class_definition(s, pos, ctx)
|
|
3811
|
+
elif s.sy == 'IDENT' and s.systring in struct_enum_union:
|
|
3812
|
+
return p_struct_enum(s, pos, ctx)
|
|
3813
|
+
elif s.sy == 'IDENT' and s.systring == 'fused':
|
|
3814
|
+
return p_fused_definition(s, pos, ctx)
|
|
3815
|
+
else:
|
|
3816
|
+
base_type = p_c_base_type(s, nonempty=True)
|
|
3817
|
+
declarator = p_c_declarator(s, ctx, is_type=True, nonempty=True)
|
|
3818
|
+
s.expect_newline("Syntax error in ctypedef statement", ignore_semicolon=True)
|
|
3819
|
+
return Nodes.CTypeDefNode(
|
|
3820
|
+
pos, base_type = base_type,
|
|
3821
|
+
declarator = declarator,
|
|
3822
|
+
visibility = visibility, api = api,
|
|
3823
|
+
in_pxd = ctx.level == 'module_pxd')
|
|
3824
|
+
|
|
3825
|
+
|
|
3826
|
+
@cython.cfunc
|
|
3827
|
+
def p_decorators(s: PyrexScanner) -> list:
|
|
3828
|
+
decorators = []
|
|
3829
|
+
while s.sy == '@':
|
|
3830
|
+
pos = s.position()
|
|
3831
|
+
s.next()
|
|
3832
|
+
decorator = p_namedexpr_test(s)
|
|
3833
|
+
decorators.append(Nodes.DecoratorNode(pos, decorator=decorator))
|
|
3834
|
+
s.expect_newline("Expected a newline after decorator")
|
|
3835
|
+
return decorators
|
|
3836
|
+
|
|
3837
|
+
|
|
3838
|
+
@cython.cfunc
|
|
3839
|
+
def _reject_cdef_modifier_in_py(s: PyrexScanner, name):
|
|
3840
|
+
"""Step over incorrectly placed cdef modifiers (@see _CDEF_MODIFIERS) to provide a good error message for them.
|
|
3841
|
+
"""
|
|
3842
|
+
if s.sy == 'IDENT' and name in _CDEF_MODIFIERS:
|
|
3843
|
+
# Special enough to provide a good error message.
|
|
3844
|
+
s.error("Cannot use cdef modifier '%s' in Python function signature. Use a decorator instead." % name, fatal=False)
|
|
3845
|
+
return p_ident(s) # Keep going, in case there are other errors.
|
|
3846
|
+
return name
|
|
3847
|
+
|
|
3848
|
+
|
|
3849
|
+
@cython.cfunc
|
|
3850
|
+
def p_def_statement(s: PyrexScanner, decorators: list = None, is_async_def: cython.bint = False):
|
|
3851
|
+
# s.sy == 'def'
|
|
3852
|
+
pos = decorators[0].pos if decorators else s.position()
|
|
3853
|
+
# PEP 492 switches the async/await keywords on in "async def" functions
|
|
3854
|
+
if is_async_def:
|
|
3855
|
+
s.enter_async()
|
|
3856
|
+
s.next()
|
|
3857
|
+
name = _reject_cdef_modifier_in_py(s, p_ident(s))
|
|
3858
|
+
s.expect(
|
|
3859
|
+
'(',
|
|
3860
|
+
"Expected '(', found '%s'. Did you use cdef syntax in a Python declaration? "
|
|
3861
|
+
"Use decorators and Python type annotations instead." % (
|
|
3862
|
+
s.systring if s.sy == 'IDENT' else s.sy))
|
|
3863
|
+
args, star_arg, starstar_arg = p_varargslist(s, terminator=')')
|
|
3864
|
+
s.expect(')')
|
|
3865
|
+
_reject_cdef_modifier_in_py(s, s.systring)
|
|
3866
|
+
return_type_annotation = None
|
|
3867
|
+
if s.sy == '->':
|
|
3868
|
+
s.next()
|
|
3869
|
+
return_type_annotation = p_annotation(s)
|
|
3870
|
+
_reject_cdef_modifier_in_py(s, s.systring)
|
|
3871
|
+
|
|
3872
|
+
doc, body = p_suite_with_docstring(s, Ctx(level='function'))
|
|
3873
|
+
if is_async_def:
|
|
3874
|
+
s.exit_async()
|
|
3875
|
+
|
|
3876
|
+
return Nodes.DefNode(
|
|
3877
|
+
pos, name=name, args=args, star_arg=star_arg, starstar_arg=starstar_arg,
|
|
3878
|
+
doc=doc, body=body, decorators=decorators, is_async_def=is_async_def,
|
|
3879
|
+
return_type_annotation=return_type_annotation)
|
|
3880
|
+
|
|
3881
|
+
|
|
3882
|
+
@cython.cfunc
|
|
3883
|
+
def p_varargslist(s: PyrexScanner, terminator: cython.Py_UCS4 = ')', annotated: cython.bint = True) -> tuple:
|
|
3884
|
+
args = p_c_arg_list(s, in_pyfunc=True, nonempty_declarators=True,
|
|
3885
|
+
annotated = annotated)
|
|
3886
|
+
star_arg = None
|
|
3887
|
+
starstar_arg = None
|
|
3888
|
+
if s.sy == '/':
|
|
3889
|
+
if len(args) == 0:
|
|
3890
|
+
s.error("Got zero positional-only arguments despite presence of "
|
|
3891
|
+
"positional-only specifier '/'")
|
|
3892
|
+
s.next()
|
|
3893
|
+
# Mark all args to the left as pos only
|
|
3894
|
+
for arg in args:
|
|
3895
|
+
arg.pos_only = 1
|
|
3896
|
+
if s.sy == ',':
|
|
3897
|
+
s.next()
|
|
3898
|
+
args.extend(p_c_arg_list(
|
|
3899
|
+
s, in_pyfunc=True, nonempty_declarators=True, annotated = annotated))
|
|
3900
|
+
elif s.sy != terminator:
|
|
3901
|
+
s.error("Syntax error in Python function argument list")
|
|
3902
|
+
if s.sy == '*':
|
|
3903
|
+
s.next()
|
|
3904
|
+
if s.sy == 'IDENT':
|
|
3905
|
+
star_arg = p_py_arg_decl(s, annotated=annotated)
|
|
3906
|
+
if s.sy == ',':
|
|
3907
|
+
s.next()
|
|
3908
|
+
args.extend(p_c_arg_list(
|
|
3909
|
+
s, in_pyfunc =True, nonempty_declarators=True, kw_only=True, annotated = annotated))
|
|
3910
|
+
elif s.sy != terminator:
|
|
3911
|
+
s.error("Syntax error in Python function argument list")
|
|
3912
|
+
if s.sy == '**':
|
|
3913
|
+
s.next()
|
|
3914
|
+
starstar_arg = p_py_arg_decl(s, annotated=annotated)
|
|
3915
|
+
if s.sy == ',':
|
|
3916
|
+
s.next()
|
|
3917
|
+
return (args, star_arg, starstar_arg)
|
|
3918
|
+
|
|
3919
|
+
|
|
3920
|
+
@cython.cfunc
|
|
3921
|
+
def p_py_arg_decl(s: PyrexScanner, annotated: cython.bint = True):
|
|
3922
|
+
pos = s.position()
|
|
3923
|
+
name = p_ident(s)
|
|
3924
|
+
annotation = None
|
|
3925
|
+
if annotated and s.sy == ':':
|
|
3926
|
+
s.next()
|
|
3927
|
+
annotation = p_annotation(s)
|
|
3928
|
+
return Nodes.PyArgDeclNode(pos, name = name, annotation = annotation)
|
|
3929
|
+
|
|
3930
|
+
|
|
3931
|
+
@cython.cfunc
|
|
3932
|
+
def p_class_statement(s: PyrexScanner, decorators):
|
|
3933
|
+
# s.sy == 'class'
|
|
3934
|
+
pos = s.position()
|
|
3935
|
+
s.next()
|
|
3936
|
+
class_name = EncodedString(p_ident(s))
|
|
3937
|
+
class_name.encoding = s.source_encoding # FIXME: why is this needed?
|
|
3938
|
+
arg_tuple = None
|
|
3939
|
+
keyword_dict = None
|
|
3940
|
+
if s.sy == '(':
|
|
3941
|
+
positional_args, keyword_args = p_call_parse_args(s, allow_genexp=False)
|
|
3942
|
+
arg_tuple, keyword_dict = p_call_build_packed_args(pos, positional_args, keyword_args)
|
|
3943
|
+
if arg_tuple is None:
|
|
3944
|
+
# XXX: empty arg_tuple
|
|
3945
|
+
arg_tuple = ExprNodes.TupleNode(pos, args=[])
|
|
3946
|
+
doc, body = p_suite_with_docstring(s, Ctx(level='class'))
|
|
3947
|
+
return Nodes.PyClassDefNode(
|
|
3948
|
+
pos, name=class_name,
|
|
3949
|
+
bases=arg_tuple,
|
|
3950
|
+
keyword_args=keyword_dict,
|
|
3951
|
+
doc=doc, body=body, decorators=decorators,
|
|
3952
|
+
force_py3_semantics=s.context.language_level >= 3)
|
|
3953
|
+
|
|
3954
|
+
|
|
3955
|
+
@cython.cfunc
|
|
3956
|
+
def p_c_class_definition(s: PyrexScanner, pos, ctx):
|
|
3957
|
+
# s.sy == 'class'
|
|
3958
|
+
s.next()
|
|
3959
|
+
module_path = []
|
|
3960
|
+
class_name = p_ident(s)
|
|
3961
|
+
while s.sy == '.':
|
|
3962
|
+
s.next()
|
|
3963
|
+
module_path.append(class_name)
|
|
3964
|
+
class_name = p_ident(s)
|
|
3965
|
+
if module_path and ctx.visibility != 'extern':
|
|
3966
|
+
error(pos, "Qualified class name only allowed for 'extern' C class")
|
|
3967
|
+
if module_path and s.sy == 'IDENT' and s.systring == 'as':
|
|
3968
|
+
s.next()
|
|
3969
|
+
as_name = p_ident(s)
|
|
3970
|
+
else:
|
|
3971
|
+
as_name = class_name
|
|
3972
|
+
objstruct_name = None
|
|
3973
|
+
typeobj_name = None
|
|
3974
|
+
bases = None
|
|
3975
|
+
check_size = None
|
|
3976
|
+
if s.sy == '(':
|
|
3977
|
+
positional_args, keyword_args = p_call_parse_args(s, allow_genexp=False)
|
|
3978
|
+
if keyword_args:
|
|
3979
|
+
s.error("C classes cannot take keyword bases.")
|
|
3980
|
+
bases, _ = p_call_build_packed_args(pos, positional_args, keyword_args)
|
|
3981
|
+
if bases is None:
|
|
3982
|
+
bases = ExprNodes.TupleNode(pos, args=[])
|
|
3983
|
+
|
|
3984
|
+
if s.sy == '[':
|
|
3985
|
+
if ctx.visibility not in ('public', 'extern') and not ctx.api:
|
|
3986
|
+
error(s.position(), "Name options only allowed for 'public', 'api', or 'extern' C class")
|
|
3987
|
+
objstruct_name, typeobj_name, check_size = p_c_class_options(s)
|
|
3988
|
+
if s.sy == ':':
|
|
3989
|
+
if ctx.level == 'module_pxd':
|
|
3990
|
+
body_level = 'c_class_pxd'
|
|
3991
|
+
else:
|
|
3992
|
+
body_level = 'c_class'
|
|
3993
|
+
doc, body = p_suite_with_docstring(s, Ctx(level=body_level))
|
|
3994
|
+
else:
|
|
3995
|
+
s.expect_newline("Syntax error in C class definition")
|
|
3996
|
+
doc = None
|
|
3997
|
+
body = None
|
|
3998
|
+
if ctx.visibility == 'extern':
|
|
3999
|
+
if not module_path:
|
|
4000
|
+
error(pos, "Module name required for 'extern' C class")
|
|
4001
|
+
if typeobj_name:
|
|
4002
|
+
error(pos, "Type object name specification not allowed for 'extern' C class")
|
|
4003
|
+
elif ctx.visibility == 'public':
|
|
4004
|
+
if not objstruct_name:
|
|
4005
|
+
error(pos, "Object struct name specification required for 'public' C class")
|
|
4006
|
+
if not typeobj_name:
|
|
4007
|
+
error(pos, "Type object name specification required for 'public' C class")
|
|
4008
|
+
elif ctx.visibility == 'private':
|
|
4009
|
+
if ctx.api:
|
|
4010
|
+
if not objstruct_name:
|
|
4011
|
+
error(pos, "Object struct name specification required for 'api' C class")
|
|
4012
|
+
if not typeobj_name:
|
|
4013
|
+
error(pos, "Type object name specification required for 'api' C class")
|
|
4014
|
+
else:
|
|
4015
|
+
error(pos, "Invalid class visibility '%s'" % ctx.visibility)
|
|
4016
|
+
return Nodes.CClassDefNode(pos,
|
|
4017
|
+
visibility = ctx.visibility,
|
|
4018
|
+
typedef_flag = ctx.typedef_flag,
|
|
4019
|
+
api = ctx.api,
|
|
4020
|
+
module_name = ".".join(module_path),
|
|
4021
|
+
class_name = class_name,
|
|
4022
|
+
as_name = as_name,
|
|
4023
|
+
bases = bases,
|
|
4024
|
+
objstruct_name = objstruct_name,
|
|
4025
|
+
typeobj_name = typeobj_name,
|
|
4026
|
+
check_size = check_size,
|
|
4027
|
+
in_pxd = ctx.level == 'module_pxd',
|
|
4028
|
+
doc = doc,
|
|
4029
|
+
body = body)
|
|
4030
|
+
|
|
4031
|
+
|
|
4032
|
+
@cython.cfunc
|
|
4033
|
+
def p_c_class_options(s: PyrexScanner) -> tuple:
|
|
4034
|
+
objstruct_name = None
|
|
4035
|
+
typeobj_name = None
|
|
4036
|
+
check_size = None
|
|
4037
|
+
s.expect('[')
|
|
4038
|
+
while 1:
|
|
4039
|
+
if s.sy != 'IDENT':
|
|
4040
|
+
break
|
|
4041
|
+
if s.systring == 'object':
|
|
4042
|
+
s.next()
|
|
4043
|
+
objstruct_name = p_ident(s)
|
|
4044
|
+
elif s.systring == 'type':
|
|
4045
|
+
s.next()
|
|
4046
|
+
typeobj_name = p_ident(s)
|
|
4047
|
+
elif s.systring == 'check_size':
|
|
4048
|
+
s.next()
|
|
4049
|
+
check_size = p_ident(s)
|
|
4050
|
+
if check_size not in ('ignore', 'warn', 'error'):
|
|
4051
|
+
s.error("Expected one of ignore, warn or error, found %r" % check_size)
|
|
4052
|
+
if s.sy != ',':
|
|
4053
|
+
break
|
|
4054
|
+
s.next()
|
|
4055
|
+
s.expect(']', "Expected 'object', 'type' or 'check_size'")
|
|
4056
|
+
return objstruct_name, typeobj_name, check_size
|
|
4057
|
+
|
|
4058
|
+
|
|
4059
|
+
@cython.cfunc
|
|
4060
|
+
def p_property_decl(s: PyrexScanner):
|
|
4061
|
+
pos = s.position()
|
|
4062
|
+
s.next() # 'property'
|
|
4063
|
+
name = p_ident(s)
|
|
4064
|
+
doc, body = p_suite_with_docstring(
|
|
4065
|
+
s, Ctx(level='property'), with_doc_only=True)
|
|
4066
|
+
return Nodes.PropertyNode(pos, name=name, doc=doc, body=body)
|
|
4067
|
+
|
|
4068
|
+
|
|
4069
|
+
@cython.cfunc
|
|
4070
|
+
def p_ignorable_statement(s: PyrexScanner):
|
|
4071
|
+
"""
|
|
4072
|
+
Parses any kind of ignorable statement that is allowed in .pxd files.
|
|
4073
|
+
"""
|
|
4074
|
+
if s.sy == 'BEGIN_STRING':
|
|
4075
|
+
pos = s.position()
|
|
4076
|
+
string_node = p_atom(s)
|
|
4077
|
+
s.expect_newline("Syntax error in string", ignore_semicolon=True)
|
|
4078
|
+
return Nodes.ExprStatNode(pos, expr=string_node)
|
|
4079
|
+
return None
|
|
4080
|
+
|
|
4081
|
+
|
|
4082
|
+
@cython.cfunc
|
|
4083
|
+
def p_doc_string(s: PyrexScanner):
|
|
4084
|
+
if s.sy == 'BEGIN_STRING':
|
|
4085
|
+
pos = s.position()
|
|
4086
|
+
kind, bytes_result, unicode_result = p_cat_string_literal(s)
|
|
4087
|
+
s.expect_newline("Syntax error in doc string", ignore_semicolon=True)
|
|
4088
|
+
if kind in ('u', ''):
|
|
4089
|
+
return unicode_result
|
|
4090
|
+
warning(pos, "Python 3 requires docstrings to be unicode strings")
|
|
4091
|
+
return bytes_result
|
|
4092
|
+
else:
|
|
4093
|
+
return None
|
|
4094
|
+
|
|
4095
|
+
|
|
4096
|
+
@cython.cfunc
|
|
4097
|
+
def _extract_docstring(node) -> tuple:
|
|
4098
|
+
"""
|
|
4099
|
+
Extract a docstring from a statement or from the first statement
|
|
4100
|
+
in a list. Remove the statement if found. Return a tuple
|
|
4101
|
+
(plain-docstring or None, node).
|
|
4102
|
+
"""
|
|
4103
|
+
doc_node = None
|
|
4104
|
+
if node is None:
|
|
4105
|
+
pass
|
|
4106
|
+
elif isinstance(node, Nodes.ExprStatNode):
|
|
4107
|
+
if node.expr.is_string_literal:
|
|
4108
|
+
doc_node = node.expr
|
|
4109
|
+
node = Nodes.StatListNode(node.pos, stats=[])
|
|
4110
|
+
elif isinstance(node, Nodes.StatListNode) and node.stats:
|
|
4111
|
+
stats = node.stats
|
|
4112
|
+
if isinstance(stats[0], Nodes.ExprStatNode):
|
|
4113
|
+
if stats[0].expr.is_string_literal:
|
|
4114
|
+
doc_node = stats[0].expr
|
|
4115
|
+
del stats[0]
|
|
4116
|
+
|
|
4117
|
+
if doc_node is None:
|
|
4118
|
+
doc = None
|
|
4119
|
+
elif isinstance(doc_node, ExprNodes.BytesNode):
|
|
4120
|
+
warning(node.pos,
|
|
4121
|
+
"Python 3 requires docstrings to be unicode strings")
|
|
4122
|
+
doc = doc_node.value
|
|
4123
|
+
else:
|
|
4124
|
+
doc = doc_node.value
|
|
4125
|
+
return doc, node
|
|
4126
|
+
|
|
4127
|
+
|
|
4128
|
+
@cython.ccall
|
|
4129
|
+
def p_code(s: PyrexScanner, level=None, ctx=Ctx):
|
|
4130
|
+
body = p_statement_list(s, ctx(level = level), first_statement=True)
|
|
4131
|
+
if s.sy != 'EOF':
|
|
4132
|
+
s.error("Syntax error in statement [%s,%s]" % (
|
|
4133
|
+
repr(s.sy), repr(s.systring)))
|
|
4134
|
+
return body
|
|
4135
|
+
|
|
4136
|
+
|
|
4137
|
+
_match_compiler_directive_comment = cython.declare(object, re.compile(
|
|
4138
|
+
r"^#\s*cython\s*:\s*((\w|[.])+\s*=.*)$").match)
|
|
4139
|
+
|
|
4140
|
+
|
|
4141
|
+
@cython.cfunc
|
|
4142
|
+
def p_compiler_directive_comments(s: PyrexScanner) -> dict:
|
|
4143
|
+
result = {}
|
|
4144
|
+
while s.sy == 'commentline':
|
|
4145
|
+
pos = s.position()
|
|
4146
|
+
m = _match_compiler_directive_comment(s.systring)
|
|
4147
|
+
if m:
|
|
4148
|
+
directives_string = m.group(1).strip()
|
|
4149
|
+
try:
|
|
4150
|
+
new_directives = Options.parse_directive_list(directives_string, ignore_unknown=True)
|
|
4151
|
+
except ValueError as e:
|
|
4152
|
+
s.error(e.args[0], fatal=False)
|
|
4153
|
+
s.next()
|
|
4154
|
+
continue
|
|
4155
|
+
|
|
4156
|
+
for name in new_directives:
|
|
4157
|
+
if name not in result:
|
|
4158
|
+
pass
|
|
4159
|
+
elif Options.directive_types.get(name) is list:
|
|
4160
|
+
result[name] += new_directives[name]
|
|
4161
|
+
new_directives[name] = result[name]
|
|
4162
|
+
elif new_directives[name] == result[name]:
|
|
4163
|
+
warning(pos, "Duplicate directive found: %s" % (name,))
|
|
4164
|
+
else:
|
|
4165
|
+
s.error("Conflicting settings found for top-level directive %s: %r and %r" % (
|
|
4166
|
+
name, result[name], new_directives[name]), pos=pos)
|
|
4167
|
+
|
|
4168
|
+
if 'language_level' in new_directives:
|
|
4169
|
+
# Make sure we apply the language level already to the first token that follows the comments.
|
|
4170
|
+
s.context.set_language_level(new_directives['language_level'])
|
|
4171
|
+
if 'legacy_implicit_noexcept' in new_directives:
|
|
4172
|
+
s.context.legacy_implicit_noexcept = new_directives['legacy_implicit_noexcept']
|
|
4173
|
+
|
|
4174
|
+
|
|
4175
|
+
result.update(new_directives)
|
|
4176
|
+
|
|
4177
|
+
s.next()
|
|
4178
|
+
return result
|
|
4179
|
+
|
|
4180
|
+
|
|
4181
|
+
@cython.ccall
|
|
4182
|
+
def p_module(s: PyrexScanner, pxd, full_module_name, ctx=Ctx):
|
|
4183
|
+
pos = s.position()
|
|
4184
|
+
|
|
4185
|
+
directive_comments = p_compiler_directive_comments(s)
|
|
4186
|
+
s.parse_comments = False
|
|
4187
|
+
|
|
4188
|
+
if s.context.language_level is None:
|
|
4189
|
+
s.context.set_language_level('3')
|
|
4190
|
+
|
|
4191
|
+
level = 'module_pxd' if pxd else 'module'
|
|
4192
|
+
doc = p_doc_string(s)
|
|
4193
|
+
body = p_statement_list(s, ctx(level=level), first_statement=True)
|
|
4194
|
+
if s.sy != 'EOF':
|
|
4195
|
+
s.error("Syntax error in statement [%s,%s]" % (
|
|
4196
|
+
repr(s.sy), repr(s.systring)))
|
|
4197
|
+
return ModuleNode(pos, doc = doc, body = body,
|
|
4198
|
+
full_module_name = full_module_name,
|
|
4199
|
+
directive_comments = directive_comments)
|
|
4200
|
+
|
|
4201
|
+
|
|
4202
|
+
@cython.cfunc
|
|
4203
|
+
def p_template_definition(s: PyrexScanner) -> tuple:
|
|
4204
|
+
name = p_ident(s)
|
|
4205
|
+
if s.sy == '=':
|
|
4206
|
+
s.expect('=')
|
|
4207
|
+
s.expect('*')
|
|
4208
|
+
required = False
|
|
4209
|
+
else:
|
|
4210
|
+
required = True
|
|
4211
|
+
return name, required
|
|
4212
|
+
|
|
4213
|
+
|
|
4214
|
+
@cython.cfunc
|
|
4215
|
+
def p_cpp_class_definition(s: PyrexScanner, pos, ctx):
|
|
4216
|
+
# s.sy == 'cppclass'
|
|
4217
|
+
s.next()
|
|
4218
|
+
class_name = p_ident(s)
|
|
4219
|
+
cname = p_opt_cname(s)
|
|
4220
|
+
if cname is None and ctx.namespace is not None:
|
|
4221
|
+
cname = ctx.namespace + "::" + class_name
|
|
4222
|
+
if s.sy == '.':
|
|
4223
|
+
error(pos, "Qualified class name not allowed C++ class")
|
|
4224
|
+
if s.sy == '[':
|
|
4225
|
+
s.next()
|
|
4226
|
+
templates = [p_template_definition(s)]
|
|
4227
|
+
while s.sy == ',':
|
|
4228
|
+
s.next()
|
|
4229
|
+
templates.append(p_template_definition(s))
|
|
4230
|
+
s.expect(']')
|
|
4231
|
+
template_names = [name for name, required in templates]
|
|
4232
|
+
else:
|
|
4233
|
+
templates = None
|
|
4234
|
+
template_names = None
|
|
4235
|
+
if s.sy == '(':
|
|
4236
|
+
s.next()
|
|
4237
|
+
base_classes = [p_c_base_type(s, templates = template_names)]
|
|
4238
|
+
while s.sy == ',':
|
|
4239
|
+
s.next()
|
|
4240
|
+
base_classes.append(p_c_base_type(s, templates = template_names))
|
|
4241
|
+
s.expect(')')
|
|
4242
|
+
else:
|
|
4243
|
+
base_classes = []
|
|
4244
|
+
if s.sy == '[':
|
|
4245
|
+
error(s.position(), "Name options not allowed for C++ class")
|
|
4246
|
+
nogil = p_nogil(s)
|
|
4247
|
+
if s.sy == ':':
|
|
4248
|
+
s.next()
|
|
4249
|
+
s.expect('NEWLINE')
|
|
4250
|
+
s.expect_indent()
|
|
4251
|
+
# Allow a cppclass to have docstrings. It will be discarded as comment.
|
|
4252
|
+
# The goal of this is consistency: we can make docstrings inside cppclass methods,
|
|
4253
|
+
# so why not on the cppclass itself ?
|
|
4254
|
+
p_doc_string(s)
|
|
4255
|
+
attributes = []
|
|
4256
|
+
body_ctx = Ctx(visibility = ctx.visibility, level='cpp_class', nogil=nogil or ctx.nogil)
|
|
4257
|
+
body_ctx.templates = template_names
|
|
4258
|
+
while s.sy != 'DEDENT':
|
|
4259
|
+
if s.sy != 'pass':
|
|
4260
|
+
attributes.append(p_cpp_class_attribute(s, body_ctx))
|
|
4261
|
+
else:
|
|
4262
|
+
s.next()
|
|
4263
|
+
s.expect_newline("Expected a newline")
|
|
4264
|
+
s.expect_dedent()
|
|
4265
|
+
else:
|
|
4266
|
+
attributes = None
|
|
4267
|
+
s.expect_newline("Syntax error in C++ class definition")
|
|
4268
|
+
return Nodes.CppClassNode(pos,
|
|
4269
|
+
name = class_name,
|
|
4270
|
+
cname = cname,
|
|
4271
|
+
base_classes = base_classes,
|
|
4272
|
+
visibility = ctx.visibility,
|
|
4273
|
+
in_pxd = ctx.level == 'module_pxd',
|
|
4274
|
+
attributes = attributes,
|
|
4275
|
+
templates = templates)
|
|
4276
|
+
|
|
4277
|
+
|
|
4278
|
+
@cython.cfunc
|
|
4279
|
+
def p_cpp_class_attribute(s: PyrexScanner, ctx):
|
|
4280
|
+
pos = s.position()
|
|
4281
|
+
decorators = None
|
|
4282
|
+
if s.sy == '@':
|
|
4283
|
+
decorators = p_decorators(s)
|
|
4284
|
+
if s.systring == 'cppclass':
|
|
4285
|
+
return p_cpp_class_definition(s, pos, ctx)
|
|
4286
|
+
elif s.systring == 'ctypedef':
|
|
4287
|
+
return p_ctypedef_statement(s, ctx)
|
|
4288
|
+
elif s.sy == 'IDENT' and s.systring in struct_enum_union:
|
|
4289
|
+
if s.systring != 'enum':
|
|
4290
|
+
return p_cpp_class_definition(s, pos, ctx)
|
|
4291
|
+
else:
|
|
4292
|
+
return p_struct_enum(s, pos, ctx)
|
|
4293
|
+
else:
|
|
4294
|
+
node = p_c_func_or_var_declaration(s, pos, ctx)
|
|
4295
|
+
if decorators is not None:
|
|
4296
|
+
tup = Nodes.CFuncDefNode, Nodes.CVarDefNode, Nodes.CClassDefNode
|
|
4297
|
+
if ctx.allow_struct_enum_decorator:
|
|
4298
|
+
tup += Nodes.CStructOrUnionDefNode, Nodes.CEnumDefNode
|
|
4299
|
+
if not isinstance(node, tup):
|
|
4300
|
+
s.error("Decorators can only be followed by functions or classes")
|
|
4301
|
+
node.decorators = decorators
|
|
4302
|
+
return node
|
|
4303
|
+
|
|
4304
|
+
|
|
4305
|
+
@cython.cfunc
|
|
4306
|
+
def p_match_statement(s: PyrexScanner, ctx):
|
|
4307
|
+
assert s.sy == "IDENT" and s.systring == "match"
|
|
4308
|
+
pos = s.position()
|
|
4309
|
+
with tentatively_scan(s) as errors:
|
|
4310
|
+
s.next()
|
|
4311
|
+
subject = p_namedexpr_test(s)
|
|
4312
|
+
subjects = None
|
|
4313
|
+
if s.sy == ",":
|
|
4314
|
+
subjects = [subject]
|
|
4315
|
+
while s.sy == ",":
|
|
4316
|
+
s.next()
|
|
4317
|
+
if s.sy == ":":
|
|
4318
|
+
break
|
|
4319
|
+
subjects.append(p_test(s))
|
|
4320
|
+
if subjects is not None:
|
|
4321
|
+
subject = ExprNodes.TupleNode(pos, args=subjects)
|
|
4322
|
+
s.expect(":")
|
|
4323
|
+
if errors:
|
|
4324
|
+
return None
|
|
4325
|
+
|
|
4326
|
+
# at this stage we are committed to it being a match block so continue
|
|
4327
|
+
# outside "with tentatively_scan"
|
|
4328
|
+
# (I think this deviates from the PEG parser slightly, and it'd
|
|
4329
|
+
# backtrack on the whole thing)
|
|
4330
|
+
s.expect_newline()
|
|
4331
|
+
s.expect_indent()
|
|
4332
|
+
cases = []
|
|
4333
|
+
while s.sy != "DEDENT":
|
|
4334
|
+
cases.append(p_case_block(s, ctx))
|
|
4335
|
+
s.expect_dedent()
|
|
4336
|
+
return MatchCaseNodes.MatchNode(pos, subject=subject, cases=cases)
|
|
4337
|
+
|
|
4338
|
+
|
|
4339
|
+
@cython.cfunc
|
|
4340
|
+
def p_case_block(s: PyrexScanner, ctx):
|
|
4341
|
+
if not (s.sy == "IDENT" and s.systring == "case"):
|
|
4342
|
+
s.expected("case")
|
|
4343
|
+
s.next()
|
|
4344
|
+
pos = s.position()
|
|
4345
|
+
pattern = p_patterns(s)
|
|
4346
|
+
guard = None
|
|
4347
|
+
if s.sy == 'if':
|
|
4348
|
+
s.next()
|
|
4349
|
+
guard = p_test(s)
|
|
4350
|
+
body = p_suite(s, ctx)
|
|
4351
|
+
|
|
4352
|
+
return MatchCaseNodes.MatchCaseNode(pos, pattern=pattern, body=body, guard=guard)
|
|
4353
|
+
|
|
4354
|
+
|
|
4355
|
+
@cython.cfunc
|
|
4356
|
+
def p_patterns(s: PyrexScanner):
|
|
4357
|
+
# note - in slight contrast to the name (which comes from the Python grammar),
|
|
4358
|
+
# returns a single pattern
|
|
4359
|
+
patterns = []
|
|
4360
|
+
seq = False
|
|
4361
|
+
pos = s.position()
|
|
4362
|
+
while True:
|
|
4363
|
+
with tentatively_scan(s) as errors:
|
|
4364
|
+
pattern = p_maybe_star_pattern(s)
|
|
4365
|
+
if errors:
|
|
4366
|
+
if patterns:
|
|
4367
|
+
break # all is good provided we have at least 1 pattern
|
|
4368
|
+
else:
|
|
4369
|
+
e = errors[0]
|
|
4370
|
+
s.error(e.args[1], pos=e.args[0])
|
|
4371
|
+
patterns.append(pattern)
|
|
4372
|
+
|
|
4373
|
+
if s.sy == ",":
|
|
4374
|
+
seq = True
|
|
4375
|
+
s.next()
|
|
4376
|
+
if s.sy in [":", "if"]:
|
|
4377
|
+
break # common reasons to break
|
|
4378
|
+
else:
|
|
4379
|
+
break
|
|
4380
|
+
|
|
4381
|
+
if seq:
|
|
4382
|
+
return MatchCaseNodes.MatchSequencePatternNode(pos, patterns=patterns)
|
|
4383
|
+
else:
|
|
4384
|
+
return patterns[0]
|
|
4385
|
+
|
|
4386
|
+
|
|
4387
|
+
@cython.cfunc
|
|
4388
|
+
def p_maybe_star_pattern(s: PyrexScanner):
|
|
4389
|
+
# For match case. Either star_pattern or pattern
|
|
4390
|
+
if s.sy == "*":
|
|
4391
|
+
# star pattern
|
|
4392
|
+
s.next()
|
|
4393
|
+
target = None
|
|
4394
|
+
if s.systring != "_": # for match-case '_' is treated as a special wildcard
|
|
4395
|
+
target = p_pattern_capture_target(s)
|
|
4396
|
+
else:
|
|
4397
|
+
s.next()
|
|
4398
|
+
pattern = MatchCaseNodes.MatchAndAssignPatternNode(
|
|
4399
|
+
s.position(), target=target, is_star=True
|
|
4400
|
+
)
|
|
4401
|
+
return pattern
|
|
4402
|
+
else:
|
|
4403
|
+
pattern = p_pattern(s)
|
|
4404
|
+
return pattern
|
|
4405
|
+
|
|
4406
|
+
|
|
4407
|
+
@cython.cfunc
|
|
4408
|
+
def p_pattern(s: PyrexScanner):
|
|
4409
|
+
# try "as_pattern" then "or_pattern"
|
|
4410
|
+
# (but practically "as_pattern" starts with "or_pattern" too)
|
|
4411
|
+
patterns = []
|
|
4412
|
+
pos = s.position()
|
|
4413
|
+
while True:
|
|
4414
|
+
patterns.append(p_closed_pattern(s))
|
|
4415
|
+
if s.sy != "|":
|
|
4416
|
+
break
|
|
4417
|
+
s.next()
|
|
4418
|
+
|
|
4419
|
+
if len(patterns) > 1:
|
|
4420
|
+
pattern = MatchCaseNodes.OrPatternNode(
|
|
4421
|
+
pos,
|
|
4422
|
+
alternatives=patterns
|
|
4423
|
+
)
|
|
4424
|
+
else:
|
|
4425
|
+
pattern = patterns[0]
|
|
4426
|
+
|
|
4427
|
+
if s.sy == 'IDENT' and s.systring == 'as':
|
|
4428
|
+
s.next()
|
|
4429
|
+
with tentatively_scan(s) as errors:
|
|
4430
|
+
pattern.as_targets.append(p_pattern_capture_target(s))
|
|
4431
|
+
if errors and s.sy == "_":
|
|
4432
|
+
s.next()
|
|
4433
|
+
# make this a specific error
|
|
4434
|
+
return Nodes.ErrorNode(errors[0].args[0], what=errors[0].args[1])
|
|
4435
|
+
elif errors:
|
|
4436
|
+
with tentatively_scan(s):
|
|
4437
|
+
expr = p_test(s)
|
|
4438
|
+
return Nodes.ErrorNode(expr.pos, what="Invalid pattern target")
|
|
4439
|
+
s.error(errors[0])
|
|
4440
|
+
return pattern
|
|
4441
|
+
|
|
4442
|
+
|
|
4443
|
+
@cython.cfunc
|
|
4444
|
+
def p_closed_pattern(s: PyrexScanner):
|
|
4445
|
+
"""
|
|
4446
|
+
The PEG parser specifies it as
|
|
4447
|
+
| literal_pattern
|
|
4448
|
+
| capture_pattern
|
|
4449
|
+
| wildcard_pattern
|
|
4450
|
+
| value_pattern
|
|
4451
|
+
| group_pattern
|
|
4452
|
+
| sequence_pattern
|
|
4453
|
+
| mapping_pattern
|
|
4454
|
+
| class_pattern
|
|
4455
|
+
|
|
4456
|
+
For the sake avoiding too much backtracking, we know:
|
|
4457
|
+
* starts with "{" is a sequence_pattern
|
|
4458
|
+
* starts with "[" is a mapping_pattern
|
|
4459
|
+
* starts with "(" is a group_pattern or sequence_pattern
|
|
4460
|
+
* wildcard pattern is just identifier=='_'
|
|
4461
|
+
The rest are then tried in order with backtracking
|
|
4462
|
+
"""
|
|
4463
|
+
if s.sy == 'IDENT' and s.systring == '_':
|
|
4464
|
+
pos = s.position()
|
|
4465
|
+
s.next()
|
|
4466
|
+
return MatchCaseNodes.MatchAndAssignPatternNode(pos)
|
|
4467
|
+
elif s.sy == '{':
|
|
4468
|
+
return p_mapping_pattern(s)
|
|
4469
|
+
elif s.sy == '[':
|
|
4470
|
+
return p_sequence_pattern(s)
|
|
4471
|
+
elif s.sy == '(':
|
|
4472
|
+
with tentatively_scan(s) as errors:
|
|
4473
|
+
result = p_group_pattern(s)
|
|
4474
|
+
if not errors:
|
|
4475
|
+
return result
|
|
4476
|
+
return p_sequence_pattern(s)
|
|
4477
|
+
|
|
4478
|
+
with tentatively_scan(s) as errors:
|
|
4479
|
+
result = p_literal_pattern(s)
|
|
4480
|
+
if not errors:
|
|
4481
|
+
return result
|
|
4482
|
+
with tentatively_scan(s) as errors:
|
|
4483
|
+
result = p_capture_pattern(s)
|
|
4484
|
+
if not errors:
|
|
4485
|
+
return result
|
|
4486
|
+
with tentatively_scan(s) as errors:
|
|
4487
|
+
result = p_value_pattern(s)
|
|
4488
|
+
if not errors:
|
|
4489
|
+
return result
|
|
4490
|
+
return p_class_pattern(s)
|
|
4491
|
+
|
|
4492
|
+
|
|
4493
|
+
@cython.cfunc
|
|
4494
|
+
def p_literal_pattern(s: PyrexScanner):
|
|
4495
|
+
# a lot of duplication in this function with "p_atom"
|
|
4496
|
+
next_must_be_a_number = False
|
|
4497
|
+
sign = ''
|
|
4498
|
+
if s.sy == '-':
|
|
4499
|
+
sign = s.sy
|
|
4500
|
+
sign_pos = s.position()
|
|
4501
|
+
s.next()
|
|
4502
|
+
next_must_be_a_number = True
|
|
4503
|
+
|
|
4504
|
+
sy = s.sy
|
|
4505
|
+
pos = s.position()
|
|
4506
|
+
|
|
4507
|
+
res = None
|
|
4508
|
+
if sy == 'INT':
|
|
4509
|
+
res = p_int_literal(s)
|
|
4510
|
+
elif sy == 'FLOAT':
|
|
4511
|
+
value = s.systring
|
|
4512
|
+
s.next()
|
|
4513
|
+
res = ExprNodes.FloatNode(pos, value=value)
|
|
4514
|
+
|
|
4515
|
+
if res is not None and sign == "-":
|
|
4516
|
+
res = ExprNodes.UnaryMinusNode(sign_pos, operand=res)
|
|
4517
|
+
|
|
4518
|
+
if res is not None and s.sy in ['+', '-']:
|
|
4519
|
+
sign = s.sy
|
|
4520
|
+
s.next()
|
|
4521
|
+
if s.sy != 'IMAG':
|
|
4522
|
+
s.error("Expected imaginary number")
|
|
4523
|
+
else:
|
|
4524
|
+
add_pos = s.position()
|
|
4525
|
+
value = s.systring[:-1]
|
|
4526
|
+
s.next()
|
|
4527
|
+
res = ExprNodes.binop_node(
|
|
4528
|
+
add_pos,
|
|
4529
|
+
sign,
|
|
4530
|
+
operand1=res,
|
|
4531
|
+
operand2=ExprNodes.ImagNode(s.position(), value=value)
|
|
4532
|
+
)
|
|
4533
|
+
|
|
4534
|
+
if res is None and sy == 'IMAG':
|
|
4535
|
+
value = s.systring[:-1]
|
|
4536
|
+
s.next()
|
|
4537
|
+
res = ExprNodes.ImagNode(pos, value=sign+value)
|
|
4538
|
+
if sign == "-":
|
|
4539
|
+
res = ExprNodes.UnaryMinusNode(sign_pos, operand=res)
|
|
4540
|
+
|
|
4541
|
+
if res is not None:
|
|
4542
|
+
return MatchCaseNodes.MatchValuePatternNode(pos, value=res)
|
|
4543
|
+
|
|
4544
|
+
if next_must_be_a_number:
|
|
4545
|
+
s.error("Expected a number")
|
|
4546
|
+
if sy == 'BEGIN_STRING':
|
|
4547
|
+
res = p_atom_string(s)
|
|
4548
|
+
# Whether f-strings are suitable is validated in PostParse.
|
|
4549
|
+
return MatchCaseNodes.MatchValuePatternNode(pos, value=res)
|
|
4550
|
+
elif sy == 'IDENT':
|
|
4551
|
+
# Note that p_atom_ident_constants includes NULL.
|
|
4552
|
+
# This is a deliberate Cython addition to the pattern matching specification
|
|
4553
|
+
result = p_atom_ident_constants(s)
|
|
4554
|
+
if result:
|
|
4555
|
+
return MatchCaseNodes.MatchValuePatternNode(pos, value=result, is_is_check=True)
|
|
4556
|
+
|
|
4557
|
+
s.error("Failed to match literal")
|
|
4558
|
+
|
|
4559
|
+
|
|
4560
|
+
@cython.cfunc
|
|
4561
|
+
def p_capture_pattern(s: PyrexScanner):
|
|
4562
|
+
return MatchCaseNodes.MatchAndAssignPatternNode(
|
|
4563
|
+
s.position(),
|
|
4564
|
+
target=p_pattern_capture_target(s)
|
|
4565
|
+
)
|
|
4566
|
+
|
|
4567
|
+
|
|
4568
|
+
@cython.cfunc
|
|
4569
|
+
def p_value_pattern(s: PyrexScanner):
|
|
4570
|
+
if s.sy != "IDENT":
|
|
4571
|
+
s.error("Expected identifier")
|
|
4572
|
+
pos = s.position()
|
|
4573
|
+
res = p_name(s, s.systring)
|
|
4574
|
+
s.next()
|
|
4575
|
+
if s.sy != '.':
|
|
4576
|
+
s.error(".")
|
|
4577
|
+
while s.sy == '.':
|
|
4578
|
+
attr_pos = s.position()
|
|
4579
|
+
s.next()
|
|
4580
|
+
attr = p_ident(s)
|
|
4581
|
+
res = ExprNodes.AttributeNode(attr_pos, obj=res, attribute=attr)
|
|
4582
|
+
if s.sy in ['(', '=']:
|
|
4583
|
+
s.error("Unexpected symbol '%s'" % s.sy)
|
|
4584
|
+
return MatchCaseNodes.MatchValuePatternNode(pos, value=res)
|
|
4585
|
+
|
|
4586
|
+
|
|
4587
|
+
@cython.cfunc
|
|
4588
|
+
def p_group_pattern(s: PyrexScanner):
|
|
4589
|
+
s.expect("(")
|
|
4590
|
+
pattern = p_pattern(s)
|
|
4591
|
+
s.expect(")")
|
|
4592
|
+
return pattern
|
|
4593
|
+
|
|
4594
|
+
|
|
4595
|
+
@cython.cfunc
|
|
4596
|
+
def p_sequence_pattern(s: PyrexScanner):
|
|
4597
|
+
opener = s.sy
|
|
4598
|
+
pos = s.position()
|
|
4599
|
+
if opener in ['[', '(']:
|
|
4600
|
+
closer = ']' if opener == '[' else ')'
|
|
4601
|
+
s.next()
|
|
4602
|
+
# maybe_sequence_pattern and open_sequence_pattern
|
|
4603
|
+
patterns = []
|
|
4604
|
+
while s.sy != closer:
|
|
4605
|
+
patterns.append(p_maybe_star_pattern(s))
|
|
4606
|
+
if s.sy == ",":
|
|
4607
|
+
s.next()
|
|
4608
|
+
else:
|
|
4609
|
+
if opener == '(' and len(patterns) == 1:
|
|
4610
|
+
s.error("tuple-like pattern of length 1 must finish with ','")
|
|
4611
|
+
break
|
|
4612
|
+
s.expect(closer)
|
|
4613
|
+
return MatchCaseNodes.MatchSequencePatternNode(pos, patterns=patterns)
|
|
4614
|
+
else:
|
|
4615
|
+
s.error("Expected '[' or '('")
|
|
4616
|
+
|
|
4617
|
+
|
|
4618
|
+
@cython.cfunc
|
|
4619
|
+
def p_mapping_pattern(s: PyrexScanner):
|
|
4620
|
+
pos = s.position()
|
|
4621
|
+
s.expect('{')
|
|
4622
|
+
if s.sy == '}':
|
|
4623
|
+
# trivial empty mapping
|
|
4624
|
+
s.next()
|
|
4625
|
+
return MatchCaseNodes.MatchMappingPatternNode(pos)
|
|
4626
|
+
|
|
4627
|
+
double_star_capture_target = None
|
|
4628
|
+
items_patterns = []
|
|
4629
|
+
star_star_arg_pos = None
|
|
4630
|
+
while s.sy != '}':
|
|
4631
|
+
if double_star_capture_target and not star_star_arg_pos:
|
|
4632
|
+
star_star_arg_pos = s.position()
|
|
4633
|
+
if s.sy == '**':
|
|
4634
|
+
s.next()
|
|
4635
|
+
double_star_capture_target = p_pattern_capture_target(s)
|
|
4636
|
+
else:
|
|
4637
|
+
# key=(literal_expr | attr)
|
|
4638
|
+
with tentatively_scan(s) as errors:
|
|
4639
|
+
pattern = p_literal_pattern(s)
|
|
4640
|
+
key = pattern.value
|
|
4641
|
+
if errors:
|
|
4642
|
+
pattern = p_value_pattern(s)
|
|
4643
|
+
key = pattern.value
|
|
4644
|
+
s.expect(':')
|
|
4645
|
+
value = p_pattern(s)
|
|
4646
|
+
items_patterns.append((key, value))
|
|
4647
|
+
if s.sy != ',':
|
|
4648
|
+
break
|
|
4649
|
+
s.next()
|
|
4650
|
+
s.expect('}')
|
|
4651
|
+
|
|
4652
|
+
if star_star_arg_pos is not None:
|
|
4653
|
+
return Nodes.ErrorNode(
|
|
4654
|
+
star_star_arg_pos,
|
|
4655
|
+
what = "** pattern must be the final part of a mapping pattern."
|
|
4656
|
+
)
|
|
4657
|
+
return MatchCaseNodes.MatchMappingPatternNode(
|
|
4658
|
+
pos,
|
|
4659
|
+
keys = [kv[0] for kv in items_patterns],
|
|
4660
|
+
value_patterns = [kv[1] for kv in items_patterns],
|
|
4661
|
+
double_star_capture_target = double_star_capture_target
|
|
4662
|
+
)
|
|
4663
|
+
|
|
4664
|
+
|
|
4665
|
+
@cython.cfunc
|
|
4666
|
+
def p_class_pattern(s: PyrexScanner):
|
|
4667
|
+
# start by parsing the class as name_or_attr
|
|
4668
|
+
pos = s.position()
|
|
4669
|
+
res = p_name(s, s.systring)
|
|
4670
|
+
s.next()
|
|
4671
|
+
while s.sy == '.':
|
|
4672
|
+
attr_pos = s.position()
|
|
4673
|
+
s.next()
|
|
4674
|
+
attr = p_ident(s)
|
|
4675
|
+
res = ExprNodes.AttributeNode(attr_pos, obj=res, attribute=attr)
|
|
4676
|
+
class_ = res
|
|
4677
|
+
|
|
4678
|
+
s.expect("(")
|
|
4679
|
+
if s.sy == ")":
|
|
4680
|
+
# trivial case with no arguments matched
|
|
4681
|
+
s.next()
|
|
4682
|
+
return MatchCaseNodes.ClassPatternNode(pos, class_=class_)
|
|
4683
|
+
|
|
4684
|
+
# parse the arguments
|
|
4685
|
+
positional_patterns = []
|
|
4686
|
+
keyword_patterns = []
|
|
4687
|
+
keyword_patterns_error = None
|
|
4688
|
+
while s.sy != ')':
|
|
4689
|
+
with tentatively_scan(s) as errors:
|
|
4690
|
+
positional_patterns.append(p_pattern(s))
|
|
4691
|
+
if not errors:
|
|
4692
|
+
if keyword_patterns:
|
|
4693
|
+
keyword_patterns_error = s.position()
|
|
4694
|
+
else:
|
|
4695
|
+
with tentatively_scan(s) as errors:
|
|
4696
|
+
keyword_patterns.append(p_keyword_pattern(s))
|
|
4697
|
+
if s.sy != ",":
|
|
4698
|
+
break
|
|
4699
|
+
s.next()
|
|
4700
|
+
s.expect(")")
|
|
4701
|
+
|
|
4702
|
+
if keyword_patterns_error is not None:
|
|
4703
|
+
return Nodes.ErrorNode(
|
|
4704
|
+
keyword_patterns_error,
|
|
4705
|
+
what="Positional patterns follow keyword patterns"
|
|
4706
|
+
)
|
|
4707
|
+
return MatchCaseNodes.ClassPatternNode(
|
|
4708
|
+
pos, class_ = class_,
|
|
4709
|
+
positional_patterns = positional_patterns,
|
|
4710
|
+
keyword_pattern_names = [kv[0] for kv in keyword_patterns],
|
|
4711
|
+
keyword_pattern_patterns = [kv[1] for kv in keyword_patterns],
|
|
4712
|
+
)
|
|
4713
|
+
|
|
4714
|
+
|
|
4715
|
+
@cython.cfunc
|
|
4716
|
+
def p_keyword_pattern(s: PyrexScanner):
|
|
4717
|
+
if s.sy != "IDENT":
|
|
4718
|
+
s.error("Expected identifier")
|
|
4719
|
+
arg = p_name(s, s.systring)
|
|
4720
|
+
s.next()
|
|
4721
|
+
s.expect("=")
|
|
4722
|
+
value = p_pattern(s)
|
|
4723
|
+
return arg, value
|
|
4724
|
+
|
|
4725
|
+
|
|
4726
|
+
@cython.cfunc
|
|
4727
|
+
def p_pattern_capture_target(s: PyrexScanner):
|
|
4728
|
+
# any name but '_', and with some constraints on what follows
|
|
4729
|
+
if s.sy != 'IDENT':
|
|
4730
|
+
s.error("Expected identifier")
|
|
4731
|
+
if s.systring == '_':
|
|
4732
|
+
s.error("Pattern capture target cannot be '_'")
|
|
4733
|
+
target = p_name(s, s.systring)
|
|
4734
|
+
s.next()
|
|
4735
|
+
if s.sy in ['.', '(', '=']:
|
|
4736
|
+
s.error("Illegal next symbol '%s'" % s.sy)
|
|
4737
|
+
return target
|
|
4738
|
+
|
|
4739
|
+
|
|
4740
|
+
|
|
4741
|
+
#----------------------------------------------
|
|
4742
|
+
#
|
|
4743
|
+
# Debugging
|
|
4744
|
+
#
|
|
4745
|
+
#----------------------------------------------
|
|
4746
|
+
|
|
4747
|
+
@cython.ccall
|
|
4748
|
+
def print_parse_tree(f, node, level: cython.long, key = None):
|
|
4749
|
+
ind: str = " " * level
|
|
4750
|
+
f.write(ind)
|
|
4751
|
+
if key:
|
|
4752
|
+
f.write(f"{key}: ")
|
|
4753
|
+
if not node:
|
|
4754
|
+
f.write("None\n")
|
|
4755
|
+
elif type(node) is tuple:
|
|
4756
|
+
f.write(f"({node[0]} @ {node[1]}\n")
|
|
4757
|
+
for item in node[2:]:
|
|
4758
|
+
print_parse_tree(f, item, level+1)
|
|
4759
|
+
f.write(f"{ind})\n")
|
|
4760
|
+
elif isinstance(node, Nodes.Node):
|
|
4761
|
+
try:
|
|
4762
|
+
tag = node.tag
|
|
4763
|
+
except AttributeError:
|
|
4764
|
+
tag = node.__class__.__name__
|
|
4765
|
+
f.write(f"{tag} @ {node.pos}\n")
|
|
4766
|
+
for name, value in sorted(node.__dict__.items()):
|
|
4767
|
+
if name != 'tag' and name != 'pos':
|
|
4768
|
+
print_parse_tree(f, value, level+1, name)
|
|
4769
|
+
elif type(node) is list:
|
|
4770
|
+
f.write("[\n")
|
|
4771
|
+
for item in node:
|
|
4772
|
+
print_parse_tree(f, item, level+1)
|
|
4773
|
+
f.write(f"{ind}]\n")
|
|
4774
|
+
else:
|
|
4775
|
+
f.write(f"{ind}{node}\n")
|