Last active
September 11, 2021 22:00
-
-
Save brianpos/e4fd4575a6d39290c8cc258f3100a2ac to your computer and use it in GitHub Desktop.
Kick starter sample for taking a FHIR Mapping Language input string and converting it into a FHIR R4 StructureMap resource
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TestMethod] | |
public void TestFmlParserReal() | |
{ | |
var expression = "map \"http://hl7.org/fhir/StructureMap/tutorial\" = tutorial\r\n"; | |
expression += "uses \"http://hl7.org/fhir/StructureDefinition/Patient\" alias pat as source\r\n"; | |
expression += "uses \"http://hl7.org/fhir/StructureDefinition/RelatedPerson\" alias rp as target\r\n"; | |
expression += "imports \"http://hl7.org/fhir/StructureMap/core\"\r\n"; | |
expression += "group tutorial\r\n"; | |
Console.WriteLine(expression); | |
// 1. | |
var tokenizer = FhirMappingLanguageTokenizer.BuildTokenizer(); | |
var tokenList = tokenizer.Tokenize(expression); | |
foreach (var token in tokenList) | |
Console.WriteLine($"{token.Kind}: {token.Span.ToStringValue()}"); | |
// 2. | |
var parser = FhirMappingLanguageTokenizer.structureMap_Parser; // parser built with combinators | |
var structureMap = parser.Parse(tokenList); | |
Console.WriteLine(new Hl7.Fhir.Serialization.FhirJsonSerializer(new Hl7.Fhir.Serialization.SerializerSettings() { Pretty = true }).SerializeToString(structureMap)); | |
} | |
namespace BrianPos.FhirMappingLanguage | |
{ | |
public class FhirMappingLanguageParser | |
{ | |
public enum FhirMappingLanguageRawToken | |
{ | |
// Primitive tokens | |
DATETIME, | |
TIME, | |
IDENTIFIER, | |
DELIMITEDIDENTIFIER, | |
STRING, | |
INTEGER, | |
NUMBER, | |
COMMENT, | |
LINE_COMMENT, | |
fragment, | |
TIMEFORMAT, | |
ESC, | |
UNICODE, | |
HEX, | |
// special tokens | |
token_STRUCTURE, | |
} | |
public struct mapId | |
{ | |
public string url; | |
public string name; | |
} | |
public struct ruleContext | |
{ | |
public string context; | |
public string element; | |
} | |
public struct sourceType | |
{ | |
public string type; | |
public int? min; | |
public string max; | |
} | |
public struct invocation | |
{ | |
public string name; | |
public IEnumerable<StructureMap.ParameterComponent> parameters; | |
} | |
public struct transform | |
{ | |
public string literal; | |
public invocation? invocation; | |
public ruleContext? ruleContext; | |
} | |
public struct dependent | |
{ | |
public IEnumerable<invocation> invocation; | |
public RuleComponent[] rules; | |
} | |
private static readonly TextParser<char> BackTicContentChar = Span.EqualTo("\\`").Value('`').Try() | |
.Or(Character.ExceptIn('`', '\\', '\r', '\n')); | |
// | |
// Summary: | |
// A | |
// 'SQL-style' | |
// string. Single quote delimiters, with embedded single quotes escaped by '' doubling. | |
public static TextParser<string> BackTicStyle | |
{ | |
get; | |
} = Character.EqualTo('`').IgnoreThen(BackTicContentChar.Many()).Then((char[] s) => Character.EqualTo('`').Value(new string(s))); | |
// Like the string parser, the number version is permissive - it's just looking | |
// for a chunk of input that looks something like a JSON number, and not | |
// necessarily a valid one. | |
static TextParser<string> DecimalToken { get; } = | |
from sign in Character.EqualTo('-').OptionalOrDefault() | |
from wholeDigits in Character.Digit.AtLeastOnce() | |
from decimalPoint in Character.In('.') | |
from fraction in Character.Digit.AtLeastOnce() | |
select $"{sign}{wholeDigits}.{fraction}"; | |
public static Tokenizer<FhirMappingLanguageRawToken> BuildTokenizer() | |
{ | |
var tokenizer = new TokenizerBuilder<FhirMappingLanguageRawToken>() | |
.Ignore(Span.WhiteSpace) | |
.Match(Comment.CStyle, FhirMappingLanguageRawToken.COMMENT) | |
.Match(Comment.CPlusPlusStyle, FhirMappingLanguageRawToken.LINE_COMMENT) | |
.Match(Span.Regex(@"@[0-9][0-9][0-9][0-9]\-[0-9][0-9]\-[0-9][0-9](T[0-9][0-9]:[0-9][0-9]:[0-9][0-9](.[0-9]+)?)?(([+\-][0-9][0-9]:[0-9][0-9])|Z)?"), FhirMappingLanguageRawToken.DATETIME) | |
.Match(Span.Regex(@"@T[0-9][0-9]:[0-9][0-9]:[0-9][0-9](.[0-9]+)?(([+\-][0-9][0-9]:[0-9][0-9])|Z)?"), FhirMappingLanguageRawToken.TIME) | |
.Match(DecimalToken, FhirMappingLanguageRawToken.NUMBER) | |
.Match(Numerics.Integer, FhirMappingLanguageRawToken.INTEGER) | |
.Match(QuotedString.SqlStyle, FhirMappingLanguageRawToken.STRING) | |
.Match(QuotedString.CStyle, FhirMappingLanguageRawToken.DELIMITEDIDENTIFIER) | |
.Match(Span.Regex(@"([A-Za-z]|'_')([A-Za-z0-9]|'_')*"), FhirMappingLanguageRawToken.IDENTIFIER) | |
.Match(Span.EqualTo("("), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo(")"), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo("{"), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo("}"), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo("->"), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo(";"), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo(".."), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo(":"), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.EqualTo("."), FhirMappingLanguageRawToken.token_STRUCTURE) | |
.Match(Span.NonWhiteSpace, FhirMappingLanguageRawToken.IDENTIFIER) | |
.Match(Span.Regex(@"\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]"), FhirMappingLanguageRawToken.HEX) | |
.Build(); | |
return tokenizer; | |
} | |
// grammar keywords (specific identifier tokens) | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> BOOL_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "true").Select(value => value.ToStringValue()) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "false").Select(value => value.ToStringValue())); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> MAP_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "map").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> ALIAS_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "alias").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> USES_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "uses").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> AS_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "as").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> IMPORTS_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "imports").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> GROUP_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "group").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> EXTENDS_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "extends").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> DEFAULT_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "default").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> WHERE_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "where").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> CHECK_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "check").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> LOG_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "log").Select(value => value.ToStringValue()); | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> THEN_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "then").Select(value => value.ToStringValue()); | |
// 'map' url '=' identifier | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, mapId> mapId_Parser = | |
(from mapToken in MAP_Parser | |
from url in url_Parser | |
from eqToken in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "=") | |
from identifier in identifier_Parser | |
select new { url, identifier }) | |
.Select(body => new mapId() { url = body.url, name = body.identifier }) | |
.Named("mapId"); | |
// DELIMITEDIDENTIFIER | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> url_Parser = | |
Token.EqualTo(FhirMappingLanguageRawToken.DELIMITEDIDENTIFIER) | |
// .Apply(Superpower.Parsers.QuotedString.SqlStyle) | |
.Select(n => n.ToStringValue().Trim('\"')) | |
.Named("url"); | |
// IDENTIFIER | DELIMITEDIDENTIFIER | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> identifier_Parser = | |
Token.EqualTo(FhirMappingLanguageRawToken.DELIMITEDIDENTIFIER) | |
.Or(Token.EqualTo(FhirMappingLanguageRawToken.IDENTIFIER)).Select(body => body.ToStringValue()) | |
.Named("identifier"); | |
// LINECOMMENT | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> lineComment_Parser = | |
Token.EqualTo(FhirMappingLanguageRawToken.LINE_COMMENT) | |
.Select(n => n.ToStringValue()) | |
.Named("lineComment"); | |
// 'source' | 'queried' | 'target' | 'produced' | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMapModelMode> modelMode_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "source") | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "queried")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "target")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "produced")) | |
.Select(n => Hl7.Fhir.Utility.EnumUtility.ParseLiteral<StructureMapModelMode>(n.ToStringValue()).Value) | |
.Named("modelMode"); | |
// 'default' fhirPath | |
public static TokenListParser<FhirMappingLanguageRawToken, string> sourceDefault_Parser = | |
(from mapToken in DEFAULT_Parser | |
from fhirpath in fhirPath_Parser | |
select fhirpath) | |
.Named("sourceDefault"); | |
// 'as' identifier | |
public static TokenListParser<FhirMappingLanguageRawToken, string> alias_Parser = | |
(from mapToken in AS_Parser | |
from alias in identifier_Parser | |
select alias) | |
.Named("alias"); | |
// 'where' fhirPath | |
public static TokenListParser<FhirMappingLanguageRawToken, string> whereClause_Parser = | |
(from mapToken in WHERE_Parser | |
from fhirpath in fhirPath_Parser | |
select fhirpath) | |
.Named("whereClause"); | |
// 'check' fhirPath | |
public static TokenListParser<FhirMappingLanguageRawToken, string> checkClause_Parser = | |
(from mapToken in CHECK_Parser | |
from fhirpath in fhirPath_Parser | |
select fhirpath) | |
.Named("checkClause"); | |
// 'log' fhirPath | |
public static TokenListParser<FhirMappingLanguageRawToken, string> log_Parser = | |
(from mapToken in LOG_Parser | |
from fhirpath in fhirPath_Parser | |
select fhirpath) | |
.Named("log"); | |
// 'uses' url structureAlias? 'as' modelMode | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, StructureMap.StructureComponent> structure_Parser = | |
(from _usesToken in USES_Parser | |
from _url in url_Parser | |
from _structureAlias in structureAlias_Parser.OptionalOrDefault() | |
from _asToken in AS_Parser | |
from _modelMode in modelMode_Parser | |
from _comment in lineComment_Parser.OptionalOrDefault() | |
select new { _url, _structureAlias, _modelMode, _comment }) | |
.Select(body => new StructureMap.StructureComponent() | |
{ | |
Url = body._url, | |
Mode = body._modelMode, | |
Alias = body._structureAlias, | |
Documentation = body._comment | |
}) | |
.Named("structure"); | |
// 'alias' identifier | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, string> structureAlias_Parser = | |
(from mapToken in ALIAS_Parser | |
from identifier in identifier_Parser | |
select identifier) | |
.Named("structureAlias"); | |
// 'imports' url | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, Canonical> imports_Parser = | |
(from mapToken in IMPORTS_Parser | |
from url in url_Parser | |
select new Canonical(url)) | |
.Named("imports"); | |
// 'group' identifier parameters extends? typeMode? rules | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, StructureMap.GroupComponent> group_Parser = | |
(from mapToken in GROUP_Parser | |
from _name in identifier_Parser | |
from _parameters in parameters_Parser | |
from _extends in extends_Parser.OptionalOrDefault() | |
from _typemode in typeMode_Parser.Optional() | |
from _rules in rules_Parser | |
select new { _name, _parameters, _extends, _typemode, _rules }) | |
.Select(body => | |
{ | |
var group = new StructureMap.GroupComponent(); | |
group.Name = body._name; | |
group.Input.AddRange(body._parameters); | |
group.ExtendsElement = body._extends; | |
if (body._typemode.HasValue) | |
group.TypeMode = body._typemode.Value; | |
else | |
group.TypeMode = StructureMapGroupTypeMode.None; | |
group.Rule.AddRange(body._rules); | |
return group; | |
}) | |
.Named("group"); | |
// 'source' | 'target' | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMapInputMode> inputMode_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "source") | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "target")) | |
.Select(n => Hl7.Fhir.Utility.EnumUtility.ParseLiteral<StructureMapInputMode>(n.ToStringValue()).Value) | |
.Named("inputMode"); | |
// 'first' | 'not_first' | 'last' | 'not_last' | 'only_one' | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMapSourceListMode> sourceListMode_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "first") | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "not_first")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "last")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "not_last")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "only_one")) | |
.Select(n => Hl7.Fhir.Utility.EnumUtility.ParseLiteral<StructureMapSourceListMode>(n.ToStringValue()).Value) | |
.Named("sourceListMode"); | |
// 'first' | 'share' | 'last' | 'collate' | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMapTargetListMode> targetListMode_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "first") | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "share")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "last")) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "collate")) | |
.Select(n => Hl7.Fhir.Utility.EnumUtility.ParseLiteral<StructureMapTargetListMode>(n.ToStringValue()).Value) | |
.Named("targetListMode"); | |
// '{' rule+ '}' | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, StructureMap.RuleComponent[]> rules_Parser = | |
(from skipToken in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, "{") | |
from _skip in lineComment_Parser.Many() | |
from _rules in rule_Parser.AtLeastOnce() | |
from _moreRules in (from skip2 in lineComment_Parser.OptionalOrDefault() | |
from _moreRules in rule_Parser | |
select _moreRules).Many() | |
from skipToken2 in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, "}") | |
select _rules.Union(_moreRules).ToArray()) | |
.Named("rules"); | |
// '<<' groupTypeMode '>>' | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, StructureMapGroupTypeMode> typeMode_Parser = | |
(from skipToken in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "<<") | |
from _groupTypeMode in groupTypeMode_Parser | |
from skipToken2 in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, ">>") | |
select _groupTypeMode) | |
.Named("typeMode"); | |
// 'extends' identifier | |
public static TokenListParser<FhirMappingLanguageRawToken, Id> extends_Parser = | |
(from mapToken in EXTENDS_Parser | |
from _identifier in identifier_Parser | |
select new Id(_identifier)) | |
.Named("extends"); | |
// '(' parameter (',' parameter)+ ')' | |
public static TokenListParser<FhirMappingLanguageRawToken, IEnumerable<StructureMap.InputComponent>> parameters_Parser = | |
(from skipToken in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, "(") | |
from _parameter in parameter_Parser | |
from _moreParameters in (from skipToken in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, ",") | |
from _parameter in parameter_Parser | |
select _parameter).AtLeastOnce() | |
from skipToken2 in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, ")") | |
select new { _parameter, _moreParameters }) | |
.Select(body => | |
{ | |
var ps = new List<StructureMap.InputComponent>(); | |
ps.Add(body._parameter); | |
ps.AddRange(body._moreParameters); | |
return ps as IEnumerable<StructureMap.InputComponent>; | |
}) | |
.Named("parameters"); | |
// inputMode identifier type? | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMap.InputComponent> parameter_Parser = | |
(from _inputMode in inputMode_Parser | |
from _identifier in identifier_Parser | |
from _type in type_Parser.OptionalOrDefault() | |
select new { _inputMode, _identifier, _type }) | |
.Select(body => | |
{ | |
var result = new StructureMap.InputComponent(); | |
result.Mode = body._inputMode; | |
result.Name = body._identifier; | |
result.Type = body._type; | |
return result; | |
}) | |
.Named("parameter"); | |
// ':' identifier | |
public static TokenListParser<FhirMappingLanguageRawToken, string> type_Parser = | |
(from mapToken in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, ":") | |
from identifier in identifier_Parser | |
select identifier) | |
.Named("type"); | |
// DELIMITEDIDENTIFIER | |
public static TokenListParser<FhirMappingLanguageRawToken, Id> ruleName_Parser = | |
Token.EqualTo(FhirMappingLanguageRawToken.DELIMITEDIDENTIFIER) | |
.Select(v => new Id(v.ToStringValue())) | |
.Named("ruleName"); | |
// ':' identifier (INTEGER '..' upperBound)? | |
public static TokenListParser<FhirMappingLanguageRawToken, sourceType> sourceType_Parser = | |
(from mapToken in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, ":") | |
from _inputMode in identifier_Parser | |
from _bounds in (from _lower in Token.EqualTo(FhirMappingLanguageRawToken.INTEGER).Apply(Numerics.IntegerInt32) | |
from skip in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, "..") | |
from _upper in upperBound_Parser | |
select new { _lower, _upper } | |
).OptionalOrDefault() | |
select new { _inputMode, _bounds }) | |
.Select(body => | |
{ | |
var result = new sourceType(); | |
result.type = body._inputMode; | |
if (body._bounds != null) | |
{ | |
result.min = body._bounds._lower; | |
result.max = body._bounds._upper; | |
} | |
return result; | |
}) | |
.Named("sourceType"); | |
// identifier ('.' identifier)? | |
public static TokenListParser<FhirMappingLanguageRawToken, ruleContext> ruleContext_Parser = | |
(from _context in identifier_Parser | |
from _element in (from skip in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, ".") | |
from _element in identifier_Parser | |
select _element).OptionalOrDefault() | |
select new ruleContext() { context = _context, element = _element }) | |
.Named("ruleContext"); | |
// ruleContext sourceType? sourceDefault? sourceListMode? alias? whereClause? checkClause? log? | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMap.SourceComponent> ruleSource_Parser = | |
(from _ruleContext in ruleContext_Parser | |
from _sourceType in sourceType_Parser.Optional() | |
from _sourceDefault in sourceDefault_Parser.OptionalOrDefault() | |
from _sourceListMode in sourceListMode_Parser.Optional() | |
from _alias in alias_Parser.OptionalOrDefault() | |
from _whereClause in whereClause_Parser.OptionalOrDefault() | |
from _checkClause in checkClause_Parser.OptionalOrDefault() | |
from _log in log_Parser.OptionalOrDefault() | |
select new { _ruleContext, _sourceType, _sourceDefault, _sourceListMode, _alias, _whereClause, _checkClause, _log }) | |
.Select(body => | |
{ | |
var result = new StructureMap.SourceComponent(); | |
result.Context = body._ruleContext.context; | |
result.Element = body._ruleContext.element; | |
if (body._sourceType.HasValue) | |
{ | |
result.Type = body._sourceType.Value.type; | |
result.Min = body._sourceType.Value.min; | |
result.Max = body._sourceType.Value.max; | |
} | |
// TODO: default? | |
result.DefaultValue = new FhirString(body._sourceDefault); | |
result.ListMode = body._sourceListMode; | |
result.Variable = body._alias; | |
result.Condition = body._whereClause; | |
result.Check = body._checkClause; | |
result.LogMessage = body._log; | |
return result; | |
}).Named("ruleSource"); | |
// ruleSource (',' ruleSource)* | |
public static TokenListParser<FhirMappingLanguageRawToken, IEnumerable<StructureMap.SourceComponent>> ruleSources_Parser = | |
(from _ruleSource in ruleSource_Parser | |
from _moreRuleSources in (from skip in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, ",") | |
from _rules in ruleSource_Parser | |
select _rules).Many() | |
select new { _ruleSource, _moreRuleSources }) | |
.Select(body => | |
{ | |
var ruleSources = new List<StructureMap.SourceComponent>(); | |
ruleSources.Add(body._ruleSource); | |
ruleSources.AddRange(body._moreRuleSources); | |
return ruleSources as IEnumerable<StructureMap.SourceComponent>; | |
}) | |
.Named("ruleSources"); | |
// INTEGER | NUMBER | STRING | DATETIME | TIME | BOOL | |
public static TokenListParser<FhirMappingLanguageRawToken, string> literal_Parser = | |
Token.EqualTo(FhirMappingLanguageRawToken.INTEGER) | |
.Or(Token.EqualTo(FhirMappingLanguageRawToken.NUMBER)) | |
.Or(Token.EqualTo(FhirMappingLanguageRawToken.STRING)) | |
.Or(Token.EqualTo(FhirMappingLanguageRawToken.DATETIME)) | |
.Or(Token.EqualTo(FhirMappingLanguageRawToken.TIME)) | |
.Select(v => v.ToStringValue()) | |
.Or(BOOL_Parser) | |
.Named("literal"); | |
// identifier '(' paramList? ')' | |
public static TokenListParser<FhirMappingLanguageRawToken, invocation> invocation_Parser = | |
(from _identifier in identifier_Parser | |
from skip in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, "(") | |
from _paramList in paramList_Parser.OptionalOrDefault() | |
from skip2 in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, ")") | |
select new { _identifier, _paramList }) | |
.Select(body => | |
{ | |
var invocation = new invocation(); | |
invocation.name = body._identifier; | |
invocation.parameters = body._paramList; | |
return invocation; | |
}) | |
.Named("invocation"); | |
// invocation (',' invocation)* | |
public static TokenListParser<FhirMappingLanguageRawToken, IEnumerable<invocation>> invocations_Parser = | |
(from _invocation in invocation_Parser | |
from _moreInvocations in (from skip in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, ",") | |
from _rules in invocation_Parser | |
select _rules).Many() | |
select new { _invocation, _moreInvocations }) | |
.Select(body => | |
{ | |
var result = new List<invocation>(); | |
result.Add(body._invocation); | |
result.AddRange(body._moreInvocations); | |
return result as IEnumerable<invocation>; | |
}) | |
.Named("invocations"); | |
// literal // trivial constant transform | |
// | ruleContext // 'copy' transform | |
// | invocation // other named transforms | |
public static TokenListParser<FhirMappingLanguageRawToken, transform> transform_Parser = | |
literal_Parser.Select(v => new transform() { literal = v }).Try() | |
.Or(invocation_Parser.Select(v => new transform() { invocation = v }).Try()) | |
.Or(ruleContext_Parser.Select(v => new transform() { ruleContext = v }).Try()) | |
.Named("transform"); | |
// ruleContext ('=' transform)? alias? targetListMode? | |
// | invocation alias? // alias is not required when simply invoking a group | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMap.TargetComponent> ruleTarget_Parser = | |
(from _ruleContext in ruleContext_Parser | |
from _transform in (from skip in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "=") | |
from _transform in transform_Parser | |
select _transform).OptionalOrDefault() | |
from _alias in alias_Parser.OptionalOrDefault() | |
from _targetListMode in targetListMode_Parser.Optional() | |
select new { _ruleContext, _transform, _alias, _targetListMode } | |
) | |
.Select(body => | |
{ | |
var result = new StructureMap.TargetComponent(); | |
result.Context = body._ruleContext.context; | |
result.Element = body._ruleContext.element; | |
result.Variable = body._alias; | |
if (!string.IsNullOrEmpty(body._alias)) | |
result.ContextType = StructureMapContextType.Variable; | |
else | |
result.ContextType = StructureMapContextType.Type; | |
result.ListMode = new List<StructureMapTargetListMode?> { body._targetListMode }; | |
if (!string.IsNullOrEmpty(body._transform.literal)) | |
result.Parameter.Add(new ParameterComponent() { Value = new FhirString(body._transform.literal) }); | |
if (body._transform.invocation.HasValue) | |
{ | |
result.Transform = Hl7.Fhir.Utility.EnumUtility.ParseLiteral<StructureMapTransform>(body._transform.invocation.Value.name); | |
result.Parameter.AddRange(body._transform.invocation.Value.parameters); | |
} | |
return result; | |
}) | |
.Or((from _invocation in invocation_Parser | |
from _alias in alias_Parser.OptionalOrDefault() | |
select new { _invocation, _alias } | |
).Select(body => | |
{ | |
var result = new StructureMap.TargetComponent(); | |
// now populate the invocation into the target object | |
result.Variable = body._invocation.name; | |
result.ContextType = StructureMapContextType.Variable; | |
return result; | |
})) | |
.Named("ruleTarget") | |
; | |
// ruleTarget (',' ruleTarget)* | |
public static TokenListParser<FhirMappingLanguageRawToken, IEnumerable<StructureMap.TargetComponent>> ruleTargets_Parser = | |
(from _ruleTarget in ruleTarget_Parser | |
from _moreRuleTargets in (from skip in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, ",") | |
from _rules in ruleTarget_Parser | |
select _rules).Many() | |
select new { _ruleTarget, _moreRuleTargets }) | |
.Select(body => | |
{ | |
var ruleTargets = new List<StructureMap.TargetComponent>(); | |
ruleTargets.Add(body._ruleTarget); | |
ruleTargets.AddRange(body._moreRuleTargets); | |
return ruleTargets as IEnumerable<StructureMap.TargetComponent>; | |
}) | |
.Named("ruleTargets"); | |
// INTEGER | '*' | |
public static TokenListParser<FhirMappingLanguageRawToken, string> upperBound_Parser = | |
Token.EqualTo(FhirMappingLanguageRawToken.INTEGER).Select(v => v.ToStringValue()) | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "*").Select(v => v.ToStringValue())) | |
.Named("upperBound"); | |
// 'then' (invocation | rules) | |
// TODO: CHECK THAT THE EXTRA INVOCATIONS ARE LEGIT | |
public static TokenListParser<FhirMappingLanguageRawToken, dependent> dependent_Parser = | |
(from skip in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "then") | |
from _dependent in (invocations_Parser.Select(value => new dependent() { invocation = value }) | |
.Or(rules_Parser.Select(value => new dependent() { rules = value }))) | |
select _dependent) | |
.Named("dependent"); | |
// literal | identifier | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMap.ParameterComponent> param_Parser = | |
literal_Parser | |
.Or(identifier_Parser) | |
.Select(body => | |
{ | |
var result = new StructureMap.ParameterComponent(); | |
result.Value = new FhirString(body); | |
return result; | |
}) | |
.Named("param"); | |
// param (',' param)* | |
public static TokenListParser<FhirMappingLanguageRawToken, IEnumerable<StructureMap.ParameterComponent>> paramList_Parser = | |
(from _ruleTarget in param_Parser | |
from _moreRuleTargets in (from skip in Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, ",") | |
from _rules in param_Parser | |
select _rules).Many() | |
select new { _ruleTarget, _moreRuleTargets }) | |
.Select(body => | |
{ | |
var ruleTargets = new List<StructureMap.ParameterComponent>(); | |
ruleTargets.Add(body._ruleTarget); | |
ruleTargets.AddRange(body._moreRuleTargets); | |
return ruleTargets as IEnumerable<StructureMap.ParameterComponent>; | |
}) | |
.Named("paramList"); | |
// literal (this should be a valid FHIRPATH expression) | |
public static TokenListParser<FhirMappingLanguageRawToken, string> fhirPath_Parser = | |
literal_Parser | |
.Or(Token.EqualTo(FhirMappingLanguageRawToken.DELIMITEDIDENTIFIER).Select(body => body.ToStringValue())) | |
.Named("fhirpath"); | |
// 'types' | 'type+' | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMapGroupTypeMode> groupTypeMode_Parser = | |
Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "types") | |
.Or(Token.EqualToValue(FhirMappingLanguageRawToken.IDENTIFIER, "types+")) | |
.Select(n => | |
{ | |
switch (n.ToStringValue()) | |
{ | |
case "types": return StructureMapGroupTypeMode.Types; | |
case "type+": return StructureMapGroupTypeMode.TypeAndTypes; | |
default: return StructureMapGroupTypeMode.None; | |
} | |
}) | |
.Named("groupTypeMode"); | |
// ruleSources ('->' ruleTargets)? dependent? ruleName? ';' | |
public static TokenListParser<FhirMappingLanguageRawToken, StructureMap.RuleComponent> rule_Parser = | |
(from _ruleSources in ruleSources_Parser | |
from _ruleTargets in | |
(from mapToken in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, "->") | |
from _rt in ruleTargets_Parser | |
select _rt | |
).OptionalOrDefault() | |
from _dependent in dependent_Parser.OptionalOrDefault() | |
from _ruleName in ruleName_Parser.OptionalOrDefault() | |
from mapToken in Token.EqualToValue(FhirMappingLanguageRawToken.token_STRUCTURE, ";") | |
from _comment in lineComment_Parser.OptionalOrDefault() | |
select new { _ruleSources, _ruleTargets, /*_dependent,*/ _ruleName, _comment }) | |
.Select(body => | |
{ | |
var result = new StructureMap.RuleComponent(); | |
result.Name = body._ruleName?.Value ?? "un-named"; | |
result.Source.AddRange(body._ruleSources); | |
if (body._ruleTargets != null) | |
result.Target.AddRange(body._ruleTargets); | |
// result.Dependent.AddRange(body._dependent); | |
result.Documentation = body._comment; | |
return result; | |
}) | |
.Named("rule"); | |
// mapId structure* imports* group+ EOF | |
public static readonly TokenListParser<FhirMappingLanguageRawToken, StructureMap> structureMap_Parser = | |
(from _comments in lineComment_Parser.Many() | |
from _mapId in mapId_Parser | |
from _structure in structure_Parser.Many() | |
from _imports in imports_Parser.Many() | |
from _group in group_Parser.AtLeastOnce() | |
select new { _comments, _mapId, _structure, _imports, _group }) | |
.AtEnd().Select(body => | |
{ | |
var sm = new StructureMap(); | |
sm.Url = body._mapId.url; | |
sm.Name = body._mapId.name; | |
sm.ImportElement.AddRange(body._imports); | |
sm.Structure.AddRange(body._structure); | |
sm.Group.AddRange(body._group); | |
return sm; | |
}) | |
.Named("structureMap"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that there are some tweaks to the grammar to parse the example SDOH content that I grabbed, specifically where multiple invocations occur on a target rule in the dependencies (then ...).