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 . import c_ast11 12 13def fix_switch_cases(switch_node):14 """ The 'case' statements in a 'switch' come out of parsing with one15 child node, so subsequent statements are just tucked to the parent16 Compound. Additionally, consecutive (fall-through) case statements17 come out messy. This is a peculiarity of the C grammar. The following:18 19 switch (myvar) {20 case 10:21 k = 10;22 p = k + 1;23 return 10;24 case 20:25 case 30:26 return 20;27 default:28 break;29 }30 31 Creates this tree (pseudo-dump):32 33 Switch34 ID: myvar35 Compound:36 Case 10:37 k = 1038 p = k + 139 return 1040 Case 20:41 Case 30:42 return 2043 Default:44 break45 46 The goal of this transform is to fix this mess, turning it into the47 following:48 49 Switch50 ID: myvar51 Compound:52 Case 10:53 k = 1054 p = k + 155 return 1056 Case 20:57 Case 30:58 return 2059 Default:60 break61 62 A fixed AST node is returned. The argument may be modified.63 """64 assert isinstance(switch_node, c_ast.Switch)65 if not isinstance(switch_node.stmt, c_ast.Compound):66 return switch_node67 68 # The new Compound child for the Switch, which will collect children in the69 # correct order70 new_compound = c_ast.Compound([], switch_node.stmt.coord)71 72 # The last Case/Default node73 last_case = None74 75 # Goes over the children of the Compound below the Switch, adding them76 # either directly below new_compound or below the last Case as appropriate77 # (for `switch(cond) {}`, block_items would have been None)78 for child in (switch_node.stmt.block_items or []):79 if isinstance(child, (c_ast.Case, c_ast.Default)):80 # If it's a Case/Default:81 # 1. Add it to the Compound and mark as "last case"82 # 2. If its immediate child is also a Case or Default, promote it83 # to a sibling.84 new_compound.block_items.append(child)85 _extract_nested_case(child, new_compound.block_items)86 last_case = new_compound.block_items[-1]87 else:88 # Other statements are added as children to the last case, if it89 # exists.90 if last_case is None:91 new_compound.block_items.append(child)92 else:93 last_case.stmts.append(child)94 95 switch_node.stmt = new_compound96 return switch_node97 98 99def _extract_nested_case(case_node, stmts_list):100 """ Recursively extract consecutive Case statements that are made nested101 by the parser and add them to the stmts_list.102 """103 if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):104 stmts_list.append(case_node.stmts.pop())105 _extract_nested_case(stmts_list[-1], stmts_list)106 107 108def fix_atomic_specifiers(decl):109 """ Atomic specifiers like _Atomic(type) are unusually structured,110 conferring a qualifier upon the contained type.111 112 This function fixes a decl with atomic specifiers to have a sane AST113 structure, by removing spurious Typename->TypeDecl pairs and attaching114 the _Atomic qualifier in the right place.115 """116 # There can be multiple levels of _Atomic in a decl; fix them until a117 # fixed point is reached.118 while True:119 decl, found = _fix_atomic_specifiers_once(decl)120 if not found:121 break122 123 # Make sure to add an _Atomic qual on the topmost decl if needed. Also124 # restore the declname on the innermost TypeDecl (it gets placed in the125 # wrong place during construction).126 typ = decl127 while not isinstance(typ, c_ast.TypeDecl):128 try:129 typ = typ.type130 except AttributeError:131 return decl132 if '_Atomic' in typ.quals and '_Atomic' not in decl.quals:133 decl.quals.append('_Atomic')134 if typ.declname is None:135 typ.declname = decl.name136 137 return decl138 139 140def _fix_atomic_specifiers_once(decl):141 """ Performs one 'fix' round of atomic specifiers.142 Returns (modified_decl, found) where found is True iff a fix was made.143 """144 parent = decl145 grandparent = None146 node = decl.type147 while node is not None:148 if isinstance(node, c_ast.Typename) and '_Atomic' in node.quals:149 break150 try:151 grandparent = parent152 parent = node153 node = node.type154 except AttributeError:155 # If we've reached a node without a `type` field, it means we won't156 # find what we're looking for at this point; give up the search157 # and return the original decl unmodified.158 return decl, False159 160 assert isinstance(parent, c_ast.TypeDecl)161 grandparent.type = node.type162 if '_Atomic' not in node.type.quals:163 node.type.quals.append('_Atomic')164 return decl, True165 