0

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|~~~|

  • The First | is the OpeningPipe.
  • The last | is the ClosingPipe.
  • The 2nd, 3rd, and 4th | are delimiters.
  • Ultimately, I would want to be able to produce this structure during parsing:
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[]>?

Ronnie Overby
  • 45,287
  • 73
  • 267
  • 346

1 Answers1

1

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();
jtate
  • 2,612
  • 7
  • 25
  • 35
  • Remove the empty string on the end. :P This was the same approach I came up with but I wasn't happy with having to remove that empty string. Now I don't feel so bad. – Ronnie Overby Jul 29 '19 at 14:14
  • you could also chop it off before you start parsing :) – jtate Jul 29 '19 at 14:18