ast_transforms.py 5.6 KB

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