proper scopes
This commit is contained in:
@@ -180,7 +180,7 @@ public class ModuleGraph
|
||||
|
||||
var astModuleCache = new Dictionary<Ast, Module>();
|
||||
|
||||
// First pass: Register modules from ast
|
||||
// Second pass: Register modules from ast
|
||||
foreach (var ast in asts)
|
||||
{
|
||||
if (!modules.ContainsKey(ast.ModuleName.Ident))
|
||||
@@ -191,7 +191,7 @@ public class ModuleGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Register struct types without fields
|
||||
// Third pass: Register struct types without fields
|
||||
foreach (var ast in asts)
|
||||
{
|
||||
var module = astModuleCache[ast];
|
||||
@@ -204,7 +204,7 @@ public class ModuleGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass: Resolve struct fields
|
||||
// Fourth pass: Resolve struct fields
|
||||
foreach (var ast in asts)
|
||||
{
|
||||
var module = astModuleCache[ast];
|
||||
@@ -219,7 +219,7 @@ public class ModuleGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Fourth pass: Register identifiers
|
||||
// Fifth pass: Register identifiers
|
||||
foreach (var ast in asts)
|
||||
{
|
||||
var module = astModuleCache[ast];
|
||||
|
||||
@@ -19,7 +19,7 @@ public class TypeChecker
|
||||
private readonly string moduleName;
|
||||
private readonly NodeDefinitionFunc function;
|
||||
private readonly ModuleGraph moduleGraph;
|
||||
private readonly Scope scope = new(null);
|
||||
private readonly Scope scope = new();
|
||||
|
||||
private TypedNodeDefinitionFunc? CheckFunction(out List<Diagnostic> diagnostics)
|
||||
{
|
||||
@@ -30,48 +30,50 @@ public class TypeChecker
|
||||
TypedNodeStatement? body = null;
|
||||
NubType? returnType = null;
|
||||
|
||||
foreach (var parameter in function.Parameters)
|
||||
using (scope.EnterScope())
|
||||
{
|
||||
scope.DeclareIdentifier(parameter.Name.Ident, ResolveType(parameter.Type));
|
||||
foreach (var parameter in function.Parameters)
|
||||
{
|
||||
NubType parameterType;
|
||||
|
||||
try
|
||||
{
|
||||
parameterType = ResolveType(parameter.Type);
|
||||
}
|
||||
catch (CompileException e)
|
||||
{
|
||||
diagnostics.Add(e.Diagnostic);
|
||||
invalidParameter = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
scope.DeclareIdentifier(parameter.Name.Ident, parameterType);
|
||||
parameters.Add(new TypedNodeDefinitionFunc.Param(parameter.Tokens, parameter.Name, parameterType));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
parameters.Add(CheckDefinitionFuncParameter(parameter));
|
||||
body = CheckStatement(function.Body);
|
||||
}
|
||||
catch (CompileException e)
|
||||
{
|
||||
diagnostics.Add(e.Diagnostic);
|
||||
invalidParameter = true;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
body = CheckStatement(function.Body);
|
||||
}
|
||||
catch (CompileException e)
|
||||
{
|
||||
diagnostics.Add(e.Diagnostic);
|
||||
}
|
||||
try
|
||||
{
|
||||
returnType = ResolveType(function.ReturnType);
|
||||
}
|
||||
catch (CompileException e)
|
||||
{
|
||||
diagnostics.Add(e.Diagnostic);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
returnType = ResolveType(function.ReturnType);
|
||||
if (body == null || returnType == null || invalidParameter)
|
||||
return null;
|
||||
|
||||
return new TypedNodeDefinitionFunc(function.Tokens, moduleName, function.Name, parameters, body, returnType);
|
||||
}
|
||||
catch (CompileException e)
|
||||
{
|
||||
diagnostics.Add(e.Diagnostic);
|
||||
}
|
||||
|
||||
if (body == null || returnType == null || invalidParameter)
|
||||
return null;
|
||||
|
||||
return new TypedNodeDefinitionFunc(function.Tokens, moduleName, function.Name, parameters, body, returnType);
|
||||
}
|
||||
|
||||
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
||||
{
|
||||
return new TypedNodeDefinitionFunc.Param(node.Tokens, node.Name, ResolveType(node.Type));
|
||||
}
|
||||
|
||||
private TypedNodeStatement CheckStatement(NodeStatement node)
|
||||
@@ -96,7 +98,11 @@ public class TypeChecker
|
||||
|
||||
private TypedNodeStatementBlock CheckStatementBlock(NodeStatementBlock statement)
|
||||
{
|
||||
return new TypedNodeStatementBlock(statement.Tokens, statement.Statements.Select(CheckStatement).ToList());
|
||||
using (scope.EnterScope())
|
||||
{
|
||||
var statements = statement.Statements.Select(CheckStatement).ToList();
|
||||
return new TypedNodeStatementBlock(statement.Tokens, statements);
|
||||
}
|
||||
}
|
||||
|
||||
private TypedNodeStatementFuncCall CheckStatementExpression(NodeStatementExpression statement)
|
||||
@@ -406,20 +412,45 @@ public class TypeChecker
|
||||
return customType;
|
||||
}
|
||||
|
||||
private class Scope(Scope? parent)
|
||||
private sealed class Scope
|
||||
{
|
||||
private readonly Dictionary<string, NubType> identifiers = new();
|
||||
private readonly Stack<Dictionary<string, NubType>> scopes = new();
|
||||
|
||||
public IDisposable EnterScope()
|
||||
{
|
||||
scopes.Push([]);
|
||||
return new ScopeGuard(this);
|
||||
}
|
||||
|
||||
public void DeclareIdentifier(string name, NubType type)
|
||||
{
|
||||
identifiers.Add(name, type);
|
||||
scopes.Peek().Add(name, type);
|
||||
}
|
||||
|
||||
public NubType? GetIdentifierType(string name)
|
||||
{
|
||||
return identifiers.TryGetValue(name, out var type)
|
||||
? type
|
||||
: parent?.GetIdentifierType(name);
|
||||
foreach (var scope in scopes)
|
||||
{
|
||||
if (scope.TryGetValue(name, out var type))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ExitScope()
|
||||
{
|
||||
scopes.Pop();
|
||||
}
|
||||
|
||||
private sealed class ScopeGuard(Scope owner) : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
owner.ExitScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user