codekingpro/portable-devtools
115k
1# ------------------------------------------------------------------------------2# pycparser: c_parser.py3#4# Recursive-descent parser for the C language.5#6# Eli Bendersky [https://eli.thegreenplace.net/]7# License: BSD8# ------------------------------------------------------------------------------9from dataclasses import dataclass10from typing import (11 Any,12 Dict,13 List,14 Literal,15 NoReturn,16 Optional,17 Tuple,18 TypedDict,19 cast,20)21 22from . import c_ast23from .c_lexer import CLexer, _Token24from .ast_transforms import fix_switch_cases, fix_atomic_specifiers25 26 27@dataclass28class Coord:29 """Coordinates of a syntactic element. Consists of:30 - File name31 - Line number32 - Column number33 """34 35 file: str36 line: int37 column: Optional[int] = None38 39 def __str__(self) -> str:40 text = f"{self.file}:{self.line}"41 if self.column:42 text += f":{self.column}"43 return text44 45 46class ParseError(Exception):47 pass48 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 parameters after `lexer` are accepted for backward compatibility with59 the old PLY-based parser and are otherwise unused.60 """61 62 def __init__(63 self,64 lex_optimize: bool = True,65 lexer: type[CLexer] = CLexer,66 lextab: str = "pycparser.lextab",67 yacc_optimize: bool = True,68 yacctab: str = "pycparser.yacctab",69 yacc_debug: bool = False,70 taboutputdir: str = "",71 ) -> None:72 self.clex: CLexer = lexer(73 error_func=self._lex_error_func,74 on_lbrace_func=self._lex_on_lbrace_func,75 on_rbrace_func=self._lex_on_rbrace_func,76 type_lookup_func=self._lex_type_lookup_func,77 )78 79 # Stack of scopes for keeping track of symbols. _scope_stack[-1] is80 # the current (topmost) scope. Each scope is a dictionary that81 # specifies whether a name is a type. If _scope_stack[n][name] is82 # True, 'name' is currently a type in the scope. If it's False,83 # 'name' is used in the scope but not as a type (for instance, if we84 # saw: int name;85 # If 'name' is not a key in _scope_stack[n] then 'name' was not defined86 # in this scope at all.87 self._scope_stack: List[Dict[str, bool]] = [dict()]88 self._tokens: _TokenStream = _TokenStream(self.clex)89 90 def parse(91 self, text: str, filename: str = "", debug: bool = False92 ) -> c_ast.FileAST:93 """Parses C code and returns an AST.94 95 text:96 A string containing the C source code97 98 filename:99 Name of the file being parsed (for meaningful100 error messages)101 102 debug:103 Deprecated debug flag (unused); for backwards compatibility.104 """105 self._scope_stack = [dict()]106 self.clex.input(text, filename)107 self._tokens = _TokenStream(self.clex)108 109 ast = self._parse_translation_unit_or_empty()110 tok = self._peek()111 if tok is not None:112 self._parse_error(f"before: {tok.value}", self._tok_coord(tok))113 return ast114 115 # ------------------------------------------------------------------116 # Scope and declaration helpers117 # ------------------------------------------------------------------118 def _coord(self, lineno: int, column: Optional[int] = None) -> Coord:119 return Coord(file=self.clex.filename, line=lineno, column=column)120 121 def _parse_error(self, msg: str, coord: Coord | str | None) -> NoReturn:122 raise ParseError(f"{coord}: {msg}")123 124 def _push_scope(self) -> None:125 self._scope_stack.append(dict())126 127 def _pop_scope(self) -> None:128 assert len(self._scope_stack) > 1129 self._scope_stack.pop()130 131 def _add_typedef_name(self, name: str, coord: Optional[Coord]) -> None:132 """Add a new typedef name (ie a TYPEID) to the current scope"""133 if not self._scope_stack[-1].get(name, True):134 self._parse_error(135 f"Typedef {name!r} previously declared as non-typedef in this scope",136 coord,137 )138 self._scope_stack[-1][name] = True139 140 def _add_identifier(self, name: str, coord: Optional[Coord]) -> None:141 """Add a new object, function, or enum member name (ie an ID) to the142 current scope143 """144 if self._scope_stack[-1].get(name, False):145 self._parse_error(146 f"Non-typedef {name!r} previously declared as typedef in this scope",147 coord,148 )149 self._scope_stack[-1][name] = False150 151 def _is_type_in_scope(self, name: str) -> bool:152 """Is *name* a typedef-name in the current scope?"""153 for scope in reversed(self._scope_stack):154 # If name is an identifier in this scope it shadows typedefs in155 # higher scopes.156 in_scope = scope.get(name)157 if in_scope is not None:158 return in_scope159 return False160 161 def _lex_error_func(self, msg: str, line: int, column: int) -> None:162 self._parse_error(msg, self._coord(line, column))163 164 def _lex_on_lbrace_func(self) -> None:165 self._push_scope()166 167 def _lex_on_rbrace_func(self) -> None:168 self._pop_scope()169 170 def _lex_type_lookup_func(self, name: str) -> bool:171 """Looks up types that were previously defined with172 typedef.173 Passed to the lexer for recognizing identifiers that174 are types.175 """176 return self._is_type_in_scope(name)177 178 # To understand what's going on here, read sections A.8.5 and179 # A.8.6 of K&R2 very carefully.180 #181 # A C type consists of a basic type declaration, with a list182 # of modifiers. For example:183 #184 # int *c[5];185 #186 # The basic declaration here is 'int c', and the pointer and187 # the array are the modifiers.188 #189 # Basic declarations are represented by TypeDecl (from module c_ast) and the190 # modifiers are FuncDecl, PtrDecl and ArrayDecl.191 #192 # The standard states that whenever a new modifier is parsed, it should be193 # added to the end of the list of modifiers. For example:194 #195 # K&R2 A.8.6.2: Array Declarators196 #197 # In a declaration T D where D has the form198 # D1 [constant-expression-opt]199 # and the type of the identifier in the declaration T D1 is200 # "type-modifier T", the type of the201 # identifier of D is "type-modifier array of T"202 #203 # This is what this method does. The declarator it receives204 # can be a list of declarators ending with TypeDecl. It205 # tacks the modifier to the end of this list, just before206 # the TypeDecl.207 #208 # Additionally, the modifier may be a list itself. This is209 # useful for pointers, that can come as a chain from the rule210 # p_pointer. In this case, the whole modifier list is spliced211 # into the new location.212 def _type_modify_decl(self, decl: Any, modifier: Any) -> c_ast.Node:213 """Tacks a type modifier on a declarator, and returns214 the modified declarator.215 216 Note: the declarator and modifier may be modified217 """218 modifier_head = modifier219 modifier_tail = modifier220 221 # The modifier may be a nested list. Reach its tail.222 while modifier_tail.type:223 modifier_tail = modifier_tail.type224 225 # If the decl is a basic type, just tack the modifier onto it.226 if isinstance(decl, c_ast.TypeDecl):227 modifier_tail.type = decl228 return modifier229 else:230 # Otherwise, the decl is a list of modifiers. Reach231 # its tail and splice the modifier onto the tail,232 # pointing to the underlying basic type.233 decl_tail = decl234 while not isinstance(decl_tail.type, c_ast.TypeDecl):235 decl_tail = decl_tail.type236 237 modifier_tail.type = decl_tail.type238 decl_tail.type = modifier_head239 return decl240 241 # Due to the order in which declarators are constructed,242 # they have to be fixed in order to look like a normal AST.243 #244 # When a declaration arrives from syntax construction, it has245 # these problems:246 # * The innermost TypeDecl has no type (because the basic247 # type is only known at the uppermost declaration level)248 # * The declaration has no variable name, since that is saved249 # in the innermost TypeDecl250 # * The typename of the declaration is a list of type251 # specifiers, and not a node. Here, basic identifier types252 # should be separated from more complex types like enums253 # and structs.254 #255 # This method fixes these problems.256 def _fix_decl_name_type(257 self,258 decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename,259 typename: List[Any],260 ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename:261 """Fixes a declaration. Modifies decl."""262 # Reach the underlying basic type263 typ = decl264 while not isinstance(typ, c_ast.TypeDecl):265 typ = typ.type266 267 decl.name = typ.declname268 typ.quals = decl.quals[:]269 270 # The typename is a list of types. If any type in this271 # list isn't an IdentifierType, it must be the only272 # type in the list (it's illegal to declare "int enum ..")273 # If all the types are basic, they're collected in the274 # IdentifierType holder.275 for tn in typename:276 if not isinstance(tn, c_ast.IdentifierType):277 if len(typename) > 1:278 self._parse_error("Invalid multiple types specified", tn.coord)279 else:280 typ.type = tn281 return decl282 283 if not typename:284 # Functions default to returning int285 if not isinstance(decl.type, c_ast.FuncDecl):286 self._parse_error("Missing type in declaration", decl.coord)287 typ.type = c_ast.IdentifierType(["int"], coord=decl.coord)288 else:289 # At this point, we know that typename is a list of IdentifierType290 # nodes. Concatenate all the names into a single list.291 typ.type = c_ast.IdentifierType(292 [name for id in typename for name in id.names], coord=typename[0].coord293 )294 return decl295 296 def _add_declaration_specifier(297 self,298 declspec: Optional["_DeclSpec"],299 newspec: Any,300 kind: "_DeclSpecKind",301 append: bool = False,302 ) -> "_DeclSpec":303 """See _DeclSpec for the specifier dictionary layout."""304 if declspec is None:305 spec: _DeclSpec = dict(306 qual=[], storage=[], type=[], function=[], alignment=[]307 )308 else:309 spec = declspec310 311 if append:312 spec[kind].append(newspec)313 else:314 spec[kind].insert(0, newspec)315 316 return spec317 318 def _build_declarations(319 self,320 spec: "_DeclSpec",321 decls: List["_DeclInfo"],322 typedef_namespace: bool = False,323 ) -> List[c_ast.Node]:324 """Builds a list of declarations all sharing the given specifiers.325 If typedef_namespace is true, each declared name is added326 to the "typedef namespace", which also includes objects,327 functions, and enum constants.328 """329 is_typedef = "typedef" in spec["storage"]330 declarations = []331 332 # Bit-fields are allowed to be unnamed.333 if decls[0].get("bitsize") is None:334 # When redeclaring typedef names as identifiers in inner scopes, a335 # problem can occur where the identifier gets grouped into336 # spec['type'], leaving decl as None. This can only occur for the337 # first declarator.338 if decls[0]["decl"] is None:339 if (340 len(spec["type"]) < 2341 or len(spec["type"][-1].names) != 1342 or not self._is_type_in_scope(spec["type"][-1].names[0])343 ):344 coord = "?"345 for t in spec["type"]:346 if hasattr(t, "coord"):347 coord = t.coord348 break349 self._parse_error("Invalid declaration", coord)350 351 # Make this look as if it came from "direct_declarator:ID"352 decls[0]["decl"] = c_ast.TypeDecl(353 declname=spec["type"][-1].names[0],354 type=None,355 quals=None,356 align=spec["alignment"],357 coord=spec["type"][-1].coord,358 )359 # Remove the "new" type's name from the end of spec['type']360 del spec["type"][-1]361 # A similar problem can occur where the declaration ends up362 # looking like an abstract declarator. Give it a name if this is363 # the case.364 elif not isinstance(365 decls[0]["decl"],366 (c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType),367 ):368 decls_0_tail = cast(Any, decls[0]["decl"])369 while not isinstance(decls_0_tail, c_ast.TypeDecl):370 decls_0_tail = decls_0_tail.type371 if decls_0_tail.declname is None:372 decls_0_tail.declname = spec["type"][-1].names[0]373 del spec["type"][-1]374 375 for decl in decls:376 assert decl["decl"] is not None377 if is_typedef:378 declaration = c_ast.Typedef(379 name=None,380 quals=spec["qual"],381 storage=spec["storage"],382 type=decl["decl"],383 coord=decl["decl"].coord,384 )385 else:386 declaration = c_ast.Decl(387 name=None,388 quals=spec["qual"],389 align=spec["alignment"],390 storage=spec["storage"],391 funcspec=spec["function"],392 type=decl["decl"],393 init=decl.get("init"),394 bitsize=decl.get("bitsize"),395 coord=decl["decl"].coord,396 )397 398 if isinstance(399 declaration.type,400 (c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType),401 ):402 fixed_decl = declaration403 else:404 fixed_decl = self._fix_decl_name_type(declaration, spec["type"])405 406 # Add the type name defined by typedef to a407 # symbol table (for usage in the lexer)408 if typedef_namespace:409 if is_typedef:410 self._add_typedef_name(fixed_decl.name, fixed_decl.coord)411 else:412 self._add_identifier(fixed_decl.name, fixed_decl.coord)413 414 fixed_decl = fix_atomic_specifiers(415 cast(c_ast.Decl | c_ast.Typedef, fixed_decl)416 )417 declarations.append(fixed_decl)418 419 return declarations420 421 def _build_function_definition(422 self,423 spec: "_DeclSpec",424 decl: c_ast.Node,425 param_decls: Optional[List[c_ast.Node]],426 body: c_ast.Node,427 ) -> c_ast.Node:428 """Builds a function definition."""429 if "typedef" in spec["storage"]:430 self._parse_error("Invalid typedef", decl.coord)431 432 declaration = self._build_declarations(433 spec=spec,434 decls=[dict(decl=decl, init=None, bitsize=None)],435 typedef_namespace=True,436 )[0]437 438 return c_ast.FuncDef(439 decl=declaration, param_decls=param_decls, body=body, coord=decl.coord440 )441 442 def _select_struct_union_class(self, token: str) -> type:443 """Given a token (either STRUCT or UNION), selects the444 appropriate AST class.445 """446 if token == "struct":447 return c_ast.Struct448 else:449 return c_ast.Union450 451 # ------------------------------------------------------------------452 # Token helpers453 # ------------------------------------------------------------------454 def _peek(self, k: int = 1) -> Optional[_Token]:455 """Return the k-th next token without consuming it (1-based)."""456 return self._tokens.peek(k)457 458 def _peek_type(self, k: int = 1) -> Optional[str]:459 """Return the type of the k-th next token, or None if absent (1-based)."""460 tok = self._peek(k)461 return tok.type if tok is not None else None462 463 def _advance(self) -> _Token:464 tok = self._tokens.next()465 if tok is None:466 self._parse_error("At end of input", self.clex.filename)467 else:468 return tok469 470 def _accept(self, token_type: str) -> Optional[_Token]:471 """Conditionally consume next token, only if it's of token_type.472 473 If it is of the expected type, consume and return it.474 Otherwise, leaves the token intact and returns None.475 """476 tok = self._peek()477 if tok is not None and tok.type == token_type:478 return self._advance()479 return None480 481 def _expect(self, token_type: str) -> _Token:482 tok = self._advance()483 if tok.type != token_type:484 self._parse_error(f"before: {tok.value}", self._tok_coord(tok))485 return tok486 487 def _mark(self) -> int:488 return self._tokens.mark()489 490 def _reset(self, mark: int) -> None:491 self._tokens.reset(mark)492 493 def _tok_coord(self, tok: _Token) -> Coord:494 return self._coord(tok.lineno, tok.column)495 496 def _starts_declaration(self, tok: Optional[_Token] = None) -> bool:497 tok = tok or self._peek()498 if tok is None:499 return False500 return tok.type in _DECL_START501 502 def _starts_expression(self, tok: Optional[_Token] = None) -> bool:503 tok = tok or self._peek()504 if tok is None:505 return False506 return tok.type in _STARTS_EXPRESSION507 508 def _starts_statement(self) -> bool:509 tok_type = self._peek_type()510 if tok_type is None:511 return False512 if tok_type in _STARTS_STATEMENT:513 return True514 return self._starts_expression()515 516 def _starts_declarator(self, id_only: bool = False) -> bool:517 tok_type = self._peek_type()518 if tok_type is None:519 return False520 if tok_type in {"TIMES", "LPAREN"}:521 return True522 if id_only:523 return tok_type == "ID"524 return tok_type in {"ID", "TYPEID"}525 526 def _peek_declarator_name_info(self) -> Tuple[Optional[str], bool]:527 mark = self._mark()528 tok_type, saw_paren = self._scan_declarator_name_info()529 self._reset(mark)530 return tok_type, saw_paren531 532 def _parse_any_declarator(533 self, allow_abstract: bool = False, typeid_paren_as_abstract: bool = False534 ) -> Tuple[Optional[c_ast.Node], bool]:535 # C declarators are ambiguous without lookahead. For example:536 # int foo(int (aa)); -> aa is a name (ID)537 # typedef char TT;538 # int bar(int (TT)); -> TT is a type (TYPEID) in parens539 name_type, saw_paren = self._peek_declarator_name_info()540 if name_type is None or (541 typeid_paren_as_abstract and name_type == "TYPEID" and saw_paren542 ):543 if not allow_abstract:544 tok = self._peek()545 coord = self._tok_coord(tok) if tok is not None else self.clex.filename546 self._parse_error("Invalid declarator", coord)547 decl = self._parse_abstract_declarator_opt()548 return decl, False549 550 if name_type == "TYPEID":551 if typeid_paren_as_abstract:552 decl = self._parse_typeid_noparen_declarator()553 else:554 decl = self._parse_typeid_declarator()555 else:556 decl = self._parse_id_declarator()557 return decl, True558 559 def _scan_declarator_name_info(self) -> Tuple[Optional[str], bool]:560 saw_paren = False561 while self._accept("TIMES"):562 while self._peek_type() in _TYPE_QUALIFIER:563 self._advance()564 565 tok = self._peek()566 if tok is None:567 return None, saw_paren568 if tok.type in {"ID", "TYPEID"}:569 self._advance()570 return tok.type, saw_paren571 if tok.type == "LPAREN":572 saw_paren = True573 self._advance()574 tok_type, nested_paren = self._scan_declarator_name_info()575 if nested_paren:576 saw_paren = True577 depth = 1578 while True:579 tok = self._peek()580 if tok is None:581 return None, saw_paren582 if tok.type == "LPAREN":583 depth += 1584 elif tok.type == "RPAREN":585 depth -= 1586 self._advance()587 if depth == 0:588 break589 continue590 self._advance()591 return tok_type, saw_paren592 return None, saw_paren593 594 def _starts_direct_abstract_declarator(self) -> bool:595 return self._peek_type() in {"LPAREN", "LBRACKET"}596 597 def _is_assignment_op(self) -> bool:598 tok = self._peek()599 return tok is not None and tok.type in _ASSIGNMENT_OPS600 601 def _try_parse_paren_type_name(602 self,603 ) -> Optional[Tuple[c_ast.Typename, int, _Token]]:604 """Parse and return a parenthesized type name if present.605 606 Returns (typ, mark, lparen_tok) when the next tokens look like607 '(' type_name ')', where typ is the parsed type name, mark is the608 token-stream position before parsing, and lparen_tok is the LPAREN609 token. Returns None if no parenthesized type name is present.610 """611 mark = self._mark()612 lparen_tok = self._accept("LPAREN")613 if lparen_tok is None:614 return None615 if not self._starts_declaration():616 self._reset(mark)617 return None618 typ = self._parse_type_name()619 if self._accept("RPAREN") is None:620 self._reset(mark)621 return None622 return typ, mark, lparen_tok623 624 # ------------------------------------------------------------------625 # Top-level626 # ------------------------------------------------------------------627 # BNF: translation_unit_or_empty : translation_unit | empty628 def _parse_translation_unit_or_empty(self) -> c_ast.FileAST:629 if self._peek() is None:630 return c_ast.FileAST([])631 return c_ast.FileAST(self._parse_translation_unit())632 633 # BNF: translation_unit : external_declaration+634 def _parse_translation_unit(self) -> List[c_ast.Node]:635 ext = []636 while self._peek() is not None:637 ext.extend(self._parse_external_declaration())638 return ext639 640 # BNF: external_declaration : function_definition641 # | declaration642 # | pp_directive643 # | pppragma_directive644 # | static_assert645 # | ';'646 def _parse_external_declaration(self) -> List[c_ast.Node]:647 tok = self._peek()648 if tok is None:649 return []650 if tok.type == "PPHASH":651 self._parse_pp_directive()652 return []653 if tok.type in {"PPPRAGMA", "_PRAGMA"}:654 return [self._parse_pppragma_directive()]655 if self._accept("SEMI"):656 return []657 if tok.type == "_STATIC_ASSERT":658 return self._parse_static_assert()659 660 if not self._starts_declaration(tok):661 # Special handling for old-style function definitions that have an662 # implicit return type, e.g.663 #664 # foo() {665 # return 5;666 # }667 #668 # These get an implicit 'int' return type.669 decl = self._parse_id_declarator()670 param_decls = None671 if self._peek_type() != "LBRACE":672 self._parse_error("Invalid function definition", decl.coord)673 spec: _DeclSpec = dict(674 qual=[],675 alignment=[],676 storage=[],677 type=[c_ast.IdentifierType(["int"], coord=decl.coord)],678 function=[],679 )680 func = self._build_function_definition(681 spec=spec,682 decl=decl,683 param_decls=param_decls,684 body=self._parse_compound_statement(),685 )686 return [func]687 688 # From here on, parsing a standard declatation/definition.689 spec, saw_type, spec_coord = self._parse_declaration_specifiers(690 allow_no_type=True691 )692 693 name_type, _ = self._peek_declarator_name_info()694 if name_type != "ID":695 decls = self._parse_decl_body_with_spec(spec, saw_type)696 self._expect("SEMI")697 return decls698 699 decl = self._parse_id_declarator()700 701 if self._peek_type() == "LBRACE" or self._starts_declaration():702 param_decls = None703 if self._starts_declaration():704 param_decls = self._parse_declaration_list()705 if self._peek_type() != "LBRACE":706 self._parse_error("Invalid function definition", decl.coord)707 if not spec["type"]:708 spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)]709 func = self._build_function_definition(710 spec=spec,711 decl=decl,712 param_decls=param_decls,713 body=self._parse_compound_statement(),714 )715 return [func]716 717 decl_dict: "_DeclInfo" = dict(decl=decl, init=None, bitsize=None)718 if self._accept("EQUALS"):719 decl_dict["init"] = self._parse_initializer()720 decls = self._parse_init_declarator_list(first=decl_dict)721 decls = self._build_declarations(spec=spec, decls=decls, typedef_namespace=True)722 self._expect("SEMI")723 return decls724 725 # ------------------------------------------------------------------726 # Declarations727 #728 # Declarations always come as lists (because they can be several in one729 # line). When returning parsed declarations, a list is always returned -730 # even if it contains a single element.731 # ------------------------------------------------------------------732 def _parse_declaration(self) -> List[c_ast.Node]:733 decls = self._parse_decl_body()734 self._expect("SEMI")735 return decls736 737 # BNF: decl_body : declaration_specifiers decl_body_with_spec738 def _parse_decl_body(self) -> List[c_ast.Node]:739 spec, saw_type, _ = self._parse_declaration_specifiers(allow_no_type=True)740 return self._parse_decl_body_with_spec(spec, saw_type)741 742 # BNF: decl_body_with_spec : init_declarator_list743 # | struct_or_union_or_enum_only744 def _parse_decl_body_with_spec(745 self, spec: "_DeclSpec", saw_type: bool746 ) -> List[c_ast.Node]:747 decls = None748 if saw_type:749 if self._starts_declarator():750 decls = self._parse_init_declarator_list()751 else:752 if self._starts_declarator(id_only=True):753 decls = self._parse_init_declarator_list(id_only=True)754 755 if decls is None:756 ty = spec["type"]757 s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum)758 if len(ty) == 1 and isinstance(ty[0], s_u_or_e):759 decls = [760 c_ast.Decl(761 name=None,762 quals=spec["qual"],763 align=spec["alignment"],764 storage=spec["storage"],765 funcspec=spec["function"],766 type=ty[0],767 init=None,768 bitsize=None,769 coord=ty[0].coord,770 )771 ]772 else:773 decls = self._build_declarations(774 spec=spec,775 decls=[dict(decl=None, init=None, bitsize=None)],776 typedef_namespace=True,777 )778 else:779 decls = self._build_declarations(780 spec=spec, decls=decls, typedef_namespace=True781 )782 783 return decls784 785 # BNF: declaration_list : declaration+786 def _parse_declaration_list(self) -> List[c_ast.Node]:787 decls = []788 while self._starts_declaration():789 decls.extend(self._parse_declaration())790 return decls791 792 # BNF: declaration_specifiers : (storage_class_specifier793 # | type_specifier794 # | type_qualifier795 # | function_specifier796 # | alignment_specifier)+797 def _parse_declaration_specifiers(798 self, allow_no_type: bool = False799 ) -> Tuple["_DeclSpec", bool, Optional[Coord]]:800 """Parse declaration-specifier sequence.801 802 allow_no_type:803 If True, allow a missing type specifier without error.804 805 Returns:806 (spec, saw_type, first_coord) where spec is a dict with807 qual/storage/type/function/alignment entries, saw_type is True808 if a type specifier was consumed, and first_coord is the coord809 of the first specifier token (used for diagnostics).810 """811 spec = None812 saw_type = False813 first_coord = None814 815 while True:816 tok = self._peek()817 if tok is None:818 break819 820 if tok.type == "_ALIGNAS":821 if first_coord is None:822 first_coord = self._tok_coord(tok)823 spec = self._add_declaration_specifier(824 spec, self._parse_alignment_specifier(), "alignment", append=True825 )826 continue827 828 if tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN":829 if first_coord is None:830 first_coord = self._tok_coord(tok)831 spec = self._add_declaration_specifier(832 spec, self._parse_atomic_specifier(), "type", append=True833 )834 saw_type = True835 continue836 837 if tok.type in _TYPE_QUALIFIER:838 if first_coord is None:839 first_coord = self._tok_coord(tok)840 spec = self._add_declaration_specifier(841 spec, self._advance().value, "qual", append=True842 )843 continue844 845 if tok.type in _STORAGE_CLASS:846 if first_coord is None:847 first_coord = self._tok_coord(tok)848 spec = self._add_declaration_specifier(849 spec, self._advance().value, "storage", append=True850 )851 continue852 853 if tok.type in _FUNCTION_SPEC:854 if first_coord is None:855 first_coord = self._tok_coord(tok)856 spec = self._add_declaration_specifier(857 spec, self._advance().value, "function", append=True858 )859 continue860 861 if tok.type in _TYPE_SPEC_SIMPLE:862 if first_coord is None:863 first_coord = self._tok_coord(tok)864 tok = self._advance()865 spec = self._add_declaration_specifier(866 spec,867 c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),868 "type",869 append=True,870 )871 saw_type = True872 continue873 874 if tok.type == "TYPEID":875 if saw_type:876 break877 if first_coord is None:878 first_coord = self._tok_coord(tok)879 tok = self._advance()880 spec = self._add_declaration_specifier(881 spec,882 c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),883 "type",884 append=True,885 )886 saw_type = True887 continue888 889 if tok.type in {"STRUCT", "UNION"}:890 if first_coord is None:891 first_coord = self._tok_coord(tok)892 spec = self._add_declaration_specifier(893 spec, self._parse_struct_or_union_specifier(), "type", append=True894 )895 saw_type = True896 continue897 898 if tok.type == "ENUM":899 if first_coord is None:900 first_coord = self._tok_coord(tok)901 spec = self._add_declaration_specifier(902 spec, self._parse_enum_specifier(), "type", append=True903 )904 saw_type = True905 continue906 907 break908 909 if spec is None:910 self._parse_error("Invalid declaration", self.clex.filename)911 912 if not saw_type and not allow_no_type:913 self._parse_error("Missing type in declaration", first_coord)914 915 return spec, saw_type, first_coord916 917 # BNF: specifier_qualifier_list : (type_specifier918 # | type_qualifier919 # | alignment_specifier)+920 def _parse_specifier_qualifier_list(self) -> "_DeclSpec":921 spec = None922 saw_type = False923 saw_alignment = False924 first_coord = None925 926 while True:927 tok = self._peek()928 if tok is None:929 break930 931 if tok.type == "_ALIGNAS":932 if first_coord is None:933 first_coord = self._tok_coord(tok)934 spec = self._add_declaration_specifier(935 spec, self._parse_alignment_specifier(), "alignment", append=True936 )937 saw_alignment = True938 continue939 940 if tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN":941 if first_coord is None:942 first_coord = self._tok_coord(tok)943 spec = self._add_declaration_specifier(944 spec, self._parse_atomic_specifier(), "type", append=True945 )946 saw_type = True947 continue948 949 if tok.type in _TYPE_QUALIFIER:950 if first_coord is None:951 first_coord = self._tok_coord(tok)952 spec = self._add_declaration_specifier(953 spec, self._advance().value, "qual", append=True954 )955 continue956 957 if tok.type in _TYPE_SPEC_SIMPLE:958 if first_coord is None:959 first_coord = self._tok_coord(tok)960 tok = self._advance()961 spec = self._add_declaration_specifier(962 spec,963 c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),964 "type",965 append=True,966 )967 saw_type = True968 continue969 970 if tok.type == "TYPEID":971 if saw_type:972 break973 if first_coord is None:974 first_coord = self._tok_coord(tok)975 tok = self._advance()976 spec = self._add_declaration_specifier(977 spec,978 c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),979 "type",980 append=True,981 )982 saw_type = True983 continue984 985 if tok.type in {"STRUCT", "UNION"}:986 if first_coord is None:987 first_coord = self._tok_coord(tok)988 spec = self._add_declaration_specifier(989 spec, self._parse_struct_or_union_specifier(), "type", append=True990 )991 saw_type = True992 continue993 994 if tok.type == "ENUM":995 if first_coord is None:996 first_coord = self._tok_coord(tok)997 spec = self._add_declaration_specifier(998 spec, self._parse_enum_specifier(), "type", append=True999 )1000 saw_type = True1001 continue1002 1003 break1004 1005 if spec is None:1006 self._parse_error("Invalid specifier list", self.clex.filename)1007 1008 if not saw_type and not saw_alignment:1009 self._parse_error("Missing type in declaration", first_coord)1010 1011 if spec.get("storage") is None:1012 spec["storage"] = []1013 if spec.get("function") is None:1014 spec["function"] = []1015 1016 return spec1017 1018 # BNF: type_qualifier_list : type_qualifier+1019 def _parse_type_qualifier_list(self) -> List[str]:1020 quals = []1021 while self._peek_type() in _TYPE_QUALIFIER:1022 quals.append(self._advance().value)1023 return quals1024 1025 # BNF: alignment_specifier : _ALIGNAS '(' type_name | constant_expression ')'1026 def _parse_alignment_specifier(self) -> c_ast.Node:1027 tok = self._expect("_ALIGNAS")1028 self._expect("LPAREN")1029 1030 if self._starts_declaration():1031 typ = self._parse_type_name()1032 self._expect("RPAREN")1033 return c_ast.Alignas(typ, self._tok_coord(tok))1034 1035 expr = self._parse_constant_expression()1036 self._expect("RPAREN")1037 return c_ast.Alignas(expr, self._tok_coord(tok))1038 1039 # BNF: atomic_specifier : _ATOMIC '(' type_name ')'1040 def _parse_atomic_specifier(self) -> c_ast.Node:1041 self._expect("_ATOMIC")1042 self._expect("LPAREN")1043 typ = self._parse_type_name()1044 self._expect("RPAREN")1045 typ.quals.append("_Atomic")1046 return typ1047 1048 # BNF: init_declarator_list : init_declarator (',' init_declarator)*1049 def _parse_init_declarator_list(1050 self, first: Optional["_DeclInfo"] = None, id_only: bool = False1051 ) -> List["_DeclInfo"]:1052 decls = (1053 [first]1054 if first is not None1055 else [self._parse_init_declarator(id_only=id_only)]1056 )1057 1058 while self._accept("COMMA"):1059 decls.append(self._parse_init_declarator(id_only=id_only))1060 return decls1061 1062 # BNF: init_declarator : declarator ('=' initializer)?1063 def _parse_init_declarator(self, id_only: bool = False) -> "_DeclInfo":1064 decl = self._parse_id_declarator() if id_only else self._parse_declarator()1065 init = None1066 if self._accept("EQUALS"):1067 init = self._parse_initializer()1068 return dict(decl=decl, init=init, bitsize=None)1069 1070 # ------------------------------------------------------------------1071 # Structs/unions/enums1072 # ------------------------------------------------------------------1073 # BNF: struct_or_union_specifier : struct_or_union ID? '{' struct_declaration_list? '}'1074 # | struct_or_union ID1075 def _parse_struct_or_union_specifier(self) -> c_ast.Node:1076 tok = self._advance()1077 klass = self._select_struct_union_class(tok.value)1078 1079 if self._peek_type() in {"ID", "TYPEID"}:1080 name_tok = self._advance()1081 if self._peek_type() == "LBRACE":1082 self._advance()1083 if self._accept("RBRACE"):1084 return klass(1085 name=name_tok.value, decls=[], coord=self._tok_coord(name_tok)1086 )1087 decls = self._parse_struct_declaration_list()1088 self._expect("RBRACE")1089 return klass(1090 name=name_tok.value, decls=decls, coord=self._tok_coord(name_tok)1091 )1092 1093 return klass(1094 name=name_tok.value, decls=None, coord=self._tok_coord(name_tok)1095 )1096 1097 if self._peek_type() == "LBRACE":1098 brace_tok = self._advance()1099 if self._accept("RBRACE"):1100 return klass(name=None, decls=[], coord=self._tok_coord(brace_tok))1101 decls = self._parse_struct_declaration_list()1102 self._expect("RBRACE")1103 return klass(name=None, decls=decls, coord=self._tok_coord(brace_tok))1104 1105 self._parse_error("Invalid struct/union declaration", self._tok_coord(tok))1106 1107 # BNF: struct_declaration_list : struct_declaration+1108 def _parse_struct_declaration_list(self) -> List[c_ast.Node]:1109 decls = []1110 while self._peek_type() not in {None, "RBRACE"}:1111 items = self._parse_struct_declaration()1112 if items is None:1113 continue1114 decls.extend(items)1115 return decls1116 1117 # BNF: struct_declaration : specifier_qualifier_list struct_declarator_list? ';'1118 # | static_assert1119 # | pppragma_directive1120 def _parse_struct_declaration(self) -> Optional[List[c_ast.Node]]:1121 if self._peek_type() == "SEMI":1122 self._advance()1123 return None1124 if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:1125 return [self._parse_pppragma_directive()]1126 1127 spec = self._parse_specifier_qualifier_list()1128 assert "typedef" not in spec.get("storage", [])1129 1130 decls = None1131 if self._starts_declarator() or self._peek_type() == "COLON":1132 decls = self._parse_struct_declarator_list()1133 if decls is not None:1134 self._expect("SEMI")1135 return self._build_declarations(spec=spec, decls=decls)1136 1137 if len(spec["type"]) == 1:1138 node = spec["type"][0]1139 if isinstance(node, c_ast.Node):1140 decl_type = node1141 else:1142 decl_type = c_ast.IdentifierType(node)1143 self._expect("SEMI")1144 return self._build_declarations(1145 spec=spec, decls=[dict(decl=decl_type, init=None, bitsize=None)]1146 )1147 1148 self._expect("SEMI")1149 return self._build_declarations(1150 spec=spec, decls=[dict(decl=None, init=None, bitsize=None)]1151 )1152 1153 # BNF: struct_declarator_list : struct_declarator (',' struct_declarator)*1154 def _parse_struct_declarator_list(self) -> List["_DeclInfo"]:1155 decls = [self._parse_struct_declarator()]1156 while self._accept("COMMA"):1157 decls.append(self._parse_struct_declarator())1158 return decls1159 1160 # BNF: struct_declarator : declarator? ':' constant_expression1161 # | declarator (':' constant_expression)?1162 def _parse_struct_declarator(self) -> "_DeclInfo":1163 if self._accept("COLON"):1164 bitsize = self._parse_constant_expression()1165 return {1166 "decl": c_ast.TypeDecl(None, None, None, None),1167 "init": None,1168 "bitsize": bitsize,1169 }1170 1171 decl = self._parse_declarator()1172 if self._accept("COLON"):1173 bitsize = self._parse_constant_expression()1174 return {"decl": decl, "init": None, "bitsize": bitsize}1175 1176 return {"decl": decl, "init": None, "bitsize": None}1177 1178 # BNF: enum_specifier : ENUM ID? '{' enumerator_list? '}'1179 # | ENUM ID1180 def _parse_enum_specifier(self) -> c_ast.Node:1181 tok = self._expect("ENUM")1182 if self._peek_type() in {"ID", "TYPEID"}:1183 name_tok = self._advance()1184 if self._peek_type() == "LBRACE":1185 self._advance()1186 enums = self._parse_enumerator_list()1187 self._expect("RBRACE")1188 return c_ast.Enum(name_tok.value, enums, self._tok_coord(tok))1189 return c_ast.Enum(name_tok.value, None, self._tok_coord(tok))1190 1191 self._expect("LBRACE")1192 enums = self._parse_enumerator_list()1193 self._expect("RBRACE")1194 return c_ast.Enum(None, enums, self._tok_coord(tok))1195 1196 # BNF: enumerator_list : enumerator (',' enumerator)* ','?1197 def _parse_enumerator_list(self) -> c_ast.Node:1198 enum = self._parse_enumerator()1199 enum_list = c_ast.EnumeratorList([enum], enum.coord)1200 while self._accept("COMMA"):