Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pycparser/c_parser.py: 83%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1491 statements  

1# ------------------------------------------------------------------------------ 

2# pycparser: c_parser.py 

3# 

4# Recursive-descent parser for the C language. 

5# 

6# Eli Bendersky [https://eli.thegreenplace.net/] 

7# License: BSD 

8# ------------------------------------------------------------------------------ 

9from collections.abc import Callable 

10from dataclasses import dataclass 

11from typing import ( 

12 Any, 

13 Literal, 

14 NoReturn, 

15 NotRequired, 

16 Optional, 

17 TypedDict, 

18 cast, 

19) 

20 

21from . import c_ast 

22from .ast_transforms import fix_atomic_specifiers, fix_switch_cases 

23from .c_lexer import CLexer, Dialect, Token 

24 

25 

26# ASTs contain many coordinates, so slots reduce their memory overhead. 

27@dataclass(slots=True) 

28class Coord: 

29 """Coordinates of a syntactic element. Consists of: 

30 - File name 

31 - Line number 

32 - Column number 

33 """ 

34 

35 file: str 

36 line: int 

37 column: int | None = None 

38 

39 def __str__(self) -> str: 

40 text = f"{self.file}:{self.line}" 

41 if self.column is not None: 

42 text += f":{self.column}" 

43 return text 

44 

45 

46class ParseError(Exception): 

47 pass 

48 

49 

50class CParser: 

51 """Recursive-descent C parser. 

52 

53 Usage: 

54 parser = CParser() 

55 ast = parser.parse(text, filename) 

56 

57 The `lexer` parameter lets you inject a lexer class (defaults to CLexer). 

58 The positional parameters other than `lexer` are accepted for backward 

59 compatibility with the old PLY-based parser and are otherwise unused. 

60 

61 The keyword-only `dialect` parameter selects keyword recognition: "legacy" 

62 preserves existing behavior, while "c23" enables C23 spellings, predefined 

63 constants, and other C23-only features. 

64 """ 

65 

66 def __init__( 

67 self, 

68 lex_optimize: bool = True, 

69 lexer: type[CLexer] = CLexer, 

70 lextab: str = "pycparser.lextab", 

71 yacc_optimize: bool = True, 

72 yacctab: str = "pycparser.yacctab", 

73 yacc_debug: bool = False, 

74 taboutputdir: str = "", 

75 *, 

76 dialect: Dialect = "legacy", 

77 ) -> None: 

78 self.clex: CLexer = lexer( 

79 error_func=self._lex_error_func, 

80 dialect=dialect, 

81 ) 

82 self._dialect = dialect 

83 

84 # Stack of scopes for keeping track of symbols. _scope_stack[-1] is 

85 # the current (topmost) scope. Each scope is a dictionary that 

86 # specifies whether a name is a type. If _scope_stack[n][name] is 

87 # True, 'name' is currently a type in the scope. If it's False, 

88 # 'name' is defined in the scope but not as a type (for instance, if we 

89 # saw: int name;) 

90 # If 'name' is not a key in _scope_stack[n] then 'name' was not defined 

91 # in this scope at all. 

92 self._scope_stack: list[dict[str, bool]] = [{}] 

93 # Cache the token already checked for typedef-name status to avoid repeated 

94 # lookups when peeking or consuming. Cleared when scopes or bindings change. 

95 self._typedef_checked_token: Token | None = None 

96 self._tokens: _TokenStream = _TokenStream(self.clex.token) 

97 

98 def parse( 

99 self, text: str, filename: str = "", debug: bool = False 

100 ) -> c_ast.FileAST: 

101 """Parses C code and returns an AST. 

102 

103 text: 

104 A string containing the C source code 

105 

106 filename: 

107 Name of the file being parsed (for meaningful error messages) 

108 

109 debug: 

110 Deprecated debug flag (unused); for backwards compatibility. 

111 """ 

112 self._scope_stack = [{}] 

113 self._typedef_checked_token = None 

114 self.clex.input(text, filename) 

115 self._tokens = _TokenStream(self.clex.token) 

116 

117 ast = self._parse_translation_unit_or_empty() 

118 tok = self._peek() 

119 if tok is not None: 

120 self._parse_error(f"before: {tok.value}", self._tok_coord(tok)) 

121 return ast 

122 

123 # ------------------------------------------------------------------ 

124 # Scope and declaration helpers 

125 # ------------------------------------------------------------------ 

126 def _parse_error(self, msg: str, coord: Coord | str | None) -> NoReturn: 

127 raise ParseError(f"{coord}: {msg}") 

128 

129 def _push_scope(self) -> None: 

130 self._scope_stack.append({}) 

131 self._typedef_checked_token = None 

132 

133 def _pop_scope(self) -> None: 

134 assert len(self._scope_stack) > 1, "Cannot pop the global scope" 

135 self._scope_stack.pop() 

136 self._typedef_checked_token = None 

137 

138 def _add_typedef_name(self, name: str, coord: Coord | None) -> None: 

139 """Add a new typedef name (ie a TYPEID) to the current scope""" 

140 if not self._scope_stack[-1].get(name, True): 

141 self._parse_error( 

142 f"Typedef {name!r} previously declared as non-typedef in this scope", 

143 coord, 

144 ) 

145 self._scope_stack[-1][name] = True 

146 self._typedef_checked_token = None 

147 

148 def _add_identifier(self, name: str, coord: Coord | None) -> None: 

149 """Add a new object, function, or enum member name (ie an ID) to the 

150 current scope 

151 """ 

152 if self._scope_stack[-1].get(name, False): 

153 self._parse_error( 

154 f"Non-typedef {name!r} previously declared as typedef in this scope", 

155 coord, 

156 ) 

157 self._scope_stack[-1][name] = False 

158 self._typedef_checked_token = None 

159 

160 def _is_type_in_scope(self, name: str) -> bool: 

161 """Is *name* a typedef-name in the current scope?""" 

162 for scope in reversed(self._scope_stack): 

163 # If name is an identifier in this scope it shadows typedefs in 

164 # higher scopes. 

165 if name in scope: 

166 return scope[name] 

167 return False 

168 

169 def _lex_error_func(self, msg: str, line: int, column: int) -> None: 

170 self._parse_error(msg, Coord(file=self.clex.filename, line=line, column=column)) 

171 

172 # To understand what's going on here, read sections A.8.5 and 

173 # A.8.6 of K&R2 very carefully. 

174 # 

175 # A C type consists of a basic type declaration, with a list 

176 # of modifiers. For example: 

177 # 

178 # int *c[5]; 

179 # 

180 # The basic declaration here is 'int c', and the pointer and 

181 # the array are the modifiers. 

182 # 

183 # Basic declarations are represented by TypeDecl (from module c_ast) and the 

184 # modifiers are FuncDecl, PtrDecl and ArrayDecl. 

185 # 

186 # The standard states that whenever a new modifier is parsed, it should be 

187 # added to the end of the list of modifiers. For example: 

188 # 

189 # K&R2 A.8.6.2: Array Declarators 

190 # 

191 # In a declaration T D where D has the form 

192 # D1 [constant-expression-opt] 

193 # and the type of the identifier in the declaration T D1 is 

194 # "type-modifier T", the type of the 

195 # identifier of D is "type-modifier array of T" 

196 # 

197 # This is what this method does. The declarator it receives 

198 # can be a list of declarators ending with TypeDecl. It 

199 # tacks the modifier to the end of this list, just before 

200 # the TypeDecl. 

201 # 

202 # Additionally, the modifier may be a list itself. This is 

203 # useful for pointers, that can come as a chain from the rule 

204 # p_pointer. In this case, the whole modifier list is spliced 

205 # into the new location. 

206 def _type_modify_decl(self, decl: Any, modifier: Any) -> c_ast.Node: 

207 """Tacks a type modifier on a declarator, and returns 

208 the modified declarator. 

209 

210 Note: the declarator and modifier may be modified 

211 """ 

212 modifier_head = modifier 

213 modifier_tail = modifier 

214 

215 # The modifier may be a nested list. Reach its tail. 

216 while modifier_tail.type: 

217 modifier_tail = modifier_tail.type 

218 

219 # If the decl is a basic type, just tack the modifier onto it. 

220 if isinstance(decl, c_ast.TypeDecl): 

221 modifier_tail.type = decl 

222 return modifier 

223 else: 

224 # Otherwise, the decl is a list of modifiers. Reach 

225 # its tail and splice the modifier onto the tail, 

226 # pointing to the underlying basic type. 

227 decl_tail = decl 

228 while not isinstance(decl_tail.type, c_ast.TypeDecl): 

229 decl_tail = decl_tail.type 

230 

231 modifier_tail.type = decl_tail.type 

232 decl_tail.type = modifier_head 

233 return decl 

234 

235 # Due to the order in which declarators are constructed, 

236 # they have to be fixed in order to look like a normal AST. 

237 # 

238 # When a declaration arrives from syntax construction, it has 

239 # these problems: 

240 # * The innermost TypeDecl has no type (because the basic 

241 # type is only known at the uppermost declaration level) 

242 # * The declaration has no variable name, since that is saved 

243 # in the innermost TypeDecl 

244 # * The typename of the declaration is a list of type 

245 # specifiers, and not a node. Here, basic identifier types 

246 # should be separated from more complex types like enums 

247 # and structs. 

248 # 

249 # This method fixes these problems. 

