codekingpro/portable-devtools
115k
1# ------------------------------------------------------------------------------2# pycparser: ast_transforms.py3#4# Some utilities used by the parser to create a friendlier AST.5#6# Eli Bendersky [https://eli.thegreenplace.net/]7# License: BSD8# ------------------------------------------------------------------------------9 10from typing import Any, List, Tuple, cast11 12from . import c_ast13 14 15def fix_switch_cases(switch_node: c_ast.Switch) -> c_ast.Switch:16 """The 'case' statements in a 'switch' come out of parsing with one17 child node, so subsequent statements are just tucked to the parent18 Compound. Additionally, consecutive (fall-through) case statements19 come out messy. This is a peculiarity of the C grammar. The following:20 21 switch (myvar) {22 case 10:23 k = 10;24 p = k + 1;25 return 10;26 case 20:27 case 30:28 return 20;29 default:30 break;31 }32 33 Creates this tree (pseudo-dump):34 35 Switch36 ID: myvar37 Compound:38 Case 10:39 k = 1040 p = k + 141 return 1042 Case 20:43 Case 30:44 return 2045 Default:46 break47 48 The goal of this transform is to fix this mess, turning it into the49 following:50 51 Switch52 ID: myvar53 Compound:54 Case 10:55 k = 1056 p = k + 157 return 1058 Case 20:59 Case 30:60 return 2061 Default:62 break63 64 A fixed AST node is returned. The argument may be modified.65 """66 assert isinstance(switch_node, c_ast.Switch)67 if not isinstance(switch_node.stmt, c_ast.Compound):68 return switch_node69 70 # The new Compound child for the Switch, which will collect children in the71 # correct order72 new_compound = c_ast.Compound([], switch_node.stmt.coord)73 74 # The last Case/Default node75 last_case: c_ast.Case | c_ast.Default | None = None76 77 # Goes over the children of the Compound below the Switch, adding them78 # either directly below new_compound or below the last Case as appropriate79 # (for `switch(cond) {}`, block_items would have been None)80 for child in switch_node.stmt.block_items or []:81 if isinstance(child, (c_ast.Case, c_ast.Default)):82 # If it's a Case/Default:83 # 1. Add it to the Compound and mark as "last case"84 # 2. If its immediate child is also a Case or Default, promote it85 # to a sibling.86 new_compound.block_items.append(child)87 _extract_nested_case(child, new_compound.block_items)88 last_case = new_compound.block_items[-1]89 else:90 # Other statements are added as children to the last case, if it91 # exists.92 if last_case is None:93 new_compound.block_items.append(child)94 else:95 last_case.stmts.append(child)96 97 switch_node.stmt = new_compound98 return switch_node99 100 101def _extract_nested_case(102 case_node: c_ast.Case | c_ast.Default, stmts_list: List[c_ast.Node]103) -> None:104 """Recursively extract consecutive Case statements that are made nested105 by the parser and add them to the stmts_list.106 """107 if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):108 nested = case_node.stmts.pop()109 stmts_list.append(nested)110 _extract_nested_case(cast(Any, nested), stmts_list)111 112 113def fix_atomic_specifiers(114 decl: c_ast.Decl | c_ast.Typedef,115) -> c_ast.Decl | c_ast.Typedef:116 """Atomic specifiers like _Atomic(type) are unusually structured,117 conferring a qualifier upon the contained type.118 119 This function fixes a decl with atomic specifiers to have a sane AST120 structure, by removing spurious Typename->TypeDecl pairs and attaching121 the _Atomic qualifier in the right place.122 """123 # There can be multiple levels of _Atomic in a decl; fix them until a124 # fixed point is reached.125 while True:126 decl, found = _fix_atomic_specifiers_once(decl)127 if not found:128 break129 130 # Make sure to add an _Atomic qual on the topmost decl if needed. Also131 # restore the declname on the innermost TypeDecl (it gets placed in the132 # wrong place during construction).133 typ: Any = decl134 while not isinstance(typ, c_ast.TypeDecl):135 try:136 typ = typ.type137 except AttributeError:138 return decl139 if "_Atomic" in typ.quals and "_Atomic" not in decl.quals:140 decl.quals.append("_Atomic")141 if typ.declname is None:142 typ.declname = decl.name143 144 return decl145 146 147def _fix_atomic_specifiers_once(148 decl: c_ast.Decl | c_ast.Typedef,149) -> Tuple[c_ast.Decl | c_ast.Typedef, bool]:150 """Performs one 'fix' round of atomic specifiers.151 Returns (modified_decl, found) where found is True iff a fix was made.152 """153 parent: Any = decl154 grandparent: Any = None155 node: Any = decl.type156 while node is not None:157 if isinstance(node, c_ast.Typename) and "_Atomic" in node.quals:158 break159 try:160 grandparent = parent161 parent = node162 node = node.type163 except AttributeError:164 # If we've reached a node without a `type` field, it means we won't165 # find what we're looking for at this point; give up the search166 # and return the original decl unmodified.167 return decl, False168 169 assert isinstance(parent, c_ast.TypeDecl)170 assert grandparent is not None171 cast(Any, grandparent).type = node.type172 if "_Atomic" not in node.type.quals:173 node.type.quals.append("_Atomic")174 return decl, True175 