Skip to content

Commit

Permalink
Merge pull request #354 from codefori/worksofliam/issue353
Browse files Browse the repository at this point in the history
Enhance comment handling and validation in parser
  • Loading branch information
worksofliam authored Dec 17, 2024
2 parents 63b8ab9 + 41f2031 commit 421fca5
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 10 deletions.
66 changes: 56 additions & 10 deletions language/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Declaration from "./models/declaration";

import oneLineTriggers from "./models/oneLineTriggers";
import { parseFLine, parseCLine, parsePLine, parseDLine, getPrettyType, prettyTypeFromToken } from "./models/fixed";
import path from "path";
import { Token } from "./types";
import { Keywords } from "./parserTypes";

Expand Down Expand Up @@ -357,20 +356,67 @@ export default class Parser {
return directIfScope.length === 0 || directIfScope.every(scope => scope.condition);
}

/**
* Removes the trailing line comment (//) but keeps the semi-colon if found.
*/
const stripComment = (inputLine: string) => {
const comment = inputLine.indexOf(`//`);
const quote = inputLine.lastIndexOf(`'`);
if (comment >= 0 && comment < quote) {
return inputLine;
let comment = -1;
let inString = false;
for (let i = inputLine.length - 1; i >= 0; i--) {
switch (inputLine[i]) {
case '/':
if (inputLine[i-1] === `/`) {
// It's a comment!
inString = false;
comment = i-1;
i--;

return inputLine.substring(0, comment).trimEnd();
}
break;
case `'`:
inString = !inString;
break;
case ';':
if (!inString) {
return inputLine.substring(0, i+1).trimEnd();
}
break;
}
}

return (comment >= 0 ? inputLine.substring(0, comment).trimEnd() : inputLine);
return inputLine;
}

/**
* Removes the trailing comment and optionally removes the semi-colon.
*/
const getValidStatement = (inputLine: string, withSep?: boolean) => {
const comment = inputLine.indexOf(`//`);
const quote = inputLine.lastIndexOf(`'`);
const sep = inputLine.indexOf(`;`, quote >= 0 ? quote : 0);
if (!inputLine.includes(`;`)) return inputLine;
let comment = -1;
let inString = false;
let sep = -1;
for (let i = inputLine.length - 1; i >= 0; i--) {
switch (inputLine[i]) {
case '/':
if (inputLine[i-1] === `/`) {
// It's a comment!
inString = false;
comment = i-1;
i--;
}
break;
case `'`:
inString = !inString;
break;
case ';':
if (!inString) {
sep = i;
break;
}
break;
}
}

if (comment >= 0 && comment < sep) {
return inputLine;
Expand Down
23 changes: 23 additions & 0 deletions tests/suite/basics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1297,4 +1297,27 @@ test(`const keyword check`, async () => {
const hello = cache.find(`hello`);
expect(hello.name).toBe(`hello`);
expect(hello.keyword[`CONST`]).toBe(`556`);
});

test('issue_353_comments', async () => {
const lines = [
`**free`,
`dcl-ds HEDINF based(p1@);`,
` HRLEN Int(10:0); // Record length`,
` HCRRN Int(10:0); // Cursor's RRN`,
` HCPOS Int(10:0); // Cursor's column`,
` HCCSID Int(10:0); // CCSID of source`,
` HRECI Int(10:0); // Nbr of input rcds`,
`end-ds;`,
`dcl-s p2@ Pointer;`,
].join(`\n`);

const cache = await parser.getDocs(uri, lines, { ignoreCache: true, withIncludes: false });

const hedinf = cache.find(`HEDINF`);
expect(hedinf).toBeDefined();
console.log(hedinf.subItems.map(s => s.name));
expect(hedinf.subItems.length).toBe(5);
const p2at = cache.find(`p2@`);
expect(p2at).toBeDefined();
});
83 changes: 83 additions & 0 deletions tests/suite/linter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3186,4 +3186,87 @@ test('Linter running on member rpgleinc', async () => {
type: 'SpecificCasing',
newValue: 'DCL-S'
});
});

test('issue_353_indent_1', async () => {
const lines = [
`**free`,
`dcl-ds HEDINF based(p1@);`,
` HRLEN Int(10:0); // Record length`,
` HCRRN Int(10:0); // Cursor's RRN`,
` HCPOS Int(10:0); // Cursor's column`,
` HCCSID Int(10:0); // CCSID of source`,
` HRECI Int(10:0); // Nbr of input rcds`,
`end-ds;`,
`dcl-s p2@ Pointer;`,
].join(`\n`);

const cache = await parser.getDocs(uri, lines, { ignoreCache: true, withIncludes: false });

const hedinf = cache.find(`HEDINF`);
expect(hedinf).toBeDefined();
expect(hedinf.subItems.length).toBe(5);
const p2at = cache.find(`p2@`);
expect(p2at).toBeDefined();

const { indentErrors, errors } = Linter.getErrors({ uri, content: lines }, {
indent: 2
}, cache);

expect(errors.length).toBe(0);
expect(indentErrors.length).toBe(0);
});

test('issue_353_indent_2', async () => {
const lines = [
`**free`,
`dcl-ds HEDINF based(p1@);`,
` HRLEN Int(10:0); // Record length`,
` HCRRN Int(10:0); // Cursor's RRN`,
` HCPOS Int(10:0); // Cursor's column`,
` HCCSID Int(10:0); // CCSID of source`,
` HRECI Int(10:0); // Nbr of input rcds`,
`end-ds;`,
`dcl-s p2@ Pointer;`,
].join(`\n`);

const cache = await parser.getDocs(uri, lines, { ignoreCache: true, withIncludes: false });

const hedinf = cache.find(`HEDINF`);
expect(hedinf).toBeDefined();
expect(hedinf.subItems.length).toBe(5);
const p2at = cache.find(`p2@`);
expect(p2at).toBeDefined();

const { indentErrors, errors } = Linter.getErrors({ uri, content: lines }, {
indent: 2
}, cache);

expect(indentErrors.length).toBe(1);
expect(indentErrors[0]).toMatchObject({
line: 3,
expectedIndent: 2,
currentIndent: 3
});
});

test('issue_353_indent_3', async () => {
const lines = [
`**free`,
`begsr displayHelp;`,
` // Do something with this program's`,
` // name and library ... assume the program is running`,
` // from the same library as contains the source.`,
` fileName = 'QRPGLESRC';`,
` library = pgSts.lib;`,
`endsr;`,
].join(`\n`);

const cache = await parser.getDocs(uri, lines, { ignoreCache: true, withIncludes: false });
const { indentErrors, errors } = Linter.getErrors({ uri, content: lines }, {
indent: 2
}, cache);

expect(errors.length).toBe(0);
expect(indentErrors.length).toBe(0);
});

0 comments on commit 421fca5

Please sign in to comment.