pycparser 3.1__tar.gz → 3.11__tar.gz

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 (188) hide show
  1. {pycparser-3.1/pycparser.egg-info → pycparser-3.11}/PKG-INFO +1 -1
  2. {pycparser-3.1 → pycparser-3.11}/pycparser/__init__.py +1 -1
  3. {pycparser-3.1 → pycparser-3.11}/pycparser/_c_ast.cfg +17 -0
  4. {pycparser-3.1 → pycparser-3.11}/pycparser/ast_transforms.py +20 -14
  5. {pycparser-3.1 → pycparser-3.11}/pycparser/c_ast.py +49 -0
  6. {pycparser-3.1 → pycparser-3.11}/pycparser/c_generator.py +50 -15
  7. {pycparser-3.1 → pycparser-3.11}/pycparser/c_lexer.py +42 -37
  8. {pycparser-3.1 → pycparser-3.11}/pycparser/c_parser.py +124 -45
  9. {pycparser-3.1 → pycparser-3.11/pycparser.egg-info}/PKG-INFO +1 -1
  10. {pycparser-3.1 → pycparser-3.11}/pycparser.egg-info/SOURCES.txt +1 -0
  11. {pycparser-3.1 → pycparser-3.11}/pyproject.toml +1 -1
  12. {pycparser-3.1 → pycparser-3.11}/tests/c_files/c11.c +31 -2
  13. {pycparser-3.1 → pycparser-3.11}/tests/test_c_generator.py +242 -4
  14. {pycparser-3.1 → pycparser-3.11}/tests/test_c_lexer.py +47 -0
  15. {pycparser-3.1 → pycparser-3.11}/tests/test_c_parser.py +454 -9
  16. {pycparser-3.1 → pycparser-3.11}/tests/test_general.py +9 -1
  17. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/_fake_defines.h +2 -0
  18. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/_fake_typedefs.h +10 -0
  19. pycparser-3.11/utils/fake_libc_include/xcb/xcb.h +2 -0
  20. {pycparser-3.1 → pycparser-3.11}/LICENSE +0 -0
  21. {pycparser-3.1 → pycparser-3.11}/MANIFEST.in +0 -0
  22. {pycparser-3.1 → pycparser-3.11}/README.rst +0 -0
  23. {pycparser-3.1 → pycparser-3.11}/examples/c-to-c.py +0 -0
  24. {pycparser-3.1 → pycparser-3.11}/examples/c_files/basic.c +0 -0
  25. {pycparser-3.1 → pycparser-3.11}/examples/c_files/funky.c +0 -0
  26. {pycparser-3.1 → pycparser-3.11}/examples/c_files/hash.c +0 -0
  27. {pycparser-3.1 → pycparser-3.11}/examples/c_files/memmgr.c +0 -0
  28. {pycparser-3.1 → pycparser-3.11}/examples/c_files/memmgr.h +0 -0
  29. {pycparser-3.1 → pycparser-3.11}/examples/c_files/pragmas.c +0 -0
  30. {pycparser-3.1 → pycparser-3.11}/examples/c_files/year.c +0 -0
  31. {pycparser-3.1 → pycparser-3.11}/examples/c_json.py +0 -0
  32. {pycparser-3.1 → pycparser-3.11}/examples/cdecl.py +0 -0
  33. {pycparser-3.1 → pycparser-3.11}/examples/construct_ast_from_scratch.py +0 -0
  34. {pycparser-3.1 → pycparser-3.11}/examples/dump_ast.py +0 -0
  35. {pycparser-3.1 → pycparser-3.11}/examples/explore_ast.py +0 -0
  36. {pycparser-3.1 → pycparser-3.11}/examples/func_calls.py +0 -0
  37. {pycparser-3.1 → pycparser-3.11}/examples/func_defs.py +0 -0
  38. {pycparser-3.1 → pycparser-3.11}/examples/func_defs_add_param.py +0 -0
  39. {pycparser-3.1 → pycparser-3.11}/examples/rewrite_ast.py +0 -0
  40. {pycparser-3.1 → pycparser-3.11}/examples/serialize_ast.py +0 -0
  41. {pycparser-3.1 → pycparser-3.11}/examples/typed_usage.py +0 -0
  42. {pycparser-3.1 → pycparser-3.11}/examples/using_cpp_libc.py +0 -0
  43. {pycparser-3.1 → pycparser-3.11}/examples/using_gcc_E_libc.py +0 -0
  44. {pycparser-3.1 → pycparser-3.11}/pycparser/_ast_gen.py +0 -0
  45. {pycparser-3.1 → pycparser-3.11}/pycparser.egg-info/dependency_links.txt +0 -0
  46. {pycparser-3.1 → pycparser-3.11}/pycparser.egg-info/top_level.txt +0 -0
  47. {pycparser-3.1 → pycparser-3.11}/setup.cfg +0 -0
  48. {pycparser-3.1 → pycparser-3.11}/setup.py +0 -0
  49. {pycparser-3.1 → pycparser-3.11}/tests/__init__.py +0 -0
  50. {pycparser-3.1 → pycparser-3.11}/tests/c_files/cppd_with_stdio_h.c +0 -0
  51. {pycparser-3.1 → pycparser-3.11}/tests/c_files/empty.h +0 -0
  52. {pycparser-3.1 → pycparser-3.11}/tests/c_files/example_c_file.c +0 -0
  53. {pycparser-3.1 → pycparser-3.11}/tests/c_files/hdir/9/inc.h +0 -0
  54. {pycparser-3.1 → pycparser-3.11}/tests/c_files/memmgr.c +0 -0
  55. {pycparser-3.1 → pycparser-3.11}/tests/c_files/memmgr.h +0 -0
  56. {pycparser-3.1 → pycparser-3.11}/tests/c_files/memmgr_with_h.c +0 -0
  57. {pycparser-3.1 → pycparser-3.11}/tests/c_files/simplemain.c +0 -0
  58. {pycparser-3.1 → pycparser-3.11}/tests/c_files/year.c +0 -0
  59. {pycparser-3.1 → pycparser-3.11}/tests/test_c_ast.py +0 -0
  60. {pycparser-3.1 → pycparser-3.11}/tests/test_examples.py +0 -0
  61. {pycparser-3.1 → pycparser-3.11}/tests/test_util.py +0 -0
  62. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/X11/Intrinsic.h +0 -0
  63. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/X11/Xlib.h +0 -0
  64. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/X11/_X11_fake_defines.h +0 -0
  65. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/X11/_X11_fake_typedefs.h +0 -0
  66. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/_ansi.h +0 -0
  67. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/_syslist.h +0 -0
  68. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/aio.h +0 -0
  69. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/alloca.h +0 -0
  70. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/ar.h +0 -0
  71. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/argz.h +0 -0
  72. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/arpa/inet.h +0 -0
  73. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/asm-generic/int-ll64.h +0 -0
  74. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/assert.h +0 -0
  75. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/complex.h +0 -0
  76. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/cpio.h +0 -0
  77. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/ctype.h +0 -0
  78. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/dirent.h +0 -0
  79. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/dlfcn.h +0 -0
  80. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/emmintrin.h +0 -0
  81. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/endian.h +0 -0
  82. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/envz.h +0 -0
  83. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/errno.h +0 -0
  84. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/fastmath.h +0 -0
  85. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/fcntl.h +0 -0
  86. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/features.h +0 -0
  87. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/fenv.h +0 -0
  88. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/float.h +0 -0
  89. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/fmtmsg.h +0 -0
  90. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/fnmatch.h +0 -0
  91. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/ftw.h +0 -0
  92. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/getopt.h +0 -0
  93. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/glob.h +0 -0
  94. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/grp.h +0 -0
  95. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/iconv.h +0 -0
  96. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/ieeefp.h +0 -0
  97. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/immintrin.h +0 -0
  98. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/inttypes.h +0 -0
  99. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/iso646.h +0 -0
  100. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/langinfo.h +0 -0
  101. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/libgen.h +0 -0
  102. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/libintl.h +0 -0
  103. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/limits.h +0 -0
  104. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/linux/socket.h +0 -0
  105. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/linux/version.h +0 -0
  106. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/locale.h +0 -0
  107. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/malloc.h +0 -0
  108. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/math.h +0 -0
  109. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/mir_toolkit/client_types.h +0 -0
  110. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/monetary.h +0 -0
  111. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/mqueue.h +0 -0
  112. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/ndbm.h +0 -0
  113. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/net/if.h +0 -0
  114. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/netdb.h +0 -0
  115. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/netinet/in.h +0 -0
  116. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/netinet/tcp.h +0 -0
  117. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/newlib.h +0 -0
  118. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/nl_types.h +0 -0
  119. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/openssl/err.h +0 -0
  120. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/openssl/evp.h +0 -0
  121. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/openssl/hmac.h +0 -0
  122. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/openssl/ssl.h +0 -0
  123. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/openssl/x509v3.h +0 -0
  124. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/paths.h +0 -0
  125. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/poll.h +0 -0
  126. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/process.h +0 -0
  127. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/pthread.h +0 -0
  128. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/pwd.h +0 -0
  129. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/reent.h +0 -0
  130. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/regdef.h +0 -0
  131. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/regex.h +0 -0
  132. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sched.h +0 -0
  133. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/search.h +0 -0
  134. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/semaphore.h +0 -0
  135. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/setjmp.h +0 -0
  136. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/signal.h +0 -0
  137. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/smmintrin.h +0 -0
  138. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/spawn.h +0 -0
  139. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdalign.h +0 -0
  140. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdarg.h +0 -0
  141. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdatomic.h +0 -0
  142. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdbool.h +0 -0
  143. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stddef.h +0 -0
  144. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdint.h +0 -0
  145. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdio.h +0 -0
  146. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdlib.h +0 -0
  147. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stdnoreturn.h +0 -0
  148. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/string.h +0 -0
  149. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/strings.h +0 -0
  150. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/stropts.h +0 -0
  151. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/ioctl.h +0 -0
  152. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/ipc.h +0 -0
  153. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/mman.h +0 -0
  154. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/msg.h +0 -0
  155. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/poll.h +0 -0
  156. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/resource.h +0 -0
  157. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/select.h +0 -0
  158. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/sem.h +0 -0
  159. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/shm.h +0 -0
  160. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/socket.h +0 -0
  161. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/stat.h +0 -0
  162. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/statvfs.h +0 -0
  163. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/sysctl.h +0 -0
  164. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/time.h +0 -0
  165. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/times.h +0 -0
  166. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/types.h +0 -0
  167. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/uio.h +0 -0
  168. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/un.h +0 -0
  169. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/utsname.h +0 -0
  170. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/sys/wait.h +0 -0
  171. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/syslog.h +0 -0
  172. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/tar.h +0 -0
  173. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/termios.h +0 -0
  174. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/tgmath.h +0 -0
  175. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/threads.h +0 -0
  176. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/time.h +0 -0
  177. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/trace.h +0 -0
  178. /pycparser-3.1/utils/fake_libc_include/ulimit.h → /pycparser-3.11/utils/fake_libc_include/uchar.h +0 -0
  179. /pycparser-3.1/utils/fake_libc_include/unctrl.h → /pycparser-3.11/utils/fake_libc_include/ulimit.h +0 -0
  180. /pycparser-3.1/utils/fake_libc_include/unistd.h → /pycparser-3.11/utils/fake_libc_include/unctrl.h +0 -0
  181. /pycparser-3.1/utils/fake_libc_include/utime.h → /pycparser-3.11/utils/fake_libc_include/unistd.h +0 -0
  182. /pycparser-3.1/utils/fake_libc_include/utmp.h → /pycparser-3.11/utils/fake_libc_include/utime.h +0 -0
  183. /pycparser-3.1/utils/fake_libc_include/utmpx.h → /pycparser-3.11/utils/fake_libc_include/utmp.h +0 -0
  184. /pycparser-3.1/utils/fake_libc_include/wchar.h → /pycparser-3.11/utils/fake_libc_include/utmpx.h +0 -0
  185. /pycparser-3.1/utils/fake_libc_include/wctype.h → /pycparser-3.11/utils/fake_libc_include/wchar.h +0 -0
  186. /pycparser-3.1/utils/fake_libc_include/wordexp.h → /pycparser-3.11/utils/fake_libc_include/wctype.h +0 -0
  187. /pycparser-3.1/utils/fake_libc_include/xcb/xcb.h → /pycparser-3.11/utils/fake_libc_include/wordexp.h +0 -0
  188. {pycparser-3.1 → pycparser-3.11}/utils/fake_libc_include/zlib.h +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycparser
3
- Version: 3.1
3
+ Version: 3.11
4
4
  Summary: C parser in Python
5
5
  Author-email: Eli Bendersky <eliben@gmail.com>
6
6
  Maintainer-email: Eli Bendersky <eliben@gmail.com>
@@ -8,7 +8,7 @@
8
8
  # License: BSD
9
9
  # -----------------------------------------------------------------
10
10
  __all__ = ["c_ast", "c_lexer", "c_parser"]
11
- __version__ = "3.01"
11
+ __version__ = "3.11"
12
12
 
13
13
  from subprocess import check_output
14
14
 
@@ -50,6 +50,8 @@ Compound: [block_items**]
50
50
  CompoundLiteral: [type*, init*]
51
51
 
52
52
  # type: int, char, float, string, etc.
53
+ # value: source spelling. A string may retain adjacent quoted fragments to
54
+ # preserve escape boundaries, e.g. "\x1" "a".
53
55
  #
54
56
  Constant: [type, value]
55
57
 
@@ -125,6 +127,18 @@ FuncDecl: [args*, type*]
125
127
  #
126
128
  FuncDef: [decl*, param_decls**, body*]
127
129
 
130
+ # One association in a C11 generic selection.
131
+ # type: Typename, or None for the default association
132
+ # expr: the result expression for this association
133
+ #
134
+ GenericAssociation: [type*, expr*]
135
+
136
+ # A C11 _Generic expression. All associations are preserved in source order.
137
+ # expr: the controlling expression (not evaluated by C)
138
+ # associations: a nonempty sequence of GenericAssociation nodes
139
+ #
140
+ GenericSelection: [expr*, associations**]
141
+
128
142
  Goto: [name]
