diff --git a/Resolver.cs b/Resolver.cs index 2df1884..d090fa0 100644 --- a/Resolver.cs +++ b/Resolver.cs @@ -1,12 +1,11 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; using Finn; using Finn.AST; -class Resolver : IExprVisitor +class Resolver : IExprVisitor>, ValueTuple> { private class NameCollection { @@ -72,78 +71,66 @@ class Resolver : IExprVisitor } } + private static readonly ValueTuple Void = ValueTuple.Create(); private readonly Interpreter Interpreter; - private readonly Stack> scopes = new(); Resolver(Interpreter interpreter) { Interpreter = interpreter; } - private void Resolve(ValueTuple context, Expr expr) + private void Resolve(ImmutableStack> scopes, Expr expr) { - expr.accept(context, this); + expr.accept(scopes, this); } - private void ResolveInNewScope(ValueTuple context, IReadOnlySet vars, Expr expr) + private void Resolve(ImmutableStack> scopes, ImmutableHashSet newScope, Expr value) { - BeginScope(vars); - Resolve(context, expr); - EndScope(); + Resolve(scopes.Push(newScope), value); } - private void BeginScope(IReadOnlySet vars) + public ValueTuple visitBinaryExpr(ImmutableStack> scopes, Binary expr) { - scopes.Push(vars); + Resolve(scopes, expr.Left); + Resolve(scopes, expr.Right); + return Void; } - private void EndScope() + public ValueTuple visitCallExpr(ImmutableStack> scopes, Call expr) { - scopes.Pop(); - } - - public ValueTuple visitBinaryExpr(ValueTuple context, Binary expr) - { - Resolve(context, expr.Left); - Resolve(context, expr.Right); - return context; - } - - public ValueTuple visitCallExpr(ValueTuple context, Call expr) - { - Resolve(context, expr.Left); + Resolve(scopes, expr.Left); foreach (var arg in expr.Arguments) { if (arg is not null) { - Resolve(context, arg); + Resolve(scopes, arg); } } - return context; + return Void; } - public ValueTuple visitGroupingExpr(ValueTuple context, Grouping expr) + public ValueTuple visitGroupingExpr(ImmutableStack> scopes, Grouping expr) { - Resolve(context, expr.Expression); - return context; + Resolve(scopes, expr.Expression); + return Void; } - public ValueTuple visitIfExpr(ValueTuple context, If expr) + public ValueTuple visitIfExpr(ImmutableStack> scopes, If expr) { - Resolve(context, expr.Condition); - Resolve(context, expr.Then); - Resolve(context, expr.Else); - return context; + Resolve(scopes, expr.Condition); + Resolve(scopes, expr.Then); + Resolve(scopes, expr.Else); + return Void; } - public ValueTuple visitIndexerExpr(ValueTuple context, Indexer expr) + public ValueTuple visitIndexerExpr(ImmutableStack> scopes, Indexer expr) { - Resolve(context, expr.Left); - Resolve(context, expr.Index); - return context; + Resolve(scopes, expr.Left); + Resolve(scopes, expr.Index); + return Void; } - public ValueTuple visitLetExpr(ValueTuple context, Let expr) + public ValueTuple visitLetExpr(ImmutableStack> scopes, Let expr) { var names = new NameCollection(); foreach (var binding in expr.Bindings) @@ -161,7 +148,7 @@ class Resolver : IExprVisitor } } - BeginScope(names.Names); + var newScopes = scopes.Push(names.Names); // TODO Put the static constructivity check here. // For now, just check that a definition only uses names that come before it. @@ -172,55 +159,54 @@ class Resolver : IExprVisitor switch (binding) { case FuncBinding fb: - ResolveFunction(context, fb); + ResolveFunction(newScopes, fb); break; case VarBinding vb: - Resolve(context, vb.Value); + Resolve(newScopes, vb.Value); break; default: throw new Exception("wtf there are no other binding types"); } } - Resolve(context, expr.Body); + Resolve(newScopes, expr.Body); - EndScope(); - return context; + return Void; } - private void ResolveFunction(ValueTuple context, FuncBinding fb) + private void ResolveFunction(ImmutableStack> scopes, FuncBinding fb) { NameCollection names = new(); foreach (var param in fb.Params) { param.accept(names, PatternNameCollector.Instance); } - ResolveInNewScope(context, names.Names, fb.Value); + Resolve(scopes, names.Names, fb.Value); } - public ValueTuple visitListExpr(ValueTuple context, List expr) + public ValueTuple visitListExpr(ImmutableStack> scopes, List expr) { foreach (var element in expr.Elements) { - Resolve(context, element); + Resolve(scopes, element); } - return context; + return Void; } - public ValueTuple visitLiteralExpr(ValueTuple context, Literal expr) + public ValueTuple visitLiteralExpr(ImmutableStack> scopes, Literal expr) { - return context; + return Void; } - public ValueTuple visitRecordExpr(ValueTuple context, Record expr) + public ValueTuple visitRecordExpr(ImmutableStack> scopes, Record expr) { if (expr.Base is not null) { - Resolve(context, expr.Base.Value); + Resolve(scopes, expr.Base.Value); foreach (var update in expr.Base.Updates) { if (update.Value is not null) { - Resolve(context, update.Value); + Resolve(scopes, update.Value); } else { @@ -232,66 +218,76 @@ class Resolver : IExprVisitor { if (extension.Value is not null) { - Resolve(context, extension.Value); + Resolve(scopes, extension.Value); } else { // TODO Resolve extension.Name like a var expression. } } - return context; + return Void; } - public ValueTuple visitSelectorExpr(ValueTuple context, Selector expr) + public ValueTuple visitSelectorExpr(ImmutableStack> scopes, Selector expr) { - Resolve(context, expr.Left); - return context; + Resolve(scopes, expr.Left); + return Void; } - public ValueTuple visitSequenceExpr(ValueTuple context, Sequence expr) + public ValueTuple visitSequenceExpr(ImmutableStack> scopes, Sequence expr) { - Resolve(context, expr.Left); - Resolve(context, expr.Right); - return context; + Resolve(scopes, expr.Left); + Resolve(scopes, expr.Right); + return Void; } - public ValueTuple visitUnaryExpr(ValueTuple context, Unary expr) + public ValueTuple visitUnaryExpr(ImmutableStack> scopes, Unary expr) { - Resolve(context, expr.Right); - return context; + Resolve(scopes, expr.Right); + return Void; } - public ValueTuple visitVariableExpr(ValueTuple context, Variable expr) + public ValueTuple visitVariableExpr(ImmutableStack> scopes, Variable expr) { - ResolveLocal(expr, expr.Value); - return context; + ResolveLocal(scopes, expr, expr.Value); + return Void; } - private void ResolveLocal(Expr expr, Token name) + private void ResolveLocal(ImmutableStack> scopes, Expr expr, Token name) { - // TODO I can't peek past the top element of the stack (as is correct for a stack). - // Switch to an immutable stack so I can just pop stuff off, or switch to a list. - throw new NotImplementedException(); + int depth = 0; + while (!scopes.IsEmpty) + { + scopes = scopes.Pop(out var scope); + if (scope.Contains((string)name.Literal!)) + { + Interpreter.Resolve(expr, depth); + return; + } + depth++; + } + // At this point, it must be a global. I don't like that we leave that resolution for runtime. + // TODO Don't leave that resolution for runtime. } - public ValueTuple visitVariantExpr(ValueTuple context, Variant expr) + public ValueTuple visitVariantExpr(ImmutableStack> scopes, Variant expr) { if (expr.Argument is not null) { - Resolve(context, expr.Argument); + Resolve(scopes, expr.Argument); } - return context; + return Void; } - public ValueTuple visitWhenExpr(ValueTuple context, When expr) + public ValueTuple visitWhenExpr(ImmutableStack> scopes, When expr) { - Resolve(context, expr.Head); + Resolve(scopes, expr.Head); foreach (var _case in expr.Cases) { var names = new NameCollection(); _case.Pattern.accept(names, PatternNameCollector.Instance); - ResolveInNewScope(context, names.Names, _case.Value); + Resolve(scopes, names.Names, _case.Value); } - return context; + return Void; } } \ No newline at end of file