This commit is contained in:
nub31
2025-05-31 23:48:29 +02:00
parent a098065136
commit 795b69df1b
14 changed files with 211 additions and 162 deletions

136
src/lang/Nub.Lang/Source.cs Normal file
View File

@@ -0,0 +1,136 @@
using System.Diagnostics.CodeAnalysis;
namespace Nub.Lang;
public readonly struct SourceSpan(SourceText content, SourceLocation start, SourceLocation end) : IEquatable<SourceSpan>
{
public SourceText Content { get; } = content;
public SourceLocation Start { get; } = start;
public SourceLocation End { get; } = end;
/// <summary>
/// Merges one or more <see cref="SourceSpan"/> from a single file to a single <see cref="SourceSpan"/>
/// </summary>
/// <param name="spanEnumerable">The spans to merged</param>
/// <returns>The merged <see cref="SourceSpan"/></returns>
public static SourceSpan Merge(IEnumerable<SourceSpan> spanEnumerable)
{
var spans = spanEnumerable.ToArray();
if (spans.Length == 0)
{
throw new ArgumentException("Cannot merge empty spans", nameof(spanEnumerable));
}
var files = spans.Select(s => s.Content).Distinct().ToArray();
if (files.Length > 1)
{
throw new ArgumentException("Cannot merge spans from multiple files", nameof(spanEnumerable));
}
var first = spans.OrderBy(s => s.Start.Line).ThenBy(s => s.Start.Column).First().Start;
var last = spans.OrderByDescending(s => s.End.Line).ThenByDescending(s => s.End.Column).Last().End;
return new SourceSpan(files[0], first, last);
}
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 override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is SourceSpan other && Equals(other);
}
public bool Equals(SourceSpan other)
{
return Content.Equals(other.Content) && Start.Equals(other.Start) && End.Equals(other.End);
}
public override int GetHashCode()
{
return HashCode.Combine(Content, Start, End);
}
public static bool operator ==(SourceSpan left, SourceSpan right)
{
return left.Equals(right);
}
public static bool operator !=(SourceSpan left, SourceSpan right)
{
return !(left == right);
}
}
public readonly struct SourceText(string text) : IEquatable<SourceText>
{
public string Text { get; } = text;
public bool Equals(SourceText other)
{
return Text == other.Text;
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is SourceText other && Equals(other);
}
public override int GetHashCode()
{
return Text.GetHashCode();
}
public static bool operator ==(SourceText left, SourceText right)
{
return left.Equals(right);
}
public static bool operator !=(SourceText left, SourceText right)
{
return !(left == right);
}
}
public readonly struct SourceLocation(int line, int column) : IEquatable<SourceLocation>
{
public int Line { get; } = line;
public int Column { get; } = column;
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is SourceLocation other && Equals(other);
}
public bool Equals(SourceLocation other)
{
return Line == other.Line && Column == other.Column;
}
public override int GetHashCode()
{
return HashCode.Combine(Line, Column);
}
public static bool operator ==(SourceLocation left, SourceLocation right)
{
return left.Equals(right);
}
public static bool operator !=(SourceLocation left, SourceLocation right)
{
return !(left == right);
}
}