250 def _fix_decl_name_type( 

251 self, 

252 decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename, 

253 typename: list[c_ast.Node], 

254 ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename: 

255 """Fixes a declaration. Modifies decl.""" 

256 # Reach the underlying basic type 

257 typ = decl 

258 while not isinstance(typ, c_ast.TypeDecl): 

259 typ = typ.type 

260 

261 decl.name = typ.declname 

262 typ.quals = decl.quals[:] 

263 

264 # The typename is a list of types. Except for _BitInt with an 

265 # optional signedness, any type that isn't an IdentifierType must 

266 # be the only type in the list (it's illegal to declare "int enum .."). 

267 # If all the types are basic, they're collected in the 

268 # IdentifierType holder. 

269 names: list[str] = [] 

270 for tn in typename: 

271 if isinstance(tn, c_ast.BitIntType): 

272 signedness = None 

273 for spec in typename: 

274 if spec is tn: 

275 continue 

276 if ( 

277 signedness is not None 

278 or not isinstance(spec, c_ast.IdentifierType) 

279 or spec.names not in (["signed"], ["unsigned"]) 

280 ): 

281 self._parse_error( 

282 "Invalid multiple types specified", spec.coord 

283 ) 

284 signedness = spec.names[0] 

285 # Specifiers are shared by comma-separated declarations; 

286 # keep the parsed specifier unchanged when attaching the sign. 

287 typ.type = c_ast.BitIntType(tn.width, signedness, tn.coord) 

288 return decl 

289 

290 if not isinstance(tn, c_ast.IdentifierType): 

291 if len(typename) > 1: 

292 self._parse_error("Invalid multiple types specified", tn.coord) 

293 else: 

294 typ.type = tn 

295 # Normalize atomic specifiers for declarations and type 

296 # names alike (casts, sizeof, unnamed parameters, etc.). 

297 return fix_atomic_specifiers(decl) 

298 names.extend(tn.names) 

299 

300 if not typename: 

301 # Functions default to returning int 

302 if not isinstance(decl.type, c_ast.FuncDecl): 

303 self._parse_error("Missing type in declaration", decl.coord) 

304 typ.type = c_ast.IdentifierType(["int"], coord=decl.coord) 

305 else: 

306 typ.type = c_ast.IdentifierType(names, coord=typename[0].coord) 

307 return decl 

308 

309 def _add_declaration_specifier( 

310 self, 

311 declspec: Optional["_DeclSpec"], 

312 newspec: str | c_ast.Node, 

313 kind: "_DeclSpecKind", 

314 ) -> "_DeclSpec": 

315 """Append a specifier in source order; see _DeclSpec for the layout.""" 

316 if declspec is None: 

317 spec: _DeclSpec = { 

318 "qual": [], 

319 "storage": [], 

320 "type": [], 

321 "function": [], 

322 "alignment": [], 

323 "type_attributes": [], 

324 } 

325 else: 

326 spec = declspec 

327 

328 if kind == "type": 

329 assert isinstance(newspec, c_ast.Node) 

330 spec[kind].append(newspec) 

331 elif kind == "alignment": 

332 assert isinstance(newspec, c_ast.Alignas) 

333 spec[kind].append(newspec) 

334 else: 

335 assert isinstance(newspec, str) 

336 spec[kind].append(newspec) 

337 return spec 

338 

339 def _build_type_name( 

340 self, spec: "_DeclSpec", decl: c_ast.Node | None, coord: Coord | None 

341 ) -> c_ast.Typename: 

342 """Build and normalize an abstract type, including its attributes. 

343 

344 Callers select the coordinate according to their grammar context. 

345 The name, if any, is taken from the declarator by _fix_decl_name_type. 

346 """ 

347 typename = c_ast.Typename( 

348 name=None, 

349 quals=spec["qual"][:], 

350 align=None, 

351 type=decl or c_ast.TypeDecl(None, None, None, None), 

352 coord=coord, 

353 ) 

354 return cast( 

355 c_ast.Typename, 

356 self._attach_type_attributes( 

357 self._fix_decl_name_type(typename, spec["type"]), spec 

358 ), 

359 ) 

360 

361 def _build_declarations( 

362 self, 

363 spec: "_DeclSpec", 

364 decls: list["_DeclInfo"], 

365 typedef_namespace: bool = False, 

366 ) -> list[c_ast.Node]: 

367 """Builds a list of declarations all sharing the given specifiers. 

368 If typedef_namespace is true, each declared name is added 

369 to the "typedef namespace", which also includes objects, 

370 functions, and enum constants. 

371 """ 

372 is_typedef = "typedef" in spec["storage"] 

373 declarations = [] 

374 

375 # Bit-fields are allowed to be unnamed. 

376 if decls[0].get("bitsize") is None: 

377 # When redeclaring typedef names as identifiers in inner scopes, a 

378 # problem can occur where the identifier gets grouped into 

379 # spec['type'], leaving decl as None. This can only occur for the 

380 # first declarator. 

381 if decls[0]["decl"] is None: 

382 if ( 

383 len(spec["type"]) < 2 

384 or not isinstance(spec["type"][-1], c_ast.IdentifierType) 

385 or len(spec["type"][-1].names) != 1 

386 or not self._is_type_in_scope(spec["type"][-1].names[0]) 

387 ): 

388 coord = "?" 

389 for t in spec["type"]: 

390 if hasattr(t, "coord"): 

391 coord = t.coord 

392 break 

393 self._parse_error("Invalid declaration", coord) 

394 

395 # Make this look as if it came from "direct_declarator:ID" 

396 decls[0]["decl"] = c_ast.TypeDecl( 

397 declname=spec["type"][-1].names[0], 

398 type=None, 

399 quals=None, 

400 align=spec["alignment"], 

401 coord=spec["type"][-1].coord, 

402 ) 

403 # Remove the "new" type's name from the end of spec['type'] 

404 del spec["type"][-1] 

405 # A similar problem can occur where the declaration ends up 

406 # looking like an abstract declarator. Give it a name if this is 

407 # the case. 

408 elif isinstance(decls[0]["decl"], _DECLARATOR_NODES): 

409 decls_0_tail = cast(Any, decls[0]["decl"]) 

410 while not isinstance(decls_0_tail, c_ast.TypeDecl): 

411 decls_0_tail = decls_0_tail.type 

412 if decls_0_tail.declname is None: 

413 type_name = cast(c_ast.IdentifierType, spec["type"][-1]) 

414 decls_0_tail.declname = type_name.names[0] 

415 del spec["type"][-1] 

416 

417 for decl in decls: 

418 assert decl["decl"] is not None 

419 if is_typedef: 

420 declaration = c_ast.Typedef( 

421 name=None, 

422 quals=spec["qual"], 

423 storage=spec["storage"], 

424 type=decl["decl"], 

425 coord=decl["decl"].coord, 

426 ) 

427 else: 

428 declaration = c_ast.Decl( 

429 name=None, 

430 quals=spec["qual"], 

431 align=spec["alignment"], 

432 storage=spec["storage"], 

433 funcspec=spec["function"], 

434 type=decl["decl"], 

435 init=decl.get("init"), 

436 bitsize=decl.get("bitsize"), 

437 coord=decl["decl"].coord, 

438 ) 

439 

440 if not isinstance(declaration.type, _DECLARATOR_NODES): 

441 fixed_decl = declaration 

442 else: 

443 fixed_decl = self._fix_decl_name_type(declaration, spec["type"]) 

444 

445 # Record the declared name in the current scope so the parser can 

446 # distinguish identifiers from typedef names. 

447 if typedef_namespace: 

448 if is_typedef: 

449 self._add_typedef_name(fixed_decl.name, fixed_decl.coord) 

450 else: 

451 self._add_identifier(fixed_decl.name, fixed_decl.coord) 

452 

453 declarations.append(self._attach_type_attributes(fixed_decl, spec)) 

454 

455 return declarations 

456 

457 def _build_function_definition( 

458 self, 

459 spec: "_DeclSpec", 

460 decl: c_ast.Node, 

461 param_decls: list[c_ast.Node] | None, 

462 body: c_ast.Node, 

463 ) -> c_ast.Node: 

464 """Builds a function definition.""" 

465 if "typedef" in spec["storage"]: 

466 self._parse_error("Invalid typedef", decl.coord) 

467 

468 declaration = self._build_declarations( 

469 spec=spec, 

470 decls=[{"decl": decl}], 

471 typedef_namespace=True, 

472 )[0] 

473 

474 return c_ast.FuncDef( 

475 decl=declaration, param_decls=param_decls, body=body, coord=decl.coord 

476 ) 

477 

478 def _select_struct_union_class(self, token: str) -> type: 

479 """Given a token (either STRUCT or UNION), selects the 

480 appropriate AST class. 

481 """ 

482 if token == "struct": 

483 return c_ast.Struct 

484 else: 

485 return c_ast.Union 

486 

487 # ------------------------------------------------------------------ 

488 # Token helpers 

489 # ------------------------------------------------------------------ 

490 def _peek(self, k: int = 1) -> Token | None: 

491 """Return the k-th next token without consuming it (1-based).""" 

492 tok = self._tokens.peek(k) 

493 if tok is not self._typedef_checked_token: 

494 self._classify_identifier(tok) 

495 return tok 

496 

497 def _classify_identifier(self, tok: Token | None) -> None: 

498 # Buffered identifiers are interpreted in the current scope, including 

499 # after backtracking. Peeking and consuming can reuse the classification 

500 # until a scope or binding changes. 

501 if tok is not None and tok.type in {"ID", "TYPEID"}: 

502 tok.type = "TYPEID" if self._is_type_in_scope(tok.value) else "ID" 

503 self._typedef_checked_token = tok 

504 

505 def _peek_type(self, k: int = 1) -> str | None: 

506 """Return the type of the k-th next token, or None if absent (1-based).""" 

507 tok = self._peek(k) 

508 return tok.type if tok is not None else None 

509 

510 def _advance(self) -> Token: 

511 tok = self._tokens.next() 

512 if tok is None: 

513 self._parse_error("At end of input", self.clex.filename) 

514 if tok is not self._typedef_checked_token: 

515 self._classify_identifier(tok) 

516 return tok 

517 

518 def _accept(self, token_type: str) -> Token | None: 

519 """Conditionally consume next token, only if it's of token_type. 

520 

521 If it is of the expected type, consume and return it. 

522 Otherwise, leaves the token intact and returns None. 

523 """ 

524 tok = self._peek() 

525 if tok is not None and tok.type == token_type: 

526 return self._advance() 

527 return None 

528 

529 def _expect(self, token_type: str) -> Token: 

530 tok = self._advance() 

531 if tok.type != token_type: 

532 self._parse_error(f"before: {tok.value}", self._tok_coord(tok)) 

533 return tok 

534 

535 def _mark(self) -> int: 

536 return self._tokens.mark() 

537 

538 def _reset(self, mark: int) -> None: 

539 self._tokens.reset(mark) 

540 

541 def _tok_coord(self, tok: Token) -> Coord: 

542 return Coord(file=tok.filename, line=tok.lineno, column=tok.column) 

543 

544 def _starts_declaration(self, tok: Token | None = None) -> bool: 

545 tok = tok or self._peek() 

546 if tok is None: 

547 return False 

548 return tok.type in _DECL_START 

549 

550 def _starts_expression(self, tok: Token | None = None) -> bool: 

551 tok = tok or self._peek() 

552 if tok is None: 

553 return False 

554 return tok.type in _STARTS_EXPRESSION 

555 

556 def _starts_statement(self) -> bool: 

557 if self._peek_type() == "ATTRIBUTE_BEGIN": 

558 mark = self._mark() 

559 self._parse_attribute_specifier_sequence() 

560 starts = not self._starts_declaration() 

561 self._reset(mark) 

562 return starts 

563 tok_type = self._peek_type() 

564 if tok_type is None: 

565 return False 

566 if tok_type in _STARTS_STATEMENT: 

567 return True 

568 return self._starts_expression() 

569 

570 def _starts_declarator(self, id_only: bool = False) -> bool: 

571 tok_type = self._peek_type() 

572 if tok_type is None: 

573 return False 

574 if tok_type in {"TIMES", "LPAREN"}: 

575 return True 

576 if id_only: 

577 return tok_type == "ID" 

578 return tok_type in {"ID", "TYPEID"} 

579 

580 def _peek_declarator_name_info(self) -> tuple[str | None, bool]: 

581 mark = self._mark() 

582 tok_type, saw_paren = self._scan_declarator_name_info() 

583 self._reset(mark) 

584 return tok_type, saw_paren 

585 

586 def _parse_any_declarator( 

587 self, allow_abstract: bool = False, typeid_paren_as_abstract: bool = False 

588 ) -> tuple[c_ast.Node | None, bool]: 

589 # C declarators are ambiguous without lookahead. For example: 

590 # int foo(int (aa)); -> aa is a name (ID) 

591 # typedef char TT; 

592 # int bar(int (TT)); -> TT is a type (TYPEID) in parens 

593 name_type, saw_paren = self._peek_declarator_name_info() 

594 if name_type is None or ( 

595 typeid_paren_as_abstract and name_type == "TYPEID" and saw_paren 

596 ): 

597 if not allow_abstract: 

598 tok = self._peek() 

599 coord = self._tok_coord(tok) if tok is not None else self.clex.filename 

600 self._parse_error("Invalid declarator", coord) 

601 decl = self._parse_abstract_declarator_opt() 

602 return decl, False 

603 

604 if name_type == "TYPEID": 

605 if typeid_paren_as_abstract: 

606 decl = self._parse_typeid_noparen_declarator() 

607 else: 

608 decl = self._parse_typeid_declarator() 

609 else: 

610 decl = self._parse_id_declarator() 

611 return decl, True 

612 

613 def _scan_declarator_name_info(self) -> tuple[str | None, bool]: 

614 saw_paren = False 

615 while self._accept("TIMES"): 

616 self._parse_attribute_specifier_sequence() 

617 while self._peek_type() in _TYPE_QUALIFIER: 

618 self._advance() 

619 

620 tok = self._peek() 

621 if tok is None: 

622 return None, saw_paren 

623 if tok.type in {"ID", "TYPEID"}: 

624 self._advance() 

625 return tok.type, saw_paren 

626 if tok.type == "LPAREN": 

627 saw_paren = True 

628 self._advance() 

629 tok_type, nested_paren = self._scan_declarator_name_info() 

630 if nested_paren: 

631 saw_paren = True 

632 depth = 1 

633 while True: 

634 tok = self._peek() 

635 if tok is None: 

636 return None, saw_paren 

637 if tok.type == "LPAREN": 

638 depth += 1 

639 elif tok.type == "RPAREN": 

640 depth -= 1 

641 self._advance() 

642 if depth == 0: 

643 break 

644 continue 

645 self._advance() 

646 return tok_type, saw_paren 

647 return None, saw_paren 

648 

649 def _starts_direct_abstract_declarator(self) -> bool: 

650 return self._peek_type() in {"LPAREN", "LBRACKET"} 

651 

652 def _is_assignment_op(self) -> bool: 

653 tok = self._peek() 

654 return tok is not None and tok.type in _ASSIGNMENT_OPS 

655 

656 def _try_parse_paren_type_name( 

657 self, 

658 ) -> tuple[c_ast.Typename, int, Token, list[str]] | None: 

659 """Parse and return a parenthesized type name if present. 

660 

661 Returns (typ, mark, lparen_tok, storage) when the next tokens look like 

662 '(' type_name ')', where typ is the parsed type name, mark is the 

663 token-stream position before parsing, and lparen_tok is the LPAREN 

664 token. C23 storage specifiers may precede the type name, but require a 

665 following '{' for a compound literal. Returns None if no parenthesized 

666 type name is present. 

667 """ 

668 lparen_tok = self._peek() 

669 # Avoid saving a backtracking position when there's no '(' to parse. 

670 if lparen_tok is None or lparen_tok.type != "LPAREN": 

671 return None 

672 mark = self._mark() 

673 self._advance() 

674 if not self._starts_declaration(): 

675 self._reset(mark) 

676 return None 

677 storage = [] 

678 while self._peek_type() in _STORAGE_CLASS: 

679 tok = self._advance() 

680 if self._dialect != "c23": 

681 self._parse_error( 

682 "Storage class specifiers in compound literals require C23", 

683 self._tok_coord(tok), 

684 ) 

685 storage.append(tok.value) 

686 typ = self._parse_type_name() 

687 if self._accept("RPAREN") is None: 

688 self._reset(mark) 

689 return None 

690 if storage and self._peek_type() != "LBRACE": 

691 self._parse_error( 

692 "Storage class specifiers require a compound literal initializer", 

693 self._tok_coord(lparen_tok), 

694 ) 

695 return typ, mark, lparen_tok, storage 

696 

697 # ------------------------------------------------------------------ 

698 # Top-level 

699 # ------------------------------------------------------------------ 

700 def _parse_attribute_specifier_sequence(self) -> list[c_ast.AttributeSpecifier]: 

701 groups = [] 

702 while self._peek_type() == "ATTRIBUTE_BEGIN": 

703 opening = self._advance() 

704 if self._dialect != "c23": 

705 self._parse_error("Attributes require C23", self._tok_coord(opening)) 

706 attributes = [] 

707 while self._peek_type() != "ATTRIBUTE_END": 

708 if self._accept("COMMA"): 

709 continue 

710 name = self._parse_identifier_or_typeid() 

711 namespace = None 

712 if self._accept("COLONCOLON"): 

713 namespace = name.name 

714 name = self._parse_identifier_or_typeid() 

715 arguments = self._accept("ATTRIBUTE_ARGUMENTS") 

716 attributes.append( 

717 c_ast.Attribute( 

718 name.name, 

719 namespace, 

720 arguments.value if arguments is not None else None, 

721 name.coord, 

722 ) 

723 ) 

724 if not self._accept("COMMA"): 

725 break 

726 self._expect("ATTRIBUTE_END") 

727 groups.append( 

728 c_ast.AttributeSpecifier(attributes, self._tok_coord(opening)) 

729 ) 

730 return groups 

731 

732 def _attribute_declarations( 

733 self, nodes: list[c_ast.Node], attributes: list[c_ast.AttributeSpecifier] 

734 ) -> list[c_ast.Node]: 

735 """Attach declaration prefixes, shared by comma-separated declarators.""" 

736 if not nodes or isinstance(nodes[0], c_ast.Pragma): 

737 self._parse_error( 

738 "Expected declaration after attributes", attributes[0].coord 

739 ) 

740 for node in nodes: 

741 # Trailing pragmas belong to the surrounding list, not to the 

742 # declaration carrying these attributes. 

743 if isinstance(node, c_ast.Pragma): 

744 continue 

745 decl = node.decl if isinstance(node, c_ast.FuncDef) else node 

746 if not isinstance(decl, (c_ast.Decl, c_ast.Typedef)): 

747 self._parse_error( 

748 "Expected declaration after attributes", attributes[0].coord 

749 ) 

750 decl.attributes = attributes 

751 return nodes 

752 

753 def _attribute_statement( 

754 self, stmt: c_ast.Node, attributes: list[c_ast.AttributeSpecifier] 

755 ) -> c_ast.Node: 

756 if isinstance(stmt, (c_ast.Label, c_ast.Case, c_ast.Default)): 

757 stmt.attributes = attributes 

758 return stmt 

759 return c_ast.AttributedStatement(attributes, stmt, attributes[0].coord) 

760 

761 def _attach_type_attributes( 

762 self, decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename, spec: "_DeclSpec" 

763 ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename: 

764 """Keep attributes after the base type, inside pointer/array modifiers.""" 

765 if spec["type_attributes"]: 

766 target = decl 

767 while isinstance( 

768 target.type, 

769 (c_ast.PtrDecl, c_ast.ArrayDecl, c_ast.FuncDecl, c_ast.TypeDecl), 

770 ): 

771 target = target.type 

772 target.type = c_ast.AttributedType( 

773 target.type, spec["type_attributes"], target.type.coord 

774 ) 

775 return decl 

776 

777 # BNF: translation_unit_or_empty : translation_unit | empty 

778 def _parse_translation_unit_or_empty(self) -> c_ast.FileAST: 

779 if self._peek() is None: 

780 return c_ast.FileAST([]) 

781 return c_ast.FileAST(self._parse_translation_unit()) 

782 

783 # BNF: translation_unit : external_declaration+ 

784 def _parse_translation_unit(self) -> list[c_ast.Node]: 

785 ext = [] 

786 while self._peek() is not None: 

787 ext.extend(self._parse_external_declaration()) 

788 return ext 

789 

790 # BNF: external_declaration : function_definition 

791 # | declaration 

792 # | pp_directive 

793 # | pppragma_directive 

794 # | static_assert 

795 # | ';' 

796 def _parse_external_declaration(self) -> list[c_ast.Node]: 

797 if self._peek_type() == "ATTRIBUTE_BEGIN": 

798 attributes = self._parse_attribute_specifier_sequence() 

799 if self._accept("SEMI"): 

800 return [c_ast.AttributeDeclaration(attributes, attributes[0].coord)] 

801 return self._attribute_declarations( 

802 self._parse_external_declaration(), attributes 

803 ) 

804 tok = self._peek() 

805 if tok is None: 

806 return [] 

807 if tok.type == "PPHASH": 

808 self._parse_pp_directive() 

809 return [] 

810 if tok.type in {"PPPRAGMA", "_PRAGMA"}: 

811 return [self._parse_pppragma_directive()] 

812 if self._accept("SEMI"): 

813 return [] 

814 if tok.type == "_STATIC_ASSERT": 

815 return [self._parse_static_assert()] 

816 

817 if not self._starts_declaration(tok): 

818 # Special handling for old-style function definitions that have an 

819 # implicit return type, e.g. 

820 # 

821 # foo() { 

822 # return 5; 

823 # } 

824 # 

825 # These get an implicit 'int' return type. 

826 decl = self._parse_id_declarator() 

827 if self._peek_type() != "LBRACE": 

828 self._parse_error("Invalid function definition", decl.coord) 

829 spec: _DeclSpec = { 

830 "qual": [], 

831 "alignment": [], 

832 "storage": [], 

833 "type": [c_ast.IdentifierType(["int"], coord=decl.coord)], 

834 "function": [], 

835 "type_attributes": [], 

836 } 

837 return [self._parse_function_definition(spec, decl)] 

838 

839 # From here on, parsing a standard declatation/definition. 

840 spec, saw_type, spec_coord = self._parse_declaration_specifiers( 

841 allow_no_type=True 

842 ) 

843 if self._is_inferred_auto(spec): 

844 decls = self._parse_auto_declaration(spec) 

845 return self._parse_declaration_end(decls) 

846 

847 name_type, _ = self._peek_declarator_name_info() 

848 if name_type != "ID": 

849 decls = self._parse_decl_body_with_spec(spec, saw_type) 

850 return self._parse_declaration_end(decls) 

851 

852 decl = self._parse_id_declarator() 

853 

854 if self._peek_type() == "LBRACE" or self._starts_declaration(): 

855 if not spec["type"]: 

856 spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)] 

857 return [self._parse_function_definition(spec, decl)] 

858 

859 decl_dict: _DeclInfo = {"decl": decl} 

860 if self._accept("EQUALS"): 

861 decl_dict["init"] = self._parse_initializer() 

862 decls = self._parse_init_declarator_list(first=decl_dict) 

863 decls = self._build_declarations(spec=spec, decls=decls, typedef_namespace=True) 

864 return self._parse_declaration_end(decls) 

865 

866 def _parse_function_definition( 

867 self, spec: "_DeclSpec", decl: c_ast.Node 

868 ) -> c_ast.Node: 

869 # Open the scope shared by parameters, old-style parameter declarations, 

870 # and the outermost body. 

871 self._push_scope() 

872 if isinstance(decl, c_ast.FuncDecl) and decl.args is not None: 

873 for param in decl.args.params: 

874 name = getattr(param, "name", None) 

875 if name: 

876 self._add_identifier(name, param.coord) 

877 param_decls = ( 

878 self._parse_declaration_list() if self._starts_declaration() else None 

879 ) 

880 if self._peek_type() != "LBRACE": 

881 self._parse_error("Invalid function definition", decl.coord) 

882 body = self._parse_compound_statement(new_scope=False) 

883 self._pop_scope() 

884 return self._build_function_definition(spec, decl, param_decls, body) 

885 

886 # ------------------------------------------------------------------ 

887 # Declarations 

888 # 

889 # Declarations always come as lists (because they can be several in one 

890 # line). When returning parsed declarations, a list is always returned - 

891 # even if it contains a single element. Pragmas before the final semicolon 

892 # follow the declarations in that list. 

893 # ------------------------------------------------------------------ 

894 # BNF: declaration : decl_body pppragma_directive* ';' | static_assert 

895 def _parse_declaration(self) -> list[c_ast.Node]: 

896 if self._peek_type() == "ATTRIBUTE_BEGIN": 

897 attributes = self._parse_attribute_specifier_sequence() 

898 if self._accept("SEMI"): 

899 return [c_ast.AttributeDeclaration(attributes, attributes[0].coord)] 

900 return self._attribute_declarations(self._parse_declaration(), attributes) 

901 if self._peek_type() == "_STATIC_ASSERT": 

902 return [self._parse_static_assert()] 

903 decls = self._parse_decl_body() 

904 return self._parse_declaration_end(decls) 

905 

906 def _parse_declaration_end(self, decls: list[c_ast.Node]) -> list[c_ast.Node]: 

907 """Consume a declaration's semicolon and retain preceding pragmas. 

908 

909 Macro expansions can place diagnostic pragmas just before the final 

910 semicolon (issue #478). Represent them after the completed declarations 

911 in the surrounding list, just like pragmas following the semicolon. 

912 """ 

913 pragmas = self._parse_pppragma_directive_list() 

914 self._expect("SEMI") 

915 return decls + pragmas 

916 

917 # BNF: decl_body : declaration_specifiers decl_body_with_spec 

918 def _parse_decl_body(self) -> list[c_ast.Node]: 

919 spec, saw_type, _ = self._parse_declaration_specifiers(allow_no_type=True) 

920 return self._parse_decl_body_with_spec(spec, saw_type) 

921 

922 # BNF: decl_body_with_spec : init_declarator_list 

923 # | struct_or_union_or_enum_only 

924 def _parse_decl_body_with_spec( 

925 self, spec: "_DeclSpec", saw_type: bool 

926 ) -> list[c_ast.Node]: 

927 if self._is_inferred_auto(spec): 

928 return self._parse_auto_declaration(spec) 

929 

930 # saw_type is True if the specifiers included an actual type (as 

931 # opposed to only storage/function/qualifiers). 

932 decl_infos: list[_DeclInfo] | None = None 

933 if saw_type: 

934 if self._starts_declarator(): 

935 decl_infos = self._parse_init_declarator_list() 

936 else: 

937 if self._starts_declarator(id_only=True): 

938 decl_infos = self._parse_init_declarator_list(id_only=True) 

939 

940 decls: list[c_ast.Node] 

941 if decl_infos is None: 

942 ty = spec["type"] 

943 s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum) 

944 if len(ty) == 1 and isinstance(ty[0], s_u_or_e): 

945 decl = c_ast.Decl( 

946 name=None, 

947 quals=spec["qual"], 

948 align=spec["alignment"], 

949 storage=spec["storage"], 

950 funcspec=spec["function"], 

951 type=ty[0], 

952 init=None, 

953 bitsize=None, 

954 coord=ty[0].coord, 

955 ) 

956 decls = [self._attach_type_attributes(decl, spec)] 

957 else: 

958 decls = self._build_declarations( 

959 spec=spec, 

960 decls=[{"decl": None}], 

961 typedef_namespace=True, 

962 ) 

963 else: 

964 decls = self._build_declarations( 

965 spec=spec, decls=decl_infos, typedef_namespace=True 

966 ) 

967 

968 return decls 

969 

970 def _is_inferred_auto(self, spec: "_DeclSpec") -> bool: 

971 return self._dialect == "c23" and "auto" in spec["storage"] and not spec["type"] 

972 

973 # C23 type inference uses one plain (possibly parenthesized) name and an 

974 # expression initializer. Preserve its syntax without resolving the type. 

975 def _parse_auto_declaration(self, spec: "_DeclSpec") -> list[c_ast.Node]: 

976 decl = self._parse_id_declarator() 

977 if not isinstance(decl, c_ast.TypeDecl): 

978 self._parse_error( 

979 "Inferred auto declaration requires a plain identifier", decl.coord 

980 ) 

981 self._expect("EQUALS") 

982 init = self._parse_assignment_expression() 

983 spec["type"] = [c_ast.InferredType(coord=decl.coord)] 

984 # Register the name only after parsing the initializer. 

985 return self._build_declarations( 

986 spec=spec, 

987 decls=[{"decl": decl, "init": init}], 

988 typedef_namespace=True, 

989 ) 

990 

991 # BNF: declaration_list : (declaration pppragma_directive*)+ 

992 def _parse_declaration_list(self) -> list[c_ast.Node]: 

993 decls = [] 

994 while self._starts_declaration(): 

995 decls.extend(self._parse_declaration()) 

996 # K&R parameter declarations retain pragmas on either side of 

997 # their semicolons, including when reparsing generated code. 

998 decls.extend(self._parse_pppragma_directive_list()) 

999 return decls 

1000 

1001 # BNF: declaration_specifiers : (storage_class_specifier 

1002 # | type_specifier 

1003 # | type_qualifier 

1004 # | function_specifier 

1005 # | alignment_specifier)+ 

1006 def _parse_declaration_specifiers( 

1007 self, allow_no_type: bool = False 

1008 ) -> tuple["_DeclSpec", bool, Coord | None]: 

1009 """Parse declaration-specifier sequence. 

1010 

1011 allow_no_type: 

1012 If True, allow a missing type specifier without error. 

1013 

1014 Returns: 

1015 (spec, saw_type, first_coord) where spec is a dict with 

1016 qual/storage/type/function/alignment/type_attributes entries, saw_type is True 

1017 if a type specifier was consumed, and first_coord is the coord 

1018 of the first specifier token (used for diagnostics). 

1019 """ 

1020 spec, saw_type, first_coord = self._parse_specifiers( 

1021 allow_storage_and_function=True 

1022 ) 

1023 if spec is None: 

1024 self._parse_error("Invalid declaration", self.clex.filename) 

1025 if not saw_type and not allow_no_type: 

1026 self._parse_error("Missing type in declaration", first_coord) 

1027 return spec, saw_type, first_coord 

1028 

1029 # BNF: specifier_qualifier_list : (type_specifier 

1030 # | type_qualifier 

1031 # | alignment_specifier)+ 

1032 def _parse_specifier_qualifier_list(self) -> "_DeclSpec": 

1033 spec, saw_type, first_coord = self._parse_specifiers( 

1034 allow_storage_and_function=False 

1035 ) 

1036 if spec is None: 

1037 self._parse_error("Invalid specifier list", self.clex.filename) 

1038 if not saw_type and not spec["alignment"]: 

1039 self._parse_error("Missing type in declaration", first_coord) 

1040 return spec 

1041 

1042 def _parse_specifiers( 

1043 self, *, allow_storage_and_function: bool 

1044 ) -> tuple["_DeclSpec | None", bool, Coord | None]: 

1045 """Consume specifiers shared by declarations, members, and type names. 

1046 

1047 Storage classes and function specifiers are only accepted in 

1048 declarations. Callers handle their own missing-specifier diagnostics. 

1049 """ 

1050 spec = None 

1051 saw_type = False 

1052 first_coord = None 

1053 

1054 while True: 

1055 tok = self._peek() 

1056 if tok is None: 

1057 break 

1058 

1059 if tok.type == "ATTRIBUTE_BEGIN" and spec is not None: 

1060 spec["type_attributes"] = self._parse_attribute_specifier_sequence() 

1061 break 

1062 

1063 # Once a type has been seen, a typedef name can be the declarator 

1064 # (e.g. the second T in "T T"), rather than another type specifier. 

1065 if tok.type == "TYPEID" and saw_type: 

1066 break 

1067 

1068 # _Atomic is a type specifier with parentheses, a qualifier without. 

1069 is_atomic_specifier = ( 

1070 tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN" 

1071 ) 

1072 if first_coord is None: 

1073 first_coord = self._tok_coord(tok) 

1074 

1075 kind: _DeclSpecKind = "type" 

1076 newspec: str | c_ast.Node 

1077 if tok.type == "_ALIGNAS": 

1078 kind = "alignment" 

1079 newspec = self._parse_alignment_specifier() 

1080 elif is_atomic_specifier: 

1081 newspec = self._parse_atomic_specifier() 

1082 elif tok.type in _TYPEOF_SPEC: 

1083 newspec = self._parse_typeof_specifier() 

1084 elif tok.type == "_BITINT": 

1085 newspec = self._parse_bitint_specifier() 

1086 elif tok.type in _TYPE_QUALIFIER: 

1087 kind = "qual" 

1088 newspec = self._advance().value 

1089 elif allow_storage_and_function and tok.type in _STORAGE_CLASS: 

1090 kind = "storage" 

1091 newspec = self._advance().value 

1092 elif allow_storage_and_function and tok.type in _FUNCTION_SPEC: 

1093 kind = "function" 

1094 newspec = self._advance().value 

1095 elif tok.type in _TYPE_SPEC_SIMPLE or tok.type == "TYPEID": 

1096 tok = self._advance() 

1097 newspec = c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)) 

1098 elif tok.type in {"STRUCT", "UNION"}: 

1099 newspec = self._parse_struct_or_union_specifier() 

1100 elif tok.type == "ENUM": 

1101 newspec = self._parse_enum_specifier() 

1102 else: 

1103 break 

1104 

1105 spec = self._add_declaration_specifier(spec, newspec, kind) 

1106 if kind == "type": 

1107 saw_type = True 

1108 

1109 return spec, saw_type, first_coord 

1110 

1111 # BNF: type_qualifier_list : type_qualifier+ 

1112 def _parse_type_qualifier_list(self) -> list[str]: 

