diff --git a/src/compiler/NubLang/Syntax/Binding/Binder.cs b/src/compiler/NubLang/Syntax/Binding/Binder.cs index f829366..261a784 100644 --- a/src/compiler/NubLang/Syntax/Binding/Binder.cs +++ b/src/compiler/NubLang/Syntax/Binding/Binder.cs @@ -6,15 +6,14 @@ using NubLang.Syntax.Tokenization; namespace NubLang.Syntax.Binding; -// TODO: Currently anonymous function does not get a new scope public sealed class Binder { private readonly SyntaxTree _syntaxTree; private readonly DefinitionTable _definitionTable; - // TODO: Implement proper variable tracking and scoping - private readonly Dictionary _variables = []; + private readonly Stack _variables = []; private readonly Stack _funcReturnTypes = []; + private readonly Stack _variableScopes = []; public Binder(SyntaxTree syntaxTree, DefinitionTable definitionTable) { @@ -26,6 +25,7 @@ public sealed class Binder { _variables.Clear(); _funcReturnTypes.Clear(); + _variableScopes.Clear(); var diagnostics = new List(); var definitions = new List(); @@ -66,13 +66,7 @@ public sealed class Binder foreach (var func in node.Functions) { var signature = BindFuncSignature(func.Signature); - - foreach (var parameter in signature.Parameters) - { - _variables[parameter.Name] = parameter.Type; - } - - var body = BindFuncBody(func.Body, signature.ReturnType); + var body = BindFuncBody(func.Body, signature.ReturnType, signature.Parameters); functions.Add(new BoundTraitFuncImpl(func.Tokens, func.Name, signature, body)); } @@ -121,13 +115,7 @@ public sealed class Binder _variables.Clear(); var signature = BindFuncSignature(node.Signature); - - foreach (var parameter in signature.Parameters) - { - _variables[parameter.Name] = parameter.Type; - } - - var body = BindFuncBody(node.Body, signature.ReturnType); + var body = BindFuncBody(node.Body, signature.ReturnType, signature.Parameters); return new BoundLocalFunc(node.Tokens, node.Namespace, node.Name, signature, body); } @@ -220,7 +208,7 @@ public sealed class Binder throw new NotImplementedException("Diagnostics not implemented"); } - _variables[statement.Name] = type; + _variables.Push(new Variable(statement.Name, type)); return new BoundVariableDeclaration(statement.Tokens, statement.Name, assignment, type); } @@ -280,11 +268,9 @@ public sealed class Binder var expectedParameterType = funcType.Parameters[i]; var parameter = expression.Parameters[i]; parameters.Add(new BoundFuncParameter(parameter.Tokens, parameter.Name, expectedParameterType)); - - _variables[parameter.Name] = expectedParameterType; } - var body = BindFuncBody(expression.Body, funcType.ReturnType); + var body = BindFuncBody(expression.Body, funcType.ReturnType, parameters); return new BoundAnonymousFunc(expression.Tokens, new NubFuncType(funcType.ReturnType, parameters.Select(x => x.Type).ToList()), parameters, funcType.ReturnType, body); } @@ -371,7 +357,11 @@ public sealed class Binder if (!expression.Namespace.HasValue) { - return new BoundVariableIdent(expression.Tokens, _variables[expression.Name], expression.Name); + var variable = _variables.FirstOrDefault(x => x.Name == expression.Name); + if (variable != null) + { + return new BoundVariableIdent(expression.Tokens, variable.Type, variable.Name); + } } throw new BindException(Diagnostic.Error($"No identifier with then name {(expression.Namespace.HasValue ? $"{expression.Namespace.Value}::" : "")}{expression.Name} exists").Build()); @@ -583,8 +573,17 @@ public sealed class Binder }; } - private BoundBlock BindBlock(BlockSyntax node) + private BoundBlock BindBlock(BlockSyntax node, IEnumerable? variables = null) { + _variableScopes.Push(_variables.Count); + if (variables != null) + { + foreach (var variable in variables) + { + _variables.Push(variable); + } + } + var statements = new List(); foreach (var statement in node.Statements) @@ -592,18 +591,50 @@ public sealed class Binder statements.Add(BindStatement(statement)); } + var count = _variableScopes.Pop(); + while (_variableScopes.Count > count) + { + _variableScopes.Pop(); + } + return new BoundBlock(node.Tokens, statements); } - private BoundBlock BindFuncBody(BlockSyntax block, NubType returnType) + private BoundBlock BindFuncBody(BlockSyntax block, NubType returnType, IEnumerable parameters) { _funcReturnTypes.Push(returnType); - var body = BindBlock(block); + var body = BindBlock(block, parameters.Select(x => new Variable(x.Name, x.Type))); _funcReturnTypes.Pop(); return body; } } +public record Variable(string Name, NubType Type); + +public class Scope(Scope? parent) +{ + private List _variables = []; + + public Variable Lookup(string name) + { + var variable = _variables.FirstOrDefault(x => x.Name == name); + if (variable == null) + { + if (parent != null) + { + return parent.Lookup(name); + } + } + + throw new BindException(Diagnostic.Error($"Variable {name} is not available in the current scope").Build()); + } + + public Scope SubScope() + { + return new Scope(this); + } +} + public class BindException : Exception { public Diagnostic Diagnostic { get; }