102 lines
2.8 KiB
C#
102 lines
2.8 KiB
C#
using Nub.Lang.Frontend.Lexing;
|
|
using Nub.Lang.Frontend.Parsing;
|
|
|
|
namespace Nub.Lang.Frontend.Diagnostics;
|
|
|
|
public readonly struct SourceText(string path, string content)
|
|
{
|
|
public string Path { get; } = path;
|
|
public string Content { get; } = content;
|
|
}
|
|
|
|
public readonly struct SourceLocation(int line, int column)
|
|
{
|
|
public int Line { get; } = line;
|
|
public int Column { get; } = column;
|
|
}
|
|
|
|
public readonly struct SourceSpan(SourceLocation start, SourceLocation end)
|
|
{
|
|
public SourceLocation Start { get; } = start;
|
|
public SourceLocation End { get; } = end;
|
|
|
|
public override string ToString()
|
|
{
|
|
if (Start.Line == End.Line)
|
|
{
|
|
if (Start.Column == End.Column)
|
|
{
|
|
return $"{Start.Line}:{Start.Column}";
|
|
}
|
|
|
|
return $"{Start.Line}:{Start.Column}-{End.Column}";
|
|
}
|
|
|
|
return $"{Start.Line}:{Start.Column}-{End.Line}:{End.Column}";
|
|
}
|
|
}
|
|
|
|
public static class SourceLocationCalculator
|
|
{
|
|
private static int[] GetLineStarts(string content)
|
|
{
|
|
var lineStarts = new List<int> { 0 };
|
|
|
|
for (var i = 0; i < content.Length; i++)
|
|
{
|
|
if (content[i] == '\n')
|
|
{
|
|
lineStarts.Add(i + 1);
|
|
}
|
|
}
|
|
|
|
return lineStarts.ToArray();
|
|
}
|
|
|
|
private static SourceLocation IndexToLocation(string content, int index)
|
|
{
|
|
if (index < 0 || index > content.Length)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(index), $"Index {index} is out of range for content of length {content.Length}");
|
|
}
|
|
|
|
var lineStarts = GetLineStarts(content);
|
|
|
|
var line = Array.BinarySearch(lineStarts, index);
|
|
if (line < 0)
|
|
{
|
|
line = ~line - 1;
|
|
}
|
|
|
|
if (line < lineStarts.Length - 1 && lineStarts[line + 1] == index && index < content.Length && content[index] == '\n')
|
|
{
|
|
line++;
|
|
}
|
|
|
|
var column = index - lineStarts[line] + 1;
|
|
return new SourceLocation(line + 1, column);
|
|
}
|
|
|
|
public static SourceSpan GetSpan(Token token)
|
|
{
|
|
var start = IndexToLocation(token.SourceText.Content, token.StartIndex);
|
|
var end = IndexToLocation(token.SourceText.Content, token.EndIndex);
|
|
return new SourceSpan(start, end);
|
|
}
|
|
|
|
public static SourceSpan GetSpan(Node node)
|
|
{
|
|
if (!node.Tokens.Any())
|
|
{
|
|
throw new ArgumentException("Node has no tokens", nameof(node));
|
|
}
|
|
|
|
var firstToken = node.Tokens[0];
|
|
var lastToken = node.Tokens[^1];
|
|
|
|
var start = IndexToLocation(firstToken.SourceText.Content, firstToken.StartIndex);
|
|
var end = IndexToLocation(lastToken.SourceText.Content, lastToken.EndIndex);
|
|
|
|
return new SourceSpan(start, end);
|
|
}
|
|
} |