1113 quals = [] 

1114 while self._peek_type() in _TYPE_QUALIFIER: 

1115 quals.append(self._advance().value) 

1116 return quals 

1117 

1118 # BNF: alignment_specifier : _ALIGNAS '(' type_name | constant_expression ')' 

1119 def _parse_alignment_specifier(self) -> c_ast.Alignas: 

1120 tok = self._expect("_ALIGNAS") 

1121 self._expect("LPAREN") 

1122 

1123 if self._starts_declaration(): 

1124 typ = self._parse_type_name() 

1125 self._expect("RPAREN") 

1126 return c_ast.Alignas(typ, self._tok_coord(tok)) 

1127 

1128 expr = self._parse_constant_expression() 

1129 self._expect("RPAREN") 

1130 return c_ast.Alignas(expr, self._tok_coord(tok)) 

1131 

1132 # BNF: atomic_specifier : _ATOMIC '(' type_name ')' 

1133 def _parse_atomic_specifier(self) -> c_ast.Node: 

1134 self._expect("_ATOMIC") 

1135 self._expect("LPAREN") 

1136 typ = self._parse_type_name() 

1137 self._expect("RPAREN") 

1138 typ.quals.append("_Atomic") 

1139 return typ 

1140 

1141 # BNF: typeof_specifier : (TYPEOF | TYPEOF_UNQUAL) '(' (type_name | expression) ')' 

1142 def _parse_typeof_specifier(self) -> c_ast.TypeOf: 

1143 tok = self._advance() 

1144 self._expect("LPAREN") 

1145 if self._starts_declaration(): 

1146 expr = self._parse_type_name() 

1147 else: 

1148 expr = self._parse_expression() 

1149 self._expect("RPAREN") 

1150 return c_ast.TypeOf(tok.value, expr, self._tok_coord(tok)) 

1151 

1152 # BNF: bitint_specifier : _BITINT '(' constant_expression ')' 

1153 def _parse_bitint_specifier(self) -> c_ast.BitIntType: 

1154 tok = self._expect("_BITINT") 

1155 self._expect("LPAREN") 

1156 width = self._parse_constant_expression() 

1157 self._expect("RPAREN") 

1158 return c_ast.BitIntType(width, None, self._tok_coord(tok)) 

1159 

1160 # BNF: init_declarator_list : init_declarator (',' init_declarator)* 

1161 def _parse_init_declarator_list( 

1162 self, first: Optional["_DeclInfo"] = None, id_only: bool = False 

1163 ) -> list["_DeclInfo"]: 

1164 decls = ( 

1165 [first] 

1166 if first is not None 

1167 else [self._parse_init_declarator(id_only=id_only)] 

1168 ) 

1169 

1170 while self._accept("COMMA"): 

1171 decls.append(self._parse_init_declarator(id_only=id_only)) 

1172 return decls 

1173 

1174 # BNF: init_declarator : declarator ('=' initializer)? 

1175 def _parse_init_declarator(self, id_only: bool = False) -> "_DeclInfo": 

1176 decl = self._parse_id_declarator() if id_only else self._parse_declarator() 

1177 if self._accept("EQUALS"): 

1178 return {"decl": decl, "init": self._parse_initializer()} 

1179 return {"decl": decl} 

1180 

1181 # ------------------------------------------------------------------ 

1182 # Structs/unions/enums 

1183 # ------------------------------------------------------------------ 

1184 # BNF: struct_or_union_specifier : struct_or_union ID? '{' struct_declaration_list? '}' 

1185 # | struct_or_union ID 

1186 def _parse_struct_or_union_specifier(self) -> c_ast.Node: 

1187 tok = self._advance() 

1188 attributes = self._parse_attribute_specifier_sequence() 

1189 klass = self._select_struct_union_class(tok.value) 

1190 

1191 if self._peek_type() in {"ID", "TYPEID"}: 

1192 name_tok = self._advance() 

1193 if self._peek_type() == "LBRACE": 

1194 self._advance() 

1195 if self._accept("RBRACE"): 

1196 return klass( 

1197 attributes=attributes, 

1198 name=name_tok.value, 

1199 decls=[], 

1200 coord=self._tok_coord(name_tok), 

1201 ) 

1202 decls = self._parse_struct_declaration_list() 

1203 self._expect("RBRACE") 

1204 return klass( 

1205 attributes=attributes, 

1206 name=name_tok.value, 

1207 decls=decls, 

1208 coord=self._tok_coord(name_tok), 

1209 ) 

1210 

1211 return klass( 

1212 attributes=attributes, 

1213 name=name_tok.value, 

1214 decls=None, 

1215 coord=self._tok_coord(name_tok), 

1216 ) 

1217 

1218 if self._peek_type() == "LBRACE": 

1219 brace_tok = self._advance() 

1220 if self._accept("RBRACE"): 

1221 return klass( 

1222 attributes=attributes, 

1223 name=None, 

1224 decls=[], 

1225 coord=self._tok_coord(brace_tok), 

1226 ) 

1227 decls = self._parse_struct_declaration_list() 

1228 self._expect("RBRACE") 

1229 return klass( 

1230 attributes=attributes, 

1231 name=None, 

1232 decls=decls, 

1233 coord=self._tok_coord(brace_tok), 

1234 ) 

1235 

1236 self._parse_error("Invalid struct/union declaration", self._tok_coord(tok)) 

1237 

1238 # BNF: struct_declaration_list : struct_declaration+ 

1239 def _parse_struct_declaration_list(self) -> list[c_ast.Node]: 

1240 decls = [] 

1241 while self._peek_type() not in {None, "RBRACE"}: 

1242 items = self._parse_struct_declaration() 

1243 if items is None: 

1244 continue 

1245 decls.extend(items) 

1246 return decls 

1247 

1248 # BNF: struct_declaration : specifier_qualifier_list struct_declarator_list? 

1249 # pppragma_directive* ';' 

1250 # | static_assert 

1251 # | pppragma_directive 

1252 def _parse_struct_declaration(self) -> list[c_ast.Node] | None: 

1253 if self._peek_type() == "ATTRIBUTE_BEGIN": 

1254 attributes = self._parse_attribute_specifier_sequence() 

1255 return self._attribute_declarations( 

1256 self._parse_struct_declaration() or [], attributes 

1257 ) 

1258 if self._peek_type() == "_STATIC_ASSERT": 

1259 return [self._parse_static_assert()] 

1260 if self._peek_type() == "SEMI": 

1261 self._advance() 

1262 return None 

1263 if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}: 

1264 return [self._parse_pppragma_directive()] 

1265 

1266 spec = self._parse_specifier_qualifier_list() 

1267 assert "typedef" not in spec.get("storage", []) 

1268 

1269 decls = None 

1270 if self._starts_declarator() or self._peek_type() == "COLON": 

1271 decls = self._parse_struct_declarator_list() 

1272 if decls is not None: 

1273 return self._parse_declaration_end( 

1274 self._build_declarations(spec=spec, decls=decls) 

1275 ) 

1276 

1277 if len(spec["type"]) == 1: 

1278 node = spec["type"][0] 

1279 if isinstance(node, c_ast.Node): 

1280 decl_type = node 

1281 else: 

1282 decl_type = c_ast.IdentifierType(node) 

1283 return self._parse_declaration_end( 

1284 self._build_declarations(spec=spec, decls=[{"decl": decl_type}]) 

1285 ) 

1286 

1287 return self._parse_declaration_end( 

1288 self._build_declarations(spec=spec, decls=[{"decl": None}]) 

1289 ) 

1290 

1291 # BNF: struct_declarator_list : struct_declarator (',' struct_declarator)* 

1292 def _parse_struct_declarator_list(self) -> list["_DeclInfo"]: 

1293 decls = [self._parse_struct_declarator()] 

1294 while self._accept("COMMA"): 

1295 decls.append(self._parse_struct_declarator()) 

1296 return decls 

1297 

1298 # BNF: struct_declarator : declarator? ':' constant_expression 

1299 # | declarator (':' constant_expression)? 

1300 def _parse_struct_declarator(self) -> "_DeclInfo": 

1301 if self._accept("COLON"): 

1302 bitsize = self._parse_constant_expression() 

1303 return { 

1304 "decl": c_ast.TypeDecl(None, None, None, None), 

1305 "bitsize": bitsize, 

1306 } 

1307 

1308 decl = self._parse_declarator() 

1309 if self._accept("COLON"): 

1310 bitsize = self._parse_constant_expression() 

1311 return {"decl": decl, "bitsize": bitsize} 

1312 

1313 return {"decl": decl} 

1314 

1315 # BNF: enum_specifier : ENUM ID? enum_type_specifier? '{' enumerator_list '}' 

1316 # | ENUM ID enum_type_specifier? 

1317 def _parse_enum_specifier(self) -> c_ast.Node: 

1318 tok = self._expect("ENUM") 

1319 attributes = self._parse_attribute_specifier_sequence() 

1320 name = None 

1321 if self._peek_type() in {"ID", "TYPEID"}: 

1322 name = self._advance().value 

1323 

1324 underlying_type = None 

1325 # A colon followed by an expression belongs to an unnamed bit-field, 

1326 # e.g. struct S { enum E : 3; }, rather than a fixed underlying type. 

1327 if self._peek_type() == "COLON" and self._starts_declaration(self._peek(2)): 

1328 underlying_type = self._parse_enum_type_specifier() 

1329 

1330 enums = None 

1331 if name is None or self._peek_type() == "LBRACE": 

1332 self._expect("LBRACE") 

1333 enums = self._parse_enumerator_list() 

1334 self._expect("RBRACE") 

1335 return c_ast.Enum( 

1336 name, 

1337 enums, 

1338 underlying_type, 

1339 self._tok_coord(tok), 

1340 attributes=attributes, 

1341 ) 

1342 

1343 # BNF: enum_type_specifier : ':' specifier_qualifier_list (C23) 

1344 def _parse_enum_type_specifier(self) -> c_ast.Typename: 

1345 colon = self._expect("COLON") 

1346 if self._dialect != "c23": 

1347 self._parse_error( 

1348 "Fixed underlying enum types require C23", self._tok_coord(colon) 

1349 ) 

1350 # The base is a specifier-qualifier list, not a full type name: any 

1351 # following pointer, array, or function declarator belongs to the enum. 

1352 spec = self._parse_specifier_qualifier_list() 

1353 coord = spec["type"][0].coord if spec["type"] else self._tok_coord(colon) 

1354 return self._build_type_name(spec, None, coord) 

1355 

1356 # BNF: enumerator_list : pppragma_directive* enumerator pppragma_directive* 

1357 # (',' pppragma_directive* enumerator pppragma_directive*)* 

1358 # (',' pppragma_directive*)? 

1359 def _parse_enumerator_list(self) -> c_ast.Node: 

1360 members = self._parse_pppragma_directive_list() 

1361 # Pragmas do not count as enumerators: even with leading directives, 

1362 # the list must contain at least one enumerator. 

1363 enum = self._parse_enumerator() 

1364 members.append(enum) 

1365 coord = enum.coord 

1366 while True: 

1367 # Directives may appear on either side of a separating comma, or 

1368 # after the last enumerator. They never replace the comma itself. 

1369 members.extend(self._parse_pppragma_directive_list()) 

1370 if not self._accept("COMMA"): 

1371 break 

1372 members.extend(self._parse_pppragma_directive_list()) 

1373 if self._peek_type() == "RBRACE": 

1374 break 

1375 members.append(self._parse_enumerator()) 

1376 return c_ast.EnumeratorList(members, coord) 

1377 

1378 # BNF: enumerator : ID ('=' constant_expression)? 

1379 def _parse_enumerator(self) -> c_ast.Node: 

1380 name_tok = self._expect("ID") 

1381 attributes = self._parse_attribute_specifier_sequence() 

1382 if self._accept("EQUALS"): 

1383 value = self._parse_constant_expression() 

1384 else: 

1385 value = None 

1386 enum = c_ast.Enumerator( 

1387 name_tok.value, 

1388 value, 

1389 self._tok_coord(name_tok), 

1390 attributes=attributes, 

1391 ) 

1392 self._add_identifier(enum.name, enum.coord) 

1393 return enum 

1394 

1395 # ------------------------------------------------------------------ 

1396 # Declarators 

1397 # ------------------------------------------------------------------ 

1398 # BNF: declarator : pointer? direct_declarator 

1399 def _parse_declarator(self) -> c_ast.Node: 

1400 decl, _ = self._parse_any_declarator( 

1401 allow_abstract=False, typeid_paren_as_abstract=False 

1402 ) 

1403 assert decl is not None 

1404 return decl 

1405 

1406 # BNF: id_declarator : declarator with ID name 

1407 def _parse_id_declarator(self) -> c_ast.Node: 

1408 return self._parse_declarator_kind(kind="id", allow_paren=True) 

1409 

1410 # BNF: typeid_declarator : declarator with TYPEID name 

1411 def _parse_typeid_declarator(self) -> c_ast.Node: 

1412 return self._parse_declarator_kind(kind="typeid", allow_paren=True) 

1413 

1414 # BNF: typeid_noparen_declarator : declarator without parenthesized name 

1415 def _parse_typeid_noparen_declarator(self) -> c_ast.Node: 

1416 return self._parse_declarator_kind(kind="typeid", allow_paren=False) 

1417 

1418 # BNF: declarator_kind : pointer? direct_declarator(kind) 

1419 def _parse_declarator_kind(self, kind: str, allow_paren: bool) -> c_ast.Node: 

1420 ptr = None 

1421 if self._peek_type() == "TIMES": 

1422 ptr = self._parse_pointer() 

1423 direct = self._parse_direct_declarator(kind, allow_paren=allow_paren) 

1424 if ptr is not None: 

1425 return self._type_modify_decl(direct, ptr) 

1426 return direct 

1427 

1428 # BNF: direct_declarator : ID | TYPEID | '(' declarator ')' 

1429 # | direct_declarator '[' ... ']' 

1430 # | direct_declarator '(' ... ')' 

1431 def _parse_direct_declarator( 

1432 self, kind: str, allow_paren: bool = True 

1433 ) -> c_ast.Node: 

1434 if allow_paren and self._accept("LPAREN"): 

1435 decl = self._parse_declarator_kind(kind, allow_paren=True) 

1436 self._expect("RPAREN") 

1437 else: 

1438 if kind == "id": 

1439 name_tok = self._expect("ID") 

1440 else: 

1441 name_tok = self._expect("TYPEID") 

