ast_transforms.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # ------------------------------------------------------------------------------
  2. # pycparser: ast_transforms.py
  3. #
  4. # Some utilities used by the parser to create a friendlier AST.
  5. #
  6. # Eli Bendersky [https://eli.thegreenplace.net/]
  7. # License: BSD
  8. # ------------------------------------------------------------------------------
  9. from typing import Any, List, Tuple, cast
  10. from . import c_ast
  11. def fix_switch_cases(switch_node: c_ast.Switch) -> c_ast.Switch:
  12. """The 'case' statements in a 'switch' come out of parsing with one
  13. child node, so subsequent statements are just tucked to the parent
  14. Compound. Additionally, consecutive (fall-through) case statements
  15. come out messy. This is a peculiarity of the C grammar. The following:
  16. switch (myvar) {
  17. case 10:
  18. k = 10;
  19. p = k + 1;
  20. return 10;
  21. case 20:
  22. case 30:
  23. return 20;
  24. default:
  25. break;
  26. }
  27. Creates this tree (pseudo-dump):
  28. Switch
  29. ID: myvar
  30. Compound:
  31. Case 10:
  32. k = 10
  33. p = k + 1
  34. return 10
  35. Case 20:
  36. Case 30:
  37. return 20
  38. Default:
  39. break
  40. The goal of this transform is to fix this mess, turning it into the
  41. following:
  42. Switch
  43. ID: myvar
  44. Compound:
  45. Case 10:
  46. k = 10
  47. p = k + 1
  48. return 10
  49. Case 20:
  50. Case 30:
  51. return 20
  52. Default:
  53. break
  54. A fixed AST node is returned. The argument may be modified.
  55. """
  56. assert isinstance(switch_node, c_ast.Switch)
  57. if not isinstance(switch_node.stmt, c_ast.Compound):
  58. return switch_node
  59. # The new Compound child for the Switch, which will collect children in the
  60. # correct order
  61. new_compound = c_ast.Compound([], switch_node.stmt.coord)
  62. # The last Case/Default node
  63. last_case: c_ast.Case | c_ast.Default | None = None
  64. # Goes over the children of the Compound below the Switch, adding them
  65. # either directly below new_compound or below the last Case as appropriate
  66. # (for `switch(cond) {}`, block_items would have been None)
  67. for child in switch_node.stmt.block_items or []:
  68. if isinstance(child, (c_ast.Case, c_ast.Default)):
  69. # If it's a Case/Default:
  70. # 1. Add it to the Compound and mark as "last case"
  71. # 2. If its immediate child is also a Case or Default, promote it
  72. # to a sibling.
  73. new_compound.block_items.append(child)
  74. _extract_nested_case(child, new_compound.block_items)
  75. last_case = new_compound.block_items[-1]
  76. else:
  77. # Other statements are added as children to the last case, if it
  78. # exists.
  79. if last_case is None:
  80. new_compound.block_items.append(child)
  81. else:
  82. last_case.stmts.append(child)
  83. switch_node.stmt = new_compound
  84. return switch_node
  85. def _extract_nested_case(
  86. case_node: c_ast.Case | c_ast.Default, stmts_list: List[c_ast.Node]
  87. ) -> None:
  88. """Recursively extract consecutive Case statements that are made nested
  89. by the parser and add them to the stmts_list.
  90. """
  91. if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):
  92. nested = case_node.stmts.pop()
  93. stmts_list.append(nested)
  94. _extract_nested_case(cast(Any, nested), stmts_list)
  95. def fix_atomic_specifiers(
  96. decl: c_ast.Decl | c_ast.Typedef,
  97. ) -> c_ast.Decl | c_ast.Typedef:
  98. """Atomic specifiers like _Atomic(type) are unusually structured,
  99. conferring a qualifier upon the contained type.
  100. This function fixes a decl with atomic specifiers to have a sane AST
  101. structure, by removing spurious Typename->TypeDecl pairs and attaching
  102. the _Atomic qualifier in the right place.
  103. """
  104. # There can be multiple levels of _Atomic in a decl; fix them until a
  105. # fixed point is reached.
  106. while True:
  107. decl, found = _fix_atomic_specifiers_once(decl)
  108. if not found:
  109. break
  110. # Make sure to add an _Atomic qual on the topmost decl if needed. Also
  111. # restore the declname on the innermost TypeDecl (it gets placed in the
  112. # wrong place during construction).
  113. typ: Any = decl
  114. while not isinstance(typ, c_ast.TypeDecl):
  115. try:
  116. typ = typ.type
  117. except AttributeError:
  118. return decl
  119. if "_Atomic" in typ.quals and "_Atomic" not in decl.quals:
  120. decl.quals.append("_Atomic")
  121. if typ.declname is None:
  122. typ.declname = decl.name
  123. return decl
  124. def _fix_atomic_specifiers_once(
  125. decl: c_ast.Decl | c_ast.Typedef,
  126. ) -> Tuple[c_ast.Decl | c_ast.Typedef, bool]:
  127. """Performs one 'fix' round of atomic specifiers.
  128. Returns (modified_decl, found) where found is True iff a fix was made.
  129. """
  130. parent: Any = decl
  131. grandparent: Any = None
  132. node: Any = decl.type
  133. while node is not None:
  134. if isinstance(node, c_ast.Typename) and "_Atomic" in node.quals:
  135. break
  136. try:
  137. grandparent = parent
  138. parent = node
  139. node = node.type
  140. except AttributeError:
  141. # If we've reached a node without a `type` field, it means we won't
  142. # find what we're looking for at this point; give up the search
  143. # and return the original decl unmodified.
  144. return decl, False
  145. assert isinstance(parent, c_ast.TypeDecl)
  146. assert grandparent is not None
  147. cast(Any, grandparent).type = node.type
  148. if "_Atomic" not in node.type.quals:
  149. node.type.quals.append("_Atomic")
  150. return decl, True