Skip to content

Commit

Permalink
Merge pull request #224 from MeasureAuthoringTool/MAT-7995_updateErro…
Browse files Browse the repository at this point in the history
…rMsgWhenDefineLacksName

Release 1.0.9
  • Loading branch information
adongare authored Dec 17, 2024
2 parents 4324088 + b4fd7d9 commit ead748c
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 70 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Use Node.js 20.x
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20.x

- name: Cache node modules
uses: actions/cache@v3
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/unit_test_coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Use Node.js 20.x
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20.x

- name: Cache node modules
uses: actions/cache@v3
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
Expand All @@ -56,7 +56,7 @@ jobs:
run: npm run-script coverage

- name: Store the coverage report as an artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/lcov.info
Expand All @@ -67,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download coverage artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: coverage

Expand All @@ -83,14 +83,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Download coverage artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: coverage

- name: Upload code coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
file: lcov.info
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # required
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@madie/cql-antlr-parser",
"version": "1.0.7",
"version": "1.0.9",
"description": "Antlr Parsing of CQL in typescript",
"publishConfig": {
"access": "public"
Expand Down
22 changes: 3 additions & 19 deletions src/AntlrUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,12 @@ export default class AntlrUtils {
}

static findChildName(children: ParseTree[] | undefined): string | undefined {

if (children?.length != 4) {
console.error(
"########### Entering.. children length is ",
children?.length
);
throw new Error("Definition might be malformed.");

}
return children ? children[1].text : undefined;
}

static findChildExpression(
children: ParseTree[] | undefined
): string | undefined {

if (children?.length != 4) {
console.error(
"########### Entering.. children length is ",
children?.length
)
throw new Error("Definition might be malformed.");
}
return children ? children[3].text : undefined;
}

Expand Down Expand Up @@ -85,11 +68,12 @@ export default class AntlrUtils {
* @param comment -> a comment with comment characters
*/
static formatComment(comment: string): string {
return comment.replace(AntlrUtils.SINGLE_LINE_COMMENT_REGEX, "")
return comment
.replace(AntlrUtils.SINGLE_LINE_COMMENT_REGEX, "")
.replace(AntlrUtils.MULTI_LINE_COMMENT_REGEX, "")
.split("\n")
.map((line) => line.trim())
.filter(line=> line !== "")
.filter((line) => line !== "")
.join("\n");
}

Expand Down
9 changes: 6 additions & 3 deletions src/CqlAntlr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BufferedTokenStream, CharStreams, CommonTokenStream} from "antlr4ts";
import { BufferedTokenStream, CharStreams, CommonTokenStream } from "antlr4ts";
import { CodePointCharStream } from "antlr4ts/CodePointCharStream";
import { ParseTreeWalker } from "antlr4ts/tree";
import { cqlLexer, cqlParser, LibraryContext, cqlListener } from "../generated";
Expand All @@ -18,9 +18,12 @@ class CqlAntlr {
const charStream: CodePointCharStream = CharStreams.fromString(this.cql);
const lexer: cqlLexer = new cqlLexer(charStream);
const bufferedTokenStream = new BufferedTokenStream(lexer);
bufferedTokenStream.fill()
bufferedTokenStream.fill();

const listener: cqlListener = new CqlAntlrListener(result, bufferedTokenStream);
const listener: cqlListener = new CqlAntlrListener(
result,
bufferedTokenStream
);
ParseTreeWalker.DEFAULT.walk(listener, tree);
/**
* Disabled. Only partially implemented and would be
Expand Down
58 changes: 39 additions & 19 deletions src/CqlAntlrListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
cqlLexer,
cqlListener,
ExpressionDefinitionContext,
FunctionDefinitionContext,
IncludeDefinitionContext,
LibraryDefinitionContext,
ParameterDefinitionContext,
Expand All @@ -27,13 +28,14 @@ import CqlParameter from "./dto/CqlParameter";
import CqlContextCreator from "./CqlContextCreator";
import CqlExpressionDefinition from "./dto/CqlExpressionDefinition";
import CqlExpressionDefinitionCreator from "./CqlExpressionDefinitionCreator";
import {CqlCode} from "./dto";
import { CqlCode } from "./dto";
import CqlIdentifier from "./dto/CqlIdentifier";
import CqlIdentifierCreator from "./CqlIdentifierCreator";
import CqlRetrieve from "./dto/CqlRetrieve";
import CqlRetrieveCreator from "./CqlRetrieveCreator";
import {BufferedTokenStream} from "antlr4ts";
import { BufferedTokenStream } from "antlr4ts";
import AntlrUtils from "./AntlrUtils";
import { ParserRuleContext } from "antlr4ts/ParserRuleContext";

export default class CqlAntlrListener implements cqlListener {
// save bufferedTokenStream from lexer
Expand Down Expand Up @@ -107,30 +109,48 @@ export default class CqlAntlrListener implements cqlListener {
this.cqlResult.context = new CqlContextCreator(ctx).buildDao();
}

enterExpressionDefinition(ctx: ExpressionDefinitionContext): void {
const cqlExpressionCreator = new CqlExpressionDefinitionCreator(ctx);
const expressionDefinition: CqlExpressionDefinition | undefined = cqlExpressionCreator.buildDao();

if (expressionDefinition) {
if (ctx.start.inputStream) {
const hiddenTokens = this.bufferedTokenStream.getHiddenTokensToLeft(ctx.start.tokenIndex, cqlLexer.HIDDEN)
let comment = "";
hiddenTokens.forEach((token) => {
if (token.text){
comment += token.text;
}
})
comment = comment.trim();
if (comment){
// if expression has comment, start needs to be adjusted to consider comments as comment is part of definition
expressionDefinition.start = cqlExpressionCreator.buildLineInfo(hiddenTokens[1]);
private processDefinitionWithComments(
ctx: ParserRuleContext,
buildDao: () => CqlExpressionDefinition | undefined
): void {
const expressionDefinition: CqlExpressionDefinition | undefined =
buildDao();

if (expressionDefinition && ctx.start.inputStream) {
const hiddenTokens = this.bufferedTokenStream.getHiddenTokensToLeft(
ctx.start.tokenIndex,
cqlLexer.HIDDEN
);

if (hiddenTokens && hiddenTokens.length > 0) {
const comment = hiddenTokens
.map((token) => token.text?.trim())
.filter(Boolean)
.join(" ");

if (comment) {
expressionDefinition.start = new CqlExpressionDefinitionCreator(
ctx
).buildLineInfo(hiddenTokens[1]);
expressionDefinition.comment = AntlrUtils.formatComment(comment);
}
}
this.cqlResult.expressionDefinitions.push(expressionDefinition);
}
}

enterExpressionDefinition(ctx: ExpressionDefinitionContext): void {
this.processDefinitionWithComments(ctx, () => {
return new CqlExpressionDefinitionCreator(ctx).buildDao();
});
}

enterFunctionDefinition(ctx: FunctionDefinitionContext): void {
this.processDefinitionWithComments(ctx, () => {
return new CqlExpressionDefinitionCreator(ctx).buildDao();
});
}

enterAggregateClause(ctx: AggregateClauseContext): void {
const identifier: CqlIdentifier | undefined = new CqlIdentifierCreator(
ctx
Expand Down
3 changes: 2 additions & 1 deletion src/CustomErrorListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Recognizer } from "antlr4ts/Recognizer";
import { Token } from "antlr4ts/Token";
import { ParserATNSimulator } from "antlr4ts/atn/ParserATNSimulator";
import CqlResult from "./dto/CqlResult";
import convertCustomError from "./util/CustomeErrorConverter";

/**
* Fires on grammar errors.
Expand Down Expand Up @@ -32,7 +33,7 @@ export default class CustomErrorListener implements ANTLRErrorListener<Token> {
1, // plus 1 to ensure full text is included in Ace Editor highlight
},
name: offendingSymbol.text,
message: msg,
message: convertCustomError(msg),
});
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/util/CustomeErrorConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const convertCustomError = (errorMessage: string): string => {
let convertedMsg: string = errorMessage;
switch (errorMessage) {
case "no viable alternative at input 'define :'": {
convertedMsg = "Definition is missing a name.";
break;
}
default: {
break;
}
}
return convertedMsg;
};

export default convertCustomError;
36 changes: 28 additions & 8 deletions test/CqlAntlr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
cqlFluentFunctions,
relatedContextRetrieve,
aggregateQuery,
cqlDefineWithNoName,
} from "./testCql";
import { CqlAntlr } from "../src";
import CqlResult from "../src/dto/CqlResult";
Expand All @@ -21,7 +22,7 @@ describe("test antlr", () => {
expect(cqlResult.valueSets.length).toBe(0);
expect(cqlResult.codeSystems.length).toBe(0);
expect(cqlResult.parameters.length).toBe(0);
expect(cqlResult.expressionDefinitions.length).toEqual(0)
expect(cqlResult.expressionDefinitions.length).toEqual(0);
});

it("parse blank CQL", () => {
Expand All @@ -30,7 +31,7 @@ describe("test antlr", () => {
expect(cqlResult.valueSets.length).toBe(0);
expect(cqlResult.codeSystems.length).toBe(0);
expect(cqlResult.parameters.length).toBe(0);
expect(cqlResult.expressionDefinitions.length).toEqual(0)
expect(cqlResult.expressionDefinitions.length).toEqual(0);
});

it("parse simple Fhir CQL Definition", () => {
Expand All @@ -45,7 +46,7 @@ describe("test antlr", () => {

expect(cqlResult.parameters.length).toBe(0);

expect(cqlResult.expressionDefinitions.length).toEqual(4);
expect(cqlResult.expressionDefinitions.length).toEqual(6);
cqlResult.expressionDefinitions.forEach((def) => {
expect(def.name).toBeDefined();
});
Expand All @@ -54,11 +55,21 @@ describe("test antlr", () => {
const cqlAntlr = new CqlAntlr(simpleDefinitionCql);
const cqlResult: CqlResult = cqlAntlr.parse();
const expressions = cqlResult.expressionDefinitions;
expect(expressions.length).toEqual(4);
expect(cqlResult.expressionDefinitions[0].comment).toEqual("ehnicity comment");
expect(expressions.length).toEqual(6);
expect(cqlResult.expressionDefinitions[0].comment).toEqual(
"ehnicity comment"
);
expect(cqlResult.expressionDefinitions[1].comment).toEqual("multi line");
expect(cqlResult.expressionDefinitions[2].comment).toEqual("@author: john doe\n@description: this is Numerator");
expect(cqlResult.expressionDefinitions[2].comment).toEqual(
"@author: john doe\n@description: this is Numerator"
);
expect(cqlResult.expressionDefinitions[3].comment).toEqual(undefined);
expect(cqlResult.expressionDefinitions[4].comment).toEqual(
"multiline comment outside of a function with multiple\nrows"
);
expect(cqlResult.expressionDefinitions[5].comment).toEqual(
"comment outside of function"
);
});
it("parse fhir cql", () => {
const cqlAntlr = new CqlAntlr(fhirTestCql);
Expand Down Expand Up @@ -88,11 +99,11 @@ describe("test antlr", () => {
expect(cqlResult.usings[0]?.name).toBe("QDM");
expect(cqlResult.valueSets.length).toBe(2);
expect(cqlResult.valueSets[0].name).toBe(
"\"Adolescent depression screening assessment\""
'"Adolescent depression screening assessment"'
);
expect(cqlResult.valueSets[0].version).toBeUndefined();
expect(cqlResult.valueSets[1].name).toBe(
"\"Adolescent depression screening assessment with version\""
'"Adolescent depression screening assessment with version"'
);
expect(cqlResult.valueSets[1].version).toBe("'urn:hl7:version:20240307'");
});
Expand Down Expand Up @@ -167,4 +178,13 @@ describe("test antlr", () => {
const cqlResult: CqlResult = cqlAntlr.parse();
expect(cqlResult.errors.length).toEqual(0);
});

it("test define with no name", (): void => {
const cqlAntlr = new CqlAntlr(cqlDefineWithNoName);
const cqlResult: CqlResult = cqlAntlr.parse();
expect(cqlResult.errors.length).toEqual(1);
expect(cqlResult.errors[0].message).toEqual(
"Definition is missing a name."
);
});
});
Loading

0 comments on commit ead748c

Please sign in to comment.