This is part of a series of posts documenting Sprache:

This post covers Optional and XOptional which are helper methods for parsing optional elements.

Optional

Construct a parser that indicates that the given parser is optional. The returned parser will succeed on any input no matter whether the given parser succeeds or not. Signature:

  • Parser<IOption<T>> Optional<T>(this Parser<T> parser)

In the following example based on an assembly language parser, the label is optional.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Parser<string> identifier = Parse.Identifier(Parse.Letter, Parse.LetterOrDigit);

Parser<string> label =
    from labelName in identifier.Token()
    from colon in Parse.Char(':').Token()
    select labelName;

Parser<Tuple<string, string[]>> instruction =
    from instructionName in Parse.LetterOrDigit.Many().Text().Token()
    from operands in identifier.Token().XDelimitedBy(Parse.Char(','))
    select Tuple.Create(instructionName, operands.ToArray());

// Example of returning anonymous type from a sprache parser
var assemblyLine =
    from l in label.Optional()
    from i in instruction.Optional()
    select new {Label = l, Instruction = i};

Assert.Equal("test", assemblyLine.Parse("test: mov ax, bx").Label.Get());
Assert.False(assemblyLine.Parse("mov ax, bx").Label.IsDefined);

XOptional

Exclusive version of Optional, differs from Optional in that the parser fails if the supplied parser partially matches.

  • Parser<IOption<T>> XOptional<T>(this Parser<T> parser)

In this example (which is a simplification of the above) an error is thrown if a label is partially parsed.

1
2
3
4
5
6
7
8
9
Parser<string> identifier = Parse.Identifier(Parse.Letter, Parse.LetterOrDigit);

Parser<string> label =
    from labelName in identifier.Token()
    from colon in Parse.Char(':').Token()
    select labelName;

// unexpected 'l'; expected :
Assert.Throws<ParseException>(() => label.XOptional().Parse("invalid label:"));