Make module nullable in custom type

This commit is contained in:
nub31
2025-10-22 14:37:40 +02:00
parent 8c973153c5
commit 0637007345
3 changed files with 96 additions and 95 deletions

View File

@@ -13,7 +13,7 @@ public sealed class TypeChecker
private readonly Dictionary<(string Module, string Name), NubType> _typeCache = new(); private readonly Dictionary<(string Module, string Name), NubType> _typeCache = new();
private readonly HashSet<(string Module, string Name)> _resolvingTypes = []; private readonly HashSet<(string Module, string Name)> _resolvingTypes = [];
private Scope CurrentScope => _scopes.Peek(); private Scope Scope => _scopes.Peek();
public List<Diagnostic> Diagnostics { get; } = []; public List<Diagnostic> Diagnostics { get; } = [];
@@ -31,10 +31,10 @@ public sealed class TypeChecker
_typeCache.Clear(); _typeCache.Clear();
_resolvingTypes.Clear(); _resolvingTypes.Clear();
var functions = new List<FuncNode>();
using (BeginRootScope(_syntaxTree.ModuleName)) using (BeginRootScope(_syntaxTree.ModuleName))
{ {
var functions = new List<FuncNode>();
foreach (var funcSyntax in _syntaxTree.Definitions.OfType<FuncSyntax>()) foreach (var funcSyntax in _syntaxTree.Definitions.OfType<FuncSyntax>())
{ {
try try
@@ -46,42 +46,51 @@ public sealed class TypeChecker
Diagnostics.Add(e.Diagnostic); Diagnostics.Add(e.Diagnostic);
} }
} }
}
var importedStructTypes = new List<NubStructType>(); var importedStructTypes = new List<NubStructType>();
var importedFunctions = new List<FuncPrototypeNode>(); var importedFunctions = new List<FuncPrototypeNode>();
foreach (var (name, module) in _importedModules) foreach (var (name, module) in _importedModules)
{
using (BeginRootScope(name))
{ {
foreach (var structSyntax in module.Structs(true)) foreach (var structSyntax in module.Structs(true))
{ {
var fields = structSyntax.Fields try
.Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null)) {
.ToList(); 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)) 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() private ScopeDisposer BeginScope()
{ {
if (_scopes.TryPeek(out var scope)) _scopes.Push(Scope.SubScope());
{
_scopes.Push(scope.SubScope());
}
else
{
_scopes.Push(new Scope(_syntaxTree.ModuleName));
}
return new ScopeDisposer(this); return new ScopeDisposer(this);
} }
@@ -105,39 +114,19 @@ public sealed class TypeChecker
private FuncNode CheckFuncDefinition(FuncSyntax node) 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); Scope.SetReturnType(prototype.ReturnType);
foreach (var parameter in prototype.Parameters)
BlockNode? body = null;
if (node.Body != null)
{
using (BeginScope())
{ {
CurrentScope.SetReturnType(prototype.ReturnType); Scope.DeclareVariable(new Variable(parameter.Name, parameter.Type));
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());
}
}
} }
}
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) private AssignmentNode CheckAssignment(AssignmentSyntax statement)
@@ -180,7 +169,7 @@ public sealed class TypeChecker
if (statement.Value != null) if (statement.Value != null)
{ {
var expectedReturnType = CurrentScope.GetReturnType(); var expectedReturnType = Scope.GetReturnType();
value = CheckExpression(statement.Value, expectedReturnType); value = CheckExpression(statement.Value, expectedReturnType);
} }
@@ -233,7 +222,7 @@ public sealed class TypeChecker
.Build()); .Build());
} }
CurrentScope.DeclareVariable(new Variable(statement.Name, type)); Scope.DeclareVariable(new Variable(statement.Name, type));
return new VariableDeclarationNode(statement.Tokens, statement.Name, assignmentNode, 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))); 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) private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null)
@@ -570,20 +559,20 @@ public sealed class TypeChecker
private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _) private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _)
{ {
var scopeIdent = CurrentScope.LookupVariable(expression.Name); var scopeIdent = Scope.LookupVariable(expression.Name);
if (scopeIdent != null) if (scopeIdent != null)
{ {
return new VariableIdentifierNode(expression.Tokens, scopeIdent.Type, expression.Name); 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); var function = module.Functions(true).FirstOrDefault(x => x.Name == expression.Name);
if (function != null) if (function != null)
{ {
var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList(); var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList();
var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType)); 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()); throw new TypeCheckerException(Diagnostic.Error($"Symbol {expression.Name} not found").At(expression).Build());
@@ -600,14 +589,17 @@ public sealed class TypeChecker
.Build()); .Build());
} }
var includePrivate = expression.Module == CurrentScope.Module; var includePrivate = expression.Module == Scope.Module;
var function = module.Functions(includePrivate).FirstOrDefault(x => x.Name == expression.Name); var function = module.Functions(includePrivate).FirstOrDefault(x => x.Name == expression.Name);
if (function != null) if (function != null)
{ {
var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList(); using (BeginRootScope(expression.Module))
var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType)); {
return new FuncIdentifierNode(expression.Tokens, type, expression.Module, expression.Name, function.Prototype.ExternSymbol); 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 throw new TypeCheckerException(Diagnostic
@@ -839,7 +831,7 @@ public sealed class TypeChecker
private NubType ResolveCustomType(CustomTypeSyntax customType) 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)) if (_typeCache.TryGetValue(key, out var cachedType))
{ {
@@ -848,14 +840,14 @@ public sealed class TypeChecker
if (!_resolvingTypes.Add(key)) 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; _typeCache[key] = placeholder;
return placeholder; return placeholder;
} }
try try
{ {
if (!_importedModules.TryGetValue(customType.Module, out var module)) if (!_importedModules.TryGetValue(customType.Module ?? Scope.Module, out var module))
{ {
throw new TypeCheckerException(Diagnostic throw new TypeCheckerException(Diagnostic
.Error($"Module {customType.Module} not found") .Error($"Module {customType.Module} not found")
@@ -864,12 +856,12 @@ public sealed class TypeChecker
.Build()); .Build());
} }
var includePrivate = customType.Module == CurrentScope.Module; var includePrivate = customType.Module == Scope.Module;
var structDef = module.Structs(includePrivate).FirstOrDefault(x => x.Name == customType.Name); var structDef = module.Structs(includePrivate).FirstOrDefault(x => x.Name == customType.Name);
if (structDef != null) if (structDef != null)
{ {
var result = new NubStructType(customType.Module, structDef.Name, []); var result = new NubStructType(customType.Module ?? Scope.Module, structDef.Name, []);
_typeCache[key] = result; _typeCache[key] = result;
var fields = structDef.Fields var fields = structDef.Fields

View File

@@ -7,7 +7,6 @@ public sealed class Parser
{ {
private List<Token> _tokens = []; private List<Token> _tokens = [];
private int _tokenIndex; private int _tokenIndex;
private string _moduleName = string.Empty;
private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null; private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null;
private bool HasToken => CurrentToken != null; private bool HasToken => CurrentToken != null;
@@ -19,34 +18,9 @@ public sealed class Parser
Diagnostics.Clear(); Diagnostics.Clear();
_tokens = tokens; _tokens = tokens;
_tokenIndex = 0; _tokenIndex = 0;
_moduleName = string.Empty;
string? moduleName = null;
var imports = new List<string>(); var imports = new List<string>();
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<DefinitionSyntax>(); var definitions = new List<DefinitionSyntax>();
while (HasToken) while (HasToken)
@@ -54,6 +28,41 @@ public sealed class Parser
try try
{ {
var startIndex = _tokenIndex; 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); var exported = TryExpectSymbol(Symbol.Export);
if (TryExpectSymbol(Symbol.Extern)) 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() private FuncParameterSyntax ParseFuncParameter()
@@ -659,7 +668,7 @@ public sealed class Parser
return new BoolTypeSyntax(GetTokens(startIndex)); return new BoolTypeSyntax(GetTokens(startIndex));
default: default:
{ {
var module = _moduleName; string? module = null;
if (TryExpectSymbol(Symbol.DoubleColon)) if (TryExpectSymbol(Symbol.DoubleColon))
{ {

View File

@@ -140,6 +140,6 @@ public record ArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyn
public record ConstArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType, long Size) : TypeSyntax(Tokens); public record ConstArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType, long Size) : TypeSyntax(Tokens);
public record CustomTypeSyntax(List<Token> Tokens, string Module, string Name) : TypeSyntax(Tokens); public record CustomTypeSyntax(List<Token> Tokens, string? Module, string Name) : TypeSyntax(Tokens);
#endregion #endregion