This commit is contained in:
nub31
2025-09-11 21:50:49 +02:00
parent fd27d2709d
commit 3712a159e2
11 changed files with 80 additions and 178 deletions

View File

@@ -1,5 +1,4 @@
using System.Reflection; using NubLang.CLI;
using NubLang.CLI;
using NubLang.Code; using NubLang.Code;
using NubLang.Diagnostics; using NubLang.Diagnostics;
using NubLang.Generation.QBE; using NubLang.Generation.QBE;
@@ -7,7 +6,7 @@ using NubLang.Parsing;
using NubLang.Parsing.Syntax; using NubLang.Parsing.Syntax;
using NubLang.Tokenization; using NubLang.Tokenization;
using NubLang.TypeChecking; using NubLang.TypeChecking;
using Module = NubLang.TypeChecking.Module; using NubLang.TypeChecking.Node;
var options = new Options(); var options = new Options();
@@ -62,16 +61,15 @@ foreach (var file in options.Files)
} }
var moduleSignatures = ModuleSignature.CollectFromSyntaxTrees(syntaxTrees); var moduleSignatures = ModuleSignature.CollectFromSyntaxTrees(syntaxTrees);
var modules = Module.CollectFromSyntaxTrees(syntaxTrees);
var typedModules = new List<TypedModule>(); var definitions = new List<DefinitionNode>();
foreach (var module in modules) foreach (var syntaxTree in syntaxTrees)
{ {
var typeChecker = new TypeChecker(module, moduleSignatures); var typeChecker = new TypeChecker(syntaxTree, moduleSignatures);
var typedModule = typeChecker.CheckModule(); var checkedDefinitions = typeChecker.Check();
diagnostics.AddRange(typeChecker.GetDiagnostics()); diagnostics.AddRange(typeChecker.GetDiagnostics());
typedModules.Add(typedModule); definitions.AddRange(checkedDefinitions);
} }
foreach (var diagnostic in diagnostics) foreach (var diagnostic in diagnostics)
@@ -84,55 +82,19 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro
return 1; return 1;
} }
var objectFiles = new List<string>(); Directory.CreateDirectory(".build");
for (var i = 0; i < typedModules.Count; i++) var generator = new QBEGenerator(definitions, moduleSignatures);
{ var ssa = generator.Emit();
var typedModule = typedModules[i]; var ssaFilePath = Path.Combine(".build", "out.ssa");
var outFileName = Path.Combine(".build", Path.ChangeExtension(options.Files[i].Path, null)); File.WriteAllText(ssaFilePath, ssa);
var outFileDir = Path.GetDirectoryName(outFileName); var asmFilePath = Path.Combine(".build", "out.asm");
if (!string.IsNullOrEmpty(outFileDir)) var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath);
{ if (!qbeSuccess) return 1;
Directory.CreateDirectory(outFileDir);
}
var generator = new QBEGenerator(typedModule, moduleSignatures); var objFilePath = Path.Combine(".build", "out.o");
var ssa = generator.Emit(); var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath);
if (!asmSuccess) return 1;
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;
}
return 0; return 0;

View File

