This commit is contained in:
nub31
2025-11-03 12:52:17 +01:00
parent 40d500fddd
commit 085f7a1a6a
17 changed files with 368 additions and 190 deletions

View File

@@ -13,10 +13,10 @@ var generator = new LlvmGenerator();
foreach (var file in args) foreach (var file in args)
{ {
tokenizer.Tokenize(file, File.ReadAllText(file)); var tokens = tokenizer.Tokenize(file, File.ReadAllText(file));
diagnostics.AddRange(tokenizer.Diagnostics); diagnostics.AddRange(tokenizer.Diagnostics);
var syntaxTree = parser.Parse(tokenizer.Tokens); var syntaxTree = parser.Parse(tokens);
diagnostics.AddRange(parser.Diagnostics); diagnostics.AddRange(parser.Diagnostics);
syntaxTrees.Add(syntaxTree); syntaxTrees.Add(syntaxTree);
@@ -69,7 +69,7 @@ for (var i = 0; i < args.Length; i++)
} }
var path = Path.Combine(".build", Path.ChangeExtension(file, "ll")); var path = Path.Combine(".build", Path.ChangeExtension(file, "ll"));
File.WriteAllText(path, generator.Emit(compilationUnit)); File.WriteAllText(path, generator.Emit(compilationUnit, moduleRepository));
} }
return 0; return 0;

View File

