简体   繁体   中英

While parsing text using Sprache, can I determine the current index within the original string?

I have Sprache set up to parse an Equation that has a number of different possible method calls in it. After it resolves the method, is there a way to determine the index values within the original string? Perhaps the Parse has a "current index" value and "length" value that's somehow accessible?

Example input string:

IndexOf("fred", 2) + IndexOf("bob")

using a parser like this...

Parser<Expression> FunctionCall = from namePart in Parse.Letter.Many().Text()
                       from lparen in Parse.Char('(')
                       from expr in Parameter.DelimitedBy(ListDelimiter)
                       from rparen in Parse.Char(')')
                       select CallMethod(namePart, Enumerable.Repeat(sourceData, 1)
                                                             .Concat(expr)
                                                             .ToArray());

Can anyone think of a "trick" that would allow me to determine that the first CallMethod handles SubString(0, 18) , and the second CallMethod handles SubString(21, 14) from the original string?

If you use a generic class and extension method you can make a more general approach

public class PositionAware<T> : IPositionAware<PositionAware<T>>
{
    public PositionAware(T value)
    {
        Value = value;
    }

    public T Value { get; }
    public Position Start { get; private set; }
    public int Length { get; private set; }
    public PositionAware<T> SetPos(Position startPos, int length)
    {
        Start = startPos;
        Length = length;
        return this;
    }

}
public static Parser<PositionAware<T>> WithPosition<T>(this Parser<T> value)
{
    return value.Select(x => new PositionAware<T>(x)).Positioned();
}

Using it:

from c in Parse.Char('a').WithPosition()
select (c.Start, c.Value)

from c in Parameter.DelimitedBy(ListDelimiter).WithPosition()
select (c.Start, c.Value)

I've managed to answer my own question. It's the Positioned() parser extension call that allows a parser to track the position within the original text.

  Parser<Expression> FunctionCall = (from namePart in Parse.Letter.Many().Text()
                            from lparen in Parse.Char('(')
                            from expr in Parameter.DelimitedBy(ListDelimiter)
                            from rparen in Parse.Char(')')
                            select new MethodPosAware(namePart, expr)).Positioned()
                            .Select(x => CallMethod(x.Value, Enumerable.Repeat(sourceData, 1)
                                        .Concat(x.Params)
                                        .ToArray(),
                                        x.Pos.Pos, x.Length));

I had to make a new MethodPosAware class to keep the position information, that derives from Sprache's IPositionAware :

class MethodPosAware : IPositionAware<MethodPosAware>
{
    public MethodPosAware(string methodName, IEnumerable<Expression> parameters)
    {
        Value = methodName;
        Params = parameters;
    }

    public MethodPosAware SetPos(Position startPos, int length)
    {
        Pos = startPos;
        Length = length;
        return this;
    }

    public Position Pos { get; set; }
    public int Length { get; set; }
    public string Value { get; set; }
    public IEnumerable<Expression> Params { get; set; }
}

I think I'll be extending this further to work with more than just Method names, but this is sufficient to answer my question for now. I hope this helps someone down the road.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM