Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pycparser/c_parser.py: 83%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 collections.abc import Callable
10from dataclasses import dataclass
11from typing import (
12 Any,
13 Literal,
14 NoReturn,
15 NotRequired,
16 Optional,
17 TypedDict,
18 cast,
19)
21from . import c_ast
22from .ast_transforms import fix_atomic_specifiers, fix_switch_cases
23from .c_lexer import CLexer, Dialect, Token
26# ASTs contain many coordinates, so slots reduce their memory overhead.
27@dataclass(slots=True)
28class Coord:
29 """Coordinates of a syntactic element. Consists of:
30 - File name
31 - Line number
32 - Column number
33 """
35 file: str
36 line: int
37 column: int | None = None
39 def __str__(self) -> str:
40 text = f"{self.file}:{self.line}"
41 if self.column is not None:
42 text += f":{self.column}"
43 return text
46class ParseError(Exception):
47 pass
50class CParser:
51 """Recursive-descent C parser.
53 Usage:
54 parser = CParser()
55 ast = parser.parse(text, filename)
57 The `lexer` parameter lets you inject a lexer class (defaults to CLexer).
58 The positional parameters other than `lexer` are accepted for backward
59 compatibility with the old PLY-based parser and are otherwise unused.
61 The keyword-only `dialect` parameter selects keyword recognition: "legacy"
62 preserves existing behavior, while "c23" enables C23 spellings, predefined
63 constants, and other C23-only features.
64 """
66 def __init__(
67 self,
68 lex_optimize: bool = True,
69 lexer: type[CLexer] = CLexer,
70 lextab: str = "pycparser.lextab",
71 yacc_optimize: bool = True,
72 yacctab: str = "pycparser.yacctab",
73 yacc_debug: bool = False,
74 taboutputdir: str = "",
75 *,
76 dialect: Dialect = "legacy",
77 ) -> None:
78 self.clex: CLexer = lexer(
79 error_func=self._lex_error_func,
80 dialect=dialect,
81 )
82 self._dialect = dialect
84 # Stack of scopes for keeping track of symbols. _scope_stack[-1] is
85 # the current (topmost) scope. Each scope is a dictionary that
86 # specifies whether a name is a type. If _scope_stack[n][name] is
87 # True, 'name' is currently a type in the scope. If it's False,
88 # 'name' is defined in the scope but not as a type (for instance, if we
89 # saw: int name;)
90 # If 'name' is not a key in _scope_stack[n] then 'name' was not defined
91 # in this scope at all.
92 self._scope_stack: list[dict[str, bool]] = [{}]
93 # Cache the token already checked for typedef-name status to avoid repeated
94 # lookups when peeking or consuming. Cleared when scopes or bindings change.
95 self._typedef_checked_token: Token | None = None
96 self._tokens: _TokenStream = _TokenStream(self.clex.token)
98 def parse(
99 self, text: str, filename: str = "", debug: bool = False
100 ) -> c_ast.FileAST:
101 """Parses C code and returns an AST.
103 text:
104 A string containing the C source code
106 filename:
107 Name of the file being parsed (for meaningful error messages)
109 debug:
110 Deprecated debug flag (unused); for backwards compatibility.
111 """
112 self._scope_stack = [{}]
113 self._typedef_checked_token = None
114 self.clex.input(text, filename)
115 self._tokens = _TokenStream(self.clex.token)
117 ast = self._parse_translation_unit_or_empty()
118 tok = self._peek()
119 if tok is not None:
120 self._parse_error(f"before: {tok.value}", self._tok_coord(tok))
121 return ast
123 # ------------------------------------------------------------------
124 # Scope and declaration helpers
125 # ------------------------------------------------------------------
126 def _parse_error(self, msg: str, coord: Coord | str | None) -> NoReturn:
127 raise ParseError(f"{coord}: {msg}")
129 def _push_scope(self) -> None:
130 self._scope_stack.append({})
131 self._typedef_checked_token = None
133 def _pop_scope(self) -> None:
134 assert len(self._scope_stack) > 1, "Cannot pop the global scope"
135 self._scope_stack.pop()
136 self._typedef_checked_token = None
138 def _add_typedef_name(self, name: str, coord: Coord | None) -> None:
139 """Add a new typedef name (ie a TYPEID) to the current scope"""
140 if not self._scope_stack[-1].get(name, True):
141 self._parse_error(
142 f"Typedef {name!r} previously declared as non-typedef in this scope",
143 coord,
144 )
145 self._scope_stack[-1][name] = True
146 self._typedef_checked_token = None
148 def _add_identifier(self, name: str, coord: Coord | None) -> None:
149 """Add a new object, function, or enum member name (ie an ID) to the
150 current scope
151 """
152 if self._scope_stack[-1].get(name, False):
153 self._parse_error(
154 f"Non-typedef {name!r} previously declared as typedef in this scope",
155 coord,
156 )
157 self._scope_stack[-1][name] = False
158 self._typedef_checked_token = None
160 def _is_type_in_scope(self, name: str) -> bool:
161 """Is *name* a typedef-name in the current scope?"""
162 for scope in reversed(self._scope_stack):
163 # If name is an identifier in this scope it shadows typedefs in
164 # higher scopes.
165 if name in scope:
166 return scope[name]
167 return False
169 def _lex_error_func(self, msg: str, line: int, column: int) -> None:
170 self._parse_error(msg, Coord(file=self.clex.filename, line=line, column=column))
172 # To understand what's going on here, read sections A.8.5 and
173 # A.8.6 of K&R2 very carefully.
174 #
175 # A C type consists of a basic type declaration, with a list
176 # of modifiers. For example:
177 #
178 # int *c[5];
179 #
180 # The basic declaration here is 'int c', and the pointer and
181 # the array are the modifiers.
182 #
183 # Basic declarations are represented by TypeDecl (from module c_ast) and the
184 # modifiers are FuncDecl, PtrDecl and ArrayDecl.
185 #
186 # The standard states that whenever a new modifier is parsed, it should be
187 # added to the end of the list of modifiers. For example:
188 #
189 # K&R2 A.8.6.2: Array Declarators
190 #
191 # In a declaration T D where D has the form
192 # D1 [constant-expression-opt]
193 # and the type of the identifier in the declaration T D1 is
194 # "type-modifier T", the type of the
195 # identifier of D is "type-modifier array of T"
196 #
197 # This is what this method does. The declarator it receives
198 # can be a list of declarators ending with TypeDecl. It
199 # tacks the modifier to the end of this list, just before
200 # the TypeDecl.
201 #
202 # Additionally, the modifier may be a list itself. This is
203 # useful for pointers, that can come as a chain from the rule
204 # p_pointer. In this case, the whole modifier list is spliced
205 # into the new location.
206 def _type_modify_decl(self, decl: Any, modifier: Any) -> c_ast.Node:
207 """Tacks a type modifier on a declarator, and returns
208 the modified declarator.
210 Note: the declarator and modifier may be modified
211 """
212 modifier_head = modifier
213 modifier_tail = modifier
215 # The modifier may be a nested list. Reach its tail.
216 while modifier_tail.type:
217 modifier_tail = modifier_tail.type
219 # If the decl is a basic type, just tack the modifier onto it.
220 if isinstance(decl, c_ast.TypeDecl):
221 modifier_tail.type = decl
222 return modifier
223 else:
224 # Otherwise, the decl is a list of modifiers. Reach
225 # its tail and splice the modifier onto the tail,
226 # pointing to the underlying basic type.
227 decl_tail = decl
228 while not isinstance(decl_tail.type, c_ast.TypeDecl):
229 decl_tail = decl_tail.type
231 modifier_tail.type = decl_tail.type
232 decl_tail.type = modifier_head
233 return decl
235 # Due to the order in which declarators are constructed,
236 # they have to be fixed in order to look like a normal AST.
237 #
238 # When a declaration arrives from syntax construction, it has
239 # these problems:
240 # * The innermost TypeDecl has no type (because the basic
241 # type is only known at the uppermost declaration level)
242 # * The declaration has no variable name, since that is saved
243 # in the innermost TypeDecl
244 # * The typename of the declaration is a list of type
245 # specifiers, and not a node. Here, basic identifier types
246 # should be separated from more complex types like enums
247 # and structs.
248 #
249 # This method fixes these problems.
250 def _fix_decl_name_type(
251 self,
252 decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename,
253 typename: list[c_ast.Node],
254 ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename:
255 """Fixes a declaration. Modifies decl."""
256 # Reach the underlying basic type
257 typ = decl
258 while not isinstance(typ, c_ast.TypeDecl):
259 typ = typ.type
261 decl.name = typ.declname
262 typ.quals = decl.quals[:]
264 # The typename is a list of types. Except for _BitInt with an
265 # optional signedness, any type that isn't an IdentifierType must
266 # be the only type in the list (it's illegal to declare "int enum ..").
267 # If all the types are basic, they're collected in the
268 # IdentifierType holder.
269 names: list[str] = []
270 for tn in typename:
271 if isinstance(tn, c_ast.BitIntType):
272 signedness = None
273 for spec in typename:
274 if spec is tn:
275 continue
276 if (
277 signedness is not None
278 or not isinstance(spec, c_ast.IdentifierType)
279 or spec.names not in (["signed"], ["unsigned"])
280 ):
281 self._parse_error(
282 "Invalid multiple types specified", spec.coord
283 )
284 signedness = spec.names[0]
285 # Specifiers are shared by comma-separated declarations;
286 # keep the parsed specifier unchanged when attaching the sign.
287 typ.type = c_ast.BitIntType(tn.width, signedness, tn.coord)
288 return decl
290 if not isinstance(tn, c_ast.IdentifierType):
291 if len(typename) > 1:
292 self._parse_error("Invalid multiple types specified", tn.coord)
293 else:
294 typ.type = tn
295 # Normalize atomic specifiers for declarations and type
296 # names alike (casts, sizeof, unnamed parameters, etc.).
297 return fix_atomic_specifiers(decl)
298 names.extend(tn.names)
300 if not typename:
301 # Functions default to returning int
302 if not isinstance(decl.type, c_ast.FuncDecl):
303 self._parse_error("Missing type in declaration", decl.coord)
304 typ.type = c_ast.IdentifierType(["int"], coord=decl.coord)
305 else:
306 typ.type = c_ast.IdentifierType(names, coord=typename[0].coord)
307 return decl
309 def _add_declaration_specifier(
310 self,
311 declspec: Optional["_DeclSpec"],
312 newspec: str | c_ast.Node,
313 kind: "_DeclSpecKind",
314 ) -> "_DeclSpec":
315 """Append a specifier in source order; see _DeclSpec for the layout."""
316 if declspec is None:
317 spec: _DeclSpec = {
318 "qual": [],
319 "storage": [],
320 "type": [],
321 "function": [],
322 "alignment": [],
323 "type_attributes": [],
324 }
325 else:
326 spec = declspec
328 if kind == "type":
329 assert isinstance(newspec, c_ast.Node)
330 spec[kind].append(newspec)
331 elif kind == "alignment":
332 assert isinstance(newspec, c_ast.Alignas)
333 spec[kind].append(newspec)
334 else:
335 assert isinstance(newspec, str)
336 spec[kind].append(newspec)
337 return spec
339 def _build_type_name(
340 self, spec: "_DeclSpec", decl: c_ast.Node | None, coord: Coord | None
341 ) -> c_ast.Typename:
342 """Build and normalize an abstract type, including its attributes.
344 Callers select the coordinate according to their grammar context.
345 The name, if any, is taken from the declarator by _fix_decl_name_type.
346 """
347 typename = c_ast.Typename(
348 name=None,
349 quals=spec["qual"][:],
350 align=None,
351 type=decl or c_ast.TypeDecl(None, None, None, None),
352 coord=coord,
353 )
354 return cast(
355 c_ast.Typename,
356 self._attach_type_attributes(
357 self._fix_decl_name_type(typename, spec["type"]), spec
358 ),
359 )
361 def _build_declarations(
362 self,
363 spec: "_DeclSpec",
364 decls: list["_DeclInfo"],
365 typedef_namespace: bool = False,
366 ) -> list[c_ast.Node]:
367 """Builds a list of declarations all sharing the given specifiers.
368 If typedef_namespace is true, each declared name is added
369 to the "typedef namespace", which also includes objects,
370 functions, and enum constants.
371 """
372 is_typedef = "typedef" in spec["storage"]
373 declarations = []
375 # Bit-fields are allowed to be unnamed.
376 if decls[0].get("bitsize") is None:
377 # When redeclaring typedef names as identifiers in inner scopes, a
378 # problem can occur where the identifier gets grouped into
379 # spec['type'], leaving decl as None. This can only occur for the
380 # first declarator.
381 if decls[0]["decl"] is None:
382 if (
383 len(spec["type"]) < 2
384 or not isinstance(spec["type"][-1], c_ast.IdentifierType)
385 or len(spec["type"][-1].names) != 1
386 or not self._is_type_in_scope(spec["type"][-1].names[0])
387 ):
388 coord = "?"
389 for t in spec["type"]:
390 if hasattr(t, "coord"):
391 coord = t.coord
392 break
393 self._parse_error("Invalid declaration", coord)
395 # Make this look as if it came from "direct_declarator:ID"
396 decls[0]["decl"] = c_ast.TypeDecl(
397 declname=spec["type"][-1].names[0],
398 type=None,
399 quals=None,
400 align=spec["alignment"],
401 coord=spec["type"][-1].coord,
402 )
403 # Remove the "new" type's name from the end of spec['type']
404 del spec["type"][-1]
405 # A similar problem can occur where the declaration ends up
406 # looking like an abstract declarator. Give it a name if this is
407 # the case.
408 elif isinstance(decls[0]["decl"], _DECLARATOR_NODES):
409 decls_0_tail = cast(Any, decls[0]["decl"])
410 while not isinstance(decls_0_tail, c_ast.TypeDecl):
411 decls_0_tail = decls_0_tail.type
412 if decls_0_tail.declname is None:
413 type_name = cast(c_ast.IdentifierType, spec["type"][-1])
414 decls_0_tail.declname = type_name.names[0]
415 del spec["type"][-1]
417 for decl in decls:
418 assert decl["decl"] is not None
419 if is_typedef:
420 declaration = c_ast.Typedef(
421 name=None,
422 quals=spec["qual"],
423 storage=spec["storage"],
424 type=decl["decl"],
425 coord=decl["decl"].coord,
426 )
427 else:
428 declaration = c_ast.Decl(
429 name=None,
430 quals=spec["qual"],
431 align=spec["alignment"],
432 storage=spec["storage"],
433 funcspec=spec["function"],
434 type=decl["decl"],
435 init=decl.get("init"),
436 bitsize=decl.get("bitsize"),
437 coord=decl["decl"].coord,
438 )
440 if not isinstance(declaration.type, _DECLARATOR_NODES):
441 fixed_decl = declaration
442 else:
443 fixed_decl = self._fix_decl_name_type(declaration, spec["type"])
445 # Record the declared name in the current scope so the parser can
446 # distinguish identifiers from typedef names.
447 if typedef_namespace:
448 if is_typedef:
449 self._add_typedef_name(fixed_decl.name, fixed_decl.coord)
450 else:
451 self._add_identifier(fixed_decl.name, fixed_decl.coord)
453 declarations.append(self._attach_type_attributes(fixed_decl, spec))
455 return declarations
457 def _build_function_definition(
458 self,
459 spec: "_DeclSpec",
460 decl: c_ast.Node,
461 param_decls: list[c_ast.Node] | None,
462 body: c_ast.Node,
463 ) -> c_ast.Node:
464 """Builds a function definition."""
465 if "typedef" in spec["storage"]:
466 self._parse_error("Invalid typedef", decl.coord)
468 declaration = self._build_declarations(
469 spec=spec,
470 decls=[{"decl": decl}],
471 typedef_namespace=True,
472 )[0]
474 return c_ast.FuncDef(
475 decl=declaration, param_decls=param_decls, body=body, coord=decl.coord
476 )
478 def _select_struct_union_class(self, token: str) -> type:
479 """Given a token (either STRUCT or UNION), selects the
480 appropriate AST class.
481 """
482 if token == "struct":
483 return c_ast.Struct
484 else:
485 return c_ast.Union
487 # ------------------------------------------------------------------
488 # Token helpers
489 # ------------------------------------------------------------------
490 def _peek(self, k: int = 1) -> Token | None:
491 """Return the k-th next token without consuming it (1-based)."""
492 tok = self._tokens.peek(k)
493 if tok is not self._typedef_checked_token:
494 self._classify_identifier(tok)
495 return tok
497 def _classify_identifier(self, tok: Token | None) -> None:
498 # Buffered identifiers are interpreted in the current scope, including
499 # after backtracking. Peeking and consuming can reuse the classification
500 # until a scope or binding changes.
501 if tok is not None and tok.type in {"ID", "TYPEID"}:
502 tok.type = "TYPEID" if self._is_type_in_scope(tok.value) else "ID"
503 self._typedef_checked_token = tok
505 def _peek_type(self, k: int = 1) -> str | None:
506 """Return the type of the k-th next token, or None if absent (1-based)."""
507 tok = self._peek(k)
508 return tok.type if tok is not None else None
510 def _advance(self) -> Token:
511 tok = self._tokens.next()
512 if tok is None:
513 self._parse_error("At end of input", self.clex.filename)
514 if tok is not self._typedef_checked_token:
515 self._classify_identifier(tok)
516 return tok
518 def _accept(self, token_type: str) -> Token | None:
519 """Conditionally consume next token, only if it's of token_type.
521 If it is of the expected type, consume and return it.
522 Otherwise, leaves the token intact and returns None.
523 """
524 tok = self._peek()
525 if tok is not None and tok.type == token_type:
526 return self._advance()
527 return None
529 def _expect(self, token_type: str) -> Token:
530 tok = self._advance()
531 if tok.type != token_type:
532 self._parse_error(f"before: {tok.value}", self._tok_coord(tok))
533 return tok
535 def _mark(self) -> int:
536 return self._tokens.mark()
538 def _reset(self, mark: int) -> None:
539 self._tokens.reset(mark)
541 def _tok_coord(self, tok: Token) -> Coord:
542 return Coord(file=tok.filename, line=tok.lineno, column=tok.column)
544 def _starts_declaration(self, tok: Token | None = None) -> bool:
545 tok = tok or self._peek()
546 if tok is None:
547 return False
548 return tok.type in _DECL_START
550 def _starts_expression(self, tok: Token | None = None) -> bool:
551 tok = tok or self._peek()
552 if tok is None:
553 return False
554 return tok.type in _STARTS_EXPRESSION
556 def _starts_statement(self) -> bool:
557 if self._peek_type() == "ATTRIBUTE_BEGIN":
558 mark = self._mark()
559 self._parse_attribute_specifier_sequence()
560 starts = not self._starts_declaration()
561 self._reset(mark)
562 return starts
563 tok_type = self._peek_type()
564 if tok_type is None:
565 return False
566 if tok_type in _STARTS_STATEMENT:
567 return True
568 return self._starts_expression()
570 def _starts_declarator(self, id_only: bool = False) -> bool:
571 tok_type = self._peek_type()
572 if tok_type is None:
573 return False
574 if tok_type in {"TIMES", "LPAREN"}:
575 return True
576 if id_only:
577 return tok_type == "ID"
578 return tok_type in {"ID", "TYPEID"}
580 def _peek_declarator_name_info(self) -> tuple[str | None, bool]:
581 mark = self._mark()
582 tok_type, saw_paren = self._scan_declarator_name_info()
583 self._reset(mark)
584 return tok_type, saw_paren
586 def _parse_any_declarator(
587 self, allow_abstract: bool = False, typeid_paren_as_abstract: bool = False
588 ) -> tuple[c_ast.Node | None, bool]:
589 # C declarators are ambiguous without lookahead. For example:
590 # int foo(int (aa)); -> aa is a name (ID)
591 # typedef char TT;
592 # int bar(int (TT)); -> TT is a type (TYPEID) in parens
593 name_type, saw_paren = self._peek_declarator_name_info()
594 if name_type is None or (
595 typeid_paren_as_abstract and name_type == "TYPEID" and saw_paren
596 ):
597 if not allow_abstract:
598 tok = self._peek()
599 coord = self._tok_coord(tok) if tok is not None else self.clex.filename
600 self._parse_error("Invalid declarator", coord)
601 decl = self._parse_abstract_declarator_opt()
602 return decl, False
604 if name_type == "TYPEID":
605 if typeid_paren_as_abstract:
606 decl = self._parse_typeid_noparen_declarator()
607 else:
608 decl = self._parse_typeid_declarator()
609 else:
610 decl = self._parse_id_declarator()
611 return decl, True
613 def _scan_declarator_name_info(self) -> tuple[str | None, bool]:
614 saw_paren = False
615 while self._accept("TIMES"):
616 self._parse_attribute_specifier_sequence()
617 while self._peek_type() in _TYPE_QUALIFIER:
618 self._advance()
620 tok = self._peek()
621 if tok is None:
622 return None, saw_paren
623 if tok.type in {"ID", "TYPEID"}:
624 self._advance()
625 return tok.type, saw_paren
626 if tok.type == "LPAREN":
627 saw_paren = True
628 self._advance()
629 tok_type, nested_paren = self._scan_declarator_name_info()
630 if nested_paren:
631 saw_paren = True
632 depth = 1
633 while True:
634 tok = self._peek()
635 if tok is None:
636 return None, saw_paren
637 if tok.type == "LPAREN":
638 depth += 1
639 elif tok.type == "RPAREN":
640 depth -= 1
641 self._advance()
642 if depth == 0:
643 break
644 continue
645 self._advance()
646 return tok_type, saw_paren
647 return None, saw_paren
649 def _starts_direct_abstract_declarator(self) -> bool:
650 return self._peek_type() in {"LPAREN", "LBRACKET"}
652 def _is_assignment_op(self) -> bool:
653 tok = self._peek()
654 return tok is not None and tok.type in _ASSIGNMENT_OPS
656 def _try_parse_paren_type_name(
657 self,
658 ) -> tuple[c_ast.Typename, int, Token, list[str]] | None:
659 """Parse and return a parenthesized type name if present.
661 Returns (typ, mark, lparen_tok, storage) when the next tokens look like
662 '(' type_name ')', where typ is the parsed type name, mark is the
663 token-stream position before parsing, and lparen_tok is the LPAREN
664 token. C23 storage specifiers may precede the type name, but require a
665 following '{' for a compound literal. Returns None if no parenthesized
666 type name is present.
667 """
668 lparen_tok = self._peek()
669 # Avoid saving a backtracking position when there's no '(' to parse.
670 if lparen_tok is None or lparen_tok.type != "LPAREN":
671 return None
672 mark = self._mark()
673 self._advance()
674 if not self._starts_declaration():
675 self._reset(mark)
676 return None
677 storage = []
678 while self._peek_type() in _STORAGE_CLASS:
679 tok = self._advance()
680 if self._dialect != "c23":
681 self._parse_error(
682 "Storage class specifiers in compound literals require C23",
683 self._tok_coord(tok),
684 )
685 storage.append(tok.value)
686 typ = self._parse_type_name()
687 if self._accept("RPAREN") is None:
688 self._reset(mark)
689 return None
690 if storage and self._peek_type() != "LBRACE":
691 self._parse_error(
692 "Storage class specifiers require a compound literal initializer",
693 self._tok_coord(lparen_tok),
694 )
695 return typ, mark, lparen_tok, storage
697 # ------------------------------------------------------------------
698 # Top-level
699 # ------------------------------------------------------------------
700 def _parse_attribute_specifier_sequence(self) -> list[c_ast.AttributeSpecifier]:
701 groups = []
702 while self._peek_type() == "ATTRIBUTE_BEGIN":
703 opening = self._advance()
704 if self._dialect != "c23":
705 self._parse_error("Attributes require C23", self._tok_coord(opening))
706 attributes = []
707 while self._peek_type() != "ATTRIBUTE_END":
708 if self._accept("COMMA"):
709 continue
710 name = self._parse_identifier_or_typeid()
711 namespace = None
712 if self._accept("COLONCOLON"):
713 namespace = name.name
714 name = self._parse_identifier_or_typeid()
715 arguments = self._accept("ATTRIBUTE_ARGUMENTS")
716 attributes.append(
717 c_ast.Attribute(
718 name.name,
719 namespace,
720 arguments.value if arguments is not None else None,
721 name.coord,
722 )
723 )
724 if not self._accept("COMMA"):
725 break
726 self._expect("ATTRIBUTE_END")
727 groups.append(
728 c_ast.AttributeSpecifier(attributes, self._tok_coord(opening))
729 )
730 return groups
732 def _attribute_declarations(
733 self, nodes: list[c_ast.Node], attributes: list[c_ast.AttributeSpecifier]
734 ) -> list[c_ast.Node]:
735 """Attach declaration prefixes, shared by comma-separated declarators."""
736 if not nodes or isinstance(nodes[0], c_ast.Pragma):
737 self._parse_error(
738 "Expected declaration after attributes", attributes[0].coord
739 )
740 for node in nodes:
741 # Trailing pragmas belong to the surrounding list, not to the
742 # declaration carrying these attributes.
743 if isinstance(node, c_ast.Pragma):
744 continue
745 decl = node.decl if isinstance(node, c_ast.FuncDef) else node
746 if not isinstance(decl, (c_ast.Decl, c_ast.Typedef)):
747 self._parse_error(
748 "Expected declaration after attributes", attributes[0].coord
749 )
750 decl.attributes = attributes
751 return nodes
753 def _attribute_statement(
754 self, stmt: c_ast.Node, attributes: list[c_ast.AttributeSpecifier]
755 ) -> c_ast.Node:
756 if isinstance(stmt, (c_ast.Label, c_ast.Case, c_ast.Default)):
757 stmt.attributes = attributes
758 return stmt
759 return c_ast.AttributedStatement(attributes, stmt, attributes[0].coord)
761 def _attach_type_attributes(
762 self, decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename, spec: "_DeclSpec"
763 ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename:
764 """Keep attributes after the base type, inside pointer/array modifiers."""
765 if spec["type_attributes"]:
766 target = decl
767 while isinstance(
768 target.type,
769 (c_ast.PtrDecl, c_ast.ArrayDecl, c_ast.FuncDecl, c_ast.TypeDecl),
770 ):
771 target = target.type
772 target.type = c_ast.AttributedType(
773 target.type, spec["type_attributes"], target.type.coord
774 )
775 return decl
777 # BNF: translation_unit_or_empty : translation_unit | empty
778 def _parse_translation_unit_or_empty(self) -> c_ast.FileAST:
779 if self._peek() is None:
780 return c_ast.FileAST([])
781 return c_ast.FileAST(self._parse_translation_unit())
783 # BNF: translation_unit : external_declaration+
784 def _parse_translation_unit(self) -> list[c_ast.Node]:
785 ext = []
786 while self._peek() is not None:
787 ext.extend(self._parse_external_declaration())
788 return ext
790 # BNF: external_declaration : function_definition
791 # | declaration
792 # | pp_directive
793 # | pppragma_directive
794 # | static_assert
795 # | ';'
796 def _parse_external_declaration(self) -> list[c_ast.Node]:
797 if self._peek_type() == "ATTRIBUTE_BEGIN":
798 attributes = self._parse_attribute_specifier_sequence()
799 if self._accept("SEMI"):
800 return [c_ast.AttributeDeclaration(attributes, attributes[0].coord)]
801 return self._attribute_declarations(
802 self._parse_external_declaration(), attributes
803 )
804 tok = self._peek()
805 if tok is None:
806 return []
807 if tok.type == "PPHASH":
808 self._parse_pp_directive()
809 return []
810 if tok.type in {"PPPRAGMA", "_PRAGMA"}:
811 return [self._parse_pppragma_directive()]
812 if self._accept("SEMI"):
813 return []
814 if tok.type == "_STATIC_ASSERT":
815 return [self._parse_static_assert()]
817 if not self._starts_declaration(tok):
818 # Special handling for old-style function definitions that have an
819 # implicit return type, e.g.
820 #
821 # foo() {
822 # return 5;
823 # }
824 #
825 # These get an implicit 'int' return type.
826 decl = self._parse_id_declarator()
827 if self._peek_type() != "LBRACE":
828 self._parse_error("Invalid function definition", decl.coord)
829 spec: _DeclSpec = {
830 "qual": [],
831 "alignment": [],
832 "storage": [],
833 "type": [c_ast.IdentifierType(["int"], coord=decl.coord)],
834 "function": [],
835 "type_attributes": [],
836 }
837 return [self._parse_function_definition(spec, decl)]
839 # From here on, parsing a standard declatation/definition.
840 spec, saw_type, spec_coord = self._parse_declaration_specifiers(
841 allow_no_type=True
842 )
843 if self._is_inferred_auto(spec):
844 decls = self._parse_auto_declaration(spec)
845 return self._parse_declaration_end(decls)
847 name_type, _ = self._peek_declarator_name_info()
848 if name_type != "ID":
849 decls = self._parse_decl_body_with_spec(spec, saw_type)
850 return self._parse_declaration_end(decls)
852 decl = self._parse_id_declarator()
854 if self._peek_type() == "LBRACE" or self._starts_declaration():
855 if not spec["type"]:
856 spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)]
857 return [self._parse_function_definition(spec, decl)]
859 decl_dict: _DeclInfo = {"decl": decl}
860 if self._accept("EQUALS"):
861 decl_dict["init"] = self._parse_initializer()
862 decls = self._parse_init_declarator_list(first=decl_dict)
863 decls = self._build_declarations(spec=spec, decls=decls, typedef_namespace=True)
864 return self._parse_declaration_end(decls)
866 def _parse_function_definition(
867 self, spec: "_DeclSpec", decl: c_ast.Node
868 ) -> c_ast.Node:
869 # Open the scope shared by parameters, old-style parameter declarations,
870 # and the outermost body.
871 self._push_scope()
872 if isinstance(decl, c_ast.FuncDecl) and decl.args is not None:
873 for param in decl.args.params:
874 name = getattr(param, "name", None)
875 if name:
876 self._add_identifier(name, param.coord)
877 param_decls = (
878 self._parse_declaration_list() if self._starts_declaration() else None
879 )
880 if self._peek_type() != "LBRACE":
881 self._parse_error("Invalid function definition", decl.coord)
882 body = self._parse_compound_statement(new_scope=False)
883 self._pop_scope()
884 return self._build_function_definition(spec, decl, param_decls, body)
886 # ------------------------------------------------------------------
887 # Declarations
888 #
889 # Declarations always come as lists (because they can be several in one
890 # line). When returning parsed declarations, a list is always returned -
891 # even if it contains a single element. Pragmas before the final semicolon
892 # follow the declarations in that list.
893 # ------------------------------------------------------------------
894 # BNF: declaration : decl_body pppragma_directive* ';' | static_assert
895 def _parse_declaration(self) -> list[c_ast.Node]:
896 if self._peek_type() == "ATTRIBUTE_BEGIN":
897 attributes = self._parse_attribute_specifier_sequence()
898 if self._accept("SEMI"):
899 return [c_ast.AttributeDeclaration(attributes, attributes[0].coord)]
900 return self._attribute_declarations(self._parse_declaration(), attributes)
901 if self._peek_type() == "_STATIC_ASSERT":
902 return [self._parse_static_assert()]
903 decls = self._parse_decl_body()
904 return self._parse_declaration_end(decls)
906 def _parse_declaration_end(self, decls: list[c_ast.Node]) -> list[c_ast.Node]:
907 """Consume a declaration's semicolon and retain preceding pragmas.
909 Macro expansions can place diagnostic pragmas just before the final
910 semicolon (issue #478). Represent them after the completed declarations
911 in the surrounding list, just like pragmas following the semicolon.
912 """
913 pragmas = self._parse_pppragma_directive_list()
914 self._expect("SEMI")
915 return decls + pragmas
917 # BNF: decl_body : declaration_specifiers decl_body_with_spec
918 def _parse_decl_body(self) -> list[c_ast.Node]:
919 spec, saw_type, _ = self._parse_declaration_specifiers(allow_no_type=True)
920 return self._parse_decl_body_with_spec(spec, saw_type)
922 # BNF: decl_body_with_spec : init_declarator_list
923 # | struct_or_union_or_enum_only
924 def _parse_decl_body_with_spec(
925 self, spec: "_DeclSpec", saw_type: bool
926 ) -> list[c_ast.Node]:
927 if self._is_inferred_auto(spec):
928 return self._parse_auto_declaration(spec)
930 # saw_type is True if the specifiers included an actual type (as
931 # opposed to only storage/function/qualifiers).
932 decl_infos: list[_DeclInfo] | None = None
933 if saw_type:
934 if self._starts_declarator():
935 decl_infos = self._parse_init_declarator_list()
936 else:
937 if self._starts_declarator(id_only=True):
938 decl_infos = self._parse_init_declarator_list(id_only=True)
940 decls: list[c_ast.Node]
941 if decl_infos is None:
942 ty = spec["type"]
943 s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum)
944 if len(ty) == 1 and isinstance(ty[0], s_u_or_e):
945 decl = c_ast.Decl(
946 name=None,
947 quals=spec["qual"],
948 align=spec["alignment"],
949 storage=spec["storage"],
950 funcspec=spec["function"],
951 type=ty[0],
952 init=None,
953 bitsize=None,
954 coord=ty[0].coord,
955 )
956 decls = [self._attach_type_attributes(decl, spec)]
957 else:
958 decls = self._build_declarations(
959 spec=spec,
960 decls=[{"decl": None}],
961 typedef_namespace=True,
962 )
963 else:
964 decls = self._build_declarations(
965 spec=spec, decls=decl_infos, typedef_namespace=True
966 )
968 return decls
970 def _is_inferred_auto(self, spec: "_DeclSpec") -> bool:
971 return self._dialect == "c23" and "auto" in spec["storage"] and not spec["type"]
973 # C23 type inference uses one plain (possibly parenthesized) name and an
974 # expression initializer. Preserve its syntax without resolving the type.
975 def _parse_auto_declaration(self, spec: "_DeclSpec") -> list[c_ast.Node]:
976 decl = self._parse_id_declarator()
977 if not isinstance(decl, c_ast.TypeDecl):
978 self._parse_error(
979 "Inferred auto declaration requires a plain identifier", decl.coord
980 )
981 self._expect("EQUALS")
982 init = self._parse_assignment_expression()
983 spec["type"] = [c_ast.InferredType(coord=decl.coord)]
984 # Register the name only after parsing the initializer.
985 return self._build_declarations(
986 spec=spec,
987 decls=[{"decl": decl, "init": init}],
988 typedef_namespace=True,
989 )
991 # BNF: declaration_list : (declaration pppragma_directive*)+
992 def _parse_declaration_list(self) -> list[c_ast.Node]:
993 decls = []
994 while self._starts_declaration():
995 decls.extend(self._parse_declaration())
996 # K&R parameter declarations retain pragmas on either side of
997 # their semicolons, including when reparsing generated code.
998 decls.extend(self._parse_pppragma_directive_list())
999 return decls
1001 # BNF: declaration_specifiers : (storage_class_specifier
1002 # | type_specifier
1003 # | type_qualifier
1004 # | function_specifier
1005 # | alignment_specifier)+
1006 def _parse_declaration_specifiers(
1007 self, allow_no_type: bool = False
1008 ) -> tuple["_DeclSpec", bool, Coord | None]:
1009 """Parse declaration-specifier sequence.
1011 allow_no_type:
1012 If True, allow a missing type specifier without error.
1014 Returns:
1015 (spec, saw_type, first_coord) where spec is a dict with
1016 qual/storage/type/function/alignment/type_attributes entries, saw_type is True
1017 if a type specifier was consumed, and first_coord is the coord
1018 of the first specifier token (used for diagnostics).
1019 """
1020 spec, saw_type, first_coord = self._parse_specifiers(
1021 allow_storage_and_function=True
1022 )
1023 if spec is None:
1024 self._parse_error("Invalid declaration", self.clex.filename)
1025 if not saw_type and not allow_no_type:
1026 self._parse_error("Missing type in declaration", first_coord)
1027 return spec, saw_type, first_coord
1029 # BNF: specifier_qualifier_list : (type_specifier
1030 # | type_qualifier
1031 # | alignment_specifier)+
1032 def _parse_specifier_qualifier_list(self) -> "_DeclSpec":
1033 spec, saw_type, first_coord = self._parse_specifiers(
1034 allow_storage_and_function=False
1035 )
1036 if spec is None:
1037 self._parse_error("Invalid specifier list", self.clex.filename)
1038 if not saw_type and not spec["alignment"]:
1039 self._parse_error("Missing type in declaration", first_coord)
1040 return spec
1042 def _parse_specifiers(
1043 self, *, allow_storage_and_function: bool
1044 ) -> tuple["_DeclSpec | None", bool, Coord | None]:
1045 """Consume specifiers shared by declarations, members, and type names.
1047 Storage classes and function specifiers are only accepted in
1048 declarations. Callers handle their own missing-specifier diagnostics.
1049 """
1050 spec = None
1051 saw_type = False
1052 first_coord = None
1054 while True:
1055 tok = self._peek()
1056 if tok is None:
1057 break
1059 if tok.type == "ATTRIBUTE_BEGIN" and spec is not None:
1060 spec["type_attributes"] = self._parse_attribute_specifier_sequence()
1061 break
1063 # Once a type has been seen, a typedef name can be the declarator
1064 # (e.g. the second T in "T T"), rather than another type specifier.
1065 if tok.type == "TYPEID" and saw_type:
1066 break
1068 # _Atomic is a type specifier with parentheses, a qualifier without.
1069 is_atomic_specifier = (
1070 tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN"
1071 )
1072 if first_coord is None:
1073 first_coord = self._tok_coord(tok)
1075 kind: _DeclSpecKind = "type"
1076 newspec: str | c_ast.Node
1077 if tok.type == "_ALIGNAS":
1078 kind = "alignment"
1079 newspec = self._parse_alignment_specifier()
1080 elif is_atomic_specifier:
1081 newspec = self._parse_atomic_specifier()
1082 elif tok.type in _TYPEOF_SPEC:
1083 newspec = self._parse_typeof_specifier()
1084 elif tok.type == "_BITINT":
1085 newspec = self._parse_bitint_specifier()
1086 elif tok.type in _TYPE_QUALIFIER:
1087 kind = "qual"
1088 newspec = self._advance().value
1089 elif allow_storage_and_function and tok.type in _STORAGE_CLASS:
1090 kind = "storage"
1091 newspec = self._advance().value
1092 elif allow_storage_and_function and tok.type in _FUNCTION_SPEC:
1093 kind = "function"
1094 newspec = self._advance().value
1095 elif tok.type in _TYPE_SPEC_SIMPLE or tok.type == "TYPEID":
1096 tok = self._advance()
1097 newspec = c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok))
1098 elif tok.type in {"STRUCT", "UNION"}:
1099 newspec = self._parse_struct_or_union_specifier()
1100 elif tok.type == "ENUM":
1101 newspec = self._parse_enum_specifier()
1102 else:
1103 break
1105 spec = self._add_declaration_specifier(spec, newspec, kind)
1106 if kind == "type":
1107 saw_type = True
1109 return spec, saw_type, first_coord
1111 # BNF: type_qualifier_list : type_qualifier+
1112 def _parse_type_qualifier_list(self) -> list[str]:
1113 quals = []
1114 while self._peek_type() in _TYPE_QUALIFIER:
1115 quals.append(self._advance().value)
1116 return quals
1118 # BNF: alignment_specifier : _ALIGNAS '(' type_name | constant_expression ')'
1119 def _parse_alignment_specifier(self) -> c_ast.Alignas:
1120 tok = self._expect("_ALIGNAS")
1121 self._expect("LPAREN")
1123 if self._starts_declaration():
1124 typ = self._parse_type_name()
1125 self._expect("RPAREN")
1126 return c_ast.Alignas(typ, self._tok_coord(tok))
1128 expr = self._parse_constant_expression()
1129 self._expect("RPAREN")
1130 return c_ast.Alignas(expr, self._tok_coord(tok))
1132 # BNF: atomic_specifier : _ATOMIC '(' type_name ')'
1133 def _parse_atomic_specifier(self) -> c_ast.Node:
1134 self._expect("_ATOMIC")
1135 self._expect("LPAREN")
1136 typ = self._parse_type_name()
1137 self._expect("RPAREN")
1138 typ.quals.append("_Atomic")
1139 return typ
1141 # BNF: typeof_specifier : (TYPEOF | TYPEOF_UNQUAL) '(' (type_name | expression) ')'
1142 def _parse_typeof_specifier(self) -> c_ast.TypeOf:
1143 tok = self._advance()
1144 self._expect("LPAREN")
1145 if self._starts_declaration():
1146 expr = self._parse_type_name()
1147 else:
1148 expr = self._parse_expression()
1149 self._expect("RPAREN")
1150 return c_ast.TypeOf(tok.value, expr, self._tok_coord(tok))
1152 # BNF: bitint_specifier : _BITINT '(' constant_expression ')'
1153 def _parse_bitint_specifier(self) -> c_ast.BitIntType:
1154 tok = self._expect("_BITINT")
1155 self._expect("LPAREN")
1156 width = self._parse_constant_expression()
1157 self._expect("RPAREN")
1158 return c_ast.BitIntType(width, None, self._tok_coord(tok))
1160 # BNF: init_declarator_list : init_declarator (',' init_declarator)*
1161 def _parse_init_declarator_list(
1162 self, first: Optional["_DeclInfo"] = None, id_only: bool = False
1163 ) -> list["_DeclInfo"]:
1164 decls = (
1165 [first]
1166 if first is not None
1167 else [self._parse_init_declarator(id_only=id_only)]
1168 )
1170 while self._accept("COMMA"):
1171 decls.append(self._parse_init_declarator(id_only=id_only))
1172 return decls
1174 # BNF: init_declarator : declarator ('=' initializer)?
1175 def _parse_init_declarator(self, id_only: bool = False) -> "_DeclInfo":
1176 decl = self._parse_id_declarator() if id_only else self._parse_declarator()
1177 if self._accept("EQUALS"):
1178 return {"decl": decl, "init": self._parse_initializer()}
1179 return {"decl": decl}
1181 # ------------------------------------------------------------------
1182 # Structs/unions/enums
1183 # ------------------------------------------------------------------
1184 # BNF: struct_or_union_specifier : struct_or_union ID? '{' struct_declaration_list? '}'
1185 # | struct_or_union ID
1186 def _parse_struct_or_union_specifier(self) -> c_ast.Node:
1187 tok = self._advance()
1188 attributes = self._parse_attribute_specifier_sequence()
1189 klass = self._select_struct_union_class(tok.value)
1191 if self._peek_type() in {"ID", "TYPEID"}:
1192 name_tok = self._advance()
1193 if self._peek_type() == "LBRACE":
1194 self._advance()
1195 if self._accept("RBRACE"):
1196 return klass(
1197 attributes=attributes,
1198 name=name_tok.value,
1199 decls=[],
1200 coord=self._tok_coord(name_tok),
1201 )
1202 decls = self._parse_struct_declaration_list()
1203 self._expect("RBRACE")
1204 return klass(
1205 attributes=attributes,
1206 name=name_tok.value,
1207 decls=decls,
1208 coord=self._tok_coord(name_tok),
1209 )
1211 return klass(
1212 attributes=attributes,
1213 name=name_tok.value,
1214 decls=None,
1215 coord=self._tok_coord(name_tok),
1216 )
1218 if self._peek_type() == "LBRACE":
1219 brace_tok = self._advance()
1220 if self._accept("RBRACE"):
1221 return klass(
1222 attributes=attributes,
1223 name=None,
1224 decls=[],
1225 coord=self._tok_coord(brace_tok),
1226 )
1227 decls = self._parse_struct_declaration_list()
1228 self._expect("RBRACE")
1229 return klass(
1230 attributes=attributes,
1231 name=None,
1232 decls=decls,
1233 coord=self._tok_coord(brace_tok),
1234 )
1236 self._parse_error("Invalid struct/union declaration", self._tok_coord(tok))
1238 # BNF: struct_declaration_list : struct_declaration+
1239 def _parse_struct_declaration_list(self) -> list[c_ast.Node]:
1240 decls = []
1241 while self._peek_type() not in {None, "RBRACE"}:
1242 items = self._parse_struct_declaration()
1243 if items is None:
1244 continue
1245 decls.extend(items)
1246 return decls
1248 # BNF: struct_declaration : specifier_qualifier_list struct_declarator_list?
1249 # pppragma_directive* ';'
1250 # | static_assert
1251 # | pppragma_directive
1252 def _parse_struct_declaration(self) -> list[c_ast.Node] | None:
1253 if self._peek_type() == "ATTRIBUTE_BEGIN":
1254 attributes = self._parse_attribute_specifier_sequence()
1255 return self._attribute_declarations(
1256 self._parse_struct_declaration() or [], attributes
1257 )
1258 if self._peek_type() == "_STATIC_ASSERT":
1259 return [self._parse_static_assert()]
1260 if self._peek_type() == "SEMI":
1261 self._advance()
1262 return None
1263 if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:
1264 return [self._parse_pppragma_directive()]
1266 spec = self._parse_specifier_qualifier_list()
1267 assert "typedef" not in spec.get("storage", [])
1269 decls = None
1270 if self._starts_declarator() or self._peek_type() == "COLON":
1271 decls = self._parse_struct_declarator_list()
1272 if decls is not None:
1273 return self._parse_declaration_end(
1274 self._build_declarations(spec=spec, decls=decls)
1275 )
1277 if len(spec["type"]) == 1:
1278 node = spec["type"][0]
1279 if isinstance(node, c_ast.Node):
1280 decl_type = node
1281 else:
1282 decl_type = c_ast.IdentifierType(node)
1283 return self._parse_declaration_end(
1284 self._build_declarations(spec=spec, decls=[{"decl": decl_type}])
1285 )
1287 return self._parse_declaration_end(
1288 self._build_declarations(spec=spec, decls=[{"decl": None}])
1289 )
1291 # BNF: struct_declarator_list : struct_declarator (',' struct_declarator)*
1292 def _parse_struct_declarator_list(self) -> list["_DeclInfo"]:
1293 decls = [self._parse_struct_declarator()]
1294 while self._accept("COMMA"):
1295 decls.append(self._parse_struct_declarator())
1296 return decls
1298 # BNF: struct_declarator : declarator? ':' constant_expression
1299 # | declarator (':' constant_expression)?
1300 def _parse_struct_declarator(self) -> "_DeclInfo":
1301 if self._accept("COLON"):
1302 bitsize = self._parse_constant_expression()
1303 return {
1304 "decl": c_ast.TypeDecl(None, None, None, None),
1305 "bitsize": bitsize,
1306 }
1308 decl = self._parse_declarator()
1309 if self._accept("COLON"):
1310 bitsize = self._parse_constant_expression()
1311 return {"decl": decl, "bitsize": bitsize}
1313 return {"decl": decl}
1315 # BNF: enum_specifier : ENUM ID? enum_type_specifier? '{' enumerator_list '}'
1316 # | ENUM ID enum_type_specifier?
1317 def _parse_enum_specifier(self) -> c_ast.Node:
1318 tok = self._expect("ENUM")
1319 attributes = self._parse_attribute_specifier_sequence()
1320 name = None
1321 if self._peek_type() in {"ID", "TYPEID"}:
1322 name = self._advance().value
1324 underlying_type = None
1325 # A colon followed by an expression belongs to an unnamed bit-field,
1326 # e.g. struct S { enum E : 3; }, rather than a fixed underlying type.
1327 if self._peek_type() == "COLON" and self._starts_declaration(self._peek(2)):
1328 underlying_type = self._parse_enum_type_specifier()
1330 enums = None
1331 if name is None or self._peek_type() == "LBRACE":
1332 self._expect("LBRACE")
1333 enums = self._parse_enumerator_list()
1334 self._expect("RBRACE")
1335 return c_ast.Enum(
1336 name,
1337 enums,
1338 underlying_type,
1339 self._tok_coord(tok),
1340 attributes=attributes,
1341 )
1343 # BNF: enum_type_specifier : ':' specifier_qualifier_list (C23)
1344 def _parse_enum_type_specifier(self) -> c_ast.Typename:
1345 colon = self._expect("COLON")
1346 if self._dialect != "c23":
1347 self._parse_error(
1348 "Fixed underlying enum types require C23", self._tok_coord(colon)
1349 )
1350 # The base is a specifier-qualifier list, not a full type name: any
1351 # following pointer, array, or function declarator belongs to the enum.
1352 spec = self._parse_specifier_qualifier_list()
1353 coord = spec["type"][0].coord if spec["type"] else self._tok_coord(colon)
1354 return self._build_type_name(spec, None, coord)
1356 # BNF: enumerator_list : pppragma_directive* enumerator pppragma_directive*
1357 # (',' pppragma_directive* enumerator pppragma_directive*)*
1358 # (',' pppragma_directive*)?
1359 def _parse_enumerator_list(self) -> c_ast.Node:
1360 members = self._parse_pppragma_directive_list()
1361 # Pragmas do not count as enumerators: even with leading directives,
1362 # the list must contain at least one enumerator.
1363 enum = self._parse_enumerator()
1364 members.append(enum)
1365 coord = enum.coord
1366 while True:
1367 # Directives may appear on either side of a separating comma, or
1368 # after the last enumerator. They never replace the comma itself.
1369 members.extend(self._parse_pppragma_directive_list())
1370 if not self._accept("COMMA"):
1371 break
1372 members.extend(self._parse_pppragma_directive_list())
1373 if self._peek_type() == "RBRACE":
1374 break
1375 members.append(self._parse_enumerator())
1376 return c_ast.EnumeratorList(members, coord)
1378 # BNF: enumerator : ID ('=' constant_expression)?
1379 def _parse_enumerator(self) -> c_ast.Node:
1380 name_tok = self._expect("ID")
1381 attributes = self._parse_attribute_specifier_sequence()
1382 if self._accept("EQUALS"):
1383 value = self._parse_constant_expression()
1384 else:
1385 value = None
1386 enum = c_ast.Enumerator(
1387 name_tok.value,
1388 value,
1389 self._tok_coord(name_tok),
1390 attributes=attributes,
1391 )
1392 self._add_identifier(enum.name, enum.coord)
1393 return enum
1395 # ------------------------------------------------------------------
1396 # Declarators
1397 # ------------------------------------------------------------------
1398 # BNF: declarator : pointer? direct_declarator
1399 def _parse_declarator(self) -> c_ast.Node:
1400 decl, _ = self._parse_any_declarator(
1401 allow_abstract=False, typeid_paren_as_abstract=False
1402 )
1403 assert decl is not None
1404 return decl
1406 # BNF: id_declarator : declarator with ID name
1407 def _parse_id_declarator(self) -> c_ast.Node:
1408 return self._parse_declarator_kind(kind="id", allow_paren=True)
1410 # BNF: typeid_declarator : declarator with TYPEID name
1411 def _parse_typeid_declarator(self) -> c_ast.Node:
1412 return self._parse_declarator_kind(kind="typeid", allow_paren=True)
1414 # BNF: typeid_noparen_declarator : declarator without parenthesized name
1415 def _parse_typeid_noparen_declarator(self) -> c_ast.Node:
1416 return self._parse_declarator_kind(kind="typeid", allow_paren=False)
1418 # BNF: declarator_kind : pointer? direct_declarator(kind)
1419 def _parse_declarator_kind(self, kind: str, allow_paren: bool) -> c_ast.Node:
1420 ptr = None
1421 if self._peek_type() == "TIMES":
1422 ptr = self._parse_pointer()
1423 direct = self._parse_direct_declarator(kind, allow_paren=allow_paren)
1424 if ptr is not None:
1425 return self._type_modify_decl(direct, ptr)
1426 return direct
1428 # BNF: direct_declarator : ID | TYPEID | '(' declarator ')'
1429 # | direct_declarator '[' ... ']'
1430 # | direct_declarator '(' ... ')'
1431 def _parse_direct_declarator(
1432 self, kind: str, allow_paren: bool = True
1433 ) -> c_ast.Node:
1434 if allow_paren and self._accept("LPAREN"):
1435 decl = self._parse_declarator_kind(kind, allow_paren=True)
1436 self._expect("RPAREN")
1437 else:
1438 if kind == "id":
1439 name_tok = self._expect("ID")
1440 else:
1441 name_tok = self._expect("TYPEID")
1442 decl = c_ast.TypeDecl(
1443 declname=name_tok.value,
1444 type=None,
1445 quals=None,
1446 align=None,
1447 coord=self._tok_coord(name_tok),
1448 )
1449 decl.attributes = self._parse_attribute_specifier_sequence()
1451 return self._parse_decl_suffixes(decl)
1453 def _parse_decl_suffixes(self, decl: c_ast.Node) -> c_ast.Node:
1454 """Parse a chain of array/function suffixes and attach them to decl."""
1455 while True:
1456 if self._peek_type() == "LBRACKET":
1457 decl = self._type_modify_decl(decl, self._parse_array_decl(decl))
1458 continue
1459 if self._peek_type() == "LPAREN":
1460 func = self._parse_function_decl(decl)
1461 decl = self._type_modify_decl(decl, func)
1462 continue
1463 break
1464 return decl
1466 # BNF: array_decl : '[' array_specifiers? assignment_expression? ']'
1467 def _parse_array_decl(self, base_decl: c_ast.Node) -> c_ast.Node:
1468 return self._parse_array_decl_common(base_type=None, coord=base_decl.coord)
1470 def _parse_array_decl_common(
1471 self, base_type: c_ast.Node | None, coord: Coord | None = None
1472 ) -> c_ast.Node:
1473 """Parse an array declarator suffix and return an ArrayDecl node.
1475 base_type:
1476 Base declarator node to attach (None for direct-declarator parsing,
1477 TypeDecl for abstract declarators).
1479 coord:
1480 Coordinate to use for the ArrayDecl. If None, uses the '[' token.
1481 """
1482 lbrack_tok = self._expect("LBRACKET")
1483 if coord is None:
1484 coord = self._tok_coord(lbrack_tok)
1486 def make_array_decl(dim, dim_quals):
1487 return c_ast.ArrayDecl(
1488 type=base_type,
1489 dim=dim,
1490 dim_quals=dim_quals,
1491 coord=coord,
1492 attributes=self._parse_attribute_specifier_sequence(),
1493 )
1495 if self._accept("STATIC"):
1496 dim_quals = ["static"] + (self._parse_type_qualifier_list() or [])
1497 dim = self._parse_assignment_expression()
1498 self._expect("RBRACKET")
1499 return make_array_decl(dim, dim_quals)
1501 if self._peek_type() in _TYPE_QUALIFIER:
1502 dim_quals = self._parse_type_qualifier_list() or []
1503 if self._accept("STATIC"):
1504 dim_quals = dim_quals + ["static"]
1505 dim = self._parse_assignment_expression()
1506 self._expect("RBRACKET")
1507 return make_array_decl(dim, dim_quals)
1508 times_tok = self._accept("TIMES")
1509 if times_tok:
1510 self._expect("RBRACKET")
1511 dim = c_ast.ID(times_tok.value, self._tok_coord(times_tok))
1512 return make_array_decl(dim, dim_quals)
1513 dim = None
1514 if self._starts_expression():
1515 dim = self._parse_assignment_expression()
1516 self._expect("RBRACKET")
1517 return make_array_decl(dim, dim_quals)
1519 times_tok = self._accept("TIMES")
1520 if times_tok:
1521 self._expect("RBRACKET")
1522 dim = c_ast.ID(times_tok.value, self._tok_coord(times_tok))
1523 return make_array_decl(dim, [])
1525 dim = None
1526 if self._starts_expression():
1527 dim = self._parse_assignment_expression()
1528 self._expect("RBRACKET")
1529 return make_array_decl(dim, [])
1531 # BNF: function_decl : '(' parameter_type_list_opt | identifier_list_opt ')'
1532 def _parse_function_decl(self, base_decl: c_ast.Node) -> c_ast.Node:
1533 self._expect("LPAREN")
1534 if self._accept("RPAREN"):
1535 args = None
1536 else:
1537 args = (
1538 self._parse_parameter_type_list()
1539 if self._starts_declaration() or self._peek_type() == "ELLIPSIS"
1540 else self._parse_identifier_list_opt()
1541 )
1542 self._expect("RPAREN")
1544 func = c_ast.FuncDecl(
1545 args=args,
1546 type=None,
1547 coord=base_decl.coord,
1548 attributes=self._parse_attribute_specifier_sequence(),
1549 )
1551 return func
1553 # BNF: pointer : '*' type_qualifier_list? pointer?
1554 def _parse_pointer(self) -> c_ast.Node | None:
1555 stars = []
1556 times_tok = self._accept("TIMES")
1557 while times_tok:
1558 attributes = self._parse_attribute_specifier_sequence()
1559 quals = self._parse_type_qualifier_list() or []
1560 stars.append((quals, self._tok_coord(times_tok), attributes))
1561 times_tok = self._accept("TIMES")
1563 if not stars:
1564 return None
1566 ptr = None
1567 for quals, coord, attributes in stars:
1568 ptr = c_ast.PtrDecl(
1569 quals=quals, type=ptr, coord=coord, attributes=attributes
1570 )
1571 return ptr
1573 # BNF: parameter_type_list : parameter_list (',' ELLIPSIS)?
1574 # | ELLIPSIS (C23)
1575 def _parse_parameter_type_list(self) -> c_ast.ParamList:
1576 # C23 permits a variadic function with no preceding named parameters.
1577 ell_tok = self._accept("ELLIPSIS")
1578 if ell_tok:
1579 coord = self._tok_coord(ell_tok)
1580 if self._dialect != "c23":
1581 self._parse_error("Ellipsis-only parameter lists require C23", coord)
1582 return c_ast.ParamList([c_ast.EllipsisParam(coord)], coord)
1584 params = self._parse_parameter_list()
1585 if self._peek_type() == "COMMA" and self._peek_type(2) == "ELLIPSIS":
1586 self._advance()
1587 ell_tok = self._advance()
1588 params.params.append(c_ast.EllipsisParam(self._tok_coord(ell_tok)))
1589 return params
1591 # BNF: parameter_list : parameter_declaration (',' parameter_declaration)*
1592 def _parse_parameter_list(self) -> c_ast.ParamList:
1593 first = self._parse_parameter_declaration()
1594 params = c_ast.ParamList([first], first.coord)
1595 while self._peek_type() == "COMMA" and self._peek_type(2) != "ELLIPSIS":
1596 self._advance()
1597 params.params.append(self._parse_parameter_declaration())
1598 return params
1600 # BNF: parameter_declaration : declaration_specifiers declarator?
1601 # | declaration_specifiers abstract_declarator_opt
1602 def _parse_parameter_declaration(self) -> c_ast.Node:
1603 attributes = self._parse_attribute_specifier_sequence()
1604 decl = self._parse_parameter_declaration_body()
1605 assert isinstance(decl, (c_ast.Decl, c_ast.Typename))
1606 decl.attributes = attributes
1607 return decl
1609 def _parse_parameter_declaration_body(self) -> c_ast.Node:
1610 spec, _, spec_coord = self._parse_declaration_specifiers(allow_no_type=True)
1612 if self._is_inferred_auto(spec):
1613 self._parse_error("Cannot infer the type of a parameter", spec_coord)
1615 if "typedef" in spec["storage"]:
1616 self._parse_error("Invalid storage class for a parameter", spec_coord)
1618 if not spec["type"]:
1619 spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)]
1621 if self._starts_declarator():
1622 decl, is_named = self._parse_any_declarator(
1623 allow_abstract=True, typeid_paren_as_abstract=True
1624 )
1625 if is_named:
1626 return self._build_declarations(
1627 spec=spec,
1628 decls=[{"decl": decl}],
1629 )[0]
1630 return self._build_parameter_declaration(spec, decl, spec_coord)
1632 decl = self._parse_abstract_declarator_opt()
1633 return self._build_parameter_declaration(spec, decl, spec_coord)
1635 def _build_parameter_declaration(
1636 self, spec: "_DeclSpec", decl: c_ast.Node | None, spec_coord: Coord | None
1637 ) -> c_ast.Node:
1638 if (
1639 len(spec["type"]) > 1
1640 and isinstance(spec["type"][-1], c_ast.IdentifierType)
1641 and len(spec["type"][-1].names) == 1
1642 and self._is_type_in_scope(spec["type"][-1].names[0])
1643 ):
1644 return self._build_declarations(spec=spec, decls=[{"decl": decl}])[0]
1646 return self._build_type_name(spec, decl, spec_coord)
1648 # BNF: identifier_list_opt : identifier_list | empty
1649 def _parse_identifier_list_opt(self) -> c_ast.Node | None:
1650 if self._peek_type() == "RPAREN":
1651 return None
1652 return self._parse_identifier_list()
1654 # BNF: identifier_list : identifier (',' identifier)*
1655 def _parse_identifier_list(self) -> c_ast.Node:
1656 first = self._parse_identifier()
1657 params = c_ast.ParamList([first], first.coord)
1658 while self._accept("COMMA"):
1659 params.params.append(self._parse_identifier())
1660 return params
1662 # ------------------------------------------------------------------
1663 # Abstract declarators
1664 # ------------------------------------------------------------------
1665 # BNF: type_name : specifier_qualifier_list abstract_declarator_opt
1666 def _parse_type_name(self) -> c_ast.Typename:
1667 spec = self._parse_specifier_qualifier_list()
1668 decl = self._parse_abstract_declarator_opt()
1670 coord = None
1671 if decl is not None:
1672 coord = decl.coord
1673 elif spec["type"]:
1674 coord = spec["type"][0].coord
1676 return self._build_type_name(spec, decl, coord)
1678 # BNF: abstract_declarator_opt : pointer? direct_abstract_declarator?
1679 def _parse_abstract_declarator_opt(self) -> c_ast.Node | None:
1680 if self._peek_type() == "TIMES":
1681 ptr = self._parse_pointer()
1682 if self._starts_direct_abstract_declarator():
1683 decl = self._parse_direct_abstract_declarator()
1684 else:
1685 decl = c_ast.TypeDecl(None, None, None, None)
1686 assert ptr is not None
1687 return self._type_modify_decl(decl, ptr)
1689 if self._starts_direct_abstract_declarator():
1690 return self._parse_direct_abstract_declarator()
1692 return None
1694 # BNF: direct_abstract_declarator : '(' parameter_type_list_opt ')'
1695 # | '(' abstract_declarator ')'
1696 # | '[' ... ']'
1697 def _parse_direct_abstract_declarator(self) -> c_ast.Node:
1698 lparen_tok = self._accept("LPAREN")
1699 if lparen_tok:
1700 if self._starts_declaration() or self._peek_type() in {
1701 "RPAREN",
1702 "ELLIPSIS",
1703 }:
1704 params = self._parse_parameter_type_list_opt()
1705 self._expect("RPAREN")
1706 decl = c_ast.FuncDecl(
1707 args=params,
1708 type=c_ast.TypeDecl(None, None, None, None),
1709 coord=self._tok_coord(lparen_tok),
1710 attributes=self._parse_attribute_specifier_sequence(),
1711 )
1712 else:
1713 decl = self._parse_abstract_declarator_opt()
1714 self._expect("RPAREN")
1715 assert decl is not None
1716 elif self._peek_type() == "LBRACKET":
1717 decl = self._parse_abstract_array_base()
1718 else:
1719 self._parse_error("Invalid abstract declarator", self.clex.filename)
1721 return self._parse_decl_suffixes(decl)
1723 # BNF: parameter_type_list_opt : parameter_type_list | empty
1724 def _parse_parameter_type_list_opt(self) -> c_ast.ParamList | None:
1725 if self._peek_type() == "RPAREN":
1726 return None
1727 return self._parse_parameter_type_list()
1729 # BNF: abstract_array_base : '[' array_specifiers? assignment_expression? ']'
1730 def _parse_abstract_array_base(self) -> c_ast.Node:
1731 return self._parse_array_decl_common(
1732 base_type=c_ast.TypeDecl(None, None, None, None), coord=None
1733 )
1735 # ------------------------------------------------------------------
1736 # Statements
1737 # ------------------------------------------------------------------
1738 # BNF: statement : labeled_statement | compound_statement
1739 # | selection_statement | iteration_statement
1740 # | jump_statement | expression_statement
1741 # | pppragma_directive
1742 def _parse_statement(self) -> c_ast.Node:
1743 if self._peek_type() == "ATTRIBUTE_BEGIN":
1744 attributes = self._parse_attribute_specifier_sequence()
1745 if self._accept("SEMI"):
1746 return c_ast.AttributeDeclaration(attributes, attributes[0].coord)
1747 return self._attribute_statement(self._parse_statement(), attributes)
1748 tok_type = self._peek_type()
1749 match tok_type:
1750 case "CASE" | "DEFAULT":
1751 return self._parse_labeled_statement()
1752 case "ID" if self._peek_type(2) == "COLON":
1753 return self._parse_labeled_statement()
1754 case "LBRACE":
1755 return self._parse_compound_statement()
1756 case "IF" | "SWITCH":
1757 return self._parse_selection_statement()
1758 case "WHILE" | "DO" | "FOR":
1759 return self._parse_iteration_statement()
1760 case "GOTO" | "BREAK" | "CONTINUE" | "RETURN":
1761 return self._parse_jump_statement()
1762 case "PPPRAGMA" | "_PRAGMA":
1763 return self._parse_pppragma_directive()
1764 case _:
1765 return self._parse_expression_statement()
1767 # BNF: pragmacomp_or_statement : pppragma_directive* statement
1768 def _parse_pragmacomp_or_statement(self) -> c_ast.Node:
1769 if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:
1770 pragmas = self._parse_pppragma_directive_list()
1771 stmt = self._parse_statement()
1772 return c_ast.Compound(block_items=pragmas + [stmt], coord=pragmas[0].coord)
1773 return self._parse_statement()
1775 # BNF: block_item : declaration | statement
1776 def _parse_block_item(self) -> c_ast.Node | list[c_ast.Node]:
1777 if self._peek_type() == "ATTRIBUTE_BEGIN":
1778 attributes = self._parse_attribute_specifier_sequence()
1779 if self._accept("SEMI"):
1780 return c_ast.AttributeDeclaration(attributes, attributes[0].coord)
1781 if self._starts_declaration():
1782 return self._attribute_declarations(
1783 self._parse_declaration(), attributes
1784 )
1785 return self._attribute_statement(self._parse_statement(), attributes)
1786 if self._peek_type() == "_STATIC_ASSERT":
1787 return self._parse_static_assert()
1788 if self._starts_declaration():
1789 return self._parse_declaration()
1790 return self._parse_statement()
1792 # BNF: block_item_list : block_item+
1793 def _parse_block_item_list(self) -> list[c_ast.Node]:
1794 items: list[c_ast.Node] = []
1795 while self._peek_type() not in {"RBRACE", None}:
1796 item = self._parse_block_item()
1797 if isinstance(item, c_ast.Node):
1798 items.append(item)
1799 elif item != [None]:
1800 items.extend(item)
1801 return items
1803 # BNF: compound_statement : '{' block_item_list? '}'
1804 def _parse_compound_statement(self, *, new_scope: bool = True) -> c_ast.Node:
1805 lbrace_tok = self._expect("LBRACE")
1806 if new_scope:
1807 self._push_scope()
1808 block_items = None
1809 if not self._accept("RBRACE"):
1810 block_items = self._parse_block_item_list()
1811 self._expect("RBRACE")
1812 if new_scope:
1813 self._pop_scope()
1814 return c_ast.Compound(
1815 block_items=block_items, coord=self._tok_coord(lbrace_tok)
1816 )
1818 # BNF: labeled_statement : ID ':' statement
1819 # | CASE constant_expression ':' statement
1820 # | DEFAULT ':' statement
1821 def _parse_labeled_statement(self) -> c_ast.Node:
1822 tok_type = self._peek_type()
1823 match tok_type:
1824 case "ID":
1825 name_tok = self._advance()
1826 self._expect("COLON")
1827 if self._starts_statement():
1828 stmt = self._parse_pragmacomp_or_statement()
1829 else:
1830 stmt = c_ast.EmptyStatement(self._tok_coord(name_tok))
1831 return c_ast.Label(name_tok.value, stmt, self._tok_coord(name_tok))
1832 case "CASE":
1833 case_tok = self._advance()
1834 expr = self._parse_constant_expression()
1835 self._expect("COLON")
1836 if self._starts_statement():
1837 stmt = self._parse_pragmacomp_or_statement()
1838 else:
1839 stmt = c_ast.EmptyStatement(self._tok_coord(case_tok))
1840 return c_ast.Case(expr, [stmt], self._tok_coord(case_tok))
1841 case "DEFAULT":
1842 def_tok = self._advance()
1843 self._expect("COLON")
1844 if self._starts_statement():
1845 stmt = self._parse_pragmacomp_or_statement()
1846 else:
1847 stmt = c_ast.EmptyStatement(self._tok_coord(def_tok))
1848 return c_ast.Default([stmt], self._tok_coord(def_tok))
1849 case _:
1850 self._parse_error("Invalid labeled statement", self.clex.filename)
1852 # BNF: selection_statement : IF '(' expression ')' statement (ELSE statement)?
1853 # | SWITCH '(' expression ')' statement
1854 def _parse_selection_statement(self) -> c_ast.Node:
1855 tok = self._advance()
1856 match tok.type:
1857 case "IF":
1858 self._expect("LPAREN")
1859 cond = self._parse_expression()
1860 self._expect("RPAREN")
1861 then_stmt = self._parse_pragmacomp_or_statement()
1862 if self._accept("ELSE"):
1863 else_stmt = self._parse_pragmacomp_or_statement()
1864 return c_ast.If(cond, then_stmt, else_stmt, self._tok_coord(tok))
1865 return c_ast.If(cond, then_stmt, None, self._tok_coord(tok))
1866 case "SWITCH":
1867 self._expect("LPAREN")
1868 expr = self._parse_expression()
1869 self._expect("RPAREN")
1870 stmt = self._parse_pragmacomp_or_statement()
1871 return fix_switch_cases(c_ast.Switch(expr, stmt, self._tok_coord(tok)))
1872 case _:
1873 self._parse_error("Invalid selection statement", self._tok_coord(tok))
1875 # BNF: iteration_statement : WHILE '(' expression ')' statement
1876 # | DO statement WHILE '(' expression ')' ';'
1877 # | FOR '(' (declaration | expression_opt) ';'
1878 # expression_opt ';' expression_opt ')' statement
1879 def _parse_iteration_statement(self) -> c_ast.Node:
1880 tok = self._advance()
1881 match tok.type:
1882 case "WHILE":
1883 self._expect("LPAREN")
1884 cond = self._parse_expression()
1885 self._expect("RPAREN")
1886 stmt = self._parse_pragmacomp_or_statement()
1887 return c_ast.While(cond, stmt, self._tok_coord(tok))
1888 case "DO":
1889 stmt = self._parse_pragmacomp_or_statement()
1890 self._expect("WHILE")
1891 self._expect("LPAREN")
1892 cond = self._parse_expression()
1893 self._expect("RPAREN")
1894 self._expect("SEMI")
1895 return c_ast.DoWhile(cond, stmt, self._tok_coord(tok))
1896 case "FOR":
1897 self._expect("LPAREN")
1898 if self._peek_type() == "_STATIC_ASSERT" or self._starts_declaration():
1899 # Names declared here are scoped to the loop (C11 6.8.5p5)
1900 self._push_scope()
1901 decls = self._parse_declaration()
1902 init = c_ast.DeclList(decls, self._tok_coord(tok))
1903 cond = self._parse_expression_opt()
1904 self._expect("SEMI")
1905 next_expr = self._parse_expression_opt()
1906 self._expect("RPAREN")
1907 stmt = self._parse_pragmacomp_or_statement()
1908 self._pop_scope()
1909 return c_ast.For(init, cond, next_expr, stmt, self._tok_coord(tok))
1911 init = self._parse_expression_opt()
1912 self._expect("SEMI")
1913 cond = self._parse_expression_opt()
1914 self._expect("SEMI")
1915 next_expr = self._parse_expression_opt()
1916 self._expect("RPAREN")
1917 stmt = self._parse_pragmacomp_or_statement()
1918 return c_ast.For(init, cond, next_expr, stmt, self._tok_coord(tok))
1919 case _:
1920 self._parse_error("Invalid iteration statement", self._tok_coord(tok))
1922 # BNF: jump_statement : GOTO ID ';' | BREAK ';' | CONTINUE ';'
1923 # | RETURN expression? ';'
1924 def _parse_jump_statement(self) -> c_ast.Node:
1925 tok = self._advance()
1926 match tok.type:
1927 case "GOTO":
1928 name_tok = self._expect("ID")
1929 self._expect("SEMI")
1930 return c_ast.Goto(name_tok.value, self._tok_coord(tok))
1931 case "BREAK":
1932 self._expect("SEMI")
1933 return c_ast.Break(self._tok_coord(tok))
1934 case "CONTINUE":
1935 self._expect("SEMI")
1936 return c_ast.Continue(self._tok_coord(tok))
1937 case "RETURN":
1938 if self._accept("SEMI"):
1939 return c_ast.Return(None, self._tok_coord(tok))
1940 expr = self._parse_expression()
1941 self._expect("SEMI")
1942 return c_ast.Return(expr, self._tok_coord(tok))
1943 case _:
1944 self._parse_error("Invalid jump statement", self._tok_coord(tok))
1946 # BNF: expression_statement : expression_opt ';'
1947 def _parse_expression_statement(self) -> c_ast.Node:
1948 expr = self._parse_expression_opt()
1949 semi_tok = self._expect("SEMI")
1950 if expr is None:
1951 return c_ast.EmptyStatement(self._tok_coord(semi_tok))
1952 return expr
1954 # ------------------------------------------------------------------
1955 # Expressions
1956 # ------------------------------------------------------------------
1957 # BNF: expression_opt : expression | empty
1958 def _parse_expression_opt(self) -> c_ast.Node | None:
1959 if self._starts_expression():
1960 return self._parse_expression()
1961 return None
1963 # BNF: expression : assignment_expression (',' assignment_expression)*
1964 def _parse_expression(self) -> c_ast.Node:
1965 expr = self._parse_assignment_expression()
1966 if not self._accept("COMMA"):
1967 return expr
1968 exprs = [expr, self._parse_assignment_expression()]
1969 while self._accept("COMMA"):
1970 exprs.append(self._parse_assignment_expression())
1971 return c_ast.ExprList(exprs, expr.coord)
1973 # BNF: assignment_expression : conditional_expression
1974 # | unary_expression assignment_op assignment_expression
1975 def _parse_assignment_expression(self) -> c_ast.Node:
1976 expr = self._parse_conditional_expression()
1977 if self._is_assignment_op():
1978 op = self._advance().value
1979 rhs = self._parse_assignment_expression()
1980 return c_ast.Assignment(op, expr, rhs, expr.coord)
1981 return expr
1983 # BNF: conditional_expression : binary_expression
1984 # | binary_expression '?' expression ':' conditional_expression
1985 def _parse_conditional_expression(self) -> c_ast.Node:
1986 expr = self._parse_binary_expression()
1987 if self._accept("CONDOP"):
1988 iftrue = self._parse_expression()
1989 self._expect("COLON")
1990 iffalse = self._parse_conditional_expression()
1991 return c_ast.TernaryOp(expr, iftrue, iffalse, expr.coord)
1992 return expr
1994 # BNF: binary_expression : cast_expression (binary_op cast_expression)*
1995 def _parse_binary_expression(
1996 self, min_prec: int = 0, lhs: c_ast.Node | None = None
1997 ) -> c_ast.Node:
1998 if lhs is None:
1999 lhs = self._parse_cast_expression()
2001 while True:
2002 tok = self._peek()
2003 if tok is None or tok.type not in _BINARY_PRECEDENCE:
2004 break
2005 prec = _BINARY_PRECEDENCE[tok.type]
2006 if prec < min_prec:
2007 break
2009 op = tok.value
2010 self._advance()
2011 rhs = self._parse_cast_expression()
2013 while True:
2014 next_tok = self._peek()
2015 if next_tok is None or next_tok.type not in _BINARY_PRECEDENCE:
2016 break
2017 next_prec = _BINARY_PRECEDENCE[next_tok.type]
2018 if next_prec > prec:
2019 rhs = self._parse_binary_expression(next_prec, rhs)
2020 else:
2021 break
2023 lhs = c_ast.BinaryOp(op, lhs, rhs, lhs.coord)
2025 return lhs
2027 # BNF: cast_expression : '(' type_name ')' cast_expression
2028 # | unary_expression
2029 def _parse_cast_expression(self) -> c_ast.Node:
2030 result = self._try_parse_paren_type_name()
2031 if result is not None:
2032 typ, mark, lparen_tok, _ = result
2033 if self._peek_type() == "LBRACE":
2034 # (type){...} is a compound literal, not a cast. Examples:
2035 # (int){1} -> compound literal, handled in postfix
2036 # (int) x -> cast, handled below
2037 self._reset(mark)
2038 else:
2039 expr = self._parse_cast_expression()
2040 return c_ast.Cast(typ, expr, self._tok_coord(lparen_tok))
2041 return self._parse_unary_expression()
2043 # BNF: unary_expression : postfix_expression
2044 # | '++' unary_expression
2045 # | '--' unary_expression
2046 # | unary_op cast_expression
2047 # | 'sizeof' unary_expression
2048 # | 'sizeof' '(' type_name ')'
2049 # | '_Alignof' '(' type_name ')'
2050 def _parse_unary_expression(self) -> c_ast.Node:
2051 tok_type = self._peek_type()
2052 if tok_type in {"PLUSPLUS", "MINUSMINUS"}:
2053 tok = self._advance()
2054 expr = self._parse_unary_expression()
2055 return c_ast.UnaryOp(tok.value, expr, expr.coord)
2057 if tok_type in {"AND", "TIMES", "PLUS", "MINUS", "NOT", "LNOT"}:
2058 tok = self._advance()
2059 expr = self._parse_cast_expression()
2060 return c_ast.UnaryOp(tok.value, expr, expr.coord)
2062 if tok_type == "SIZEOF":
2063 tok = self._advance()
2064 result = self._try_parse_paren_type_name()
2065 if result is not None:
2066 typ, mark, _, _ = result
2067 if self._peek_type() != "LBRACE":
2068 return c_ast.UnaryOp(tok.value, typ, self._tok_coord(tok))
2069 # sizeof (type){...} has an expression operand, including
2070 # any postfix operators following the compound literal.
2071 self._reset(mark)
2072 expr = self._parse_unary_expression()
2073 return c_ast.UnaryOp(tok.value, expr, self._tok_coord(tok))
2075 if tok_type == "_ALIGNOF":
2076 tok = self._advance()
2077 self._expect("LPAREN")
2078 typ = self._parse_type_name()
2079 self._expect("RPAREN")
2080 return c_ast.UnaryOp(tok.value, typ, self._tok_coord(tok))
2082 return self._parse_postfix_expression()
2084 # BNF: postfix_expression : primary_expression postfix_suffix*
2085 # | compound_literal postfix_suffix*
2086 # compound_literal : '(' storage_class_specifiers? type_name ')'
2087 # braced_initializer
2088 def _parse_postfix_expression(self) -> c_ast.Node:
2089 expr = None
2090 result = self._try_parse_paren_type_name()
2091 if result is not None:
2092 typ, mark, lparen_tok, storage = result
2093 # Disambiguate between casts and compound literals:
2094 # (int) x -> cast
2095 # (int) {1} -> compound literal
2096 if self._peek_type() == "LBRACE":
2097 init = self._parse_initializer()
2098 expr = c_ast.CompoundLiteral(
2099 typ, init, storage, self._tok_coord(lparen_tok)
2100 )
2101 else:
2102 self._reset(mark)
2104 if expr is None:
2105 expr = self._parse_primary_expression()
2106 while True:
2107 tok_type = self._peek_type()
2108 if tok_type == "LBRACKET":
2109 self._advance()
2110 sub = self._parse_expression()
2111 self._expect("RBRACKET")
2112 expr = c_ast.ArrayRef(expr, sub, expr.coord)
2113 continue
2114 if tok_type == "LPAREN":
2115 self._advance()
2116 if self._peek_type() == "RPAREN":
2117 self._advance()
2118 args = None
2119 else:
2120 args = self._parse_argument_expression_list()
2121 self._expect("RPAREN")
2122 expr = c_ast.FuncCall(expr, args, expr.coord)
2123 continue
2124 if tok_type in {"PERIOD", "ARROW"}:
2125 op_tok = self._advance()
2126 name_tok = self._advance()
2127 if name_tok.type not in {"ID", "TYPEID"}:
2128 self._parse_error(
2129 "Invalid struct reference", self._tok_coord(name_tok)
2130 )
2131 field = c_ast.ID(name_tok.value, self._tok_coord(name_tok))
2132 expr = c_ast.StructRef(expr, op_tok.value, field, expr.coord)
2133 continue
2134 if tok_type in {"PLUSPLUS", "MINUSMINUS"}:
2135 tok = self._advance()
2136 expr = c_ast.UnaryOp("p" + tok.value, expr, expr.coord)
2137 continue
2138 break
2139 return expr
2141 # BNF: primary_expression : ID | constant | string_literal
2142 # | '(' expression ')' | generic_selection | offsetof
2143 # | '(' compound_statement ')'
2144 # | '__builtin_va_arg' '('
2145 # assignment_expression ',' type_name ')'
2146 def _parse_primary_expression(self) -> c_ast.Node:
2147 tok_type = self._peek_type()
2148 if tok_type == "ID":
2149 return self._parse_identifier()
2150 if (
2151 tok_type in _INT_CONST
2152 or tok_type in _FLOAT_CONST
2153 or tok_type in _CHAR_CONST
2154 or tok_type in _PREDEFINED_CONST
2155 ):
2156 return self._parse_constant()
2157 if tok_type in _STRING_LITERAL:
2158 return self._parse_unified_string_literal()
2159 if tok_type == "LPAREN":
2160 self._advance()
2161 if self._peek_type() == "LBRACE":
2162 # GNU statement expressions participate in all expression
2163 # operators, including postfix operators.
2164 expr = self._parse_compound_statement()
2165 else:
2166 expr = self._parse_expression()
2167 self._expect("RPAREN")
2168 return expr
2169 if tok_type == "_GENERIC":
2170 return self._parse_generic_selection()
2171 if tok_type == "__BUILTIN_VA_ARG":
2172 tok = self._advance()
2173 self._expect("LPAREN")
2174 expr = self._parse_assignment_expression()
2175 self._expect("COMMA")
2176 typ = self._parse_type_name()
2177 self._expect("RPAREN")
2178 coord = self._tok_coord(tok)
2179 return c_ast.FuncCall(
2180 c_ast.ID(tok.value, coord),
2181 c_ast.ExprList([expr, typ], coord),
2182 coord,
2183 )
2184 if tok_type == "OFFSETOF":
2185 off_tok = self._advance()
2186 self._expect("LPAREN")
2187 typ = self._parse_type_name()
2188 self._expect("COMMA")
2189 designator = self._parse_offsetof_member_designator()
2190 self._expect("RPAREN")
2191 coord = self._tok_coord(off_tok)
2192 return c_ast.FuncCall(
2193 c_ast.ID(off_tok.value, coord),
2194 c_ast.ExprList([typ, designator], coord),
2195 coord,
2196 )
2198 self._parse_error("Invalid expression", self.clex.filename)
2200 # BNF: generic_selection : _GENERIC '(' assignment_expression ','
2201 # generic_association (',' generic_association)* ')'
2202 def _parse_generic_selection(self) -> c_ast.GenericSelection:
2203 tok = self._expect("_GENERIC")
2204 self._expect("LPAREN")
2205 expr = self._parse_assignment_expression()
2206 self._expect("COMMA")
2207 associations = [self._parse_generic_association()]
2208 while self._accept("COMMA"):
2209 associations.append(self._parse_generic_association())
2210 self._expect("RPAREN")
2211 return c_ast.GenericSelection(expr, associations, self._tok_coord(tok))
2213 # BNF: generic_association : type_name ':' assignment_expression
2214 # | DEFAULT ':' assignment_expression
2215 def _parse_generic_association(self) -> c_ast.GenericAssociation:
2216 tok = self._peek()
2217 if tok is None:
2218 self._parse_error("At end of input", self.clex.filename)
2219 typ = None if self._accept("DEFAULT") else self._parse_type_name()
2220 self._expect("COLON")
2221 expr = self._parse_assignment_expression()
2222 return c_ast.GenericAssociation(typ, expr, self._tok_coord(tok))
2224 # BNF: offsetof_member_designator : identifier_or_typeid
2225 # ('.' identifier_or_typeid | '[' expression ']')*
2226 def _parse_offsetof_member_designator(self) -> c_ast.Node:
2227 node = self._parse_identifier_or_typeid()
2228 while True:
2229 if self._accept("PERIOD"):
2230 field = self._parse_identifier_or_typeid()
2231 node = c_ast.StructRef(node, ".", field, node.coord)
2232 continue
2233 if self._accept("LBRACKET"):
2234 expr = self._parse_expression()
2235 self._expect("RBRACKET")
2236 node = c_ast.ArrayRef(node, expr, node.coord)
2237 continue
2238 break
2239 return node
2241 # BNF: argument_expression_list : assignment_expression (',' assignment_expression)*
2242 def _parse_argument_expression_list(self) -> c_ast.Node:
2243 expr = self._parse_assignment_expression()
2244 exprs = [expr]
2245 while self._accept("COMMA"):
2246 exprs.append(self._parse_assignment_expression())
2247 return c_ast.ExprList(exprs, expr.coord)
2249 # BNF: constant_expression : conditional_expression
2250 def _parse_constant_expression(self) -> c_ast.Node:
2251 return self._parse_conditional_expression()
2253 # ------------------------------------------------------------------
2254 # Terminals
2255 # ------------------------------------------------------------------
2256 # BNF: identifier : ID
2257 def _parse_identifier(self) -> c_ast.Node:
2258 tok = self._expect("ID")
2259 return c_ast.ID(tok.value, self._tok_coord(tok))
2261 # BNF: identifier_or_typeid : ID | TYPEID
2262 def _parse_identifier_or_typeid(self) -> c_ast.ID:
2263 tok = self._advance()
2264 if tok.type not in {"ID", "TYPEID"}:
2265 self._parse_error("Expected identifier", self._tok_coord(tok))
2266 return c_ast.ID(tok.value, self._tok_coord(tok))
2268 # BNF: constant : INT_CONST | FLOAT_CONST | CHAR_CONST | predefined_constant
2269 def _parse_constant(self) -> c_ast.Node:
2270 tok = self._advance()
2271 if tok.type in _PREDEFINED_CONST:
2272 return c_ast.Constant(
2273 _PREDEFINED_CONST[tok.type], tok.value, self._tok_coord(tok)
2274 )
2276 if tok.type in _INT_CONST:
2277 # Integer suffixes are at most three characters (e.g. ull, wbu).
2278 suffix = tok.value[-3:].lower()
2279 if suffix.endswith(("wb", "wbu")):
2280 if self._dialect != "c23":
2281 self._parse_error(
2282 "Bit-precise integer constants require C23",
2283 self._tok_coord(tok),
2284 )
2285 # Classify by suffix, as with other integer constants; retain
2286 # the spelling without computing the value or its bit width.
2287 t = "unsigned _BitInt" if "u" in suffix else "_BitInt"
2288 return c_ast.Constant(t, tok.value, self._tok_coord(tok))
2290 u_count = 0
2291 l_count = 0
2292 for ch in tok.value[-3:]:
2293 if ch in ("l", "L"):
2294 l_count += 1
2295 elif ch in ("u", "U"):
2296 u_count += 1
2297 if u_count > 1:
2298 raise ValueError("Constant cannot have more than one u/U suffix.")
2299 if l_count > 2:
2300 raise ValueError("Constant cannot have more than two l/L suffix.")
2301 prefix = "unsigned " * u_count + "long " * l_count
2302 return c_ast.Constant(prefix + "int", tok.value, self._tok_coord(tok))
2304 if tok.type in _FLOAT_CONST:
2305 if tok.value[-2:] in {"df", "DF"}:
2306 t = "_Decimal32"
2307 elif tok.value[-2:] in {"dd", "DD"}:
2308 t = "_Decimal64"
2309 elif tok.value[-2:] in {"dl", "DL"}:
2310 t = "_Decimal128"
2311 elif tok.value[-1] in ("f", "F"):
2312 t = "float"
2313 elif tok.value[-1] in ("l", "L"):
2314 t = "long double"
2315 else:
2316 t = "double"
2317 if (
2318 t in {"_Decimal32", "_Decimal64", "_Decimal128"}
2319 and self._dialect != "c23"
2320 ):
2321 self._parse_error(
2322 "Decimal floating constants require C23", self._tok_coord(tok)
2323 )
2324 return c_ast.Constant(t, tok.value, self._tok_coord(tok))
2326 if tok.type in _CHAR_CONST:
2327 return c_ast.Constant("char", tok.value, self._tok_coord(tok))
2329 self._parse_error("Invalid constant", self._tok_coord(tok))
2331 # BNF: unified_string_literal : (STRING_LITERAL | WSTRING_LITERAL
2332 # | U8STRING_LITERAL | U16STRING_LITERAL
2333 # | U32STRING_LITERAL)+
2334 def _parse_unified_string_literal(self) -> c_ast.Constant:
2335 # Represent the whole run of adjacent literals with one Constant.
2336 # C interprets escapes before concatenating the character sequences,
2337 # whereas Constant.value holds source spelling. Simply removing the
2338 # quotes between "\1" "23" would produce "\123", changing three
2339 # characters into one (issue #392).
2340 tok = self._advance()
2341 if tok.type not in _STRING_LITERAL:
2342 self._parse_error("Invalid string literal", self._tok_coord(tok))
2343 # Split off the optional encoding prefix and remove the closing quote.
2344 # Each part holds unquoted source text that can safely share one literal.
2345 prefix, contents = tok.value.split('"', 1)
2346 parts = [contents[:-1]]
2347 while self._peek_type() in _STRING_LITERAL:
2348 tok2 = self._advance()
2349 next_prefix, next_contents = tok2.value.split('"', 1)
2350 # The first nonempty prefix applies to the whole run, even if it
2351 # occurs after ordinary literals. Conflicting prefixes retain the
2352 # parser's existing permissive behavior: the first one wins.
2353 prefix = prefix or next_prefix
2354 next_contents = next_contents[:-1]
2355 # Extend the current part when safe; otherwise start a new one so
2356 # a quote boundary survives. Empty literals can merge, but do not
2357 # end a trailing escape: "\1" "" "23" still needs a boundary.
2358 if self._string_literal_needs_separator(parts[-1], next_contents):
2359 parts.append(next_contents)
2360 else:
2361 parts[-1] += next_contents
2362 # Re-quote each part, keeping only the necessary literal boundaries.
2363 # The prefix on the first literal applies to the adjacent sequence.
2364 # The generator can emit this value directly without decoding escapes.
2365 value = prefix + '"' + '" "'.join(parts) + '"'
2366 return c_ast.Constant("string", value, self._tok_coord(tok))
2368 @staticmethod
2369 def _string_literal_needs_separator(left: str, right: str) -> bool:
2370 """Whether joining two literal bodies would change their C spelling."""
2371 if not left or not right:
2372 return False
2374 # A generated trigraph would be replaced before C string processing;
2375 # for example, merging "?" "?=" would create the trigraph for '#'.
2376 boundary = left[-2:] + right[:2]
2377 if any("??" + ch in boundary for ch in "=/'()!<>-"):
2378 return True
2380 # Only an escape reaching the end of left can absorb characters from
2381 # right. The last backslash is the only possible start of that escape.
2382 escape_start = left.rfind("\\")
2383 if escape_start == -1:
2384 return False
2385 # An even run of backslashes represents escaped backslashes, not the
2386 # start of the numeric escape that the following digits might suggest.
2387 run_start = escape_start
2388 while run_start > 0 and left[run_start - 1] == "\\":
2389 run_start -= 1
2390 if (escape_start - run_start + 1) % 2 == 0:
2391 return False
2393 escape = left[escape_start + 1 :]
2394 if escape.startswith("x"):
2395 # Hex escapes have no digit limit: "\x1" "a" must stay separated.
2396 hex_digits = "0123456789abcdefABCDEF"
2397 return right[0] in hex_digits and all(ch in hex_digits for ch in escape[1:])
2398 # Octal escapes consume at most three digits: "\12" "3" needs a
2399 # boundary, but "\123" "4" can become "\1234". Universal character names
2400 # have fixed widths and cannot absorb characters from the next literal.
2401 return (
2402 1 <= len(escape) < 3
2403 and all(ch in "01234567" for ch in escape)
2404 and right[0] in "01234567"
2405 )
2407 # ------------------------------------------------------------------
2408 # Initializers
2409 # ------------------------------------------------------------------
2410 # BNF: initializer : assignment_expression
2411 # | '{' initializer_list ','? '}'
2412 # | '{' '}'
2413 def _parse_initializer(self) -> c_ast.Node:
2414 lbrace_tok = self._accept("LBRACE")
2415 if lbrace_tok:
2416 if self._accept("RBRACE"):
2417 return c_ast.InitList([], self._tok_coord(lbrace_tok))
2418 init_list = self._parse_initializer_list()
2419 self._accept("COMMA")
2420 self._expect("RBRACE")
2421 return init_list
2423 return self._parse_assignment_expression()
2425 # BNF: initializer_list : initializer_item (',' initializer_item)* ','?
2426 def _parse_initializer_list(self) -> c_ast.Node:
2427 items = [self._parse_initializer_item()]
2428 while self._accept("COMMA"):
2429 if self._peek_type() == "RBRACE":
2430 break
2431 items.append(self._parse_initializer_item())
2432 return c_ast.InitList(items, items[0].coord)
2434 # BNF: initializer_item : designation? initializer
2435 def _parse_initializer_item(self) -> c_ast.Node:
2436 designation = None
2437 if self._peek_type() in {"LBRACKET", "PERIOD"}:
2438 designation = self._parse_designation()
2439 init = self._parse_initializer()
2440 if designation is not None:
2441 return c_ast.NamedInitializer(designation, init)
2442 return init
2444 # BNF: designation : designator_list '='
2445 def _parse_designation(self) -> list[c_ast.Node]:
2446 designators = self._parse_designator_list()
2447 self._expect("EQUALS")
2448 return designators
2450 # BNF: designator_list : designator+
2451 def _parse_designator_list(self) -> list[c_ast.Node]:
2452 designators = []
2453 while self._peek_type() in {"LBRACKET", "PERIOD"}:
2454 designators.append(self._parse_designator())
2455 return designators
2457 # BNF: designator : '[' constant_expression ']'
2458 # | '.' identifier_or_typeid
2459 def _parse_designator(self) -> c_ast.Node:
2460 if tok := self._accept("LBRACKET"):
2461 expr = self._parse_constant_expression()
2462 self._expect("RBRACKET")
2463 return c_ast.ArrayDesignator(expr, self._tok_coord(tok))
2464 if self._accept("PERIOD"):
2465 return self._parse_identifier_or_typeid()
2466 self._parse_error("Invalid designator", self.clex.filename)
2468 # ------------------------------------------------------------------
2469 # Preprocessor-like directives
2470 # ------------------------------------------------------------------
2471 # BNF: pp_directive : '#' ... (unsupported)
2472 def _parse_pp_directive(self) -> NoReturn:
2473 tok = self._expect("PPHASH")
2474 self._parse_error("Directives not supported yet", self._tok_coord(tok))
2476 # BNF: pppragma_directive : PPPRAGMA PPPRAGMASTR?
2477 # | _PRAGMA '(' string_literal ')'
2478 def _parse_pppragma_directive(self) -> c_ast.Node:
2479 if self._peek_type() == "PPPRAGMA":
2480 tok = self._advance()
2481 if self._peek_type() == "PPPRAGMASTR":
2482 str_tok = self._advance()
2483 return c_ast.Pragma(str_tok.value, self._tok_coord(str_tok))
2484 return c_ast.Pragma("", self._tok_coord(tok))
2486 if self._peek_type() == "_PRAGMA":
2487 tok = self._advance()
2488 lparen = self._expect("LPAREN")
2489 literal = self._parse_unified_string_literal()
2490 self._expect("RPAREN")
2491 return c_ast.Pragma(literal, self._tok_coord(lparen))
2493 self._parse_error("Invalid pragma", self.clex.filename)
2495 # BNF: pppragma_directive_list : pppragma_directive+
2496 def _parse_pppragma_directive_list(self) -> list[c_ast.Node]:
2497 pragmas = []
2498 while self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:
2499 pragmas.append(self._parse_pppragma_directive())
2500 return pragmas
2502 # BNF: static_assert : _STATIC_ASSERT '(' constant_expression
2503 # (',' unified_string_literal)? ')' ';'
2504 # C11 requires a message; omission is supported as an extension.
2505 def _parse_static_assert(self) -> c_ast.StaticAssert:
2506 tok = self._expect("_STATIC_ASSERT")
2507 self._expect("LPAREN")
2508 cond = self._parse_constant_expression()
2509 msg = None
2510 if self._accept("COMMA"):
2511 msg = self._parse_unified_string_literal()
2512 self._expect("RPAREN")
2513 self._expect("SEMI")
2514 return c_ast.StaticAssert(cond, msg, self._tok_coord(tok))
2517_ASSIGNMENT_OPS = {
2518 "EQUALS",
2519 "XOREQUAL",
2520 "TIMESEQUAL",
2521 "DIVEQUAL",
2522 "MODEQUAL",
2523 "PLUSEQUAL",
2524 "MINUSEQUAL",
2525 "LSHIFTEQUAL",
2526 "RSHIFTEQUAL",
2527 "ANDEQUAL",
2528 "OREQUAL",
2529}
2531# Precedence of operators (lower number = weather binding)
2532# If this changes, c_generator.CGenerator.precedence_map needs to change as
2533# well
2534_BINARY_PRECEDENCE = {
2535 "LOR": 0,
2536 "LAND": 1,
2537 "OR": 2,
2538 "XOR": 3,
2539 "AND": 4,
2540 "EQ": 5,
2541 "NE": 5,
2542 "GT": 6,
2543 "GE": 6,
2544 "LT": 6,
2545 "LE": 6,
2546 "RSHIFT": 7,
2547 "LSHIFT": 7,
2548 "PLUS": 8,
2549 "MINUS": 8,
2550 "TIMES": 9,
2551 "DIVIDE": 9,
2552 "MOD": 9,
2553}
2555_STORAGE_CLASS = {
2556 "AUTO",
2557 "CONSTEXPR",
2558 "REGISTER",
2559 "STATIC",
2560 "EXTERN",
2561 "TYPEDEF",
2562 "_THREAD_LOCAL",
2563}
2565_FUNCTION_SPEC = {"INLINE", "_NORETURN"}
2567_TYPE_QUALIFIER = {"CONST", "RESTRICT", "VOLATILE", "_ATOMIC"}
2569_TYPEOF_SPEC = {"TYPEOF", "TYPEOF_UNQUAL"}
2571_TYPE_SPEC_SIMPLE = {
2572 "VOID",
2573 "_BOOL",
2574 "CHAR",
2575 "SHORT",
2576 "INT",
2577 "LONG",
2578 "FLOAT",
2579 "DOUBLE",
2580 "_DECIMAL32",
2581 "_DECIMAL64",
2582 "_DECIMAL128",
2583 "_COMPLEX",
2584 "SIGNED",
2585 "UNSIGNED",
2586 "__INT128",
2587}
2589# Struct members without declarators carry a type specifier directly instead.
2590_DECLARATOR_NODES = (c_ast.TypeDecl, c_ast.PtrDecl, c_ast.ArrayDecl, c_ast.FuncDecl)
2592_DECL_START = (
2593 _STORAGE_CLASS
2594 | _FUNCTION_SPEC
2595 | _TYPE_QUALIFIER
2596 | _TYPE_SPEC_SIMPLE
2597 | _TYPEOF_SPEC
2598 | {
2599 "TYPEID",
2600 "STRUCT",
2601 "UNION",
2602 "ENUM",
2603 "_ALIGNAS",
2604 "_ATOMIC",
2605 "_BITINT",
2606 "ATTRIBUTE_BEGIN",
2607 }
2608)
2610_EXPR_START = {
2611 "ID",
2612 "LPAREN",
2613 "PLUSPLUS",
2614 "MINUSMINUS",
2615 "PLUS",
2616 "MINUS",
2617 "TIMES",
2618 "AND",
2619 "NOT",
2620 "LNOT",
2621 "SIZEOF",
2622 "_ALIGNOF",
2623 "_GENERIC",
2624 "OFFSETOF",
2625 "__BUILTIN_VA_ARG",
2626}
2628_INT_CONST = {
2629 "INT_CONST_DEC",
2630 "INT_CONST_OCT",
2631 "INT_CONST_HEX",
2632 "INT_CONST_BIN",
2633 "INT_CONST_CHAR",
2634}
2636_FLOAT_CONST = {"FLOAT_CONST", "HEX_FLOAT_CONST"}
2638_PREDEFINED_CONST = {"BOOL_CONST": "bool", "NULLPTR": "nullptr_t"}
2640_CHAR_CONST = {
2641 "CHAR_CONST",
2642 "WCHAR_CONST",
2643 "U8CHAR_CONST",
2644 "U16CHAR_CONST",
2645 "U32CHAR_CONST",
2646}
2648_STRING_LITERAL = {
2649 "STRING_LITERAL",
2650 "WSTRING_LITERAL",
2651 "U8STRING_LITERAL",
2652 "U16STRING_LITERAL",
2653 "U32STRING_LITERAL",
2654}
2656_STARTS_EXPRESSION = (
2657 _EXPR_START
2658 | _INT_CONST
2659 | _FLOAT_CONST
2660 | _CHAR_CONST
2661 | _STRING_LITERAL
2662 | _PREDEFINED_CONST.keys()
2663)
2665_STARTS_STATEMENT = {
2666 "LBRACE",
2667 "IF",
2668 "SWITCH",
2669 "WHILE",
2670 "DO",
2671 "FOR",
2672 "GOTO",
2673 "BREAK",
2674 "CONTINUE",
2675 "RETURN",
2676 "CASE",
2677 "DEFAULT",
2678 "PPPRAGMA",
2679 "_PRAGMA",
2680 "SEMI",
2681}
2684class _TokenStream:
2685 """Provides buffered access to tokens supplied by a reader."""
2687 def __init__(self, read_token: Callable[[], Token | None]) -> None:
2688 self._read_token = read_token
2689 self._buffer: list[Token | None] = []
2690 self._index = 0
2692 def peek(self, k: int = 1) -> Token | None:
2693 """Peek at the k-th next token in the stream, without consuming it.
2695 Examples:
2696 k=1 returns the immediate next token.
2697 k=2 returns the token after that.
2698 """
2699 if k <= 0:
2700 return None
2701 pos = self._index + k - 1
2702 if pos >= len(self._buffer):
2703 self._fill(k)
2704 return self._buffer[pos]
2706 def next(self) -> Token | None:
2707 """Consume a single token and return it."""
2708 if self._index >= len(self._buffer):
2709 self._fill(1)
2710 tok = self._buffer[self._index]
2711 self._index += 1
2712 return tok
2714 # The 'mark' and 'reset' methods are useful for speculative parsing with
2715 # backtracking; when the parser needs to examine a sequence of tokens
2716 # and potentially decide to try a different path on the same sequence, it
2717 # can call 'mark' to obtain the current token position, and if the first
2718 # path fails restore the position with `reset(pos)`.
2719 def mark(self) -> int:
2720 return self._index
2722 def reset(self, mark: int) -> None:
2723 self._index = mark
2725 def _fill(self, n: int) -> None:
2726 while len(self._buffer) < self._index + n:
2727 tok = self._read_token()
2728 self._buffer.append(tok)
2729 if tok is None:
2730 break
2733# Declaration specifiers are represented by a dictionary with entries:
2734# - qual: a list of type qualifiers
2735# - storage: a list of storage class specifiers
2736# - type: a list of type specifiers
2737# - function: a list of function specifiers
2738# - alignment: a list of alignment specifiers
2739# - type_attributes: attribute groups following the specifier sequence
2740class _DeclSpec(TypedDict):
2741 qual: list[str]
2742 storage: list[str]
2743 type: list[c_ast.Node]
2744 function: list[str]
2745 alignment: list[c_ast.Alignas]
2746 type_attributes: list[c_ast.AttributeSpecifier]
2749_DeclSpecKind = Literal["qual", "storage", "type", "function", "alignment"]
2752class _DeclInfo(TypedDict):
2753 # Declarator payloads used by declaration/initializer parsing:
2754 # - decl: the declarator node (may be None for abstract/implicit cases)
2755 # - init: initializer expression; the key may be omitted when absent
2756 # - bitsize: bit-field width expression; the key may be omitted when absent
2757 decl: c_ast.Node | None
2758 init: NotRequired[c_ast.Node | None]
2759 bitsize: NotRequired[c_ast.Node | None]