129
143
 
130
144
  ID: [name]
@@ -156,6 +170,9 @@ PtrDecl: [quals, type*]
156
170
 
157
171
  Return: [expr*]
158
172
 
173
+ # A static assertion declaration. The condition is preserved without evaluation.
174
+ # message: a string Constant, or None for the message-less extension
175
+ #
159
176
  StaticAssert: [cond*, message*]
160
177
 
161
178
  # name: struct tag name
@@ -7,6 +7,7 @@
7
7
  # License: BSD
8
8
  # ------------------------------------------------------------------------------
9
9
 
10
+ from copy import deepcopy
10
11
  from typing import Any, cast
11
12
 
12
13
  from . import c_ast
@@ -111,8 +112,8 @@ def _extract_nested_case(
111
112
 
112
113
 
113
114
  def fix_atomic_specifiers(
114
- decl: c_ast.Decl | c_ast.Typedef,
115
- ) -> c_ast.Decl | c_ast.Typedef:
115
+ decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename,
116
+ ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename:
116
117
  """Atomic specifiers like _Atomic(type) are unusually structured,
117
118
  conferring a qualifier upon the contained type.
118
119
 
@@ -127,17 +128,16 @@ def fix_atomic_specifiers(
127
128
  if not found:
128
129
  break
129
130
 
130
- # Make sure to add an _Atomic qual on the topmost decl if needed. Also
131
- # restore the declname on the innermost TypeDecl (it gets placed in the
132
- # wrong place during construction).
131
+ # Match the declaration's qualifiers to the innermost TypeDecl, as for
132
+ # ordinary declarations. Qualifiers moved onto pointers belong only on
133
+ # those PtrDecl nodes. Also restore the name lost with the wrapper TypeDecl.
133
134
  typ: Any = decl
134
135
  while not isinstance(typ, c_ast.TypeDecl):
135
136
  try:
136
137
  typ = typ.type
137
138
  except AttributeError:
138
139
  return decl
139
- if "_Atomic" in typ.quals and "_Atomic" not in decl.quals:
140
- decl.quals.append("_Atomic")
140
+ decl.quals = typ.quals[:]
141
141
  if typ.declname is None:
142
142
  typ.declname = decl.name
143
143
 
@@ -145,8 +145,8 @@ def fix_atomic_specifiers(
145
145
 
146
146
 
147
147
  def _fix_atomic_specifiers_once(
148
- decl: c_ast.Decl | c_ast.Typedef,
149
- ) -> tuple[c_ast.Decl | c_ast.Typedef, bool]:
148
+ decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename,
149
+ ) -> tuple[c_ast.Decl | c_ast.Typedef | c_ast.Typename, bool]:
150
150
  """Performs one 'fix' round of atomic specifiers.
151
151
  Returns (modified_decl, found) where found is True iff a fix was made.
152
152
  """
@@ -168,11 +168,17 @@ def _fix_atomic_specifiers_once(
168
168
 
169
169
  assert isinstance(parent, c_ast.TypeDecl)
170
170
  assert grandparent is not None
171
- if node.type.coord is None:
171
+ # Declaration specifiers are shared by comma-separated declarators. Copy
172
+ # the contained type before attaching qualifiers, coordinates, and a name.
173
+ typ = deepcopy(node.type)
174
+ if typ.coord is None:
172
175
  # Preserve the declarator coord for _Atomic(T) so TypeDecl doesn't lose
173
176
  # its location when we replace the wrapper Typename.
174
- node.type.coord = parent.coord
175
- cast(Any, grandparent).type = node.type
176
- if "_Atomic" not in node.type.quals:
177
- node.type.quals.append("_Atomic")
177
+ typ.coord = parent.coord
178
+ # Qualifiers outside _Atomic(T) apply to T itself. In particular, const in
179
+ # const _Atomic(int *) qualifies the pointer, not the pointed-to int.
180
+ typ.quals = parent.quals + typ.quals
181
+ if "_Atomic" not in typ.quals:
182
+ typ.quals.append("_Atomic")
183
+ cast(Any, grandparent).type = typ
178
184
  return decl, True
@@ -831,6 +831,55 @@ class FuncDef(Node):
831
831
  attr_names = ()
832
832
 
833
833
 
834
+ class GenericAssociation(Node):
835
+ __slots__ = ("type", "expr", "coord", "__weakref__")
836
+
837
+ def __init__(self, type, expr, coord=None):
838
+ self.type = type
839
+ self.expr = expr
840
+ self.coord = coord
841
+
842
+ def children(self):
843
+ nodelist = []
844
+ if self.type is not None:
845
+ nodelist.append(("type", self.type))
846
+ if self.expr is not None:
847
+ nodelist.append(("expr", self.expr))
848
+ return tuple(nodelist)
849
+
850
+ def __iter__(self):
851
+ if self.type is not None:
852
+ yield self.type
853
+ if self.expr is not None:
854
+ yield self.expr
855
+
856
+ attr_names = ()
857
+
858
+
859
+ class GenericSelection(Node):
860
+ __slots__ = ("expr", "associations", "coord", "__weakref__")
861
+
862
+ def __init__(self, expr, associations, coord=None):
863
+ self.expr = expr
864
+ self.associations = associations
865
+ self.coord = coord
866
+
867
+ def children(self):
868
+ nodelist = []
869
+ if self.expr is not None:
870
+ nodelist.append(("expr", self.expr))
871
+ for i, child in enumerate(self.associations or []):
872
+ nodelist.append((f"associations[{i}]", child))
873
+ return tuple(nodelist)
874
+
875
+ def __iter__(self):
876
+ if self.expr is not None:
877
+ yield self.expr
878
+ yield from self.associations or []
879
+
880
+ attr_names = ()
881
+
882
+
834
883
  class Goto(Node):
835
884
  __slots__ = ("name", "coord", "__weakref__")
836
885
 
@@ -70,6 +70,17 @@ class CGenerator:
70
70
  args = self.visit(n.args) if n.args is not None else ""
71
71
  return fref + "(" + args + ")"
72
72
 
73
+ def visit_GenericSelection(self, n: c_ast.GenericSelection) -> str:
74
+ # The controlling and association expressions are assignment-expressions:
75
+ # preserve parentheses around comma expressions with _visit_expr.
76
+ expr = self._visit_expr(n.expr)
77
+ associations = ", ".join(self.visit(a) for a in n.associations)
78
+ return f"_Generic({expr}, {associations})"
79
+
80
+ def visit_GenericAssociation(self, n: c_ast.GenericAssociation) -> str:
81
+ typ = self.visit(n.type) if n.type is not None else "default"
82
+ return f"{typ}: {self._visit_expr(n.expr)}"
83
+
73
84
  def visit_UnaryOp(self, n: c_ast.UnaryOp) -> str:
74
85
  match n.op:
75
86
  case "sizeof":
@@ -170,10 +181,13 @@ class CGenerator:
170
181
  return self.visit(n)
171
182
 
172
183
  def visit_Decl(self, n: c_ast.Decl, no_type: bool = False) -> str:
173
- # no_type is used when a Decl is part of a DeclList, where the type is
174
- # explicitly only for the first declaration in a list.
175
- #
176
- s = n.name if no_type else self._generate_decl(n)
184
+ # Later declarations in a DeclList omit the shared specifiers, but
185
+ # still need their own pointer, array, and function declarator parts.
186
+ s = (
187
+ self._generate_type(n.type, emit_base_type=False)
188
+ if no_type
189
+ else self._generate_decl(n)
190
+ )
177
191
  if n.bitsize:
178
192
  s += " : " + self.visit(n.bitsize)
179
193
  if n.init:
@@ -327,7 +341,7 @@ class CGenerator:
327
341
  s = "_Static_assert("
328
342
  s += self.visit(n.cond)
329
343
  if n.message:
330
- s += ","
344
+ s += ", "
331
345
  s += self.visit(n.message)
332
346
  s += ")"
333
347
  return s
@@ -451,10 +465,11 @@ class CGenerator:
451
465
  | c_ast.Typedef()
452
466
  | c_ast.ExprList()
453
467
  | c_ast.CompoundLiteral()
468
+ | c_ast.GenericSelection()
469
+ | c_ast.StaticAssert()
454
470
  ):
455
- # These can also appear in an expression context so no semicolon
456
- # is added to them automatically
457
- #
471
+ # These visitors leave the semicolon to the enclosing context,
472
+ # so they can also be used in expressions or for clauses.
458
473
  return indent + self.visit(n) + ";\n"
459
474
  case c_ast.Compound():
460
475
  # No extra indentation required before the opening brace of a
@@ -475,7 +490,7 @@ class CGenerator:
475
490
  if n.storage:
476
491
  s += " ".join(n.storage) + " "
477
492
  if n.align:
478
- s += self.visit(n.align[0]) + " "
493
+ s += " ".join(self.visit(align) for align in n.align) + " "
479
494
  s += self._generate_type(n.type)
480
495
  return s
481
496
 
@@ -484,11 +499,14 @@ class CGenerator:
484
499
  n: c_ast.Node,
485
500
  modifiers: list[c_ast.Node] | None = None,
486
501
  emit_declname: bool = True,
502
+ emit_base_type: bool = True,
487
503
  ) -> str:
488
504
  """Recursive generation from a type node. n is the type node.
489
505
  modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers
490
506
  encountered on the way down to a TypeDecl, to allow proper
491
507
  generation from it.
508
+ emit_base_type controls whether the shared type specifier and its
509
+ qualifiers are emitted, independently of the declarator parts.
492
510
  """
493
511
  if modifiers is None:
494
512
  modifiers = []
@@ -497,9 +515,10 @@ class CGenerator:
497
515
  match n:
498
516
  case c_ast.TypeDecl():
499
517
  s = ""
500
- if n.quals:
501
- s += " ".join(n.quals) + " "
502
- s += self.visit(n.type)
518
+ if emit_base_type:
519
+ if n.quals:
520
+ s += " ".join(n.quals) + " "
521
+ s += self.visit(n.type)
503
522
 
504
523
  nstr = n.declname if n.declname and emit_declname else ""
505
524
  # Resolve modifiers.
@@ -533,18 +552,27 @@ class CGenerator:
533
552
  nstr = f"* {quals}{suffix}"
534
553
  else:
535
554
  nstr = "*" + nstr
555
+ if not emit_base_type:
556
+ return nstr
536
557
  if nstr:
537
558
  s += " " + nstr
538
559
  return s
539
560
  case c_ast.Decl():
540
561
  return self._generate_decl(n.type)
541
562
  case c_ast.Typename():
542
- return self._generate_type(n.type, emit_declname=emit_declname)
563
+ return self._generate_type(
564
+ n.type,
565
+ emit_declname=emit_declname,
566
+ emit_base_type=emit_base_type,
567
+ )
543
568
  case c_ast.IdentifierType():
544
569
  return " ".join(n.names) + " "
545
570
  case c_ast.ArrayDecl() | c_ast.PtrDecl() | c_ast.FuncDecl():
546
571
  return self._generate_type(
547
- n.type, modifiers + [n], emit_declname=emit_declname
572
+ n.type,
573
+ modifiers + [n],
574
+ emit_declname=emit_declname,
575
+ emit_base_type=emit_base_type,
548
576
  )
549
577
  case _:
550
578
  return self.visit(n)
@@ -571,5 +599,12 @@ class CGenerator:
571
599
  """
572
600
  return isinstance(
573
601
  n,
574
- (c_ast.Constant, c_ast.ID, c_ast.ArrayRef, c_ast.StructRef, c_ast.FuncCall),
602
+ (
603
+ c_ast.Constant,
604
+ c_ast.ID,
605
+ c_ast.ArrayRef,
606
+ c_ast.StructRef,
607
+ c_ast.FuncCall,
608
+ c_ast.GenericSelection,
609
+ ),
575
610
  )
@@ -425,6 +425,7 @@ _keywords: tuple[str, ...] = (
425
425
  "__INT128",
426
426
  "_BOOL",
427
427
  "_COMPLEX",
428
+ "_GENERIC",
428
429
  "_NORETURN",
429
430
  "_THREAD_LOCAL",
430
431
  "_STATIC_ASSERT",
@@ -472,51 +473,55 @@ _bad_octal_constant = "0[0-7]*[89]"
472
473
  _unsupported_c_style_comment = r"\/\*"
473
474
  _unsupported_cxx_style_comment = r"\/\/"
474
475
 
475
- # character constants (K&R2: A.2.5.2)
476
- # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
477
- # directives with Windows paths as filenames (..\..\dir\file)
478
- # For the same reason, decimal_escape allows all digit sequences. We want to
479
- # parse all correct code, even if it means to sometimes parse incorrect
480
- # code.
476
+ # Character constants and escape sequences.
481
477
  #
482
- # The original regexes were taken verbatim from the C syntax definition,
483
- # and were later modified to avoid worst-case exponential running time.
478
+ # Tokens preserve their source spelling; the lexer does not decode escapes or
479
+ # validate Unicode code points. Each complete escape must match as one character
480
+ # in _cconst_char, which is used for both single and multicharacter constants.
481
+ # Matching is deliberately permissive: simple escapes accept extra letters and
482
+ # punctuation, and numeric escapes accept decimal digits, not just octal digits.
484
483
  #
485
- # simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
486
- # decimal_escape = r"""(\d+)"""
487
- # hex_escape = r"""(x[0-9a-fA-F]+)"""
488
- # bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
489
- #
490
- # The following modifications were made to avoid the ambiguity that allowed
491
- # backtracking: (https://github.com/eliben/pycparser/issues/61)
492
- #
493
- # - \x was removed from simple_escape, unless it was not followed by a hex
494
- # digit, to avoid ambiguity with hex_escape.
495
- # - hex_escape allows one or more hex characters, but requires that the next
496
- # character(if any) is not hex
497
- # - decimal_escape allows one or more decimal characters, but requires that the
498
- # next character(if any) is not a decimal
499
- # - bad_escape does not allow any decimals (8-9), to avoid conflicting with the
500
- # permissive decimal_escape.
501
- #
502
- # Without this change, python's `re` module would recursively try parsing each
503
- # ambiguous escape sequence in multiple ways. e.g. `\123` could be parsed as
504
- # `\1`+`23`, `\12`+`3`, and `\123`.
505
-
506
- _simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))"""
484
+ # _cconst_char is also repeated in the error patterns. If an escape can match in
485
+ # several ways, long malformed constants can cause exponential backtracking
486
+ # (https://github.com/eliben/pycparser/issues/61). The lookaheads prevent this:
487
+ # - Decimal and hexadecimal escapes consume the whole run of digits. For
488
+ # example, \123 cannot be split into \1 followed by the characters 2 and 3.
489
+ # - A simple \x escape is allowed only when no hexadecimal digit follows it.
490
+ # - A complete Unicode escape cannot fall back to a simple \u or \U escape.
491
+ # _bad_escape also excludes all decimal digits to agree with this permissiveness.
492
+
493
+ # Universal character names consume exactly four hex digits after u or eight
494
+ # after U. A following hex digit is a separate character, unlike with \x.
495
+ _unicode_escape = r"(u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})"
496
+ # This lookahead runs immediately after the backslash. If a complete Unicode
497
+ # escape follows, only _unicode_escape may match it: \u00e9 cannot be read as
498
+ # \u followed by 0, 0, e, 9. Incomplete forms retain the permissive behavior
499
+ # of simple escapes.
500
+ _simple_escape = (
501
+ rf"(?!{_unicode_escape})"
502
+ r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))"""
503
+ )
507
504
  _decimal_escape = r"""(\d+)(?!\d)"""
