From 96670b120166ede44c69cf74852a3629f9f8e488 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 9 Feb 2026 19:34:47 +0100 Subject: [PATCH] ... --- compiler/Generator.cs | 56 +++++----- compiler/Parser.cs | 71 +++++++++---- compiler/Program.cs | 20 ++-- compiler/Tokenizer.cs | 17 ++++ compiler/TypeChecker.cs | 215 +++++++-------------------------------- compiler/TypeResolver.cs | 212 ++++++++++++++++++++++++++++++++++---- compiler/test.nub | 6 +- 7 files changed, 335 insertions(+), 262 deletions(-) diff --git a/compiler/Generator.cs b/compiler/Generator.cs index 0705898..b747579 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -2,15 +2,15 @@ namespace Compiler; -public sealed class Generator(TypedAst ast) +public sealed class Generator(List functions) { - public static string Emit(TypedAst ast) + public static string Emit(List functions) { - return new Generator(ast).Emit(); + return new Generator(functions).Emit(); } - private IndentedTextWriter writer = new(); - private Dictionary structTypeNames = new(); + private readonly IndentedTextWriter writer = new(); + private readonly Dictionary structTypeNames = new(); private string Emit() { @@ -28,31 +28,31 @@ public sealed class Generator(TypedAst ast) """); - for (var i = 0; i < ast.StructTypes.Count; i++) - { - var structType = ast.StructTypes[i]; - structTypeNames[structType] = $"s{i}"; - } - - foreach (var structType in ast.StructTypes) - { - var name = structTypeNames[structType]; - writer.WriteLine($"struct {name}"); - writer.WriteLine("{"); - using (writer.Indent()) - { - foreach (var field in structType.Fields) - { - writer.WriteLine($"{CType(field.Type, field.Name)};"); - } - } - - writer.WriteLine("};"); - } + // for (var i = 0; i < ast.StructTypes.Count; i++) + // { + // var structType = ast.StructTypes[i]; + // structTypeNames[structType] = $"s{i}"; + // } + // + // foreach (var structType in ast.StructTypes) + // { + // var name = structTypeNames[structType]; + // writer.WriteLine($"struct {name}"); + // writer.WriteLine("{"); + // using (writer.Indent()) + // { + // foreach (var field in structType.Fields) + // { + // writer.WriteLine($"{CType(field.Type, field.Name)};"); + // } + // } + // + // 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)); writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});"); @@ -60,7 +60,7 @@ public sealed class Generator(TypedAst ast) 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)); writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)})"); diff --git a/compiler/Parser.cs b/compiler/Parser.cs index 83870a9..d13c225 100644 --- a/compiler/Parser.cs +++ b/compiler/Parser.cs @@ -13,26 +13,14 @@ public sealed class Parser(string fileName, List tokens) private Ast Parse(out List diagnostics) { - var functionDefinitions = new List(); - var structDefinitions = new List(); + var definitions = new List(); diagnostics = []; try { while (Peek() != null) { - var definition = ParseDefinition(); - switch (definition) - { - case NodeDefinitionFunc def: - functionDefinitions.Add(def); - break; - case NodeDefinitionStruct def: - structDefinitions.Add(def); - break; - default: - throw new ArgumentOutOfRangeException(nameof(definition)); - } + definitions.Add(ParseDefinition()); } } catch (CompileException e) @@ -40,7 +28,7 @@ public sealed class Parser(string fileName, List tokens) diagnostics.Add(e.Diagnostic); } - return new Ast(structDefinitions, functionDefinitions); + return new Ast(definitions); } private NodeDefinition ParseDefinition() @@ -88,6 +76,18 @@ public sealed class Parser(string fileName, List tokens) 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()); } @@ -248,7 +248,10 @@ public sealed class Parser(string fileName, List tokens) } else if (TryExpectKeyword(Keyword.Struct)) { + var module = ExpectIdent(); + ExpectSymbol(Symbol.ColonColon); var name = ExpectIdent(); + var initializers = new List(); ExpectSymbol(Symbol.OpenCurly); @@ -261,7 +264,7 @@ public sealed class Parser(string fileName, List tokens) 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 { @@ -330,7 +333,9 @@ public sealed class Parser(string fileName, List tokens) case "u64": return new NodeTypeUInt(TokensFrom(startIndex), 64); 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 tokens) 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) { if (Peek() is TokenKeyword token && token.Keyword == keyword) @@ -516,10 +532,9 @@ public sealed class Parser(string fileName, List tokens) } } -public sealed class Ast(List structs, List functions) +public sealed class Ast(List definitions) { - public readonly List Structs = structs; - public readonly List Functions = functions; + public readonly List Definitions = definitions; } public abstract class Node(List tokens) @@ -529,6 +544,16 @@ public abstract class Node(List tokens) public abstract class NodeDefinition(List tokens) : Node(tokens); +public sealed class NodeDefinitionModule(List tokens, TokenIdent name) : NodeDefinition(tokens) +{ + public readonly TokenIdent Name = name; +} + +public sealed class NodeDefinitionImport(List tokens, TokenIdent name) : NodeDefinition(tokens) +{ + public readonly TokenIdent Name = name; +} + public sealed class NodeDefinitionFunc(List tokens, TokenIdent name, List parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens) { public readonly TokenIdent Name = name; @@ -616,8 +641,9 @@ public sealed class NodeExpressionBoolLiteral(List tokens, TokenBoolLiter public readonly TokenBoolLiteral Value = value; } -public sealed class NodeExpressionStructLiteral(List tokens, TokenIdent name, List initializers) : NodeExpression(tokens) +public sealed class NodeExpressionStructLiteral(List tokens, TokenIdent module, TokenIdent name, List initializers) : NodeExpression(tokens) { + public readonly TokenIdent Module = module; public readonly TokenIdent Name = name; public readonly List Initializers = initializers; @@ -702,8 +728,9 @@ public sealed class NodeTypeBool(List tokens) : NodeType(tokens); public sealed class NodeTypeString(List tokens) : NodeType(tokens); -public sealed class NodeTypeCustom(List tokens, TokenIdent name) : NodeType(tokens) +public sealed class NodeTypeCustom(List tokens, TokenIdent module, TokenIdent name) : NodeType(tokens) { + public readonly TokenIdent Module = module; public readonly TokenIdent Name = name; } diff --git a/compiler/Program.cs b/compiler/Program.cs index 84dd4c9..f5c3f4a 100644 --- a/compiler/Program.cs +++ b/compiler/Program.cs @@ -29,15 +29,22 @@ foreach (var fileName in args) if (typeResolverDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error)) return 1; - var typedAst = TypeChecker.Check(fileName, ast, typeResolver, out var typeCheckerDiagnostics); + var functions = new List(); - foreach (var diagnostic in typeCheckerDiagnostics) - DiagnosticFormatter.Print(diagnostic, Console.Error); + foreach (var function in ast.Definitions.OfType()) + { + var typedFunction = TypeChecker.CheckFunction(fileName, function, typeResolver, out var typeCheckerDiagnostics); - if (typeCheckerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error)) - return 1; + foreach (var diagnostic in typeCheckerDiagnostics) + 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.CreateDirectory(".build"); @@ -48,5 +55,4 @@ foreach (var fileName in args) Process.Start("gcc", ["-Og", "-g", "-c", "-o", Path.ChangeExtension(outFilePath, "o"), outFilePath]); } - return 0; \ No newline at end of file diff --git a/compiler/Tokenizer.cs b/compiler/Tokenizer.cs index c3ea37d..218e4ad 100644 --- a/compiler/Tokenizer.cs +++ b/compiler/Tokenizer.cs @@ -177,6 +177,12 @@ public sealed class Tokenizer(string fileName, string contents) Consume(); 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 ':': { Consume(); @@ -340,6 +346,9 @@ public sealed class Tokenizer(string fileName, string contents) "else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else), "while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While), "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), "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), _ => new TokenIdent(line, startColumn, column - startColumn, value) @@ -430,6 +439,7 @@ public enum Symbol Comma, Period, Colon, + ColonColon, Caret, Bang, Equal, @@ -471,6 +481,9 @@ public enum Keyword Else, While, Return, + Module, + Import, + Export, } 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.Period => ",", Symbol.Colon => ":", + Symbol.ColonColon => "::", Symbol.Caret => "^", Symbol.Bang => "!", Symbol.Equal => "=", @@ -531,6 +545,9 @@ public static class TokenExtensions Keyword.Else => "else", Keyword.While => "while", Keyword.Return => "return", + Keyword.Module => "module", + Keyword.Import => "import", + Keyword.Export => "export", _ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null) }; } diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index 3b9448f..42e6bd5 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -1,43 +1,58 @@ 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 diagnostics) + public static TypedNodeDefinitionFunc? CheckFunction(string fileName, NodeDefinitionFunc function, TypeResolver typeResolver, out List 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 TypedAst Check(out List diagnostics) + private TypedNodeDefinitionFunc? CheckFunction(out List diagnostics) { diagnostics = []; - var functions = new List(); - foreach (var funcDef in ast.Functions) - { - var type = new NubTypeFunc(funcDef.Parameters.Select(x => typeResolver.Resolve(x.Type)).ToList(), typeResolver.Resolve(funcDef.ReturnType)); - scope.DeclareIdentifier(funcDef.Name.Ident, type); - } + var parameters = new List(); + var invalidParameter = false; + TypedNodeStatement? body = null; + NubType? returnType = null; - foreach (var funcDef in ast.Functions) + foreach (var parameter in function.Parameters) { try { - functions.Add(CheckDefinitionFunc(funcDef)); + parameters.Add(CheckDefinitionFuncParameter(parameter)); } catch (CompileException e) { 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) - { - return new TypedNodeDefinitionFunc(definition.Tokens, definition.Name, definition.Parameters.Select(CheckDefinitionFuncParameter).ToList(), CheckStatement(definition.Body), typeResolver.Resolve(definition.ReturnType)); + try + { + 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) @@ -291,9 +306,9 @@ public sealed class TypeChecker(string fileName, Ast ast, TypeResolver typeResol 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) - 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(); 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 structTypes, List functions) -{ - public readonly List StructTypes = structTypes; - public readonly List Functions = functions; -} - public abstract class TypedNode(List tokens) { public readonly List Tokens = tokens; @@ -498,160 +507,4 @@ public sealed class TypedNodeExpressionUnary(List tokens, NubType type, T Negate, Invert, } -} - -public abstract class NubType : IEquatable -{ - 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? _resolvedFields; - public List Fields => _resolvedFields ?? throw new InvalidOperationException(); - - public void ResolveFields(List fields) - { - if (_resolvedFields != null) - throw new InvalidOperationException($"{ToString()} already resolved"); - - _resolvedFields = fields; - } - - public override string ToString() - { - if (_resolvedFields == null) - return "struct "; - - 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 parameters, NubType returnType) : NubType -{ - public readonly List 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(); - } } \ No newline at end of file diff --git a/compiler/TypeResolver.cs b/compiler/TypeResolver.cs index 74944a2..f153d8b 100644 --- a/compiler/TypeResolver.cs +++ b/compiler/TypeResolver.cs @@ -2,38 +2,50 @@ namespace Compiler; public sealed class TypeResolver(string fileName) { - private readonly Dictionary structTypes = []; + private readonly Dictionary<(string Module, string Name), NubTypeStruct> structTypes = []; - public List GetAllStructs() => structTypes.Values.ToList(); - public NubTypeStruct? GetNamedStruct(string name) => structTypes.GetValueOrDefault(name); + public NubTypeStruct? GetNamedStruct(string module, string name) => structTypes.GetValueOrDefault((module, name)); public static TypeResolver Create(string fileName, Ast ast, out List diagnostics) { diagnostics = []; var resolver = new TypeResolver(fileName); - foreach (var structDef in ast.Structs) + var moduleDefinitions = ast.Definitions.OfType().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()) { - diagnostics.Add(Diagnostic.Error($"Duplicate struct: {structDef.Name.Ident}").At(fileName, structDef.Name).Build()); - continue; + if (resolver.structTypes.ContainsKey((currentModule, structDef.Name.Ident))) + { + 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.Structs) - { - var structType = resolver.structTypes[structDef.Name.Ident]; - - try + foreach (var structDef in ast.Definitions.OfType()) { - structType.ResolveFields(structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, resolver.Resolve(f.Type))).ToList()); - } - catch (CompileException e) - { - diagnostics.Add(e.Diagnostic); + var structType = resolver.structTypes[(currentModule, 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); + } } } @@ -58,10 +70,166 @@ public sealed class TypeResolver(string fileName) 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) - 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; } +} + +public abstract class NubType : IEquatable +{ + 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? _resolvedFields; + public List Fields => _resolvedFields ?? throw new InvalidOperationException(); + + public void ResolveFields(List fields) + { + if (_resolvedFields != null) + throw new InvalidOperationException($"{ToString()} already resolved"); + + _resolvedFields = fields; + } + + public override string ToString() + { + if (_resolvedFields == null) + return "struct "; + + 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 parameters, NubType returnType) : NubType +{ + public readonly List 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(); + } } \ No newline at end of file diff --git a/compiler/test.nub b/compiler/test.nub index 8fe11bf..39d6975 100644 --- a/compiler/test.nub +++ b/compiler/test.nub @@ -1,4 +1,6 @@ -struct person { +module main + +struct person { age: i32 name: string } @@ -23,7 +25,7 @@ func main(): i32 { 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) return x