From 3107849915f73cf7378f6a60069167fa41ffab0f Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 29 Sep 2025 12:48:11 +0200 Subject: [PATCH] wip: enums --- compiler/NubLang.CLI/Program.cs | 42 ++++++++++++++++++- compiler/NubLang/Parsing/Parser.cs | 35 +++++++++++++++- .../Parsing/Syntax/DefinitionSyntax.cs | 6 ++- compiler/NubLang/Tokenization/Token.cs | 1 + compiler/NubLang/Tokenization/Tokenizer.cs | 1 + example/src/main.nub | 7 ++++ 6 files changed, 87 insertions(+), 5 deletions(-) diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index a1bc56d..f68acbc 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -1,4 +1,5 @@ -using NubLang.CLI; +using System.Diagnostics; +using NubLang.CLI; using NubLang.Code; using NubLang.Diagnostics; using NubLang.Generation.QBE; @@ -9,6 +10,8 @@ using NubLang.Tokenization; using NubLang.TypeChecking; using NubLang.TypeChecking.Node; +var sw = Stopwatch.StartNew(); + var options = new Options(); for (var i = 0; i < args.Length; i++) @@ -35,6 +38,9 @@ for (var i = 0; i < args.Length; i++) } } +Console.WriteLine($"Parse cli args: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + foreach (var file in options.Files) { if (!File.Exists(file.Path)) @@ -44,6 +50,9 @@ foreach (var file in options.Files) } } +Console.WriteLine($"Check file exists: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + var diagnostics = new List(); var syntaxTrees = new List(); @@ -53,16 +62,26 @@ foreach (var file in options.Files) var tokens = tokenizer.Tokenize().ToList(); diagnostics.AddRange(tokenizer.GetDiagnostics()); + Console.WriteLine($"Tokenize: {Path.GetFileName(file.Path)}: {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + var parser = new Parser(); var syntaxTree = parser.Parse(tokens); - diagnostics.AddRange(parser.GetDiagnostics()); + Console.WriteLine($"Parse: {Path.GetFileName(file.Path)}: {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + syntaxTrees.Add(syntaxTree); } +sw.Restart(); + var moduleRepository = new ModuleRepository(syntaxTrees); +Console.WriteLine($"Create module repository: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + var definitions = new List(); var referencedStructTypes = new HashSet(); @@ -72,6 +91,9 @@ foreach (var syntaxTree in syntaxTrees) var typeChecker = new TypeChecker(syntaxTree, moduleRepository); typeChecker.Check(); + Console.WriteLine($"Type check {syntaxTree.Metadata.ModuleName}: {sw.ElapsedMilliseconds}ms"); + sw.Restart(); + definitions.AddRange(typeChecker.Definitions); diagnostics.AddRange(typeChecker.Diagnostics); @@ -81,11 +103,16 @@ foreach (var syntaxTree in syntaxTrees) } } +sw.Restart(); + foreach (var diagnostic in diagnostics) { Console.Error.WriteLine(diagnostic.FormatANSI()); } +Console.WriteLine($"Print diagnostics: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)) { return 1; @@ -93,17 +120,28 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro Directory.CreateDirectory(".build"); +sw.Restart(); + var generator = new QBEGenerator(definitions, referencedStructTypes); var ssa = generator.Emit(); var ssaFilePath = Path.Combine(".build", "out.ssa"); File.WriteAllText(ssaFilePath, ssa); +Console.WriteLine($"Emit ssa: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + var asmFilePath = Path.Combine(".build", "out.asm"); var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath); if (!qbeSuccess) return 1; +Console.WriteLine($"Emit asm: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + var objFilePath = Path.Combine(".build", "out.o"); var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath); if (!asmSuccess) return 1; +Console.WriteLine($"Assemble: {sw.ElapsedMilliseconds}ms"); +sw.Restart(); + return 0; \ No newline at end of file diff --git a/compiler/NubLang/Parsing/Parser.cs b/compiler/NubLang/Parsing/Parser.cs index c21b21f..21d9ab4 100644 --- a/compiler/NubLang/Parsing/Parser.cs +++ b/compiler/NubLang/Parsing/Parser.cs @@ -8,10 +8,10 @@ namespace NubLang.Parsing; public sealed class Parser { private readonly List _diagnostics = []; + private readonly HashSet _templateArguments = []; private List _tokens = []; private int _tokenIndex; private string _moduleName = string.Empty; - private HashSet _templateArguments = []; private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null; private bool HasToken => CurrentToken != null; @@ -86,10 +86,11 @@ public sealed class Parser } var keyword = ExpectSymbol(); - DefinitionSyntax definition = keyword.Symbol switch + var definition = keyword.Symbol switch { Symbol.Func => ParseFunc(startIndex, exported, null), Symbol.Struct => ParseStruct(startIndex, exported), + Symbol.Enum => ParseEnum(startIndex, exported), _ => throw new ParseException(Diagnostic .Error($"Expected 'func' or 'struct' but found '{keyword.Symbol}'") .WithHelp("Valid definition keywords are 'func' and 'struct'") @@ -228,6 +229,36 @@ public sealed class Parser return new StructSyntax(GetTokens(startIndex), name.Value, exported, fields, funcs); } + private EnumSyntax ParseEnum(int startIndex, bool exported) + { + var name = ExpectIdentifier(); + + TypeSyntax? type = null; + if (TryExpectSymbol(Symbol.Colon)) + { + type = ParseType(); + } + + var values = new List(); + + ExpectSymbol(Symbol.OpenBrace); + while (!TryExpectSymbol(Symbol.CloseBrace)) + { + var valueStartIndex = _tokenIndex; + var valueName = ExpectIdentifier().Value; + + ExpressionSyntax? valueValue = null; + if (TryExpectSymbol(Symbol.Assign)) + { + valueValue = ParseExpression(); + } + + values.Add(new EnumValueSyntax(GetTokens(valueStartIndex), valueName, valueValue)); + } + + return new EnumSyntax(GetTokens(startIndex), name.Value, exported, type, values); + } + private StatementSyntax ParseStatement() { var startIndex = _tokenIndex; diff --git a/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs b/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs index 753a788..d39ab77 100644 --- a/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs +++ b/compiler/NubLang/Parsing/Syntax/DefinitionSyntax.cs @@ -16,4 +16,8 @@ public record StructFuncSyntax(List Tokens, string Name, string? Hook, Fu public record StructSyntax(List Tokens, string Name, bool Exported, List Fields, List Functions) : DefinitionSyntax(Tokens, Name, Exported); -public record StructTemplateSyntax(List Tokens, List TemplateArguments, string Name, bool Exported, List Fields, List Functions) : DefinitionSyntax(Tokens, Name, Exported); \ No newline at end of file +public record StructTemplateSyntax(List Tokens, List TemplateArguments, string Name, bool Exported, List Fields, List Functions) : DefinitionSyntax(Tokens, Name, Exported); + +public record EnumValueSyntax(List Tokens, string Name, ExpressionSyntax? Value) : SyntaxNode(Tokens); + +public record EnumSyntax(List Tokens, string Name, bool Exported, TypeSyntax? Type, List Values) : DefinitionSyntax(Tokens, Name, Exported); \ No newline at end of file diff --git a/compiler/NubLang/Tokenization/Token.cs b/compiler/NubLang/Tokenization/Token.cs index 9ead836..4ff0002 100644 --- a/compiler/NubLang/Tokenization/Token.cs +++ b/compiler/NubLang/Tokenization/Token.cs @@ -82,4 +82,5 @@ public enum Symbol Export, Defer, At, + Enum, } \ No newline at end of file diff --git a/compiler/NubLang/Tokenization/Tokenizer.cs b/compiler/NubLang/Tokenization/Tokenizer.cs index 7c31de1..6b4298f 100644 --- a/compiler/NubLang/Tokenization/Tokenizer.cs +++ b/compiler/NubLang/Tokenization/Tokenizer.cs @@ -24,6 +24,7 @@ public sealed class Tokenizer ["export"] = Symbol.Export, ["import"] = Symbol.Import, ["defer"] = Symbol.Defer, + ["enum"] = Symbol.Enum, }; private static readonly Dictionary Symbols = new() diff --git a/example/src/main.nub b/example/src/main.nub index 808250b..d8b0548 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -3,6 +3,13 @@ import "raylib" module "main" +enum State +{ + NONE + TEST + SOMEOTHERVALUE +} + extern "main" func main(args: []cstring): i64 { raylib::InitWindow(1600, 900, "Hi from nub-lang")