@@ -1,4 +1,5 @@
using NubLang.Ast; using NubLang.Ast;
using NubLang.Syntax;
using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; 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) public static bool ContainsPosition(this Node node, int line, int character)
{ {
if (node.Tokens.Count == 0) if (node.Tokens.Count == 0)

View File

@@ -1,4 +1,5 @@
using NubLang.Ast; using NubLang.Ast;
using NubLang.Modules;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Models;
@@ -29,13 +30,6 @@ internal class CompletionHandler(WorkspaceManager workspaceManager) : Completion
Label = "module", Label = "module",
InsertTextFormat = InsertTextFormat.Snippet, InsertTextFormat = InsertTextFormat.Snippet,
InsertText = "module \"$0\"", 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) private CompletionList HandleSync(CompletionParams request, CancellationToken cancellationToken)
{ {
var completions = new List<CompletionItem>(); var completions = new List<CompletionItem>();
var position = request.Position;
var uri = request.TextDocument.Uri; var compilationUnit = workspaceManager.GetTopLevelNodes(request.TextDocument.Uri.GetFileSystemPath());
var compilationUnit = workspaceManager.GetCompilationUnit(uri);
if (compilationUnit != null) var repository = workspaceManager.GetModuleRepository();
var function = compilationUnit
.OfType<FuncNode>()
.FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(request.Position.Line, request.Position.Character));
if (function != null)
{ {
var function = compilationUnit.OfType<FuncNode>().FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character)); completions.AddRange(_statementSnippets);
if (function != null)
foreach (var module in repository.GetAll())
{ {
completions.AddRange(_statementSnippets); foreach (var prototype in module.FunctionPrototypes)
// foreach (var (module, prototypes) in compilationUnit.ImportedFunctions)
// {
// foreach (var prototype in prototypes)
// {
// var parameterStrings = new List<string>();
// 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)
{ {
var parameterStrings = new List<string>();
foreach (var (index, parameter) in prototype.Parameters.Index())
{
parameterStrings.AddRange($"${{{index + 1}:{parameter.NameToken.Value}}}");
}
var isCurrentModule = false;
var moduleDecl = compilationUnit.OfType<ModuleNode>().FirstOrDefault();
if (moduleDecl != null)
{
if (moduleDecl.NameToken.Value == module.Name)
{
isCurrentModule = true;
}
}
completions.Add(new CompletionItem completions.Add(new CompletionItem
{ {
Kind = CompletionItemKind.Variable, Kind = CompletionItemKind.Function,
Label = parameter.NameToken.Value, Label = isCurrentModule ? prototype.NameToken.Value : $"{module.Name}::{prototype.NameToken.Value}",
InsertText = parameter.NameToken.Value, InsertTextFormat = InsertTextFormat.Snippet,
}); InsertText = $"{(isCurrentModule ? "" : $"{module.Name}::")}{prototype.NameToken.Value}({string.Join(", ", parameterStrings)})",
}
var variables = function.Body!
.Descendants()
.OfType<VariableDeclarationNode>();
foreach (var variable in variables)
{
completions.Add(new CompletionItem
{
Kind = CompletionItemKind.Variable,
Label = variable.NameToken.Value,
InsertText = variable.NameToken.Value,
}); });
} }
} }
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<VariableDeclarationNode>();
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); return new CompletionList(completions, false);

View File

@@ -20,37 +20,58 @@ internal class DefinitionHandler(WorkspaceManager workspaceManager) : Definition
private LocationOrLocationLinks? HandleSync(DefinitionParams request, CancellationToken cancellationToken) private LocationOrLocationLinks? HandleSync(DefinitionParams request, CancellationToken cancellationToken)
{ {
var uri = request.TextDocument.Uri; var uri = request.TextDocument.Uri;
var compilationUnit = workspaceManager.GetCompilationUnit(uri); var topLevelNodes = workspaceManager.GetTopLevelNodes(uri.GetFileSystemPath());
if (compilationUnit == null)
{
return null;
}
var line = request.Position.Line; var line = request.Position.Line;
var character = request.Position.Character; var character = request.Position.Character;
var node = compilationUnit.DeepestNodeAtPosition(line, character); var node = topLevelNodes.DeepestNodeAtPosition(line, character);
switch (node) switch (node)
{ {
case VariableIdentifierNode variableIdentifierNode: 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) if (parameter != null)
{ {
return new LocationOrLocationLinks(parameter.ToLocation()); return new LocationOrLocationLinks(parameter.NameToken.ToLocation());
} }
var variable = function?.Body? var variable = funcNode?.Body?
.Descendants() .Descendants()
.OfType<VariableDeclarationNode>() .OfType<VariableDeclarationNode>()
.FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value); .FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value);
if (variable != null) if (variable != null)
{ {
return new LocationOrLocationLinks(variable.ToLocation()); return new LocationOrLocationLinks(variable.NameToken.ToLocation());
}
return null;
}
case LocalFuncIdentifierNode localFuncIdentifierNode:
{
var funcNode = topLevelNodes.OfType<FuncNode>().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; return null;

View File

@@ -25,24 +25,15 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas
private Hover? HandleSync(HoverParams request, CancellationToken cancellationToken) private Hover? HandleSync(HoverParams request, CancellationToken cancellationToken)
{ {
var compilationUnit = workspaceManager.GetCompilationUnit(request.TextDocument.Uri); var topLevelNodes = workspaceManager.GetTopLevelNodes(request.TextDocument.Uri.GetFileSystemPath());
if (compilationUnit == null)
{
return null;
}
var moduleDecl = compilationUnit.OfType<ModuleNode>().FirstOrDefault(); var moduleDecl = topLevelNodes.OfType<ModuleNode>().FirstOrDefault();
if (moduleDecl == null) if (moduleDecl == null)
{ {
return null; return null;
} }
var moduleRepository = workspaceManager.GetModuleRepository(); var moduleRepository = workspaceManager.GetModuleRepository();
if (moduleRepository == null)
{
return null;
}
if (!moduleRepository.TryGet(moduleDecl.NameToken, out var module)) if (!moduleRepository.TryGet(moduleDecl.NameToken, out var module))
{ {
return null; return null;
@@ -51,14 +42,14 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas
var line = request.Position.Line; var line = request.Position.Line;
var character = request.Position.Character; var character = request.Position.Character;
var hoveredNode = compilationUnit.DeepestNodeAtPosition(line, character); var hoveredNode = topLevelNodes.DeepestNodeAtPosition(line, character);
if (hoveredNode == null) if (hoveredNode == null)
{ {
return null; return null;
} }
var message = CreateMessage(hoveredNode, moduleRepository, module); var message = CreateMessage(hoveredNode, moduleRepository, module, line, character);
if (message == null) if (message == null)
{ {
return 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 return hoveredNode switch
{ {
@@ -99,6 +90,7 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas
U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()), U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()),
U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()), U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()),
U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.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 // Expressions can have a generic fallback showing the resulting type
ExpressionNode expressionNode => $""" ExpressionNode expressionNode => $"""
**Expression** `{expressionNode.GetType().Name}` **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) private static string CreateLiteralMessage(NubType type, string value)
{ {
return $""" return $"""

View File

@@ -15,25 +15,25 @@ internal class TextDocumentSyncHandler(WorkspaceManager workspaceManager) : Text
public override Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken) public override Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
{ {
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); workspaceManager.Update();
return Unit.Task; return Unit.Task;
} }
public override Task<Unit> Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken) public override Task<Unit> Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken)
{ {
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); workspaceManager.Update();
return Unit.Task; return Unit.Task;
} }
public override Task<Unit> Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken) public override Task<Unit> Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken)
{ {
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); workspaceManager.Update();
return Unit.Task; return Unit.Task;
} }
public override Task<Unit> Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken) public override Task<Unit> Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken)
{ {
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath()); workspaceManager.Update();
return Unit.Task; return Unit.Task;
} }

