Skip to content

Instantly share code, notes, and snippets.

@brianpos
Last active September 11, 2021 22:00
Show Gist options
  • Save brianpos/e4fd4575a6d39290c8cc258f3100a2ac to your computer and use it in GitHub Desktop.
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
[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");
}
}
@brianpos
Copy link
Author

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 ...).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment