A semantic search engine for source code https://bitshift.benkurtovic.com/
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.
 
 
 
 
 
 

177 lines
5.1 KiB

  1. import ast
  2. class _TreeCutter(ast.NodeVisitor):
  3. """
  4. Local node visitor for python abstract syntax trees.
  5. :ivar accum: (dict) Information on variables, functions, and classes
  6. accumulated from an abstract syntax tree.
  7. :ivar cache: (dict or None) Information stored about parent nodes. Added
  8. to accum when node reaches the lowest possible level.
  9. .. todo::
  10. Add visit funciton for ast.Name to record all uses of a variable.
  11. Use self.cache to store extra information about nodes.
  12. """
  13. def __init__(self):
  14. """
  15. Create a _TreeCutter instance.
  16. """
  17. self.accum = {'vars': {}, 'functions': {}, 'classes': {}}
  18. self.cache = None
  19. def start_n_end(self, node):
  20. """
  21. Helper function to get the start and end lines of an AST node.
  22. :param node: The node.
  23. :type node: ast.FunctionDef or ast.ClassDef or ast.Module
  24. """
  25. start_line, start_col = node.lineno, node.col_offset
  26. temp_node = node
  27. while 'body' in temp_node.__dict__:
  28. temp_node = temp_node.body[-1]
  29. end_line, end_col = temp_node.lineno, temp_node.col_offset
  30. return (start_line, start_col, end_line, end_col)
  31. def visit_Assign(self, node):
  32. """
  33. Visits Assign nodes in a tree. Adds relevant data about them to accum.
  34. :param node: The current node.
  35. :type node: ast.Assign
  36. .. todo::
  37. Add value and type metadata to accum.
  38. """
  39. for t in node.targets:
  40. if isinstance(t, ast.Tuple):
  41. for n in t.elts:
  42. line, col = n.lineno, n.col_offset
  43. if not self.accum['vars'].has_key(node.name):
  44. self.accum['vars'][node.name] = {'declaration': {}, 'uses': []}
  45. pos = {'coord': {}}
  46. pos['coord']['start_line'] = line
  47. pos['coord']['start_col'] = col
  48. pos['coord']['end_line'] = line
  49. pos['coord']['end_col'] = col
  50. self.accum['vars'][n.id]['declaration'] = pos
  51. else:
  52. line, col = t.lineno, t.col_offset
  53. pos = {'coord': {}}
  54. pos['coord']['start_line'] = line
  55. pos['coord']['start_col'] = col
  56. pos['coord']['end_line'] = line
  57. pos['coord']['end_col'] = col
  58. self.accum['vars'][t.id]['declaration'] = pos
  59. self.generic_visit(node)
  60. def visit_FunctionDef(self, node):
  61. """
  62. Visits FunctionDef nodes in a tree. Adds relevant data about them to accum.
  63. :param node: The current node.
  64. :type node: ast.FunctionDef
  65. .. todo::
  66. Add arguments and decorators metadata to accum.
  67. """
  68. start_line, start_col, end_line, end_col = self.start_n_end(node)
  69. if not self.accum['functions'].has_key(node.name):
  70. self.accum['functions'][node.name] = {'declaration': {}, 'calls': []}
  71. pos = {'coord': {}}
  72. pos['coord']['start_ln']= start_line
  73. pos['coord']['start_col'] = start_col
  74. pos['coord']['end_ln'] = end_line
  75. pos['coord']['end_col'] = end_col
  76. self.accum['functions'][node.name]['declaration'] = pos
  77. self.generic_visit(node)
  78. def visit_Call(self, node):
  79. """
  80. Visits Function Call nodes in a tree. Adds relevant data about them
  81. in the functions section for accum.
  82. :param node: The current node.
  83. :type node: ast.Call
  84. .. todo::
  85. Add arguments and decorators metadata to accum.
  86. """
  87. line, col = node.line_no, node.col_offset
  88. if not self.accum['functions'].has_key(node.name):
  89. self.accum['functions'][node.name] = {'declaration': {}, 'calls': []}
  90. pos = {'coord': {}}
  91. pos['coord']['start_line'] = line
  92. pos['coord']['start_col'] = col
  93. pos['coord']['end_line'] = line
  94. pos['coord']['end_col'] = col
  95. self.accum['functions'][node.name]['calls'].append(pos)
  96. self.generic_visit(node)
  97. def visit_ClassDef(self, node):
  98. """
  99. Visits ClassDef nodes in a tree. Adds relevant data about them to accum.
  100. :param node: The current node.
  101. :type node: ast.ClassDef
  102. .. todo::
  103. Add arguments, inherits, and decorators metadata to accum.
  104. """
  105. start_line, start_col, end_line, end_col = self.start_n_end(node)
  106. pos = {'coord': {}}
  107. pos['coord']['start_ln']= start_line
  108. pos['coord']['start_col'] = start_col
  109. pos['coord']['end_ln'] = end_line
  110. pos['coord']['end_col'] = end_col
  111. self.accum['classes'][node.name] = pos
  112. self.generic_visit(node)
  113. def visit_Name(self, node):
  114. pass
  115. def parse_py(codelet):
  116. """
  117. Adds 'symbols' field to the codelet after parsing the python code.
  118. :param codelet: The codelet object to parsed.
  119. :type code: Codelet
  120. """
  121. tree = ast.parse(codelet.code)
  122. cutter = _TreeCutter()
  123. cutter.visit(tree)
  124. codelet.symbols = cutter.accum