1442 decl = c_ast.TypeDecl( 

1443 declname=name_tok.value, 

1444 type=None, 

1445 quals=None, 

1446 align=None, 

1447 coord=self._tok_coord(name_tok), 

1448 ) 

1449 decl.attributes = self._parse_attribute_specifier_sequence() 

1450 

1451 return self._parse_decl_suffixes(decl) 

1452 

1453 def _parse_decl_suffixes(self, decl: c_ast.Node) -> c_ast.Node: 

1454 """Parse a chain of array/function suffixes and attach them to decl.""" 

1455 while True: 

1456 if self._peek_type() == "LBRACKET": 

1457 decl = self._type_modify_decl(decl, self._parse_array_decl(decl)) 

1458 continue 

1459 if self._peek_type() == "LPAREN": 

1460 func = self._parse_function_decl(decl) 

1461 decl = self._type_modify_decl(decl, func) 

1462 continue 

1463 break 

1464 return decl 

1465 

1466 # BNF: array_decl : '[' array_specifiers? assignment_expression? ']' 

1467 def _parse_array_decl(self, base_decl: c_ast.Node) -> c_ast.Node: 

1468 return self._parse_array_decl_common(base_type=None, coord=base_decl.coord) 

1469 

1470 def _parse_array_decl_common( 

1471 self, base_type: c_ast.Node | None, coord: Coord | None = None 

1472 ) -> c_ast.Node: 

1473 """Parse an array declarator suffix and return an ArrayDecl node. 

1474 

1475 base_type: 

1476 Base declarator node to attach (None for direct-declarator parsing, 

1477 TypeDecl for abstract declarators). 

1478 

1479 coord: 

1480 Coordinate to use for the ArrayDecl. If None, uses the '[' token. 

1481 """ 

1482 lbrack_tok = self._expect("LBRACKET") 

1483 if coord is None: 

1484 coord = self._tok_coord(lbrack_tok) 

1485 

1486 def make_array_decl(dim, dim_quals): 

1487 return c_ast.ArrayDecl( 

1488 type=base_type, 

1489 dim=dim, 

1490 dim_quals=dim_quals, 

1491 coord=coord, 

1492 attributes=self._parse_attribute_specifier_sequence(), 

1493 ) 

1494 

1495 if self._accept("STATIC"): 

1496 dim_quals = ["static"] + (self._parse_type_qualifier_list() or []) 

1497 dim = self._parse_assignment_expression() 

1498 self._expect("RBRACKET") 

1499 return make_array_decl(dim, dim_quals) 

1500 

1501 if self._peek_type() in _TYPE_QUALIFIER: 

1502 dim_quals = self._parse_type_qualifier_list() or [] 

1503 if self._accept("STATIC"): 

1504 dim_quals = dim_quals + ["static"] 

1505 dim = self._parse_assignment_expression() 

1506 self._expect("RBRACKET") 

1507 return make_array_decl(dim, dim_quals) 

1508 times_tok = self._accept("TIMES") 

1509 if times_tok: 

1510 self._expect("RBRACKET") 

1511 dim = c_ast.ID(times_tok.value, self._tok_coord(times_tok)) 

1512 return make_array_decl(dim, dim_quals) 

1513 dim = None 

1514 if self._starts_expression(): 

1515 dim = self._parse_assignment_expression() 

1516 self._expect("RBRACKET") 

1517 return make_array_decl(dim, dim_quals) 

1518 

1519 times_tok = self._accept("TIMES") 

1520 if times_tok: 

1521 self._expect("RBRACKET") 

1522 dim = c_ast.ID(times_tok.value, self._tok_coord(times_tok)) 

1523 return make_array_decl(dim, []) 

1524 

1525 dim = None 

1526 if self._starts_expression(): 

1527 dim = self._parse_assignment_expression() 

1528 self._expect("RBRACKET") 

1529 return make_array_decl(dim, []) 

1530 

1531 # BNF: function_decl : '(' parameter_type_list_opt | identifier_list_opt ')' 

1532 def _parse_function_decl(self, base_decl: c_ast.Node) -> c_ast.Node: 

1533 self._expect("LPAREN") 

1534 if self._accept("RPAREN"): 

1535 args = None 

1536 else: 

1537 args = ( 

1538 self._parse_parameter_type_list() 

1539 if self._starts_declaration() or self._peek_type() == "ELLIPSIS" 

1540 else self._parse_identifier_list_opt() 

1541 ) 

1542 self._expect("RPAREN") 

1543 

1544 func = c_ast.FuncDecl( 

1545 args=args, 

1546 type=None, 

1547 coord=base_decl.coord, 

1548 attributes=self._parse_attribute_specifier_sequence(), 

1549 ) 

1550 

1551 return func 

1552 

1553 # BNF: pointer : '*' type_qualifier_list? pointer? 

1554 def _parse_pointer(self) -> c_ast.Node | None: 

1555 stars = [] 

1556 times_tok = self._accept("TIMES") 

1557 while times_tok: 

1558 attributes = self._parse_attribute_specifier_sequence() 

1559 quals = self._parse_type_qualifier_list() or [] 

1560 stars.append((quals, self._tok_coord(times_tok), attributes)) 

1561 times_tok = self._accept("TIMES") 

1562 

1563 if not stars: 

1564 return None 

1565 

1566 ptr = None 

1567 for quals, coord, attributes in stars: 

1568 ptr = c_ast.PtrDecl( 

1569 quals=quals, type=ptr, coord=coord, attributes=attributes 

1570 ) 

1571 return ptr 

1572 

1573 # BNF: parameter_type_list : parameter_list (',' ELLIPSIS)? 

1574 # | ELLIPSIS (C23) 

1575 def _parse_parameter_type_list(self) -> c_ast.ParamList: 

1576 # C23 permits a variadic function with no preceding named parameters. 

1577 ell_tok = self._accept("ELLIPSIS") 

1578 if ell_tok: 

1579 coord = self._tok_coord(ell_tok) 

1580 if self._dialect != "c23": 

1581 self._parse_error("Ellipsis-only parameter lists require C23", coord) 

1582 return c_ast.ParamList([c_ast.EllipsisParam(coord)], coord) 

1583 

1584 params = self._parse_parameter_list() 

1585 if self._peek_type() == "COMMA" and self._peek_type(2) == "ELLIPSIS": 

1586 self._advance() 

1587 ell_tok = self._advance() 

1588 params.params.append(c_ast.EllipsisParam(self._tok_coord(ell_tok))) 

1589 return params 

1590 

1591 # BNF: parameter_list : parameter_declaration (',' parameter_declaration)* 

1592 def _parse_parameter_list(self) -> c_ast.ParamList: 

1593 first = self._parse_parameter_declaration() 

1594 params = c_ast.ParamList([first], first.coord) 

1595 while self._peek_type() == "COMMA" and self._peek_type(2) != "ELLIPSIS": 

1596 self._advance() 

1597 params.params.append(self._parse_parameter_declaration()) 

1598 return params 

1599 

1600 # BNF: parameter_declaration : declaration_specifiers declarator? 

1601 # | declaration_specifiers abstract_declarator_opt 

1602 def _parse_parameter_declaration(self) -> c_ast.Node: 

1603 attributes = self._parse_attribute_specifier_sequence() 

1604 decl = self._parse_parameter_declaration_body() 

1605 assert isinstance(decl, (c_ast.Decl, c_ast.Typename)) 

1606 decl.attributes = attributes 

1607 return decl 

1608 

1609 def _parse_parameter_declaration_body(self) -> c_ast.Node: 

1610 spec, _, spec_coord = self._parse_declaration_specifiers(allow_no_type=True) 

1611 

1612 if self._is_inferred_auto(spec): 

1613 self._parse_error("Cannot infer the type of a parameter", spec_coord) 

1614 

1615 if "typedef" in spec["storage"]: 

1616 self._parse_error("Invalid storage class for a parameter", spec_coord) 

1617 

1618 if not spec["type"]: 

1619 spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)] 

1620 

1621 if self._starts_declarator(): 

1622 decl, is_named = self._parse_any_declarator( 

1623 allow_abstract=True, typeid_paren_as_abstract=True 

1624 ) 

1625 if is_named: 

1626 return self._build_declarations( 

1627 spec=spec, 

1628 decls=[{"decl": decl}], 

1629 )[0] 

1630 return self._build_parameter_declaration(spec, decl, spec_coord) 

1631 

1632 decl = self._parse_abstract_declarator_opt() 

1633 return self._build_parameter_declaration(spec, decl, spec_coord) 

1634 

1635 def _build_parameter_declaration( 

1636 self, spec: "_DeclSpec", decl: c_ast.Node | None, spec_coord: Coord | None 

1637 ) -> c_ast.Node: 

1638 if ( 

1639 len(spec["type"]) > 1 

1640 and isinstance(spec["type"][-1], c_ast.IdentifierType) 

1641 and len(spec["type"][-1].names) == 1 

1642 and self._is_type_in_scope(spec["type"][-1].names[0]) 

1643 ): 

1644 return self._build_declarations(spec=spec, decls=[{"decl": decl}])[0] 

1645 

1646 return self._build_type_name(spec, decl, spec_coord) 

1647 

1648 # BNF: identifier_list_opt : identifier_list | empty 

1649 def _parse_identifier_list_opt(self) -> c_ast.Node | None: 

1650 if self._peek_type() == "RPAREN": 

1651 return None 

1652 return self._parse_identifier_list() 

1653 

1654 # BNF: identifier_list : identifier (',' identifier)* 

1655 def _parse_identifier_list(self) -> c_ast.Node: 

1656 first = self._parse_identifier() 

1657 params = c_ast.ParamList([first], first.coord) 

1658 while self._accept("COMMA"): 

1659 params.params.append(self._parse_identifier()) 

1660 return params 

1661 

1662 # ------------------------------------------------------------------ 

1663 # Abstract declarators 

1664 # ------------------------------------------------------------------ 

1665 # BNF: type_name : specifier_qualifier_list abstract_declarator_opt 

1666 def _parse_type_name(self) -> c_ast.Typename: 

1667 spec = self._parse_specifier_qualifier_list() 

1668 decl = self._parse_abstract_declarator_opt() 

1669 

1670 coord = None 

1671 if decl is not None: 

1672 coord = decl.coord 

1673 elif spec["type"]: 

1674 coord = spec["type"][0].coord 

1675 

1676 return self._build_type_name(spec, decl, coord) 

1677 

1678 # BNF: abstract_declarator_opt : pointer? direct_abstract_declarator? 

1679 def _parse_abstract_declarator_opt(self) -> c_ast.Node | None: 

1680 if self._peek_type() == "TIMES": 

1681 ptr = self._parse_pointer() 

1682 if self._starts_direct_abstract_declarator(): 

1683 decl = self._parse_direct_abstract_declarator() 

1684 else: 

1685 decl = c_ast.TypeDecl(None, None, None, None) 

1686 assert ptr is not None 

1687 return self._type_modify_decl(decl, ptr) 

1688 

1689 if self._starts_direct_abstract_declarator(): 

1690 return self._parse_direct_abstract_declarator() 

1691 

1692 return None 

1693 

1694 # BNF: direct_abstract_declarator : '(' parameter_type_list_opt ')' 

1695 # | '(' abstract_declarator ')' 

1696 # | '[' ... ']' 

1697 def _parse_direct_abstract_declarator(self) -> c_ast.Node: 

1698 lparen_tok = self._accept("LPAREN") 

1699 if lparen_tok: 

1700 if self._starts_declaration() or self._peek_type() in { 

1701 "RPAREN", 

1702 "ELLIPSIS", 

1703 }: 

1704 params = self._parse_parameter_type_list_opt() 

1705 self._expect("RPAREN") 

1706 decl = c_ast.FuncDecl( 

1707 args=params, 

1708 type=c_ast.TypeDecl(None, None, None, None), 

1709 coord=self._tok_coord(lparen_tok), 

1710 attributes=self._parse_attribute_specifier_sequence(), 

1711 ) 

1712 else: 

1713 decl = self._parse_abstract_declarator_opt() 

1714 self._expect("RPAREN") 

1715 assert decl is not None 

1716 elif self._peek_type() == "LBRACKET": 

1717 decl = self._parse_abstract_array_base() 

1718 else: 

1719 self._parse_error("Invalid abstract declarator", self.clex.filename) 

1720 

1721 return self._parse_decl_suffixes(decl) 

1722 

1723 # BNF: parameter_type_list_opt : parameter_type_list | empty 

1724 def _parse_parameter_type_list_opt(self) -> c_ast.ParamList | None: 

1725 if self._peek_type() == "RPAREN": 

1726 return None 

1727 return self._parse_parameter_type_list() 

1728 

1729 # BNF: abstract_array_base : '[' array_specifiers? assignment_expression? ']' 

1730 def _parse_abstract_array_base(self) -> c_ast.Node: 

1731 return self._parse_array_decl_common( 

1732 base_type=c_ast.TypeDecl(None, None, None, None), coord=None 

1733 ) 

1734 

1735 # ------------------------------------------------------------------ 

1736 # Statements 

1737 # ------------------------------------------------------------------ 

1738 # BNF: statement : labeled_statement | compound_statement 

1739 # | selection_statement | iteration_statement 

1740 # | jump_statement | expression_statement 

1741 # | pppragma_directive 

1742 def _parse_statement(self) -> c_ast.Node: 

1743 if self._peek_type() == "ATTRIBUTE_BEGIN": 

1744 attributes = self._parse_attribute_specifier_sequence() 

1745 if self._accept("SEMI"): 

1746 return c_ast.AttributeDeclaration(attributes, attributes[0].coord) 

1747 return self._attribute_statement(self._parse_statement(), attributes) 

1748 tok_type = self._peek_type() 

1749 match tok_type: 

1750 case "CASE" | "DEFAULT": 

1751 return self._parse_labeled_statement() 

1752 case "ID" if self._peek_type(2) == "COLON": 

1753 return self._parse_labeled_statement() 

1754 case "LBRACE": 

1755 return self._parse_compound_statement() 

1756 case "IF" | "SWITCH": 

1757 return self._parse_selection_statement() 

1758 case "WHILE" | "DO" | "FOR": 

1759 return self._parse_iteration_statement() 

1760 case "GOTO" | "BREAK" | "CONTINUE" | "RETURN": 

1761 return self._parse_jump_statement() 

1762 case "PPPRAGMA" | "_PRAGMA": 

1763 return self._parse_pppragma_directive() 

1764 case _: 

1765 return self._parse_expression_statement() 

1766 

1767 # BNF: pragmacomp_or_statement : pppragma_directive* statement 

1768 def _parse_pragmacomp_or_statement(self) -> c_ast.Node: 

