diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index cb88b04..6e835d9 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -13,10 +13,10 @@ var generator = new LlvmGenerator(); foreach (var file in args) { - tokenizer.Tokenize(file, File.ReadAllText(file)); + var tokens = tokenizer.Tokenize(file, File.ReadAllText(file)); diagnostics.AddRange(tokenizer.Diagnostics); - var syntaxTree = parser.Parse(tokenizer.Tokens); + var syntaxTree = parser.Parse(tokens); diagnostics.AddRange(parser.Diagnostics); syntaxTrees.Add(syntaxTree); @@ -69,7 +69,7 @@ for (var i = 0; i < args.Length; i++) } var path = Path.Combine(".build", Path.ChangeExtension(file, "ll")); - File.WriteAllText(path, generator.Emit(compilationUnit)); + File.WriteAllText(path, generator.Emit(compilationUnit, moduleRepository)); } return 0; \ No newline at end of file diff --git a/compiler/NubLang.LSP/AstExtensions.cs b/compiler/NubLang.LSP/AstExtensions.cs index e26675b..15d73b8 100644 --- a/compiler/NubLang.LSP/AstExtensions.cs +++ b/compiler/NubLang.LSP/AstExtensions.cs @@ -1,4 +1,5 @@ using NubLang.Ast; +using NubLang.Syntax; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; @@ -20,6 +21,47 @@ public static class AstExtensions }; } + public static Location ToLocation(this Token token) + { + return new Location + { + Uri = token.Span.FilePath, + Range = new Range(token.Span.Start.Line - 1, token.Span.Start.Column - 1, token.Span.End.Line - 1, token.Span.End.Column - 1) + }; + } + + public static bool ContainsPosition(this Token token, int line, int character) + { + var start = token.Span.Start; + var end = token.Span.End; + + var startLine = start.Line - 1; + var startChar = start.Column - 1; + var endLine = end.Line - 1; + var endChar = end.Column - 1; + + if (line < startLine || line > endLine) return false; + + if (line > startLine && line < endLine) return true; + + if (startLine == endLine) + { + return character >= startChar && character <= endChar; + } + + if (line == startLine) + { + return character >= startChar; + } + + if (line == endLine) + { + return character <= endChar; + } + + return false; + } + public static bool ContainsPosition(this Node node, int line, int character) { if (node.Tokens.Count == 0) diff --git a/compiler/NubLang.LSP/CompletionHandler.cs b/compiler/NubLang.LSP/CompletionHandler.cs index 4dde192..0e2a64a 100644 --- a/compiler/NubLang.LSP/CompletionHandler.cs +++ b/compiler/NubLang.LSP/CompletionHandler.cs @@ -1,4 +1,5 @@ using NubLang.Ast; +using NubLang.Modules; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -29,13 +30,6 @@ internal class CompletionHandler(WorkspaceManager workspaceManager) : Completion Label = "module", InsertTextFormat = InsertTextFormat.Snippet, InsertText = "module \"$0\"", - }, - new() - { - Kind = CompletionItemKind.Keyword, - Label = "import", - InsertTextFormat = InsertTextFormat.Snippet, - InsertText = "import \"$0\"", } ]; @@ -112,65 +106,76 @@ internal class CompletionHandler(WorkspaceManager workspaceManager) : Completion private CompletionList HandleSync(CompletionParams request, CancellationToken cancellationToken) { var completions = new List(); - var position = request.Position; - var uri = request.TextDocument.Uri; - var compilationUnit = workspaceManager.GetCompilationUnit(uri); - if (compilationUnit != null) + var compilationUnit = workspaceManager.GetTopLevelNodes(request.TextDocument.Uri.GetFileSystemPath()); + + var repository = workspaceManager.GetModuleRepository(); + + var function = compilationUnit + .OfType() + .FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(request.Position.Line, request.Position.Character)); + + if (function != null) { - var function = compilationUnit.OfType().FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character)); - if (function != null) + completions.AddRange(_statementSnippets); + + foreach (var module in repository.GetAll()) { - completions.AddRange(_statementSnippets); - - // foreach (var (module, prototypes) in compilationUnit.ImportedFunctions) - // { - // foreach (var prototype in prototypes) - // { - // var parameterStrings = new List(); - // foreach (var (index, parameter) in prototype.Parameters.Index()) - // { - // parameterStrings.AddRange($"${{{index + 1}:{parameter.NameToken.Value}}}"); - // } - // - // completions.Add(new CompletionItem - // { - // Kind = CompletionItemKind.Function, - // Label = $"{module.Value}::{prototype.NameToken.Value}", - // InsertTextFormat = InsertTextFormat.Snippet, - // InsertText = $"{module.Value}::{prototype.NameToken.Value}({string.Join(", ", parameterStrings)})", - // }); - // } - // } - - foreach (var parameter in function.Prototype.Parameters) + foreach (var prototype in module.FunctionPrototypes) { + var parameterStrings = new List(); + foreach (var (index, parameter) in prototype.Parameters.Index()) + { + parameterStrings.AddRange($"${{{index + 1}:{parameter.NameToken.Value}}}"); + } + + var isCurrentModule = false; + var moduleDecl = compilationUnit.OfType().FirstOrDefault(); + if (moduleDecl != null) + { + if (moduleDecl.NameToken.Value == module.Name) + { + isCurrentModule = true; + } + } + completions.Add(new CompletionItem { - Kind = CompletionItemKind.Variable, - Label = parameter.NameToken.Value, - InsertText = parameter.NameToken.Value, - }); - } - - var variables = function.Body! - .Descendants() - .OfType(); - - foreach (var variable in variables) - { - completions.Add(new CompletionItem - { - Kind = CompletionItemKind.Variable, - Label = variable.NameToken.Value, - InsertText = variable.NameToken.Value, + Kind = CompletionItemKind.Function, + Label = isCurrentModule ? prototype.NameToken.Value : $"{module.Name}::{prototype.NameToken.Value}", + InsertTextFormat = InsertTextFormat.Snippet, + InsertText = $"{(isCurrentModule ? "" : $"{module.Name}::")}{prototype.NameToken.Value}({string.Join(", ", parameterStrings)})", }); } } - else + + foreach (var parameter in function.Prototype.Parameters) { - completions.AddRange(_definitionSnippets); + completions.Add(new CompletionItem + { + Kind = CompletionItemKind.Variable, + Label = parameter.NameToken.Value, + InsertText = parameter.NameToken.Value, + }); } + + var variables = function.Body! + .Descendants() + .OfType(); + + foreach (var variable in variables) + { + completions.Add(new CompletionItem + { + Kind = CompletionItemKind.Variable, + Label = variable.NameToken.Value, + InsertText = variable.NameToken.Value, + }); + } + } + else + { + completions.AddRange(_definitionSnippets); } return new CompletionList(completions, false); diff --git a/compiler/NubLang.LSP/DefinitionHandler.cs b/compiler/NubLang.LSP/DefinitionHandler.cs index 2117320..4b5302f 100644 --- a/compiler/NubLang.LSP/DefinitionHandler.cs +++ b/compiler/NubLang.LSP/DefinitionHandler.cs @@ -20,37 +20,58 @@ internal class DefinitionHandler(WorkspaceManager workspaceManager) : Definition private LocationOrLocationLinks? HandleSync(DefinitionParams request, CancellationToken cancellationToken) { var uri = request.TextDocument.Uri; - var compilationUnit = workspaceManager.GetCompilationUnit(uri); - if (compilationUnit == null) - { - return null; - } + var topLevelNodes = workspaceManager.GetTopLevelNodes(uri.GetFileSystemPath()); var line = request.Position.Line; var character = request.Position.Character; - var node = compilationUnit.DeepestNodeAtPosition(line, character); + var node = topLevelNodes.DeepestNodeAtPosition(line, character); switch (node) { case VariableIdentifierNode variableIdentifierNode: { - var function = compilationUnit.FunctionAtPosition(line, character); + var funcNode = topLevelNodes.FunctionAtPosition(line, character); - var parameter = function?.Prototype.Parameters.FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value); + var parameter = funcNode?.Prototype.Parameters.FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value); if (parameter != null) { - return new LocationOrLocationLinks(parameter.ToLocation()); + return new LocationOrLocationLinks(parameter.NameToken.ToLocation()); } - var variable = function?.Body? + var variable = funcNode?.Body? .Descendants() .OfType() .FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value); if (variable != null) { - return new LocationOrLocationLinks(variable.ToLocation()); + return new LocationOrLocationLinks(variable.NameToken.ToLocation()); + } + + return null; + } + case LocalFuncIdentifierNode localFuncIdentifierNode: + { + var funcNode = topLevelNodes.OfType().FirstOrDefault(x => x.NameToken.Value == localFuncIdentifierNode.NameToken.Value); + if (funcNode != null) + { + return new LocationOrLocationLinks(funcNode.NameToken.ToLocation()); + } + + return null; + } + case ModuleFuncIdentifierNode localFuncIdentifierNode: + { + var repository = workspaceManager.GetModuleRepository(); + if (!repository.TryGet(localFuncIdentifierNode.ModuleToken, out var module)) + { + return null; + } + + if (module.TryResolveFunc(localFuncIdentifierNode.NameToken, out var func, out _)) + { + return new LocationOrLocationLinks(func.NameToken.ToLocation()); } return null; diff --git a/compiler/NubLang.LSP/HoverHandler.cs b/compiler/NubLang.LSP/HoverHandler.cs index a6464f5..3deb112 100644 --- a/compiler/NubLang.LSP/HoverHandler.cs +++ b/compiler/NubLang.LSP/HoverHandler.cs @@ -25,24 +25,15 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas private Hover? HandleSync(HoverParams request, CancellationToken cancellationToken) { - var compilationUnit = workspaceManager.GetCompilationUnit(request.TextDocument.Uri); - if (compilationUnit == null) - { - return null; - } + var topLevelNodes = workspaceManager.GetTopLevelNodes(request.TextDocument.Uri.GetFileSystemPath()); - var moduleDecl = compilationUnit.OfType().FirstOrDefault(); + var moduleDecl = topLevelNodes.OfType().FirstOrDefault(); if (moduleDecl == null) { return null; } var moduleRepository = workspaceManager.GetModuleRepository(); - if (moduleRepository == null) - { - return null; - } - if (!moduleRepository.TryGet(moduleDecl.NameToken, out var module)) { return null; @@ -51,14 +42,14 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas var line = request.Position.Line; var character = request.Position.Character; - var hoveredNode = compilationUnit.DeepestNodeAtPosition(line, character); + var hoveredNode = topLevelNodes.DeepestNodeAtPosition(line, character); if (hoveredNode == null) { return null; } - var message = CreateMessage(hoveredNode, moduleRepository, module); + var message = CreateMessage(hoveredNode, moduleRepository, module, line, character); if (message == null) { return null; @@ -74,7 +65,7 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas }; } - private static string? CreateMessage(Node hoveredNode, ModuleRepository repository, ModuleRepository.Module currentModule) + private static string? CreateMessage(Node hoveredNode, ModuleRepository repository, ModuleRepository.Module currentModule, int line, int character) { return hoveredNode switch { @@ -99,6 +90,7 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()), U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()), U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()), + StructInitializerNode structInitializerNode => CreateStructInitializerMessage(structInitializerNode, line, character), // Expressions can have a generic fallback showing the resulting type ExpressionNode expressionNode => $""" **Expression** `{expressionNode.GetType().Name}` @@ -111,6 +103,41 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas }; } + private static string CreateStructInitializerMessage(StructInitializerNode structInitializerNode, int line, int character) + { + var hoveredInitializerName = structInitializerNode + .Initializers + .Select(x => x.Key) + .FirstOrDefault(x => x.ContainsPosition(line, character)); + + var structType = (NubStructType)structInitializerNode.Type; + + if (hoveredInitializerName != null) + { + var field = structType.Fields.FirstOrDefault(x => x.Name == hoveredInitializerName.Value); + if (field != null) + { + return $""" + **Field** in `{structType}` + ```nub + {hoveredInitializerName.Value}: {field.Type} + ``` + """; + } + else + { + return $""" + **Field** in `{structType}` + ```nub + // Field not found + ``` + """; + } + } + + return $"**Struct initializer** `{structType}`"; + } + private static string CreateLiteralMessage(NubType type, string value) { return $""" diff --git a/compiler/NubLang.LSP/TextDocumentSyncHandler.cs b/compiler/NubLang.LSP/TextDocumentSyncHandler.cs index e29aa44..bca7cd9 100644 --- a/compiler/NubLang.LSP/TextDocumentSyncHandler.cs +++ b/compiler/NubLang.LSP/TextDocumentSyncHandler.cs @@ -15,25 +15,25 @@ internal class TextDocumentSyncHandler(WorkspaceManager workspaceManager) : Text public override Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken) { - workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); + workspaceManager.Update(); return Unit.Task; } public override Task Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken) { - workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); + workspaceManager.Update(); return Unit.Task; } public override Task Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken) { - workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); + workspaceManager.Update(); return Unit.Task; } public override Task Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken) { - workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); + workspaceManager.Update(); return Unit.Task; } diff --git a/compiler/NubLang.LSP/WorkspaceManager.cs b/compiler/NubLang.LSP/WorkspaceManager.cs index 9bf6b30..3fbe6eb 100644 --- a/compiler/NubLang.LSP/WorkspaceManager.cs +++ b/compiler/NubLang.LSP/WorkspaceManager.cs @@ -2,100 +2,67 @@ using NubLang.Ast; using NubLang.Diagnostics; using NubLang.Modules; using NubLang.Syntax; -using OmniSharp.Extensions.LanguageServer.Protocol; namespace NubLang.LSP; public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher) { - private readonly Dictionary _syntaxTrees = new(); - private readonly Dictionary> _compilationUnits = new(); - private ModuleRepository? _repository; + private record Unit(SyntaxTree SyntaxTree, DateTimeOffset FileTimestamp, List Diagnostics); + + private readonly Tokenizer _tokenizer = new(); + private readonly Parser _parser = new(); + private readonly TypeChecker _typeChecker = new(); + private string? _rootPath; + private readonly Dictionary _units = []; + private readonly Dictionary> _possiblyOutdatedTopLevelNodes = []; + private ModuleRepository _repository = new([]); public void Init(string rootPath) { - var files = Directory.GetFiles(rootPath, "*.nub", SearchOption.AllDirectories); - foreach (var path in files) + _rootPath = rootPath; + Update(); + } + + public void Update() + { + if (_rootPath == null) return; + var files = Directory.GetFiles(_rootPath, "*.nub", SearchOption.AllDirectories); + foreach (var file in files) { - var text = File.ReadAllText(path); - var tokenizer = new Tokenizer(); - - tokenizer.Tokenize(path, text); - diagnosticsPublisher.Publish(path, tokenizer.Diagnostics); - - var parser = new Parser(); - var parseResult = parser.Parse(tokenizer.Tokens); - diagnosticsPublisher.Publish(path, parser.Diagnostics); - - _syntaxTrees[path] = parseResult; - } - - ModuleRepository repository; - - foreach (var (fsPath, syntaxTree) in _syntaxTrees) - { - try + var lastUpdated = File.GetLastWriteTimeUtc(file); + var unit = _units.GetValueOrDefault(file); + if (unit == null || lastUpdated > unit.FileTimestamp) { - repository = ModuleRepository.Create(_syntaxTrees.Select(x => x.Value).ToList()); - } - catch (CompileException e) - { - return; + _units[file] = Update(file, lastUpdated); } + } - var typeChecker = new TypeChecker(); - var result = typeChecker.Check(syntaxTree, repository); - diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics); - _compilationUnits[fsPath] = result; + _repository = ModuleRepository.Create(_units.Select(x => x.Value.SyntaxTree).ToList()); + + foreach (var (file, unit) in _units) + { + var topLevelNodes = _typeChecker.Check(unit.SyntaxTree, _repository); + _possiblyOutdatedTopLevelNodes[file] = topLevelNodes; + diagnosticsPublisher.Publish(file, [..unit.Diagnostics, .._typeChecker.Diagnostics]); } } - public void UpdateFile(DocumentUri path) + private Unit Update(string file, DateTimeOffset lastUpdated) { - var fsPath = path.GetFileSystemPath(); + var text = File.ReadAllText(file); + var tokens = _tokenizer.Tokenize(file, text); + var syntaxTree = _parser.Parse(tokens); - var text = File.ReadAllText(fsPath); - var tokenizer = new Tokenizer(); - tokenizer.Tokenize(fsPath, text); - diagnosticsPublisher.Publish(path, tokenizer.Diagnostics); - - var parser = new Parser(); - var syntaxTree = parser.Parse(tokenizer.Tokens); - diagnosticsPublisher.Publish(path, parser.Diagnostics); - _syntaxTrees[fsPath] = syntaxTree; - - ModuleRepository repository; - - try - { - repository = ModuleRepository.Create(_syntaxTrees.Select(x => x.Value).ToList()); - } - catch (CompileException e) - { - diagnosticsPublisher.Publish(path, [e.Diagnostic]); - return; - } - - var typeChecker = new TypeChecker(); - var result = typeChecker.Check(syntaxTree, repository); - diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics); - _compilationUnits[fsPath] = result; + return new Unit(syntaxTree, lastUpdated, [.._tokenizer.Diagnostics, .._parser.Diagnostics]); } - public List? GetCompilationUnit(DocumentUri path) + public List GetTopLevelNodes(string path) { - return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath()); + return _possiblyOutdatedTopLevelNodes.GetValueOrDefault(path, []); } - public ModuleRepository? GetModuleRepository() + public ModuleRepository GetModuleRepository() { - try - { - return ModuleRepository.Create(_syntaxTrees.Select(x => x.Value).ToList()); - } - catch (CompileException e) - { - return null; - } + return _repository; } } \ No newline at end of file diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs index a9510a5..0b6c812 100644 --- a/compiler/NubLang/Diagnostics/Diagnostic.cs +++ b/compiler/NubLang/Diagnostics/Diagnostic.cs @@ -106,7 +106,7 @@ public class Diagnostic var text = File.ReadAllText(Span.Value.FilePath); var tokenizer = new Tokenizer(); - tokenizer.Tokenize(Span.Value.FilePath, text); + var tokens = tokenizer.Tokenize(Span.Value.FilePath, text); var lines = text.Split('\n'); @@ -144,7 +144,7 @@ public class Diagnostic sb.Append("│ "); sb.Append(i.ToString().PadRight(numberPadding)); sb.Append(" │ "); - sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens)); + sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokens)); // sb.Append(line.PadRight(codePadding)); sb.Append(" │"); sb.AppendLine(); diff --git a/compiler/NubLang/Generation/LlvmGenerator.cs b/compiler/NubLang/Generation/LlvmGenerator.cs index 0ab7fa4..e87ccf2 100644 --- a/compiler/NubLang/Generation/LlvmGenerator.cs +++ b/compiler/NubLang/Generation/LlvmGenerator.cs @@ -1,5 +1,6 @@ using System.Text; using NubLang.Ast; +using NubLang.Modules; using NubLang.Types; namespace NubLang.Generation; @@ -12,7 +13,7 @@ public class LlvmGenerator private List<(string Name, int Size, string Text)> _stringLiterals = []; private Stack<(string breakLabel, string continueLabel)> _loopStack = []; - public string Emit(List topLevelNodes) + public string Emit(List topLevelNodes, ModuleRepository repository) { _stringLiterals = []; _loopStack = []; @@ -26,6 +27,30 @@ public class LlvmGenerator writer.WriteLine("declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)"); writer.WriteLine(); + var declaredExternFunctions = new HashSet(); + + foreach (var module in repository.GetAll()) + { + foreach (var prototype in module.FunctionPrototypes) + { + // note(nub31): If we are in the current module and the function has a body, we skip it to prevent duplicate definition + if (module.Name == _module && topLevelNodes.OfType().First(x => x.NameToken.Value == prototype.NameToken.Value).Body != null) + { + continue; + } + + if (prototype.ExternSymbolToken != null && !declaredExternFunctions.Add(prototype.ExternSymbolToken.Value)) + { + continue; + } + + var parameters = prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}"); + var funcName = FuncName(module.Name, prototype.NameToken.Value, prototype.ExternSymbolToken?.Value); + writer.WriteLine($"declare {MapType(prototype.ReturnType)} @{funcName}({string.Join(", ", parameters)})"); + writer.WriteLine(); + } + } + foreach (var structNode in topLevelNodes.OfType()) { var types = structNode.Fields.Select(x => MapType(x.Type)); @@ -57,15 +82,6 @@ public class LlvmGenerator writer.WriteLine(); } - foreach (var funcNode in topLevelNodes.OfType()) - { - if (funcNode.Body != null) continue; - var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}"); - - writer.WriteLine($"declare {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)})"); - writer.WriteLine(); - } - foreach (var funcNode in topLevelNodes.OfType()) { if (funcNode.Body == null) continue; @@ -335,7 +351,8 @@ public class LlvmGenerator Float32LiteralNode float32LiteralNode => EmitFloat32Literal(writer, float32LiteralNode), Float64LiteralNode float64LiteralNode => EmitFloat64Literal(writer, float64LiteralNode), FuncCallNode funcCallNode => EmitFuncCall(writer, funcCallNode), - ModuleFuncIdentifierNode funcIdentifierNode => EmitFuncIdentifier(writer, funcIdentifierNode), + ModuleFuncIdentifierNode moduleFuncIdentifierNode => EmitModuleFuncIdentifier(writer, moduleFuncIdentifierNode), + LocalFuncIdentifierNode localFuncIdentifierNode => EmitLocalFuncIdentifier(writer, localFuncIdentifierNode), I16LiteralNode i16LiteralNode => EmitI16Literal(writer, i16LiteralNode), I32LiteralNode i32LiteralNode => EmitI32Literal(writer, i32LiteralNode), I64LiteralNode i64LiteralNode => EmitI64Literal(writer, i64LiteralNode), @@ -769,12 +786,18 @@ public class LlvmGenerator return new Tmp(result, funcCallNode.Type, false); } - private Tmp EmitFuncIdentifier(IndentedTextWriter writer, ModuleFuncIdentifierNode moduleFuncIdentifierNode) + private Tmp EmitModuleFuncIdentifier(IndentedTextWriter writer, ModuleFuncIdentifierNode moduleFuncIdentifierNode) { var name = FuncName(moduleFuncIdentifierNode.ModuleToken.Value, moduleFuncIdentifierNode.NameToken.Value, moduleFuncIdentifierNode.ExternSymbolToken?.Value); return new Tmp($"@{name}", moduleFuncIdentifierNode.Type, false); } + private Tmp EmitLocalFuncIdentifier(IndentedTextWriter writer, LocalFuncIdentifierNode localFuncIdentifierNode) + { + var name = FuncName(_module, localFuncIdentifierNode.NameToken.Value, localFuncIdentifierNode.ExternSymbolToken?.Value); + return new Tmp($"@{name}", localFuncIdentifierNode.Type, false); + } + private Tmp EmitI16Literal(IndentedTextWriter writer, I16LiteralNode i16LiteralNode) { return new Tmp(i16LiteralNode.Value.ToString(), i16LiteralNode.Type, false); diff --git a/compiler/NubLang/Modules/ModuleRepository.cs b/compiler/NubLang/Modules/ModuleRepository.cs index 92a1023..f3e2706 100644 --- a/compiler/NubLang/Modules/ModuleRepository.cs +++ b/compiler/NubLang/Modules/ModuleRepository.cs @@ -143,7 +143,7 @@ public sealed class ModuleRepository } } - private ModuleRepository(Dictionary modules) + public ModuleRepository(Dictionary modules) { _modules = modules; } @@ -167,6 +167,17 @@ public sealed class ModuleRepository return module != null; } + public bool TryGet(string name, [NotNullWhen(true)] out Module? module) + { + module = _modules.GetValueOrDefault(name); + return module != null; + } + + public List GetAll() + { + return _modules.Values.ToList(); + } + public sealed class Module { public required string Name { get; init; } @@ -174,6 +185,21 @@ public sealed class ModuleRepository public required List StructTypes { get; init; } = []; public required Dictionary EnumTypes { get; init; } = []; + public bool TryResolveFunc(string name, [NotNullWhen(true)] out FuncPrototypeNode? value, [NotNullWhen(false)] out Diagnostic? diagnostic) + { + value = FunctionPrototypes.FirstOrDefault(x => x.NameToken.Value == name); + + if (value == null) + { + value = null; + diagnostic = Diagnostic.Error($"Func {name} not found in module {Name}").Build(); + return false; + } + + diagnostic = null; + return true; + } + public bool TryResolveFunc(IdentifierToken name, [NotNullWhen(true)] out FuncPrototypeNode? value, [NotNullWhen(false)] out Diagnostic? diagnostic) { value = FunctionPrototypes.FirstOrDefault(x => x.NameToken.Value == name.Value); @@ -199,6 +225,21 @@ public sealed class ModuleRepository return value; } + public bool TryResolveStruct(string name, [NotNullWhen(true)] out NubStructType? value, [NotNullWhen(false)] out Diagnostic? diagnostic) + { + value = StructTypes.FirstOrDefault(x => x.Name == name); + + if (value == null) + { + value = null; + diagnostic = Diagnostic.Error($"Struct {name} not found in module {Name}").Build(); + return false; + } + + diagnostic = null; + return true; + } + public bool TryResolveStruct(IdentifierToken name, [NotNullWhen(true)] out NubStructType? value, [NotNullWhen(false)] out Diagnostic? diagnostic) { value = StructTypes.FirstOrDefault(x => x.Name == name.Value); @@ -224,6 +265,21 @@ public sealed class ModuleRepository return value; } + public bool TryResolveEnum(string name, [NotNullWhen(true)] out NubIntType? value, [NotNullWhen(false)] out Diagnostic? diagnostic) + { + value = EnumTypes.GetValueOrDefault(name); + + if (value == null) + { + value = null; + diagnostic = Diagnostic.Error($"Enum {name} not found in module {Name}").Build(); + return false; + } + + diagnostic = null; + return true; + } + public bool TryResolveEnum(IdentifierToken name, [NotNullWhen(true)] out NubIntType? value, [NotNullWhen(false)] out Diagnostic? diagnostic) { value = EnumTypes.GetValueOrDefault(name.Value); diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index bc5b365..f0ee5a3 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -417,7 +417,7 @@ public sealed class Parser case Symbol.Pipe: binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr; return true; - case Symbol.XOr: + case Symbol.Tilde: binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor; return true; default: @@ -439,12 +439,12 @@ public sealed class Parser IdentifierToken identifier => ParseIdentifier(startIndex, identifier), SymbolToken symbolToken => symbolToken.Symbol switch { - Symbol.Ampersand => new AddressOfSyntax(GetTokens(startIndex), ParsePrimaryExpression()), + Symbol.Ampersand => ParseAddressOf(startIndex), Symbol.OpenParen => ParseParenthesizedExpression(), - Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()), - Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()), + Symbol.Minus => ParseUnaryNegate(startIndex), + Symbol.Bang => ParseUnaryInvert(startIndex), Symbol.OpenBracket => ParseArrayInitializer(startIndex), - Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()), + Symbol.OpenBrace => ParseUnnamedStructInitializer(startIndex), Symbol.Struct => ParseStructInitializer(startIndex), Symbol.At => ParseBuiltinFunction(startIndex), _ => throw new CompileException(Diagnostic @@ -463,6 +463,24 @@ public sealed class Parser return ParsePostfixOperators(expr); } + private AddressOfSyntax ParseAddressOf(int startIndex) + { + var expression = ParsePrimaryExpression(); + return new AddressOfSyntax(GetTokens(startIndex), expression); + } + + private UnaryExpressionSyntax ParseUnaryInvert(int startIndex) + { + var expression = ParsePrimaryExpression(); + return new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, expression); + } + + private UnaryExpressionSyntax ParseUnaryNegate(int startIndex) + { + var expression = ParsePrimaryExpression(); + return new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, expression); + } + private ExpressionSyntax ParseBuiltinFunction(int startIndex) { var name = ExpectIdentifier(); @@ -587,6 +605,12 @@ public sealed class Parser return new StructInitializerSyntax(GetTokens(startIndex), type, initializers); } + private StructInitializerSyntax ParseUnnamedStructInitializer(int startIndex) + { + var body = ParseStructInitializerBody(); + return new StructInitializerSyntax(GetTokens(startIndex), null, body); + } + private Dictionary ParseStructInitializerBody() { Dictionary initializers = []; diff --git a/compiler/NubLang/Syntax/Token.cs b/compiler/NubLang/Syntax/Token.cs index 3be0c77..9ad3417 100644 --- a/compiler/NubLang/Syntax/Token.cs +++ b/compiler/NubLang/Syntax/Token.cs @@ -121,6 +121,7 @@ public enum Symbol Star, ForwardSlash, Caret, + Tilde, Ampersand, Semi, Percent, @@ -129,7 +130,6 @@ public enum Symbol Pipe, And, Or, - XOr, At, QuestionMark, } @@ -189,6 +189,7 @@ public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span) Symbol.Pipe => "|", Symbol.At => "@", Symbol.QuestionMark => "?", + Symbol.Tilde => "~", _ => "none", }; } diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs index 5afbae5..12a8cb4 100644 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ b/compiler/NubLang/Syntax/Tokenizer.cs @@ -11,20 +11,20 @@ public sealed class Tokenizer private int _column = 1; public List Diagnostics { get; set; } = new(16); - public List Tokens { get; set; } = new(256); - public void Tokenize(string fileName, string content) + public List Tokenize(string fileName, string content) { _fileName = fileName; _content = content; Diagnostics = []; - Tokens = []; _index = 0; _line = 1; _column = 1; + var tokens = new List(); + while (_index < _content.Length) { try @@ -54,7 +54,7 @@ public sealed class Tokenizer continue; } - Tokens.Add(ParseToken(current, _line, _column)); + tokens.Add(ParseToken(current, _line, _column)); } catch (CompileException e) { @@ -62,6 +62,8 @@ public sealed class Tokenizer Next(); } } + + return tokens; } private Token ParseToken(char current, int lineStart, int columnStart) @@ -295,7 +297,6 @@ public sealed class Tokenizer "&&" => Symbol.And, "||" => Symbol.Or, "::" => Symbol.DoubleColon, - "x|" => Symbol.XOr, _ => Symbol.None }, 1 => span[0] switch @@ -324,6 +325,7 @@ public sealed class Tokenizer '|' => Symbol.Pipe, '@' => Symbol.At, '?' => Symbol.QuestionMark, + '~' => Symbol.Tilde, _ => Symbol.None }, _ => Symbol.None diff --git a/examples/playgroud/build.sh b/examples/playgroud/build.sh index 1ca616f..1df6fc6 100755 --- a/examples/playgroud/build.sh +++ b/examples/playgroud/build.sh @@ -2,5 +2,5 @@ set -euo pipefail -nubc main.nub -clang .build/main.ll -o .build/out \ No newline at end of file +nubc main.nub test.nub +clang .build/main.ll .build/test.ll -o .build/out \ No newline at end of file diff --git a/examples/playgroud/main.nub b/examples/playgroud/main.nub index eac807c..020560f 100644 --- a/examples/playgroud/main.nub +++ b/examples/playgroud/main.nub @@ -9,5 +9,9 @@ struct Test extern "main" func main(argc: i64, argv: [?]^i8) { - let x: Test = {} + let x: Test = { + field = 23 + } + + test::test() } \ No newline at end of file diff --git a/examples/playgroud/test.nub b/examples/playgroud/test.nub new file mode 100644 index 0000000..b8f9d6a --- /dev/null +++ b/examples/playgroud/test.nub @@ -0,0 +1,8 @@ +module test + +extern "puts" func puts(text: ^i8) + +func test() +{ + puts("uwu") +} \ No newline at end of file diff --git a/examples/raylib/main.nub b/examples/raylib/main.nub index f1837d6..ba0f752 100644 --- a/examples/raylib/main.nub +++ b/examples/raylib/main.nub @@ -1,5 +1,3 @@ -import raylib - module main extern "main" func main(argc: i64, argv: [?]^i8): i64