Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert RuleSet at code path #942

Merged
merged 4 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions antlr/src/main/antlr/FSH.g4
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ vsMetadata: id | title | description;
vsRule: vsComponent | caretValueRule | insertRule;
codeSystem: KW_CODESYSTEM name csMetadata* csRule*;
csMetadata: id | title | description;
csRule: concept | codeCaretValueRule | insertRule;
csRule: concept | codeCaretValueRule | codeInsertRule;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will want to update the spec with this new grammar, which I think shouldn't be fine, since the grammar is just informative.


ruleSet: KW_RULESET RULESET_REFERENCE ruleSetRule+;
ruleSetRule: sdRule | addElementRule | concept | codeCaretValueRule | vsComponent | mappingRule;
ruleSetRule: sdRule | addElementRule | concept | codeCaretValueRule | codeInsertRule | vsComponent | mappingRule;

paramRuleSet: KW_RULESET PARAM_RULESET_REFERENCE paramRuleSetContent;
paramRuleSetContent: STAR
Expand Down Expand Up @@ -75,6 +75,7 @@ caretValueRule: STAR path? caretPath EQUAL value;
codeCaretValueRule: STAR CODE* caretPath EQUAL value;
mappingRule: STAR path? ARROW STRING STRING? CODE?;
insertRule: STAR path? KW_INSERT (RULESET_REFERENCE | PARAM_RULESET_REFERENCE);
codeInsertRule: STAR CODE* KW_INSERT (RULESET_REFERENCE | PARAM_RULESET_REFERENCE);
addElementRule: STAR path CARD flag* targetType (KW_OR targetType)* STRING (STRING | MULTILINE_STRING)?;
pathRule: STAR path;

Expand Down
7 changes: 7 additions & 0 deletions src/fhirtypes/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,13 @@ export function applyInsertRules(
}
ruleSetRuleClone.path = newPath;
}
if (rule.pathArray.length > 0) {
if (ruleSetRuleClone instanceof ConceptRule) {
ruleSetRuleClone.hierarchy.unshift(...rule.pathArray);
} else if (ruleSetRuleClone instanceof CaretValueRule) {
ruleSetRuleClone.pathArray.unshift(...rule.pathArray);
}
}
if (ruleSetRuleClone instanceof ConceptRule && fshDefinition instanceof FshCodeSystem) {
// ConceptRules should not have a path context, so if one exists, show an error.
// The concept is still added to the CodeSystem.
Expand Down
9 changes: 8 additions & 1 deletion src/fshtypes/rules/InsertRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Rule } from './Rule';
export class InsertRule extends Rule {
ruleSet: string;
params: string[];
pathArray: string[] = [];

constructor(path: string) {
super(path);
Expand All @@ -22,7 +23,13 @@ export class InsertRule extends Rule {
}

toFSH(): string {
let printablePath: string;
if (this.pathArray.length) {
printablePath = this.pathArray.map(code => `#${code}`).join(' ') + ' ';
} else {
printablePath = this.path !== '' ? `${this.path} ` : '';
}
const paramPart = this.params.length > 0 ? `(${this.fshifyParameters()})` : '';
return `* ${this.path !== '' ? this.path + ' ' : ''}insert ${this.ruleSet}${paramPart}`;
return `* ${printablePath}insert ${this.ruleSet}${paramPart}`;
}
}
143 changes: 76 additions & 67 deletions src/import/FSHImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1104,8 +1104,8 @@ export class FSHImporter extends FSHVisitor {
return this.visitConcept(ctx.concept());
} else if (ctx.codeCaretValueRule()) {
return this.visitCodeCaretValueRule(ctx.codeCaretValueRule());
} else if (ctx.insertRule()) {
return this.visitInsertRule(ctx.insertRule());
} else if (ctx.codeInsertRule()) {
return this.visitCodeInsertRule(ctx.codeInsertRule());
}
}

Expand Down Expand Up @@ -1626,70 +1626,23 @@ export class FSHImporter extends FSHVisitor {
this.getPathWithContext(this.visitPath(ctx.path()), ctx);
}

visitCodeInsertRule(ctx: pc.CodeInsertRuleContext): InsertRule {
const insertRule = new InsertRule('')
.withLocation(this.extractStartStop(ctx))
.withFile(this.currentFile);
const localCodePath = ctx.CODE().map(code => {
return this.parseCodeLexeme(code.getText(), ctx).code;
});
const fullCodePath = this.getArrayPathWithContext(localCodePath, ctx);
insertRule.pathArray = fullCodePath;
return this.applyRuleSetParams(ctx, insertRule);
}

