Search code examples
c#sprache

How can I use Sprache to parse an expression when the start of the expression is the same


I am having some trouble with my Sprache based parser. I am trying to parse a string in which there are two possible things to parse. One option is a regular variable name like x or a more complex version x[1]. The first references a simple variable the second an aggregate variable made up of many variables, x[1] references the first variable in the aggregate.

This is what I have so far.

    private static readonly Parser<VariableModel> SingletonVariableName =
        from leading in Sprache.Parse.WhiteSpace.Many()
        from variable in Identifier
        from trailing in Sprache.Parse.WhiteSpace.Many()
        select variable;

    private static readonly Parser<VariableModel> Identifier =
        from first in Sprache.Parse.Letter.Once().Text()
        from rest in Sprache.Parse.LetterOrDigit.Many().Text()
        select new VariableModel(string.Concat(first, rest));

   private static readonly Parser<AggregateVariableReference> AggregateVariableReference =
        from variableName in Identifier
        from openingSubscript in Sprache.Parse.Char('[').Once()
        from subscriptStatement in Sprache.Parse.Number.Text()
        from closingSubscript in Sprache.Parse.Char(']').Once()
        select new AggregateVariableReference(variableName.Name,
                                              Convert.ToInt32(subscriptStatement));
private static readonly Parser<Expression> LeftHandSide =
    SingletonVariableName.Select(Models.Expression.CreateIdentifier)
       .Or(AggregateVariableReference.Select(Models.Expression.CreateAggregateReference));

I read somewhere that the Or method only works if the first character will decide which expression wins. In my case that isn't the case. Both start with a variable name. What does XOr do? Will that help in this case?

I get the following exception: Sprache.ParseException : Parsing failure: unexpected '['; expected = or != or >= or <= or > or < (Line 1, Column 3); recently consumed: xx


Solution

  • The way to do this is to order the arguments to the Or() combinator so that the one consuming the most input is first:

    static readonly Parser<Expression> LeftHandSide =
        AggregateVariableReference
            .Select(Models.Expression.CreateAggregateReference)
       .Or(SingletonVariableName
            .Select(Models.Expression.CreateIdentifier));