This is part of a series of posts documenting Sprache:

This post covers the Positioned method, intended to allow parsers to return information about the position of parsed elements in the input. To use this, the types returned by the parser must implement the IPositionAware interface.

The below example implements a very simple expression parser which also returns position information.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
enum BinaryOperator
{
    Add,
    Subtract
}

class Node : IPositionAware<Node>
{
    public int Length { get; protected set; }
    public Position StartPos { get; protected set; }

    public Node SetPos(Position startPos, int length)
    {
        Length = length;
        StartPos = startPos;
        return this;
    }
}

class Literal : Node, IPositionAware<Literal>
{
    public int Value { get; }

    public Literal(int value)
    {
        Value = value;
    }

    public static Literal Create(string value) => new Literal(int.Parse(value));

    public new Literal SetPos(Position startPos, int length) => (Literal) base.SetPos(startPos, length);
}

class BinaryExpression : Node, IPositionAware<BinaryExpression>
{
    public BinaryOperator Op { get; }
    public Node Left { get; }
    public Node Right { get; }

    public BinaryExpression(BinaryOperator op, Node left, Node right)
    {
        Op = op;
        Left = left;
        Right = right;
    }

    public static Node Create(BinaryOperator op, Node left, Node right) => new BinaryExpression(op, left, right);

    public new BinaryExpression SetPos(Position startPos, int length) => (BinaryExpression)base.SetPos(startPos, length);
}

static readonly Parser<BinaryOperator> Add = Parse.Char('+').Token().Return(BinaryOperator.Add);
static readonly Parser<BinaryOperator> Subtract = Parse.Char('-').Token().Return(BinaryOperator.Subtract);
static readonly Parser<Node> Number = Parse.Number.Token().Select(Literal.Create).Positioned();

static readonly Parser<Node> Expr = Parse.ChainOperator(
    Add.Or(Subtract), Number, BinaryExpression.Create).Positioned();

[Fact]
public void PositionTest()
{
    var aaa = (BinaryExpression)Expr.Parse("1 + 2");

    Assert.Equal(4, aaa.Right.StartPos.Pos);
}

Something thats a little awkward is that because of the generic type parameter each type in the inheritance hierachy needs to implement IPositionAware separately for the Positioned extension method to be usable.