Pass around an immutable scope stack in resolver

This commit is contained in:
Brandon Dyck 2023-10-04 00:05:10 -06:00
parent d5ffd1e414
commit ce7a755b07

View File

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