...
This commit is contained in:
102
src/lang/Nub.Lang/Frontend/Diagnostics/SourceText.cs
Normal file
102
src/lang/Nub.Lang/Frontend/Diagnostics/SourceText.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user