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

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

1309 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 dataclasses import dataclass 

10from typing import ( 

11 Any, 

12 Literal, 

13 NoReturn, 

14 Optional, 

15 TypedDict, 

16 cast, 

17) 

18 

19from . import c_ast 

20from .ast_transforms import fix_atomic_specifiers, fix_switch_cases 

21from .c_lexer import CLexer, Token 

22 

23 

24@dataclass 

25class Coord: 

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

27 - File name 

28 - Line number 

29 - Column number 

30 """ 

31 

32 file: str 

33 line: int 

34 column: int | None = None 

35 

36 def __str__(self) -> str: 

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

38 if self.column is not None: 

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

40 return text 

41 

42 

43class ParseError(Exception): 

44 pass 

45 

46 

47class CParser: 

48 """Recursive-descent C parser. 

49 

50 Usage: 

51 parser = CParser() 

52 ast = parser.parse(text, filename) 

53 

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

55 The parameters after `lexer` are accepted for backward compatibility with 

56 the old PLY-based parser and are otherwise unused. 

57 """ 

58 

59 def __init__( 

60 self, 

61 lex_optimize: bool = True, 

62 lexer: type[CLexer] = CLexer, 

63 lextab: str = "pycparser.lextab", 

64 yacc_optimize: bool = True, 

65 yacctab: str = "pycparser.yacctab", 

66 yacc_debug: bool = False, 

67 taboutputdir: str = "", 

68 ) -> None: 

69 self.clex: CLexer = lexer( 

70 error_func=self._lex_error_func, 

71 on_lbrace_func=self._lex_on_lbrace_func, 

72 on_rbrace_func=self._lex_on_rbrace_func, 

73 type_lookup_func=self._lex_type_lookup_func, 

74 ) 

75 

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

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

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

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

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

81 # saw: int name;) 

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

83 # in this scope at all. 

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

85 self._tokens: _TokenStream = _TokenStream(self.clex) 

86 

87 def parse( 

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

89 ) -> c_ast.FileAST: 

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

91 

92 text: 

93 A string containing the C source code 

94 

95 filename: 

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

97 

98 debug: 

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

100 """ 

101 self._scope_stack = [{}] 

102 self.clex.input(text, filename) 

103 self._tokens = _TokenStream(self.clex) 

104 

105 ast = self._parse_translation_unit_or_empty() 

106 tok = self._peek() 

107 if tok is not None: 

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

109 return ast 

110 

111 # ------------------------------------------------------------------ 

112 # Scope and declaration helpers 

113 # ------------------------------------------------------------------ 

114 def _coord(self, lineno: int, column: int | None = None) -> Coord: 

115 return Coord(file=self.clex.filename, line=lineno, column=column) 

116 

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

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

119 

120 def _push_scope(self) -> None: 

121 self._scope_stack.append({}) 

122 

123 def _pop_scope(self) -> None: 

124 if len(self._scope_stack) <= 1: 

125 raise ParseError("Unmatched '}'") 

126 self._scope_stack.pop() 

127 

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

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

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

131 self._parse_error( 

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

133 coord, 

134 ) 

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

136 

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

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

139 current scope 

140 """ 

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

142 self._parse_error( 

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

144 coord, 

145 ) 

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

147 

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

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

150 for scope in reversed(self._scope_stack): 

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

152 # higher scopes. 

153 if name in scope: 

154 return scope[name] 

155 return False 

156 

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

158 self._parse_error(msg, self._coord(line, column)) 

159 

160 def _lex_on_lbrace_func(self) -> None: 

161 self._push_scope() 

162 

163 def _lex_on_rbrace_func(self) -> None: 

164 self._pop_scope() 

165 

166 def _lex_type_lookup_func(self, name: str) -> bool: 

167 """Looks up types that were previously defined with typedef. 

168 

169 Passed to the lexer for recognizing identifiers that are types. 

170 """ 

171 return self._is_type_in_scope(name) 

172 

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

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

175 # 

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

177 # of modifiers. For example: 

178 # 

179 # int *c[5]; 

180 # 

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

182 # the array are the modifiers. 

183 # 

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

185 # modifiers are FuncDecl, PtrDecl and ArrayDecl. 

186 # 

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

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

189 # 

190 # K&R2 A.8.6.2: Array Declarators 

191 # 

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

193 # D1 [constant-expression-opt] 

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

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

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

197 # 

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

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

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

201 # the TypeDecl. 

202 # 

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

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

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

206 # into the new location. 

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

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

209 the modified declarator. 

210 

211 Note: the declarator and modifier may be modified 

212 """ 

213 modifier_head = modifier 

214 modifier_tail = modifier 

215 

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

217 while modifier_tail.type: 

218 modifier_tail = modifier_tail.type 

219 

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

221 if isinstance(decl, c_ast.TypeDecl): 

222 modifier_tail.type = decl 

223 return modifier 

224 else: 

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

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

227 # pointing to the underlying basic type. 

228 decl_tail = decl 

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

230 decl_tail = decl_tail.type 

231 

232 modifier_tail.type = decl_tail.type 

233 decl_tail.type = modifier_head 

234 return decl 

235 

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

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

238 # 

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

240 # these problems: 

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

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

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

244 # in the innermost TypeDecl 

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

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

247 # should be separated from more complex types like enums 

248 # and structs. 

249 # 

250 # This method fixes these problems. 

