Skip to content

Commit

Permalink
Merge branch 'feature/fhirpath-position' into feature/fhirpath-parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
brianpos committed Apr 23, 2024
2 parents 7b86874 + 92c8537 commit b7e0dc5
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 58 deletions.
54 changes: 53 additions & 1 deletion src/Hl7.Fhir.Base/FhirPath/Expressions/ExpressionNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,30 @@ protected CustomExpression(TypeSpecifier type, ISourcePositionInfo location = nu
public abstract Expression Reduce();
}


public class SubTokenExpression : IPositionAware<SubTokenExpression>
{
public SubTokenExpression(string value, ISourcePositionInfo location = null)
{
Value = value;
Location = location;
}
public SubTokenExpression(char value, ISourcePositionInfo location = null)
{
Value = $"{value}";
Location = location;
}

public string Value { get; private set; }
public ISourcePositionInfo Location { get; private set; }

SubTokenExpression IPositionAware<SubTokenExpression>.SetPos(Position startPos, int length)
{
Location = new FhirPathExpressionLocationInfo() { LinePosition = startPos.Column, LineNumber = startPos.Line, RawPosition = startPos.Pos, Length = length };
return this;
}
}

public class IdentifierExpression : ConstantExpression, Sprache.IPositionAware<IdentifierExpression>
{
public IdentifierExpression(object value, TypeSpecifier type, ISourcePositionInfo location = null)
Expand All @@ -121,15 +145,23 @@ public IdentifierExpression(object value, ISourcePositionInfo location = null)
}

