Compare commits
3 Commits
00714ea4b0
...
b31a2d01c6
| Author | SHA1 | Date | |
|---|---|---|---|
| b31a2d01c6 | |||
| 6f03e2203f | |||
| e20f6cd7af |
154
compiler/Compiler/Diagnostic.cs
Normal file
154
compiler/Compiler/Diagnostic.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
namespace Compiler;
|
||||||
|
|
||||||
|
public sealed class Diagnostic(DiagnosticSeverity severity, string message, string? help, FileInfo? file)
|
||||||
|
{
|
||||||
|
public static DiagnosticBuilder Info(string message) => new DiagnosticBuilder(DiagnosticSeverity.Info, message);
|
||||||
|
public static DiagnosticBuilder Warning(string message) => new DiagnosticBuilder(DiagnosticSeverity.Warning, message);
|
||||||
|
public static DiagnosticBuilder Error(string message) => new DiagnosticBuilder(DiagnosticSeverity.Error, message);
|
||||||
|
|
||||||
|
public readonly DiagnosticSeverity Severity = severity;
|
||||||
|
public readonly string Message = message;
|
||||||
|
public readonly string? Help = help;
|
||||||
|
public readonly FileInfo? File = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DiagnosticBuilder(DiagnosticSeverity severity, string message)
|
||||||
|
{
|
||||||
|
private FileInfo? file;
|
||||||
|
private string? help;
|
||||||
|
|
||||||
|
public DiagnosticBuilder At(string fileName, int line, int column, int length)
|
||||||
|
{
|
||||||
|
file = new FileInfo(fileName, line, column, length);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticBuilder At(string fileName, Token? token)
|
||||||
|
{
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
At(fileName, token.Line, token.Column, token.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticBuilder At(string fileName, Node? node)
|
||||||
|
{
|
||||||
|
if (node != null && node.Tokens.Count != 0)
|
||||||
|
{
|
||||||
|
// todo(nub31): Calculate length based on last token
|
||||||
|
At(fileName, node.Tokens[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiagnosticBuilder WithHelp(string helpMessage)
|
||||||
|
{
|
||||||
|
help = helpMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Diagnostic Build()
|
||||||
|
{
|
||||||
|
return new Diagnostic(severity, message, help, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class FileInfo(string file, int line, int column, int length)
|
||||||
|
{
|
||||||
|
public readonly string File = file;
|
||||||
|
public readonly int Line = line;
|
||||||
|
public readonly int Column = column;
|
||||||
|
public readonly int Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DiagnosticSeverity
|
||||||
|
{
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CompileException(Diagnostic diagnostic) : Exception
|
||||||
|
{
|
||||||
|
public readonly Diagnostic Diagnostic = diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DiagnosticFormatter
|
||||||
|
{
|
||||||
|
public static void Print(Diagnostic diagnostic, TextWriter writer)
|
||||||
|
{
|
||||||
|
var (label, color) = diagnostic.Severity switch
|
||||||
|
{
|
||||||
|
DiagnosticSeverity.Info => ("info", Ansi.Cyan),
|
||||||
|
DiagnosticSeverity.Warning => ("warning", Ansi.Yellow),
|
||||||
|
DiagnosticSeverity.Error => ("error", Ansi.Red),
|
||||||
|
_ => ("unknown", Ansi.Reset),
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.Write(color);
|
||||||
|
writer.Write(label);
|
||||||
|
writer.Write(Ansi.Reset);
|
||||||
|
writer.Write(": ");
|
||||||
|
writer.WriteLine(diagnostic.Message);
|
||||||
|
|
||||||
|
if (diagnostic.File is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var file = diagnostic.File;
|
||||||
|
var lineNumberWidth = diagnostic.File.Line.ToString().Length;
|
||||||
|
|
||||||
|
writer.WriteLine($" {new string(' ', lineNumberWidth)}{file.File}:{file.Line}:{file.Column}");
|
||||||
|
writer.WriteLine($"{new string(' ', lineNumberWidth)} | ");
|
||||||
|
|
||||||
|
var sourceLine = TryReadLine(file.File, file.Line);
|
||||||
|
if (sourceLine != null)
|
||||||
|
{
|
||||||
|
writer.Write($"{file.Line.ToString().PadLeft(lineNumberWidth)} | ");
|
||||||
|
writer.WriteLine(sourceLine);
|
||||||
|
|
||||||
|
writer.Write(new string(' ', lineNumberWidth));
|
||||||
|
writer.Write(" | ");
|
||||||
|
writer.Write(new string(' ', file.Column - 1));
|
||||||
|
writer.Write(color);
|
||||||
|
writer.Write(new string('^', Math.Max(1, file.Length)));
|
||||||
|
writer.WriteLine(Ansi.Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine($"{new string(' ', lineNumberWidth)} |");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(diagnostic.Help))
|
||||||
|
{
|
||||||
|
writer.WriteLine($" = help: {diagnostic.Help}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? TryReadLine(string file, int line)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(file);
|
||||||
|
for (var i = 1; i < line; i++)
|
||||||
|
{
|
||||||
|
if (reader.ReadLine() == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader.ReadLine();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Ansi
|
||||||
|
{
|
||||||
|
public const string Reset = "\e[0m";
|
||||||
|
public const string Red = "\e[31m";
|
||||||
|
public const string Yellow = "\e[33m";
|
||||||
|
public const string Cyan = "\e[36m";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -185,10 +185,11 @@ public sealed class Generator(List<NodeDefinition> nodes)
|
|||||||
return node switch
|
return node switch
|
||||||
{
|
{
|
||||||
NodeExpressionBinary expression => EmitExpressionBinary(expression),
|
NodeExpressionBinary expression => EmitExpressionBinary(expression),
|
||||||
|
NodeExpressionUnary expression => EmitExpressionUnary(expression),
|
||||||
NodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false",
|
NodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false",
|
||||||
NodeExpressionIntLiteral expression => expression.Value.Value.ToString(),
|
NodeExpressionIntLiteral expression => expression.Value.Value.ToString(),
|
||||||
NodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
|
NodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
|
||||||
NodeExpressionStructLiteral expression => EmitStructLiteral(expression),
|
NodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
|
||||||
NodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
|
NodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
|
||||||
NodeExpressionIdent expression => expression.Value.Ident,
|
NodeExpressionIdent expression => expression.Value.Ident,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
||||||
@@ -221,23 +222,19 @@ public sealed class Generator(List<NodeDefinition> nodes)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CType(NodeType node, string? varName = null)
|
private string EmitExpressionUnary(NodeExpressionUnary expression)
|
||||||
{
|
{
|
||||||
return node switch
|
var target = EmitExpression(expression.Target);
|
||||||
|
|
||||||
|
return expression.Operation switch
|
||||||
{
|
{
|
||||||
NodeTypeVoid => "void" + (varName != null ? $" {varName}" : ""),
|
NodeExpressionUnary.Op.Negate => $"(-{target})",
|
||||||
NodeTypeBool => "bool" + (varName != null ? $" {varName}" : ""),
|
NodeExpressionUnary.Op.Invert => $"(!{target})",
|
||||||
NodeTypeCustom type => $"struct {type.Name.Ident}" + (varName != null ? $" {varName}" : ""),
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
NodeTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""),
|
|
||||||
NodeTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""),
|
|
||||||
NodeTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"),
|
|
||||||
NodeTypeString => "struct string" + (varName != null ? $" {varName}" : ""),
|
|
||||||
NodeTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})",
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitStructLiteral(NodeExpressionStructLiteral expression)
|
private string EmitExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
||||||
{
|
{
|
||||||
var initializerValues = new Dictionary<string, string>();
|
var initializerValues = new Dictionary<string, string>();
|
||||||
|
|
||||||
@@ -257,6 +254,22 @@ public sealed class Generator(List<NodeDefinition> nodes)
|
|||||||
var target = EmitExpression(expression.Target);
|
var target = EmitExpression(expression.Target);
|
||||||
return $"{target}.{expression.Name.Ident}";
|
return $"{target}.{expression.Name.Ident}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string CType(NodeType node, string? varName = null)
|
||||||
|
{
|
||||||
|
return node switch
|
||||||
|
{
|
||||||
|
NodeTypeVoid => "void" + (varName != null ? $" {varName}" : ""),
|
||||||
|
NodeTypeBool => "bool" + (varName != null ? $" {varName}" : ""),
|
||||||
|
NodeTypeCustom type => $"struct {type.Name.Ident}" + (varName != null ? $" {varName}" : ""),
|
||||||
|
NodeTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""),
|
||||||
|
NodeTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""),
|
||||||
|
NodeTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"),
|
||||||
|
NodeTypeString => "struct string" + (varName != null ? $" {varName}" : ""),
|
||||||
|
NodeTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class IndentedTextWriter
|
internal class IndentedTextWriter
|
||||||
|
|||||||
@@ -2,21 +2,31 @@
|
|||||||
|
|
||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class Parser(List<Token> tokens)
|
public sealed class Parser(string fileName, List<Token> tokens)
|
||||||
{
|
{
|
||||||
public static List<NodeDefinition> Parse(List<Token> tokens)
|
public static List<NodeDefinition> Parse(string fileName, List<Token> tokens, out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
return new Parser(tokens).Parse();
|
return new Parser(fileName, tokens).Parse(out diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
private List<NodeDefinition> Parse()
|
private List<NodeDefinition> Parse(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var nodes = new List<NodeDefinition>();
|
var nodes = new List<NodeDefinition>();
|
||||||
|
diagnostics = [];
|
||||||
|
|
||||||
while (Peek() != null)
|
try
|
||||||
nodes.Add(ParseDefinition());
|
{
|
||||||
|
while (Peek() != null)
|
||||||
|
{
|
||||||
|
nodes.Add(ParseDefinition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
@@ -66,7 +76,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
|
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Not a valid definition");
|
throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeStatement ParseStatement()
|
private NodeStatement ParseStatement()
|
||||||
@@ -135,7 +145,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
return new NodeStatementAssignment(TokensFrom(startIndex), target, value);
|
return new NodeStatementAssignment(TokensFrom(startIndex), target, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Not a valid followup for expression statement");
|
throw new CompileException(Diagnostic.Error("Cannot use expression in statement context unless called as a function or used in assignment").At(fileName, target).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeExpression ParseExpression(int minPrecedence = -1)
|
private NodeExpression ParseExpression(int minPrecedence = -1)
|
||||||
@@ -198,6 +208,16 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
ExpectSymbol(Symbol.CloseParen);
|
ExpectSymbol(Symbol.CloseParen);
|
||||||
expr = value;
|
expr = value;
|
||||||
}
|
}
|
||||||
|
else if (TryExpectSymbol(Symbol.Minus))
|
||||||
|
{
|
||||||
|
var target = ParseExpression();
|
||||||
|
expr = new NodeExpressionUnary(TokensFrom(startIndex), target, NodeExpressionUnary.Op.Negate);
|
||||||
|
}
|
||||||
|
else if (TryExpectSymbol(Symbol.Bang))
|
||||||
|
{
|
||||||
|
var target = ParseExpression();
|
||||||
|
expr = new NodeExpressionUnary(TokensFrom(startIndex), target, NodeExpressionUnary.Op.Invert);
|
||||||
|
}
|
||||||
else if (TryExpectIntLiteral(out var intLiteral))
|
else if (TryExpectIntLiteral(out var intLiteral))
|
||||||
{
|
{
|
||||||
expr = new NodeExpressionIntLiteral(TokensFrom(startIndex), intLiteral);
|
expr = new NodeExpressionIntLiteral(TokensFrom(startIndex), intLiteral);
|
||||||
@@ -233,7 +253,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("Not a valid expression leaf");
|
throw new CompileException(Diagnostic.Error("Expected start of expression").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Period))
|
if (TryExpectSymbol(Symbol.Period))
|
||||||
@@ -302,7 +322,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Not a valid type");
|
throw new CompileException(Diagnostic.Error("Expected type").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Token> TokensFrom(int startIndex)
|
private List<Token> TokensFrom(int startIndex)
|
||||||
@@ -329,7 +349,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception($"Expected symbol '{symbol}'");
|
throw new CompileException(Diagnostic.Error($"Expected '{symbol.AsString()}'").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryExpectSymbol(Symbol symbol)
|
private bool TryExpectSymbol(Symbol symbol)
|
||||||
@@ -351,7 +371,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Expected ident");
|
throw new CompileException(Diagnostic.Error("Expected identifier").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryExpectIdent([NotNullWhen(true)] out TokenIdent? ident)
|
private bool TryExpectIdent([NotNullWhen(true)] out TokenIdent? ident)
|
||||||
@@ -409,7 +429,7 @@ public sealed class Parser(List<Token> tokens)
|
|||||||
private Token Consume()
|
private Token Consume()
|
||||||
{
|
{
|
||||||
if (index >= tokens.Count)
|
if (index >= tokens.Count)
|
||||||
throw new Exception("End of tokens");
|
throw new CompileException(Diagnostic.Error("Unexpected end of tokens").At(fileName, Peek()).Build());
|
||||||
|
|
||||||
return tokens[index++];
|
return tokens[index++];
|
||||||
}
|
}
|
||||||
@@ -607,7 +627,6 @@ public sealed class NodeExpressionBinary(List<Token> tokens, NodeExpression left
|
|||||||
public readonly Op Operation = operation;
|
public readonly Op Operation = operation;
|
||||||
public readonly NodeExpression Right = right;
|
public readonly NodeExpression Right = right;
|
||||||
|
|
||||||
|
|
||||||
public enum Op
|
public enum Op
|
||||||
{
|
{
|
||||||
Add,
|
Add,
|
||||||
@@ -635,6 +654,18 @@ public sealed class NodeExpressionBinary(List<Token> tokens, NodeExpression left
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class NodeExpressionUnary(List<Token> tokens, NodeExpression target, NodeExpressionUnary.Op op) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public NodeExpression Target { get; } = target;
|
||||||
|
public Op Operation { get; } = op;
|
||||||
|
|
||||||
|
public enum Op
|
||||||
|
{
|
||||||
|
Negate,
|
||||||
|
Invert,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class NodeType(List<Token> tokens) : Node(tokens);
|
public abstract class NodeType(List<Token> tokens) : Node(tokens);
|
||||||
|
|
||||||
public sealed class NodeTypeVoid(List<Token> tokens) : NodeType(tokens);
|
public sealed class NodeTypeVoid(List<Token> tokens) : NodeType(tokens);
|
||||||
|
|||||||
@@ -1,42 +1,33 @@
|
|||||||
using Compiler;
|
using Compiler;
|
||||||
|
|
||||||
const string contents = """
|
var file = File.ReadAllText("test.nub");
|
||||||
struct person {
|
|
||||||
age: i32
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
func main(): i32 {
|
var tokens = Tokenizer.Tokenize("test.nub", file, out var tokenizerDiagnostics);
|
||||||
let x: i32 = 23
|
|
||||||
x = 24
|
|
||||||
|
|
||||||
if true {
|
|
||||||
x = 49
|
|
||||||
} else {
|
|
||||||
x = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
let i: i32 = 0
|
|
||||||
|
|
||||||
x = 1 + 2 * 34
|
|
||||||
|
|
||||||
while i < 10 {
|
|
||||||
i = i + 1
|
|
||||||
x = i
|
|
||||||
}
|
|
||||||
|
|
||||||
let me: person = struct person { age = 21 name = "Oliver" }
|
|
||||||
|
|
||||||
do_something(me.name)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func do_something(text: string): void {
|
foreach (var diagnostic in tokenizerDiagnostics)
|
||||||
}
|
{
|
||||||
""";
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenizerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodes = Parser.Parse("test.nub", tokens, out var parserDiagnostics);
|
||||||
|
|
||||||
|
foreach (var diagnostic in parserDiagnostics)
|
||||||
|
{
|
||||||
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parserDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
var tokens = Tokenizer.Tokenize(contents);
|
|
||||||
var nodes = Parser.Parse(tokens);
|
|
||||||
var output = Generator.Emit(nodes);
|
var output = Generator.Emit(nodes);
|
||||||
|
|
||||||
File.WriteAllText("C:/Users/oliste/repos/nub-lang/compiler/Compiler/out.c", output);
|
File.WriteAllText("C:/Users/oliste/repos/nub-lang/compiler/Compiler/out.c", output);
|
||||||
|
|
||||||
|
return 0;
|
||||||
@@ -3,33 +3,40 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class Tokenizer(string contents)
|
public sealed class Tokenizer(string fileName, string contents)
|
||||||
{
|
{
|
||||||
public static List<Token> Tokenize(string contents)
|
public static List<Token> Tokenize(string fileName, string contents, out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
return new Tokenizer(contents).Tokenize();
|
return new Tokenizer(fileName, contents).Tokenize(out diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int index;
|
private int index;
|
||||||
private int line = 1;
|
private int line = 1;
|
||||||
private int column = 1;
|
private int column = 1;
|
||||||
|
|
||||||
private List<Token> Tokenize()
|
private List<Token> Tokenize(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var tokens = new List<Token>();
|
var tokens = new List<Token>();
|
||||||
|
diagnostics = [];
|
||||||
while (true)
|
try
|
||||||
{
|
{
|
||||||
if (!TryPeek(out var c))
|
while (true)
|
||||||
break;
|
|
||||||
|
|
||||||
if (char.IsWhiteSpace(c))
|
|
||||||
{
|
{
|
||||||
Consume();
|
if (!TryPeek(out var c))
|
||||||
continue;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
tokens.Add(ParseToken());
|
if (char.IsWhiteSpace(c))
|
||||||
|
{
|
||||||
|
Consume();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.Add(ParseToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokens;
|
return tokens;
|
||||||
@@ -347,7 +354,7 @@ public sealed class Tokenizer(string contents)
|
|||||||
private char Consume()
|
private char Consume()
|
||||||
{
|
{
|
||||||
if (index >= contents.Length)
|
if (index >= contents.Length)
|
||||||
throw new Exception("End of tokens");
|
throw new CompileException(Diagnostic.Error("Unexpected end of file").At(fileName, line, column, 0).Build());
|
||||||
|
|
||||||
var c = contents[index];
|
var c = contents[index];
|
||||||
|
|
||||||
@@ -469,4 +476,62 @@ public enum Keyword
|
|||||||
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
||||||
{
|
{
|
||||||
public readonly Keyword Keyword = keyword;
|
public readonly Keyword Keyword = keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TokenExtensions
|
||||||
|
{
|
||||||
|
public static string AsString(this Symbol symbol)
|
||||||
|
{
|
||||||
|
return symbol switch
|
||||||
|
{
|
||||||
|
Symbol.OpenCurly => "{",
|
||||||
|
Symbol.CloseCurly => "}",
|
||||||
|
Symbol.OpenParen => "(",
|
||||||
|
Symbol.CloseParen => ")",
|
||||||
|
Symbol.Comma => ",",
|
||||||
|
Symbol.Period => ",",
|
||||||
|
Symbol.Colon => ":",
|
||||||
|
Symbol.Caret => "^",
|
||||||
|
Symbol.Bang => "!",
|
||||||
|
Symbol.Equal => "=",
|
||||||
|
Symbol.EqualEqual => "==",
|
||||||
|
Symbol.BangEqual => "!+",
|
||||||
|
Symbol.LessThan => "<",
|
||||||
|
Symbol.LessThanLessThan => "<<",
|
||||||
|
Symbol.LessThanEqual => "<=",
|
||||||
|
Symbol.GreaterThan => ">",
|
||||||
|
Symbol.GreaterThanGreaterThan => ">>",
|
||||||
|
Symbol.GreaterThanEqual => ">=",
|
||||||
|
Symbol.Plus => "+",
|
||||||
|
Symbol.PlusEqual => "+=",
|
||||||
|
Symbol.Minus => "-",
|
||||||
|
Symbol.MinusEqual => "-=",
|
||||||
|
Symbol.Star => "*",
|
||||||
|
Symbol.StarEqual => "*=",
|
||||||
|
Symbol.ForwardSlash => "/",
|
||||||
|
Symbol.ForwardSlashEqual => "/=",
|
||||||
|
Symbol.Percent => "%",
|
||||||
|
Symbol.PercentEqual => "%=",
|
||||||
|
Symbol.Ampersand => "&",
|
||||||
|
Symbol.AmpersandAmpersand => "&&",
|
||||||
|
Symbol.Pipe => "|",
|
||||||
|
Symbol.PipePipe => "||",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AsString(this Keyword symbol)
|
||||||
|
{
|
||||||
|
return symbol switch
|
||||||
|
{
|
||||||
|
Keyword.Func => "func",
|
||||||
|
Keyword.Struct => "struct",
|
||||||
|
Keyword.Let => "let",
|
||||||
|
Keyword.If => "if",
|
||||||
|
Keyword.Else => "else",
|
||||||
|
Keyword.While => "while",
|
||||||
|
Keyword.Return => "return",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
33
compiler/Compiler/test.nub
Normal file
33
compiler/Compiler/test.nub
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
struct person {
|
||||||
|
age: i32
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main(): i32 {
|
||||||
|
let x: i32 = 23
|
||||||
|
x = 24
|
||||||
|
|
||||||
|
if !true {
|
||||||
|
x = 49
|
||||||
|
return x
|
||||||
|
} else {
|
||||||
|
x = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
let i: i32 = 0
|
||||||
|
|
||||||
|
x = 1 + 2 * 34
|
||||||
|
|
||||||
|
while i < 10 {
|
||||||
|
i = i + 1
|
||||||
|
x = i
|
||||||
|
}
|
||||||
|
|
||||||
|
let me: person = struct person { age = 21 name = "Oliver" }
|
||||||
|
|
||||||
|
do_something(me.name)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func do_something(text: string): void {
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user