WIP: dev #1
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class Generator(TypedAst ast)
|
public sealed class Generator(List<TypedNodeDefinitionFunc> functions)
|
||||||
{
|
{
|
||||||
public static string Emit(TypedAst ast)
|
public static string Emit(List<TypedNodeDefinitionFunc> functions)
|
||||||
{
|
{
|
||||||
return new Generator(ast).Emit();
|
return new Generator(functions).Emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndentedTextWriter writer = new();
|
private readonly IndentedTextWriter writer = new();
|
||||||
private Dictionary<NubTypeStruct, string> structTypeNames = new();
|
private readonly Dictionary<NubTypeStruct, string> structTypeNames = new();
|
||||||
|
|
||||||
private string Emit()
|
private string Emit()
|
||||||
{
|
{
|
||||||
@@ -28,31 +28,31 @@ public sealed class Generator(TypedAst ast)
|
|||||||
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
for (var i = 0; i < ast.StructTypes.Count; i++)
|
// for (var i = 0; i < ast.StructTypes.Count; i++)
|
||||||
{
|
// {
|
||||||
var structType = ast.StructTypes[i];
|
// var structType = ast.StructTypes[i];
|
||||||
structTypeNames[structType] = $"s{i}";
|
// structTypeNames[structType] = $"s{i}";
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
foreach (var structType in ast.StructTypes)
|
// foreach (var structType in ast.StructTypes)
|
||||||
{
|
// {
|
||||||
var name = structTypeNames[structType];
|
// var name = structTypeNames[structType];
|
||||||
writer.WriteLine($"struct {name}");
|
// writer.WriteLine($"struct {name}");
|
||||||
writer.WriteLine("{");
|
// writer.WriteLine("{");
|
||||||
using (writer.Indent())
|
// using (writer.Indent())
|
||||||
{
|
// {
|
||||||
foreach (var field in structType.Fields)
|
// foreach (var field in structType.Fields)
|
||||||
{
|
// {
|
||||||
writer.WriteLine($"{CType(field.Type, field.Name)};");
|
// writer.WriteLine($"{CType(field.Type, field.Name)};");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
writer.WriteLine("};");
|
// writer.WriteLine("};");
|
||||||
}
|
// }
|
||||||
|
|
||||||
writer.WriteLine();
|
writer.WriteLine();
|
||||||
|
|
||||||
foreach (var node in ast.Functions)
|
foreach (var node in functions)
|
||||||
{
|
{
|
||||||
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
||||||
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});");
|
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});");
|
||||||
@@ -60,7 +60,7 @@ public sealed class Generator(TypedAst ast)
|
|||||||
|
|
||||||
writer.WriteLine();
|
writer.WriteLine();
|
||||||
|
|
||||||
foreach (var node in ast.Functions)
|
foreach (var node in functions)
|
||||||
{
|
{
|
||||||
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
||||||
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)})");
|
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)})");
|
||||||
|
|||||||
@@ -13,26 +13,14 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
|
|
||||||
private Ast Parse(out List<Diagnostic> diagnostics)
|
private Ast Parse(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var functionDefinitions = new List<NodeDefinitionFunc>();
|
var definitions = new List<NodeDefinition>();
|
||||||
var structDefinitions = new List<NodeDefinitionStruct>();
|
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (Peek() != null)
|
while (Peek() != null)
|
||||||
{
|
{
|
||||||
var definition = ParseDefinition();
|
definitions.Add(ParseDefinition());
|
||||||
switch (definition)
|
|
||||||
{
|
|
||||||
case NodeDefinitionFunc def:
|
|
||||||
functionDefinitions.Add(def);
|
|
||||||
break;
|
|
||||||
case NodeDefinitionStruct def:
|
|
||||||
structDefinitions.Add(def);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(definition));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (CompileException e)
|
catch (CompileException e)
|
||||||
@@ -40,7 +28,7 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
diagnostics.Add(e.Diagnostic);
|
diagnostics.Add(e.Diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Ast(structDefinitions, functionDefinitions);
|
return new Ast(definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeDefinition ParseDefinition()
|
private NodeDefinition ParseDefinition()
|
||||||
@@ -88,6 +76,18 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
|
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TryExpectKeyword(Keyword.Module))
|
||||||
|
{
|
||||||
|
var name = ExpectIdent();
|
||||||
|
return new NodeDefinitionModule(TokensFrom(startIndex), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryExpectKeyword(Keyword.Import))
|
||||||
|
{
|
||||||
|
var name = ExpectIdent();
|
||||||
|
return new NodeDefinitionImport(TokensFrom(startIndex), name);
|
||||||
|
}
|
||||||
|
|
||||||
throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build());
|
throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +248,10 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
}
|
}
|
||||||
else if (TryExpectKeyword(Keyword.Struct))
|
else if (TryExpectKeyword(Keyword.Struct))
|
||||||
{
|
{
|
||||||
|
var module = ExpectIdent();
|
||||||
|
ExpectSymbol(Symbol.ColonColon);
|
||||||
var name = ExpectIdent();
|
var name = ExpectIdent();
|
||||||
|
|
||||||
var initializers = new List<NodeExpressionStructLiteral.Initializer>();
|
var initializers = new List<NodeExpressionStructLiteral.Initializer>();
|
||||||
|
|
||||||
ExpectSymbol(Symbol.OpenCurly);
|
ExpectSymbol(Symbol.OpenCurly);
|
||||||
@@ -261,7 +264,7 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue));
|
initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), name, initializers);
|
expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), module, name, initializers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -330,7 +333,9 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
case "u64":
|
case "u64":
|
||||||
return new NodeTypeUInt(TokensFrom(startIndex), 64);
|
return new NodeTypeUInt(TokensFrom(startIndex), 64);
|
||||||
default:
|
default:
|
||||||
return new NodeTypeCustom(TokensFrom(startIndex), ident);
|
ExpectSymbol(Symbol.ColonColon);
|
||||||
|
var name = ExpectIdent();
|
||||||
|
return new NodeTypeCustom(TokensFrom(startIndex), ident, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +347,17 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
return tokens.GetRange(startIndex, index - startIndex);
|
return tokens.GetRange(startIndex, index - startIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExpectKeyword(Keyword keyword)
|
||||||
|
{
|
||||||
|
if (Peek() is TokenKeyword token && token.Keyword == keyword)
|
||||||
|
{
|
||||||
|
Consume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CompileException(Diagnostic.Error($"Expected '{keyword.AsString()}'").At(fileName, Peek()).Build());
|
||||||
|
}
|
||||||
|
|
||||||
private bool TryExpectKeyword(Keyword keyword)
|
private bool TryExpectKeyword(Keyword keyword)
|
||||||
{
|
{
|
||||||
if (Peek() is TokenKeyword token && token.Keyword == keyword)
|
if (Peek() is TokenKeyword token && token.Keyword == keyword)
|
||||||
@@ -516,10 +532,9 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Ast(List<NodeDefinitionStruct> structs, List<NodeDefinitionFunc> functions)
|
public sealed class Ast(List<NodeDefinition> definitions)
|
||||||
{
|
{
|
||||||
public readonly List<NodeDefinitionStruct> Structs = structs;
|
public readonly List<NodeDefinition> Definitions = definitions;
|
||||||
public readonly List<NodeDefinitionFunc> Functions = functions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class Node(List<Token> tokens)
|
public abstract class Node(List<Token> tokens)
|
||||||
@@ -529,6 +544,16 @@ public abstract class Node(List<Token> tokens)
|
|||||||
|
|
||||||
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
|
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
|
||||||
|
|
||||||
|
public sealed class NodeDefinitionModule(List<Token> tokens, TokenIdent name) : NodeDefinition(tokens)
|
||||||
|
{
|
||||||
|
public readonly TokenIdent Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeDefinitionImport(List<Token> tokens, TokenIdent name) : NodeDefinition(tokens)
|
||||||
|
{
|
||||||
|
public readonly TokenIdent Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
|
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
|
||||||
{
|
{
|
||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
@@ -616,8 +641,9 @@ public sealed class NodeExpressionBoolLiteral(List<Token> tokens, TokenBoolLiter
|
|||||||
public readonly TokenBoolLiteral Value = value;
|
public readonly TokenBoolLiteral Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class NodeExpressionStructLiteral(List<Token> tokens, TokenIdent name, List<NodeExpressionStructLiteral.Initializer> initializers) : NodeExpression(tokens)
|
public sealed class NodeExpressionStructLiteral(List<Token> tokens, TokenIdent module, TokenIdent name, List<NodeExpressionStructLiteral.Initializer> initializers) : NodeExpression(tokens)
|
||||||
{
|
{
|
||||||
|
public readonly TokenIdent Module = module;
|
||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
public readonly List<Initializer> Initializers = initializers;
|
public readonly List<Initializer> Initializers = initializers;
|
||||||
|
|
||||||
@@ -702,8 +728,9 @@ public sealed class NodeTypeBool(List<Token> tokens) : NodeType(tokens);
|
|||||||
|
|
||||||
public sealed class NodeTypeString(List<Token> tokens) : NodeType(tokens);
|
public sealed class NodeTypeString(List<Token> tokens) : NodeType(tokens);
|
||||||
|
|
||||||
public sealed class NodeTypeCustom(List<Token> tokens, TokenIdent name) : NodeType(tokens)
|
public sealed class NodeTypeCustom(List<Token> tokens, TokenIdent module, TokenIdent name) : NodeType(tokens)
|
||||||
{
|
{
|
||||||
|
public readonly TokenIdent Module = module;
|
||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,15 +29,22 @@ foreach (var fileName in args)
|
|||||||
if (typeResolverDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
if (typeResolverDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
var typedAst = TypeChecker.Check(fileName, ast, typeResolver, out var typeCheckerDiagnostics);
|
var functions = new List<TypedNodeDefinitionFunc>();
|
||||||
|
|
||||||
foreach (var diagnostic in typeCheckerDiagnostics)
|
foreach (var function in ast.Definitions.OfType<NodeDefinitionFunc>())
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
{
|
||||||
|
var typedFunction = TypeChecker.CheckFunction(fileName, function, typeResolver, out var typeCheckerDiagnostics);
|
||||||
|
|
||||||
if (typeCheckerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
foreach (var diagnostic in typeCheckerDiagnostics)
|
||||||
return 1;
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
|
||||||
var output = Generator.Emit(typedAst);
|
if (typedFunction == null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
functions.Add(typedFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = Generator.Emit(functions);
|
||||||
|
|
||||||
Directory.Delete(".build", recursive: true);
|
Directory.Delete(".build", recursive: true);
|
||||||
Directory.CreateDirectory(".build");
|
Directory.CreateDirectory(".build");
|
||||||
@@ -48,5 +55,4 @@ foreach (var fileName in args)
|
|||||||
Process.Start("gcc", ["-Og", "-g", "-c", "-o", Path.ChangeExtension(outFilePath, "o"), outFilePath]);
|
Process.Start("gcc", ["-Og", "-g", "-c", "-o", Path.ChangeExtension(outFilePath, "o"), outFilePath]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -177,6 +177,12 @@ public sealed class Tokenizer(string fileName, string contents)
|
|||||||
Consume();
|
Consume();
|
||||||
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period);
|
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period);
|
||||||
}
|
}
|
||||||
|
case ':' when Peek(1) is ':':
|
||||||
|
{
|
||||||
|
Consume();
|
||||||
|
Consume();
|
||||||
|
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ColonColon);
|
||||||
|
}
|
||||||
case ':':
|
case ':':
|
||||||
{
|
{
|
||||||
Consume();
|
Consume();
|
||||||
@@ -340,6 +346,9 @@ public sealed class Tokenizer(string fileName, string contents)
|
|||||||
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
|
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
|
||||||
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
|
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
|
||||||
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
|
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
|
||||||
|
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module),
|
||||||
|
"import" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Import),
|
||||||
|
"export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export),
|
||||||
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
|
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
|
||||||
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
|
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
|
||||||
_ => new TokenIdent(line, startColumn, column - startColumn, value)
|
_ => new TokenIdent(line, startColumn, column - startColumn, value)
|
||||||
@@ -430,6 +439,7 @@ public enum Symbol
|
|||||||
Comma,
|
Comma,
|
||||||
Period,
|
Period,
|
||||||
Colon,
|
Colon,
|
||||||
|
ColonColon,
|
||||||
Caret,
|
Caret,
|
||||||
Bang,
|
Bang,
|
||||||
Equal,
|
Equal,
|
||||||
@@ -471,6 +481,9 @@ public enum Keyword
|
|||||||
Else,
|
Else,
|
||||||
While,
|
While,
|
||||||
Return,
|
Return,
|
||||||
|
Module,
|
||||||
|
Import,
|
||||||
|
Export,
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
||||||
@@ -491,6 +504,7 @@ public static class TokenExtensions
|
|||||||
Symbol.Comma => ",",
|
Symbol.Comma => ",",
|
||||||
Symbol.Period => ",",
|
Symbol.Period => ",",
|
||||||
Symbol.Colon => ":",
|
Symbol.Colon => ":",
|
||||||
|
Symbol.ColonColon => "::",
|
||||||
Symbol.Caret => "^",
|
Symbol.Caret => "^",
|
||||||
Symbol.Bang => "!",
|
Symbol.Bang => "!",
|
||||||
Symbol.Equal => "=",
|
Symbol.Equal => "=",
|
||||||
@@ -531,6 +545,9 @@ public static class TokenExtensions
|
|||||||
Keyword.Else => "else",
|
Keyword.Else => "else",
|
||||||
Keyword.While => "while",
|
Keyword.While => "while",
|
||||||
Keyword.Return => "return",
|
Keyword.Return => "return",
|
||||||
|
Keyword.Module => "module",
|
||||||
|
Keyword.Import => "import",
|
||||||
|
Keyword.Export => "export",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,58 @@
|
|||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class TypeChecker(string fileName, Ast ast, TypeResolver typeResolver)
|
public sealed class TypeChecker(string fileName, NodeDefinitionFunc function, TypeResolver typeResolver)
|
||||||
{
|
{
|
||||||
public static TypedAst Check(string fileName, Ast ast, TypeResolver typeResolver, out List<Diagnostic> diagnostics)
|
public static TypedNodeDefinitionFunc? CheckFunction(string fileName, NodeDefinitionFunc function, TypeResolver typeResolver, out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
return new TypeChecker(fileName, ast, typeResolver).Check(out diagnostics);
|
return new TypeChecker(fileName, function, typeResolver).CheckFunction(out diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scope scope = new(null);
|
private Scope scope = new(null);
|
||||||
|
|
||||||
private TypedAst Check(out List<Diagnostic> diagnostics)
|
private TypedNodeDefinitionFunc? CheckFunction(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
var functions = new List<TypedNodeDefinitionFunc>();
|
|
||||||
|
|
||||||
foreach (var funcDef in ast.Functions)
|
var parameters = new List<TypedNodeDefinitionFunc.Param>();
|
||||||
{
|
var invalidParameter = false;
|
||||||
var type = new NubTypeFunc(funcDef.Parameters.Select(x => typeResolver.Resolve(x.Type)).ToList(), typeResolver.Resolve(funcDef.ReturnType));
|
TypedNodeStatement? body = null;
|
||||||
scope.DeclareIdentifier(funcDef.Name.Ident, type);
|
NubType? returnType = null;
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var funcDef in ast.Functions)
|
foreach (var parameter in function.Parameters)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
functions.Add(CheckDefinitionFunc(funcDef));
|
parameters.Add(CheckDefinitionFuncParameter(parameter));
|
||||||
}
|
}
|
||||||
catch (CompileException e)
|
catch (CompileException e)
|
||||||
{
|
{
|
||||||
diagnostics.Add(e.Diagnostic);
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
invalidParameter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TypedAst(typeResolver.GetAllStructs(), functions);
|
try
|
||||||
}
|
{
|
||||||
|
body = CheckStatement(function.Body);
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
private TypedNodeDefinitionFunc CheckDefinitionFunc(NodeDefinitionFunc definition)
|
try
|
||||||
{
|
{
|
||||||
return new TypedNodeDefinitionFunc(definition.Tokens, definition.Name, definition.Parameters.Select(CheckDefinitionFuncParameter).ToList(), CheckStatement(definition.Body), typeResolver.Resolve(definition.ReturnType));
|
returnType = typeResolver.Resolve(function.ReturnType);
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body == null || returnType == null || invalidParameter)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new TypedNodeDefinitionFunc(function.Tokens, function.Name, parameters, body, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
||||||
@@ -291,9 +306,9 @@ public sealed class TypeChecker(string fileName, Ast ast, TypeResolver typeResol
|
|||||||
|
|
||||||
private TypedNodeExpressionStructLiteral CheckExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
private TypedNodeExpressionStructLiteral CheckExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
||||||
{
|
{
|
||||||
var type = typeResolver.GetNamedStruct(expression.Name.Ident);
|
var type = typeResolver.GetNamedStruct(expression.Module.Ident, expression.Name.Ident);
|
||||||
if (type == null)
|
if (type == null)
|
||||||
throw new CompileException(Diagnostic.Error($"Undeclared struct '{expression.Name.Ident}'").At(fileName, expression.Name).Build());
|
throw new CompileException(Diagnostic.Error($"Undeclared struct '{expression.Module.Ident}::{expression.Name.Ident}'").At(fileName, expression.Name).Build());
|
||||||
|
|
||||||
var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>();
|
var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>();
|
||||||
foreach (var initializer in expression.Initializers)
|
foreach (var initializer in expression.Initializers)
|
||||||
@@ -330,12 +345,6 @@ public sealed class TypeChecker(string fileName, Ast ast, TypeResolver typeResol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TypedAst(List<NubTypeStruct> structTypes, List<TypedNodeDefinitionFunc> functions)
|
|
||||||
{
|
|
||||||
public readonly List<NubTypeStruct> StructTypes = structTypes;
|
|
||||||
public readonly List<TypedNodeDefinitionFunc> Functions = functions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class TypedNode(List<Token> tokens)
|
public abstract class TypedNode(List<Token> tokens)
|
||||||
{
|
{
|
||||||
public readonly List<Token> Tokens = tokens;
|
public readonly List<Token> Tokens = tokens;
|
||||||
@@ -499,159 +508,3 @@ public sealed class TypedNodeExpressionUnary(List<Token> tokens, NubType type, T
|
|||||||
Invert,
|
Invert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class NubType : IEquatable<NubType>
|
|
||||||
{
|
|
||||||
public abstract override string ToString();
|
|
||||||
|
|
||||||
public abstract bool Equals(NubType? other);
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
if (obj is NubType otherNubType)
|
|
||||||
{
|
|
||||||
return Equals(otherNubType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract override int GetHashCode();
|
|
||||||
|
|
||||||
public static bool operator ==(NubType? left, NubType? right) => Equals(left, right);
|
|
||||||
public static bool operator !=(NubType? left, NubType? right) => !Equals(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeVoid : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "void";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeVoid;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeVoid));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeUInt(int width) : NubType
|
|
||||||
{
|
|
||||||
public readonly int Width = width;
|
|
||||||
|
|
||||||
public override string ToString() => $"u{Width}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeUInt otherUInt && Width == otherUInt.Width;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeUInt), Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeSInt(int width) : NubType
|
|
||||||
{
|
|
||||||
public readonly int Width = width;
|
|
||||||
|
|
||||||
public override string ToString() => $"i{Width}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeSInt otherUInt && Width == otherUInt.Width;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeSInt), Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeBool : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "bool";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeBool;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeBool));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeString : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "string";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeString;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeString));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeStruct : NubType
|
|
||||||
{
|
|
||||||
private List<Field>? _resolvedFields;
|
|
||||||
public List<Field> Fields => _resolvedFields ?? throw new InvalidOperationException();
|
|
||||||
|
|
||||||
public void ResolveFields(List<Field> fields)
|
|
||||||
{
|
|
||||||
if (_resolvedFields != null)
|
|
||||||
throw new InvalidOperationException($"{ToString()} already resolved");
|
|
||||||
|
|
||||||
_resolvedFields = fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if (_resolvedFields == null)
|
|
||||||
return "struct <unresolved>";
|
|
||||||
|
|
||||||
return $"struct {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other)
|
|
||||||
{
|
|
||||||
if (other is not NubTypeStruct structType)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (Fields.Count != structType.Fields.Count)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (var i = 0; i < Fields.Count; i++)
|
|
||||||
{
|
|
||||||
if (Fields[i].Name != structType.Fields[i].Name)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (Fields[i].Type != structType.Fields[i].Type)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
var hash = new HashCode();
|
|
||||||
hash.Add(typeof(NubTypeStruct));
|
|
||||||
foreach (var field in Fields)
|
|
||||||
{
|
|
||||||
hash.Add(field.Name);
|
|
||||||
hash.Add(field.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash.ToHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Field(string name, NubType type)
|
|
||||||
{
|
|
||||||
public readonly string Name = name;
|
|
||||||
public readonly NubType Type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypePointer(NubType to) : NubType
|
|
||||||
{
|
|
||||||
public readonly NubType To = to;
|
|
||||||
public override string ToString() => $"^{To}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypePointer pointer && To == pointer.To;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypePointer));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeFunc(List<NubType> parameters, NubType returnType) : NubType
|
|
||||||
{
|
|
||||||
public readonly List<NubType> Parameters = parameters;
|
|
||||||
public readonly NubType ReturnType = returnType;
|
|
||||||
public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeFunc func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
var hash = new HashCode();
|
|
||||||
hash.Add(typeof(NubTypeFunc));
|
|
||||||
hash.Add(ReturnType);
|
|
||||||
foreach (var param in Parameters)
|
|
||||||
hash.Add(param);
|
|
||||||
|
|
||||||
return hash.ToHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,38 +2,50 @@ namespace Compiler;
|
|||||||
|
|
||||||
public sealed class TypeResolver(string fileName)
|
public sealed class TypeResolver(string fileName)
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, NubTypeStruct> structTypes = [];
|
private readonly Dictionary<(string Module, string Name), NubTypeStruct> structTypes = [];
|
||||||
|
|
||||||
public List<NubTypeStruct> GetAllStructs() => structTypes.Values.ToList();
|
public NubTypeStruct? GetNamedStruct(string module, string name) => structTypes.GetValueOrDefault((module, name));
|
||||||
public NubTypeStruct? GetNamedStruct(string name) => structTypes.GetValueOrDefault(name);
|
|
||||||
|
|
||||||
public static TypeResolver Create(string fileName, Ast ast, out List<Diagnostic> diagnostics)
|
public static TypeResolver Create(string fileName, Ast ast, out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
var resolver = new TypeResolver(fileName);
|
var resolver = new TypeResolver(fileName);
|
||||||
|
|
||||||
foreach (var structDef in ast.Structs)
|
var moduleDefinitions = ast.Definitions.OfType<NodeDefinitionModule>().ToList();
|
||||||
|
|
||||||
|
if (moduleDefinitions.Count == 0)
|
||||||
|
diagnostics.Add(Diagnostic.Error($"'{fileName}' is not part of a module").At(fileName, 1, 1, 1).Build());
|
||||||
|
|
||||||
|
foreach (var moduleDefinition in moduleDefinitions.Skip(1))
|
||||||
|
diagnostics.Add(Diagnostic.Warning("Duplicate module definition").At(fileName, moduleDefinition).Build());
|
||||||
|
|
||||||
|
if (moduleDefinitions.Count >= 1)
|
||||||
{
|
{
|
||||||
if (resolver.structTypes.ContainsKey(structDef.Name.Ident))
|
var currentModule = moduleDefinitions[0].Name.Ident;
|
||||||
|
|
||||||
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
||||||
{
|
{
|
||||||
diagnostics.Add(Diagnostic.Error($"Duplicate struct: {structDef.Name.Ident}").At(fileName, structDef.Name).Build());
|
if (resolver.structTypes.ContainsKey((currentModule, structDef.Name.Ident)))
|
||||||
continue;
|
{
|
||||||
|
diagnostics.Add(Diagnostic.Error($"Duplicate struct: {structDef.Name.Ident}").At(fileName, structDef.Name).Build());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.structTypes.Add((currentModule, structDef.Name.Ident), new NubTypeStruct());
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver.structTypes.Add(structDef.Name.Ident, new NubTypeStruct());
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var structDef in ast.Structs)
|
|
||||||
{
|
|
||||||
var structType = resolver.structTypes[structDef.Name.Ident];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
structType.ResolveFields(structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, resolver.Resolve(f.Type))).ToList());
|
var structType = resolver.structTypes[(currentModule, structDef.Name.Ident)];
|
||||||
}
|
|
||||||
catch (CompileException e)
|
try
|
||||||
{
|
{
|
||||||
diagnostics.Add(e.Diagnostic);
|
structType.ResolveFields(structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, resolver.Resolve(f.Type))).ToList());
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,10 +70,166 @@ public sealed class TypeResolver(string fileName)
|
|||||||
|
|
||||||
private NubTypeStruct ResolveStruct(NodeTypeCustom type)
|
private NubTypeStruct ResolveStruct(NodeTypeCustom type)
|
||||||
{
|
{
|
||||||
var structType = structTypes.GetValueOrDefault(type.Name.Ident);
|
var structType = structTypes.GetValueOrDefault((type.Module.Ident, type.Name.Ident));
|
||||||
if (structType == null)
|
if (structType == null)
|
||||||
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Name.Ident}").At(fileName, type).Build());
|
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").At(fileName, type).Build());
|
||||||
|
|
||||||
return structType;
|
return structType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class NubType : IEquatable<NubType>
|
||||||
|
{
|
||||||
|
public abstract override string ToString();
|
||||||
|
|
||||||
|
public abstract bool Equals(NubType? other);
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is NubType otherNubType)
|
||||||
|
{
|
||||||
|
return Equals(otherNubType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract override int GetHashCode();
|
||||||
|
|
||||||
|
public static bool operator ==(NubType? left, NubType? right) => Equals(left, right);
|
||||||
|
public static bool operator !=(NubType? left, NubType? right) => !Equals(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeVoid : NubType
|
||||||
|
{
|
||||||
|
public override string ToString() => "void";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypeVoid;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeVoid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeUInt(int width) : NubType
|
||||||
|
{
|
||||||
|
public readonly int Width = width;
|
||||||
|
|
||||||
|
public override string ToString() => $"u{Width}";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypeUInt otherUInt && Width == otherUInt.Width;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeUInt), Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeSInt(int width) : NubType
|
||||||
|
{
|
||||||
|
public readonly int Width = width;
|
||||||
|
|
||||||
|
public override string ToString() => $"i{Width}";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypeSInt otherUInt && Width == otherUInt.Width;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeSInt), Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeBool : NubType
|
||||||
|
{
|
||||||
|
public override string ToString() => "bool";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypeBool;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeBool));
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeString : NubType
|
||||||
|
{
|
||||||
|
public override string ToString() => "string";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypeString;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeString));
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeStruct : NubType
|
||||||
|
{
|
||||||
|
private List<Field>? _resolvedFields;
|
||||||
|
public List<Field> Fields => _resolvedFields ?? throw new InvalidOperationException();
|
||||||
|
|
||||||
|
public void ResolveFields(List<Field> fields)
|
||||||
|
{
|
||||||
|
if (_resolvedFields != null)
|
||||||
|
throw new InvalidOperationException($"{ToString()} already resolved");
|
||||||
|
|
||||||
|
_resolvedFields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (_resolvedFields == null)
|
||||||
|
return "struct <unresolved>";
|
||||||
|
|
||||||
|
return $"struct {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other)
|
||||||
|
{
|
||||||
|
if (other is not NubTypeStruct structType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Fields.Count != structType.Fields.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < Fields.Count; i++)
|
||||||
|
{
|
||||||
|
if (Fields[i].Name != structType.Fields[i].Name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Fields[i].Type != structType.Fields[i].Type)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var hash = new HashCode();
|
||||||
|
hash.Add(typeof(NubTypeStruct));
|
||||||
|
foreach (var field in Fields)
|
||||||
|
{
|
||||||
|
hash.Add(field.Name);
|
||||||
|
hash.Add(field.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Field(string name, NubType type)
|
||||||
|
{
|
||||||
|
public readonly string Name = name;
|
||||||
|
public readonly NubType Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypePointer(NubType to) : NubType
|
||||||
|
{
|
||||||
|
public readonly NubType To = to;
|
||||||
|
public override string ToString() => $"^{To}";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypePointer pointer && To == pointer.To;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypePointer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeFunc(List<NubType> parameters, NubType returnType) : NubType
|
||||||
|
{
|
||||||
|
public readonly List<NubType> Parameters = parameters;
|
||||||
|
public readonly NubType ReturnType = returnType;
|
||||||
|
public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}";
|
||||||
|
|
||||||
|
public override bool Equals(NubType? other) => other is NubTypeFunc func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var hash = new HashCode();
|
||||||
|
hash.Add(typeof(NubTypeFunc));
|
||||||
|
hash.Add(ReturnType);
|
||||||
|
foreach (var param in Parameters)
|
||||||
|
hash.Add(param);
|
||||||
|
|
||||||
|
return hash.ToHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
struct person {
|
module main
|
||||||
|
|
||||||
|
struct person {
|
||||||
age: i32
|
age: i32
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
@@ -23,7 +25,7 @@ func main(): i32 {
|
|||||||
x = i
|
x = i
|
||||||
}
|
}
|
||||||
|
|
||||||
let me: person = struct person { age = 21 name = "Oliver" }
|
let me: main::person = struct main::person { age = 21 name = "Oliver" }
|
||||||
|
|
||||||
do_something(me.name)
|
do_something(me.name)
|
||||||
return x
|
return x
|
||||||
|
|||||||
Reference in New Issue
Block a user