Skip to content

Commit

Permalink
Support insert rule on value set concept
Browse files Browse the repository at this point in the history
Because caret value rules can be applied to concepts, it is also
reasonable to want to apply a rule set containing caret value rules at a
concept. This uses the existing grammar rule for a codeCaretRule, but
only one concept is allowed for the insert rule's path.
  • Loading branch information
mint-thompson committed Jul 26, 2023
1 parent c713628 commit cb87c1a
Show file tree
Hide file tree
Showing 8 changed files with 693 additions and 597 deletions.
2 changes: 1 addition & 1 deletion antlr/src/main/antlr/FSH.g4
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ invariantRule: fixedValueRule | insertRule | pathRule;

valueSet: KW_VALUESET name vsMetadata* vsRule*;
vsMetadata: id | title | description;
vsRule: vsComponent | caretValueRule | codeCaretValueRule | insertRule;
vsRule: vsComponent | caretValueRule | codeCaretValueRule | insertRule | codeInsertRule;
codeSystem: KW_CODESYSTEM name csMetadata* csRule*;
csMetadata: id | title | description;
csRule: concept | codeCaretValueRule | codeInsertRule;
Expand Down
9 changes: 5 additions & 4 deletions src/import/FSHErrorListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,15 @@ export class FSHErrorListener extends ErrorListener<Token> {
// * onset[x], abatement[x] MS
// > extraneous input 'abatement[x]' expecting {<EOF>, KW_ALIAS, KW_PROFILE, KW_EXTENSION,
// > KW_INSTANCE, KW_INVARIANT, KW_VALUESET, KW_CODESYSTEM, KW_RULESET, KW_MAPPING, KW_LOGICAL, KW_RESOURCE}
// * #hippo, #crocodile , #emu from system ZOO
// > extraneous input '#crocodile' expecting {<EOF>, KW_ALIAS, KW_PROFILE, KW_EXTENSION,
// > KW_INSTANCE, KW_INVARIANT, KW_VALUESET, KW_CODESYSTEM, KW_RULESET, KW_MAPPING, KW_LOGICAL, KW_RESOURCE}
// * codes from valueset FirstZooVS, SecondZooVS
// > extraneous input 'SecondZooVS' expecting {<EOF>, KW_ALIAS, KW_PROFILE, KW_EXTENSION,
// > KW_INSTANCE, KW_INVARIANT, KW_VALUESET, KW_CODESYSTEM, KW_RULESET, KW_MAPPING, KW_LOGICAL, KW_RESOURCE}
// * #hippo, #crocodile , #emu from system ZOO
// > no viable alternative at input '\n* #hippo, #crocodile ,'
// * #hippo, #crocodile, #emu from system ZOO
// > no viable alternative at input '\n* #hippo, #crocodile, #emu from'
else if (
/^extraneous input/.test(msg) &&
(/^extraneous input/.test(msg) || /^no viable alternative at input '/.test(msg)) &&
(/,$/.test(oneTokenBack?.text) || /,$/.test(twoTokensBack?.text))
) {
message = "Using ',' to list items is no longer supported. Use 'and' to list multiple items.";
Expand Down
30 changes: 25 additions & 5 deletions src/import/FSHImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1191,7 +1191,17 @@ export class FSHImporter extends FSHVisitor {
return rule;
}
} else if (ctx.insertRule()) {
return this.visitInsertRule(ctx.insertRule());
return this.visitInsertRule(ctx.insertRule(), true);
} else if (ctx.codeInsertRule()) {
const rule = this.visitCodeInsertRule(ctx.codeInsertRule(), true);
if (rule.pathArray.length > 1) {
logger.error(
'Only one concept may be listed before an insert rule on a ValueSet.',
rule.sourceInfo
);
} else {
return rule;
}
}
}

Expand Down Expand Up @@ -1777,22 +1787,32 @@ export class FSHImporter extends FSHVisitor {
return pathRule;
}

visitCodeInsertRule(ctx: pc.CodeInsertRuleContext): InsertRule {
visitCodeInsertRule(ctx: pc.CodeInsertRuleContext, keepSystem = false): 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 parsedCode = this.parseCodeLexeme(code.getText(), ctx);
if (keepSystem) {
return `${parsedCode.system ?? ''}#${parsedCode.code}`;
} else {
return parsedCode.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))
visitInsertRule(ctx: pc.InsertRuleContext, withPathArray = false): InsertRule {
const localPath = this.visitPath(ctx.path());
const fullPathArray = this.getArrayPathWithContext(localPath === '' ? [] : [localPath], ctx);
const insertRule = new InsertRule(fullPathArray.join('.'))
.withLocation(this.extractStartStop(ctx))
.withFile(this.currentFile);
if (withPathArray) {
insertRule.pathArray = fullPathArray;
}
return this.applyRuleSetParams(ctx, insertRule);
}

Expand Down
2 changes: 1 addition & 1 deletion src/import/generated/FSH.interp

Large diffs are not rendered by default.

1,183 changes: 597 additions & 586 deletions src/import/generated/FSHParser.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/import/parserContexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface VsRuleContext extends ParserRuleContext {
caretValueRule(): CaretValueRuleContext;
codeCaretValueRule(): CodeCaretValueRuleContext;
insertRule(): InsertRuleContext;
codeInsertRule(): CodeInsertRuleContext;
}

export interface CodeSystemContext extends ParserRuleContext {
Expand Down
27 changes: 27 additions & 0 deletions test/import/FSHImporter.ValueSet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,7 @@ describe('FSHImporter', () => {
assertCaretValueRule(valueSet.rules[1], '', 'designation.value', 'hipopótamo', false, [
'ZOO#hippo'
]);
expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
});
});

