Reworked scope again

This commit is contained in:
Deukhoofd 2018-11-19 12:17:21 +01:00
parent 860f2cc7e5
commit 1f57eed3e7
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
6 changed files with 111 additions and 53 deletions

View File

@ -1,8 +1,14 @@
using Upsilon.Evaluator;
namespace Upsilon.BaseTypes
{
public interface IIndexable
internal interface IIndexable
{
LuaType Get(string s, int scopeIdentifier);
int EvaluatorIdentifier { get; }
LuaType Get(string s, EvaluationScope scope);
}
internal interface IScopeOwner
{
EvaluationScope EvaluationScope { get; }
}
}

View File

@ -1,14 +1,17 @@
using System.Collections.Immutable;
using Upsilon.Binder;
using Upsilon.Evaluator;
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;
Block = block;
EvaluationScope = new EvaluationScope(parentScope);
}
public override Type Type => Type.Function;
@ -19,5 +22,6 @@ namespace Upsilon.BaseTypes
public ImmutableArray<VariableSymbol> Parameters { get; }
public BoundBlockStatement Block { get; }
public EvaluationScope EvaluationScope { get; }
}
}

View File

@ -1,38 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Upsilon.Binder;
using Upsilon.Evaluator;
namespace Upsilon.BaseTypes
{
public class LuaTable : LuaType, IIndexable
internal class LuaTable : LuaType, IIndexable, IScopeOwner
{
private readonly Dictionary<string, VariableSymbol> _variableLookup;
private readonly Dictionary<VariableSymbol, LuaType> _map;
public int EvaluatorIdentifier { get; }
public EvaluationScope EvaluationScope { get; }
public LuaTable(Dictionary<VariableSymbol, LuaType> map, int scopeIdentifier)
public LuaTable(EvaluationScope scope)
{
_map = map;
EvaluatorIdentifier = scopeIdentifier;
_variableLookup = map.ToDictionary(x => x.Key.Name, x => x.Key);
EvaluationScope = scope;
_variableLookup = scope.Variables.ToDictionary(x => x.Key.Name, x => x.Key);
}
public override Type Type => Type.Table;
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))
return new LuaNull();
if (EvaluatorIdentifier != scopeIdentifier && variableSymbol.Local)
if (variableSymbol.Local && scope != EvaluationScope)
return new LuaNull();
return _map[variableSymbol];
if (EvaluationScope.TryGet(variableSymbol, out var o))
{
return o;
}
return new LuaNull();
}
}
}

View File

@ -7,6 +7,8 @@ namespace Upsilon.Evaluator
internal class EvaluationScope
{
private readonly EvaluationScope _parentScope;
private EvaluationScope _getOnlyParentScope;
internal readonly Dictionary<VariableSymbol, LuaType> Variables;
@ -15,11 +17,18 @@ namespace Upsilon.Evaluator
_parentScope = parentScope;
Variables = new Dictionary<VariableSymbol, LuaType>();
}
internal EvaluationScope(Dictionary<VariableSymbol, LuaType> 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)
{
if (Variables.ContainsKey(symbol))
@ -49,6 +58,9 @@ namespace Upsilon.Evaluator
if (_parentScope != null)
if (_parentScope.TryGet(symbol, out obj))
return true;
if (_getOnlyParentScope != null)
if (_getOnlyParentScope.TryGet(symbol, out obj))
return true;
return false;
}

View File

@ -153,7 +153,6 @@ namespace Upsilon.Evaluator
return EvaluateBoundFunctionStatement((BoundFunctionExpression) e);
case BoundKind.BoundPromise:
return EvaluateUnboundFunctionStatement((UnboundFunctionExpression) e);
break;
default:
throw new NotImplementedException();
}
@ -270,13 +269,13 @@ namespace Upsilon.Evaluator
private LuaType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression)
{
var func = new LuaFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block);
var func = new LuaFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope);
return func;
}
private LuaType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression)
{
var func = new LuaFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block);
var func = new LuaFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope);
return func;
}
@ -288,7 +287,7 @@ namespace Upsilon.Evaluator
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++)
{
var parameterVariable = function.Parameters[i];
@ -309,42 +308,32 @@ namespace Upsilon.Evaluator
private LuaType EvaluateTableExpression(BoundTableExpression e)
{
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;
foreach (var boundStatement in e.Statements)
{
if (boundStatement.Kind == BoundKind.BoundAssignmentStatement)
switch (boundStatement.Kind)
{
var assignment = (BoundVariableAssignment)boundStatement;
var key = assignment.Variable;
var value = EvaluateExpression(assignment.BoundExpression);
dic.Add(key, value);
}
else if (boundStatement.Kind == BoundKind.BoundFunctionExpression)
{
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);
if (innerEvaluator._lastValue != null)
dic.Add(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
innerEvaluator._lastValue);
innerEvaluator._lastValue = null;
case BoundKind.BoundAssignmentStatement:
case BoundKind.BoundFunctionExpression:
case BoundKind.BoundFunctionAssignmentStatement:
innerEvaluator.Evaluate(boundStatement);
break;
default:
{
innerEvaluator.EvaluateStatement(boundStatement);
if (innerEvaluator._lastValue != null)
tableScope.Set(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
innerEvaluator._lastValue);
innerEvaluator._lastValue = null;
break;
}
}
currentPos++;
}
return new LuaTable(dic, innerEvaluator.Identifier);
return new LuaTable(tableScope);
}
private LuaType EvaluateIndexExpression(BoundIndexExpression e)
@ -355,10 +344,15 @@ namespace Upsilon.Evaluator
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 indexer = innerEvaluator.EvaluateExpression(e.Index);
return indexable.Get(indexer.ToString(), Identifier);
var evaluator = new Evaluator(_diagnostics, scope);
var indexer = evaluator.EvaluateExpression(e.Index);
return indexable.Get(indexer.ToString(), scope);
}
}
}

View File

@ -123,5 +123,43 @@ return table[""func""]()()[1]()
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);
}
}
}