From 0637007345eb36ca8dc03dd081f82333e9947f19 Mon Sep 17 00:00:00 2001 From: nub31 Date: Wed, 22 Oct 2025 14:37:40 +0200 Subject: [PATCH] Make module nullable in custom type --- compiler/NubLang/Ast/TypeChecker.cs | 122 +++++++++++++--------------- compiler/NubLang/Syntax/Parser.cs | 67 ++++++++------- compiler/NubLang/Syntax/Syntax.cs | 2 +- 3 files changed, 96 insertions(+), 95 deletions(-) diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index 722bd17..41ee037 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -13,7 +13,7 @@ public sealed class TypeChecker private readonly Dictionary<(string Module, string Name), NubType> _typeCache = new(); private readonly HashSet<(string Module, string Name)> _resolvingTypes = []; - private Scope CurrentScope => _scopes.Peek(); + private Scope Scope => _scopes.Peek(); public List Diagnostics { get; } = []; @@ -31,10 +31,10 @@ public sealed class TypeChecker _typeCache.Clear(); _resolvingTypes.Clear(); + var functions = new List(); + using (BeginRootScope(_syntaxTree.ModuleName)) { - var functions = new List(); - foreach (var funcSyntax in _syntaxTree.Definitions.OfType()) { try @@ -46,42 +46,51 @@ public sealed class TypeChecker Diagnostics.Add(e.Diagnostic); } } + } - var importedStructTypes = new List(); - var importedFunctions = new List(); + var importedStructTypes = new List(); + var importedFunctions = new List(); - foreach (var (name, module) in _importedModules) + foreach (var (name, module) in _importedModules) + { + using (BeginRootScope(name)) { foreach (var structSyntax in module.Structs(true)) { - var fields = structSyntax.Fields - .Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null)) - .ToList(); + try + { + var fields = structSyntax.Fields + .Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null)) + .ToList(); - importedStructTypes.Add(new NubStructType(name, structSyntax.Name, fields)); + importedStructTypes.Add(new NubStructType(name, structSyntax.Name, fields)); + } + catch (TypeCheckerException e) + { + Diagnostics.Add(e.Diagnostic); + } } foreach (var funcSyntax in module.Functions(true)) { - importedFunctions.Add(CheckFuncPrototype(funcSyntax.Prototype)); + try + { + importedFunctions.Add(CheckFuncPrototype(funcSyntax.Prototype)); + } + catch (TypeCheckerException e) + { + Diagnostics.Add(e.Diagnostic); + } } } - - return new CompilationUnit(functions, importedStructTypes, importedFunctions); } + + return new CompilationUnit(functions, importedStructTypes, importedFunctions); } private ScopeDisposer BeginScope() { - if (_scopes.TryPeek(out var scope)) - { - _scopes.Push(scope.SubScope()); - } - else - { - _scopes.Push(new Scope(_syntaxTree.ModuleName)); - } - + _scopes.Push(Scope.SubScope()); return new ScopeDisposer(this); } @@ -105,39 +114,19 @@ public sealed class TypeChecker private FuncNode CheckFuncDefinition(FuncSyntax node) { - foreach (var parameter in node.Prototype.Parameters) + using (BeginScope()) { - CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type))); - } + var prototype = CheckFuncPrototype(node.Prototype); - var prototype = CheckFuncPrototype(node.Prototype); - - BlockNode? body = null; - if (node.Body != null) - { - using (BeginScope()) + Scope.SetReturnType(prototype.ReturnType); + foreach (var parameter in prototype.Parameters) { - CurrentScope.SetReturnType(prototype.ReturnType); - body = CheckBlock(node.Body); - - if (!AlwaysReturns(body)) - { - if (prototype.ReturnType is NubVoidType) - { - body.Statements.Add(new ReturnNode(node.Tokens.Skip(node.Tokens.Count - 1).ToList(), null)); - } - else - { - Diagnostics.Add(Diagnostic - .Error("Not all code paths return a value") - .At(node.Body) - .Build()); - } - } + Scope.DeclareVariable(new Variable(parameter.Name, parameter.Type)); } - } - return new FuncNode(node.Tokens, prototype, body); + var body = node.Body == null ? null : CheckBlock(node.Body); + return new FuncNode(node.Tokens, prototype, body); + } } private AssignmentNode CheckAssignment(AssignmentSyntax statement) @@ -180,7 +169,7 @@ public sealed class TypeChecker if (statement.Value != null) { - var expectedReturnType = CurrentScope.GetReturnType(); + var expectedReturnType = Scope.GetReturnType(); value = CheckExpression(statement.Value, expectedReturnType); } @@ -233,7 +222,7 @@ public sealed class TypeChecker .Build()); } - CurrentScope.DeclareVariable(new Variable(statement.Name, type)); + Scope.DeclareVariable(new Variable(statement.Name, type)); return new VariableDeclarationNode(statement.Tokens, statement.Name, assignmentNode, type); } @@ -253,7 +242,7 @@ public sealed class TypeChecker parameters.Add(new FuncParameterNode(parameter.Tokens, parameter.Name, ResolveType(parameter.Type))); } - return new FuncPrototypeNode(statement.Tokens, CurrentScope.Module, statement.Name, statement.ExternSymbol, parameters, ResolveType(statement.ReturnType)); + return new FuncPrototypeNode(statement.Tokens, Scope.Module, statement.Name, statement.ExternSymbol, parameters, ResolveType(statement.ReturnType)); } private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null) @@ -570,20 +559,20 @@ public sealed class TypeChecker private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _) { - var scopeIdent = CurrentScope.LookupVariable(expression.Name); + var scopeIdent = Scope.LookupVariable(expression.Name); if (scopeIdent != null) { return new VariableIdentifierNode(expression.Tokens, scopeIdent.Type, expression.Name); } - var module = _importedModules[CurrentScope.Module]; + var module = _importedModules[Scope.Module]; var function = module.Functions(true).FirstOrDefault(x => x.Name == expression.Name); if (function != null) { var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList(); var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType)); - return new FuncIdentifierNode(expression.Tokens, type, CurrentScope.Module, expression.Name, function.Prototype.ExternSymbol); + return new FuncIdentifierNode(expression.Tokens, type, Scope.Module, expression.Name, function.Prototype.ExternSymbol); } throw new TypeCheckerException(Diagnostic.Error($"Symbol {expression.Name} not found").At(expression).Build()); @@ -600,14 +589,17 @@ public sealed class TypeChecker .Build()); } - var includePrivate = expression.Module == CurrentScope.Module; + var includePrivate = expression.Module == Scope.Module; var function = module.Functions(includePrivate).FirstOrDefault(x => x.Name == expression.Name); if (function != null) { - var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList(); - var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType)); - return new FuncIdentifierNode(expression.Tokens, type, expression.Module, expression.Name, function.Prototype.ExternSymbol); + using (BeginRootScope(expression.Module)) + { + var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList(); + var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType)); + return new FuncIdentifierNode(expression.Tokens, type, expression.Module, expression.Name, function.Prototype.ExternSymbol); + } } throw new TypeCheckerException(Diagnostic @@ -839,7 +831,7 @@ public sealed class TypeChecker private NubType ResolveCustomType(CustomTypeSyntax customType) { - var key = (customType.Module, customType.Name); + var key = (customType.Module ?? Scope.Module, customType.Name); if (_typeCache.TryGetValue(key, out var cachedType)) { @@ -848,14 +840,14 @@ public sealed class TypeChecker if (!_resolvingTypes.Add(key)) { - var placeholder = new NubStructType(customType.Module, customType.Name, []); + var placeholder = new NubStructType(customType.Module ?? Scope.Module, customType.Name, []); _typeCache[key] = placeholder; return placeholder; } try { - if (!_importedModules.TryGetValue(customType.Module, out var module)) + if (!_importedModules.TryGetValue(customType.Module ?? Scope.Module, out var module)) { throw new TypeCheckerException(Diagnostic .Error($"Module {customType.Module} not found") @@ -864,12 +856,12 @@ public sealed class TypeChecker .Build()); } - var includePrivate = customType.Module == CurrentScope.Module; + var includePrivate = customType.Module == Scope.Module; var structDef = module.Structs(includePrivate).FirstOrDefault(x => x.Name == customType.Name); if (structDef != null) { - var result = new NubStructType(customType.Module, structDef.Name, []); + var result = new NubStructType(customType.Module ?? Scope.Module, structDef.Name, []); _typeCache[key] = result; var fields = structDef.Fields diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index 901523b..ca886a9 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -7,7 +7,6 @@ public sealed class Parser { private List _tokens = []; private int _tokenIndex; - private string _moduleName = string.Empty; private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null; private bool HasToken => CurrentToken != null; @@ -19,34 +18,9 @@ public sealed class Parser Diagnostics.Clear(); _tokens = tokens; _tokenIndex = 0; - _moduleName = string.Empty; + string? moduleName = null; var imports = new List(); - - try - { - while (TryExpectSymbol(Symbol.Import)) - { - imports.Add(ExpectStringLiteral().Value); - } - - ExpectSymbol(Symbol.Module); - _moduleName = ExpectStringLiteral().Value; - } - catch (ParseException e) - { - Diagnostics.Add(e.Diagnostic); - while (HasToken) - { - if (CurrentToken is SymbolToken { Symbol: Symbol.Module or Symbol.Import }) - { - break; - } - - Next(); - } - } - var definitions = new List(); while (HasToken) @@ -54,6 +28,41 @@ public sealed class Parser try { var startIndex = _tokenIndex; + + if (TryExpectSymbol(Symbol.Import)) + { + var name = ExpectStringLiteral(); + if (imports.Contains(name.Value)) + { + Diagnostics.Add(Diagnostic + .Warning($"Module {name.Value} is imported twice") + .At(name) + .WithHelp($"Remove duplicate import \"{name.Value}\"") + .Build()); + } + else + { + imports.Add(name.Value); + } + + continue; + } + + if (TryExpectSymbol(Symbol.Module)) + { + if (moduleName != null) + { + throw new ParseException(Diagnostic + .Error("Module is declared more than once") + .At(CurrentToken) + .WithHelp("Remove duplicate module declaration") + .Build()); + } + + moduleName = ExpectStringLiteral().Value; + continue; + } + var exported = TryExpectSymbol(Symbol.Export); if (TryExpectSymbol(Symbol.Extern)) @@ -93,7 +102,7 @@ public sealed class Parser } } - return new SyntaxTree(definitions, _moduleName, imports); + return new SyntaxTree(definitions, moduleName ?? "default", imports); } private FuncParameterSyntax ParseFuncParameter() @@ -659,7 +668,7 @@ public sealed class Parser return new BoolTypeSyntax(GetTokens(startIndex)); default: { - var module = _moduleName; + string? module = null; if (TryExpectSymbol(Symbol.DoubleColon)) { diff --git a/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs index 1b3bc6b..360e981 100644 --- a/compiler/NubLang/Syntax/Syntax.cs +++ b/compiler/NubLang/Syntax/Syntax.cs @@ -140,6 +140,6 @@ public record ArrayTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyn public record ConstArrayTypeSyntax(List Tokens, TypeSyntax BaseType, long Size) : TypeSyntax(Tokens); -public record CustomTypeSyntax(List Tokens, string Module, string Name) : TypeSyntax(Tokens); +public record CustomTypeSyntax(List Tokens, string? Module, string Name) : TypeSyntax(Tokens); #endregion \ No newline at end of file