old intents
This commit is contained in:
+131
@@ -0,0 +1,131 @@
|
||||
import json
|
||||
import tree_sitter
|
||||
from tree_sitter import Language, Parser
|
||||
import tree_sitter_python as tspython
|
||||
import tree_sitter_javascript as tsjavascript
|
||||
import tree_sitter_typescript as tstypescript
|
||||
import tree_sitter_html as tshtml
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
class CodeAnalyzer:
|
||||
"""Анализирует код с помощью tree-sitter и извлекает структуру."""
|
||||
LANGUAGES = {
|
||||
'.py': Language(tspython.language()),
|
||||
'.js': Language(tsjavascript.language()),
|
||||
'.ts': Language(tstypescript.language_typescript()),
|
||||
'.tsx': Language(tstypescript.language_tsx()),
|
||||
'.html': Language(tshtml.language()),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.parsers = {}
|
||||
for ext, language in self.LANGUAGES.items():
|
||||
# Язык теперь передаётся сразу при создании парсера
|
||||
self.parsers[ext] = Parser(language)
|
||||
|
||||
def analyze_file(self, content: str, extension: str) -> Dict[str, Any]:
|
||||
"""Анализирует содержимое файла и возвращает структурированную информацию."""
|
||||
parser = self.parsers.get(extension)
|
||||
if not parser:
|
||||
# Для неподдерживаемых расширений возвращаем сырой текст (обрезанный до разумного)
|
||||
return {"raw": content[:1000] + "..." if len(content) > 1000 else content}
|
||||
|
||||
tree = parser.parse(bytes(content, 'utf-8'))
|
||||
root = tree.root_node
|
||||
|
||||
info = {
|
||||
"imports": [],
|
||||
"exports": [],
|
||||
"functions": [],
|
||||
"classes": [],
|
||||
"variables": [],
|
||||
"has_side_effects": False,
|
||||
"jsx_elements": [] if extension in ('.tsx', '.jsx') else None
|
||||
}
|
||||
|
||||
# Обходим все узлы
|
||||
self._traverse(root, info, extension)
|
||||
|
||||
return info
|
||||
|
||||
def _traverse(self, node, info, extension):
|
||||
"""Рекурсивно обходит AST и заполняет info."""
|
||||
if node.type == 'function_definition' or node.type == 'function_declaration':
|
||||
func_name = self._get_node_text(node.child_by_field_name('name'))
|
||||
if func_name:
|
||||
params = []
|
||||
params_node = node.child_by_field_name('parameters')
|
||||
if params_node:
|
||||
for p in params_node.children:
|
||||
if p.type == 'identifier' or p.type == 'formal_parameter':
|
||||
params.append(self._get_node_text(p))
|
||||
info['functions'].append({
|
||||
"name": func_name,
|
||||
"params": params
|
||||
})
|
||||
|
||||
elif node.type == 'class_definition' or node.type == 'class_declaration':
|
||||
class_name = self._get_node_text(node.child_by_field_name('name'))
|
||||
if class_name:
|
||||
methods = []
|
||||
body = node.child_by_field_name('body')
|
||||
if body:
|
||||
for child in body.children:
|
||||
if child.type == 'method_definition' or child.type == 'function_definition':
|
||||
mname = self._get_node_text(child.child_by_field_name('name'))
|
||||
if mname:
|
||||
methods.append(mname)
|
||||
info['classes'].append({
|
||||
"name": class_name,
|
||||
"methods": methods
|
||||
})
|
||||
|
||||
elif node.type == 'import_statement' or node.type == 'import_from_statement':
|
||||
# Для Python и JS/TS импорты выглядят по-разному, но мы упростим
|
||||
import_text = self._get_node_text(node)
|
||||
info['imports'].append(import_text.strip())
|
||||
|
||||
elif node.type == 'export_statement' or node.type == 'export_named_statement':
|
||||
export_text = self._get_node_text(node)
|
||||
info['exports'].append(export_text.strip())
|
||||
|
||||
elif node.type == 'assignment' or node.type == 'variable_declaration':
|
||||
# Попытка извлечь имена переменных верхнего уровня
|
||||
var_names = []
|
||||
if node.type == 'assignment':
|
||||
left = node.child_by_field_name('left')
|
||||
if left and left.type == 'identifier':
|
||||
var_names.append(self._get_node_text(left))
|
||||
elif node.type == 'variable_declaration':
|
||||
for child in node.children:
|
||||
if child.type == 'variable_declarator':
|
||||
name_node = child.child_by_field_name('name')
|
||||
if name_node:
|
||||
var_names.append(self._get_node_text(name_node))
|
||||
info['variables'].extend(var_names)
|
||||
|
||||
# Побочные эффекты: выражение верхнего уровня, не являющееся импортом/экспортом/декларацией
|
||||
if node.type == 'expression_statement':
|
||||
# Исключаем вызовы, которые являются частью модуля (например, вызов ReactDOM.render)
|
||||
# Считаем, что любое выражение верхнего уровня — потенциальный side-effect
|
||||
# Можно уточнить позже
|
||||
info['has_side_effects'] = True
|
||||
|
||||
if extension in ('.tsx', '.jsx') and node.type == 'jsx_element':
|
||||
# Извлекаем название компонента
|
||||
opening = node.child_by_field_name('opening_element')
|
||||
if opening:
|
||||
name_node = opening.child_by_field_name('name')
|
||||
if name_node:
|
||||
info['jsx_elements'].append(self._get_node_text(name_node))
|
||||
|
||||
# Рекурсивно обходим детей
|
||||
for child in node.children:
|
||||
self._traverse(child, info, extension)
|
||||
|
||||
@staticmethod
|
||||
def _get_node_text(node) -> str:
|
||||
if node is None:
|
||||
return ""
|
||||
return node.text.decode('utf-8')
|
||||
Reference in New Issue
Block a user