251 def _fix_decl_name_type( 

252 self, 

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

254 typename: list[Any], 

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

256 """Fixes a declaration. Modifies decl.""" 

257 # Reach the underlying basic type 

258 typ = decl 

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

260 typ = typ.type 

261 

262 decl.name = typ.declname 

263 typ.quals = decl.quals[:] 

264 

265 # The typename is a list of types. If any type in this 

266 # list isn't an IdentifierType, it must be the only 

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

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

269 # IdentifierType holder. 

270 for tn in typename: 

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

272 if len(typename) > 1: 

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

274 else: 

275 typ.type = tn 

276 return decl 

277 

278 if not typename: 

279 # Functions default to returning int 

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

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

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

283 else: 

284 # At this point, we know that typename is a list of IdentifierType 

285 # nodes. Concatenate all the names into a single list. 

286 typ.type = c_ast.IdentifierType( 

287 [name for id in typename for name in id.names], coord=typename[0].coord 

288 ) 

289 return decl 

290 

291 def _add_declaration_specifier( 

292 self, 

293 declspec: Optional["_DeclSpec"], 

294 newspec: Any, 

295 kind: "_DeclSpecKind", 

296 append: bool = False, 

297 ) -> "_DeclSpec": 

298 """See _DeclSpec for the specifier dictionary layout.""" 

299 if declspec is None: 

300 spec: _DeclSpec = { 

301 "qual": [], 

302 "storage": [], 

303 "type": [], 

304 "function": [], 

305 "alignment": [], 

306 } 

307 else: 

308 spec = declspec 

309 

310 if append: 

311 spec[kind].append(newspec) 

312 else: 

313 spec[kind].insert(0, newspec) 

314 

315 return spec 

316 

317 def _build_declarations( 

318 self, 

319 spec: "_DeclSpec", 

320 decls: list["_DeclInfo"], 

321 typedef_namespace: bool = False, 

322 ) -> list[c_ast.Node]: 

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

324 If typedef_namespace is true, each declared name is added 

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

326 functions, and enum constants. 

327 """ 

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

329 declarations = [] 

330 

331 # Bit-fields are allowed to be unnamed. 

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

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

334 # problem can occur where the identifier gets grouped into 

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

336 # first declarator. 

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

338 if ( 

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

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

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

342 ): 

343 coord = "?" 

344 for t in spec["type"]: 

345 if hasattr(t, "coord"): 

346 coord = t.coord 

347 break 

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

349 

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

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

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

353 type=None, 

354 quals=None, 

355 align=spec["alignment"], 

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

357 ) 

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

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

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

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

362 # the case. 

363 elif not isinstance( 

364 decls[0]["decl"], 

365 (c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType), 

366 ): 

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

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

369 decls_0_tail = decls_0_tail.type 

370 if decls_0_tail.declname is None: 

371 decls_0_tail.declname = spec["type"][-1].names[0] 

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

373 

374 for decl in decls: 

375 assert decl["decl"] is not None 

376 if is_typedef: 

377 declaration = c_ast.Typedef( 

378 name=None, 

379 quals=spec["qual"], 

380 storage=spec["storage"], 

381 type=decl["decl"], 

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

383 ) 

384 else: 

385 declaration = c_ast.Decl( 

386 name=None, 

387 quals=spec["qual"], 

388 align=spec["alignment"], 

389 storage=spec["storage"], 

390 funcspec=spec["function"], 

391 type=decl["decl"], 

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

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

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

395 ) 

396 

397 if isinstance( 

398 declaration.type, 

399 (c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType), 

400 ): 

401 fixed_decl = declaration 

402 else: 

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

404 

405 # Add the type name defined by typedef to a 

406 # symbol table (for usage in the lexer) 

407 if typedef_namespace: 

408 if is_typedef: 

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

410 else: 

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

412 

413 fixed_decl = fix_atomic_specifiers( 

414 cast(c_ast.Decl | c_ast.Typedef, fixed_decl) 

415 ) 

416 declarations.append(fixed_decl) 

417 

418 return declarations 

419 

420 def _build_function_definition( 

421 self, 

422 spec: "_DeclSpec", 

423 decl: c_ast.Node, 

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

425 body: c_ast.Node, 

426 ) -> c_ast.Node: 

427 """Builds a function definition.""" 

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

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

430 

431 declaration = self._build_declarations( 

432 spec=spec, 

433 decls=[{"decl": decl, "init": None, "bitsize": None}], 

434 typedef_namespace=True, 

435 )[0] 

436 

437 return c_ast.FuncDef( 

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

439 ) 

440 

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

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

443 appropriate AST class. 

444 """ 

445 if token == "struct": 

446 return c_ast.Struct 

447 else: 

448 return c_ast.Union 

449 

450 # ------------------------------------------------------------------ 

451 # Token helpers 

452 # ------------------------------------------------------------------ 

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

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

455 return self._tokens.peek(k) 

456 

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

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

459 tok = self._peek(k) 

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

461 

462 def _advance(self) -> Token: 

463 tok = self._tokens.next() 

464 if tok is None: 

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

466 else: 

467 return tok 

468 

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

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

471 

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

473 Otherwise, leaves the token intact and returns None. 

474 """ 

475 tok = self._peek() 

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

477 return self._advance() 

478 return None 

479 

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

481 tok = self._advance() 

482 if tok.type != token_type: 

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

484 return tok 

485 

486 def _mark(self) -> int: 

487 return self._tokens.mark() 

488 

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

490 self._tokens.reset(mark) 

491 

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

493 return self._coord(tok.lineno, tok.column) 

494 

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

496 tok = tok or self._peek() 

497 if tok is None: 

498 return False 

499 return tok.type in _DECL_START 

500 

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

502 tok = tok or self._peek() 

503 if tok is None: 

504 return False 

505 return tok.type in _STARTS_EXPRESSION 

506 

507 def _starts_statement(self) -> bool: 

508 tok_type = self._peek_type() 

509 if tok_type is None: 

510 return False 

511 if tok_type in _STARTS_STATEMENT: 

512 return True 

513 return self._starts_expression() 

514 

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

516 tok_type = self._peek_type() 

517 if tok_type is None: 

518 return False 

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

520 return True 

521 if id_only: 

522 return tok_type == "ID" 

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

524 

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

526 mark = self._mark() 

527 tok_type, saw_paren = self._scan_declarator_name_info() 

528 self._reset(mark) 

529 return tok_type, saw_paren 

530 

531 def _parse_any_declarator( 

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

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

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

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

536 # typedef char TT; 

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

538 name_type, saw_paren = self._peek_declarator_name_info() 

539 if name_type is None or ( 

540 typeid_paren_as_abstract and name_type == "TYPEID" and saw_paren 

541 ): 

542 if not allow_abstract: 

543 tok = self._peek() 

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

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

546 decl = self._parse_abstract_declarator_opt() 

547 return decl, False 

548 

549 if name_type == "TYPEID": 

550 if typeid_paren_as_abstract: 

551 decl = self._parse_typeid_noparen_declarator() 

552 else: 

553 decl = self._parse_typeid_declarator() 

554 else: 

555 decl = self._parse_id_declarator() 

556 return decl, True 

557 

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

559 saw_paren = False 

560 while self._accept("TIMES"): 

561 while self._peek_type() in _TYPE_QUALIFIER: 

562 self._advance() 

563 

564 tok = self._peek() 

565 if tok is None: 

566 return None, saw_paren 

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

568 self._advance() 

569 return tok.type, saw_paren 

570 if tok.type == "LPAREN": 

571 saw_paren = True 

572 self._advance() 

573 tok_type, nested_paren = self._scan_declarator_name_info() 

574 if nested_paren: 

575 saw_paren = True 

576 depth = 1 

577 while True: 

578 tok = self._peek() 

579 if tok is None: 

580 return None, saw_paren 

581 if tok.type == "LPAREN": 

582 depth += 1 

583 elif tok.type == "RPAREN": 

584 depth -= 1 

585 self._advance() 

586 if depth == 0: 

587 break 

588 continue 

589 self._advance() 

590 return tok_type, saw_paren 

591 return None, saw_paren 

592 

593 def _starts_direct_abstract_declarator(self) -> bool: 

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

595 

596 def _is_assignment_op(self) -> bool: 

597 tok = self._peek() 

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

599 

600 def _try_parse_paren_type_name( 

601 self, 

602 ) -> tuple[c_ast.Typename, int, Token] | None: 

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

604 

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

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

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

608 token. Returns None if no parenthesized type name is present. 

609 """ 

610 mark = self._mark() 

611 lparen_tok = self._accept("LPAREN") 

612 if lparen_tok is None: 

613 return None 

614 if not self._starts_declaration(): 

615 self._reset(mark) 

616 return None 

617 typ = self._parse_type_name() 

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

619 self._reset(mark) 

620 return None 

621 return typ, mark, lparen_tok 

622 

623 # ------------------------------------------------------------------ 

624 # Top-level 

625 # ------------------------------------------------------------------ 

626 # BNF: translation_unit_or_empty : translation_unit | empty 

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

628 if self._peek() is None: 

629 return c_ast.FileAST([]) 

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

631 

632 # BNF: translation_unit : external_declaration+ 

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

634 ext = [] 

635 while self._peek() is not None: 

636 ext.extend(self._parse_external_declaration()) 

637 return ext 

638 

639 # BNF: external_declaration : function_definition 

640 # | declaration 

641 # | pp_directive 

642 # | pppragma_directive 

643 # | static_assert 

644 # | ';' 

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

646 tok = self._peek() 

647 if tok is None: 

648 return [] 

649 if tok.type == "PPHASH": 

650 self._parse_pp_directive() 

651 return [] 

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

653 return [self._parse_pppragma_directive()] 

654 if self._accept("SEMI"): 

655 return [] 

656 if tok.type == "_STATIC_ASSERT": 

657 return self._parse_static_assert() 

658 

659 if not self._starts_declaration(tok): 

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

661 # implicit return type, e.g. 

662 # 

663 # foo() { 

664 # return 5; 

665 # } 

666 # 

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

668 decl = self._parse_id_declarator() 

669 param_decls = None 

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

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

672 spec: _DeclSpec = { 

673 "qual": [], 

674 "alignment": [], 

675 "storage": [], 

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

677 "function": [], 

678 } 

679 func = self._build_function_definition( 

680 spec=spec, 

681 decl=decl, 

682 param_decls=param_decls, 

683 body=self._parse_compound_statement(), 

684 ) 

685 return [func] 

686 

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

688 spec, saw_type, spec_coord = self._parse_declaration_specifiers( 

689 allow_no_type=True 

690 ) 

691 

692 name_type, _ = self._peek_declarator_name_info() 

693 if name_type != "ID": 

694 decls = self._parse_decl_body_with_spec(spec, saw_type) 

695 self._expect("SEMI") 

696 return decls 

697 

698 decl = self._parse_id_declarator() 

699 

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

701 param_decls = None 

702 if self._starts_declaration(): 

703 param_decls = self._parse_declaration_list() 

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

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

706 if not spec["type"]: 

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

708 func = self._build_function_definition( 

709 spec=spec, 

710 decl=decl, 

711 param_decls=param_decls, 

712 body=self._parse_compound_statement(), 

713 ) 

714 return [func] 

715 

716 decl_dict: _DeclInfo = {"decl": decl, "init": None, "bitsize": None} 

717 if self._accept("EQUALS"): 

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

719 decls = self._parse_init_declarator_list(first=decl_dict) 

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

721 self._expect("SEMI") 

722 return decls 

723 

724 # ------------------------------------------------------------------ 

725 # Declarations 

726 # 

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

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

729 # even if it contains a single element. 

730 # ------------------------------------------------------------------ 

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

732 decls = self._parse_decl_body() 

733 self._expect("SEMI") 

734 return decls 

735 

736 # BNF: decl_body : declaration_specifiers decl_body_with_spec 

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

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

739 return self._parse_decl_body_with_spec(spec, saw_type) 

740 

741 # BNF: decl_body_with_spec : init_declarator_list 

742 # | struct_or_union_or_enum_only 

743 def _parse_decl_body_with_spec( 

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

745 ) -> list[c_ast.Node]: 

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

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

748 decl_infos: list[_DeclInfo] | None = None 

749 if saw_type: 

750 if self._starts_declarator(): 

751 decl_infos = self._parse_init_declarator_list() 

752 else: 

753 if self._starts_declarator(id_only=True): 

754 decl_infos = self._parse_init_declarator_list(id_only=True) 

755 

756 decls: list[c_ast.Node] 

757 if decl_infos is None: 

758 ty = spec["type"] 

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

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

761 decls = [ 

762 c_ast.Decl( 

763 name=None, 

764 quals=spec["qual"], 

765 align=spec["alignment"], 

766 storage=spec["storage"], 

767 funcspec=spec["function"], 

768 type=ty[0], 

769 init=None, 

770 bitsize=None, 

771 coord=ty[0].coord, 

772 ) 

773 ] 

774 else: 

775 decls = self._build_declarations( 

776 spec=spec, 

777 decls=[{"decl": None, "init": None, "bitsize": None}], 

778 typedef_namespace=True, 

779 ) 

780 else: 

781 decls = self._build_declarations( 

782 spec=spec, decls=decl_infos, typedef_namespace=True 

783 ) 

784 

785 return decls 

786 

787 # BNF: declaration_list : declaration+ 

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

789 decls = [] 

790 while self._starts_declaration(): 

791 decls.extend(self._parse_declaration()) 

792 return decls 

793 

794 # BNF: declaration_specifiers : (storage_class_specifier 

795 # | type_specifier 

796 # | type_qualifier 

797 # | function_specifier 

798 # | alignment_specifier)+ 

799 def _parse_declaration_specifiers( 

800 self, allow_no_type: bool = False 

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

802 """Parse declaration-specifier sequence. 

803 

804 allow_no_type: 

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

806 

807 Returns: 

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

809 qual/storage/type/function/alignment entries, saw_type is True 

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

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

812 """ 

813 spec = None 

814 saw_type = False 

815 first_coord = None 

816 

817 while True: 

818 tok = self._peek() 

819 if tok is None: 

820 break 

821 

822 if tok.type == "_ALIGNAS": 

823 if first_coord is None: 

824 first_coord = self._tok_coord(tok) 

825 spec = self._add_declaration_specifier( 

826 spec, self._parse_alignment_specifier(), "alignment", append=True 

827 ) 

828 continue 

829 

830 if tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN": 

831 if first_coord is None: 

832 first_coord = self._tok_coord(tok) 

833 spec = self._add_declaration_specifier( 

834 spec, self._parse_atomic_specifier(), "type", append=True 

835 ) 

836 saw_type = True 

837 continue 

838 

839 if tok.type in _TYPE_QUALIFIER: 

840 if first_coord is None: 

841 first_coord = self._tok_coord(tok) 

842 spec = self._add_declaration_specifier( 

843 spec, self._advance().value, "qual", append=True 

844 ) 

845 continue 

846 

847 if tok.type in _STORAGE_CLASS: 

848 if first_coord is None: 

849 first_coord = self._tok_coord(tok) 

850 spec = self._add_declaration_specifier( 

851 spec, self._advance().value, "storage", append=True 

852 ) 

853 continue 

854 

855 if tok.type in _FUNCTION_SPEC: 

856 if first_coord is None: 

857 first_coord = self._tok_coord(tok) 

858 spec = self._add_declaration_specifier( 

859 spec, self._advance().value, "function", append=True 

860 ) 

861 continue 

862 

863 if tok.type in _TYPE_SPEC_SIMPLE: 

864 if first_coord is None: 

865 first_coord = self._tok_coord(tok) 

866 tok = self._advance() 

867 spec = self._add_declaration_specifier( 

868 spec, 

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

870 "type", 

871 append=True, 

872 ) 

873 saw_type = True 

874 continue 

875 

876 if tok.type == "TYPEID": 

877 if saw_type: 

878 break 

879 if first_coord is None: 

880 first_coord = self._tok_coord(tok) 

881 tok = self._advance() 

882 spec = self._add_declaration_specifier( 

883 spec, 

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

885 "type", 

886 append=True, 

887 ) 

888 saw_type = True 

889 continue 

890 

891 if tok.type in {"STRUCT", "UNION"}: 

892 if first_coord is None: 

893 first_coord = self._tok_coord(tok) 

894 spec = self._add_declaration_specifier( 

895 spec, self._parse_struct_or_union_specifier(), "type", append=True 

896 ) 

897 saw_type = True 

898 continue 

899 

900 if tok.type == "ENUM": 

901 if first_coord is None: 

902 first_coord = self._tok_coord(tok) 

903 spec = self._add_declaration_specifier( 

904 spec, self._parse_enum_specifier(), "type", append=True 

905 ) 

906 saw_type = True 

907 continue 

908 

909 break 

910 

911 if spec is None: 

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

913 

914 if not saw_type and not allow_no_type: 

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

916 

917 return spec, saw_type, first_coord 

918 

919 # BNF: specifier_qualifier_list : (type_specifier 

920 # | type_qualifier 

921 # | alignment_specifier)+ 

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

923 spec = None 

924 saw_type = False 

925 saw_alignment = False 

926 first_coord = None 

927 

928 while True: 

929 tok = self._peek() 

930 if tok is None: 

931 break 

932 

933 if tok.type == "_ALIGNAS": 

934 if first_coord is None: 

935 first_coord = self._tok_coord(tok) 

936 spec = self._add_declaration_specifier( 

937 spec, self._parse_alignment_specifier(), "alignment", append=True 

938 ) 

939 saw_alignment = True 

940 continue 

941 

942 if tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN": 

943 if first_coord is None: 

944 first_coord = self._tok_coord(tok) 

945 spec = self._add_declaration_specifier( 

946 spec, self._parse_atomic_specifier(), "type", append=True 

947 ) 

948 saw_type = True 

949 continue 

950 

951 if tok.type in _TYPE_QUALIFIER: 

952 if first_coord is None: 

953 first_coord = self._tok_coord(tok) 

954 spec = self._add_declaration_specifier( 

955 spec, self._advance().value, "qual", append=True 

956 ) 

957 continue 

958 

959 if tok.type in _TYPE_SPEC_SIMPLE: 

960 if first_coord is None: 

961 first_coord = self._tok_coord(tok) 

962 tok = self._advance() 

963 spec = self._add_declaration_specifier( 

964 spec, 

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

966 "type", 

967 append=True, 

968 ) 

969 saw_type = True 

970 continue 

971 

972 if tok.type == "TYPEID": 

973 if saw_type: 

974 break 

975 if first_coord is None: 

976 first_coord = self._tok_coord(tok) 

977 tok = self._advance() 

978 spec = self._add_declaration_specifier( 

979 spec, 

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

981 "type", 

982 append=True, 

983 ) 

984 saw_type = True 

985 continue 

986 

987 if tok.type in {"STRUCT", "UNION"}: 

988 if first_coord is None: 

989 first_coord = self._tok_coord(tok) 

990 spec = self._add_declaration_specifier( 

991 spec, self._parse_struct_or_union_specifier(), "type", append=True 

992 ) 

993 saw_type = True 

994 continue 

995 

996 if tok.type == "ENUM": 

997 if first_coord is None: 

998 first_coord = self._tok_coord(tok) 

999 spec = self._add_declaration_specifier( 

1000 spec, self._parse_enum_specifier(), "type", append=True 

1001 ) 

1002 saw_type = True 

1003 continue 

1004 

1005 break 

1006 

1007 if spec is None: 

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

1009 

1010 if not saw_type and not saw_alignment: 

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

1012 

1013 if spec.get("storage") is None: 

1014 spec["storage"] = [] 

1015 if spec.get("function") is None: 

1016 spec["function"] = [] 

1017 

1018 return spec 

1019 

1020 # BNF: type_qualifier_list : type_qualifier+ 

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

1022 quals = [] 

1023 while self._peek_type() in _TYPE_QUALIFIER: 

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

1025 return quals 

1026 

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

1028 def _parse_alignment_specifier(self) -> c_ast.Node: 

1029 tok = self._expect("_ALIGNAS") 

1030 self._expect("LPAREN") 

1031 

1032 if self._starts_declaration(): 

1033 typ = self._parse_type_name() 

1034 self._expect("RPAREN") 

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

1036 

1037 expr = self._parse_constant_expression() 

1038 self._expect("RPAREN") 

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

1040 

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

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

1043 self._expect("_ATOMIC") 

1044 self._expect("LPAREN") 

1045 typ = self._parse_type_name() 

1046 self._expect("RPAREN") 

1047 typ.quals.append("_Atomic") 

1048 return typ 

1049 

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

1051 def _parse_init_declarator_list( 

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

1053 ) -> list["_DeclInfo"]: 

1054 decls = ( 

1055 [first] 

1056 if first is not None 

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

1058 ) 

1059 

1060 while self._accept("COMMA"): 

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

1062 return decls 

1063 

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

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

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

1067 init = None 

1068 if self._accept("EQUALS"): 

1069 init = self._parse_initializer() 

1070 return {"decl": decl, "init": init, "bitsize": None} 

1071 

1072 # ------------------------------------------------------------------ 

1073 # Structs/unions/enums 

1074 # ------------------------------------------------------------------ 

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

1076 # | struct_or_union ID 

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

1078 tok = self._advance() 

1079 klass = self._select_struct_union_class(tok.value) 

1080 

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

1082 name_tok = self._advance() 

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

1084 self._advance() 

1085 if self._accept("RBRACE"): 

1086 return klass( 

1087 name=name_tok.value, decls=[], coord=self._tok_coord(name_tok) 

1088 ) 

1089 decls = self._parse_struct_declaration_list() 

1090 self._expect("RBRACE") 

1091 return klass( 

1092 name=name_tok.value, decls=decls, coord=self._tok_coord(name_tok) 

1093 ) 

1094 

1095 return klass( 

1096 name=name_tok.value, decls=None, coord=self._tok_coord(name_tok) 

1097 ) 

1098 

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

1100 brace_tok = self._advance() 

1101 if self._accept("RBRACE"): 

1102 return klass(name=None, decls=[], coord=self._tok_coord(brace_tok)) 

1103 decls = self._parse_struct_declaration_list() 

1104 self._expect("RBRACE") 

1105 return klass(name=None, decls=decls, coord=self._tok_coord(brace_tok)) 

1106 

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

1108 

1109 # BNF: struct_declaration_list : struct_declaration+ 

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

1111 decls = [] 

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

1113 items = self._parse_struct_declaration() 

1114 if items is None: 

1115 continue 

1116 decls.extend(items) 

1117 return decls 

1118 

1119 # BNF: struct_declaration : specifier_qualifier_list struct_declarator_list? ';' 

1120 # | static_assert 

1121 # | pppragma_directive 

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

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

1124 self._advance() 

1125 return None 

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

1127 return [self._parse_pppragma_directive()] 

1128 

1129 spec = self._parse_specifier_qualifier_list() 

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

1131 

1132 decls = None 

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

1134 decls = self._parse_struct_declarator_list() 

1135 if decls is not None: 

1136 self._expect("SEMI") 

1137 return self._build_declarations(spec=spec, decls=decls) 

1138 

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

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

1141 if isinstance(node, c_ast.Node): 

1142 decl_type = node 

1143 else: 

1144 decl_type = c_ast.IdentifierType(node) 

1145 self._expect("SEMI") 

1146 return self._build_declarations( 

1147 spec=spec, 

1148 decls=[{"decl": decl_type, "init": None, "bitsize": None}], 

1149 ) 

1150 

1151 self._expect("SEMI") 

1152 return self._build_declarations( 

1153 spec=spec, decls=[{"decl": None, "init": None, "bitsize": None}] 

1154 ) 

1155 

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

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

1158 decls = [self._parse_struct_declarator()] 

1159 while self._accept("COMMA"): 

1160 decls.append(self._parse_struct_declarator()) 

1161 return decls 

1162 

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

1164 # | declarator (':' constant_expression)? 

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

1166 if self._accept("COLON"): 

1167 bitsize = self._parse_constant_expression() 

1168 return { 

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

1170 "init": None, 

1171 "bitsize": bitsize, 

1172 } 

1173 

1174 decl = self._parse_declarator() 

1175 if self._accept("COLON"): 

1176 bitsize = self._parse_constant_expression() 

1177 return {"decl": decl, "init": None, "bitsize": bitsize} 

1178 

1179 return {"decl": decl, "init": None, "bitsize": None} 

1180 

1181 # BNF: enum_specifier : ENUM ID? '{' enumerator_list? '}' 

1182 # | ENUM ID 

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

1184 tok = self._expect("ENUM") 

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

1186 name_tok = self._advance() 

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

1188 self._advance() 

1189 enums = self._parse_enumerator_list() 

1190 self._expect("RBRACE") 

1191 return c_ast.Enum(name_tok.value, enums, self._tok_coord(tok)) 

1192 return c_ast.Enum(name_tok.value, None, self._tok_coord(tok)) 

1193 

1194 self._expect("LBRACE") 

1195 enums = self._parse_enumerator_list() 

1196 self._expect("RBRACE") 

1197 return c_ast.Enum(None, enums, self._tok_coord(tok)) 

1198 

1199 # BNF: enumerator_list : enumerator (',' enumerator)* ','? 

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

1201 enum = self._parse_enumerator() 

1202 enum_list = c_ast.EnumeratorList([enum], enum.coord) 

1203 while self._accept("COMMA"): 

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

1205 break 

1206 enum = self._parse_enumerator() 

1207 enum_list.enumerators.append(enum) 

1208 return enum_list 

1209 

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

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

1212 name_tok = self._expect("ID") 

1213 if self._accept("EQUALS"): 

1214 value = self._parse_constant_expression() 

1215 else: 

1216 value = None 

1217 enum = c_ast.Enumerator(name_tok.value, value, self._tok_coord(name_tok)) 

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

1219 return enum 

1220 

1221 # ------------------------------------------------------------------ 

1222 # Declarators 

1223 # ------------------------------------------------------------------ 

1224 # BNF: declarator : pointer? direct_declarator 

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

1226 decl, _ = self._parse_any_declarator( 

1227 allow_abstract=False, typeid_paren_as_abstract=False 

1228 ) 

1229 assert decl is not None 

1230 return decl 

1231 

1232 # BNF: id_declarator : declarator with ID name 

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

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

1235 

1236 # BNF: typeid_declarator : declarator with TYPEID name 

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

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

1239 

1240 # BNF: typeid_noparen_declarator : declarator without parenthesized name 

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

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

1243 

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

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

1246 ptr = None 

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

1248 ptr = self._parse_pointer() 

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

1250 if ptr is not None: 

1251 return self._type_modify_decl(direct, ptr) 

1252 return direct 

1253 

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

1255 # | direct_declarator '[' ... ']' 

1256 # | direct_declarator '(' ... ')' 

1257 def _parse_direct_declarator( 

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

1259 ) -> c_ast.Node: 

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

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

1262 self._expect("RPAREN") 

1263 else: 

1264 if kind == "id": 

1265 name_tok = self._expect("ID") 

1266 else: 

1267 name_tok = self._expect("TYPEID") 

1268 decl = c_ast.TypeDecl( 

1269 declname=name_tok.value, 

1270 type=None, 

1271 quals=None, 

1272 align=None, 

1273 coord=self._tok_coord(name_tok), 

1274 ) 

1275 

1276 return self._parse_decl_suffixes(decl) 

1277 

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

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

1280 while True: 

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

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

1283 continue 

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

1285 func = self._parse_function_decl(decl) 

1286 decl = self._type_modify_decl(decl, func) 

1287 continue 

1288 break 

1289 return decl 

1290 

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

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

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

1294 

1295 def _parse_array_decl_common( 

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

1297 ) -> c_ast.Node: 

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

1299 

1300 base_type: 

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

1302 TypeDecl for abstract declarators). 

1303 

1304 coord: 

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

1306 """ 

1307 lbrack_tok = self._expect("LBRACKET") 

1308 if coord is None: 

1309 coord = self._tok_coord(lbrack_tok) 

1310 

1311 def make_array_decl(dim, dim_quals): 

1312 return c_ast.ArrayDecl( 

1313 type=base_type, dim=dim, dim_quals=dim_quals, coord=coord 

1314 ) 

1315 

1316 if self._accept("STATIC"): 

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

1318 dim = self._parse_assignment_expression() 

1319 self._expect("RBRACKET") 

1320 return make_array_decl(dim, dim_quals) 

1321 

1322 if self._peek_type() in _TYPE_QUALIFIER: 

1323 dim_quals = self._parse_type_qualifier_list() or [] 

1324 if self._accept("STATIC"): 

1325 dim_quals = dim_quals + ["static"] 

1326 dim = self._parse_assignment_expression() 

1327 self._expect("RBRACKET") 

1328 return make_array_decl(dim, dim_quals) 

1329 times_tok = self._accept("TIMES") 

1330 if times_tok: 

1331 self._expect("RBRACKET") 

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

1333 return make_array_decl(dim, dim_quals) 

1334 dim = None 

1335 if self._starts_expression(): 

1336 dim = self._parse_assignment_expression() 

1337 self._expect("RBRACKET") 

1338 return make_array_decl(dim, dim_quals) 

1339 

1340 times_tok = self._accept("TIMES") 

1341 if times_tok: 

1342 self._expect("RBRACKET") 

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

1344 return make_array_decl(dim, []) 

1345 

1346 dim = None 

1347 if self._starts_expression(): 

1348 dim = self._parse_assignment_expression() 

1349 self._expect("RBRACKET") 

1350 return make_array_decl(dim, []) 

1351 

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

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

1354 self._expect("LPAREN") 

1355 if self._accept("RPAREN"): 

1356 args = None 

1357 else: 

1358 args = ( 

1359 self._parse_parameter_type_list() 

1360 if self._starts_declaration() 

1361 else self._parse_identifier_list_opt() 

1362 ) 

1363 self._expect("RPAREN") 

1364 

1365 func = c_ast.FuncDecl(args=args, type=None, coord=base_decl.coord) 

1366 

1367 if self._peek_type() == "LBRACE" and func.args is not None: 

1368 for param in func.args.params: 

1369 if isinstance(param, c_ast.EllipsisParam): 

1370 break 

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

1372 if name: 

1373 self._add_identifier(name, param.coord) 

1374 

1375 return func 

1376 

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

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

1379 stars = [] 

1380 times_tok = self._accept("TIMES") 

1381 while times_tok: 

1382 quals = self._parse_type_qualifier_list() or [] 

1383 stars.append((quals, self._tok_coord(times_tok))) 

1384 times_tok = self._accept("TIMES") 

1385 

1386 if not stars: 

1387 return None 

1388 

1389 ptr = None 

1390 for quals, coord in stars: 

1391 ptr = c_ast.PtrDecl(quals=quals, type=ptr, coord=coord) 

1392 return ptr 

1393 

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

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

1396 params = self._parse_parameter_list() 

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

1398 self._advance() 

1399 ell_tok = self._advance() 

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

1401 return params 

1402 

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

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

1405 first = self._parse_parameter_declaration() 

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

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

1408 self._advance() 

1409 params.params.append(self._parse_parameter_declaration()) 

1410 return params 

1411 

1412 # BNF: parameter_declaration : declaration_specifiers declarator? 

1413 # | declaration_specifiers abstract_declarator_opt 

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

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

1416 

1417 if not spec["type"]: 

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

1419 

1420 if self._starts_declarator(): 

1421 decl, is_named = self._parse_any_declarator( 

1422 allow_abstract=True, typeid_paren_as_abstract=True 

1423 ) 

1424 if is_named: 

1425 return self._build_declarations( 

1426 spec=spec, 

1427 decls=[{"decl": decl, "init": None, "bitsize": None}], 

1428 )[0] 

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

1430 

1431 decl = self._parse_abstract_declarator_opt() 

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

1433 

1434 def _build_parameter_declaration( 

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

1436 ) -> c_ast.Node: 

1437 if ( 

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

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

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

1441 ): 

1442 return self._build_declarations( 

1443 spec=spec, decls=[{"decl": decl, "init": None, "bitsize": None}] 

1444 )[0] 

1445 

1446 decl = c_ast.Typename( 

1447 name="", 

1448 quals=spec["qual"], 

1449 align=None, 

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

1451 coord=spec_coord, 

1452 ) 

1453 return self._fix_decl_name_type(decl, spec["type"]) 

1454 

1455 # BNF: identifier_list_opt : identifier_list | empty 

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

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

1458 return None 

1459 return self._parse_identifier_list() 

1460 

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

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

1463 first = self._parse_identifier() 

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

1465 while self._accept("COMMA"): 

1466 params.params.append(self._parse_identifier()) 

1467 return params 

1468 

1469 # ------------------------------------------------------------------ 

1470 # Abstract declarators 

1471 # ------------------------------------------------------------------ 

1472 # BNF: type_name : specifier_qualifier_list abstract_declarator_opt 

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

1474 spec = self._parse_specifier_qualifier_list() 

1475 decl = self._parse_abstract_declarator_opt() 

1476 

1477 coord = None 

1478 if decl is not None: 

1479 coord = decl.coord 

1480 elif spec["type"]: 

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

1482 

1483 typename = c_ast.Typename( 

1484 name="", 

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

1486 align=None, 

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

1488 coord=coord, 

1489 ) 

1490 return cast(c_ast.Typename, self._fix_decl_name_type(typename, spec["type"])) 

1491 

1492 # BNF: abstract_declarator_opt : pointer? direct_abstract_declarator? 

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

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

1495 ptr = self._parse_pointer() 

1496 if self._starts_direct_abstract_declarator(): 

1497 decl = self._parse_direct_abstract_declarator() 

1498 else: 

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

1500 assert ptr is not None 

1501 return self._type_modify_decl(decl, ptr) 

1502 

1503 if self._starts_direct_abstract_declarator(): 

1504 return self._parse_direct_abstract_declarator() 

1505 

1506 return None 

1507 

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

1509 # | '(' abstract_declarator ')' 

1510 # | '[' ... ']' 

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

1512 lparen_tok = self._accept("LPAREN") 

1513 if lparen_tok: 

1514 if self._starts_declaration() or self._peek_type() == "RPAREN": 

1515 params = self._parse_parameter_type_list_opt() 

1516 self._expect("RPAREN") 

1517 decl = c_ast.FuncDecl( 

1518 args=params, 

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

1520 coord=self._tok_coord(lparen_tok), 

1521 ) 

1522 else: 

1523 decl = self._parse_abstract_declarator_opt() 

1524 self._expect("RPAREN") 

1525 assert decl is not None 

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

1527 decl = self._parse_abstract_array_base() 

1528 else: 

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

1530 

1531 return self._parse_decl_suffixes(decl) 

1532 

1533 # BNF: parameter_type_list_opt : parameter_type_list | empty 

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

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

1536 return None 

1537 return self._parse_parameter_type_list() 

1538 

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

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

1541 return self._parse_array_decl_common( 

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

1543 ) 

1544 

1545 # ------------------------------------------------------------------ 

1546 # Statements 

1547 # ------------------------------------------------------------------ 

1548 # BNF: statement : labeled_statement | compound_statement 

1549 # | selection_statement | iteration_statement 

1550 # | jump_statement | expression_statement 

1551 # | static_assert | pppragma_directive 

1552 def _parse_statement(self) -> c_ast.Node | list[c_ast.Node]: 

1553 tok_type = self._peek_type() 

1554 match tok_type: 

1555 case "CASE" | "DEFAULT": 

1556 return self._parse_labeled_statement() 

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

1558 return self._parse_labeled_statement() 

1559 case "LBRACE": 

1560 return self._parse_compound_statement() 

1561 case "IF" | "SWITCH": 

1562 return self._parse_selection_statement() 

1563 case "WHILE" | "DO" | "FOR": 

1564 return self._parse_iteration_statement() 

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

1566 return self._parse_jump_statement() 

1567 case "PPPRAGMA" | "_PRAGMA": 

1568 return self._parse_pppragma_directive() 

1569 case "_STATIC_ASSERT": 

1570 return self._parse_static_assert() 

1571 case _: 

1572 return self._parse_expression_statement() 

1573 

1574 # BNF: pragmacomp_or_statement : pppragma_directive* statement 

1575 def _parse_pragmacomp_or_statement(self) -> c_ast.Node | list[c_ast.Node]: 

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

1577 pragmas = self._parse_pppragma_directive_list() 

1578 stmt = self._parse_statement() 

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

1580 return self._parse_statement() 

1581 

1582 # BNF: block_item : declaration | statement 

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

1584 if self._starts_declaration(): 

1585 return self._parse_declaration() 

1586 return self._parse_statement() 

1587 

1588 # BNF: block_item_list : block_item+ 

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

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

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

1592 item = self._parse_block_item() 

1593 if isinstance(item, c_ast.Node): 

1594 items.append(item) 

1595 elif item != [None]: 

1596 items.extend(item) 

1597 return items 

1598 

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

1600 def _parse_compound_statement(self) -> c_ast.Node: 

1601 lbrace_tok = self._expect("LBRACE") 

1602 if self._accept("RBRACE"): 

1603 return c_ast.Compound(block_items=None, coord=self._tok_coord(lbrace_tok)) 

1604 block_items = self._parse_block_item_list() 

1605 self._expect("RBRACE") 

1606 return c_ast.Compound( 

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

1608 ) 

1609 

1610 # BNF: labeled_statement : ID ':' statement 

1611 # | CASE constant_expression ':' statement 

1612 # | DEFAULT ':' statement 

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

1614 tok_type = self._peek_type() 

1615 match tok_type: 

1616 case "ID": 

1617 name_tok = self._advance() 

1618 self._expect("COLON") 

1619 if self._starts_statement(): 

1620 stmt = self._parse_pragmacomp_or_statement() 

1621 else: 

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

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

1624 case "CASE": 

1625 case_tok = self._advance() 

1626 expr = self._parse_constant_expression() 

1627 self._expect("COLON") 

1628 if self._starts_statement(): 

1629 stmt = self._parse_pragmacomp_or_statement() 

1630 else: 

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

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

1633 case "DEFAULT": 

1634 def_tok = self._advance() 

1635 self._expect("COLON") 

1636 if self._starts_statement(): 

1637 stmt = self._parse_pragmacomp_or_statement() 

1638 else: 

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

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

1641 case _: 

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

1643 

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

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

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

1647 tok = self._advance() 

1648 match tok.type: 

1649 case "IF": 

1650 self._expect("LPAREN") 

1651 cond = self._parse_expression() 

1652 self._expect("RPAREN") 

1653 then_stmt = self._parse_pragmacomp_or_statement() 

1654 if self._accept("ELSE"): 

1655 else_stmt = self._parse_pragmacomp_or_statement() 

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

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

1658 case "SWITCH": 

1659 self._expect("LPAREN") 

1660 expr = self._parse_expression() 

1661 self._expect("RPAREN") 

1662 stmt = self._parse_pragmacomp_or_statement() 

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

1664 case _: 

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

1666 

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

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

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

1670 # expression_opt ';' expression_opt ')' statement 

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

1672 tok = self._advance() 

1673 match tok.type: 

1674 case "WHILE": 

1675 self._expect("LPAREN") 

1676 cond = self._parse_expression() 

1677 self._expect("RPAREN") 

1678 stmt = self._parse_pragmacomp_or_statement() 

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

1680 case "DO": 

1681 stmt = self._parse_pragmacomp_or_statement() 

1682 self._expect("WHILE") 

1683 self._expect("LPAREN") 

1684 cond = self._parse_expression() 

1685 self._expect("RPAREN") 

1686 self._expect("SEMI") 

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

1688 case "FOR": 

1689 self._expect("LPAREN") 

1690 if self._starts_declaration(): 

1691 decls = self._parse_declaration() 

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

1693 cond = self._parse_expression_opt() 

1694 self._expect("SEMI") 

1695 next_expr = self._parse_expression_opt() 

1696 self._expect("RPAREN") 

1697 stmt = self._parse_pragmacomp_or_statement() 

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

1699 

1700 init = self._parse_expression_opt() 

1701 self._expect("SEMI") 

1702 cond = self._parse_expression_opt() 

1703 self._expect("SEMI") 

1704 next_expr = self._parse_expression_opt() 

1705 self._expect("RPAREN") 

1706 stmt = self._parse_pragmacomp_or_statement() 

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

1708 case _: 

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

1710 

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

1712 # | RETURN expression? ';' 

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

1714 tok = self._advance() 

1715 match tok.type: 

1716 case "GOTO": 

1717 name_tok = self._expect("ID") 

1718 self._expect("SEMI") 

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

1720 case "BREAK": 

1721 self._expect("SEMI") 

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

1723 case "CONTINUE": 

1724 self._expect("SEMI") 

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

1726 case "RETURN": 

1727 if self._accept("SEMI"): 

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

1729 expr = self._parse_expression() 

1730 self._expect("SEMI") 

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

1732 case _: 

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

1734 

1735 # BNF: expression_statement : expression_opt ';' 

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

1737 expr = self._parse_expression_opt() 

1738 semi_tok = self._expect("SEMI") 

1739 if expr is None: 

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

1741 return expr 

1742 

1743 # ------------------------------------------------------------------ 

1744 # Expressions 

1745 # ------------------------------------------------------------------ 

1746 # BNF: expression_opt : expression | empty 

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

1748 if self._starts_expression(): 

1749 return self._parse_expression() 

1750 return None 

1751 

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

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

1754 expr = self._parse_assignment_expression() 

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

1756 return expr 

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

1758 while self._accept("COMMA"): 

1759 exprs.append(self._parse_assignment_expression()) 

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

1761 

1762 # BNF: assignment_expression : conditional_expression 

1763 # | unary_expression assignment_op assignment_expression 

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

1765 if self._peek_type() == "LPAREN" and self._peek_type(2) == "LBRACE": 

1766 self._advance() 

1767 comp = self._parse_compound_statement() 

1768 self._expect("RPAREN") 

1769 return comp 

1770 

1771 expr = self._parse_conditional_expression() 

1772 if self._is_assignment_op(): 

1773 op = self._advance().value 

1774 rhs = self._parse_assignment_expression() 

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

1776 return expr 

1777 

1778 # BNF: conditional_expression : binary_expression 

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

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

1781 expr = self._parse_binary_expression() 

1782 if self._accept("CONDOP"): 

1783 iftrue = self._parse_expression() 

1784 self._expect("COLON") 

1785 iffalse = self._parse_conditional_expression() 

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

1787 return expr 

1788 

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

1790 def _parse_binary_expression( 

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

1792 ) -> c_ast.Node: 

1793 if lhs is None: 

1794 lhs = self._parse_cast_expression() 

1795 

1796 while True: 

1797 tok = self._peek() 

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

1799 break 

1800 prec = _BINARY_PRECEDENCE[tok.type] 

1801 if prec < min_prec: 

1802 break 

1803 

1804 op = tok.value 

1805 self._advance() 

1806 rhs = self._parse_cast_expression() 

1807 

1808 while True: 

1809 next_tok = self._peek() 

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

1811 break 

1812 next_prec = _BINARY_PRECEDENCE[next_tok.type] 

1813 if next_prec > prec: 

1814 rhs = self._parse_binary_expression(next_prec, rhs) 

1815 else: 

1816 break 

1817 

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

1819 

1820 return lhs 

1821 

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

1823 # | unary_expression 

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

1825 result = self._try_parse_paren_type_name() 

1826 if result is not None: 

1827 typ, mark, lparen_tok = result 

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

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

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

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

1832 self._reset(mark) 

1833 else: 

1834 expr = self._parse_cast_expression() 

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

1836 return self._parse_unary_expression() 

1837 

1838 # BNF: unary_expression : postfix_expression 

1839 # | '++' unary_expression 

1840 # | '--' unary_expression 

1841 # | unary_op cast_expression 

1842 # | 'sizeof' unary_expression 

1843 # | 'sizeof' '(' type_name ')' 

1844 # | '_Alignof' '(' type_name ')' 

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

1846 tok_type = self._peek_type() 

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

1848 tok = self._advance() 

1849 expr = self._parse_unary_expression() 

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

1851 

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

1853 tok = self._advance() 

1854 expr = self._parse_cast_expression() 

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

1856 

1857 if tok_type == "SIZEOF": 

1858 tok = self._advance() 

1859 result = self._try_parse_paren_type_name() 

1860 if result is not None: 

1861 typ, _, _ = result 

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

1863 expr = self._parse_unary_expression() 

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

1865 

1866 if tok_type == "_ALIGNOF": 

1867 tok = self._advance() 

1868 self._expect("LPAREN") 

1869 typ = self._parse_type_name() 

1870 self._expect("RPAREN") 

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

1872 

1873 return self._parse_postfix_expression() 

1874 

1875 # BNF: postfix_expression : primary_expression postfix_suffix* 

1876 # | '(' type_name ')' '{' initializer_list ','? '}' 

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

1878 result = self._try_parse_paren_type_name() 

1879 if result is not None: 

1880 typ, mark, _ = result 

1881 # Disambiguate between casts and compound literals: 

1882 # (int) x -> cast 

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

1884 if self._accept("LBRACE"): 

1885 init = self._parse_initializer_list() 

1886 self._accept("COMMA") 

1887 self._expect("RBRACE") 

1888 return c_ast.CompoundLiteral(typ, init) 

1889 else: 

1890 self._reset(mark) 

1891 

1892 expr = self._parse_primary_expression() 

1893 while True: 

1894 if self._accept("LBRACKET"): 

1895 sub = self._parse_expression() 

1896 self._expect("RBRACKET") 

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

1898 continue 

1899 if self._accept("LPAREN"): 

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

1901 self._advance() 

1902 args = None 

1903 else: 

1904 args = self._parse_argument_expression_list() 

1905 self._expect("RPAREN") 

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

1907 continue 

1908 if self._peek_type() in {"PERIOD", "ARROW"}: 

1909 op_tok = self._advance() 

1910 name_tok = self._advance() 

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

1912 self._parse_error( 

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

1914 ) 

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

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

1917 continue 

1918 if self._peek_type() in {"PLUSPLUS", "MINUSMINUS"}: 

1919 tok = self._advance() 

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

1921 continue 

1922 break 

1923 return expr 

1924 

1925 # BNF: primary_expression : ID | constant | string_literal 

1926 # | '(' expression ')' | offsetof 

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

1928 tok_type = self._peek_type() 

1929 if tok_type == "ID": 

1930 return self._parse_identifier() 

1931 if ( 

1932 tok_type in _INT_CONST 

1933 or tok_type in _FLOAT_CONST 

1934 or tok_type in _CHAR_CONST 

1935 ): 

1936 return self._parse_constant() 

1937 if tok_type in _STRING_LITERAL: 

1938 return self._parse_unified_string_literal() 

1939 if tok_type in _WSTR_LITERAL: 

1940 return self._parse_unified_wstring_literal() 

1941 if tok_type == "LPAREN": 

1942 self._advance() 

1943 expr = self._parse_expression() 

1944 self._expect("RPAREN") 

1945 return expr 

1946 if tok_type == "OFFSETOF": 

1947 off_tok = self._advance() 

1948 self._expect("LPAREN") 

1949 typ = self._parse_type_name() 

1950 self._expect("COMMA") 

1951 designator = self._parse_offsetof_member_designator() 

1952 self._expect("RPAREN") 

1953 coord = self._tok_coord(off_tok) 

1954 return c_ast.FuncCall( 

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

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

1957 coord, 

1958 ) 

1959 

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

1961 

1962 # BNF: offsetof_member_designator : identifier_or_typeid 

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

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

1965 node = self._parse_identifier_or_typeid() 

1966 while True: 

1967 if self._accept("PERIOD"): 

1968 field = self._parse_identifier_or_typeid() 

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

1970 continue 

1971 if self._accept("LBRACKET"): 

1972 expr = self._parse_expression() 

1973 self._expect("RBRACKET") 

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

1975 continue 

1976 break 

1977 return node 

1978 

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

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

1981 expr = self._parse_assignment_expression() 

1982 exprs = [expr] 

1983 while self._accept("COMMA"): 

1984 exprs.append(self._parse_assignment_expression()) 

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

1986 

1987 # BNF: constant_expression : conditional_expression 

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

1989 return self._parse_conditional_expression() 

1990 

1991 # ------------------------------------------------------------------ 

1992 # Terminals 

1993 # ------------------------------------------------------------------ 

1994 # BNF: identifier : ID 

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

1996 tok = self._expect("ID") 

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

1998 

1999 # BNF: identifier_or_typeid : ID | TYPEID 

2000 def _parse_identifier_or_typeid(self) -> c_ast.Node: 

2001 tok = self._advance() 

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

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

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

2005 

2006 # BNF: constant : INT_CONST | FLOAT_CONST | CHAR_CONST 

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

2008 tok = self._advance() 

2009 if tok.type in _INT_CONST: 

2010 u_count = 0 

2011 l_count = 0 

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

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

2014 l_count += 1 

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

2016 u_count += 1 

2017 if u_count > 1: 

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

2019 if l_count > 2: 

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

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

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

2023 

2024 if tok.type in _FLOAT_CONST: 

2025 if tok.value[-1] in ("f", "F"): 

2026 t = "float" 

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

2028 t = "long double" 

2029 else: 

2030 t = "double" 

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

2032 

2033 if tok.type in _CHAR_CONST: 

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

2035 

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

2037 

2038 # BNF: unified_string_literal : STRING_LITERAL+ 

2039 def _parse_unified_string_literal(self) -> c_ast.Node: 

2040 tok = self._expect("STRING_LITERAL") 

2041 node = c_ast.Constant("string", tok.value, self._tok_coord(tok)) 

2042 while self._peek_type() == "STRING_LITERAL": 

2043 tok2 = self._advance() 

2044 node.value = node.value[:-1] + tok2.value[1:] 

2045 return node 

2046 

2047 # BNF: unified_wstring_literal : WSTRING_LITERAL+ 

2048 def _parse_unified_wstring_literal(self) -> c_ast.Node: 

2049 tok = self._advance() 

2050 if tok.type not in _WSTR_LITERAL: 

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

2052 node = c_ast.Constant("string", tok.value, self._tok_coord(tok)) 

2053 while self._peek_type() in _WSTR_LITERAL: 

2054 tok2 = self._advance() 

2055 node.value = node.value.rstrip()[:-1] + tok2.value[2:] 

2056 return node 

2057 

2058 # ------------------------------------------------------------------ 

2059 # Initializers 

2060 # ------------------------------------------------------------------ 

2061 # BNF: initializer : assignment_expression 

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

2063 # | '{' '}' 

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

2065 lbrace_tok = self._accept("LBRACE") 

2066 if lbrace_tok: 

2067 if self._accept("RBRACE"): 

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

2069 init_list = self._parse_initializer_list() 

2070 self._accept("COMMA") 

2071 self._expect("RBRACE") 

2072 return init_list 

2073 

2074 return self._parse_assignment_expression() 

2075 

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

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

2078 items = [self._parse_initializer_item()] 

2079 while self._accept("COMMA"): 

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

2081 break 

2082 items.append(self._parse_initializer_item()) 

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

2084 

2085 # BNF: initializer_item : designation? initializer 

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

2087 designation = None 

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

2089 designation = self._parse_designation() 

2090 init = self._parse_initializer() 

2091 if designation is not None: 

2092 return c_ast.NamedInitializer(designation, init) 

2093 return init 

2094 

2095 # BNF: designation : designator_list '=' 

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

2097 designators = self._parse_designator_list() 

2098 self._expect("EQUALS") 

2099 return designators 

2100 

2101 # BNF: designator_list : designator+ 

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

2103 designators = [] 

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

2105 designators.append(self._parse_designator()) 

2106 return designators 

2107 

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

2109 # | '.' identifier_or_typeid 

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

2111 if self._accept("LBRACKET"): 

2112 expr = self._parse_constant_expression() 

2113 self._expect("RBRACKET") 

2114 return expr 

2115 if self._accept("PERIOD"): 

2116 return self._parse_identifier_or_typeid() 

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

2118 

2119 # ------------------------------------------------------------------ 

2120 # Preprocessor-like directives 

2121 # ------------------------------------------------------------------ 

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

2123 def _parse_pp_directive(self) -> NoReturn: 

2124 tok = self._expect("PPHASH") 

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

2126 

2127 # BNF: pppragma_directive : PPPRAGMA PPPRAGMASTR? 

2128 # | _PRAGMA '(' string_literal ')' 

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

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

2131 tok = self._advance() 

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

2133 str_tok = self._advance() 

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

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

2136 

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

2138 tok = self._advance() 

2139 lparen = self._expect("LPAREN") 

2140 literal = self._parse_unified_string_literal() 

2141 self._expect("RPAREN") 

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

2143 

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

2145 

2146 # BNF: pppragma_directive_list : pppragma_directive+ 

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

2148 pragmas = [] 

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

2150 pragmas.append(self._parse_pppragma_directive()) 

2151 return pragmas 

2152 

2153 # BNF: static_assert : _STATIC_ASSERT '(' constant_expression (',' string_literal)? ')' 

2154 def _parse_static_assert(self) -> list[c_ast.Node]: 

2155 tok = self._expect("_STATIC_ASSERT") 

2156 self._expect("LPAREN") 

2157 cond = self._parse_constant_expression() 

2158 msg = None 

2159 if self._accept("COMMA"): 

2160 msg = self._parse_unified_string_literal() 

2161 self._expect("RPAREN") 

2162 return [c_ast.StaticAssert(cond, msg, self._tok_coord(tok))] 

2163 

2164 

2165_ASSIGNMENT_OPS = { 

2166 "EQUALS", 

2167 "XOREQUAL", 

2168 "TIMESEQUAL", 

2169 "DIVEQUAL", 

2170 "MODEQUAL", 

2171 "PLUSEQUAL", 

2172 "MINUSEQUAL", 

2173 "LSHIFTEQUAL", 

2174 "RSHIFTEQUAL", 

2175 "ANDEQUAL", 

2176 "OREQUAL", 

2177} 

2178 

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

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

2181# well 

2182_BINARY_PRECEDENCE = { 

2183 "LOR": 0, 

2184 "LAND": 1, 

2185 "OR": 2, 

2186 "XOR": 3, 

2187 "AND": 4, 

2188 "EQ": 5, 

2189 "NE": 5, 

2190 "GT": 6, 

2191 "GE": 6, 

2192 "LT": 6, 

2193 "LE": 6, 

2194 "RSHIFT": 7, 

2195 "LSHIFT": 7, 

2196 "PLUS": 8, 

2197 "MINUS": 8, 

2198 "TIMES": 9, 

2199 "DIVIDE": 9, 

2200 "MOD": 9, 

2201} 

2202 

2203_STORAGE_CLASS = {"AUTO", "REGISTER", "STATIC", "EXTERN", "TYPEDEF", "_THREAD_LOCAL"} 

2204 

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

2206 

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

2208 

2209_TYPE_SPEC_SIMPLE = { 

2210 "VOID", 

2211 "_BOOL", 

2212 "CHAR", 

2213 "SHORT", 

2214 "INT", 

2215 "LONG", 

2216 "FLOAT", 

2217 "DOUBLE", 

2218 "_COMPLEX", 

2219 "SIGNED", 

2220 "UNSIGNED", 

2221 "__INT128", 

2222} 

2223 

2224_DECL_START = ( 

2225 _STORAGE_CLASS 

2226 | _FUNCTION_SPEC 

2227 | _TYPE_QUALIFIER 

2228 | _TYPE_SPEC_SIMPLE 

2229 | {"TYPEID", "STRUCT", "UNION", "ENUM", "_ALIGNAS", "_ATOMIC"} 

2230) 

2231 

2232_EXPR_START = { 

2233 "ID", 

2234 "LPAREN", 

2235 "PLUSPLUS", 

2236 "MINUSMINUS", 

2237 "PLUS", 

2238 "MINUS", 

2239 "TIMES", 

2240 "AND", 

2241 "NOT", 

2242 "LNOT", 

2243 "SIZEOF", 

2244 "_ALIGNOF", 

2245 "OFFSETOF", 

2246} 

2247 

2248_INT_CONST = { 

2249 "INT_CONST_DEC", 

2250 "INT_CONST_OCT", 

2251 "INT_CONST_HEX", 

2252 "INT_CONST_BIN", 

2253 "INT_CONST_CHAR", 

2254} 

2255 

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

2257 

2258_CHAR_CONST = { 

2259 "CHAR_CONST", 

2260 "WCHAR_CONST", 

2261 "U8CHAR_CONST", 

2262 "U16CHAR_CONST", 

2263 "U32CHAR_CONST", 

2264} 

2265 

2266_STRING_LITERAL = {"STRING_LITERAL"} 

2267 

2268_WSTR_LITERAL = { 

2269 "WSTRING_LITERAL", 

2270 "U8STRING_LITERAL", 

2271 "U16STRING_LITERAL", 

2272 "U32STRING_LITERAL", 

2273} 

2274 

2275_STARTS_EXPRESSION = ( 

2276 _EXPR_START 

2277 | _INT_CONST 

2278 | _FLOAT_CONST 

2279 | _CHAR_CONST 

2280 | _STRING_LITERAL 

2281 | _WSTR_LITERAL 

2282) 

2283 

2284_STARTS_STATEMENT = { 

2285 "LBRACE", 

2286 "IF", 

2287 "SWITCH", 

2288 "WHILE", 

2289 "DO", 

2290 "FOR", 

2291 "GOTO", 

2292 "BREAK", 

2293 "CONTINUE", 

2294 "RETURN", 

2295 "CASE", 

2296 "DEFAULT", 

2297 "PPPRAGMA", 

2298 "_PRAGMA", 

2299 "_STATIC_ASSERT", 

2300 "SEMI", 

2301} 

2302 

2303 

2304class _TokenStream: 

2305 """Wraps a lexer to provide convenient, buffered access to the underlying 

2306 token stream. The lexer is expected to be initialized with the input 

2307 string already. 

2308 """ 

2309 

2310 def __init__(self, lexer: CLexer) -> None: 

2311 self._lexer = lexer 

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

2313 self._index = 0 

2314 

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

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

2317 

2318 Examples: 

2319 k=1 returns the immediate next token. 

2320 k=2 returns the token after that. 

2321 """ 

2322 if k <= 0: 

2323 return None 

2324 self._fill(k) 

2325 return self._buffer[self._index + k - 1] 

2326 

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

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

2329 self._fill(1) 

2330 tok = self._buffer[self._index] 

2331 self._index += 1 

2332 return tok 

2333 

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

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

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

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

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

2339 def mark(self) -> int: 

2340 return self._index 

2341 

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

2343 self._index = mark 

2344 

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

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

2347 tok = self._lexer.token() 

2348 self._buffer.append(tok) 

2349 if tok is None: 

2350 break 

2351 

2352 

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

2354# - qual: a list of type qualifiers 

2355# - storage: a list of storage class specifiers 

2356# - type: a list of type specifiers 

2357# - function: a list of function specifiers 

2358# - alignment: a list of alignment specifiers 

2359class _DeclSpec(TypedDict): 

2360 qual: list[Any] 

2361 storage: list[Any] 

2362 type: list[Any] 

2363 function: list[Any] 

2364 alignment: list[Any] 

2365 

2366 

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

2368 

2369 

2370class _DeclInfo(TypedDict): 

2371 # Declarator payloads used by declaration/initializer parsing: 

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

2373 # - init: optional initializer expression 

2374 # - bitsize: optional bit-field width expression (for struct declarators) 

2375 decl: c_ast.Node | None 

2376 init: c_ast.Node | None 

2377 bitsize: c_ast.Node | None