Files
nub-lang/src/compiler/Nub.Lang/Frontend/Diagnostics/SourceFile.cs
2025-05-25 01:27:11 +02:00

103 lines
2.8 KiB
C#

using System.Diagnostics.CodeAnalysis;
using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Parsing;
namespace Nub.Lang.Frontend.Diagnostics;
public readonly struct SourceFile(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.SourceFile.Content, token.StartIndex);
var end = IndexToLocation(token.SourceFile.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.SourceFile.Content, firstToken.StartIndex);
var end = IndexToLocation(lastToken.SourceFile.Content, lastToken.EndIndex);
return new SourceSpan(start, end);
}
}