1769 if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}: 

1770 pragmas = self._parse_pppragma_directive_list() 

1771 stmt = self._parse_statement() 

1772 return c_ast.Compound(block_items=pragmas + [stmt], coord=pragmas[0].coord) 

1773 return self._parse_statement() 

1774 

1775 # BNF: block_item : declaration | statement 

1776 def _parse_block_item(self) -> c_ast.Node | list[c_ast.Node]: 

1777 if self._peek_type() == "ATTRIBUTE_BEGIN": 

1778 attributes = self._parse_attribute_specifier_sequence() 

1779 if self._accept("SEMI"): 

1780 return c_ast.AttributeDeclaration(attributes, attributes[0].coord) 

1781 if self._starts_declaration(): 

1782 return self._attribute_declarations( 

1783 self._parse_declaration(), attributes 

1784 ) 

1785 return self._attribute_statement(self._parse_statement(), attributes) 

1786 if self._peek_type() == "_STATIC_ASSERT": 

1787 return self._parse_static_assert() 

1788 if self._starts_declaration(): 

1789 return self._parse_declaration() 

1790 return self._parse_statement() 

1791 

1792 # BNF: block_item_list : block_item+ 

1793 def _parse_block_item_list(self) -> list[c_ast.Node]: 

1794 items: list[c_ast.Node] = [] 

1795 while self._peek_type() not in {"RBRACE", None}: 

1796 item = self._parse_block_item() 

1797 if isinstance(item, c_ast.Node): 

1798 items.append(item) 

1799 elif item != [None]: 

1800 items.extend(item) 

1801 return items 

1802 

1803 # BNF: compound_statement : '{' block_item_list? '}' 

1804 def _parse_compound_statement(self, *, new_scope: bool = True) -> c_ast.Node: 

1805 lbrace_tok = self._expect("LBRACE") 

1806 if new_scope: 

1807 self._push_scope() 

1808 block_items = None 

1809 if not self._accept("RBRACE"): 

1810 block_items = self._parse_block_item_list() 

1811 self._expect("RBRACE") 

1812 if new_scope: 

1813 self._pop_scope() 

1814 return c_ast.Compound( 

1815 block_items=block_items, coord=self._tok_coord(lbrace_tok) 

1816 ) 

1817 

1818 # BNF: labeled_statement : ID ':' statement 

1819 # | CASE constant_expression ':' statement 

1820 # | DEFAULT ':' statement 

1821 def _parse_labeled_statement(self) -> c_ast.Node: 

1822 tok_type = self._peek_type() 

1823 match tok_type: 

1824 case "ID": 

1825 name_tok = self._advance() 

1826 self._expect("COLON") 

1827 if self._starts_statement(): 

1828 stmt = self._parse_pragmacomp_or_statement() 

1829 else: 

1830 stmt = c_ast.EmptyStatement(self._tok_coord(name_tok)) 

1831 return c_ast.Label(name_tok.value, stmt, self._tok_coord(name_tok)) 

1832 case "CASE": 

1833 case_tok = self._advance() 

1834 expr = self._parse_constant_expression() 

1835 self._expect("COLON") 

1836 if self._starts_statement(): 

1837 stmt = self._parse_pragmacomp_or_statement() 

1838 else: 

1839 stmt = c_ast.EmptyStatement(self._tok_coord(case_tok)) 

1840 return c_ast.Case(expr, [stmt], self._tok_coord(case_tok)) 

1841 case "DEFAULT": 

1842 def_tok = self._advance() 

1843 self._expect("COLON") 

1844 if self._starts_statement(): 

1845 stmt = self._parse_pragmacomp_or_statement() 

1846 else: 

1847 stmt = c_ast.EmptyStatement(self._tok_coord(def_tok)) 

1848 return c_ast.Default([stmt], self._tok_coord(def_tok)) 

1849 case _: 

1850 self._parse_error("Invalid labeled statement", self.clex.filename) 

1851 

1852 # BNF: selection_statement : IF '(' expression ')' statement (ELSE statement)? 

1853 # | SWITCH '(' expression ')' statement 

1854 def _parse_selection_statement(self) -> c_ast.Node: 

1855 tok = self._advance() 

1856 match tok.type: 

1857 case "IF": 

1858 self._expect("LPAREN") 

1859 cond = self._parse_expression() 

1860 self._expect("RPAREN") 

1861 then_stmt = self._parse_pragmacomp_or_statement() 

1862 if self._accept("ELSE"): 

1863 else_stmt = self._parse_pragmacomp_or_statement() 

1864 return c_ast.If(cond, then_stmt, else_stmt, self._tok_coord(tok)) 

1865 return c_ast.If(cond, then_stmt, None, self._tok_coord(tok)) 

1866 case "SWITCH": 

1867 self._expect("LPAREN") 

1868 expr = self._parse_expression() 

1869 self._expect("RPAREN") 

1870 stmt = self._parse_pragmacomp_or_statement() 

1871 return fix_switch_cases(c_ast.Switch(expr, stmt, self._tok_coord(tok))) 

1872 case _: 

1873 self._parse_error("Invalid selection statement", self._tok_coord(tok)) 

1874 

1875 # BNF: iteration_statement : WHILE '(' expression ')' statement 

1876 # | DO statement WHILE '(' expression ')' ';' 

1877 # | FOR '(' (declaration | expression_opt) ';' 

1878 # expression_opt ';' expression_opt ')' statement 

1879 def _parse_iteration_statement(self) -> c_ast.Node: 

1880 tok = self._advance() 

1881 match tok.type: 

1882 case "WHILE": 

1883 self._expect("LPAREN") 

1884 cond = self._parse_expression() 

1885 self._expect("RPAREN") 

1886 stmt = self._parse_pragmacomp_or_statement() 

1887 return c_ast.While(cond, stmt, self._tok_coord(tok)) 

1888 case "DO": 

1889 stmt = self._parse_pragmacomp_or_statement() 

1890 self._expect("WHILE") 

1891 self._expect("LPAREN") 

1892 cond = self._parse_expression() 

1893 self._expect("RPAREN") 

1894 self._expect("SEMI") 

1895 return c_ast.DoWhile(cond, stmt, self._tok_coord(tok)) 

1896 case "FOR": 

1897 self._expect("LPAREN") 

1898 if self._peek_type() == "_STATIC_ASSERT" or self._starts_declaration(): 

1899 # Names declared here are scoped to the loop (C11 6.8.5p5) 

1900 self._push_scope() 

1901 decls = self._parse_declaration() 

1902 init = c_ast.DeclList(decls, self._tok_coord(tok)) 

1903 cond = self._parse_expression_opt() 

1904 self._expect("SEMI") 

1905 next_expr = self._parse_expression_opt() 

1906 self._expect("RPAREN") 

1907 stmt = self._parse_pragmacomp_or_statement() 

1908 self._pop_scope() 

1909 return c_ast.For(init, cond, next_expr, stmt, self._tok_coord(tok)) 

1910 

1911 init = self._parse_expression_opt() 

1912 self._expect("SEMI") 

1913 cond = self._parse_expression_opt() 

1914 self._expect("SEMI") 

1915 next_expr = self._parse_expression_opt() 

1916 self._expect("RPAREN") 

1917 stmt = self._parse_pragmacomp_or_statement() 

1918 return c_ast.For(init, cond, next_expr, stmt, self._tok_coord(tok)) 

1919 case _: 

1920 self._parse_error("Invalid iteration statement", self._tok_coord(tok)) 

1921 

1922 # BNF: jump_statement : GOTO ID ';' | BREAK ';' | CONTINUE ';' 

1923 # | RETURN expression? ';' 

1924 def _parse_jump_statement(self) -> c_ast.Node: 

1925 tok = self._advance() 

1926 match tok.type: 

1927 case "GOTO": 

1928 name_tok = self._expect("ID") 

1929 self._expect("SEMI") 

1930 return c_ast.Goto(name_tok.value, self._tok_coord(tok)) 

1931 case "BREAK": 

1932 self._expect("SEMI") 

1933 return c_ast.Break(self._tok_coord(tok)) 

1934 case "CONTINUE": 

1935 self._expect("SEMI") 

1936 return c_ast.Continue(self._tok_coord(tok)) 

1937 case "RETURN": 

1938 if self._accept("SEMI"): 

1939 return c_ast.Return(None, self._tok_coord(tok)) 

1940 expr = self._parse_expression() 

1941 self._expect("SEMI") 

1942 return c_ast.Return(expr, self._tok_coord(tok)) 

1943 case _: 

1944 self._parse_error("Invalid jump statement", self._tok_coord(tok)) 

1945 

1946 # BNF: expression_statement : expression_opt ';' 

1947 def _parse_expression_statement(self) -> c_ast.Node: 

1948 expr = self._parse_expression_opt() 

1949 semi_tok = self._expect("SEMI") 

1950 if expr is None: 

1951 return c_ast.EmptyStatement(self._tok_coord(semi_tok)) 

1952 return expr 

1953 

1954 # ------------------------------------------------------------------ 

1955 # Expressions 

1956 # ------------------------------------------------------------------ 

1957 # BNF: expression_opt : expression | empty 

1958 def _parse_expression_opt(self) -> c_ast.Node | None: 

1959 if self._starts_expression(): 

1960 return self._parse_expression() 

1961 return None 

1962 

1963 # BNF: expression : assignment_expression (',' assignment_expression)* 

1964 def _parse_expression(self) -> c_ast.Node: 

1965 expr = self._parse_assignment_expression() 

1966 if not self._accept("COMMA"): 

1967 return expr 

1968 exprs = [expr, self._parse_assignment_expression()] 

1969 while self._accept("COMMA"): 

1970 exprs.append(self._parse_assignment_expression()) 

1971 return c_ast.ExprList(exprs, expr.coord) 

1972 

1973 # BNF: assignment_expression : conditional_expression 

1974 # | unary_expression assignment_op assignment_expression 

1975 def _parse_assignment_expression(self) -> c_ast.Node: 

1976 expr = self._parse_conditional_expression() 

1977 if self._is_assignment_op(): 

1978 op = self._advance().value 

1979 rhs = self._parse_assignment_expression() 

1980 return c_ast.Assignment(op, expr, rhs, expr.coord) 

1981 return expr 

1982 

1983 # BNF: conditional_expression : binary_expression 

1984 # | binary_expression '?' expression ':' conditional_expression 

1985 def _parse_conditional_expression(self) -> c_ast.Node: 

1986 expr = self._parse_binary_expression() 

1987 if self._accept("CONDOP"): 

1988 iftrue = self._parse_expression() 

1989 self._expect("COLON") 

1990 iffalse = self._parse_conditional_expression() 

1991 return c_ast.TernaryOp(expr, iftrue, iffalse, expr.coord) 

1992 return expr 

1993 

1994 # BNF: binary_expression : cast_expression (binary_op cast_expression)* 

1995 def _parse_binary_expression( 

1996 self, min_prec: int = 0, lhs: c_ast.Node | None = None 

1997 ) -> c_ast.Node: 

1998 if lhs is None: 

1999 lhs = self._parse_cast_expression() 

2000 

2001 while True: 

2002 tok = self._peek() 

2003 if tok is None or tok.type not in _BINARY_PRECEDENCE: 

2004 break 

2005 prec = _BINARY_PRECEDENCE[tok.type] 

2006 if prec < min_prec: 

2007 break 

2008 

2009 op = tok.value 

2010 self._advance() 

2011 rhs = self._parse_cast_expression() 

2012 

2013 while True: 

2014 next_tok = self._peek() 

2015 if next_tok is None or next_tok.type not in _BINARY_PRECEDENCE: 

2016 break 

2017 next_prec = _BINARY_PRECEDENCE[next_tok.type] 

2018 if next_prec > prec: 

2019 rhs = self._parse_binary_expression(next_prec, rhs) 

2020 else: 

2021 break 

2022 

2023 lhs = c_ast.BinaryOp(op, lhs, rhs, lhs.coord) 

2024 

2025 return lhs 

2026 

2027 # BNF: cast_expression : '(' type_name ')' cast_expression 

2028 # | unary_expression 

2029 def _parse_cast_expression(self) -> c_ast.Node: 

2030 result = self._try_parse_paren_type_name() 

2031 if result is not None: 

2032 typ, mark, lparen_tok, _ = result 

2033 if self._peek_type() == "LBRACE": 

2034 # (type){...} is a compound literal, not a cast. Examples: 

2035 # (int){1} -> compound literal, handled in postfix 

2036 # (int) x -> cast, handled below 

2037 self._reset(mark) 

2038 else: 

2039 expr = self._parse_cast_expression() 

2040 return c_ast.Cast(typ, expr, self._tok_coord(lparen_tok)) 

2041 return self._parse_unary_expression() 

2042 

2043 # BNF: unary_expression : postfix_expression 

2044 # | '++' unary_expression 

2045 # | '--' unary_expression 

2046 # | unary_op cast_expression 

2047 # | 'sizeof' unary_expression 

2048 # | 'sizeof' '(' type_name ')' 

2049 # | '_Alignof' '(' type_name ')' 

2050 def _parse_unary_expression(self) -> c_ast.Node: 

2051 tok_type = self._peek_type() 

2052 if tok_type in {"PLUSPLUS", "MINUSMINUS"}: 

2053 tok = self._advance() 

2054 expr = self._parse_unary_expression() 

2055 return c_ast.UnaryOp(tok.value, expr, expr.coord) 

2056 

2057 if tok_type in {"AND", "TIMES", "PLUS", "MINUS", "NOT", "LNOT"}: 

2058 tok = self._advance() 

2059 expr = self._parse_cast_expression() 

2060 return c_ast.UnaryOp(tok.value, expr, expr.coord) 

2061 

2062 if tok_type == "SIZEOF": 

2063 tok = self._advance() 

2064 result = self._try_parse_paren_type_name() 

2065 if result is not None: 

2066 typ, mark, _, _ = result 

2067 if self._peek_type() != "LBRACE": 

2068 return c_ast.UnaryOp(tok.value, typ, self._tok_coord(tok)) 

2069 # sizeof (type){...} has an expression operand, including 

2070 # any postfix operators following the compound literal. 

2071 self._reset(mark) 

2072 expr = self._parse_unary_expression() 

2073 return c_ast.UnaryOp(tok.value, expr, self._tok_coord(tok)) 

2074 

2075 if tok_type == "_ALIGNOF": 

2076 tok = self._advance() 

2077 self._expect("LPAREN") 

2078 typ = self._parse_type_name() 

2079 self._expect("RPAREN") 