View File

@@ -2,100 +2,67 @@ using NubLang.Ast;
using NubLang.Diagnostics; using NubLang.Diagnostics;
using NubLang.Modules; using NubLang.Modules;
using NubLang.Syntax; using NubLang.Syntax;
using OmniSharp.Extensions.LanguageServer.Protocol;
namespace NubLang.LSP; namespace NubLang.LSP;
public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher) public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
{ {
private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new(); private record Unit(SyntaxTree SyntaxTree, DateTimeOffset FileTimestamp, List<Diagnostic> Diagnostics);
private readonly Dictionary<string, List<TopLevelNode>> _compilationUnits = new();
private ModuleRepository? _repository; private readonly Tokenizer _tokenizer = new();
private readonly Parser _parser = new();
private readonly TypeChecker _typeChecker = new();
private string? _rootPath;
private readonly Dictionary<string, Unit> _units = [];
private readonly Dictionary<string, List<TopLevelNode>> _possiblyOutdatedTopLevelNodes = [];
private ModuleRepository _repository = new([]);
public void Init(string rootPath) public void Init(string rootPath)
{ {
var files = Directory.GetFiles(rootPath, "*.nub", SearchOption.AllDirectories); _rootPath = rootPath;
foreach (var path in files) 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 lastUpdated = File.GetLastWriteTimeUtc(file);
var tokenizer = new Tokenizer(); var unit = _units.GetValueOrDefault(file);
if (unit == null || lastUpdated > unit.FileTimestamp)
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
{ {
repository = ModuleRepository.Create(_syntaxTrees.Select(x => x.Value).ToList()); _units[file] = Update(file, lastUpdated);
}
catch (CompileException e)
{
return;
} }
}
var typeChecker = new TypeChecker(); _repository = ModuleRepository.Create(_units.Select(x => x.Value.SyntaxTree).ToList());
var result = typeChecker.Check(syntaxTree, repository);
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics); foreach (var (file, unit) in _units)
_compilationUnits[fsPath] = result; {
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); return new Unit(syntaxTree, lastUpdated, [.._tokenizer.Diagnostics, .._parser.Diagnostics]);
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;
} }
public List<TopLevelNode>? GetCompilationUnit(DocumentUri path) public List<TopLevelNode> GetTopLevelNodes(string path)
{ {
return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath()); return _possiblyOutdatedTopLevelNodes.GetValueOrDefault(path, []);
} }
public ModuleRepository? GetModuleRepository() public ModuleRepository GetModuleRepository()
{ {
try return _repository;
{
return ModuleRepository.Create(_syntaxTrees.Select(x => x.Value).ToList());
}
catch (CompileException e)
{
return null;
}
} }
} }

View File

@@ -106,7 +106,7 @@ public class Diagnostic
var text = File.ReadAllText(Span.Value.FilePath); var text = File.ReadAllText(Span.Value.FilePath);
var tokenizer = new Tokenizer(); var tokenizer = new Tokenizer();
tokenizer.Tokenize(Span.Value.FilePath, text); var tokens = tokenizer.Tokenize(Span.Value.FilePath, text);
var lines = text.Split('\n'); var lines = text.Split('\n');
@@ -144,7 +144,7 @@ public class Diagnostic
sb.Append("│ "); sb.Append("│ ");
sb.Append(i.ToString().PadRight(numberPadding)); sb.Append(i.ToString().PadRight(numberPadding));
sb.Append(" │ "); 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(line.PadRight(codePadding));
sb.Append(" │"); sb.Append(" │");
sb.AppendLine(); sb.AppendLine();

