struct resolution
This commit is contained in:
@@ -8,38 +8,34 @@ foreach (var fileName in args)
|
|||||||
var tokens = Tokenizer.Tokenize(fileName, file, out var tokenizerDiagnostics);
|
var tokens = Tokenizer.Tokenize(fileName, file, out var tokenizerDiagnostics);
|
||||||
|
|
||||||
foreach (var diagnostic in tokenizerDiagnostics)
|
foreach (var diagnostic in tokenizerDiagnostics)
|
||||||
{
|
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenizerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
if (tokenizerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
var ast = Parser.Parse(fileName, tokens, out var parserDiagnostics);
|
var ast = Parser.Parse(fileName, tokens, out var parserDiagnostics);
|
||||||
|
|
||||||
foreach (var diagnostic in parserDiagnostics)
|
foreach (var diagnostic in parserDiagnostics)
|
||||||
{
|
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
}
|
|
||||||
|
|
||||||
if (parserDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
if (parserDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
var typedAst = TypeChecker.Check(fileName, ast, out var typeCheckerDiagnostics);
|
var typeResolver = TypeResolver.Create(fileName, ast, out var typeResolverDiagnostics);
|
||||||
|
|
||||||
|
foreach (var diagnostic in typeResolverDiagnostics)
|
||||||
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
|
||||||
|
if (typeResolverDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
var typedAst = TypeChecker.Check(fileName, ast, typeResolver, out var typeCheckerDiagnostics);
|
||||||
|
|
||||||
foreach (var diagnostic in typeCheckerDiagnostics)
|
foreach (var diagnostic in typeCheckerDiagnostics)
|
||||||
{
|
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
}
|
|
||||||
|
|
||||||
if (typeCheckerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
if (typeCheckerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
var output = Generator.Emit(typedAst);
|
var output = Generator.Emit(typedAst);
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,22 @@
|
|||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class TypeChecker(string fileName, Ast ast)
|
public sealed class TypeChecker(string fileName, Ast ast, TypeResolver typeResolver)
|
||||||
{
|
{
|
||||||
public static TypedAst Check(string fileName, Ast ast, out List<Diagnostic> diagnostics)
|
public static TypedAst Check(string fileName, Ast ast, TypeResolver typeResolver, out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
return new TypeChecker(fileName, ast).Check(out diagnostics);
|
return new TypeChecker(fileName, ast, typeResolver).Check(out diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scope scope = new(null);
|
private Scope scope = new(null);
|
||||||
private Dictionary<string, NubTypeStruct> structTypes = new();
|
|
||||||
|
|
||||||
private TypedAst Check(out List<Diagnostic> diagnostics)
|
private TypedAst Check(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var functions = new List<TypedNodeDefinitionFunc>();
|
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
|
var functions = new List<TypedNodeDefinitionFunc>();
|
||||||
// todo(nub31): Types must be resolved better to prevent circular dependencies and independent ordering
|
|
||||||
foreach (var structDef in ast.Structs)
|
|
||||||
{
|
|
||||||
var fields = structDef.Fields.Select(x => new NubTypeStruct.Field(x.Name.Ident, CheckType(x.Type))).ToList();
|
|
||||||
structTypes.Add(structDef.Name.Ident, new NubTypeStruct(fields));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var funcDef in ast.Functions)
|
foreach (var funcDef in ast.Functions)
|
||||||
{
|
{
|
||||||
var type = new NubTypeFunc(funcDef.Parameters.Select(x => CheckType(x.Type)).ToList(), CheckType(funcDef.ReturnType));
|
var type = new NubTypeFunc(funcDef.Parameters.Select(x => typeResolver.Resolve(x.Type)).ToList(), typeResolver.Resolve(funcDef.ReturnType));
|
||||||
scope.DeclareIdentifier(funcDef.Name.Ident, type);
|
scope.DeclareIdentifier(funcDef.Name.Ident, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,17 +32,17 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TypedAst(structTypes.Values.ToList(), functions);
|
return new TypedAst(typeResolver.GetAllStructs(), functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeDefinitionFunc CheckDefinitionFunc(NodeDefinitionFunc definition)
|
private TypedNodeDefinitionFunc CheckDefinitionFunc(NodeDefinitionFunc definition)
|
||||||
{
|
{
|
||||||
return new TypedNodeDefinitionFunc(definition.Tokens, definition.Name, definition.Parameters.Select(CheckDefinitionFuncParameter).ToList(), CheckStatement(definition.Body), CheckType(definition.ReturnType));
|
return new TypedNodeDefinitionFunc(definition.Tokens, definition.Name, definition.Parameters.Select(CheckDefinitionFuncParameter).ToList(), CheckStatement(definition.Body), typeResolver.Resolve(definition.ReturnType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
||||||
{
|
{
|
||||||
return new TypedNodeDefinitionFunc.Param(node.Tokens, node.Name, CheckType(node.Type));
|
return new TypedNodeDefinitionFunc.Param(node.Tokens, node.Name, typeResolver.Resolve(node.Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeStatement CheckStatement(NodeStatement node)
|
private TypedNodeStatement CheckStatement(NodeStatement node)
|
||||||
@@ -95,7 +87,7 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
|
|
||||||
private TypedNodeStatementVariableDeclaration CheckStatementVariableDeclaration(NodeStatementVariableDeclaration statement)
|
private TypedNodeStatementVariableDeclaration CheckStatementVariableDeclaration(NodeStatementVariableDeclaration statement)
|
||||||
{
|
{
|
||||||
var type = CheckType(statement.Type);
|
var type = typeResolver.Resolve(statement.Type);
|
||||||
var value = CheckExpression(statement.Value);
|
var value = CheckExpression(statement.Value);
|
||||||
|
|
||||||
if (type != value.Type)
|
if (type != value.Type)
|
||||||
@@ -299,7 +291,7 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
|
|
||||||
private TypedNodeExpressionStructLiteral CheckExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
private TypedNodeExpressionStructLiteral CheckExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
||||||
{
|
{
|
||||||
var type = structTypes.GetValueOrDefault(expression.Name.Ident);
|
var type = typeResolver.GetNamedStruct(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.Name.Ident}'").At(fileName, expression.Name).Build());
|
||||||
|
|
||||||
@@ -320,34 +312,9 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
return new TypedNodeExpressionStructLiteral(expression.Tokens, type, initializers);
|
return new TypedNodeExpressionStructLiteral(expression.Tokens, type, initializers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NubType CheckType(NodeType node)
|
|
||||||
{
|
|
||||||
return node switch
|
|
||||||
{
|
|
||||||
NodeTypeBool type => new NubTypeBool(),
|
|
||||||
NodeTypeCustom type => CheckStructType(type),
|
|
||||||
NodeTypeFunc type => new NubTypeFunc(type.Parameters.Select(CheckType).ToList(), CheckType(type.ReturnType)),
|
|
||||||
NodeTypePointer type => new NubTypePointer(CheckType(type.To)),
|
|
||||||
NodeTypeSInt type => new NubTypeSInt(type.Width),
|
|
||||||
NodeTypeUInt type => new NubTypeUInt(type.Width),
|
|
||||||
NodeTypeString type => new NubTypeString(),
|
|
||||||
NodeTypeVoid type => new NubTypeVoid(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private NubTypeStruct CheckStructType(NodeTypeCustom type)
|
|
||||||
{
|
|
||||||
var structType = structTypes.GetValueOrDefault(type.Name.Ident);
|
|
||||||
if (structType == null)
|
|
||||||
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type}").At(fileName, type).Build());
|
|
||||||
|
|
||||||
return structType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Scope(Scope? parent)
|
private class Scope(Scope? parent)
|
||||||
{
|
{
|
||||||
private Dictionary<string, NubType> identifiers = new();
|
private readonly Dictionary<string, NubType> identifiers = new();
|
||||||
|
|
||||||
public void DeclareIdentifier(string name, NubType type)
|
public void DeclareIdentifier(string name, NubType type)
|
||||||
{
|
{
|
||||||
@@ -599,10 +566,26 @@ public sealed class NubTypeString : NubType
|
|||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeString));
|
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeString));
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class NubTypeStruct(List<NubTypeStruct.Field> fields) : NubType
|
public sealed class NubTypeStruct : NubType
|
||||||
{
|
{
|
||||||
public readonly List<Field> Fields = fields;
|
private List<Field>? _resolvedFields;
|
||||||
public override string ToString() => $"struct {{ {string.Join(' ', Fields.Select(x => $"{x.Name}: {x.Type}"))} }}";
|
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)
|
public override bool Equals(NubType? other)
|
||||||
{
|
{
|
||||||
|
|||||||
67
compiler/TypeResolver.cs
Normal file
67
compiler/TypeResolver.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
namespace Compiler;
|
||||||
|
|
||||||
|
public sealed class TypeResolver(string fileName)
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, NubTypeStruct> structTypes = [];
|
||||||
|
|
||||||
|
public List<NubTypeStruct> GetAllStructs() => structTypes.Values.ToList();
|
||||||
|
public NubTypeStruct? GetNamedStruct(string name) => structTypes.GetValueOrDefault(name);
|
||||||
|
|
||||||
|
public static TypeResolver Create(string fileName, Ast ast, out List<Diagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
diagnostics = [];
|
||||||
|
var resolver = new TypeResolver(fileName);
|
||||||
|
|
||||||
|
foreach (var structDef in ast.Structs)
|
||||||
|
{
|
||||||
|
if (resolver.structTypes.ContainsKey(structDef.Name.Ident))
|
||||||
|
{
|
||||||
|
diagnostics.Add(Diagnostic.Error($"Duplicate struct: {structDef.Name.Ident}").At(fileName, structDef.Name).Build());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.structTypes.Add(structDef.Name.Ident, new NubTypeStruct());
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NubType Resolve(NodeType node)
|
||||||
|
{
|
||||||
|
return node switch
|
||||||
|
{
|
||||||
|
NodeTypeBool type => new NubTypeBool(),
|
||||||
|
NodeTypeCustom type => ResolveStruct(type),
|
||||||
|
NodeTypeFunc type => new NubTypeFunc(type.Parameters.Select(Resolve).ToList(), Resolve(type.ReturnType)),
|
||||||
|
NodeTypePointer type => new NubTypePointer(Resolve(type.To)),
|
||||||
|
NodeTypeSInt type => new NubTypeSInt(type.Width),
|
||||||
|
NodeTypeUInt type => new NubTypeUInt(type.Width),
|
||||||
|
NodeTypeString type => new NubTypeString(),
|
||||||
|
NodeTypeVoid type => new NubTypeVoid(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private NubTypeStruct ResolveStruct(NodeTypeCustom type)
|
||||||
|
{
|
||||||
|
var structType = structTypes.GetValueOrDefault(type.Name.Ident);
|
||||||
|
if (structType == null)
|
||||||
|
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type}").At(fileName, type).Build());
|
||||||
|
|
||||||
|
return structType;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user