using System.Diagnostics.CodeAnalysis; namespace StellaOps.PolicyDsl; /// /// Represents a precise source location within a policy DSL document. /// public readonly struct SourceLocation : IEquatable, IComparable { public SourceLocation(int offset, int line, int column) { if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (line < 1) { throw new ArgumentOutOfRangeException(nameof(line)); } if (column < 1) { throw new ArgumentOutOfRangeException(nameof(column)); } Offset = offset; Line = line; Column = column; } public int Offset { get; } public int Line { get; } public int Column { get; } public override string ToString() => $"(L{Line}, C{Column})"; public bool Equals(SourceLocation other) => Offset == other.Offset && Line == other.Line && Column == other.Column; public override bool Equals([NotNullWhen(true)] object? obj) => obj is SourceLocation other && Equals(other); public override int GetHashCode() => HashCode.Combine(Offset, Line, Column); public int CompareTo(SourceLocation other) => Offset.CompareTo(other.Offset); public static bool operator ==(SourceLocation left, SourceLocation right) => left.Equals(right); public static bool operator !=(SourceLocation left, SourceLocation right) => !left.Equals(right); public static bool operator <(SourceLocation left, SourceLocation right) => left.CompareTo(right) < 0; public static bool operator <=(SourceLocation left, SourceLocation right) => left.CompareTo(right) <= 0; public static bool operator >(SourceLocation left, SourceLocation right) => left.CompareTo(right) > 0; public static bool operator >=(SourceLocation left, SourceLocation right) => left.CompareTo(right) >= 0; } /// /// Represents a start/end location pair within a policy DSL source document. /// public readonly struct SourceSpan : IEquatable { public SourceSpan(SourceLocation start, SourceLocation end) { if (start.Offset > end.Offset) { throw new ArgumentException("Start must not be after end.", nameof(start)); } Start = start; End = end; } public SourceLocation Start { get; } public SourceLocation End { get; } public override string ToString() => $"{Start}->{End}"; public bool Equals(SourceSpan other) => Start.Equals(other.Start) && End.Equals(other.End); public override bool Equals([NotNullWhen(true)] object? obj) => obj is SourceSpan other && Equals(other); public override int GetHashCode() => HashCode.Combine(Start, End); public static SourceSpan Combine(SourceSpan first, SourceSpan second) { var start = first.Start <= second.Start ? first.Start : second.Start; var end = first.End >= second.End ? first.End : second.End; return new SourceSpan(start, end); } }