// Discuss: Reduce function? - Skip this in the compile stage? - Update the visitor to skip the bracket too
// public class BracketExpression : FunctionCallExpression, Sprache.IPositionAware<BracketExpression>, Sprache.IPositionAware<Expression>
public class BracketExpression : CustomExpression, Sprache.IPositionAware<BracketExpression>
{
public BracketExpression(Expression operand, ISourcePositionInfo location = null) : base(operand.ExpressionType, location)
{
Operand = operand;
}

public BracketExpression(SubTokenExpression leftBrace, SubTokenExpression rightBrace, Expression operand, ISourcePositionInfo location = null) : base(operand.ExpressionType, location)
{
Operand = operand;
LeftBrace = leftBrace;
RightBrace = rightBrace;
}

public Expression Operand { get; private set; }
public SubTokenExpression LeftBrace { get; private set; }
public SubTokenExpression RightBrace { get; private set; }

public override T Accept<T>(ExpressionVisitor<T> visitor) => visitor.VisitCustomExpression(this);

Expand Down Expand Up @@ -201,6 +233,20 @@ public FunctionCallExpression(Expression focus, string name, TypeSpecifier type,
Arguments = arguments != null ? arguments.ToArray() : throw Error.ArgumentNull("arguments");
}

public FunctionCallExpression(Expression focus, string name, SubTokenExpression leftBrace, SubTokenExpression rightBrace, TypeSpecifier type, params Expression[] arguments) : this(focus, name, leftBrace, rightBrace, type, (IEnumerable<Expression>)arguments)
{
}

public FunctionCallExpression(Expression focus, string name, SubTokenExpression leftBrace, SubTokenExpression rightBrace, TypeSpecifier type, IEnumerable<Expression> arguments, ISourcePositionInfo location = null) : base(type, location)
{
if (string.IsNullOrEmpty(name)) throw Error.ArgumentNull(nameof(name));
Focus = focus;
FunctionName = name;
Arguments = arguments != null ? arguments.ToArray() : throw Error.ArgumentNull(nameof(arguments));
LeftBrace = leftBrace;
RightBrace = rightBrace;
}

public FunctionCallExpression(Expression focus, string name, TypeSpecifier type, Expression argument, ISourcePositionInfo location = null) : base(type, location)
{
if (string.IsNullOrEmpty(name)) throw Error.ArgumentNull(nameof(name));
Expand All @@ -212,6 +258,8 @@ public FunctionCallExpression(Expression focus, string name, TypeSpecifier type,

public Expression Focus { get; private set; }
public string FunctionName { get; private set; }
public SubTokenExpression LeftBrace { get; private set; }
public SubTokenExpression RightBrace { get; private set; }

public IEnumerable<Expression> Arguments { get; private set; }

Expand Down Expand Up @@ -258,6 +306,10 @@ public class ChildExpression : FunctionCallExpression, Sprache.IPositionAware<Ch
{
}

public ChildExpression(Expression focus, ConstantExpression name) : base(focus, OP_PREFIX + "children", null, null, TypeSpecifier.Any, name)
{
}

public string ChildName
{
get
Expand Down
3 changes: 0 additions & 3 deletions src/Hl7.Fhir.Base/FhirPath/Expressions/ExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/

using Hl7.Fhir.Language;
using Hl7.Fhir.Language.Debugging;

namespace Hl7.FhirPath.Expressions
{
public abstract class ExpressionVisitor<T>
Expand Down
94 changes: 54 additions & 40 deletions src/Hl7.Fhir.Base/FhirPath/Parser/Grammar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ private static IResult<P.Quantity> quantityParser(IInput i)
// | '{' '}' #nullLiteral
// ;
public static readonly Parser<Expression> BracketExpr =
(from lparen in Parse.Char('(')
from expr in Parse.Ref(() => Expression) //.Positioned()
from rparen in Parse.Char(')')
select new BracketExpression(expr))
(from lparen in Parse.Char('(').Token().Select(v => new SubTokenExpression(v)).Positioned()
from expr in Parse.Ref(() => Expression)
from rparen in Parse.Char(')').Token().Select(v => new SubTokenExpression(v)).Positioned()
select new BracketExpression(lparen, rparen, expr))
.Positioned()
.Named("BracketExpr");

Expand All @@ -76,16 +76,15 @@ private static IResult<P.Quantity> quantityParser(IInput i)
.Select(v => NewNodeListInitExpression.Empty)
.Positioned();

public static Parser<Expression> Function(Expression context)
public static Parser<FunctionCallExpression> Function(Expression context)
{
return
(
from n in Lexer.Identifier.Select(name => name)
from lparen in Parse.Char('(').TokenIgnoreComments()
from paramList in Parse.Ref(() => FunctionParameter(n).Named("parameter")).DelimitedBy(Parse.Char(',').TokenIgnoreComments()).Optional()
from rparen in Parse.Char(')').TokenIgnoreComments()
select new FunctionCallExpression(context, n, TypeSpecifier.Any, paramList.GetOrElse(Enumerable.Empty<Expression>())))
.Positioned();
from n in Lexer.Identifier.Select(name => new IdentifierExpression(name)).Positioned()
from lparen in Parse.Char('(').TokenIgnoreComments().Select(v => new SubTokenExpression(v)).Positioned()
from paramList in Parse.Ref(() => FunctionParameter(n.Value).Named("parameter")).DelimitedBy(Parse.Char(',').TokenIgnoreComments()).Optional()
from rparen in Parse.Char(')').TokenIgnoreComments().Select(v => new SubTokenExpression(v)).Positioned()
select new FunctionCallExpression(context, n.Value, lparen, rparen, TypeSpecifier.Any, paramList.GetOrElse(Enumerable.Empty<Expression>()))
.UsePositionFrom(n.Location);
}

public static Parser<Expression> FunctionParameter(string name) =>
Expand All @@ -97,32 +96,32 @@ public static Parser<Expression> Function(Expression context)
public static Parser<Expression> FunctionInvocation(Expression focus)
{
return Function(focus)
.Or(Lexer.Identifier.Select(i => new ChildExpression(focus, i)).Positioned())
.Or(Lexer.Identifier.Select(i => new ConstantExpression(i)).Positioned().Select(i => new ChildExpression(focus, i)).Positioned())
//.XOr(Lexer.Axis.Select(a => new AxisExpression(a)))
.TokenIgnoreComments();
}

public static readonly Parser<Expression> Term =
Literal
.Or(FunctionInvocation(AxisExpression.That))
.XOr(Lexer.ExternalConstant.Select(n => BuildVariableRefExpression(n)).Positioned()) //Was .XOr(Lexer.ExternalConstant.Select(v => Eval.ExternalConstant(v)))
.XOr(Lexer.ExternalConstant.Select(n => new SubTokenExpression(n)).Positioned().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)).Positioned())
.TokenIgnoreComments()
.Named("Term");


public static Expression BuildVariableRefExpression(string name)
public static Expression BuildVariableRefExpression(SubTokenExpression name)
{
if (name.StartsWith("ext-"))
return new FunctionCallExpression(AxisExpression.That, "builtin.coreexturl", TypeSpecifier.String, new ConstantExpression(name.Substring(4)));
if (name.Value.StartsWith("ext-"))
return new FunctionCallExpression(AxisExpression.That, "builtin.coreexturl", null, null, TypeSpecifier.String, new ConstantExpression(name.Value.Substring(4)).UsePositionFrom(name.Location));
#pragma warning disable IDE0046 // Convert to conditional expression
else if (name.StartsWith("vs-"))
else if (name.Value.StartsWith("vs-"))
#pragma warning restore IDE0046 // Convert to conditional expression
return new FunctionCallExpression(AxisExpression.That, "builtin.corevsurl", TypeSpecifier.String, new ConstantExpression(name.Substring(3)));
return new FunctionCallExpression(AxisExpression.That, "builtin.corevsurl", null, null, TypeSpecifier.String, new ConstantExpression(name.Value.Substring(3)).UsePositionFrom(name.Location));
else
return new VariableRefExpression(name);
return new VariableRefExpression(name.Value).UsePositionFrom(name.Location);
}

public static readonly Parser<string> TypeSpec =
Expand Down Expand Up @@ -166,7 +165,7 @@ public static Parser<Expression> Invocation(Expression focus)
// '.' invocation
public static Parser<Expression> DotInvocation(Expression focus)
{
return Parse.Char('.').Then(op => FunctionInvocation(focus));
return Parse.Char('.').Select(v => new SubTokenExpression(v)).Positioned().Then(op => FunctionInvocation(focus).Select(t => { t.SetPositionFrom(op.Location); return t; }));
}

// '[' expression ']' #indexerExpression
Expand All @@ -178,14 +177,13 @@ public static Parser<Expression> IndexerInvocation(Expression focus)

// | ('+' | '-') expression #polarityExpression
public static readonly Parser<Expression> PolarityExpression =
(from op in Lexer.PolarityOperator.Optional()
from op in Lexer.PolarityOperator.Select(v => new SubTokenExpression(v)).Positioned().Optional()
from indexer in InvocationExpression
select op.IsEmpty ? indexer : new UnaryExpression(op.Get(), indexer))
.Positioned();
select op.IsEmpty ? indexer : new UnaryExpression(op.Get().Value[0], indexer).UsePositionFrom(op.Get().Location);

public static Parser<Expression> BinaryExpression(Parser<string> oper, Parser<Expression> operands)
public static Parser<Expression> BinaryExpression(Parser<SubTokenExpression> oper, Parser<Expression> operands)
{
return Parse.ChainOperator(oper, operands, (op, left, right) => new BinaryExpression(op, left, right)).Positioned();
return Parse.ChainOperator(oper, operands, (op, left, right) => new BinaryExpression(op.Value, left, right).UsePositionFrom(op.Location));
//return
// from left in operands
// from right in (from op in oper
Expand All @@ -195,41 +193,57 @@ public static Parser<Expression> BinaryExpression(Parser<string> oper, Parser<Ex
}

// | expression('*' | '/' | 'div' | 'mod') expression #multiplicativeExpression
public static readonly Parser<Expression> MulExpression = BinaryExpression(Lexer.MulOperator, PolarityExpression);
public static readonly Parser<Expression> MulExpression = BinaryExpression(
Lexer.MulOperator.Select(v => new SubTokenExpression(v)).Positioned(),
PolarityExpression);

// | expression('+' | '-' ) expression #additiveExpression
public static readonly Parser<Expression> AddExpression = BinaryExpression(Lexer.AddOperator, MulExpression);
public static readonly Parser<Expression> AddExpression = BinaryExpression(
Lexer.AddOperator.Select(v => new SubTokenExpression(v)).Positioned(),
MulExpression);

// | expression '|' expression #unionExpression
public static readonly Parser<Expression> UnionExpression = BinaryExpression(Lexer.UnionOperator, AddExpression);
public static readonly Parser<Expression> UnionExpression = BinaryExpression(
Lexer.UnionOperator.Select(v => new SubTokenExpression(v)).Positioned(),
AddExpression);

// | expression('<=' | '<' | '>' | '>=') expression #inequalityExpression
public static readonly Parser<Expression> InEqExpression = BinaryExpression(Lexer.InEqOperator, UnionExpression);
public static readonly Parser<Expression> InEqExpression = BinaryExpression(
Lexer.InEqOperator.Select(v => new SubTokenExpression(v)).Positioned(),
UnionExpression);

// | expression('is' | 'as') typeSpecifier #typeExpression
public static readonly Parser<Expression> TypeExpression =
InEqExpression.Then(
ineq => (from isas in Lexer.TypeOperator
ineq => (from isas in Lexer.TypeOperator.Select(v => new SubTokenExpression(v)).Positioned()
from tp in TypeSpec.Select(v => new IdentifierExpression(v)).Positioned()
select new BinaryExpression(isas, ineq, tp))
.Or(Parse.Return(ineq).Positioned()))
// .Positioned()
;
select new BinaryExpression(isas.Value, ineq, tp).UsePositionFrom(isas.Location))
.Or(Parse.Return(ineq)));

// | expression('=' | '~' | '!=' | '!~' | '<>') expression #equalityExpression
public static readonly Parser<Expression> EqExpression = BinaryExpression(Lexer.EqOperator, TypeExpression);
public static readonly Parser<Expression> EqExpression = BinaryExpression(
Lexer.EqOperator.Select(v => new SubTokenExpression(v)).Positioned(),
TypeExpression);

// | expression('in' | 'contains') expression #membershipExpression
public static readonly Parser<Expression> MembershipExpression = BinaryExpression(Lexer.MembershipOperator, EqExpression);
public static readonly Parser<Expression> MembershipExpression = BinaryExpression(
Lexer.MembershipOperator.Select(v => new SubTokenExpression(v)).Positioned(),
EqExpression);

// | expression 'and' expression #andExpression
public static readonly Parser<Expression> AndExpression = BinaryExpression(Lexer.AndOperator, MembershipExpression);
public static readonly Parser<Expression> AndExpression = BinaryExpression(
Lexer.AndOperator.Select(v => new SubTokenExpression(v)).Positioned(),
MembershipExpression);

// | expression('or' | 'xor') expression #orExpression
public static readonly Parser<Expression> OrExpression = BinaryExpression(Lexer.OrOperator, AndExpression);
public static readonly Parser<Expression> OrExpression = BinaryExpression(
Lexer.OrOperator.Select(v => new SubTokenExpression(v)).Positioned(),
AndExpression);

// | expression 'implies' expression #impliesExpression
public static readonly Parser<Expression> ImpliesExpression = BinaryExpression(Lexer.ImpliesOperator, OrExpression);
public static readonly Parser<Expression> ImpliesExpression = BinaryExpression(
Lexer.ImpliesOperator.Select(v => new SubTokenExpression(v)).Positioned(),
OrExpression);

public static readonly Parser<Expression> Expression = ImpliesExpression;
}
Expand Down
38 changes: 37 additions & 1 deletion src/Hl7.Fhir.Base/FhirPath/Sprache/Parse.Positioned.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

using Hl7.Fhir.Language.Debugging;

namespace Hl7.FhirPath.Sprache
{
partial class Parse
{
/// <summary>
/// Construct a parser that will set the position to the position-aware
/// T on succsessful match.
/// T on successful match.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parser"></param>
Expand All @@ -23,5 +25,39 @@ partial class Parse
return r;
};
}

/// <summary>
/// Construct a parser that will set the position to the position-aware
/// T on successful match.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="me"></param>
/// <param name="positionInfo"></param>
/// <returns></returns>
public static T UsePositionFrom<T>(this T me, ISourcePositionInfo positionInfo)
where T : IPositionAware<T>
{
if (positionInfo is Expressions.FhirPathExpressionLocationInfo li)
{
Position startPos = new Position(li.RawPosition, li.LineNumber, li.LinePosition);
me.SetPos(startPos, li.Length);
}
return me;
}

/// <summary>
/// Only use in Function Invocation
/// </summary>
/// <param name="me"></param>
/// <param name="positionInfo"></param>
/// <returns></returns>
public static void SetPositionFrom(this Hl7.FhirPath.Expressions.Expression me, ISourcePositionInfo positionInfo)
{
if (positionInfo is Expressions.FhirPathExpressionLocationInfo li)
{
Position startPos = new Position(li.RawPosition, li.LineNumber, li.LinePosition);
me.SetPos<Expressions.Expression>(startPos, li.Length);
}
}
}
}

0 comments on commit b7e0dc5

Please sign in to comment.