...
This commit is contained in:
@@ -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 outFileDir = Path.GetDirectoryName(outFileName);
|
||||
if (!string.IsNullOrEmpty(outFileDir))
|
||||
{
|
||||
Directory.CreateDirectory(outFileDir);
|
||||
}
|
||||
|
||||
var generator = new QBEGenerator(typedModule, moduleSignatures);
|
||||
var generator = new QBEGenerator(definitions, moduleSignatures);
|
||||
var ssa = generator.Emit();
|
||||
|
||||
var ssaFilePath = Path.ChangeExtension(outFileName, "ssa");
|
||||
var ssaFilePath = Path.Combine(".build", "out.ssa");
|
||||
File.WriteAllText(ssaFilePath, ssa);
|
||||
|
||||
var asmFilePath = Path.ChangeExtension(outFileName, "s");
|
||||
var asmFilePath = Path.Combine(".build", "out.asm");
|
||||
var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath);
|
||||
if (!qbeSuccess) return 1;
|
||||
|
||||
if (!qbeSuccess)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var objFilePath = Path.ChangeExtension(outFileName, "o");
|
||||
var objFilePath = Path.Combine(".build", "out.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;
|
||||
}
|
||||
if (!asmSuccess) return 1;
|
||||
|
||||
return 0;
|
||||
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
@@ -3,3 +3,5 @@
|
||||
public abstract record Node;
|
||||
|
||||
public record BlockNode(IReadOnlyList<StatementNode> Statements) : Node;
|
||||
|
||||
public record TypedSyntaxTree(IReadOnlyList<DefinitionNode> Definitions);
|
||||
@@ -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,11 +322,9 @@ public sealed class TypeChecker
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private ExpressionNode CheckIdentifier(IdentifierSyntax 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))
|
||||
private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression)
|
||||
{
|
||||
// First, look in the current scope for a matching identifier
|
||||
var scopeIdent = Scope.Lookup(expression.Name);
|
||||
if (scopeIdent != null)
|
||||
{
|
||||
@@ -345,20 +344,22 @@ public sealed class TypeChecker
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user