From 3712a159e2f2aa2c9c464ce1b3128ac4b8d4dde3 Mon Sep 17 00:00:00 2001 From: nub31 Date: Thu, 11 Sep 2025 21:50:49 +0200 Subject: [PATCH] ... --- compiler/NubLang.CLI/Program.cs | 74 +++++-------------- .../NubLang/Generation/QBE/QBEGenerator.cs | 18 ++--- compiler/NubLang/Parsing/Parser.cs | 2 +- .../Parsing/Syntax/ExpressionSyntax.cs | 4 +- compiler/NubLang/Parsing/Syntax/SyntaxNode.cs | 2 +- compiler/NubLang/TypeChecking/Module.cs | 66 +---------------- .../TypeChecking/Node/DefinitionNode.cs | 8 +- compiler/NubLang/TypeChecking/Node/Node.cs | 4 +- compiler/NubLang/TypeChecking/TypeChecker.cs | 69 ++++++++--------- example/main.nub | 2 +- example/makefile | 9 +-- 11 files changed, 80 insertions(+), 178 deletions(-) diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index 8421840..bd824c7 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using NubLang.CLI; +using NubLang.CLI; using NubLang.Code; using NubLang.Diagnostics; using NubLang.Generation.QBE; @@ -7,7 +6,7 @@ using NubLang.Parsing; using NubLang.Parsing.Syntax; using NubLang.Tokenization; using NubLang.TypeChecking; -using Module = NubLang.TypeChecking.Module; +using NubLang.TypeChecking.Node; var options = new Options(); @@ -62,16 +61,15 @@ foreach (var file in options.Files) } var moduleSignatures = ModuleSignature.CollectFromSyntaxTrees(syntaxTrees); -var modules = Module.CollectFromSyntaxTrees(syntaxTrees); -var typedModules = new List(); +var definitions = new List(); -foreach (var module in modules) +foreach (var syntaxTree in syntaxTrees) { - var typeChecker = new TypeChecker(module, moduleSignatures); - var typedModule = typeChecker.CheckModule(); + var typeChecker = new TypeChecker(syntaxTree, moduleSignatures); + var checkedDefinitions = typeChecker.Check(); diagnostics.AddRange(typeChecker.GetDiagnostics()); - typedModules.Add(typedModule); + definitions.AddRange(checkedDefinitions); } foreach (var diagnostic in diagnostics) @@ -84,55 +82,19 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro return 1; } -var objectFiles = new List(); +Directory.CreateDirectory(".build"); -for (var i = 0; i < typedModules.Count; i++) -{ - var typedModule = typedModules[i]; - var outFileName = Path.Combine(".build", Path.ChangeExtension(options.Files[i].Path, null)); +var generator = new QBEGenerator(definitions, moduleSignatures); +var ssa = generator.Emit(); +var ssaFilePath = Path.Combine(".build", "out.ssa"); +File.WriteAllText(ssaFilePath, ssa); - var outFileDir = Path.GetDirectoryName(outFileName); - if (!string.IsNullOrEmpty(outFileDir)) - { - Directory.CreateDirectory(outFileDir); - } +var asmFilePath = Path.Combine(".build", "out.asm"); +var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath); +if (!qbeSuccess) return 1; - var generator = new QBEGenerator(typedModule, moduleSignatures); - var ssa = generator.Emit(); - - var ssaFilePath = Path.ChangeExtension(outFileName, "ssa"); - File.WriteAllText(ssaFilePath, ssa); - - var asmFilePath = Path.ChangeExtension(outFileName, "s"); - var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath); - - if (!qbeSuccess) - { - return 1; - } - - var objFilePath = Path.ChangeExtension(outFileName, "o"); - var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath); - - if (!asmSuccess) - { - return 1; - } - - objectFiles.Add(objFilePath); -} - -var outPath = options.OutputPath ?? Path.Combine("out.a"); -var outDir = Path.GetDirectoryName(outPath); -if (!string.IsNullOrEmpty(outDir)) -{ - Directory.CreateDirectory(outDir); -} - -var archiveResult = await Archive.Invoke(outPath, objectFiles); -if (!archiveResult) -{ - return 1; -} +var objFilePath = Path.Combine(".build", "out.o"); +var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath); +if (!asmSuccess) return 1; return 0; \ No newline at end of file diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 6040310..e4d3eac 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -10,7 +10,7 @@ namespace NubLang.Generation.QBE; public class QBEGenerator { private readonly QBEWriter _writer; - private readonly TypedModule _module; + private readonly IReadOnlyList _definitions; private readonly IReadOnlyDictionary _moduleSignatures; private readonly List _cStringLiterals = []; @@ -23,9 +23,9 @@ public class QBEGenerator private int _stringLiteralIndex; private bool _codeIsReachable = true; - public QBEGenerator(TypedModule module, IReadOnlyDictionary moduleSignatures) + public QBEGenerator(IReadOnlyList definitions, IReadOnlyDictionary moduleSignatures) { - _module = module; + _definitions = definitions; _moduleSignatures = moduleSignatures; _writer = new QBEWriter(); } @@ -51,19 +51,19 @@ public class QBEGenerator } } - foreach (var structDef in _module.Definitions.OfType()) + foreach (var structDef in _definitions.OfType()) { EmitStructDefinition(structDef); _writer.NewLine(); } - foreach (var funcDef in _module.Definitions.OfType()) + foreach (var funcDef in _definitions.OfType()) { EmitFuncDefinition(funcDef); _writer.NewLine(); } - // foreach (var structDef in _module.Definitions.OfType().Where(x => x.InterfaceImplementations.Count > 0)) + // foreach (var structDef in _definitions.OfType().Where(x => x.InterfaceImplementations.Count > 0)) // { // _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ "); // @@ -386,7 +386,7 @@ public class QBEGenerator _writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' '); } - _writer.Write(FuncName(_module.Name, funcDef.Name)); + _writer.Write(FuncName(funcDef.Module, funcDef.Name)); _writer.Write("("); foreach (var parameter in funcDef.Signature.Parameters) @@ -410,7 +410,7 @@ public class QBEGenerator private void EmitStructDefinition(StructNode structDef) { - var type = TypeResolver.ResolveStructType(_module.Name, structDef.Name, _moduleSignatures); + var type = TypeResolver.ResolveStructType(structDef.Module, structDef.Name, _moduleSignatures); // _writer.WriteLine($"export function {StructCtorName(_module.Name, structDef.Name)}() {{"); // _writer.WriteLine("@start"); @@ -432,7 +432,7 @@ public class QBEGenerator _writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' '); } - _writer.Write(StructFuncName(_module.Name, structDef.Name, function.Name)); + _writer.Write(StructFuncName(structDef.Module, structDef.Name, function.Name)); _writer.Write("(l %this, "); foreach (var parameter in function.Signature.Parameters) diff --git a/compiler/NubLang/Parsing/Parser.cs b/compiler/NubLang/Parsing/Parser.cs index 3766e34..f4f40cc 100644 --- a/compiler/NubLang/Parsing/Parser.cs +++ b/compiler/NubLang/Parsing/Parser.cs @@ -474,7 +474,7 @@ public sealed class Parser var expr = token switch { LiteralToken literal => new LiteralSyntax(GetTokens(startIndex), literal.Value, literal.Kind), - IdentifierToken identifier => new IdentifierSyntax(GetTokens(startIndex), Optional.Empty(), identifier.Value), + IdentifierToken identifier => new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value), SymbolToken symbolToken => symbolToken.Symbol switch { Symbol.OpenParen => ParseParenthesizedExpression(), diff --git a/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs b/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs index f5f4a30..ea21aa7 100644 --- a/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs +++ b/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs @@ -40,7 +40,9 @@ public record FuncCallSyntax(IEnumerable Tokens, ExpressionSyntax Express public record DotFuncCallSyntax(IEnumerable Tokens, string Name, ExpressionSyntax ThisParameter, IReadOnlyList Parameters) : ExpressionSyntax(Tokens); -public record IdentifierSyntax(IEnumerable Tokens, Optional Module, string Name) : ExpressionSyntax(Tokens); +public record LocalIdentifierSyntax(IEnumerable Tokens, string Name) : ExpressionSyntax(Tokens); + +public record ModuleIdentifierSyntax(IEnumerable Tokens, string Module, string Name) : ExpressionSyntax(Tokens); public record ArrayInitializerSyntax(IEnumerable Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); diff --git a/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs b/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs index 7435d18..57a85f6 100644 --- a/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs +++ b/compiler/NubLang/Parsing/Syntax/SyntaxNode.cs @@ -4,7 +4,7 @@ namespace NubLang.Parsing.Syntax; public abstract record SyntaxNode(IEnumerable Tokens); -public record SyntaxTreeMetadata(string? ModuleName, IReadOnlyList Imports); +public record SyntaxTreeMetadata(string ModuleName, IReadOnlyList Imports); public record SyntaxTree(IReadOnlyList Definitions, SyntaxTreeMetadata Metadata); diff --git a/compiler/NubLang/TypeChecking/Module.cs b/compiler/NubLang/TypeChecking/Module.cs index 5a39eeb..365394b 100644 --- a/compiler/NubLang/TypeChecking/Module.cs +++ b/compiler/NubLang/TypeChecking/Module.cs @@ -3,66 +3,6 @@ using NubLang.TypeChecking.Node; namespace NubLang.TypeChecking; -public class Module -{ - public static IReadOnlyList CollectFromSyntaxTrees(IReadOnlyList syntaxTrees) - { - var modules = new Dictionary(); - - foreach (var syntaxTree in syntaxTrees) - { - var name = syntaxTree.Metadata.ModuleName; - if (name == null) - { - continue; - } - - if (!modules.TryGetValue(name, out var module)) - { - module = new Module(name, syntaxTree.Metadata.Imports); - modules[name] = module; - } - - foreach (var definition in syntaxTree.Definitions) - { - module.AddDefinition(definition); - } - } - - return modules.Values.ToList(); - } - - private readonly List _definitions = []; - - public Module(string name, IReadOnlyList imports) - { - Name = name; - Imports = imports; - } - - public string Name { get; } - public IReadOnlyList Imports { get; } - - public IReadOnlyList Definitions => _definitions; - - private void AddDefinition(DefinitionSyntax syntax) - { - _definitions.Add(syntax); - } -} - -public class TypedModule -{ - public TypedModule(string name, IReadOnlyList definitions) - { - Name = name; - Definitions = definitions; - } - - public string Name { get; } - public IReadOnlyList Definitions { get; } -} - public class ModuleSignature { public static IReadOnlyDictionary CollectFromSyntaxTrees(IReadOnlyList syntaxTrees) @@ -72,10 +12,6 @@ public class ModuleSignature foreach (var syntaxTree in syntaxTrees) { var moduleName = syntaxTree.Metadata.ModuleName; - if (moduleName == null) - { - continue; - } if (!modules.TryGetValue(moduleName, out var module)) { @@ -85,7 +21,7 @@ public class ModuleSignature foreach (var def in syntaxTree.Definitions) { - if (!def.Exported) continue; + // if (!def.Exported) continue; switch (def) { diff --git a/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs b/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs index 2169f79..b851d38 100644 --- a/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs +++ b/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs @@ -1,19 +1,19 @@ namespace NubLang.TypeChecking.Node; -public abstract record DefinitionNode : Node; +public abstract record DefinitionNode(string Module, string Name) : Node; public record FuncParameterNode(string Name, TypeNode Type) : Node; public record FuncSignatureNode(IReadOnlyList Parameters, TypeNode ReturnType) : Node; -public record FuncNode(string Name, FuncSignatureNode Signature, BlockNode? Body) : DefinitionNode; +public record FuncNode(string Module, string Name, FuncSignatureNode Signature, BlockNode? Body) : DefinitionNode(Module, Name); public record StructFieldNode(int Index, string Name, TypeNode Type, Optional Value) : Node; public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node; -public record StructNode(string Name, IReadOnlyList Fields, IReadOnlyList Functions, IReadOnlyList InterfaceImplementations) : DefinitionNode; +public record StructNode(string Module, string Name, IReadOnlyList Fields, IReadOnlyList Functions, IReadOnlyList InterfaceImplementations) : DefinitionNode(Module, Name); public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node; -public record InterfaceNode(string Name, IReadOnlyList Functions) : DefinitionNode; \ No newline at end of file +public record InterfaceNode(string Module, string Name, IReadOnlyList Functions) : DefinitionNode(Module, Name); \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/Node/Node.cs b/compiler/NubLang/TypeChecking/Node/Node.cs index 4b75960..6a4b83b 100644 --- a/compiler/NubLang/TypeChecking/Node/Node.cs +++ b/compiler/NubLang/TypeChecking/Node/Node.cs @@ -2,4 +2,6 @@ public abstract record Node; -public record BlockNode(IReadOnlyList Statements) : Node; \ No newline at end of file +public record BlockNode(IReadOnlyList Statements) : Node; + +public record TypedSyntaxTree(IReadOnlyList Definitions); \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index 972a6d7..114ba37 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -7,7 +7,7 @@ namespace NubLang.TypeChecking; public sealed class TypeChecker { - private readonly Module _currentModule; + private readonly SyntaxTree _syntaxTree; private readonly IReadOnlyDictionary _moduleSignatures; private readonly Stack _scopes = []; @@ -16,22 +16,22 @@ public sealed class TypeChecker private Scope Scope => _scopes.Peek(); - public TypeChecker(Module currentModule, IReadOnlyDictionary moduleSignatures) + public TypeChecker(SyntaxTree syntaxTree, IReadOnlyDictionary moduleSignatures) { - _currentModule = currentModule; - _moduleSignatures = moduleSignatures.Where(x => currentModule.Imports.Contains(x.Key) || _currentModule.Name == x.Key).ToDictionary(); + _syntaxTree = syntaxTree; + _moduleSignatures = moduleSignatures.Where(x => syntaxTree.Metadata.Imports.Contains(x.Key) || _syntaxTree.Metadata.ModuleName == x.Key).ToDictionary(); } public IReadOnlyList GetDiagnostics() => _diagnostics; - public TypedModule CheckModule() + public IReadOnlyList Check() { _diagnostics.Clear(); _scopes.Clear(); var definitions = new List(); - foreach (var definition in _currentModule.Definitions) + foreach (var definition in _syntaxTree.Definitions) { try { @@ -43,7 +43,7 @@ public sealed class TypeChecker } } - return new TypedModule(_currentModule.Name, definitions); + return definitions; } private DefinitionNode CheckDefinition(DefinitionSyntax node) @@ -105,7 +105,7 @@ public sealed class TypeChecker interfaceImplementations.Add(interfaceType); } - return new StructNode(node.Name, fields, functions, interfaceImplementations); + return new StructNode(_syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations); } private FuncNode CheckFuncDefinition(FuncSyntax node) @@ -124,7 +124,7 @@ public sealed class TypeChecker _funcReturnTypes.Pop(); } - return new FuncNode(node.Name, CheckFuncSignature(node.Signature), body); + return new FuncNode(_syntaxTree.Metadata.ModuleName, node.Name, CheckFuncSignature(node.Signature), body); } private StatementNode CheckStatement(StatementSyntax node) @@ -223,7 +223,8 @@ public sealed class TypeChecker DereferenceSyntax expression => CheckDereference(expression), DotFuncCallSyntax expression => CheckDotFuncCall(expression), FuncCallSyntax expression => CheckFuncCall(expression), - IdentifierSyntax expression => CheckIdentifier(expression), + LocalIdentifierSyntax expression => CheckLocalIdentifier(expression), + ModuleIdentifierSyntax expression => CheckModuleIdentifier(expression), LiteralSyntax expression => CheckLiteral(expression, expectedType), StructFieldAccessSyntax expression => CheckStructFieldAccess(expression), StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType), @@ -321,44 +322,44 @@ public sealed class TypeChecker throw new NotImplementedException(); } - private ExpressionNode CheckIdentifier(IdentifierSyntax expression) + private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression) { - // If the identifier does not have a module specified, first check if a local variable or function parameter with that identifier exists - if (!expression.Module.TryGetValue(out var moduleName)) + // First, look in the current scope for a matching identifier + var scopeIdent = Scope.Lookup(expression.Name); + if (scopeIdent != null) { - var scopeIdent = Scope.Lookup(expression.Name); - if (scopeIdent != null) + switch (scopeIdent.Kind) { - switch (scopeIdent.Kind) + case IdentifierKind.Variable: { - case IdentifierKind.Variable: - { - return new VariableIdentifierNode(scopeIdent.Type, expression.Name); - } - case IdentifierKind.FunctionParameter: - { - return new FuncParameterIdentifierNode(scopeIdent.Type, expression.Name); - } - default: - { - throw new ArgumentOutOfRangeException(); - } + return new VariableIdentifierNode(scopeIdent.Type, expression.Name); + } + case IdentifierKind.FunctionParameter: + { + return new FuncParameterIdentifierNode(scopeIdent.Type, expression.Name); + } + default: + { + throw new ArgumentOutOfRangeException(); } } } - moduleName ??= _currentModule.Name; - if (_moduleSignatures.TryGetValue(moduleName, out var module)) + // Second, look in the current module for a function matching the identifier + var module = _moduleSignatures[_syntaxTree.Metadata.ModuleName]; + if (module.Functions.TryGetValue(expression.Name, out var function)) { - if (module.Functions.TryGetValue(expression.Name, out var function)) - { - return new FuncIdentifierNode(function, moduleName, expression.Name); - } + return new FuncIdentifierNode(function, _syntaxTree.Metadata.ModuleName, expression.Name); } throw new TypeCheckerException(Diagnostic.Error($"Identifier {expression.Name} not found").At(expression).Build()); } + private ExpressionNode CheckModuleIdentifier(ModuleIdentifierSyntax expression) + { + throw new NotImplementedException(); + } + private LiteralNode CheckLiteral(LiteralSyntax expression, TypeNode? expectedType) { // todo(nub31): Check if the types can actually be represented as another one. For example, an int should be passed when a string is expected diff --git a/example/main.nub b/example/main.nub index 1586d8c..5e82dfc 100644 --- a/example/main.nub +++ b/example/main.nub @@ -2,7 +2,7 @@ module "main" extern "puts" func puts(text: cstring) -extern "main" func main(args: []cstring): i64 +func main(args: []cstring): i64 { puts("test") return 0 diff --git a/example/makefile b/example/makefile index f63b491..d2eebc9 100644 --- a/example/makefile +++ b/example/makefile @@ -1,10 +1,9 @@ NUBC = ../compiler/NubLang.CLI/bin/Debug/net9.0/nubc -out: out.a - gcc -nostartfiles -o out x86_64.s out.a - rm out.a +out: .build/out.o + gcc -nostartfiles -o out x86_64.s .build/out.o -out.a: $(NUBC) main.nub +.build/out.o: $(NUBC) main.nub $(NUBC) main.nub .PHONY: $(NUBC) @@ -13,4 +12,4 @@ $(NUBC): clean: @rm -r .build 2>/dev/null || true - @rm out.a 2>/dev/null || true + @rm out 2>/dev/null || true