2080 return c_ast.UnaryOp(tok.value, typ, self._tok_coord(tok)) 

2081 

2082 return self._parse_postfix_expression() 

2083 

2084 # BNF: postfix_expression : primary_expression postfix_suffix* 

2085 # | compound_literal postfix_suffix* 

2086 # compound_literal : '(' storage_class_specifiers? type_name ')' 

2087 # braced_initializer 

2088 def _parse_postfix_expression(self) -> c_ast.Node: 

2089 expr = None 

2090 result = self._try_parse_paren_type_name() 

2091 if result is not None: 

2092 typ, mark, lparen_tok, storage = result 

2093 # Disambiguate between casts and compound literals: 

2094 # (int) x -> cast 

2095 # (int) {1} -> compound literal 

2096 if self._peek_type() == "LBRACE": 

2097 init = self._parse_initializer() 

2098 expr = c_ast.CompoundLiteral( 

2099 typ, init, storage, self._tok_coord(lparen_tok) 

2100 ) 

2101 else: 

2102 self._reset(mark) 

2103 

2104 if expr is None: 

2105 expr = self._parse_primary_expression() 

2106 while True: 

2107 tok_type = self._peek_type() 

2108 if tok_type == "LBRACKET": 

2109 self._advance() 

2110 sub = self._parse_expression() 

2111 self._expect("RBRACKET") 

2112 expr = c_ast.ArrayRef(expr, sub, expr.coord) 

2113 continue 

2114 if tok_type == "LPAREN": 

2115 self._advance() 

2116 if self._peek_type() == "RPAREN": 

2117 self._advance() 

2118 args = None 

2119 else: 

2120 args = self._parse_argument_expression_list() 

2121 self._expect("RPAREN") 

2122 expr = c_ast.FuncCall(expr, args, expr.coord) 

2123 continue 

2124 if tok_type in {"PERIOD", "ARROW"}: 

2125 op_tok = self._advance() 

2126 name_tok = self._advance() 

2127 if name_tok.type not in {"ID", "TYPEID"}: 

2128 self._parse_error( 

2129 "Invalid struct reference", self._tok_coord(name_tok) 

2130 ) 

2131 field = c_ast.ID(name_tok.value, self._tok_coord(name_tok)) 

2132 expr = c_ast.StructRef(expr, op_tok.value, field, expr.coord) 

2133 continue 

2134 if tok_type in {"PLUSPLUS", "MINUSMINUS"}: 

2135 tok = self._advance() 

2136 expr = c_ast.UnaryOp("p" + tok.value, expr, expr.coord) 

2137 continue 

2138 break 

2139 return expr 

2140 

2141 # BNF: primary_expression : ID | constant | string_literal 

2142 # | '(' expression ')' | generic_selection | offsetof 

2143 # | '(' compound_statement ')' 

2144 # | '__builtin_va_arg' '(' 

2145 # assignment_expression ',' type_name ')' 

2146 def _parse_primary_expression(self) -> c_ast.Node: 

2147 tok_type = self._peek_type() 

2148 if tok_type == "ID": 

2149 return self._parse_identifier() 

2150 if ( 

2151 tok_type in _INT_CONST 

2152 or tok_type in _FLOAT_CONST 

2153 or tok_type in _CHAR_CONST 

2154 or tok_type in _PREDEFINED_CONST 

2155 ): 

2156 return self._parse_constant() 

2157 if tok_type in _STRING_LITERAL: 

2158 return self._parse_unified_string_literal() 

2159 if tok_type == "LPAREN": 

2160 self._advance() 

2161 if self._peek_type() == "LBRACE": 

2162 # GNU statement expressions participate in all expression 

2163 # operators, including postfix operators. 

2164 expr = self._parse_compound_statement() 

2165 else: 

2166 expr = self._parse_expression() 

2167 self._expect("RPAREN") 

2168 return expr 

2169 if tok_type == "_GENERIC": 

2170 return self._parse_generic_selection() 

2171 if tok_type == "__BUILTIN_VA_ARG": 

2172 tok = self._advance() 

2173 self._expect("LPAREN") 

2174 expr = self._parse_assignment_expression() 

2175 self._expect("COMMA") 

2176 typ = self._parse_type_name() 

2177 self._expect("RPAREN") 

2178 coord = self._tok_coord(tok) 

2179 return c_ast.FuncCall( 

2180 c_ast.ID(tok.value, coord), 

2181 c_ast.ExprList([expr, typ], coord), 

2182 coord, 

2183 ) 

2184 if tok_type == "OFFSETOF": 

2185 off_tok = self._advance() 

2186 self._expect("LPAREN") 

2187 typ = self._parse_type_name() 

2188 self._expect("COMMA") 

2189 designator = self._parse_offsetof_member_designator() 

2190 self._expect("RPAREN") 

2191 coord = self._tok_coord(off_tok) 

2192 return c_ast.FuncCall( 

2193 c_ast.ID(off_tok.value, coord), 

2194 c_ast.ExprList([typ, designator], coord), 

2195 coord, 

2196 ) 

2197 

2198 self._parse_error("Invalid expression", self.clex.filename) 

2199 

2200 # BNF: generic_selection : _GENERIC '(' assignment_expression ',' 

2201 # generic_association (',' generic_association)* ')' 

2202 def _parse_generic_selection(self) -> c_ast.GenericSelection: 

2203 tok = self._expect("_GENERIC") 

2204 self._expect("LPAREN") 

2205 expr = self._parse_assignment_expression() 

2206 self._expect("COMMA") 

2207 associations = [self._parse_generic_association()] 

2208 while self._accept("COMMA"): 

2209 associations.append(self._parse_generic_association()) 

2210 self._expect("RPAREN") 

2211 return c_ast.GenericSelection(expr, associations, self._tok_coord(tok)) 

2212 

2213 # BNF: generic_association : type_name ':' assignment_expression 

2214 # | DEFAULT ':' assignment_expression 

2215 def _parse_generic_association(self) -> c_ast.GenericAssociation: 

2216 tok = self._peek() 

2217 if tok is None: 

2218 self._parse_error("At end of input", self.clex.filename) 

2219 typ = None if self._accept("DEFAULT") else self._parse_type_name() 

2220 self._expect("COLON") 

2221 expr = self._parse_assignment_expression() 

2222 return c_ast.GenericAssociation(typ, expr, self._tok_coord(tok)) 

2223 

2224 # BNF: offsetof_member_designator : identifier_or_typeid 

2225 # ('.' identifier_or_typeid | '[' expression ']')* 

2226 def _parse_offsetof_member_designator(self) -> c_ast.Node: 

2227 node = self._parse_identifier_or_typeid() 

2228 while True: 

2229 if self._accept("PERIOD"): 

2230 field = self._parse_identifier_or_typeid() 

2231 node = c_ast.StructRef(node, ".", field, node.coord) 

2232 continue 

2233 if self._accept("LBRACKET"): 

2234 expr = self._parse_expression() 

2235 self._expect("RBRACKET") 

2236 node = c_ast.ArrayRef(node, expr, node.coord) 

2237 continue 

2238 break 

2239 return node 

2240 

2241 # BNF: argument_expression_list : assignment_expression (',' assignment_expression)* 

2242 def _parse_argument_expression_list(self) -> c_ast.Node: 

2243 expr = self._parse_assignment_expression() 

2244 exprs = [expr] 

2245 while self._accept("COMMA"): 

2246 exprs.append(self._parse_assignment_expression()) 

2247 return c_ast.ExprList(exprs, expr.coord) 

2248 

2249 # BNF: constant_expression : conditional_expression 

2250 def _parse_constant_expression(self) -> c_ast.Node: 

2251 return self._parse_conditional_expression() 

2252 

2253 # ------------------------------------------------------------------ 

2254 # Terminals 

2255 # ------------------------------------------------------------------ 

2256 # BNF: identifier : ID 

2257 def _parse_identifier(self) -> c_ast.Node: 

2258 tok = self._expect("ID") 

2259 return c_ast.ID(tok.value, self._tok_coord(tok)) 

2260 

2261 # BNF: identifier_or_typeid : ID | TYPEID 

2262 def _parse_identifier_or_typeid(self) -> c_ast.ID: 

2263 tok = self._advance() 

2264 if tok.type not in {"ID", "TYPEID"}: 

2265 self._parse_error("Expected identifier", self._tok_coord(tok)) 

2266 return c_ast.ID(tok.value, self._tok_coord(tok)) 

2267 

2268 # BNF: constant : INT_CONST | FLOAT_CONST | CHAR_CONST | predefined_constant 

2269 def _parse_constant(self) -> c_ast.Node: 

2270 tok = self._advance() 

2271 if tok.type in _PREDEFINED_CONST: 

2272 return c_ast.Constant( 

2273 _PREDEFINED_CONST[tok.type], tok.value, self._tok_coord(tok) 

2274 ) 

2275 

2276 if tok.type in _INT_CONST: 

2277 # Integer suffixes are at most three characters (e.g. ull, wbu). 

2278 suffix = tok.value[-3:].lower() 

2279 if suffix.endswith(("wb", "wbu")): 

2280 if self._dialect != "c23": 

2281 self._parse_error( 

2282 "Bit-precise integer constants require C23", 

2283 self._tok_coord(tok), 

2284 ) 

2285 # Classify by suffix, as with other integer constants; retain 

2286 # the spelling without computing the value or its bit width. 

2287 t = "unsigned _BitInt" if "u" in suffix else "_BitInt" 

2288 return c_ast.Constant(t, tok.value, self._tok_coord(tok)) 

2289 

2290 u_count = 0 

2291 l_count = 0 

2292 for ch in tok.value[-3:]: 

2293 if ch in ("l", "L"): 

2294 l_count += 1 

2295 elif ch in ("u", "U"): 

2296 u_count += 1 

2297 if u_count > 1: 

2298 raise ValueError("Constant cannot have more than one u/U suffix.") 

2299 if l_count > 2: 

2300 raise ValueError("Constant cannot have more than two l/L suffix.") 

2301 prefix = "unsigned " * u_count + "long " * l_count 

2302 return c_ast.Constant(prefix + "int", tok.value, self._tok_coord(tok)) 

2303 

2304 if tok.type in _FLOAT_CONST: 

2305 if tok.value[-2:] in {"df", "DF"}: 

2306 t = "_Decimal32" 

2307 elif tok.value[-2:] in {"dd", "DD"}: 

2308 t = "_Decimal64" 

2309 elif tok.value[-2:] in {"dl", "DL"}: 

2310 t = "_Decimal128" 

2311 elif tok.value[-1] in ("f", "F"): 

2312 t = "float" 

2313 elif tok.value[-1] in ("l", "L"): 

2314 t = "long double" 

2315 else: 

2316 t = "double" 

2317 if ( 

2318 t in {"_Decimal32", "_Decimal64", "_Decimal128"} 

2319 and self._dialect != "c23" 

2320 ): 

2321 self._parse_error( 

2322 "Decimal floating constants require C23", self._tok_coord(tok) 

2323 ) 

2324 return c_ast.Constant(t, tok.value, self._tok_coord(tok)) 

2325 

2326 if tok.type in _CHAR_CONST: 

2327 return c_ast.Constant("char", tok.value, self._tok_coord(tok)) 

2328 

2329 self._parse_error("Invalid constant", self._tok_coord(tok)) 

2330 

2331 # BNF: unified_string_literal : (STRING_LITERAL | WSTRING_LITERAL 

2332 # | U8STRING_LITERAL | U16STRING_LITERAL 

2333 # | U32STRING_LITERAL)+ 

2334 def _parse_unified_string_literal(self) -> c_ast.Constant: 

2335 # Represent the whole run of adjacent literals with one Constant. 

2336 # C interprets escapes before concatenating the character sequences, 

2337 # whereas Constant.value holds source spelling. Simply removing the 

2338 # quotes between "\1" "23" would produce "\123", changing three 

2339 # characters into one (issue #392). 

2340 tok = self._advance() 

2341 if tok.type not in _STRING_LITERAL: 

2342 self._parse_error("Invalid string literal", self._tok_coord(tok)) 

2343 # Split off the optional encoding prefix and remove the closing quote. 

2344 # Each part holds unquoted source text that can safely share one literal. 

2345 prefix, contents = tok.value.split('"', 1) 

2346 parts = [contents[:-1]] 

2347 while self._peek_type() in _STRING_LITERAL: 

2348 tok2 = self._advance() 

2349 next_prefix, next_contents = tok2.value.split('"', 1) 

2350 # The first nonempty prefix applies to the whole run, even if it 

2351 # occurs after ordinary literals. Conflicting prefixes retain the 

2352 # parser's existing permissive behavior: the first one wins. 

2353 prefix = prefix or next_prefix 

2354 next_contents = next_contents[:-1] 

2355 # Extend the current part when safe; otherwise start a new one so 

2356 # a quote boundary survives. Empty literals can merge, but do not 

2357 # end a trailing escape: "\1" "" "23" still needs a boundary. 

2358 if self._string_literal_needs_separator(parts[-1], next_contents): 

2359 parts.append(next_contents) 

2360 else: 

2361 parts[-1] += next_contents 

2362 # Re-quote each part, keeping only the necessary literal boundaries. 

2363 # The prefix on the first literal applies to the adjacent sequence. 

2364 # The generator can emit this value directly without decoding escapes. 

2365 value = prefix + '"' + '" "'.join(parts) + '"' 

2366 return c_ast.Constant("string", value, self._tok_coord(tok)) 

2367 

2368 @staticmethod 

2369 def _string_literal_needs_separator(left: str, right: str) -> bool: 

2370 """Whether joining two literal bodies would change their C spelling.""" 

2371 if not left or not right: 

2372 return False 

2373 

2374 # A generated trigraph would be replaced before C string processing; 

2375 # for example, merging "?" "?=" would create the trigraph for '#'. 

2376 boundary = left[-2:] + right[:2] 

2377 if any("??" + ch in boundary for ch in "=/'()!<>-"): 

2378 return True 

2379 

2380 # Only an escape reaching the end of left can absorb characters from 

2381 # right. The last backslash is the only possible start of that escape. 

2382 escape_start = left.rfind("\\") 

2383 if escape_start == -1: 

2384 return False 

2385 # An even run of backslashes represents escaped backslashes, not the 

2386 # start of the numeric escape that the following digits might suggest. 

2387 run_start = escape_start 

2388 while run_start > 0 and left[run_start - 1] == "\\": 

2389 run_start -= 1 

2390 if (escape_start - run_start + 1) % 2 == 0: 

2391 return False 

