superpower

Trying to build TextParser<string[]> with alike open/close/delimiter characters


I'm working on building a parser with the SuperPower library.

Here's a sample of the source input that I want to parse:

|ABC|xyz|1 3|~~~|

new string[]{"ABC", "xyz", "1 3", "~~~"}

I think that the trouble I'm having is that my delimiter character is the same as my ClosingPipe character.

How should I build this TextParser<string[]>?


Solution

  • Here are some parsers that should work for you:

    public static TextParser<char> Pipe =>
        from c in Character.EqualTo('|')
        select c;
    
    public static TextParser<string> Content =>
        from c in Character.Except('|').Many()
        select new string(c);
    
    public static TextParser<string[]> Parser =>
        from openingPipe in Pipe
        from content1 in Content
        from closingPipe in Pipe
        from contents in Content.ManyDelimitedBy(Pipe)
        select new[] { content1 }
            .Concat(contents.Take(contents.Length - 1)) // remove the empty string on the end
            .ToArray();
    

    Then use it like so:

    string[] result = Parser.Parse("|ABC|xyz|1 3|~~~|");
    

    And result should be { "ABC", "xyz", "1 3", "~~~" }

    The idea is to parse out the opening pipe, first content, then the closing pipe, and then since (I'm assuming) the rest of the number of delimiters can change, you can use Superpower's built in method ManyDelimitedBy to parse out as many other content pieces as there are, separated by pipes. But since your input always has a pipe at the end, the ManyDelimitedBy will leave an empty string at the end of the contents array, which I'm removing before returning the final output.

    EDIT

    Here is a way to do it without having to chop off the empty string:

    public static TextParser<string> ExtraContent =>
        from content in Content
        from pipe in Pipe
        select content;
    
    public static TextParser<string[]> Parser2 =>
        from openingPipe in Pipe
        from content1 in Content
        from closingPipe in Pipe
        from contents in ExtraContent.Many()
        select new[] { content1 }.Concat(contents).ToArray();