Fixed Scoping issue

This commit is contained in:
Deukhoofd 2018-11-24 12:42:54 +01:00
parent 638394d25b
commit 62a18e22d4
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
9 changed files with 76 additions and 104 deletions

View File

@ -45,7 +45,7 @@ namespace Upsilon.BaseTypes
{
var parameterVariable = Parameters[i];
var parameterValue = variables[i];
innerEvaluator.Scope.Set(parameterVariable, parameterValue, true);
innerEvaluator.Scope.CreateLocal(parameterVariable, parameterValue);
}
return innerEvaluator.EvaluateNode(Block);
}

View File

@ -41,7 +41,7 @@ namespace Upsilon.BaseTypes
public void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value)
{
var s = index.ToString();
EvaluationScope.Set(new VariableSymbol(s, value.Type, false), value, true);
EvaluationScope.CreateLocal(new VariableSymbol(s, value.Type, false), value);
}
public IEnumerator<(string Key, ScriptType value)> GetEnumerator()

View File

@ -38,7 +38,7 @@ namespace Upsilon.Binder
Scope = new BoundScope(Scope);
foreach (var valueParameter in unboundFunctionStatement.Value.Parameters)
{
Scope.SetVariable(valueParameter);
Scope.AssignToNearest(valueParameter);
}
unboundFunctionStatement.Value.Block =
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.Value.UnboundBlock);
@ -202,7 +202,7 @@ namespace Upsilon.Binder
var functionVariable = function.Parameters[index];
var callingVariable = parameters[index];
functionVariable.Type = callingVariable.Type;
Scope.SetVariable(functionVariable);
Scope.DefineLocalVariable(functionVariable);
}
var unboundFunctionStatement = _unboundFunctions[function.Name];
@ -253,9 +253,9 @@ namespace Upsilon.Binder
}
if (isLocal)
Scope.SetVariable(variable);
Scope.DefineLocalVariable(variable);
else
Scope.SetGlobalVariable(variable);
Scope.AssignToNearest(variable);
}
else
{
@ -299,7 +299,7 @@ namespace Upsilon.Binder
var boundVariable = TryBindVariable(name, isLocal, boundExpression);
if (boundVariable != null)
{
return new BoundVariableAssignment(boundVariable, boundExpression);
return new BoundVariableAssignment(boundVariable, boundExpression, isLocal);
}
}
@ -369,7 +369,7 @@ namespace Upsilon.Binder
{
var vari = new VariableSymbol(identifierToken.Name, Type.Unknown, true);
parameters.Add(vari);
innerScope.SetVariable(vari);
innerScope.DefineLocalVariable(vari);
}
if (parameters.Count == 0)
@ -408,7 +408,7 @@ namespace Upsilon.Binder
{
var vari = new VariableSymbol(identifierToken.Name, Type.Unknown, true);
parameters.Add(vari);
innerScope.SetVariable(vari);
innerScope.DefineLocalVariable(vari);
}
@ -417,9 +417,9 @@ namespace Upsilon.Binder
variable = new FunctionVariableSymbol(name, Type.Function, isLocal, parameters.ToImmutable());
((FunctionVariableSymbol) variable).IsBound = !(func is UnboundFunctionExpression);
if (isLocal)
Scope.SetVariable(variable);
Scope.DefineLocalVariable(variable);
else
Scope.SetGlobalVariable(variable);
Scope.AssignToNearest(variable);
}
else
{
@ -528,7 +528,7 @@ namespace Upsilon.Binder
var variableName = e.Identifier.Name;
Scope = new BoundScope(Scope);
var variable = new VariableSymbol(variableName, Type.Number, true);
Scope.SetVariable(variable);
Scope.DefineLocalVariable(variable);
var boundStart = BindExpression(e.StartExpression);
var boundStop = BindExpression(e.StopExpression);
BoundExpression boundStep = null;
@ -546,7 +546,7 @@ namespace Upsilon.Binder
foreach (var variableIdentifier in e.Variables)
{
var variable = new VariableSymbol(variableIdentifier.Name, Type.Unknown, true);
Scope.SetVariable(variable);
Scope.DefineLocalVariable(variable);
array.Add(variable);
}
var boundEnumerableExpression = BindExpression(e.EnumerableExpression);

View File

@ -7,20 +7,17 @@ namespace Upsilon.Binder
public readonly BoundScope ParentScope;
private BoundScope _readOnlyScope;
public readonly Dictionary<string, VariableSymbol> Variables;
internal readonly Dictionary<string, VariableSymbol> LocalVariables;
public BoundScope(BoundScope parentScope)
{
ParentScope = parentScope;
Variables = new Dictionary<string, VariableSymbol>();
LocalVariables = new Dictionary<string, VariableSymbol>();
}
public BoundScope(Dictionary<string, VariableSymbol> variables, BoundScope parentScope)
{
ParentScope = parentScope;
Variables = variables;
LocalVariables = new Dictionary<string, VariableSymbol>();
}
public static BoundScope WithReadOnlyScope(BoundScope readOnlyScope)
@ -29,43 +26,29 @@ namespace Upsilon.Binder
return scope;
}
public void SetVariable(VariableSymbol var)
public void DefineLocalVariable(VariableSymbol var)
{
if (var.Local)
Variables.Add(var.Name, var);
}
public void AssignToNearest(VariableSymbol var)
{
if (Variables.ContainsKey(var.Name))
{
if (LocalVariables.ContainsKey(var.Name))
LocalVariables[var.Name] = var;
else
LocalVariables.Add(var.Name, var);
}
else if (ParentScope != null)
{
ParentScope.AssignToNearest(var);
}
else
{
if (Variables.ContainsKey(var.Name))
Variables[var.Name] = var;
else
Variables.Add(var.Name, var);
Variables.Add(var.Name, var);
}
}
public void SetGlobalVariable(VariableSymbol var)
{
if (ParentScope == null)
{
SetVariable(var);
}
else
{
ParentScope.SetGlobalVariable(var);
}
}
public bool TryGetVariable(string key, bool allowUpperScopes, out VariableSymbol result)
{
if (LocalVariables.TryGetValue(key, out result))
{
return true;
}
if (Variables.TryGetValue(key, out result))
{
return true;

View File

@ -4,11 +4,13 @@ namespace Upsilon.Binder
{
public VariableSymbol Variable { get; }
public BoundExpression BoundExpression { get; }
public bool IsLocalDefinition { get; }
public BoundVariableAssignment(VariableSymbol variable, BoundExpression boundExpression)
public BoundVariableAssignment(VariableSymbol variable, BoundExpression boundExpression, bool isLocalDefinition)
{
Variable = variable;
BoundExpression = boundExpression;
IsLocalDefinition = isLocalDefinition;
}
public override BoundKind Kind => BoundKind.BoundAssignmentStatement;

View File

@ -10,20 +10,17 @@ namespace Upsilon.Evaluator
private EvaluationScope _getOnlyParentScope;
public readonly Dictionary<string, ScriptType> Variables;
private readonly Dictionary<string, ScriptType> _localVariables;
internal EvaluationScope(EvaluationScope parentScope)
{
_parentScope = parentScope;
Variables = new Dictionary<string, ScriptType>();
_localVariables = new Dictionary<string, ScriptType>();
}
internal EvaluationScope(Dictionary<string, ScriptType> vars)
{
Variables = vars;
_localVariables = new Dictionary<string, ScriptType>();
}
internal static EvaluationScope CreateWithGetOnlyParent(EvaluationScope parent)
@ -32,55 +29,30 @@ namespace Upsilon.Evaluator
return scope;
}
public void Set(VariableSymbol symbol, ScriptType obj, bool createNew)
public void AssignToNearest(VariableSymbol symbol, ScriptType value)
{
if (symbol.Local && createNew)
if (Variables.ContainsKey(symbol.Name))
{
if (_localVariables.ContainsKey(symbol.Name))
{
_localVariables[symbol.Name] = obj;
}
else
{
_localVariables.Add(symbol.Name, obj);
}
Variables[symbol.Name] = value;
}
else if (_parentScope != null)
{
_parentScope.AssignToNearest(symbol, value);
}
else
{
if (Variables.ContainsKey(symbol.Name))
{
Variables[symbol.Name] = obj;
}
else if (_localVariables.ContainsKey(symbol.Name))
{
_localVariables[symbol.Name] = obj;
}
else if (_parentScope != null && _parentScope.TryGet(symbol.Name, out _))
{
_parentScope.Set(symbol, obj, false);
}
else
{
Variables.Add(symbol.Name, obj);
}
Variables.Add(symbol.Name, value);
}
}
public void SetGlobal(VariableSymbol symbol, ScriptType obj)
public void CreateLocal(VariableSymbol symbol, ScriptType value)
{
if (_parentScope != null)
_parentScope.SetGlobal(symbol, obj);
else
{
Set(symbol, obj, true);
}
Variables[symbol.Name] = value;
}
public bool TryGet(VariableSymbol symbol, out ScriptType obj)
{
if (_localVariables.TryGetValue(symbol.Name, out obj))
return true;
if (Variables.TryGetValue(symbol.Name, out obj))
return true;
if (_parentScope != null)
@ -94,8 +66,6 @@ namespace Upsilon.Evaluator
public bool TryGet(string variable, out ScriptType obj)
{
if (_localVariables.TryGetValue(variable, out obj))
return true;
if (Variables.TryGetValue(variable, out obj))
return true;
if (_parentScope != null)

View File

@ -59,7 +59,7 @@ namespace Upsilon.Evaluator
{
var parameter = parameters[index];
var parameterName = function.Parameters[index];
innerEvaluator.Scope.Set(parameterName, parameter, true);
innerEvaluator.Scope.CreateLocal(parameterName, parameter);
}
var result = innerEvaluator.EvaluateNode(function.Block);
@ -295,13 +295,13 @@ namespace Upsilon.Evaluator
private void EvaluateAssignmentStatement(BoundVariableAssignment e)
{
var val = EvaluateExpression(e.BoundExpression);
if (e.Variable.Local)
if (e.IsLocalDefinition)
{
Scope.Set(e.Variable, val, true);
Scope.CreateLocal(e.Variable, val);
}
else
{
Scope.SetGlobal(e.Variable, val);
Scope.AssignToNearest(e.Variable, val);
}
_lastValue = val;
}
@ -320,9 +320,9 @@ namespace Upsilon.Evaluator
continue;
var value = table.Get(_diagnostics, e.Span, new ScriptNumberLong(i + 1), Scope);
if (variableSymbol.Local)
Scope.Set(variableSymbol, value, true);
Scope.CreateLocal(variableSymbol, value);
else
Scope.SetGlobal(variableSymbol, value);
Scope.AssignToNearest(variableSymbol, value);
}
}
else
@ -374,9 +374,9 @@ namespace Upsilon.Evaluator
{
var func = EvaluateBoundFunctionStatement(e.Func);
if (e.Variable.Local)
Scope.Set(e.Variable, func, true);
Scope.CreateLocal(e.Variable, func);
else
Scope.SetGlobal(e.Variable, func);
Scope.AssignToNearest(e.Variable, func);
}
private ScriptType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression)
@ -434,8 +434,8 @@ namespace Upsilon.Evaluator
{
innerEvaluator.EvaluateStatement(boundStatement);
if (innerEvaluator._lastValue != null)
tableScope.Set(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
innerEvaluator._lastValue, true);
tableScope.CreateLocal(new VariableSymbol(currentPos.ToString(), innerEvaluator._lastValue.Type, false),
innerEvaluator._lastValue);
innerEvaluator._lastValue = null;
break;
}
@ -506,7 +506,7 @@ namespace Upsilon.Evaluator
{
var innerEvaluator = new Evaluator(_diagnostics, Scope);
var startVal = (ScriptNumberLong)innerEvaluator.EvaluateExpression(e.BoundStart);
innerEvaluator.Scope.Set(e.Variable, startVal, true);
innerEvaluator.Scope.CreateLocal(e.Variable, startVal);
var stopVal = (ScriptNumberLong)innerEvaluator.EvaluateExpression(e.BoundStop);
long step = 1;
if (e.BoundStep != null)
@ -546,18 +546,16 @@ namespace Upsilon.Evaluator
using (var enumerator = iterable.GetEnumerator())
{
bool first = true;
while (enumerator.MoveNext())
{
var (key, value) = enumerator.Current;
if (e.Variables[0].Name != "_")
innerEvaluator.Scope.Set(e.Variables[0], key.ToLuaType(), first);
innerEvaluator.Scope.CreateLocal(e.Variables[0], key.ToLuaType());
if (e.Variables[1].Name != "_")
innerEvaluator.Scope.Set(e.Variables[1], value, first);
innerEvaluator.Scope.CreateLocal(e.Variables[1], value);
innerEvaluator.EvaluateBoundBlockStatement((BoundBlockStatement) e.Block);
if (innerEvaluator.HasBroken)
break;
first = false;
}
}
}

View File

@ -45,8 +45,9 @@ namespace Upsilon.StandardLibraries
var scope = new EvaluationScope(basicFunctions.ToDictionary(x => x.Key, x => (ScriptType)x.Value));
var boundScope =
new BoundScope(
scope.Variables.ToDictionary(x => x.Key
, x => (VariableSymbol)new FunctionVariableSymbol(x.Key, x.Value.Type, false, ImmutableArray<VariableSymbol>.Empty){IsBound = true}),
scope.Variables.ToDictionary(x => x.Key,
x => (VariableSymbol) new FunctionVariableSymbol(x.Key, x.Value.Type, false,
ImmutableArray<VariableSymbol>.Empty) {IsBound = true}),
null);
return (scope, boundScope);
}
@ -55,8 +56,8 @@ namespace Upsilon.StandardLibraries
{
var luaVariable = value.ToLuaType();
var varSymbol = new VariableSymbol(name, luaVariable.Type, false);
BoundScope.SetVariable(varSymbol);
Scope.Set(varSymbol, luaVariable, true);
BoundScope.AssignToNearest(varSymbol);
Scope.AssignToNearest(varSymbol, luaVariable);
}
}
}

View File

@ -45,5 +45,23 @@ b = a
Assert.Equal(100, a);
}
[Fact]
public void InnerScopeDefinesToUpperLocalIfLogical()
{
const string input = @"
arr = {100, 56, 28}
local value = 0
for key, val in arr do
value = value + val
end
return value
";
var script = new Script(input, BoundScope, StaticScope);
Assert.Empty(script.Diagnostics.Messages);
var result = script.Evaluate<long>();
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(184, result);
}
}
}