View File

@@ -1,5 +1,6 @@
using System.Text; using System.Text;
using NubLang.Ast; using NubLang.Ast;
using NubLang.Modules;
using NubLang.Types; using NubLang.Types;
namespace NubLang.Generation; namespace NubLang.Generation;
@@ -12,7 +13,7 @@ public class LlvmGenerator
private List<(string Name, int Size, string Text)> _stringLiterals = []; private List<(string Name, int Size, string Text)> _stringLiterals = [];
private Stack<(string breakLabel, string continueLabel)> _loopStack = []; private Stack<(string breakLabel, string continueLabel)> _loopStack = [];
public string Emit(List<TopLevelNode> topLevelNodes) public string Emit(List<TopLevelNode> topLevelNodes, ModuleRepository repository)
{ {
_stringLiterals = []; _stringLiterals = [];
_loopStack = []; _loopStack = [];
@@ -26,6 +27,30 @@ public class LlvmGenerator
writer.WriteLine("declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)"); writer.WriteLine("declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)");
writer.WriteLine(); writer.WriteLine();
var declaredExternFunctions = new HashSet<string>();
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<FuncNode>().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<StructNode>()) foreach (var structNode in topLevelNodes.OfType<StructNode>())
{ {
var types = structNode.Fields.Select(x => MapType(x.Type)); var types = structNode.Fields.Select(x => MapType(x.Type));
@@ -57,15 +82,6 @@ public class LlvmGenerator
writer.WriteLine(); writer.WriteLine();
} }
foreach (var funcNode in topLevelNodes.OfType<FuncNode>())
{
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<FuncNode>()) foreach (var funcNode in topLevelNodes.OfType<FuncNode>())
{ {
if (funcNode.Body == null) continue; if (funcNode.Body == null) continue;
@@ -335,7 +351,8 @@ public class LlvmGenerator
Float32LiteralNode float32LiteralNode => EmitFloat32Literal(writer, float32LiteralNode), Float32LiteralNode float32LiteralNode => EmitFloat32Literal(writer, float32LiteralNode),
Float64LiteralNode float64LiteralNode => EmitFloat64Literal(writer, float64LiteralNode), Float64LiteralNode float64LiteralNode => EmitFloat64Literal(writer, float64LiteralNode),
FuncCallNode funcCallNode => EmitFuncCall(writer, funcCallNode), 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), I16LiteralNode i16LiteralNode => EmitI16Literal(writer, i16LiteralNode),
I32LiteralNode i32LiteralNode => EmitI32Literal(writer, i32LiteralNode), I32LiteralNode i32LiteralNode => EmitI32Literal(writer, i32LiteralNode),
I64LiteralNode i64LiteralNode => EmitI64Literal(writer, i64LiteralNode), I64LiteralNode i64LiteralNode => EmitI64Literal(writer, i64LiteralNode),
@@ -769,12 +786,18 @@ public class LlvmGenerator
return new Tmp(result, funcCallNode.Type, false); 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); var name = FuncName(moduleFuncIdentifierNode.ModuleToken.Value, moduleFuncIdentifierNode.NameToken.Value, moduleFuncIdentifierNode.ExternSymbolToken?.Value);
return new Tmp($"@{name}", moduleFuncIdentifierNode.Type, false); 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) private Tmp EmitI16Literal(IndentedTextWriter writer, I16LiteralNode i16LiteralNode)
{ {
return new Tmp(i16LiteralNode.Value.ToString(), i16LiteralNode.Type, false); return new Tmp(i16LiteralNode.Value.ToString(), i16LiteralNode.Type, false);

View File

@@ -143,7 +143,7 @@ public sealed class ModuleRepository
} }
} }
private ModuleRepository(Dictionary<string, Module> modules) public ModuleRepository(Dictionary<string, Module> modules)
{ {
_modules = modules; _modules = modules;
} }
@@ -167,6 +167,17 @@ public sealed class ModuleRepository
return module != null; return module != null;
} }
public bool TryGet(string name, [NotNullWhen(true)] out Module? module)
{
module = _modules.GetValueOrDefault(name);
return module != null;
}
public List<Module> GetAll()
{
return _modules.Values.ToList();
}
public sealed class Module public sealed class Module
{ {
public required string Name { get; init; } public required string Name { get; init; }
@@ -174,6 +185,21 @@ public sealed class ModuleRepository
public required List<NubStructType> StructTypes { get; init; } = []; public required List<NubStructType> StructTypes { get; init; } = [];
public required Dictionary<string, NubIntType> EnumTypes { get; init; } = []; public required Dictionary<string, NubIntType> 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) public bool TryResolveFunc(IdentifierToken name, [NotNullWhen(true)] out FuncPrototypeNode? value, [NotNullWhen(false)] out Diagnostic? diagnostic)
{ {
value = FunctionPrototypes.FirstOrDefault(x => x.NameToken.Value == name.Value); value = FunctionPrototypes.FirstOrDefault(x => x.NameToken.Value == name.Value);
@@ -199,6 +225,21 @@ public sealed class ModuleRepository
return value; 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) public bool TryResolveStruct(IdentifierToken name, [NotNullWhen(true)] out NubStructType? value, [NotNullWhen(false)] out Diagnostic? diagnostic)
{ {
value = StructTypes.FirstOrDefault(x => x.Name == name.Value); value = StructTypes.FirstOrDefault(x => x.Name == name.Value);
@@ -224,6 +265,21 @@ public sealed class ModuleRepository
return value; 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) public bool TryResolveEnum(IdentifierToken name, [NotNullWhen(true)] out NubIntType? value, [NotNullWhen(false)] out Diagnostic? diagnostic)
{ {
value = EnumTypes.GetValueOrDefault(name.Value); value = EnumTypes.GetValueOrDefault(name.Value);

View File

@@ -417,7 +417,7 @@ public sealed class Parser
case Symbol.Pipe: case Symbol.Pipe:
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr; binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr;
return true; return true;
case Symbol.XOr: case Symbol.Tilde:
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor; binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor;
return true; return true;
default: default:
@@ -439,12 +439,12 @@ public sealed class Parser
IdentifierToken identifier => ParseIdentifier(startIndex, identifier), IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
SymbolToken symbolToken => symbolToken.Symbol switch SymbolToken symbolToken => symbolToken.Symbol switch
{ {
Symbol.Ampersand => new AddressOfSyntax(GetTokens(startIndex), ParsePrimaryExpression()), Symbol.Ampersand => ParseAddressOf(startIndex),
Symbol.OpenParen => ParseParenthesizedExpression(), Symbol.OpenParen => ParseParenthesizedExpression(),
Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()), Symbol.Minus => ParseUnaryNegate(startIndex),
Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()), Symbol.Bang => ParseUnaryInvert(startIndex),
Symbol.OpenBracket => ParseArrayInitializer(startIndex), Symbol.OpenBracket => ParseArrayInitializer(startIndex),
Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()), Symbol.OpenBrace => ParseUnnamedStructInitializer(startIndex),
Symbol.Struct => ParseStructInitializer(startIndex), Symbol.Struct => ParseStructInitializer(startIndex),
Symbol.At => ParseBuiltinFunction(startIndex), Symbol.At => ParseBuiltinFunction(startIndex),
_ => throw new CompileException(Diagnostic _ => throw new CompileException(Diagnostic
@@ -463,6 +463,24 @@ public sealed class Parser
return ParsePostfixOperators(expr); 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) private ExpressionSyntax ParseBuiltinFunction(int startIndex)
{ {
var name = ExpectIdentifier(); var name = ExpectIdentifier();
@@ -587,6 +605,12 @@ public sealed class Parser
return new StructInitializerSyntax(GetTokens(startIndex), type, initializers); return new StructInitializerSyntax(GetTokens(startIndex), type, initializers);
} }
private StructInitializerSyntax ParseUnnamedStructInitializer(int startIndex)
{
var body = ParseStructInitializerBody();
return new StructInitializerSyntax(GetTokens(startIndex), null, body);
}
private Dictionary<IdentifierToken, ExpressionSyntax> ParseStructInitializerBody() private Dictionary<IdentifierToken, ExpressionSyntax> ParseStructInitializerBody()
{ {
Dictionary<IdentifierToken, ExpressionSyntax> initializers = []; Dictionary<IdentifierToken, ExpressionSyntax> initializers = [];

View File

@@ -121,6 +121,7 @@ public enum Symbol
Star, Star,
ForwardSlash, ForwardSlash,
Caret, Caret,
Tilde,
Ampersand, Ampersand,
Semi, Semi,
Percent, Percent,
@@ -129,7 +130,6 @@ public enum Symbol
Pipe, Pipe,
And, And,
Or, Or,
XOr,
At, At,
QuestionMark, QuestionMark,
} }
@@ -189,6 +189,7 @@ public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span)
Symbol.Pipe => "|", Symbol.Pipe => "|",
Symbol.At => "@", Symbol.At => "@",
Symbol.QuestionMark => "?", Symbol.QuestionMark => "?",
Symbol.Tilde => "~",
_ => "none", _ => "none",
}; };
} }

