This is part of a series of posts documenting Sprache:

This post covers Or and XOr, which are used to match either one of two parsers.

Or

Or is used to match either one of two parsers.

  • Parser<T> Or<T>(this Parser<T> first, Parser<T> second)

The following example matches any one of the 4 keywords “return”, “function”, “switch” and “if”.

1
2
3
4
5
6
7
8
Parser<string> keyword = Parse.String("return")
    .Or(Parse.String("function"))
    .Or(Parse.String("switch"))
    .Or(Parse.String("if"))
    .Text();

Assert.Equal("return", keyword.Parse("return"));
Assert.Equal("if", keyword.Parse("if"));

The order of the parsers matters - if the first parser matches that result will be returned regardless of whether the second parser matches. In this next example the parser looking for a “foobar” keyword will never match, as the identifier parser will always match first.

1
2
3
4
5
6
Parser<Identifier> identifier = Parse.LetterOrDigit.Many().Text().Select(name => new Identifier {Name = name});
Parser<FooKeyword> fooKeyword = Parse.String("foobar").Return(new FooKeyword());

Parser<Node> keyword = identifier.Or<Node>(fooKeyword);

Assert.Equal("foo", ((Identifier)keyword.Parse("foobar")).Name);

Explicitly supplying T is useful in cases where the two parsers don’t return the same type.

XOr

XOr is also used to match either one of two parsers.

  • Parser<T> XOr<T>(this Parser<T> first, Parser<T> second)

XOr differs from Or in that it won’t try the second parser if the first parser matched 1 or more characters, as we can see in the following contrived example where “far” (which is a valid identifier) isn’t parsed by the parser as it partially matched “foo”.

1
2
3
4
5
var parser = Parse.String("foo")
    .XOr(Parse.Identifier(Parse.Letter, Parse.LetterOrDigit));

//  unexpected 'a'; expected o
Assert.Throws<ParseException>(() => parser.Parse("far"));

As I understand it this (along with all the other X* parser variants), is intended to give better error messsages by failing fast, however the Or method has logic built in to choose the most sensible error message so in reality I struggled to come up with a good example where using XOr helped.