508
505
  _hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])"""
509
506
  _bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])"""
510
507
 
511
508
  _escape_sequence = (
512
- r"""(\\(""" + _simple_escape + "|" + _decimal_escape + "|" + _hex_escape + "))"
509
+ r"""(\\("""
510
+ + _simple_escape
511
+ + "|"
512
+ + _decimal_escape
513
+ + "|"
514
+ + _hex_escape
515
+ + "|"
516
+ + _unicode_escape
517
+ + "))"
513
518
  )
514
519
 
515
- # This complicated regex with lookahead might be slow for strings, so because
516
- # all of the valid escapes (including \x) allowed
517
- # 0 or more non-escaped characters after the first character,
518
- # simple_escape+decimal_escape+hex_escape got simplified to
519
-
520
+ # Strings do not need to count the characters represented by escapes. Match
521
+ # just the backslash and a permitted following character; _string_char consumes
522
+ # any remaining numeric or Unicode digits as ordinary text. This avoids matching
523
+ # complete escape sequences repeatedly. The permissive spellings also support
524
+ # Windows paths in #line filenames, which are matched with _string_literal.
520
525
  _escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])"""
521
526
 
522
527
  _cconst_char = r"""([^'\\\n]|""" + _escape_sequence + ")"
@@ -531,7 +536,7 @@ _bad_char_const = (
531
536
  r"""('""" + _cconst_char + """[^'\n]+')|('')|('""" + _bad_escape + r"""[^'\n]*')"""
532
537
  )
533
538
 
534
- # string literals (K&R2: A.2.6)
539
+ # String literals, including wide and Unicode prefixes.
535
540
  _string_char = r"""([^"\\\n]|""" + _escape_sequence_start_in_string + ")"
536
541
  _string_literal = '"' + _string_char + '*"'
537
542
  _wstring_literal = "L" + _string_literal