View File

@@ -11,20 +11,20 @@ public sealed class Tokenizer
private int _column = 1; private int _column = 1;
public List<Diagnostic> Diagnostics { get; set; } = new(16); public List<Diagnostic> Diagnostics { get; set; } = new(16);
public List<Token> Tokens { get; set; } = new(256);
public void Tokenize(string fileName, string content) public List<Token> Tokenize(string fileName, string content)
{ {
_fileName = fileName; _fileName = fileName;
_content = content; _content = content;
Diagnostics = []; Diagnostics = [];
Tokens = [];
_index = 0; _index = 0;
_line = 1; _line = 1;
_column = 1; _column = 1;
var tokens = new List<Token>();
while (_index < _content.Length) while (_index < _content.Length)
{ {
try try
@@ -54,7 +54,7 @@ public sealed class Tokenizer
continue; continue;
} }
Tokens.Add(ParseToken(current, _line, _column)); tokens.Add(ParseToken(current, _line, _column));
} }
catch (CompileException e) catch (CompileException e)
{ {
@@ -62,6 +62,8 @@ public sealed class Tokenizer
Next(); Next();
} }
} }
return tokens;
} }
private Token ParseToken(char current, int lineStart, int columnStart) private Token ParseToken(char current, int lineStart, int columnStart)
@@ -295,7 +297,6 @@ public sealed class Tokenizer
"&&" => Symbol.And, "&&" => Symbol.And,
"||" => Symbol.Or, "||" => Symbol.Or,
"::" => Symbol.DoubleColon, "::" => Symbol.DoubleColon,
"x|" => Symbol.XOr,
_ => Symbol.None _ => Symbol.None
}, },
1 => span[0] switch 1 => span[0] switch
@@ -324,6 +325,7 @@ public sealed class Tokenizer
'|' => Symbol.Pipe, '|' => Symbol.Pipe,
'@' => Symbol.At, '@' => Symbol.At,
'?' => Symbol.QuestionMark, '?' => Symbol.QuestionMark,
'~' => Symbol.Tilde,
_ => Symbol.None _ => Symbol.None
}, },
_ => Symbol.None _ => Symbol.None

View File

@@ -2,5 +2,5 @@
set -euo pipefail set -euo pipefail
nubc main.nub nubc main.nub test.nub
clang .build/main.ll -o .build/out clang .build/main.ll .build/test.ll -o .build/out

View File

@@ -9,5 +9,9 @@ struct Test
extern "main" func main(argc: i64, argv: [?]^i8) extern "main" func main(argc: i64, argv: [?]^i8)
{ {
let x: Test = {} let x: Test = {
field = 23
}
test::test()
} }

View File

@@ -0,0 +1,8 @@
module test
extern "puts" func puts(text: ^i8)
func test()
{
puts("uwu")
}

View File

@@ -1,5 +1,3 @@
import raylib
module main module main
extern "main" func main(argc: i64, argv: [?]^i8): i64 extern "main" func main(argc: i64, argv: [?]^i8): i64