Pass around an immutable scope stack in resolver
This commit is contained in:
parent
d5ffd1e414
commit
ce7a755b07
160
Resolver.cs
160
Resolver.cs
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user