From 034a7f0583bf3489248b86ff4eea22b2e5f35ffa Mon Sep 17 00:00:00 2001 From: charles-antoine fournel Date: Thu, 12 Mar 2026 16:32:31 +0100 Subject: [PATCH 1/2] add mql5 support --- src/extraction/grammars.ts | 17 ++++-- src/extraction/tree-sitter.ts | 68 ++++++++++++++++++++-- src/extraction/wasm/tree-sitter-mql5.wasm | Bin 0 -> 23637 bytes src/types.ts | 1 + 4 files changed, 75 insertions(+), 11 deletions(-) create mode 100755 src/extraction/wasm/tree-sitter-mql5.wasm 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..358722f4 100644 --- a/src/extraction/tree-sitter.ts +++ b/src/extraction/tree-sitter.ts @@ -813,6 +813,64 @@ const EXTRACTORS: Partial> = { return node.type === 'declConst'; }, }, + mql5: { + functionTypes: ['declProc'], + classTypes: ['declClass'], + methodTypes: ['declProc'], + interfaceTypes: ['declIntf'], + structTypes: [], + enumTypes: ['declEnum'], + typeAliasTypes: ['declType'], + importTypes: ['declUses'], + callTypes: ['exprCall'], + variableTypes: ['declField', 'declConst'], + nameField: 'name', + bodyField: 'body', + paramsField: 'args', + returnField: 'type', + getSignature: (node, source) => { + const args = getChildByField(node, 'args'); + const returnType = node.namedChildren.find( + (c: SyntaxNode) => c.type === 'typeref' + ); + if (!args && !returnType) return undefined; + let sig = ''; + if (args) sig = getNodeText(args, source); + if (returnType) { + sig += ': ' + getNodeText(returnType, source); + } + return sig || undefined; + }, + getVisibility: (node) => { + let current = node.parent; + while (current) { + if (current.type === 'declSection') { + for (let i = 0; i < current.childCount; i++) { + const child = current.child(i); + if (child?.type === 'kPublic' || child?.type === 'kPublished') + return 'public'; + if (child?.type === 'kPrivate') return 'private'; + if (child?.type === 'kProtected') return 'protected'; + } + } + current = current.parent; + } + return undefined; + }, + isExported: (_node, _source) => { + // In Pascal, symbols declared in the interface section are exported + return false; + }, + isStatic: (node) => { + for (let i = 0; i < node.childCount; i++) { + if (node.child(i)?.type === 'kClass') return true; + } + return false; + }, + isConst: (node) => { + return node.type === 'declConst'; + }, + }, }; // TSX and JSX use the same extractors as their base languages @@ -1392,7 +1450,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 +1646,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 +1747,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 0000000000000000000000000000000000000000..14442f53a917267f715ea597bc6aea4df39a7f06 GIT binary patch literal 23637 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 literal 0 HcmV?d00001 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'; // ============================================================================= From 8b5423e13ed03d6f1f13af5ab87f2cc2219aa00b Mon Sep 17 00:00:00 2001 From: charles-antoine fournel Date: Thu, 12 Mar 2026 18:26:42 +0100 Subject: [PATCH 2/2] fix index issue --- src/extraction/tree-sitter.ts | 81 ++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/extraction/tree-sitter.ts b/src/extraction/tree-sitter.ts index 358722f4..7b6277e6 100644 --- a/src/extraction/tree-sitter.ts +++ b/src/extraction/tree-sitter.ts @@ -814,63 +814,64 @@ const EXTRACTORS: Partial> = { }, }, mql5: { - functionTypes: ['declProc'], - classTypes: ['declClass'], - methodTypes: ['declProc'], - interfaceTypes: ['declIntf'], - structTypes: [], - enumTypes: ['declEnum'], - typeAliasTypes: ['declType'], - importTypes: ['declUses'], - callTypes: ['exprCall'], - variableTypes: ['declField', 'declConst'], + 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: 'body', - paramsField: 'args', + bodyField: 'block', + paramsField: 'parameter', returnField: 'type', + getSignature: (node, source) => { - const args = getChildByField(node, 'args'); - const returnType = node.namedChildren.find( - (c: SyntaxNode) => c.type === 'typeref' - ); - if (!args && !returnType) return undefined; + const params = getChildByField(node, 'parameter'); + const returnType = getChildByField(node, 'type'); + let sig = ''; - if (args) sig = getNodeText(args, source); + + if (params) { + sig += getNodeText(params, source); + } + if (returnType) { - sig += ': ' + getNodeText(returnType, source); + sig = `${getNodeText(returnType, source)} ${sig}`; } + return sig || undefined; }, - getVisibility: (node) => { - let current = node.parent; - while (current) { - if (current.type === 'declSection') { - for (let i = 0; i < current.childCount; i++) { - const child = current.child(i); - if (child?.type === 'kPublic' || child?.type === 'kPublished') - return 'public'; - if (child?.type === 'kPrivate') return 'private'; - if (child?.type === 'kProtected') return 'protected'; - } - } - current = current.parent; - } - return undefined; + + getVisibility: (_node) => { + // MQL5 n'a pas de visibilité explicite + return 'public'; }, + isExported: (_node, _source) => { - // In Pascal, symbols declared in the interface section are exported - return false; + // Tout est visible dans MQL5 + return true; }, + isStatic: (node) => { for (let i = 0; i < node.childCount; i++) { - if (node.child(i)?.type === 'kClass') return true; + const child = node.child(i); + if (child?.type === 'static') return true; } return false; }, + isConst: (node) => { - return node.type === 'declConst'; - }, - }, + 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