Expand All @@ -1284,6 +1285,32 @@ describe('FSHImporter', () => {
expect(vs.rules).toHaveLength(1);
assertInsertRule(vs.rules[0] as Rule, '', 'MyRuleSet');
});

it('should parse an insert rule at a concept', () => {
const input = leftAlign(`
ValueSet: ZooVS
* ZOO#hippo insert DesignationRules
`);
const result = importSingleText(input, 'Insert.fsh');
const vs = result.valueSets.get('ZooVS');
expect(vs.rules).toHaveLength(1);
assertInsertRule(vs.rules[0] as Rule, '', 'DesignationRules', [], ['ZOO#hippo']);
expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
});

it('should log an error when an insert rule has more than one concept', () => {
const input = leftAlign(`
ValueSet: ZooVS
* ZOO#hippo ZOO#big-hippo insert DesignationRules
`);
const result = importSingleText(input, 'Insert.fsh');
const vs = result.valueSets.get('ZooVS');
expect(vs.rules).toHaveLength(0);
expect(loggerSpy.getAllMessages('error')).toHaveLength(1);
expect(loggerSpy.getLastMessage('error')).toMatch(
/Only one concept may be listed before an insert rule on a ValueSet\..*File: Insert\.fsh.*Line: 3\D*/s
);
});
});
});
});
36 changes: 36 additions & 0 deletions test/import/FSHImporter.context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1070,5 +1070,41 @@ describe('FSHImporter', () => {
]);
expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
});

it('should parse a value set that uses an indented InsertRule after a ValueSetConceptComponentRule', () => {
const input = leftAlign(`
ValueSet: ZooVS
* ZOO#hippo "Hippopotamus"
* insert MyRuleSet
`);
const result = importSingleText(input, 'Zoo.fsh');
expect(result.valueSets.size).toBe(1);
const valueSet = result.valueSets.get('ZooVS');
expect(valueSet.rules.length).toBe(2);
assertValueSetConceptComponent(valueSet.rules[0], 'ZOO', undefined, [
new FshCode('hippo', 'ZOO', 'Hippopotamus').withLocation([3, 3, 3, 26]).withFile('Zoo.fsh')
]);
assertInsertRule(valueSet.rules[1], 'ZOO#hippo', 'MyRuleSet', [], ['ZOO#hippo']);
expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
});

it('should log an error when parsing a value set that uses an indented InsertRule with a concept after a ValueSetConceptComponentRule', () => {
const input = leftAlign(`
ValueSet: ZooVS
* ZOO#hippo "Hippopotamus"
* ZOO#big-hippo insert MyRuleSet
`);
const result = importSingleText(input, 'Zoo.fsh');
expect(result.valueSets.size).toBe(1);
const valueSet = result.valueSets.get('ZooVS');
expect(valueSet.rules.length).toBe(1);
assertValueSetConceptComponent(valueSet.rules[0], 'ZOO', undefined, [
new FshCode('hippo', 'ZOO', 'Hippopotamus').withLocation([3, 3, 3, 26]).withFile('Zoo.fsh')
]);
expect(loggerSpy.getAllMessages('error')).toHaveLength(1);
expect(loggerSpy.getLastMessage('error')).toMatch(
/Only one concept may be listed before an insert rule on a ValueSet\..*File: Zoo\.fsh.*Line: 4\D*/s
);
});
});
});

0 comments on commit cb87c1a

Please sign in to comment.