Skip to content

Commit

Permalink
Merge pull request #2760 from FirelyTeam/feature/fhirpath-environment…
Browse files Browse the repository at this point in the history
…-vars

Added fhirpath environment vars
  • Loading branch information
Kasdejong authored Apr 10, 2024
2 parents ed38d79 + 1a1a02a commit 471eda9
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 2 deletions.
10 changes: 10 additions & 0 deletions src/Hl7.Fhir.Base/FhirPath/EvaluationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public EvaluationContext(ITypedElement resource, ITypedElement rootResource)
Resource = resource;
RootResource = rootResource ?? resource;
}

public EvaluationContext(ITypedElement resource, ITypedElement rootResource, IDictionary<string, IEnumerable<ITypedElement>> environment) : this(resource, rootResource)
{
Environment = environment;
}

/// <summary>
/// The data represented by <c>%rootResource</c>.
Expand All @@ -39,6 +44,11 @@ public EvaluationContext(ITypedElement resource, ITypedElement rootResource)
/// The data represented by <c>%resource</c>.
/// </summary>
public ITypedElement Resource { get; set; }

/// <summary>
/// The environment variables that are available to the FHIRPath expressions.
/// </summary>
public IDictionary<string, IEnumerable<ITypedElement>> Environment { get; set; }

/// <summary>
/// A delegate that handles the output for the <c>trace()</c> function.
Expand Down
5 changes: 5 additions & 0 deletions src/Hl7.Fhir.Base/FhirPath/Expressions/EvaluatorVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public override Invokee VisitVariableRef(FP.VariableRefExpression expression)
if (expression.Name == "rootResource")
return InvokeeFactory.GetRootResource;

if (expression is ContextVariableRefExpression Cvre)
{
return Cvre.Resolve;
}

// Variables are still functions without arguments. For now variables are treated separately here,
//Functions are handled elsewhere.
return resolve(Symbols, expression.Name, Enumerable.Empty<Type>());
Expand Down
8 changes: 8 additions & 0 deletions src/Hl7.Fhir.Base/FhirPath/Expressions/ExpressionNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ public override int GetHashCode()
return base.GetHashCode() ^ Name.GetHashCode();
}
}

public class ContextVariableRefExpression(string name) : VariableRefExpression(name)
{
internal IEnumerable<ITypedElement> Resolve(Closure context, IEnumerable<Invokee> _)
{
return context.EvaluationContext.Environment[Name] ?? throw Error.InvalidOperation($"Variable {Name} not found in environment");
}
}

public class AxisExpression : VariableRefExpression
{
Expand Down
10 changes: 10 additions & 0 deletions src/Hl7.Fhir.Base/FhirPath/FhirEvaluationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Hl7.Fhir.Specification.Terminology;
using Hl7.FhirPath;
using System;
using System.Collections.Generic;

namespace Hl7.Fhir.FhirPath
{
Expand All @@ -33,6 +34,15 @@ public FhirEvaluationContext(ITypedElement resource, ITypedElement rootResource)
{
}

/// <summary>
/// Create a FhirEvaluationContext with a resource and an environment.
/// </summary>
/// <param name="resource"></param>
/// <param name="environment"></param>
public FhirEvaluationContext(ITypedElement resource, IDictionary<string, IEnumerable<ITypedElement>> environment) : base(resource, null, environment)
{
}

/// <summary>
/// Create a FhirEvaluationContext and also set the variables <c>%resource</c> and <c>%rootResource</c> to their correct values.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Hl7.Fhir.Base/FhirPath/Parser/Grammar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ internal class Grammar
// : invocation #invocationTerm
// | literal #literalTerm
// | externalConstant #externalConstantTerm
// | externalVariable #externalVariableTerm
// | '(' expression ')' #parenthesizedTerm
// | '{' '}' #nullLiteral
// ;
Expand Down Expand Up @@ -101,7 +102,8 @@ public static Parser<Expression> FunctionInvocation(Expression focus)
public static readonly Parser<Expression> Term =
Literal
.Or(FunctionInvocation(AxisExpression.That))
.XOr(Lexer.ExternalConstant.Select(n => BuildVariableRefExpression(n))) //Was .XOr(Lexer.ExternalConstant.Select(v => Eval.ExternalConstant(v)))
.XOr(Lexer.ExternalVariable.Select(n => new ContextVariableRefExpression(n)))
.Or(Lexer.ExternalConstant.Select(n => BuildVariableRefExpression(n))) //Was .XOr(Lexer.ExternalConstant.Select(v => Eval.ExternalConstant(v)))
.XOr(BracketExpr)
.XOr(EmptyList)
.XOr(Lexer.Axis.Select(a => new AxisExpression(a)))
Expand Down
9 changes: 8 additions & 1 deletion src/Hl7.Fhir.Base/FhirPath/Parser/Lexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,19 @@ from closeQ in Parse.Char(delimiter)
// ;
public static readonly Parser<string> Identifier =
Id.XOr(DelimitedIdentifier);

// externalVariable
// : '%%' identifier
// ;
public static readonly Parser<string> ExternalVariable =
Parse.String("%%").Then(_ => Identifier.XOr(String))
.Named("external variable");

// externalConstant
// : '%' identifier
// ;
public static readonly Parser<string> ExternalConstant =
Parse.Char('%').Then(c => Identifier.XOr(String))
Parse.Char('%').Then(_ => Identifier.XOr(String))
.Named("external constant");

// DATE
Expand Down
20 changes: 20 additions & 0 deletions src/Hl7.FhirPath.Tests/Tests/EnviromentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Hl7.Fhir.ElementModel;
using Hl7.FhirPath;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;

namespace HL7.FhirPath.Tests.Tests;

[TestClass]
public class EnviromentTests
{
[TestMethod]
public void TestEnvironment()
{
var compiler = new FhirPathCompiler();
var expr = compiler.Compile("%%var = 1");

expr.IsTrue(null, new EvaluationContext(null, null, new Dictionary<string, IEnumerable<ITypedElement>> { { "var", new [] { ElementNode.ForPrimitive(1) } } }));
expr.IsBoolean(false, null, new EvaluationContext(null, null, new Dictionary<string, IEnumerable<ITypedElement>> { { "var", new[] { ElementNode.ForPrimitive(2) } } }));
}
}
1 change: 1 addition & 0 deletions src/Hl7.FhirPath.Tests/Tests/FhirPathGrammarTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public void FhirPath_Gramm_Term()
AssertParser.SucceedsMatch(parser, "doSomething('hi', 3.14)", new FunctionCallExpression(AxisExpression.This, "doSomething", TypeSpecifier.Any,
new ConstantExpression("hi"), new ConstantExpression(3.14m)));
AssertParser.SucceedsMatch(parser, "%external", new VariableRefExpression("external"));
AssertParser.SucceedsMatch(parser, "%%contextvar", new ContextVariableRefExpression("contextvar"));
AssertParser.SucceedsMatch(parser, "@2013-12", new ConstantExpression(P.Date.Parse("2013-12")));
AssertParser.SucceedsMatch(parser, "@2013-12T", new ConstantExpression(P.DateTime.Parse("2013-12")));
AssertParser.SucceedsMatch(parser, "3", new ConstantExpression(3));
Expand Down

0 comments on commit 471eda9

Please sign in to comment.