ast_transforms.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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)