This commit is contained in:
nub31
2025-11-03 17:10:15 +01:00
parent 7d49bf43b7
commit 47fef6bc9f
7 changed files with 175 additions and 210 deletions

View File

@@ -11,6 +11,7 @@ public class Diagnostic
private readonly string _message;
private SourceSpan? _span;
private string? _help;
private List<Token>? _tokens;
public DiagnosticBuilder(DiagnosticSeverity severity, string message)
{
@@ -18,18 +19,32 @@ public class Diagnostic
_message = message;
}
public DiagnosticBuilder At(SyntaxNode? node)
public DiagnosticBuilder At(SyntaxNode? node, List<Token>? tokens = null)
{
if (tokens != null)
{
_tokens = tokens;
}
if (node != null)
{
_span = SourceSpan.Merge(node.Tokens.Select(x => x.Span));
var first = node.Tokens.FirstOrDefault();
if (first != null)
{
_span = SourceSpan.Merge(node.Tokens.Select(x => x.Span));
}
}
return this;
}
public DiagnosticBuilder At(Token? token)
public DiagnosticBuilder At(Token? token, List<Token>? tokens = null)
{
if (tokens != null)
{
_tokens = tokens;
}
if (token != null)
{
At(token.Span);
@@ -48,11 +63,11 @@ public class Diagnostic
return this;
}
public DiagnosticBuilder At(string filePath, int line, int column)
{
_span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column));
return this;
}
// public DiagnosticBuilder At(string filePath, int line, int column)
// {
// _span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column));
// return this;
// }
public DiagnosticBuilder WithHelp(string help)
{
@@ -60,20 +75,23 @@ public class Diagnostic
return this;
}
public Diagnostic Build() => new(_severity, _message, _help, _span);
public Diagnostic Build() => new(_severity, _message, _help, _span, _tokens);
}
public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message);
public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message);
public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message);
private readonly List<Token>? _tokens;
public DiagnosticSeverity Severity { get; }
public string Message { get; }
public string? Help { get; }
public SourceSpan? Span { get; }
private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span)
private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span, List<Token>? tokens)
{
_tokens = tokens;
Severity = severity;
Message = message;
Help = help;
@@ -103,15 +121,12 @@ public class Diagnostic
if (Span.HasValue)
{
sb.AppendLine();
var text = File.ReadAllText(Span.Value.FilePath);
var tokenizer = new Tokenizer();
var tokens = tokenizer.Tokenize(Span.Value.FilePath, text);
var text = Span.Value.Source;
var lines = text.Split('\n');
var startLine = Span.Value.Start.Line;
var endLine = Span.Value.End.Line;
var startLine = Span.Value.StartLine;
var endLine = Span.Value.EndLine;
const int CONTEXT_LINES = 3;
@@ -144,8 +159,15 @@ public class Diagnostic
sb.Append("│ ");
sb.Append(i.ToString().PadRight(numberPadding));
sb.Append(" │ ");
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokens));
// sb.Append(line.PadRight(codePadding));
if (_tokens != null)
{
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, _tokens));
}
else
{
sb.Append(line.PadRight(codePadding));
}
sb.Append(" │");
sb.AppendLine();
@@ -156,12 +178,12 @@ public class Diagnostic
if (i == startLine)
{
markerStartColumn = Span.Value.Start.Column;
markerStartColumn = Span.Value.StartColumn;
}
if (i == endLine)
{
markerEndColumn = Span.Value.End.Column;
markerEndColumn = Span.Value.EndColumn;
}
var markerLength = markerEndColumn - markerStartColumn;
@@ -206,8 +228,8 @@ public class Diagnostic
{
var sb = new StringBuilder();
var lineTokens = tokens
.Where(t => t.Span.Start.Line == lineNumber)
.OrderBy(t => t.Span.Start.Column)
.Where(t => t.Span.StartLine == lineNumber)
.OrderBy(t => t.Span.StartColumn)
.ToList();
if (lineTokens.Count == 0)
@@ -219,8 +241,10 @@ public class Diagnostic
foreach (var token in lineTokens)
{
var tokenStart = token.Span.Start.Column;
var tokenEnd = token.Span.End.Column;
if (token is WhitespaceToken) continue;
var tokenStart = token.Span.StartColumn;
var tokenEnd = token.Span.EndColumn;
if (tokenStart > currentColumn && currentColumn - 1 < line.Length)
{

View File

@@ -1,112 +1,56 @@
namespace NubLang.Diagnostics;
public readonly struct SourceSpan : IEquatable<SourceSpan>, IComparable<SourceSpan>
public readonly struct SourceSpan
{
private readonly int _startIndex;
private readonly int _endIndex;
public static SourceSpan Merge(params IEnumerable<SourceSpan> 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));
return new SourceSpan(string.Empty, string.Empty, 0, 0, 0, 0, 0, 0);
}
var minStart = spanArray.Min(s => s.Start);
var maxEnd = spanArray.Max(s => s.End);
var first = spanArray.MinBy(x => x._startIndex);
var last = spanArray.MaxBy(x => x._endIndex);
return new SourceSpan(spanArray[0].FilePath, minStart, maxEnd);
return new SourceSpan(first.SourcePath, first.Source, first._startIndex, last._endIndex, first.StartLine, last.EndLine, first.StartColumn, last.EndColumn);
}
public SourceSpan(string filePath, SourceLocation start, SourceLocation end)
public SourceSpan(string sourcePath, string source, int startIndex, int endIndex, int startLine, int startColumn, int endLine, int endColumn)
{
if (start > end)
{
throw new ArgumentException("Start location cannot be after end location");
}
FilePath = filePath;
Start = start;
End = end;
_startIndex = startIndex;
_endIndex = endIndex;
SourcePath = sourcePath;
Source = source;
StartLine = startLine;
StartColumn = startColumn;
EndLine = endLine;
EndColumn = endColumn;
}
public string FilePath { get; }
public SourceLocation Start { get; }
public SourceLocation End { get; }
public int StartLine { get; }
public int StartColumn { get; }
public int EndLine { get; }
public int EndColumn { get; }
public string SourcePath { get; }
public string Source { get; }
public override string ToString()
{
if (Start == End)
if (StartLine == EndLine && StartColumn == EndColumn)
{
return $"{FilePath}:{Start}";
return $"{SourcePath}:{StartColumn}:{StartColumn}";
}
if (Start.Line == End.Line)
if (StartLine == EndLine)
{
return Start.Column == End.Column ? $"{FilePath}:{Start}" : $"{FilePath}:{Start.Line}:{Start.Column}-{End.Column}";
return $"{SourcePath}:{StartLine}:{StartColumn}-{EndColumn}";
}
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<SourceLocation>, IComparable<SourceLocation>
{
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);
return $"{SourcePath}:{StartLine}:{StartColumn}-{EndLine}:{EndColumn}";
}
}