From ce7c49607ae258974d91ea50fad2ec253a771a05 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Thu, 12 Dec 2024 13:44:33 -0500 Subject: [PATCH 1/2] Support for program parameters in free-format Signed-off-by: worksofliam --- language/parser.ts | 52 ++++++++++++++++++++++--------------- tests/suite/partial.test.ts | 10 ++++--- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/language/parser.ts b/language/parser.ts index 7fbc0d7..198c0dd 100644 --- a/language/parser.ts +++ b/language/parser.ts @@ -28,6 +28,8 @@ const lineTokens = (input: string, lineNumber: number, lineIndex: number): Token return tokens; } +const PROGRAMPARMS_NAME = `PROGRAMPARMS`; + export default class Parser { parsedCache: {[thePath: string]: Cache} = {}; tables: TableDetail = {}; @@ -951,40 +953,48 @@ export default class Parser { case `DCL-PI`: //Procedures can only exist in the global scope. - if (currentProcName) { - if (parts.length > 0) { + if (parts.length > 0) { + if (currentProcName) { currentGroup = `procedures`; currentItem = scopes[0].procedures.find(proc => proc.name === currentProcName); + } else { + currentItem = new Declaration(`struct`); + currentItem.name = PROGRAMPARMS_NAME; + } + if (currentItem) { const endInline = tokens.findIndex(part => part.value.toUpperCase() === `END-PI`); - if (currentItem) { - - // Indicates that the PI starts and ends on the same line - if (endInline >= 0) { - tokens.splice(endInline, 1); - currentItem.readParms = false; - resetDefinition = true; - } - - currentItem.keyword = { - ...currentItem.keyword, - ...Parser.expandKeywords(tokens.slice(2)) - } - currentItem.readParms = true; + // Indicates that the PI starts and ends on the same line + if (endInline >= 0) { + tokens.splice(endInline, 1); + currentItem.readParms = false; + resetDefinition = true; + } - currentDescription = []; + currentItem.keyword = { + ...currentItem.keyword, + ...Parser.expandKeywords(tokens.slice(2)) } + currentItem.readParms = true; + + currentDescription = []; } } break; case `END-PI`: //Procedures can only exist in the global scope. - currentItem = scopes[0].procedures.find(proc => proc.name === currentProcName); + if (currentProcName) { + currentItem = scopes[0].procedures.find(proc => proc.name === currentProcName); - if (currentItem && currentItem.type === `procedure`) { - currentItem.readParms = false; + if (currentItem && currentItem.type === `procedure`) { + currentItem.readParms = false; + resetDefinition = true; + } + } else if (currentItem && currentItem.name === PROGRAMPARMS_NAME) { + // Assign this scopes parameters to the subitems of the program parameters struct + scopes[0].parameters = currentItem.subItems; resetDefinition = true; } break; @@ -1202,7 +1212,7 @@ export default class Parser { currentItem.subItems.push(currentSub); currentSub = undefined; - if (currentItem.type === `struct`) { + if (currentItem.type === `struct` && currentItem.name !== PROGRAMPARMS_NAME) { resetDefinition = true; } } diff --git a/tests/suite/partial.test.ts b/tests/suite/partial.test.ts index 0941f81..b9347c3 100644 --- a/tests/suite/partial.test.ts +++ b/tests/suite/partial.test.ts @@ -17,6 +17,8 @@ test("Parser partial tests", { timeout }, async () => { const parser = setupParser(projectPath); const list = await getSourcesList(projectPath); + totalFiles += list.length; + for (let i = 0; i < list.length; i++) { const relativePath = list[i]; const basename = path.basename(relativePath); @@ -51,13 +53,13 @@ test("Parser partial tests", { timeout }, async () => { lengths.push(pe - ps); } - const lengthsAverage = lengths.reduce((a, b) => a + b, 0) / lengths.length; - const total = lengths.reduce((a, b) => a + b, 0); - const last = lengths[lengths.length - 1]; + // const lengthsAverage = lengths.reduce((a, b) => a + b, 0) / lengths.length; + // const total = lengths.reduce((a, b) => a + b, 0); + // const last = lengths[lengths.length - 1]; // console.log(`\tAverage: ${lengthsAverage}ms, Full: ${last}ms, Total: ${total}`); // console.log(``); } } - console.log(`Parsed ${totalFiles} files, ${SPLIT_SIZE} each.`); + console.log(`Parsed ${totalFiles} files, ${SPLIT_SIZE} times each.`); }); \ No newline at end of file From e8bd6ce8b1482d4c6a4bec67765b5986d71d1ece Mon Sep 17 00:00:00 2001 From: worksofliam Date: Thu, 12 Dec 2024 13:44:39 -0500 Subject: [PATCH 2/2] Add tests for reference collection in free-format parameters Signed-off-by: worksofliam --- tests/suite/references.test.ts | 53 +++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/suite/references.test.ts b/tests/suite/references.test.ts index cdc1c96..3eadb38 100644 --- a/tests/suite/references.test.ts +++ b/tests/suite/references.test.ts @@ -381,10 +381,14 @@ test("references_9", async () => { const cache = await parser.getDocs(uri, lines, {ignoreCache: true, withIncludes: true, collectReferences: true}); const procedure = cache.find(`InputIsValid`); - const validationResult = procedure.scope.find(`validationResult`); + const validationResult = procedure.scope.find(`validationResult`); expect(validationResult.references.length).toEqual(7); expect(validationResult.references.every(ref => lines.substring(ref.offset.start, ref.offset.end) === `validationResult`)).toBe(true); + + const comp = procedure.scope.find(`comp`); + expect(comp.references.length).toEqual(2); + expect(comp.references.every(ref => lines.substring(ref.offset.start, ref.offset.end) === `comp`)).toBe(true); }); test('references_10', async () => { @@ -1833,4 +1837,51 @@ test('references_27_fixed_reference', async () => { expect(offsetContent.toUpperCase()).toBe(`WCFGKEY`); } +}); + +test('reference_28_parameters', async () => { + const lines = [ + `**free`, + ``, + `ctl-opt dftactgrp(*no);`, + ``, + `dcl-pi upddept;`, + ` deptno char(3);`, + ` deptname char(36);`, + ` mgrno char(6);`, + ` admrdept char(3);`, + ` location char(16);`, + `end-pi;`, + ``, + `dcl-ds result qualified dim(1);`, + ` success char(1);`, + `end-ds;`, + ``, + `exec sql`, + ` update dept`, + ` set deptname = :deptname,`, + ` mgrno = :mgrno,`, + ` admrdept = :admrdept,`, + ` location = :location`, + ` where deptno = :deptno;`, + ``, + `if (SQLCOD = 0);`, + ` result(1).success = 'Y';`, + `else;`, + ` result(1).success = 'N';`, + `endif;`, + ``, + `dcl-s return char(length) inz('Y');`, + ``, + `exec sql set result sets array :result for 1 rows;`, + `// Hello`, + `return;`, + ].join(`\n`); + + const cache = await parser.getDocs(uri, lines, { ignoreCache: true, withIncludes: true, collectReferences: true }); + + const deptno = cache.find(`deptno`); + expect(deptno).toBeDefined(); + expect(deptno.references.length).toBe(2); + expect(deptno.references.every(ref => lines.substring(ref.offset.start, ref.offset.end) === `deptno`)).toBe(true); }); \ No newline at end of file