1

I have 2 Superpower TextParser - each targets to parse a certain input - differentiated by prefix and parameters. I am trying to create a combined parser that gives out the result when either of the TextParser can parse the input. The output is a list with certain strings describing the input.

e.g. Some input and output pair as follows

"interrupt(20)" -> List { "interrupt", "20" }
"insert(20,12)" -> List {"insert", "20", "12" }
"delete(18)" -> Error

For the sample input and output pairs, I wrote the following in C# with superpower reference added.

// Works fine for "insert(12,24)"
string failingTest = "interrupt(12)";

TextParser<List<string>> identifier001 =
    from prefix in Span.EqualTo("insert")
    from open in Character.EqualTo('(')
    from num0 in Character.Numeric.Many()
    from comma1 in Character.EqualTo(',')
    from num1 in Character.Numeric.Many()
    from close in Character.EqualTo(')')
    select new List<string> { prefix.ToStringValue(), string.Join("", num0), string.Join("", num1) };

TextParser<List<string>> identifier002 =
    from prefix in Span.EqualTo("interrupt")
    from open in Character.EqualTo('(')
    from num0 in Character.Numeric.Many()
    from close in Character.EqualTo(')')
    select new List<string> { prefix.ToStringValue(), string.Join("", num0) };

TextParser<List<string>> combined =
    from span in identifier001.Or(identifier002)
    select span;

var res = combined.Parse(failingTest);
foreach (var item in res)
{
    Console.WriteLine(item);
}

My combined identifier cannot parse the input interrupt(12) and gives the following error

Syntax error (line 1, column 3): unexpected t, expected s.

Is there any proper way of doing the "Or" combination of the identifiers?

Parth Pandya
  • 1,050
  • 7
  • 15
  • 22
cppxaxa
  • 135
  • 1
  • 9

1 Answers1

3

You have to call Try() after your first parser, then chain it with the Or() function, like this:

TextParser<List<string>> combined =
    from span in identifier001.Try().Or(identifier002)
    select span;

What happens is that your first parser identifier001 has already consumed the "in" portion of the string, which leaves identifier002 to try and parse the string "terrupt(20)" which fails. The call to Try() tells the parser to backtrack to the point of origin of the previous parser, which is what you want.

The documentation on Try() is this:

Construct a parser that tries one parser, and backtracks if unsuccessful so that no input appears to have been consumed by subsequent checks against the result.

jtate
  • 2,612
  • 7
  • 25
  • 35
  • 1
    I wonder why this is not the default. Surely consuming input on failure is not a common requirement? – Andrew Savinykh Aug 14 '20 at 10:51
  • @AndrewSavinykh I agree, especially when chaining an `Or()` call on a parser you'd expect it to backtrack automatically. I personally struggled with this for a while too until I combed thru the documentation to find the `Try()` function. I haven't looked at the source code for Superpower, but there may be a reason for it being this way. – jtate Aug 14 '20 at 12:31
  • I looked through the code and opened [this issue](https://github.com/datalust/superpower/issues/118). The library development is very slow lately, though, so I'm not sure if it gets enough traction. – Andrew Savinykh Aug 14 '20 at 23:19