-
Notifications
You must be signed in to change notification settings - Fork 216
Open
Description
If I call Positioned
after ChainOperator
, it will only work for the top-level node and may overwrite the position of the operand when there is no operation in the input.
For example (see the end for the full example code):
static readonly Parser<Node> parserExpression =
Parse.ChainOperator(parserOperator, parserInt, (op, lhs, rhs) => new BinaryExpression(op, lhs, rhs))
.Positioned(); // only work for top-level
input: 1 + 2 - 3 + 4
expected output:
1 + 2 - 3 + 4 ( Pos 0, Line 1, Column 1, Length 13 )
├─1 + 2 - 3 ( Pos 0, Line 1, Column 1, Length 9 )
│ ├─1 + 2 ( Pos 0, Line 1, Column 1, Length 5 )
│ │ ├─1 ( Pos 0, Line 1, Column 1, Length 1 )
│ │ └─2 ( Pos 4, Line 1, Column 5, Length 1 )
│ └─3 ( Pos 8, Line 1, Column 9, Length 1 )
└─4 ( Pos 12, Line 1, Column 13, Length 1 )
actual output:
1 + 2 - 3 + 4 ( Pos 0, Line 1, Column 1, Length 13 )
├─1 + 2 - 3
│ ├─1 + 2
│ │ ├─1 ( Pos 0, Line 1, Column 1, Length 1 )
│ │ └─2 ( Pos 4, Line 1, Column 5, Length 1 )
│ └─3 ( Pos 8, Line 1, Column 9, Length 1 )
└─4 ( Pos 12, Line 1, Column 13, Length 1 )
input: 1
(1 surrounded by space characters)
expected output::
1 ( Pos 1, Line 1, Column 2, Length 1 )
actual output:
1 ( Pos 0, Line 1, Column 1, Length 3 )
Is there a proper way to obtain the position information of the intermediate node?
Full example code
node.cs
using Sprache;
using System.Collections.Generic;
namespace SphracheTest
{
public abstract class Node : IPositionAware<Node>
{
public abstract IEnumerable<Node> Children { get; }
public Position? StartPos { get; private set; }
public int Length { get; private set; }
public Node SetPos(Position startPos, int length)
{
StartPos = startPos;
Length = length;
return this;
}
}
}
literal.cs
using Sprache;
using System;
using System.Collections.Generic;
namespace SphracheTest
{
public class Literal<T> : Node, IPositionAware<Literal<T>>
{
public T Value { get; }
public Literal(T value) => Value = value;
public override IEnumerable<Node> Children => Array.Empty<Node>();
public override string? ToString() => Value?.ToString();
Literal<T> IPositionAware<Literal<T>>.SetPos(Position startPos, int length) => (Literal<T>)SetPos(startPos, length);
}
}
binaryexpression.cs
using Sprache;
using System;
using System.Collections.Generic;
namespace SphracheTest
{
public enum BinaryOperator
{
Plus,
Subtract,
}
public class BinaryExpression : Node, IPositionAware<BinaryExpression>
{
public override IEnumerable<Node> Children => new Node[] { Left, Right };
public BinaryOperator Operator { get; }
public Node Left { get; }
public Node Right { get; }
public BinaryExpression(BinaryOperator op, Node lhs, Node rhs)
{
Operator = op;
Left = lhs;
Right = rhs;
}
public override string? ToString() => string.Join(GetOperatorString(), Left, Right);
private string GetOperatorString() => Operator switch
{
BinaryOperator.Plus => " + ",
BinaryOperator.Subtract => " - ",
_ => throw new ArgumentException("Invalid operator", nameof(Operator)),
};
BinaryExpression IPositionAware<BinaryExpression>.SetPos(Position startPos, int length) => (BinaryExpression)SetPos(startPos, length);
}
}
nodeextension.cs
using System.Text;
namespace SphracheTest
{
public static class NodeExtension
{
public static string ToTreeForm(this Node node) => node.ToTreeFormImpl(new StringBuilder().AppendTreeNode(node), prefix: string.Empty).ToString();
private static StringBuilder ToTreeFormImpl(this Node node, StringBuilder acc, string prefix)
{
var iter = node.Children.GetEnumerator();
if (!iter.MoveNext())
return acc;
while (true)
{
var child = iter.Current;
acc.Append(prefix);
if (iter.MoveNext())
{
child.ToTreeFormImpl(acc.Append("├─").AppendTreeNode(child), prefix + "│ ");
}
else
{
return child.ToTreeFormImpl(acc.Append("└─").AppendTreeNode(child), prefix + " ");
}
}
}
private static StringBuilder AppendTreeNode(this StringBuilder acc, Node node)
{
acc.Append(node.ToString());
if (node.StartPos != null)
{
acc
.Append(" ( Pos ")
.Append(node.StartPos.Pos)
.Append(", Line ")
.Append(node.StartPos.Line)
.Append(", Column ")
.Append(node.StartPos.Column)
.Append(", Length ")
.Append(node.Length)
.Append(" )");
}
return acc.AppendLine();
}
}
}
program.cs
using Sprache;
using System;
namespace SphracheTest
{
static class Program
{
static readonly Parser<Node> parserInt =
(from n in Parse.Number select new Literal<int>(int.Parse(n)))
.Positioned()
.Token();
static readonly Parser<BinaryOperator> parserOperatorPlus =
Parse.Char('+').Return(BinaryOperator.Plus);
static readonly Parser<BinaryOperator> parserOperatorSubtract =
Parse.Char('-').Return(BinaryOperator.Subtract);
static readonly Parser<BinaryOperator> parserOperator =
parserOperatorPlus.XOr(parserOperatorSubtract).Token();
static readonly Parser<Node> parserExpression =
Parse.ChainOperator(parserOperator, parserInt, (op, lhs, rhs) => new BinaryExpression(op, lhs, rhs))
.Positioned(); // only work for top-level
static void Main(string[] args)
{
Console.WriteLine(parserExpression.Parse(Console.ReadLine()).ToTreeForm());
}
}
}
Metadata
Metadata
Assignees
Labels
No labels