You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ast_transforms.py 3.5KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  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 child in switch_node.stmt.block_items:
  66. if isinstance(child, (c_ast.Case, c_ast.Default)):
  67. # If it's a Case/Default:
  68. # 1. Add it to the Compound and mark as "last case"
  69. # 2. If its immediate child is also a Case or Default, promote it
  70. # to a sibling.
  71. new_compound.block_items.append(child)
  72. _extract_nested_case(child, new_compound.block_items)
  73. last_case = new_compound.block_items[-1]
  74. else:
  75. # Other statements are added as children to the last case, if it
  76. # exists.
  77. if last_case is None:
  78. new_compound.block_items.append(child)
  79. else:
  80. last_case.stmts.append(child)
  81. switch_node.stmt = new_compound
  82. return switch_node
  83. def _extract_nested_case(case_node, stmts_list):
  84. """ Recursively extract consecutive Case statements that are made nested
  85. by the parser and add them to the stmts_list.
  86. """
  87. if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):
  88. stmts_list.append(case_node.stmts.pop())
  89. _extract_nested_case(stmts_list[-1], stmts_list)