Search code examples
c#parsingsprache

“Sprache” parser `Present` semantics


I am writing a parser that conforms to System for Cross-domain Identity Management: Protocol Filtering spec. I was able to parse almost any expression with Sprache, except "pr" operator. Can't wrap my head how to make it work properly.

Here is main parsing stuff:

   {
        And = Operator("and", "And");
        Or = Operator("or", "Or");
    
        Le = Operator("le", "LessThanOrEqual");
        Lt = Operator("lt", "LessThan");
        Gt = Operator("gt", "GreaterThan");
        Ge = Operator("ge", "GreaterThanOrEqual");
        Eq = Operator("eq", "Equal");
        Ne = Operator("ne", "NotEqual");
        Co = Operator("co", "Contains");
        Sw = Operator("sw", "StartsWith");
        Ew = Operator("ew", "EndsWith");
    
        Pr = Operator("pr", "Present");

        ...


        Operand = (Parentheses
            .XOr(Literal.Or(AttrPath.Token()))
            .XOr(CaseInsensitiveString)).Token();

        //compareOp = "eq" / "ne" / "co" /
        //        "sw" / "ew" /
        //        "gt" / "lt" /
        //        "ge" / "le"
        CompareExpression = Parse.XChainOperator(Le.Or(Lt).XOr(Ge.Or(Gt)).XOr(Eq.Or(Ne)).XOr(Sw.Or(Ew)).XOr(Co).XOr(Pr), Operand, FilterExpression.Binary);

        PresentsExpression = Parse.XChainRightOperator(Pr, Operand, FilterExpression.Binary).Or(CompareExpression);

        // logExp    = FILTER SP ("and" / "or") SP FILTER
        AndExpression = Parse.XChainOperator(And, CompareExpression, FilterExpression.Binary);

        // logExp    = FILTER SP ("and" / "or") SP FILTER
        OrExpression = Parse.XChainOperator(Or, AndExpression, FilterExpression.Binary);
        Filter = OrExpression;
    }
    private static Parser<string> Operator(string op, string opName)
    {
        return Parse.String(op).Token().Return(opName);
    }

This is where I add "pr" parsing:

 Parse.XChainRightOperator(Pr, Operand, FilterExpression.Binary).Or(CompareExpression);

This filter fails: title pr and addresses[type eq \"work\"].streetAddress eq \"workStreet\"
And this is ok:

  addresses[type eq \"work\"].streetAddress eq \"workStreet\" 
and (userType eq \"Employee\" or userType eq \"ParttimeEmployee\")

Maybe someone can help me out with this.

Edit: Maybe I should chain operators with XChainRightOperator at some point, because pr title works fine. But chain extensions are for binary expressions, and I need unary.


Solution

  • I have found answer. Here is correct parsing code:

        Operand = (Parentheses
            .XOr(Literal.Or(AttrPath.Token()))
            .XOr(CaseInsensitiveString)).Token();
    
        // compareOp = "eq" / "ne" / "co" /
        //        "sw" / "ew" /
        //        "gt" / "lt" /
        //        "ge" / "le"
        Comparison = Parse.XChainOperator(Le.Or(Lt).XOr(Ge.Or(Gt)).XOr(Eq.Or(Ne)).XOr(Sw.Or(Ew)).XOr(Co).XOr(Pr), Operand, FilterExpression.Binary);
    
        // attrPath SP "pr"
        Presence = Operand.SelectMany(operand => Pr, (operand, pr) => FilterExpression.Unary(pr, operand));
    
        // attrExp = (attrPath SP "pr") /
        //   (attrPath SP compareOp SP compValue)
        AttributeExpression = Presence.Or(Comparison);
    
        // logExp    = FILTER SP ("and" / "or") SP FILTER
        LogicalExpression = Parse.XChainOperator(Or.Or(And), AttributeExpression, FilterExpression.Binary);
        Filter = LogicalExpression;
    

    Trick was to check for operand first and operation after it and using Or instead of XOr in AttributeExpression = Presence.Or(Comparison);