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
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
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)
19from . import c_ast
20from .ast_transforms import fix_atomic_specifiers, fix_switch_cases
21from .c_lexer import CLexer, Token
24@dataclass
25class Coord:
26 """Coordinates of a syntactic element. Consists of:
27 - File name
28 - Line number
29 - Column number
30 """
32 file: str
33 line: int
34 column: int | None = None
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
43class ParseError(Exception):
44 pass
47class CParser:
48 """Recursive-descent C parser.
50 Usage:
51 parser = CParser()
52 ast = parser.parse(text, filename)
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 """
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 )
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)
87 def parse(
88 self, text: str, filename: str = "", debug: bool = False
89 ) -> c_ast.FileAST:
90 """Parses C code and returns an AST.
92 text:
93 A string containing the C source code
95 filename:
96 Name of the file being parsed (for meaningful error messages)
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)
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
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)
117 def _parse_error(self, msg: str, coord: Coord | str | None) -> NoReturn:
118 raise ParseError(f"{coord}: {msg}")
120 def _push_scope(self) -> None:
121 self._scope_stack.append({})
123 def _pop_scope(self) -> None:
124 if len(self._scope_stack) <= 1:
125 raise ParseError("Unmatched '}'")
126 self._scope_stack.pop()
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
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
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
157 def _lex_error_func(self, msg: str, line: int, column: int) -> None:
158 self._parse_error(msg, self._coord(line, column))
160 def _lex_on_lbrace_func(self) -> None:
161 self._push_scope()
163 def _lex_on_rbrace_func(self) -> None:
164 self._pop_scope()
166 def _lex_type_lookup_func(self, name: str) -> bool:
167 """Looks up types that were previously defined with typedef.
169 Passed to the lexer for recognizing identifiers that are types.
170 """
171 return self._is_type_in_scope(name)
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.
211 Note: the declarator and modifier may be modified
212 """
213 modifier_head = modifier
214 modifier_tail = modifier
216 # The modifier may be a nested list. Reach its tail.
217 while modifier_tail.type:
218 modifier_tail = modifier_tail.type
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
232 modifier_tail.type = decl_tail.type
233 decl_tail.type = modifier_head
234 return decl
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
262 decl.name = typ.declname
263 typ.quals = decl.quals[:]
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
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
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
310 if append:
311 spec[kind].append(newspec)
312 else:
313 spec[kind].insert(0, newspec)
315 return spec
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 = []
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)
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]
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 )
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"])
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)
413 fixed_decl = fix_atomic_specifiers(
414 cast(c_ast.Decl | c_ast.Typedef, fixed_decl)
415 )
416 declarations.append(fixed_decl)
418 return declarations
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)
431 declaration = self._build_declarations(
432 spec=spec,
433 decls=[{"decl": decl, "init": None, "bitsize": None}],
434 typedef_namespace=True,
435 )[0]
437 return c_ast.FuncDef(
438 decl=declaration, param_decls=param_decls, body=body, coord=decl.coord
439 )
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
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)
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
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
469 def _accept(self, token_type: str) -> Token | None:
470 """Conditionally consume next token, only if it's of token_type.
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
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
486 def _mark(self) -> int:
487 return self._tokens.mark()
489 def _reset(self, mark: int) -> None:
490 self._tokens.reset(mark)
492 def _tok_coord(self, tok: Token) -> Coord:
493 return self._coord(tok.lineno, tok.column)
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
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
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()
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"}
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
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
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
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()
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
593 def _starts_direct_abstract_declarator(self) -> bool:
594 return self._peek_type() in {"LPAREN", "LBRACKET"}
596 def _is_assignment_op(self) -> bool:
597 tok = self._peek()
598 return tok is not None and tok.type in _ASSIGNMENT_OPS
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.
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
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())
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
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()
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]
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 )
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
698 decl = self._parse_id_declarator()
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]
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
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
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)
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)
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 )
785 return decls
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
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.
804 allow_no_type:
805 If True, allow a missing type specifier without error.
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
817 while True:
818 tok = self._peek()
819 if tok is None:
820 break
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
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
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
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
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
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
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
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
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
909 break
911 if spec is None:
912 self._parse_error("Invalid declaration", self.clex.filename)
914 if not saw_type and not allow_no_type:
915 self._parse_error("Missing type in declaration", first_coord)
917 return spec, saw_type, first_coord
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
928 while True:
929 tok = self._peek()
930 if tok is None:
931 break
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
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
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
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
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
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
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
1005 break
1007 if spec is None:
1008 self._parse_error("Invalid specifier list", self.clex.filename)
1010 if not saw_type and not saw_alignment:
1011 self._parse_error("Missing type in declaration", first_coord)
1013 if spec.get("storage") is None:
1014 spec["storage"] = []
1015 if spec.get("function") is None:
1016 spec["function"] = []
1018 return spec
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
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")
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))
1037 expr = self._parse_constant_expression()
1038 self._expect("RPAREN")
1039 return c_ast.Alignas(expr, self._tok_coord(tok))
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
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 )
1060 while self._accept("COMMA"):
1061 decls.append(self._parse_init_declarator(id_only=id_only))
1062 return decls
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}
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)
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 )
1095 return klass(
1096 name=name_tok.value, decls=None, coord=self._tok_coord(name_tok)
1097 )
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))
1107 self._parse_error("Invalid struct/union declaration", self._tok_coord(tok))
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
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()]
1129 spec = self._parse_specifier_qualifier_list()
1130 assert "typedef" not in spec.get("storage", [])
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)
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 )
1151 self._expect("SEMI")
1152 return self._build_declarations(
1153 spec=spec, decls=[{"decl": None, "init": None, "bitsize": None}]
1154 )
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
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 }
1174 decl = self._parse_declarator()
1175 if self._accept("COLON"):
1176 bitsize = self._parse_constant_expression()
1177 return {"decl": decl, "init": None, "bitsize": bitsize}
1179 return {"decl": decl, "init": None, "bitsize": None}
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))
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))
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
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
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
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)
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)
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)
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
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 )
1276 return self._parse_decl_suffixes(decl)
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
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)
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.
1300 base_type:
1301 Base declarator node to attach (None for direct-declarator parsing,
1302 TypeDecl for abstract declarators).
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)
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 )
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)
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)
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, [])
1346 dim = None
1347 if self._starts_expression():
1348 dim = self._parse_assignment_expression()
1349 self._expect("RBRACKET")
1350 return make_array_decl(dim, [])
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")
1365 func = c_ast.FuncDecl(args=args, type=None, coord=base_decl.coord)
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)
1375 return func
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")
1386 if not stars:
1387 return None
1389 ptr = None
1390 for quals, coord in stars:
1391 ptr = c_ast.PtrDecl(quals=quals, type=ptr, coord=coord)
1392 return ptr
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
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
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)
1417 if not spec["type"]:
1418 spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)]
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)
1431 decl = self._parse_abstract_declarator_opt()
1432 return self._build_parameter_declaration(spec, decl, spec_coord)
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]
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"])
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()
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
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()
1477 coord = None
1478 if decl is not None:
1479 coord = decl.coord
1480 elif spec["type"]:
1481 coord = spec["type"][0].coord
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"]))
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)
1503 if self._starts_direct_abstract_declarator():
1504 return self._parse_direct_abstract_declarator()
1506 return None
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)
1531 return self._parse_decl_suffixes(decl)
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()
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 )
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()
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()
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()
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
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 )
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)
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))
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))
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))
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))
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
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
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)
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
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
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
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()
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
1804 op = tok.value
1805 self._advance()
1806 rhs = self._parse_cast_expression()
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
1818 lhs = c_ast.BinaryOp(op, lhs, rhs, lhs.coord)
1820 return lhs
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()
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)
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)
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))
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))
1873 return self._parse_postfix_expression()
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)
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
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 )
1960 self._parse_error("Invalid expression", self.clex.filename)
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
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)
1987 # BNF: constant_expression : conditional_expression
1988 def _parse_constant_expression(self) -> c_ast.Node:
1989 return self._parse_conditional_expression()
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))
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))
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))
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))
2033 if tok.type in _CHAR_CONST:
2034 return c_ast.Constant("char", tok.value, self._tok_coord(tok))
2036 self._parse_error("Invalid constant", self._tok_coord(tok))
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
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
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
2074 return self._parse_assignment_expression()
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)
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
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
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
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)
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))
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))
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))
2144 self._parse_error("Invalid pragma", self.clex.filename)
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
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))]
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}
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}
2203_STORAGE_CLASS = {"AUTO", "REGISTER", "STATIC", "EXTERN", "TYPEDEF", "_THREAD_LOCAL"}
2205_FUNCTION_SPEC = {"INLINE", "_NORETURN"}
2207_TYPE_QUALIFIER = {"CONST", "RESTRICT", "VOLATILE", "_ATOMIC"}
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}
2224_DECL_START = (
2225 _STORAGE_CLASS
2226 | _FUNCTION_SPEC
2227 | _TYPE_QUALIFIER
2228 | _TYPE_SPEC_SIMPLE
2229 | {"TYPEID", "STRUCT", "UNION", "ENUM", "_ALIGNAS", "_ATOMIC"}
2230)
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}
2248_INT_CONST = {
2249 "INT_CONST_DEC",
2250 "INT_CONST_OCT",
2251 "INT_CONST_HEX",
2252 "INT_CONST_BIN",
2253 "INT_CONST_CHAR",
2254}
2256_FLOAT_CONST = {"FLOAT_CONST", "HEX_FLOAT_CONST"}
2258_CHAR_CONST = {
2259 "CHAR_CONST",
2260 "WCHAR_CONST",
2261 "U8CHAR_CONST",
2262 "U16CHAR_CONST",
2263 "U32CHAR_CONST",
2264}
2266_STRING_LITERAL = {"STRING_LITERAL"}
2268_WSTR_LITERAL = {
2269 "WSTRING_LITERAL",
2270 "U8STRING_LITERAL",
2271 "U16STRING_LITERAL",
2272 "U32STRING_LITERAL",
2273}
2275_STARTS_EXPRESSION = (
2276 _EXPR_START
2277 | _INT_CONST
2278 | _FLOAT_CONST
2279 | _CHAR_CONST
2280 | _STRING_LITERAL
2281 | _WSTR_LITERAL
2282)
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}
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 """
2310 def __init__(self, lexer: CLexer) -> None:
2311 self._lexer = lexer
2312 self._buffer: list[Token | None] = []
2313 self._index = 0
2315 def peek(self, k: int = 1) -> Token | None:
2316 """Peek at the k-th next token in the stream, without consuming it.
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]
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
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
2342 def reset(self, mark: int) -> None:
2343 self._index = mark
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
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]
2367_DeclSpecKind = Literal["qual", "storage", "type", "function", "alignment"]
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