diff --git a/src/extraction/grammars.ts b/src/extraction/grammars.ts index d2e0de44..d8141d30 100644 --- a/src/extraction/grammars.ts +++ b/src/extraction/grammars.ts @@ -34,6 +34,7 @@ const WASM_GRAMMAR_FILES: Record = { kotlin: 'tree-sitter-kotlin.wasm', dart: 'tree-sitter-dart.wasm', pascal: 'tree-sitter-pascal.wasm', + mql5: 'tree-sitter-mql5.wasm' }; /** @@ -74,6 +75,9 @@ export const EXTENSION_MAP: Record = { '.lpr': 'pascal', '.dfm': 'pascal', '.fmx': 'pascal', + '.mq5': 'mql5', + '.mqh': 'mql5', + '.mql5': 'mql5', }; /** @@ -99,12 +103,12 @@ export async function initGrammars(): Promise { const entries = Object.entries(WASM_GRAMMAR_FILES) as [GrammarLanguage, string][]; for (const [lang, wasmFile] of entries) { try { - // Pascal ships its own WASM (not in tree-sitter-wasms) - const wasmPath = lang === 'pascal' - ? path.join(__dirname, 'wasm', wasmFile) - : require.resolve(`tree-sitter-wasms/out/${wasmFile}`); - const language = await WasmLanguage.load(wasmPath); - languageCache.set(lang, language); + // Pascal & MQL5 ships its own WASM (not in tree-sitter-wasms) + const wasmPath = (lang === 'pascal' || lang === 'mql5') + ? path.join(__dirname, 'wasm', wasmFile) + : require.resolve(`tree-sitter-wasms/out/${wasmFile}`); + const language = await WasmLanguage.load(wasmPath); + languageCache.set(lang, language); } catch (error) { const message = error instanceof Error ? error.message : String(error); console.warn(`[CodeGraph] Failed to load ${lang} grammar — parsing will be unavailable: ${message}`); @@ -214,6 +218,7 @@ export function getLanguageDisplayName(language: Language): string { svelte: 'Svelte', liquid: 'Liquid', pascal: 'Pascal / Delphi', + mql5: 'mql5', unknown: 'Unknown', }; return names[language] || language; diff --git a/src/extraction/tree-sitter.ts b/src/extraction/tree-sitter.ts index 4712bb46..7b6277e6 100644 --- a/src/extraction/tree-sitter.ts +++ b/src/extraction/tree-sitter.ts @@ -813,6 +813,65 @@ const EXTRACTORS: Partial> = { return node.type === 'declConst'; }, }, + mql5: { + functionTypes: ['function_definition'], + classTypes: ['class_definition'], + methodTypes: ['function_definition'], + interfaceTypes: [], + structTypes: ['struct_definition'], + enumTypes: ['enum_definition'], + typeAliasTypes: ['type'], + importTypes: ['preproc_include'], + callTypes: ['function_call'], + variableTypes: ['variable_declaration'], + nameField: 'name', + bodyField: 'block', + paramsField: 'parameter', + returnField: 'type', + + getSignature: (node, source) => { + const params = getChildByField(node, 'parameter'); + const returnType = getChildByField(node, 'type'); + + let sig = ''; + + if (params) { + sig += getNodeText(params, source); + } + + if (returnType) { + sig = `${getNodeText(returnType, source)} ${sig}`; + } + + return sig || undefined; + }, + + getVisibility: (_node) => { + // MQL5 n'a pas de visibilité explicite + return 'public'; + }, + + isExported: (_node, _source) => { + // Tout est visible dans MQL5 + return true; + }, + + isStatic: (node) => { + for (let i = 0; i < node.childCount; i++) { + const child = node.child(i); + if (child?.type === 'static') return true; + } + return false; + }, + + isConst: (node) => { + for (let i = 0; i < node.childCount; i++) { + const child = node.child(i); + if (child?.type === 'const') return true; + } + return false; + } + } }; // TSX and JSX use the same extractors as their base languages @@ -1392,7 +1451,7 @@ export class TreeSitterExtractor { // Extract variable declarators based on language if (this.language === 'typescript' || this.language === 'javascript' || - this.language === 'tsx' || this.language === 'jsx') { + this.language === 'tsx' || this.language === 'jsx') { // Handle lexical_declaration and variable_declaration // These contain one or more variable_declarator children for (let i = 0; i < node.namedChildCount; i++) { @@ -1588,7 +1647,7 @@ export class TreeSitterExtractor { let moduleName = ''; if (this.language === 'typescript' || this.language === 'javascript' || - this.language === 'tsx' || this.language === 'jsx') { + this.language === 'tsx' || this.language === 'jsx') { const source = getChildByField(node, 'source'); if (source) { moduleName = getNodeText(source, this.source).replace(/['"]/g, ''); @@ -1689,9 +1748,9 @@ export class TreeSitterExtractor { if (!firstChild) return getNodeText(scopedNode, this.source); if (firstChild.type === 'identifier' || - firstChild.type === 'crate' || - firstChild.type === 'super' || - firstChild.type === 'self') { + firstChild.type === 'crate' || + firstChild.type === 'super' || + firstChild.type === 'self') { return getNodeText(firstChild, this.source); } else if (firstChild.type === 'scoped_identifier') { return getRootModule(firstChild); diff --git a/src/extraction/wasm/tree-sitter-mql5.wasm b/src/extraction/wasm/tree-sitter-mql5.wasm new file mode 100755 index 00000000..14442f53 Binary files /dev/null and b/src/extraction/wasm/tree-sitter-mql5.wasm differ diff --git a/src/types.ts b/src/types.ts index fe64faa4..ec896446 100644 --- a/src/types.ts +++ b/src/types.ts @@ -75,6 +75,7 @@ export type Language = | 'svelte' | 'liquid' | 'pascal' + | 'mql5' | 'unknown'; // =============================================================================