Reworked scope again
This commit is contained in:
parent
860f2cc7e5
commit
1f57eed3e7
|
@ -1,8 +1,14 @@
|
||||||
|
using Upsilon.Evaluator;
|
||||||
|
|
||||||
namespace Upsilon.BaseTypes
|
namespace Upsilon.BaseTypes
|
||||||
{
|
{
|
||||||
public interface IIndexable
|
internal interface IIndexable
|
||||||
{
|
{
|
||||||
LuaType Get(string s, int scopeIdentifier);
|
LuaType Get(string s, EvaluationScope scope);
|
||||||
int EvaluatorIdentifier { get; }
|
}
|
||||||
|
|
||||||
|
internal interface IScopeOwner
|
||||||
|
{
|
||||||
|
EvaluationScope EvaluationScope { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Upsilon.Binder;
|
using Upsilon.Binder;
|
||||||
|
using Upsilon.Evaluator;
|
||||||
|
|
||||||
namespace Upsilon.BaseTypes
|
namespace Upsilon.BaseTypes
|
||||||
{
|
{
|
||||||
internal class LuaFunction : LuaType
|
internal class LuaFunction : LuaType, IScopeOwner
|
||||||
{
|
{
|
||||||
public LuaFunction(ImmutableArray<VariableSymbol> parameters, BoundBlockStatement block)
|
public LuaFunction(ImmutableArray<VariableSymbol> parameters, BoundBlockStatement block,
|
||||||
|
EvaluationScope parentScope)
|
||||||
{
|
{
|
||||||
Parameters = parameters;
|
Parameters = parameters;
|
||||||
Block = block;
|
Block = block;
|
||||||
|
EvaluationScope = new EvaluationScope(parentScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Type Type => Type.Function;
|
public override Type Type => Type.Function;
|
||||||
|
@ -19,5 +22,6 @@ namespace Upsilon.BaseTypes
|
||||||
|
|
||||||
public ImmutableArray<VariableSymbol> Parameters { get; }
|
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||||
public BoundBlockStatement Block { get; }
|
public BoundBlockStatement Block { get; }
|
||||||
|
public EvaluationScope EvaluationScope { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,38 +1,42 @@
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Upsilon.Binder;
|
using Upsilon.Binder;
|
||||||
|
using Upsilon.Evaluator;
|
||||||
|
|
||||||
namespace Upsilon.BaseTypes
|
namespace Upsilon.BaseTypes
|
||||||
{
|
{
|
||||||
public class LuaTable : LuaType, IIndexable
|
internal class LuaTable : LuaType, IIndexable, IScopeOwner
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, VariableSymbol> _variableLookup;
|
private readonly Dictionary<string, VariableSymbol> _variableLookup;
|
||||||
private readonly Dictionary<VariableSymbol, LuaType> _map;
|
public EvaluationScope EvaluationScope { get; }
|
||||||
public int EvaluatorIdentifier { get; }
|
|
||||||
|
|
||||||
public LuaTable(Dictionary<VariableSymbol, LuaType> map, int scopeIdentifier)
|
public LuaTable(EvaluationScope scope)
|
||||||
{
|
{
|
||||||
_map = map;
|
EvaluationScope = scope;
|
||||||
EvaluatorIdentifier = scopeIdentifier;
|
_variableLookup = scope.Variables.ToDictionary(x => x.Key.Name, x => x.Key);
|
||||||
_variableLookup = map.ToDictionary(x => x.Key.Name, x => x.Key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override Type Type => Type.Table;
|
public override Type Type => Type.Table;
|
||||||
public override object ToCSharpObject()
|
public override object ToCSharpObject()
|
||||||
{
|
{
|
||||||
return _map.Where(x => !x.Key.Local).ToDictionary(x => x.Key.Name, x => x.Value.ToCSharpObject());
|
return EvaluationScope.Variables.Where(x => !x.Key.Local)
|
||||||
|
.ToDictionary(x => x.Key.Name, x => x.Value.ToCSharpObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public LuaType Get(string s, int scopeIdentifier)
|
public LuaType Get(string s, EvaluationScope scope)
|
||||||
{
|
{
|
||||||
if (!_variableLookup.TryGetValue(s, out var variableSymbol))
|
if (!_variableLookup.TryGetValue(s, out var variableSymbol))
|
||||||
return new LuaNull();
|
return new LuaNull();
|
||||||
if (EvaluatorIdentifier != scopeIdentifier && variableSymbol.Local)
|
if (variableSymbol.Local && scope != EvaluationScope)
|
||||||
|
return new LuaNull();
|
||||||
|
if (EvaluationScope.TryGet(variableSymbol, out var o))
|
||||||
|
{
|
||||||
|
return o;
|
||||||
|
}
|
||||||
return new LuaNull();
|
return new LuaNull();
|
||||||
return _map[variableSymbol];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,8 @@ namespace Upsilon.Evaluator
|
||||||
internal class EvaluationScope
|
internal class EvaluationScope
|
||||||
{
|
{
|
||||||
private readonly EvaluationScope _parentScope;
|
private readonly EvaluationScope _parentScope;
|
||||||
|
private EvaluationScope _getOnlyParentScope;
|
||||||
|
|
||||||
internal readonly Dictionary<VariableSymbol, LuaType> Variables;
|
internal readonly Dictionary<VariableSymbol, LuaType> Variables;
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,11 +17,18 @@ namespace Upsilon.Evaluator
|
||||||
_parentScope = parentScope;
|
_parentScope = parentScope;
|
||||||
Variables = new Dictionary<VariableSymbol, LuaType>();
|
Variables = new Dictionary<VariableSymbol, LuaType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal EvaluationScope(Dictionary<VariableSymbol, LuaType> vars)
|
internal EvaluationScope(Dictionary<VariableSymbol, LuaType> vars)
|
||||||
{
|
{
|
||||||
Variables = vars;
|
Variables = vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static EvaluationScope CreateWithGetOnlyParent(EvaluationScope parent)
|
||||||
|
{
|
||||||
|
var scope = new EvaluationScope(new Dictionary<VariableSymbol, LuaType>()) {_getOnlyParentScope = parent};
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
public void Set(VariableSymbol symbol, LuaType obj)
|
public void Set(VariableSymbol symbol, LuaType obj)
|
||||||
{
|
{
|
||||||
if (Variables.ContainsKey(symbol))
|
if (Variables.ContainsKey(symbol))
|
||||||
|
@ -49,6 +58,9 @@ namespace Upsilon.Evaluator
|
||||||
if (_parentScope != null)
|
if (_parentScope != null)
|
||||||
if (_parentScope.TryGet(symbol, out obj))
|
if (_parentScope.TryGet(symbol, out obj))
|
||||||
return true;
|
return true;
|
||||||
|
if (_getOnlyParentScope != null)
|
||||||
|
if (_getOnlyParentScope.TryGet(symbol, out obj))
|
||||||
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,6 @@ namespace Upsilon.Evaluator
|
||||||
return EvaluateBoundFunctionStatement((BoundFunctionExpression) e);
|
return EvaluateBoundFunctionStatement((BoundFunctionExpression) e);
|
||||||
case BoundKind.BoundPromise:
|
case BoundKind.BoundPromise:
|
||||||
return EvaluateUnboundFunctionStatement((UnboundFunctionExpression) e);
|
return EvaluateUnboundFunctionStatement((UnboundFunctionExpression) e);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -270,13 +269,13 @@ namespace Upsilon.Evaluator
|
||||||
|
|
||||||
private LuaType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression)
|
private LuaType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression)
|
||||||
{
|
{
|
||||||
var func = new LuaFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block);
|
var func = new LuaFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope);
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LuaType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression)
|
private LuaType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression)
|
||||||
{
|
{
|
||||||
var func = new LuaFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block);
|
var func = new LuaFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope);
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +287,7 @@ namespace Upsilon.Evaluator
|
||||||
throw new Exception($"Variable is not a function.");
|
throw new Exception($"Variable is not a function.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerEvaluator = new Evaluator(_diagnostics, Scope);
|
var innerEvaluator = new Evaluator(_diagnostics, function.EvaluationScope);
|
||||||
for (var i = 0; i < function.Parameters.Length; i++)
|
for (var i = 0; i < function.Parameters.Length; i++)
|
||||||
{
|
{
|
||||||
var parameterVariable = function.Parameters[i];
|
var parameterVariable = function.Parameters[i];
|
||||||
|
@ -309,42 +308,32 @@ namespace Upsilon.Evaluator
|
||||||
private LuaType EvaluateTableExpression(BoundTableExpression e)
|
private LuaType EvaluateTableExpression(BoundTableExpression e)
|
||||||
{
|
{
|
||||||
var dic = new Dictionary<VariableSymbol, LuaType>();
|
var dic = new Dictionary<VariableSymbol, LuaType>();
|
||||||
var innerEvaluator = new Evaluator(_diagnostics, Scope);
|
var tableScope = EvaluationScope.CreateWithGetOnlyParent(Scope);
|
||||||
|
var innerEvaluator = new Evaluator(_diagnostics, tableScope);
|
||||||
var currentPos = 1;
|
var currentPos = 1;
|
||||||
foreach (var boundStatement in e.Statements)
|
foreach (var boundStatement in e.Statements)
|
||||||
{
|
{
|
||||||
if (boundStatement.Kind == BoundKind.BoundAssignmentStatement)
|
switch (boundStatement.Kind)
|
||||||
{
|
{
|
||||||
var assignment = (BoundVariableAssignment)boundStatement;
|
case BoundKind.BoundAssignmentStatement:
|
||||||
var key = assignment.Variable;
|
case BoundKind.BoundFunctionExpression:
|
||||||
var value = EvaluateExpression(assignment.BoundExpression);
|
case BoundKind.BoundFunctionAssignmentStatement:
|
||||||
dic.Add(key, value);
|
innerEvaluator.Evaluate(boundStatement);
|
||||||
}
|
break;
|
||||||
else if (boundStatement.Kind == BoundKind.BoundFunctionExpression)
|
default:
|
||||||
{
|
|
||||||
var expressionStatement = (BoundExpressionStatement)boundStatement;
|
|
||||||
var function = (BoundFunctionExpression) expressionStatement.Expression;
|
|
||||||
var func = new LuaFunction(function.Parameters, function.Block);
|
|
||||||
dic.Add(new VariableSymbol(currentPos.ToString(), func.Type, false), func);
|
|
||||||
}
|
|
||||||
else if (boundStatement.Kind == BoundKind.BoundFunctionAssignmentStatement)
|
|
||||||
{
|
|
||||||
var assignment = (BoundFunctionAssignmentStatement)boundStatement;
|
|
||||||
var key = assignment.Variable;
|
|
||||||
var value = EvaluateExpression(assignment.Func);
|
|
||||||
dic.Add(key, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
innerEvaluator.EvaluateStatement(boundStatement);
|
innerEvaluator.EvaluateStatement(boundStatement);
|
||||||
if (innerEvaluator._lastValue != null)
|
if (innerEvaluator._lastValue != null)
|
||||||
dic.Add(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
|
tableScope.Set(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
|
||||||
innerEvaluator._lastValue);
|
innerEvaluator._lastValue);
|
||||||
innerEvaluator._lastValue = null;
|
innerEvaluator._lastValue = null;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentPos++;
|
currentPos++;
|
||||||
}
|
}
|
||||||
return new LuaTable(dic, innerEvaluator.Identifier);
|
return new LuaTable(tableScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LuaType EvaluateIndexExpression(BoundIndexExpression e)
|
private LuaType EvaluateIndexExpression(BoundIndexExpression e)
|
||||||
|
@ -355,10 +344,15 @@ namespace Upsilon.Evaluator
|
||||||
throw new Exception("Variable is not indexable.");
|
throw new Exception("Variable is not indexable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scope = Scope;
|
||||||
|
if (variable is IScopeOwner scopeOwner)
|
||||||
|
{
|
||||||
|
scope = scopeOwner.EvaluationScope;
|
||||||
|
}
|
||||||
|
|
||||||
var innerEvaluator = new Evaluator(_diagnostics, Scope, indexable.EvaluatorIdentifier);
|
var evaluator = new Evaluator(_diagnostics, scope);
|
||||||
var indexer = innerEvaluator.EvaluateExpression(e.Index);
|
var indexer = evaluator.EvaluateExpression(e.Index);
|
||||||
return indexable.Get(indexer.ToString(), Identifier);
|
return indexable.Get(indexer.ToString(), scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -123,5 +123,43 @@ return table[""func""]()()[1]()
|
||||||
Assert.Equal(100, evaluated);
|
Assert.Equal(100, evaluated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ScopeInTables()
|
||||||
|
{
|
||||||
|
const string input = @"
|
||||||
|
table = {
|
||||||
|
local val = 400,
|
||||||
|
function test()
|
||||||
|
return val
|
||||||
|
end,
|
||||||
|
another = 10_000,
|
||||||
|
}
|
||||||
|
return table[""test""]()
|
||||||
|
";
|
||||||
|
var script = new Script(input);
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
var evaluated = script.Evaluate<long>();
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
Assert.Equal(400, evaluated);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UnnamedFunctionInTable()
|
||||||
|
{
|
||||||
|
const string input = @"
|
||||||
|
table = {
|
||||||
|
function()
|
||||||
|
return 400
|
||||||
|
end
|
||||||
|
}
|
||||||
|
return table[1]()
|
||||||
|
";
|
||||||
|
var script = new Script(input);
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
var evaluated = script.Evaluate<long>();
|
||||||
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
|
Assert.Equal(400, evaluated);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue