import Parser from 'tree-sitter';
import Python from 'tree-sitter-python';
import { BaseParser } from './base-parser.js';
import { CodeElement } from '../types/index.js';
export class PythonParser extends BaseParser {
constructor() {
super('python');
}
setLanguage(): void {
this.parser.setLanguage(Python);
}
extractElements(tree: Parser.Tree, sourceCode: string, filePath: string): CodeElement[] {
const elements: CodeElement[] = [];
const cursor = tree.walk();
const visit = (node: Parser.SyntaxNode) => {
const element = this.nodeToElement(node, sourceCode, filePath);
if (element) {
elements.push(element);
}
for (const child of node.children) {
visit(child);
}
};
visit(cursor.currentNode);
return elements;
}
private nodeToElement(node: Parser.SyntaxNode, sourceCode: string, filePath: string): CodeElement | null {
const typeMap: Record<string, CodeElement['type']> = {
'function_definition': 'function',
'class_definition': 'class'
};
const elementType = typeMap[node.type];
if (!elementType) return null;
const nameNode = node.childForFieldName('name');
if (!nameNode) return null;
const name = this.nodeToString(nameNode, sourceCode);
const { has, doc } = this.hasDocumentation(node, sourceCode);
return {
type: elementType,
name,
line: node.startPosition.row + 1,
column: node.startPosition.column,
hasDocumentation: has,
documentation: doc,
signature: this.extractSignature(node, sourceCode),
filePath
};
}
hasDocumentation(node: Parser.SyntaxNode, sourceCode: string): { has: boolean; doc?: string } {
// Python uses docstrings as first statement in function/class body
const body = node.childForFieldName('body');
if (!body) return { has: false };
const firstChild = body.children.find(c => c.type !== 'comment');
if (firstChild && firstChild.type === 'expression_statement') {
const string = firstChild.child(0);
if (string && string.type === 'string') {
const docstring = this.nodeToString(string, sourceCode);
const cleaned = docstring.replace(/^['"]|['"]$/g, '').trim();
return { has: true, doc: cleaned };
}
}
return { has: false };
}
private extractSignature(node: Parser.SyntaxNode, sourceCode: string): string {
const defLine = this.nodeToString(node, sourceCode).split('\n')[0];
return defLine.trim();
}
}