2392 

2393 escape = left[escape_start + 1 :] 

2394 if escape.startswith("x"): 

2395 # Hex escapes have no digit limit: "\x1" "a" must stay separated. 

2396 hex_digits = "0123456789abcdefABCDEF" 

2397 return right[0] in hex_digits and all(ch in hex_digits for ch in escape[1:]) 

2398 # Octal escapes consume at most three digits: "\12" "3" needs a 

2399 # boundary, but "\123" "4" can become "\1234". Universal character names 

2400 # have fixed widths and cannot absorb characters from the next literal. 

2401 return ( 

2402 1 <= len(escape) < 3 

2403 and all(ch in "01234567" for ch in escape) 

2404 and right[0] in "01234567" 

2405 ) 

2406 

2407 # ------------------------------------------------------------------ 

2408 # Initializers 

2409 # ------------------------------------------------------------------ 

2410 # BNF: initializer : assignment_expression 

2411 # | '{' initializer_list ','? '}' 

2412 # | '{' '}' 

2413 def _parse_initializer(self) -> c_ast.Node: 

2414 lbrace_tok = self._accept("LBRACE") 

2415 if lbrace_tok: 

2416 if self._accept("RBRACE"): 

2417 return c_ast.InitList([], self._tok_coord(lbrace_tok)) 

2418 init_list = self._parse_initializer_list() 

2419 self._accept("COMMA") 

2420 self._expect("RBRACE") 

2421 return init_list 

2422 

2423 return self._parse_assignment_expression() 

2424 

2425 # BNF: initializer_list : initializer_item (',' initializer_item)* ','? 

2426 def _parse_initializer_list(self) -> c_ast.Node: 

2427 items = [self._parse_initializer_item()] 

2428 while self._accept("COMMA"): 

2429 if self._peek_type() == "RBRACE": 

2430 break 

2431 items.append(self._parse_initializer_item()) 

2432 return c_ast.InitList(items, items[0].coord) 

2433 

2434 # BNF: initializer_item : designation? initializer 

2435 def _parse_initializer_item(self) -> c_ast.Node: 

2436 designation = None 

2437 if self._peek_type() in {"LBRACKET", "PERIOD"}: 

2438 designation = self._parse_designation() 

2439 init = self._parse_initializer() 

2440 if designation is not None: 

2441 return c_ast.NamedInitializer(designation, init) 

2442 return init 

2443 

2444 # BNF: designation : designator_list '=' 

2445 def _parse_designation(self) -> list[c_ast.Node]: 

2446 designators = self._parse_designator_list() 

2447 self._expect("EQUALS") 

2448 return designators 

2449 

2450 # BNF: designator_list : designator+ 

2451 def _parse_designator_list(self) -> list[c_ast.Node]: 

2452 designators = [] 

2453 while self._peek_type() in {"LBRACKET", "PERIOD"}: 

2454 designators.append(self._parse_designator()) 

2455 return designators 

2456 

2457 # BNF: designator : '[' constant_expression ']' 

2458 # | '.' identifier_or_typeid 

2459 def _parse_designator(self) -> c_ast.Node: 

2460 if tok := self._accept("LBRACKET"): 

2461 expr = self._parse_constant_expression() 

2462 self._expect("RBRACKET") 

2463 return c_ast.ArrayDesignator(expr, self._tok_coord(tok)) 

2464 if self._accept("PERIOD"): 

2465 return self._parse_identifier_or_typeid() 

2466 self._parse_error("Invalid designator", self.clex.filename) 

2467 

2468 # ------------------------------------------------------------------ 

2469 # Preprocessor-like directives 

2470 # ------------------------------------------------------------------ 

2471 # BNF: pp_directive : '#' ... (unsupported) 

2472 def _parse_pp_directive(self) -> NoReturn: 

2473 tok = self._expect("PPHASH") 

2474 self._parse_error("Directives not supported yet", self._tok_coord(tok)) 

2475 

2476 # BNF: pppragma_directive : PPPRAGMA PPPRAGMASTR? 

2477 # | _PRAGMA '(' string_literal ')' 

2478 def _parse_pppragma_directive(self) -> c_ast.Node: 

2479 if self._peek_type() == "PPPRAGMA": 

2480 tok = self._advance() 

2481 if self._peek_type() == "PPPRAGMASTR": 

2482 str_tok = self._advance() 

2483 return c_ast.Pragma(str_tok.value, self._tok_coord(str_tok)) 

2484 return c_ast.Pragma("", self._tok_coord(tok)) 

2485 

2486 if self._peek_type() == "_PRAGMA": 

2487 tok = self._advance() 

2488 lparen = self._expect("LPAREN") 

2489 literal = self._parse_unified_string_literal() 

2490 self._expect("RPAREN") 

2491 return c_ast.Pragma(literal, self._tok_coord(lparen)) 

2492 

2493 self._parse_error("Invalid pragma", self.clex.filename) 

2494 

2495 # BNF: pppragma_directive_list : pppragma_directive+ 

2496 def _parse_pppragma_directive_list(self) -> list[c_ast.Node]: 

2497 pragmas = [] 

2498 while self._peek_type() in {"PPPRAGMA", "_PRAGMA"}: 

2499 pragmas.append(self._parse_pppragma_directive()) 

2500 return pragmas 

2501 

2502 # BNF: static_assert : _STATIC_ASSERT '(' constant_expression 

2503 # (',' unified_string_literal)? ')' ';' 

2504 # C11 requires a message; omission is supported as an extension. 

2505 def _parse_static_assert(self) -> c_ast.StaticAssert: 

2506 tok = self._expect("_STATIC_ASSERT") 

2507 self._expect("LPAREN") 

2508 cond = self._parse_constant_expression() 

2509 msg = None 

2510 if self._accept("COMMA"): 

2511 msg = self._parse_unified_string_literal() 

2512 self._expect("RPAREN") 

2513 self._expect("SEMI") 

2514 return c_ast.StaticAssert(cond, msg, self._tok_coord(tok)) 

2515 

2516 

2517_ASSIGNMENT_OPS = { 

2518 "EQUALS", 

2519 "XOREQUAL", 

2520 "TIMESEQUAL", 

2521 "DIVEQUAL", 

2522 "MODEQUAL", 

2523 "PLUSEQUAL", 

2524 "MINUSEQUAL", 

2525 "LSHIFTEQUAL", 

2526 "RSHIFTEQUAL", 

2527 "ANDEQUAL", 

2528 "OREQUAL", 

2529} 

2530 

2531# Precedence of operators (lower number = weather binding) 

2532# If this changes, c_generator.CGenerator.precedence_map needs to change as 

2533# well 

2534_BINARY_PRECEDENCE = { 

2535 "LOR": 0, 

2536 "LAND": 1, 

2537 "OR": 2, 

2538 "XOR": 3, 

2539 "AND": 4, 

2540 "EQ": 5, 

2541 "NE": 5, 

2542 "GT": 6, 

2543 "GE": 6, 

2544 "LT": 6, 

2545 "LE": 6, 

2546 "RSHIFT": 7, 

2547 "LSHIFT": 7, 

2548 "PLUS": 8, 

2549 "MINUS": 8, 

2550 "TIMES": 9, 

2551 "DIVIDE": 9, 

2552 "MOD": 9, 

2553} 

2554 

2555_STORAGE_CLASS = { 

2556 "AUTO", 

2557 "CONSTEXPR", 

2558 "REGISTER", 

2559 "STATIC", 

2560 "EXTERN", 

2561 "TYPEDEF", 

2562 "_THREAD_LOCAL", 

2563} 

2564 

2565_FUNCTION_SPEC = {"INLINE", "_NORETURN"} 

2566 

2567_TYPE_QUALIFIER = {"CONST", "RESTRICT", "VOLATILE", "_ATOMIC"} 

2568 

2569_TYPEOF_SPEC = {"TYPEOF", "TYPEOF_UNQUAL"} 

2570 

2571_TYPE_SPEC_SIMPLE = { 

2572 "VOID", 

2573 "_BOOL", 

2574 "CHAR", 

2575 "SHORT", 

2576 "INT", 

2577 "LONG", 

2578 "FLOAT", 

2579 "DOUBLE", 

2580 "_DECIMAL32", 

2581 "_DECIMAL64", 

2582 "_DECIMAL128", 

2583 "_COMPLEX", 

2584 "SIGNED", 

2585 "UNSIGNED", 

2586 "__INT128", 

2587} 

2588 

2589# Struct members without declarators carry a type specifier directly instead. 

2590_DECLARATOR_NODES = (c_ast.TypeDecl, c_ast.PtrDecl, c_ast.ArrayDecl, c_ast.FuncDecl) 

2591 

2592_DECL_START = ( 

2593 _STORAGE_CLASS 

2594 | _FUNCTION_SPEC 

2595 | _TYPE_QUALIFIER 

2596 | _TYPE_SPEC_SIMPLE 

2597 | _TYPEOF_SPEC 

2598 | { 

2599 "TYPEID", 

2600 "STRUCT", 

2601 "UNION", 

2602 "ENUM", 

2603 "_ALIGNAS", 

2604 "_ATOMIC", 

2605 "_BITINT", 

2606 "ATTRIBUTE_BEGIN", 

2607 } 

2608) 

2609 

2610_EXPR_START = { 

2611 "ID", 

2612 "LPAREN", 

2613 "PLUSPLUS", 

2614 "MINUSMINUS", 

2615 "PLUS", 

2616 "MINUS", 

2617 "TIMES", 

2618 "AND", 

2619 "NOT", 

2620 "LNOT", 

2621 "SIZEOF", 

2622 "_ALIGNOF", 

2623 "_GENERIC", 

2624 "OFFSETOF", 

2625 "__BUILTIN_VA_ARG", 

2626} 

2627 

2628_INT_CONST = { 

2629 "INT_CONST_DEC", 

2630 "INT_CONST_OCT", 

2631 "INT_CONST_HEX", 

2632 "INT_CONST_BIN", 

2633 "INT_CONST_CHAR", 

2634} 

2635 

2636_FLOAT_CONST = {"FLOAT_CONST", "HEX_FLOAT_CONST"} 

2637 

2638_PREDEFINED_CONST = {"BOOL_CONST": "bool", "NULLPTR": "nullptr_t"} 

2639 

2640_CHAR_CONST = { 

2641 "CHAR_CONST", 

2642 "WCHAR_CONST", 

2643 "U8CHAR_CONST", 

2644 "U16CHAR_CONST", 

2645 "U32CHAR_CONST", 

2646} 

2647 

2648_STRING_LITERAL = { 

2649 "STRING_LITERAL", 

2650 "WSTRING_LITERAL", 

2651 "U8STRING_LITERAL", 

2652 "U16STRING_LITERAL", 

2653 "U32STRING_LITERAL", 

2654} 

2655 

2656_STARTS_EXPRESSION = ( 

2657 _EXPR_START 

2658 | _INT_CONST 

2659 | _FLOAT_CONST 

2660 | _CHAR_CONST 

2661 | _STRING_LITERAL 

2662 | _PREDEFINED_CONST.keys() 

2663) 

2664 

2665_STARTS_STATEMENT = { 

2666 "LBRACE", 

2667 "IF", 

2668 "SWITCH", 

2669 "WHILE", 

2670 "DO", 

2671 "FOR", 

2672 "GOTO", 

2673 "BREAK", 

2674 "CONTINUE", 

2675 "RETURN", 

2676 "CASE", 

2677 "DEFAULT", 

2678 "PPPRAGMA", 

2679 "_PRAGMA", 

2680 "SEMI", 

2681} 

2682 

2683 

2684class _TokenStream: 

2685 """Provides buffered access to tokens supplied by a reader.""" 

2686 

2687 def __init__(self, read_token: Callable[[], Token | None]) -> None: 

2688 self._read_token = read_token 

2689 self._buffer: list[Token | None] = [] 

2690 self._index = 0 

2691 

2692 def peek(self, k: int = 1) -> Token | None: 

2693 """Peek at the k-th next token in the stream, without consuming it. 

2694 

2695 Examples: 

2696 k=1 returns the immediate next token. 

2697 k=2 returns the token after that. 

2698 """ 

2699 if k <= 0: 

2700 return None 

2701 pos = self._index + k - 1 

2702 if pos >= len(self._buffer): 

2703 self._fill(k) 

2704 return self._buffer[pos] 

2705 

2706 def next(self) -> Token | None: 

2707 """Consume a single token and return it.""" 

2708 if self._index >= len(self._buffer): 

2709 self._fill(1) 

2710 tok = self._buffer[self._index] 

2711 self._index += 1 

2712 return tok 

2713 

2714 # The 'mark' and 'reset' methods are useful for speculative parsing with 

2715 # backtracking; when the parser needs to examine a sequence of tokens 

2716 # and potentially decide to try a different path on the same sequence, it 

2717 # can call 'mark' to obtain the current token position, and if the first 

2718 # path fails restore the position with `reset(pos)`. 

2719 def mark(self) -> int: 

2720 return self._index 

2721 

2722 def reset(self, mark: int) -> None: 

2723 self._index = mark 

2724 

2725 def _fill(self, n: int) -> None: 

2726 while len(self._buffer) < self._index + n: 

2727 tok = self._read_token() 

2728 self._buffer.append(tok) 

2729 if tok is None: 

2730 break 

2731 

2732 

2733# Declaration specifiers are represented by a dictionary with entries: 

2734# - qual: a list of type qualifiers 

2735# - storage: a list of storage class specifiers 

2736# - type: a list of type specifiers 

2737# - function: a list of function specifiers 

2738# - alignment: a list of alignment specifiers 

2739# - type_attributes: attribute groups following the specifier sequence 

2740class _DeclSpec(TypedDict): 

2741 qual: list[str] 

2742 storage: list[str] 

2743 type: list[c_ast.Node] 

2744 function: list[str] 

2745 alignment: list[c_ast.Alignas] 

2746 type_attributes: list[c_ast.AttributeSpecifier] 

2747 

2748 

2749_DeclSpecKind = Literal["qual", "storage", "type", "function", "alignment"] 

2750 

2751 

2752class _DeclInfo(TypedDict): 

2753 # Declarator payloads used by declaration/initializer parsing: 

2754 # - decl: the declarator node (may be None for abstract/implicit cases) 

2755 # - init: initializer expression; the key may be omitted when absent 

2756 # - bitsize: bit-field width expression; the key may be omitted when absent 

2757 decl: c_ast.Node | None 

2758 init: NotRequired[c_ast.Node | None] 

2759 bitsize: NotRequired[c_ast.Node | None]