Skip to content

Instantly share code, notes, and snippets.

@brianpos
Created September 6, 2021 05:53
Show Gist options
  • Save brianpos/2f2d33a0f22ef566947bc4a5b7dc1eb8 to your computer and use it in GitHub Desktop.
Save brianpos/2f2d33a0f22ef566947bc4a5b7dc1eb8 to your computer and use it in GitHub Desktop.
StructureMap Serialization for FHIR Mapping Language
/// <summary>
/// https://hl7.org/fhir/mapping.g4
/// </summary>
public class StructureMap_Serializer
{
public string Write_structureMap(StructureMap sm)
{
// mapId structure* imports* group+ EOF
StringBuilder sb = new StringBuilder();
sb.AppendLine(Write_mapId(sm));
foreach (var structure_ in sm.Structure)
sb.AppendLine(Write_structure(structure_));
foreach (var import in sm.Import)
sb.AppendLine(Write_imports(import));
foreach (var group in sm.Group)
sb.AppendLine(Write_group(group));
return sb.ToString();
}
string Write_mapId(StructureMap sm)
{
// 'map' url '=' identifier
return string.Join(" ",
"map", Write_url(sm.Url), "=", Write_identifier(sm.Name));
}
string Write_url(string value)
{
// DELIMITEDIDENTIFIER
return Write_delimitedIdentifier(value);
}
string Write_delimitedIdentifier(string value)
{
var delimited = value.Replace("\"", "\\\"");
return $"\"{delimited}\"";
}
string Write_identifier(string value)
{
// IDENTIFIER
// | DELIMITEDIDENTIFIER
if (string.IsNullOrEmpty(value))
return null;
Regex r = new Regex("^([A-Za-z]|'_')([A-Za-z0-9]|'_')*$");
if (r.IsMatch(value))
return value;
return "\"" + value.Replace("\"", "\\\"") + "\"";
}
string Write_structure(StructureMap.StructureComponent structure_)
{
// 'uses' url structureAlias? 'as' modelMode
return String.Join(" ",
"uses", Write_url(structure_.Url), Write_structureAlias(structure_.Alias), "as", structure_.Mode.GetLiteral());
}
string Write_structureAlias(string alias)
{
// 'alias' identifier
if (!string.IsNullOrEmpty(alias))
return String.Join(" ",
"alias", Write_identifier(alias));
return null;
}
string Write_imports(string url)
{
// 'imports' url
return String.Join(" ",
"imports", Write_url(url));
}
string Write_group(StructureMap.GroupComponent group)
{
// 'group' identifier parameters extends? typeMode? rules
StringBuilder sb = new StringBuilder();
sb.Append("group ");
sb.Append(Write_identifier(group.Name));
sb.Append(" " + Write_parameters(group.Input));
if (!String.IsNullOrEmpty(group.Extends))
sb.Append(" " + Write_extends(group.Extends));
if (group.TypeMode.HasValue)
sb.Append(" " + Write_typeMode(group.TypeMode.GetLiteral()));
sb.Append(Write_rules(group.Rule));
return sb.ToString();
}
string Write_rules(IEnumerable<StructureMap.RuleComponent> rules)
{
// '{' rule+ '}'
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("{");
foreach (var rule in rules)
sb.AppendLine(Write_rule(rule));
sb.AppendLine("}");
return sb.ToString();
}
string Write_typeMode(string typeMode)
{
// '<<' groupTypeMode '>>'
return "<<" + typeMode + ">>";
}
string Write_extends(string extends)
{
// 'extends' identifier
return string.Join(" ",
"extends", Write_identifier(extends));
}
string Write_parameters(IEnumerable<StructureMap.InputComponent> inputs)
{
// '(' parameter (',' parameter)+ ')'
return "(" + string.Join(", ", inputs.Select(input => Write_parameter(input))) + ")";
}
string Write_parameter(StructureMap.InputComponent input)
{
// inputMode identifier type?
return String.Join(" ",
input.Mode.GetLiteral(), Write_identifier(input.Name), Write_type(input.Type));
}
string Write_type(string type)
{
// ':' identifier
return ": " + Write_identifier(type);
}
string Write_rule(StructureMap.RuleComponent rule)
{
// ruleSources ('->' ruleTargets)? dependent? ruleName? ';'
StringBuilder sb = new StringBuilder();
sb.Append(Write_ruleSources(rule.Source));
if (rule.Target.Any())
{
sb.Append(" -> ");
sb.Append(Write_ruleTargets(rule.Target));
}
if (rule.Dependent.Any())
{
sb.Append(" " + Write_dependent(rule, rule.Dependent));
}
if (!string.IsNullOrEmpty(rule.Name))
sb.Append(" " + Write_ruleName(rule.Name));
sb.Append(";");
if (!string.IsNullOrEmpty(rule.Documentation))
sb.Append("// " + rule.Documentation);
return sb.ToString();
}
string Write_ruleName(string name)
{
// DELIMITEDIDENTIFIER
return Write_delimitedIdentifier(name);
}
string Write_ruleSources(IEnumerable<StructureMap.SourceComponent> sources)
{
// ruleSource (',' ruleSource)*
return String.Join(", ", sources.Select(source => Write_ruleSource(source)));
}
string Write_ruleSource(StructureMap.SourceComponent source)
{
// ruleContext sourceType? sourceDefault? sourceListMode? alias? whereClause? checkClause? log?
StringBuilder sb = new StringBuilder();
sb.Append(" " + Write_ruleContext(source.Context, source.Element));
if (!string.IsNullOrEmpty(source.Type))
{
sb.Append(" " + Write_sourceType(source));
}
if (source.DefaultValue != null)
{
sb.Append(" " + Write_sourceDefault(source));
}
if (source.ListMode.HasValue)
{
sb.Append($" {source.ListMode.GetLiteral()}");
}
if (!string.IsNullOrEmpty(source.Variable))
{
sb.Append(" " + Write_alias(source.Variable));
}
if (!string.IsNullOrEmpty(source.Condition))
{
sb.Append(" " + Write_whereClause(source.Condition));
}
if (!string.IsNullOrEmpty(source.Check))
{
sb.Append(" " + Write_checkClause(source.Check));
}
if (!string.IsNullOrEmpty(source.LogMessage))
{
sb.Append(" " + Write_log(source.LogMessage));
}
return sb.ToString();
}
string Write_ruleTargets(IEnumerable<StructureMap.TargetComponent> targets)
{
// ruleTarget (',' ruleTarget)*
return String.Join(", ", targets.Select(target => Write_ruleTarget(target)));
}
string Write_sourceType(StructureMap.SourceComponent source)
{
// identifier (INTEGER '..' upperBound)?
StringBuilder sb = new StringBuilder();
sb.Append(Write_identifier(source.Type));
if (source.Min.HasValue || !string.IsNullOrEmpty(source.Max))
{
sb.Append(" (");
sb.Append(source.Min);
sb.Append(" .. ");
sb.Append(Write_upperBound(source.Max));
sb.Append(")");
}
return sb.ToString();
}
string Write_upperBound(string upper)
{
// INTEGER
// | '*'
if (!string.IsNullOrEmpty(upper))
return upper;
return "*";
}
string Write_ruleContext(string context, string element)
{
// identifier ('.' identifier)?
if (!string.IsNullOrEmpty(element))
return Write_identifier(context) + "." + Write_identifier(element);
return Write_identifier(context);
}
string Write_sourceDefault(Element value)
{
// 'default' fhirPath
if (value is FhirString fs)
return "default " + Write_fhirPath(fs.Value);
return null;
}
string Write_alias(string value)
{
// 'as' identifier
return "as " + Write_identifier(value);
}
string Write_whereClause(string value)
{
// 'where' fhirPath
return " where " + Write_fhirPath(value);
}
string Write_checkClause(string value)
{
// 'check' fhirPath
return " check " + Write_fhirPath(value);
}
string Write_log(string fhirpath)
{
// 'log' fhirPath
return "log " + Write_fhirPath(fhirpath);
}
string Write_dependent(StructureMap.RuleComponent rule, IEnumerable<StructureMap.DependentComponent> dependents)
{
// 'then' (invocation | rules)
if (rule.Rule.Any())
return " then" + Write_rules(rule.Rule);
return " then" + Write_invocation(rule);
}
string Write_ruleTarget(StructureMap.TargetComponent target)
{
// ruleContext ('=' transform)? alias? targetListMode?
// | invocation alias? // alias is not required when simply invoking a group
StringBuilder sb = new StringBuilder();
if (!string.IsNullOrEmpty(target.Context))
{
sb.Append(" " + Write_ruleContext(target.Context, target.Element));
if (target.Transform.HasValue)
sb.Append(" = " + Write_transform(target));
if (!string.IsNullOrEmpty(target.Variable))
{
sb.Append(" " + Write_alias(target.Variable));
}
if (target.ListMode.Any())
sb.Append(" " + target.ListMode.First().GetLiteral());
}
else
{
// invocation
if (!string.IsNullOrEmpty(target.Variable))
{
sb.Append(" " + Write_alias(target.Variable));
}
}
return sb.ToString();
}
string Write_transform(StructureMap.TargetComponent target)
{
if (target.Transform == StructureMap.StructureMapTransform.Copy)
{
return Write_ruleContext(target.Context, target.Element);
}
if (target.Transform == StructureMap.StructureMapTransform.Evaluate && target.Parameter.Count() == 1)
{
if (target.Parameter.First().Value is FhirString fs)
{
string value = fs.Value;
value.Replace("\"", "\\\"");
return $"\"{value}\"";
}
}
// literal // trivial constant transform
// | ruleContext // 'copy' transform
// | invocation // other named transforms
return "";
}
string Write_invocation(StructureMap.RuleComponent rule)
{
// identifier '(' paramList? ')'
// return $"{target.Transform.GetLiteral()}({Write_paramList(target.Parameter)})";
return "";
}
string Write_invocation(StructureMap.TargetComponent target)
{
// identifier '(' paramList? ')'
return $"{target.Transform.GetLiteral()}({Write_paramList(target.Parameter)})";
}
string Write_paramList(IEnumerable<StructureMap.ParameterComponent> parameters)
{
// param (',' param)*
return String.Join(", ", parameters.Select(parameter => Write_param(parameter)));
}
string Write_param(StructureMap.ParameterComponent parameter)
{
// literal
// | identifier
if (parameter.Value is Id id)
return id.Value;
if (parameter.Value is FhirString fs)
return fs.Value;
if (parameter.Value is FhirBoolean fb)
return fb.Value.ToString();
if (parameter.Value is Integer i)
return i.Value.ToString();
if (parameter.Value is FhirDecimal fd)
return fd.Value.ToString();
return "";
}
string Write_fhirPath(string fhirpath)
{
// literal // insert reference to FhirPath grammar here
return fhirpath;
}
/*
literal
: INTEGER
| NUMBER
| STRING
| DATETIME
| TIME
| BOOL
;
groupTypeMode
: 'types' | 'type+'
;
sourceListMode
: 'first' | 'not_first' | 'last' | 'not_last' | 'only_one'
;
targetListMode
: 'first' | 'share' | 'last' | 'collate'
;
inputMode
: 'source' | 'target'
;
modelMode // StructureMapModelMode binding
: 'source' | 'queried' | 'target' | 'produced'
;
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment