Make module nullable in custom type
This commit is contained in:
@@ -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();
|
||||||
|
|
||||||
using (BeginRootScope(_syntaxTree.ModuleName))
|
|
||||||
{
|
|
||||||
var functions = new List<FuncNode>();
|
var functions = new List<FuncNode>();
|
||||||
|
|
||||||
|
using (BeginRootScope(_syntaxTree.ModuleName))
|
||||||
|
{
|
||||||
foreach (var funcSyntax in _syntaxTree.Definitions.OfType<FuncSyntax>())
|
foreach (var funcSyntax in _syntaxTree.Definitions.OfType<FuncSyntax>())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -46,13 +46,18 @@ 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))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var fields = structSyntax.Fields
|
var fields = structSyntax.Fields
|
||||||
.Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null))
|
.Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null))
|
||||||
@@ -60,28 +65,32 @@ public sealed class TypeChecker
|
|||||||
|
|
||||||
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))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
importedFunctions.Add(CheckFuncPrototype(funcSyntax.Prototype));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,41 +113,21 @@ public sealed class TypeChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
private FuncNode CheckFuncDefinition(FuncSyntax node)
|
private FuncNode CheckFuncDefinition(FuncSyntax node)
|
||||||
{
|
|
||||||
foreach (var parameter in node.Prototype.Parameters)
|
|
||||||
{
|
|
||||||
CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var prototype = CheckFuncPrototype(node.Prototype);
|
|
||||||
|
|
||||||
BlockNode? body = null;
|
|
||||||
if (node.Body != null)
|
|
||||||
{
|
{
|
||||||
using (BeginScope())
|
using (BeginScope())
|
||||||
{
|
{
|
||||||
CurrentScope.SetReturnType(prototype.ReturnType);
|
var prototype = CheckFuncPrototype(node.Prototype);
|
||||||
body = CheckBlock(node.Body);
|
|
||||||
|
|
||||||
if (!AlwaysReturns(body))
|
Scope.SetReturnType(prototype.ReturnType);
|
||||||
|
foreach (var parameter in prototype.Parameters)
|
||||||
{
|
{
|
||||||
if (prototype.ReturnType is NubVoidType)
|
Scope.DeclareVariable(new Variable(parameter.Name, parameter.Type));
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var body = node.Body == null ? null : CheckBlock(node.Body);
|
||||||
return new FuncNode(node.Tokens, prototype, 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,15 +589,18 @@ 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)
|
||||||
|
{
|
||||||
|
using (BeginRootScope(expression.Module))
|
||||||
{
|
{
|
||||||
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, expression.Module, expression.Name, function.Prototype.ExternSymbol);
|
return new FuncIdentifierNode(expression.Tokens, type, expression.Module, expression.Name, function.Prototype.ExternSymbol);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw new TypeCheckerException(Diagnostic
|
throw new TypeCheckerException(Diagnostic
|
||||||
.Error($"No exported symbol {expression.Name} not found in module {expression.Module}")
|
.Error($"No exported symbol {expression.Name} not found in module {expression.Module}")
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user