...
This commit is contained in:
376
src/compiler/Nub.Lang/Frontend/Diagnostics/Diagnostics.cs
Normal file
376
src/compiler/Nub.Lang/Frontend/Diagnostics/Diagnostics.cs
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Nub.Lang.Frontend.Lexing;
|
||||||
|
using Nub.Lang.Frontend.Parsing;
|
||||||
|
|
||||||
|
namespace Nub.Lang.Frontend.Diagnostics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a source location with line and column information
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct SourceLocation(int line, int column, int index)
|
||||||
|
{
|
||||||
|
public int Line { get; } = line;
|
||||||
|
public int Column { get; } = column;
|
||||||
|
public int Index { get; } = index;
|
||||||
|
|
||||||
|
public override string ToString() => $"{Line}:{Column}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a span of source code
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct SourceSpan(SourceLocation start, SourceLocation end)
|
||||||
|
{
|
||||||
|
public SourceLocation Start { get; } = start;
|
||||||
|
public SourceLocation End { get; } = end;
|
||||||
|
|
||||||
|
public override string ToString() => $"{Start}-{End}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Severity levels for diagnostics
|
||||||
|
/// </summary>
|
||||||
|
public enum DiagnosticSeverity
|
||||||
|
{
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a diagnostic message with source location
|
||||||
|
/// </summary>
|
||||||
|
public class Diagnostic
|
||||||
|
{
|
||||||
|
public DiagnosticSeverity Severity { get; }
|
||||||
|
public string Message { get; }
|
||||||
|
public SourceFile SourceFile { get; }
|
||||||
|
public SourceSpan Span { get; }
|
||||||
|
public string? Help { get; }
|
||||||
|
|
||||||
|
public Diagnostic(DiagnosticSeverity severity, string message, SourceFile sourceFile,
|
||||||
|
SourceSpan span, string? help = null)
|
||||||
|
{
|
||||||
|
Severity = severity;
|
||||||
|
Message = message;
|
||||||
|
SourceFile = sourceFile;
|
||||||
|
Span = span;
|
||||||
|
Help = help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class for converting indices to line/column positions
|
||||||
|
/// </summary>
|
||||||
|
public static class SourceLocationCalculator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a character index to line/column position
|
||||||
|
/// </summary>
|
||||||
|
public static SourceLocation IndexToLocation(string content, int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index > content.Length)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int line = 1;
|
||||||
|
int column = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < index && i < content.Length; i++)
|
||||||
|
{
|
||||||
|
if (content[i] == '\n')
|
||||||
|
{
|
||||||
|
line++;
|
||||||
|
column = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
column++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SourceLocation(line, column, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the source span for a token
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the source span for a node (from first to last token)
|
||||||
|
/// </summary>
|
||||||
|
public static SourceSpan GetSpan(Node node)
|
||||||
|
{
|
||||||
|
if (!node.Tokens.Any())
|
||||||
|
throw new ArgumentException("Node has no tokens");
|
||||||
|
|
||||||
|
var firstToken = node.Tokens.First();
|
||||||
|
var lastToken = node.Tokens.Last();
|
||||||
|
|
||||||
|
var start = IndexToLocation(firstToken.SourceFile.Content, firstToken.StartIndex);
|
||||||
|
var end = IndexToLocation(lastToken.SourceFile.Content, lastToken.EndIndex);
|
||||||
|
|
||||||
|
return new SourceSpan(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builder for creating diagnostic messages
|
||||||
|
/// </summary>
|
||||||
|
public class DiagnosticBuilder
|
||||||
|
{
|
||||||
|
private DiagnosticSeverity _severity;
|
||||||
|
private string _message = string.Empty;
|
||||||
|
private SourceFile _sourceFile;
|
||||||
|
private SourceSpan _span;
|
||||||
|
private string? _help;
|
||||||
|
|
||||||
|
public static DiagnosticBuilder Error(string message) =>
|
||||||
|
new() { _severity = DiagnosticSeverity.Error, _message = message };
|
||||||
|
|
||||||
|
public static DiagnosticBuilder Warning(string message) =>
|
||||||
|
new() { _severity = DiagnosticSeverity.Warning, _message = message };
|
||||||
|
|
||||||
|
public static DiagnosticBuilder Info(string message) =>
|
||||||
|
new() { _severity = DiagnosticSeverity.Info, _message = message };
|
||||||
|
|
||||||
|
public DiagnosticBuilder At(Token token)
|
||||||
|
{
|
||||||
|
_sourceFile = token.SourceFile;
|
||||||
|
_span = SourceLocationCalculator.GetSpan(token);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticBuilder At(Node node)
|
||||||
|
{
|
||||||
|
if (!node.Tokens.Any())
|
||||||
|
throw new ArgumentException("Node has no tokens");
|
||||||
|
|
||||||
|
_sourceFile = node.Tokens.First().SourceFile;
|
||||||
|
_span = SourceLocationCalculator.GetSpan(node);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticBuilder At(SourceFile sourceFile, SourceSpan span)
|
||||||
|
{
|
||||||
|
_sourceFile = sourceFile;
|
||||||
|
_span = span;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticBuilder WithHelp(string help)
|
||||||
|
{
|
||||||
|
_help = help;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Diagnostic Build() => new(_severity, _message, _sourceFile, _span, _help);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formats diagnostic messages for display
|
||||||
|
/// </summary>
|
||||||
|
public class DiagnosticFormatter
|
||||||
|
{
|
||||||
|
private readonly DiagnosticFormatterOptions _options;
|
||||||
|
|
||||||
|
public DiagnosticFormatter(DiagnosticFormatterOptions? options = null)
|
||||||
|
{
|
||||||
|
_options = options ?? new DiagnosticFormatterOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format a diagnostic as a string
|
||||||
|
/// </summary>
|
||||||
|
public string Format(Diagnostic diagnostic)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Header line: severity, location, and message
|
||||||
|
sb.Append(GetSeverityPrefix(diagnostic.Severity));
|
||||||
|
sb.Append($" at {diagnostic.SourceFile.Path}:{diagnostic.Span}: ");
|
||||||
|
sb.AppendLine(diagnostic.Message);
|
||||||
|
|
||||||
|
// Show source context
|
||||||
|
if (_options.ShowSourceContext)
|
||||||
|
{
|
||||||
|
AppendSourceContext(sb, diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show help if available
|
||||||
|
if (!string.IsNullOrEmpty(diagnostic.Help))
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.Append(_options.HelpPrefix);
|
||||||
|
sb.AppendLine(diagnostic.Help);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format multiple diagnostics
|
||||||
|
/// </summary>
|
||||||
|
public string Format(IEnumerable<Diagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var diagnosticList = diagnostics.ToList();
|
||||||
|
|
||||||
|
for (int i = 0; i < diagnosticList.Count; i++)
|
||||||
|
{
|
||||||
|
sb.Append(Format(diagnosticList[i]));
|
||||||
|
|
||||||
|
if (i < diagnosticList.Count - 1)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSeverityPrefix(DiagnosticSeverity severity) => severity switch
|
||||||
|
{
|
||||||
|
DiagnosticSeverity.Error => _options.ErrorPrefix,
|
||||||
|
DiagnosticSeverity.Warning => _options.WarningPrefix,
|
||||||
|
DiagnosticSeverity.Info => _options.InfoPrefix,
|
||||||
|
_ => "diagnostic"
|
||||||
|
};
|
||||||
|
|
||||||
|
private void AppendSourceContext(StringBuilder sb, Diagnostic diagnostic)
|
||||||
|
{
|
||||||
|
var lines = diagnostic.SourceFile.Content.Split('\n');
|
||||||
|
var startLine = diagnostic.Span.Start.Line;
|
||||||
|
var endLine = diagnostic.Span.End.Line;
|
||||||
|
|
||||||
|
// Calculate line number width for padding
|
||||||
|
var maxLineNum = Math.Min(endLine + _options.ContextLines, lines.Length);
|
||||||
|
var lineNumWidth = maxLineNum.ToString().Length;
|
||||||
|
|
||||||
|
// Show context before error
|
||||||
|
var contextStart = Math.Max(1, startLine - _options.ContextLines);
|
||||||
|
for (int lineNum = contextStart; lineNum < startLine; lineNum++)
|
||||||
|
{
|
||||||
|
if (lineNum <= lines.Length)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{lineNum.ToString().PadLeft(lineNumWidth)} | {lines[lineNum - 1]}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show error lines with highlighting
|
||||||
|
for (int lineNum = startLine; lineNum <= endLine && lineNum <= lines.Length; lineNum++)
|
||||||
|
{
|
||||||
|
var line = lines[lineNum - 1];
|
||||||
|
sb.AppendLine($"{lineNum.ToString().PadLeft(lineNumWidth)} | {line}");
|
||||||
|
|
||||||
|
// Add error indicators
|
||||||
|
if (_options.ShowErrorIndicators)
|
||||||
|
{
|
||||||
|
sb.Append(new string(' ', lineNumWidth + 3)); // Padding for line number + " | "
|
||||||
|
|
||||||
|
if (lineNum == startLine && lineNum == endLine)
|
||||||
|
{
|
||||||
|
// Single line error
|
||||||
|
var startCol = Math.Max(0, diagnostic.Span.Start.Column - 1);
|
||||||
|
var endCol = Math.Min(line.Length, diagnostic.Span.End.Column - 1);
|
||||||
|
var length = Math.Max(1, endCol - startCol);
|
||||||
|
|
||||||
|
sb.Append(new string(' ', startCol));
|
||||||
|
sb.AppendLine(new string(_options.ErrorIndicatorChar, length));
|
||||||
|
}
|
||||||
|
else if (lineNum == startLine)
|
||||||
|
{
|
||||||
|
// First line of multi-line error
|
||||||
|
var startCol = Math.Max(0, diagnostic.Span.Start.Column - 1);
|
||||||
|
sb.Append(new string(' ', startCol));
|
||||||
|
sb.AppendLine(new string(_options.ErrorIndicatorChar, line.Length - startCol));
|
||||||
|
}
|
||||||
|
else if (lineNum == endLine)
|
||||||
|
{
|
||||||
|
// Last line of multi-line error
|
||||||
|
var endCol = Math.Min(line.Length, diagnostic.Span.End.Column - 1);
|
||||||
|
sb.AppendLine(new string(_options.ErrorIndicatorChar, endCol));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Middle line of multi-line error
|
||||||
|
sb.AppendLine(new string(_options.ErrorIndicatorChar, line.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show context after error
|
||||||
|
var contextEnd = Math.Min(lines.Length, endLine + _options.ContextLines);
|
||||||
|
for (int lineNum = endLine + 1; lineNum <= contextEnd; lineNum++)
|
||||||
|
{
|
||||||
|
if (lineNum <= lines.Length)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{lineNum.ToString().PadLeft(lineNumWidth)} | {lines[lineNum - 1]}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration options for diagnostic formatting
|
||||||
|
/// </summary>
|
||||||
|
public class DiagnosticFormatterOptions
|
||||||
|
{
|
||||||
|
public string ErrorPrefix { get; set; } = "error";
|
||||||
|
public string WarningPrefix { get; set; } = "warning";
|
||||||
|
public string InfoPrefix { get; set; } = "info";
|
||||||
|
public string HelpPrefix { get; set; } = "help: ";
|
||||||
|
|
||||||
|
public bool ShowSourceContext { get; set; } = true;
|
||||||
|
public bool ShowErrorIndicators { get; set; } = true;
|
||||||
|
public char ErrorIndicatorChar { get; set; } = '^';
|
||||||
|
public int ContextLines { get; set; } = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for convenient error reporting
|
||||||
|
/// </summary>
|
||||||
|
public static class ErrorReportingExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create an error diagnostic for a token
|
||||||
|
/// </summary>
|
||||||
|
public static Diagnostic Error(this Token token, string message, string? help = null) =>
|
||||||
|
DiagnosticBuilder.Error(message).At(token).WithHelp(help).Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a warning diagnostic for a token
|
||||||
|
/// </summary>
|
||||||
|
public static Diagnostic Warning(this Token token, string message, string? help = null) =>
|
||||||
|
DiagnosticBuilder.Warning(message).At(token).WithHelp(help).Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an info diagnostic for a token
|
||||||
|
/// </summary>
|
||||||
|
public static Diagnostic Info(this Token token, string message, string? help = null) =>
|
||||||
|
DiagnosticBuilder.Info(message).At(token).WithHelp(help).Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an error diagnostic for a node
|
||||||
|
/// </summary>
|
||||||
|
public static Diagnostic Error(this Node node, string message, string? help = null) =>
|
||||||
|
DiagnosticBuilder.Error(message).At(node).WithHelp(help).Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a warning diagnostic for a node
|
||||||
|
/// </summary>
|
||||||
|
public static Diagnostic Warning(this Node node, string message, string? help = null) =>
|
||||||
|
DiagnosticBuilder.Warning(message).At(node).WithHelp(help).Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an info diagnostic for a node
|
||||||
|
/// </summary>
|
||||||
|
public static Diagnostic Info(this Node node, string message, string? help = null) =>
|
||||||
|
DiagnosticBuilder.Info(message).At(node).WithHelp(help).Build();
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Nub.Lang.Frontend.Lexing;
|
namespace Nub.Lang.Frontend.Lexing;
|
||||||
|
|
||||||
public class DocumentationToken(string filePath, int startIndex, int endIndex, string documentation) : Token(filePath, startIndex, endIndex)
|
public class DocumentationToken(SourceFile sourceFile, int startIndex, int endIndex, string documentation) : Token(sourceFile, startIndex, endIndex)
|
||||||
{
|
{
|
||||||
public string Documentation { get; } = documentation;
|
public string Documentation { get; } = documentation;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Nub.Lang.Frontend.Lexing;
|
namespace Nub.Lang.Frontend.Lexing;
|
||||||
|
|
||||||
public class IdentifierToken(string filePath, int startIndex, int endIndex, string value) : Token(filePath, startIndex, endIndex)
|
public class IdentifierToken(SourceFile sourceFile, int startIndex, int endIndex, string value) : Token(sourceFile, startIndex, endIndex)
|
||||||
{
|
{
|
||||||
public string Value { get; } = value;
|
public string Value { get; } = value;
|
||||||
}
|
}
|
||||||
@@ -55,13 +55,13 @@ public class Lexer
|
|||||||
};
|
};
|
||||||
|
|
||||||
private string _src = null!;
|
private string _src = null!;
|
||||||
private string _filePath = null!;
|
private SourceFile _sourceFile;
|
||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
public List<Token> Lex(string src, string filePath)
|
public List<Token> Lex(string src, SourceFile sourceFile)
|
||||||
{
|
{
|
||||||
_src = src;
|
_src = src;
|
||||||
_filePath = filePath;
|
_sourceFile = sourceFile;
|
||||||
_index = 0;
|
_index = 0;
|
||||||
|
|
||||||
List<Token> tokens = [];
|
List<Token> tokens = [];
|
||||||
@@ -121,7 +121,7 @@ public class Lexer
|
|||||||
|
|
||||||
if (documentation != null)
|
if (documentation != null)
|
||||||
{
|
{
|
||||||
return new DocumentationToken(_filePath, startIndex, _index, documentation);
|
return new DocumentationToken(_sourceFile, startIndex, _index, documentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsumeWhitespace();
|
ConsumeWhitespace();
|
||||||
@@ -144,20 +144,20 @@ public class Lexer
|
|||||||
|
|
||||||
if (Keywords.TryGetValue(buffer, out var keywordSymbol))
|
if (Keywords.TryGetValue(buffer, out var keywordSymbol))
|
||||||
{
|
{
|
||||||
return new SymbolToken(_filePath, startIndex, _index, keywordSymbol);
|
return new SymbolToken(_sourceFile, startIndex, _index, keywordSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Modifiers.TryGetValue(buffer, out var modifer))
|
if (Modifiers.TryGetValue(buffer, out var modifer))
|
||||||
{
|
{
|
||||||
return new ModifierToken(_filePath, startIndex, _index, modifer);
|
return new ModifierToken(_sourceFile, startIndex, _index, modifer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer is "true" or "false")
|
if (buffer is "true" or "false")
|
||||||
{
|
{
|
||||||
return new LiteralToken(_filePath, startIndex, _index, NubPrimitiveType.Bool, buffer);
|
return new LiteralToken(_sourceFile, startIndex, _index, NubPrimitiveType.Bool, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IdentifierToken(_filePath, startIndex, _index, buffer);
|
return new IdentifierToken(_sourceFile, startIndex, _index, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char.IsDigit(current))
|
if (char.IsDigit(current))
|
||||||
@@ -195,7 +195,7 @@ public class Lexer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LiteralToken(_filePath, startIndex, _index, isFloat ? NubPrimitiveType.F64 : NubPrimitiveType.I64, buffer);
|
return new LiteralToken(_sourceFile, startIndex, _index, isFloat ? NubPrimitiveType.F64 : NubPrimitiveType.I64, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Revisit this
|
// TODO: Revisit this
|
||||||
@@ -215,7 +215,7 @@ public class Lexer
|
|||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SymbolToken(_filePath, startIndex, _index, chain.Value);
|
return new SymbolToken(_sourceFile, startIndex, _index, chain.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ public class Lexer
|
|||||||
if (Chars.TryGetValue(current, out var charSymbol))
|
if (Chars.TryGetValue(current, out var charSymbol))
|
||||||
{
|
{
|
||||||
Next();
|
Next();
|
||||||
return new SymbolToken(_filePath, startIndex, _index, charSymbol);
|
return new SymbolToken(_sourceFile, startIndex, _index, charSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current == '"')
|
if (current == '"')
|
||||||
@@ -248,7 +248,7 @@ public class Lexer
|
|||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LiteralToken(_filePath, startIndex, _index, NubPrimitiveType.String, buffer);
|
return new LiteralToken(_sourceFile, startIndex, _index, NubPrimitiveType.String, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception($"Unknown character {current}");
|
throw new Exception($"Unknown character {current}");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Nub.Lang.Frontend.Lexing;
|
namespace Nub.Lang.Frontend.Lexing;
|
||||||
|
|
||||||
public class LiteralToken(string filePath, int startIndex, int endIndex, NubType type, string value) : Token(filePath, startIndex, endIndex)
|
public class LiteralToken(SourceFile sourceFile, int startIndex, int endIndex, NubType type, string value) : Token(sourceFile, startIndex, endIndex)
|
||||||
{
|
{
|
||||||
public NubType Type { get; } = type;
|
public NubType Type { get; } = type;
|
||||||
public string Value { get; } = value;
|
public string Value { get; } = value;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Nub.Lang.Frontend.Lexing;
|
namespace Nub.Lang.Frontend.Lexing;
|
||||||
|
|
||||||
public class ModifierToken(string filePath, int startIndex, int endIndex, Modifier modifier) : Token(filePath, startIndex, endIndex)
|
public class ModifierToken(SourceFile sourceFile, int startIndex, int endIndex, Modifier modifier) : Token(sourceFile, startIndex, endIndex)
|
||||||
{
|
{
|
||||||
public Modifier Modifier { get; } = modifier;
|
public Modifier Modifier { get; } = modifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Nub.Lang.Frontend.Lexing;
|
namespace Nub.Lang.Frontend.Lexing;
|
||||||
|
|
||||||
public class SymbolToken(string filePath, int startIndex, int endIndex, Symbol symbol) : Token(filePath, startIndex, endIndex)
|
public class SymbolToken(SourceFile sourceFile, int startIndex, int endIndex, Symbol symbol) : Token(sourceFile, startIndex, endIndex)
|
||||||
{
|
{
|
||||||
public Symbol Symbol { get; } = symbol;
|
public Symbol Symbol { get; } = symbol;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
namespace Nub.Lang.Frontend.Lexing;
|
namespace Nub.Lang.Frontend.Lexing;
|
||||||
|
|
||||||
public abstract class Token(string filePath, int startIndex, int endIndex)
|
public abstract class Token(SourceFile sourceFile, int startIndex, int endIndex)
|
||||||
{
|
{
|
||||||
public string FilePath { get; } = filePath;
|
public SourceFile SourceFile { get; } = sourceFile;
|
||||||
public int StartIndex { get; } = startIndex;
|
public int StartIndex { get; } = startIndex;
|
||||||
public int EndIndex { get; } = endIndex;
|
public int EndIndex { get; } = endIndex;
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ internal static class Program
|
|||||||
foreach (var filePath in filePaths)
|
foreach (var filePath in filePaths)
|
||||||
{
|
{
|
||||||
var src = File.ReadAllText(filePath);
|
var src = File.ReadAllText(filePath);
|
||||||
tokens.AddRange(Lexer.Lex(src, filePath));
|
tokens.AddRange(Lexer.Lex(src, new SourceFile(filePath, src)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var module = Parser.ParseModule(tokens, rootFilePath);
|
var module = Parser.ParseModule(tokens, rootFilePath);
|
||||||
|
|||||||
7
src/compiler/Nub.Lang/SourceFile.cs
Normal file
7
src/compiler/Nub.Lang/SourceFile.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Nub.Lang;
|
||||||
|
|
||||||
|
public readonly struct SourceFile(string path, string content)
|
||||||
|
{
|
||||||
|
public string Path { get; } = path;
|
||||||
|
public string Content { get; } = content;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user