Fixed Scoping issue
This commit is contained in:
parent
638394d25b
commit
62a18e22d4
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue