namespace NubLang.Diagnostics; public readonly struct SourceSpan : IEquatable, IComparable { public static SourceSpan Merge(params IEnumerable spans) { var spanArray = spans as SourceSpan[] ?? spans.ToArray(); if (spanArray.Length == 0) { return new SourceSpan(string.Empty, new SourceLocation(0, 0), new SourceLocation(0, 0)); } var minStart = spanArray.Min(s => s.Start); var maxEnd = spanArray.Max(s => s.End); return new SourceSpan(spanArray[0].FilePath, minStart, maxEnd); } public SourceSpan(string filePath, SourceLocation start, SourceLocation end) { if (start > end) { throw new ArgumentException("Start location cannot be after end location"); } FilePath = filePath; Start = start; End = end; } public string FilePath { get; } public SourceLocation Start { get; } public SourceLocation End { get; } public override string ToString() { if (Start == End) { return $"{FilePath}:{Start}"; } if (Start.Line == End.Line) { return Start.Column == End.Column ? $"{FilePath}:{Start}" : $"{FilePath}:{Start.Line}:{Start.Column}-{End.Column}"; } return $"{FilePath}:{Start}-{End}"; } public bool Equals(SourceSpan other) => Start == other.Start && End == other.End; public override bool Equals(object? obj) => obj is SourceSpan other && Equals(other); public override int GetHashCode() => HashCode.Combine(typeof(SourceSpan), Start, End); public static bool operator ==(SourceSpan left, SourceSpan right) => Equals(left, right); public static bool operator !=(SourceSpan left, SourceSpan right) => !Equals(left, right); public static bool operator <(SourceSpan left, SourceSpan right) => left.CompareTo(right) < 0; public static bool operator <=(SourceSpan left, SourceSpan right) => left.CompareTo(right) <= 0; public static bool operator >(SourceSpan left, SourceSpan right) => left.CompareTo(right) > 0; public static bool operator >=(SourceSpan left, SourceSpan right) => left.CompareTo(right) >= 0; public int CompareTo(SourceSpan other) { var startComparison = Start.CompareTo(other.Start); return startComparison != 0 ? startComparison : End.CompareTo(other.End); } } public readonly struct SourceLocation : IEquatable, IComparable { public SourceLocation(int line, int column) { Line = line; Column = column; } public int Line { get; } public int Column { get; } public override string ToString() { return $"{Line}:{Column}"; } public override bool Equals(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(typeof(SourceLocation), Line, Column); } public static bool operator ==(SourceLocation left, SourceLocation right) => Equals(left, right); public static bool operator !=(SourceLocation left, SourceLocation right) => !Equals(left, right); public static bool operator <(SourceLocation left, SourceLocation right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column); public static bool operator >(SourceLocation left, SourceLocation right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column); public static bool operator <=(SourceLocation left, SourceLocation right) => left.Line <= right.Line || (left.Line == right.Line && left.Column <= right.Column); public static bool operator >=(SourceLocation left, SourceLocation right) => left.Line >= right.Line || (left.Line == right.Line && left.Column >= right.Column); public int CompareTo(SourceLocation other) { var lineComparison = Line.CompareTo(other.Line); return lineComparison != 0 ? lineComparison : Column.CompareTo(other.Column); } }