@@ -10,7 +10,7 @@ namespace NubLang.Generation.QBE;
public class QBEGenerator public class QBEGenerator
{ {
private readonly QBEWriter _writer; private readonly QBEWriter _writer;
private readonly TypedModule _module; private readonly IReadOnlyList<DefinitionNode> _definitions;
private readonly IReadOnlyDictionary<string, ModuleSignature> _moduleSignatures; private readonly IReadOnlyDictionary<string, ModuleSignature> _moduleSignatures;
private readonly List<CStringLiteral> _cStringLiterals = []; private readonly List<CStringLiteral> _cStringLiterals = [];
@@ -23,9 +23,9 @@ public class QBEGenerator
private int _stringLiteralIndex; private int _stringLiteralIndex;
private bool _codeIsReachable = true; private bool _codeIsReachable = true;
public QBEGenerator(TypedModule module, IReadOnlyDictionary<string, ModuleSignature> moduleSignatures) public QBEGenerator(IReadOnlyList<DefinitionNode> definitions, IReadOnlyDictionary<string, ModuleSignature> moduleSignatures)
{ {
_module = module; _definitions = definitions;
_moduleSignatures = moduleSignatures; _moduleSignatures = moduleSignatures;
_writer = new QBEWriter(); _writer = new QBEWriter();
} }
@@ -51,19 +51,19 @@ public class QBEGenerator
} }
} }
foreach (var structDef in _module.Definitions.OfType<StructNode>()) foreach (var structDef in _definitions.OfType<StructNode>())
{ {
EmitStructDefinition(structDef); EmitStructDefinition(structDef);
_writer.NewLine(); _writer.NewLine();
} }
foreach (var funcDef in _module.Definitions.OfType<FuncNode>()) foreach (var funcDef in _definitions.OfType<FuncNode>())
{ {
EmitFuncDefinition(funcDef); EmitFuncDefinition(funcDef);
_writer.NewLine(); _writer.NewLine();
} }
// foreach (var structDef in _module.Definitions.OfType<StructNode>().Where(x => x.InterfaceImplementations.Count > 0)) // foreach (var structDef in _definitions.OfType<StructNode>().Where(x => x.InterfaceImplementations.Count > 0))
// { // {
// _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ "); // _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ ");
// //
@@ -386,7 +386,7 @@ public class QBEGenerator
_writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' '); _writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' ');
} }
_writer.Write(FuncName(_module.Name, funcDef.Name)); _writer.Write(FuncName(funcDef.Module, funcDef.Name));
_writer.Write("("); _writer.Write("(");
foreach (var parameter in funcDef.Signature.Parameters) foreach (var parameter in funcDef.Signature.Parameters)
@@ -410,7 +410,7 @@ public class QBEGenerator
private void EmitStructDefinition(StructNode structDef) 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($"export function {StructCtorName(_module.Name, structDef.Name)}() {{");
// _writer.WriteLine("@start"); // _writer.WriteLine("@start");
@@ -432,7 +432,7 @@ public class QBEGenerator
_writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' '); _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, "); _writer.Write("(l %this, ");
foreach (var parameter in function.Signature.Parameters) foreach (var parameter in function.Signature.Parameters)

View File

@@ -474,7 +474,7 @@ public sealed class Parser
var expr = token switch var expr = token switch
{ {
LiteralToken literal => new LiteralSyntax(GetTokens(startIndex), literal.Value, literal.Kind), LiteralToken literal => new LiteralSyntax(GetTokens(startIndex), literal.Value, literal.Kind),
IdentifierToken identifier => new IdentifierSyntax(GetTokens(startIndex), Optional<string>.Empty(), identifier.Value), IdentifierToken identifier => new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value),
SymbolToken symbolToken => symbolToken.Symbol switch SymbolToken symbolToken => symbolToken.Symbol switch
{ {
Symbol.OpenParen => ParseParenthesizedExpression(), Symbol.OpenParen => ParseParenthesizedExpression(),

View File

@@ -40,7 +40,9 @@ public record FuncCallSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Express
public record DotFuncCallSyntax(IEnumerable<Token> Tokens, string Name, ExpressionSyntax ThisParameter, IReadOnlyList<ExpressionSyntax> Parameters) : ExpressionSyntax(Tokens); public record DotFuncCallSyntax(IEnumerable<Token> Tokens, string Name, ExpressionSyntax ThisParameter, IReadOnlyList<ExpressionSyntax> Parameters) : ExpressionSyntax(Tokens);
public record IdentifierSyntax(IEnumerable<Token> Tokens, Optional<string> Module, string Name) : ExpressionSyntax(Tokens); public record LocalIdentifierSyntax(IEnumerable<Token> Tokens, string Name) : ExpressionSyntax(Tokens);
public record ModuleIdentifierSyntax(IEnumerable<Token> Tokens, string Module, string Name) : ExpressionSyntax(Tokens);
public record ArrayInitializerSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens); public record ArrayInitializerSyntax(IEnumerable<Token> Tokens, ExpressionSyntax Capacity, TypeSyntax ElementType) : ExpressionSyntax(Tokens);

View File

@@ -4,7 +4,7 @@ namespace NubLang.Parsing.Syntax;
public abstract record SyntaxNode(IEnumerable<Token> Tokens); public abstract record SyntaxNode(IEnumerable<Token> Tokens);
public record SyntaxTreeMetadata(string? ModuleName, IReadOnlyList<string> Imports); public record SyntaxTreeMetadata(string ModuleName, IReadOnlyList<string> Imports);
public record SyntaxTree(IReadOnlyList<DefinitionSyntax> Definitions, SyntaxTreeMetadata Metadata); public record SyntaxTree(IReadOnlyList<DefinitionSyntax> Definitions, SyntaxTreeMetadata Metadata);

View File

@@ -3,66 +3,6 @@ using NubLang.TypeChecking.Node;
namespace NubLang.TypeChecking; namespace NubLang.TypeChecking;
public class Module
{
public static IReadOnlyList<Module> CollectFromSyntaxTrees(IReadOnlyList<SyntaxTree> syntaxTrees)
{
var modules = new Dictionary<string, Module>();
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<DefinitionSyntax> _definitions = [];
public Module(string name, IReadOnlyList<string> imports)
{
Name = name;
Imports = imports;
}
public string Name { get; }
public IReadOnlyList<string> Imports { get; }
public IReadOnlyList<DefinitionSyntax> Definitions => _definitions;
private void AddDefinition(DefinitionSyntax syntax)
{
_definitions.Add(syntax);
}
}
public class TypedModule
{
public TypedModule(string name, IReadOnlyList<DefinitionNode> definitions)
{
Name = name;
Definitions = definitions;
}
public string Name { get; }
public IReadOnlyList<DefinitionNode> Definitions { get; }
}
public class ModuleSignature public class ModuleSignature
{ {
public static IReadOnlyDictionary<string, ModuleSignature> CollectFromSyntaxTrees(IReadOnlyList<SyntaxTree> syntaxTrees) public static IReadOnlyDictionary<string, ModuleSignature> CollectFromSyntaxTrees(IReadOnlyList<SyntaxTree> syntaxTrees)
@@ -72,10 +12,6 @@ public class ModuleSignature
foreach (var syntaxTree in syntaxTrees) foreach (var syntaxTree in syntaxTrees)
{ {
var moduleName = syntaxTree.Metadata.ModuleName; var moduleName = syntaxTree.Metadata.ModuleName;
if (moduleName == null)
{
continue;
}
if (!modules.TryGetValue(moduleName, out var module)) if (!modules.TryGetValue(moduleName, out var module))
{ {
@@ -85,7 +21,7 @@ public class ModuleSignature
foreach (var def in syntaxTree.Definitions) foreach (var def in syntaxTree.Definitions)
{ {
if (!def.Exported) continue; // if (!def.Exported) continue;
switch (def) switch (def)
{ {

View File

@@ -1,19 +1,19 @@
namespace NubLang.TypeChecking.Node; 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 FuncParameterNode(string Name, TypeNode Type) : Node;
public record FuncSignatureNode(IReadOnlyList<FuncParameterNode> Parameters, TypeNode ReturnType) : Node; public record FuncSignatureNode(IReadOnlyList<FuncParameterNode> 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<ExpressionNode> Value) : Node; public record StructFieldNode(int Index, string Name, TypeNode Type, Optional<ExpressionNode> Value) : Node;
public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node; public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node;
public record StructNode(string Name, IReadOnlyList<StructFieldNode> Fields, IReadOnlyList<StructFuncNode> Functions, IReadOnlyList<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode; public record StructNode(string Module, string Name, IReadOnlyList<StructFieldNode> Fields, IReadOnlyList<StructFuncNode> Functions, IReadOnlyList<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode(Module, Name);
public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node; public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node;
public record InterfaceNode(string Name, IReadOnlyList<InterfaceFuncNode> Functions) : DefinitionNode; public record InterfaceNode(string Module, string Name, IReadOnlyList<InterfaceFuncNode> Functions) : DefinitionNode(Module, Name);

View File

@@ -3,3 +3,5 @@
public abstract record Node; public abstract record Node;
public record BlockNode(IReadOnlyList<StatementNode> Statements) : Node; public record BlockNode(IReadOnlyList<StatementNode> Statements) : Node;
public record TypedSyntaxTree(IReadOnlyList<DefinitionNode> Definitions);

View File

@@ -7,7 +7,7 @@ namespace NubLang.TypeChecking;
public sealed class TypeChecker public sealed class TypeChecker
{ {
private readonly Module _currentModule; private readonly SyntaxTree _syntaxTree;
private readonly IReadOnlyDictionary<string, ModuleSignature> _moduleSignatures; private readonly IReadOnlyDictionary<string, ModuleSignature> _moduleSignatures;
private readonly Stack<Scope> _scopes = []; private readonly Stack<Scope> _scopes = [];
@@ -16,22 +16,22 @@ public sealed class TypeChecker
private Scope Scope => _scopes.Peek(); private Scope Scope => _scopes.Peek();
public TypeChecker(Module currentModule, IReadOnlyDictionary<string, ModuleSignature> moduleSignatures) public TypeChecker(SyntaxTree syntaxTree, IReadOnlyDictionary<string, ModuleSignature> moduleSignatures)
{ {
_currentModule = currentModule; _syntaxTree = syntaxTree;
_moduleSignatures = moduleSignatures.Where(x => currentModule.Imports.Contains(x.Key) || _currentModule.Name == x.Key).ToDictionary(); _moduleSignatures = moduleSignatures.Where(x => syntaxTree.Metadata.Imports.Contains(x.Key) || _syntaxTree.Metadata.ModuleName == x.Key).ToDictionary();
} }
public IReadOnlyList<Diagnostic> GetDiagnostics() => _diagnostics; public IReadOnlyList<Diagnostic> GetDiagnostics() => _diagnostics;
public TypedModule CheckModule() public IReadOnlyList<DefinitionNode> Check()
{ {
_diagnostics.Clear(); _diagnostics.Clear();
_scopes.Clear(); _scopes.Clear();
var definitions = new List<DefinitionNode>(); var definitions = new List<DefinitionNode>();
foreach (var definition in _currentModule.Definitions) foreach (var definition in _syntaxTree.Definitions)
{ {
try try
{ {
@@ -43,7 +43,7 @@ public sealed class TypeChecker
} }
} }
return new TypedModule(_currentModule.Name, definitions); return definitions;
} }
private DefinitionNode CheckDefinition(DefinitionSyntax node) private DefinitionNode CheckDefinition(DefinitionSyntax node)
@@ -105,7 +105,7 @@ public sealed class TypeChecker
interfaceImplementations.Add(interfaceType); 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) private FuncNode CheckFuncDefinition(FuncSyntax node)
@@ -124,7 +124,7 @@ public sealed class TypeChecker
_funcReturnTypes.Pop(); _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) private StatementNode CheckStatement(StatementSyntax node)
@@ -223,7 +223,8 @@ public sealed class TypeChecker
DereferenceSyntax expression => CheckDereference(expression), DereferenceSyntax expression => CheckDereference(expression),
DotFuncCallSyntax expression => CheckDotFuncCall(expression), DotFuncCallSyntax expression => CheckDotFuncCall(expression),
FuncCallSyntax expression => CheckFuncCall(expression), FuncCallSyntax expression => CheckFuncCall(expression),
IdentifierSyntax expression => CheckIdentifier(expression), LocalIdentifierSyntax expression => CheckLocalIdentifier(expression),
ModuleIdentifierSyntax expression => CheckModuleIdentifier(expression),
LiteralSyntax expression => CheckLiteral(expression, expectedType), LiteralSyntax expression => CheckLiteral(expression, expectedType),
StructFieldAccessSyntax expression => CheckStructFieldAccess(expression), StructFieldAccessSyntax expression => CheckStructFieldAccess(expression),
StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType), StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType),
@@ -321,44 +322,44 @@ public sealed class TypeChecker
throw new NotImplementedException(); 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 // First, look in the current scope for a matching identifier
if (!expression.Module.TryGetValue(out var moduleName)) var scopeIdent = Scope.Lookup(expression.Name);
if (scopeIdent != null)
{ {
var scopeIdent = Scope.Lookup(expression.Name); switch (scopeIdent.Kind)
if (scopeIdent != null)
{ {
switch (scopeIdent.Kind) case IdentifierKind.Variable:
{ {
case IdentifierKind.Variable: return new VariableIdentifierNode(scopeIdent.Type, expression.Name);
{ }
return new VariableIdentifierNode(scopeIdent.Type, expression.Name); case IdentifierKind.FunctionParameter:
} {
case IdentifierKind.FunctionParameter: return new FuncParameterIdentifierNode(scopeIdent.Type, expression.Name);
{ }
return new FuncParameterIdentifierNode(scopeIdent.Type, expression.Name); default:
} {
default: throw new ArgumentOutOfRangeException();
{
throw new ArgumentOutOfRangeException();
}
} }
} }
} }
moduleName ??= _currentModule.Name; // Second, look in the current module for a function matching the identifier
if (_moduleSignatures.TryGetValue(moduleName, out var module)) 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, _syntaxTree.Metadata.ModuleName, expression.Name);
{
return new FuncIdentifierNode(function, moduleName, expression.Name);
}
} }
throw new TypeCheckerException(Diagnostic.Error($"Identifier {expression.Name} not found").At(expression).Build()); 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) 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 // 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

View File

@@ -2,7 +2,7 @@ module "main"
extern "puts" func puts(text: cstring) extern "puts" func puts(text: cstring)
extern "main" func main(args: []cstring): i64 func main(args: []cstring): i64
{ {
puts("test") puts("test")
return 0 return 0

View File

@@ -1,10 +1,9 @@
NUBC = ../compiler/NubLang.CLI/bin/Debug/net9.0/nubc NUBC = ../compiler/NubLang.CLI/bin/Debug/net9.0/nubc
out: out.a out: .build/out.o
gcc -nostartfiles -o out x86_64.s out.a gcc -nostartfiles -o out x86_64.s .build/out.o
rm out.a
out.a: $(NUBC) main.nub .build/out.o: $(NUBC) main.nub
$(NUBC) main.nub $(NUBC) main.nub
.PHONY: $(NUBC) .PHONY: $(NUBC)
@@ -13,4 +12,4 @@ $(NUBC):
clean: clean:
@rm -r .build 2>/dev/null || true @rm -r .build 2>/dev/null || true
@rm out.a 2>/dev/null || true @rm out 2>/dev/null || true