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.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<TypedModule>();
var definitions = new List<DefinitionNode>();
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<string>();
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;

View File

@@ -10,7 +10,7 @@ namespace NubLang.Generation.QBE;
public class QBEGenerator
{
private readonly QBEWriter _writer;
private readonly TypedModule _module;
private readonly IReadOnlyList<DefinitionNode> _definitions;
private readonly IReadOnlyDictionary<string, ModuleSignature> _moduleSignatures;
private readonly List<CStringLiteral> _cStringLiterals = [];
@@ -23,9 +23,9 @@ public class QBEGenerator
private int _stringLiteralIndex;
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;
_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);
_writer.NewLine();
}
foreach (var funcDef in _module.Definitions.OfType<FuncNode>())
foreach (var funcDef in _definitions.OfType<FuncNode>())
{
EmitFuncDefinition(funcDef);
_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)} = {{ ");
//
@@ -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)

View File

@@ -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<string>.Empty(), identifier.Value),
IdentifierToken identifier => new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value),
SymbolToken symbolToken => symbolToken.Symbol switch
{
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 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);

View File

@@ -4,7 +4,7 @@ namespace NubLang.Parsing.Syntax;
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);

View File

@@ -3,66 +3,6 @@ using NubLang.TypeChecking.Node;
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 static IReadOnlyDictionary<string, ModuleSignature> CollectFromSyntaxTrees(IReadOnlyList<SyntaxTree> 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)
{

View File

@@ -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<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 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 InterfaceNode(string Name, IReadOnlyList<InterfaceFuncNode> Functions) : DefinitionNode;
public record InterfaceNode(string Module, string Name, IReadOnlyList<InterfaceFuncNode> Functions) : DefinitionNode(Module, Name);

View File

@@ -2,4 +2,6 @@
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
{
private readonly Module _currentModule;
private readonly SyntaxTree _syntaxTree;
private readonly IReadOnlyDictionary<string, ModuleSignature> _moduleSignatures;
private readonly Stack<Scope> _scopes = [];
@@ -16,22 +16,22 @@ public sealed class TypeChecker
private Scope Scope => _scopes.Peek();
public TypeChecker(Module currentModule, IReadOnlyDictionary<string, ModuleSignature> moduleSignatures)
public TypeChecker(SyntaxTree syntaxTree, IReadOnlyDictionary<string, ModuleSignature> 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<Diagnostic> GetDiagnostics() => _diagnostics;
public TypedModule CheckModule()
public IReadOnlyList<DefinitionNode> Check()
{
_diagnostics.Clear();
_scopes.Clear();
var definitions = new List<DefinitionNode>();
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