diff --git a/Upsilon/BaseTypes/IIndexable.cs b/Upsilon/BaseTypes/IIndexable.cs index 644f9b8..1912917 100644 --- a/Upsilon/BaseTypes/IIndexable.cs +++ b/Upsilon/BaseTypes/IIndexable.cs @@ -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; } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/LuaFunction.cs b/Upsilon/BaseTypes/LuaFunction.cs index f24015b..fe98861 100644 --- a/Upsilon/BaseTypes/LuaFunction.cs +++ b/Upsilon/BaseTypes/LuaFunction.cs @@ -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 parameters, BoundBlockStatement block) + public LuaFunction(ImmutableArray 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 Parameters { get; } public BoundBlockStatement Block { get; } + public EvaluationScope EvaluationScope { get; } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/LuaTable.cs b/Upsilon/BaseTypes/LuaTable.cs index 8d6fbdf..f78d078 100644 --- a/Upsilon/BaseTypes/LuaTable.cs +++ b/Upsilon/BaseTypes/LuaTable.cs @@ -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 _variableLookup; - private readonly Dictionary _map; - public int EvaluatorIdentifier { get; } + public EvaluationScope EvaluationScope { get; } - public LuaTable(Dictionary 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(); } - } } \ No newline at end of file diff --git a/Upsilon/Evaluator/EvaluationScope.cs b/Upsilon/Evaluator/EvaluationScope.cs index a10a2d2..fab9831 100644 --- a/Upsilon/Evaluator/EvaluationScope.cs +++ b/Upsilon/Evaluator/EvaluationScope.cs @@ -7,6 +7,8 @@ namespace Upsilon.Evaluator internal class EvaluationScope { private readonly EvaluationScope _parentScope; + private EvaluationScope _getOnlyParentScope; + internal readonly Dictionary Variables; @@ -15,11 +17,18 @@ namespace Upsilon.Evaluator _parentScope = parentScope; Variables = new Dictionary(); } + internal EvaluationScope(Dictionary vars) { Variables = vars; } + internal static EvaluationScope CreateWithGetOnlyParent(EvaluationScope parent) + { + var scope = new EvaluationScope(new Dictionary()) {_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; } diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 284e3d3..26d965c 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -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(); - 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); } } } \ No newline at end of file diff --git a/UpsilonTests/TableTests.cs b/UpsilonTests/TableTests.cs index cddcc8d..3b873ca 100644 --- a/UpsilonTests/TableTests.cs +++ b/UpsilonTests/TableTests.cs @@ -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(); + 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(); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(400, evaluated); + } + } } \ No newline at end of file