Cython 3.1.0__py3-none-any.whl

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