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 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; }
} }
} }

View File

@ -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; }
} }
} }

View File

@ -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(); 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 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;
} }

View File

@ -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; innerEvaluator.EvaluateStatement(boundStatement);
var function = (BoundFunctionExpression) expressionStatement.Expression; if (innerEvaluator._lastValue != null)
var func = new LuaFunction(function.Parameters, function.Block); tableScope.Set(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
dic.Add(new VariableSymbol(currentPos.ToString(), func.Type, false), func); innerEvaluator._lastValue);
} innerEvaluator._lastValue = null;
else if (boundStatement.Kind == BoundKind.BoundFunctionAssignmentStatement) break;
{ }
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;
} }
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);
} }
} }
} }

View File

@ -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);
}
} }
} }