visitInsertRule(ctx: pc.InsertRuleContext): InsertRule {
const insertRule = new InsertRule(this.getPathWithContext(this.visitPath(ctx.path()), ctx))
.withLocation(this.extractStartStop(ctx))
.withFile(this.currentFile);
const [rulesetName, ruleParams] = this.parseRulesetReference(
ctx.RULESET_REFERENCE()?.getText() ?? ctx.PARAM_RULESET_REFERENCE()?.getText() ?? ''
);
insertRule.ruleSet = rulesetName;
if (ruleParams) {
insertRule.params = this.parseInsertRuleParams(ruleParams);
const ruleSet = this.paramRuleSets.get(insertRule.ruleSet);
if (ruleSet) {
const ruleSetIdentifier = JSON.stringify([ruleSet.name, ...insertRule.params]);
if (ruleSet.parameters.length === insertRule.params.length) {
// no need to create the appliedRuleSet again if we already have it
if (!this.currentDoc.appliedRuleSets.has(ruleSetIdentifier)) {
// create a new document with the substituted parameters
const appliedFsh = `RuleSet: ${ruleSet.name}${EOL}${ruleSet.applyParameters(
insertRule.params
)}${EOL}`;
const appliedRuleSet = this.parseGeneratedRuleSet(
appliedFsh,
ruleSet.name,
ctx,
insertRule
);
if (appliedRuleSet) {
// set the source info based on the original source info
appliedRuleSet.sourceInfo.file = ruleSet.sourceInfo.file;
appliedRuleSet.sourceInfo.location = { ...ruleSet.sourceInfo.location };
appliedRuleSet.rules.forEach(rule => {
rule.sourceInfo.file = appliedRuleSet.sourceInfo.file;
rule.sourceInfo.location.startLine +=
appliedRuleSet.sourceInfo.location.startLine - 1;
rule.sourceInfo.location.endLine +=
appliedRuleSet.sourceInfo.location.startLine - 1;
});
this.currentDoc.appliedRuleSets.set(ruleSetIdentifier, appliedRuleSet);
} else {
logger.error(
`Failed to parse RuleSet ${
insertRule.ruleSet
} with provided parameters (${insertRule.params.join(', ')})`,
insertRule.sourceInfo
);
return;
}
}
} else {
logger.error(
`Incorrect number of parameters applied to RuleSet ${insertRule.ruleSet}`,
insertRule.sourceInfo
);
return;
}
} else {
logger.error(
`Could not find parameterized RuleSet named ${insertRule.ruleSet}`,
insertRule.sourceInfo
);
return;
}
}
return insertRule;
return this.applyRuleSetParams(ctx, insertRule);
}

private parseRulesetReference(reference: string): [string, string] {
Expand Down Expand Up @@ -1747,12 +1700,68 @@ export class FSHImporter extends FSHVisitor {
return paramList.map(param => param.trim());
}

private parseGeneratedRuleSet(
input: string,
name: string,
ctx: pc.InsertRuleContext,
private applyRuleSetParams(
ctx: pc.InsertRuleContext | pc.CodeInsertRuleContext,
insertRule: InsertRule
) {
): InsertRule {
const [rulesetName, ruleParams] = this.parseRulesetReference(
ctx.RULESET_REFERENCE()?.getText() ?? ctx.PARAM_RULESET_REFERENCE()?.getText() ?? ''
);
insertRule.ruleSet = rulesetName;
if (ruleParams) {
insertRule.params = this.parseInsertRuleParams(ruleParams);
const ruleSet = this.paramRuleSets.get(insertRule.ruleSet);
if (ruleSet) {
const ruleSetIdentifier = JSON.stringify([ruleSet.name, ...insertRule.params]);
if (ruleSet.parameters.length === insertRule.params.length) {
// no need to create the appliedRuleSet again if we already have it
if (!this.currentDoc.appliedRuleSets.has(ruleSetIdentifier)) {
// create a new document with the substituted parameters
const appliedFsh = `RuleSet: ${ruleSet.name}${EOL}${ruleSet.applyParameters(
insertRule.params
)}${EOL}`;
const appliedRuleSet = this.parseGeneratedRuleSet(appliedFsh, ruleSet.name, insertRule);
if (appliedRuleSet) {
// set the source info based on the original source info
appliedRuleSet.sourceInfo.file = ruleSet.sourceInfo.file;
appliedRuleSet.sourceInfo.location = { ...ruleSet.sourceInfo.location };
appliedRuleSet.rules.forEach(rule => {
rule.sourceInfo.file = appliedRuleSet.sourceInfo.file;
rule.sourceInfo.location.startLine +=
appliedRuleSet.sourceInfo.location.startLine - 1;
rule.sourceInfo.location.endLine +=
appliedRuleSet.sourceInfo.location.startLine - 1;
});
this.currentDoc.appliedRuleSets.set(ruleSetIdentifier, appliedRuleSet);
} else {
logger.error(
`Failed to parse RuleSet ${
insertRule.ruleSet
} with provided parameters (${insertRule.params.join(', ')})`,
insertRule.sourceInfo
);
return;
}
}
} else {
logger.error(
`Incorrect number of parameters applied to RuleSet ${insertRule.ruleSet}`,
insertRule.sourceInfo
);
return;
}
} else {
logger.error(
`Could not find parameterized RuleSet named ${insertRule.ruleSet}`,
insertRule.sourceInfo
);
return;
}
}
return insertRule;
}

private parseGeneratedRuleSet(input: string, name: string, insertRule: InsertRule) {
// define a temporary document that will contain this RuleSet
const tempDocument = new FSHDocument(this.currentFile);
// save the currentDoc so it can be restored after parsing this RuleSet
Expand Down
3 changes: 2 additions & 1 deletion src/import/generated/FSH.interp

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions src/import/generated/FSHListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,15 @@ FSHListener.prototype.exitInsertRule = function(ctx) {
};


// Enter a parse tree produced by FSHParser#codeInsertRule.
FSHListener.prototype.enterCodeInsertRule = function(ctx) {
};

// Exit a parse tree produced by FSHParser#codeInsertRule.
FSHListener.prototype.exitCodeInsertRule = function(ctx) {
};


// Enter a parse tree produced by FSHParser#addElementRule.
FSHListener.prototype.enterAddElementRule = function(ctx) {
};
Expand Down
Loading