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 parameterVariable = Parameters[i];
var parameterValue = variables[i]; var parameterValue = variables[i];
innerEvaluator.Scope.Set(parameterVariable, parameterValue, true); innerEvaluator.Scope.CreateLocal(parameterVariable, parameterValue);
} }
return innerEvaluator.EvaluateNode(Block); return innerEvaluator.EvaluateNode(Block);
} }

View File

@ -41,7 +41,7 @@ namespace Upsilon.BaseTypes
public void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value) public void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value)
{ {
var s = index.ToString(); 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() public IEnumerator<(string Key, ScriptType value)> GetEnumerator()

View File

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

View File

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

View File

@ -4,11 +4,13 @@ namespace Upsilon.Binder
{ {
public VariableSymbol Variable { get; } public VariableSymbol Variable { get; }
public BoundExpression BoundExpression { 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; Variable = variable;
BoundExpression = boundExpression; BoundExpression = boundExpression;
IsLocalDefinition = isLocalDefinition;
} }
public override BoundKind Kind => BoundKind.BoundAssignmentStatement; public override BoundKind Kind => BoundKind.BoundAssignmentStatement;

View File

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

View File

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

View File

@ -45,5 +45,23 @@ b = a
Assert.Equal(100, 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);
}
} }
} }