Adds assignment to tables
This commit is contained in:
parent
f4ae57c550
commit
7d551b6313
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using Upsilon.Binder;
|
||||
using Upsilon.Evaluator;
|
||||
|
||||
namespace Upsilon.BaseTypes
|
||||
|
@ -28,5 +29,10 @@ namespace Upsilon.BaseTypes
|
|||
}
|
||||
return new LuaNull();
|
||||
}
|
||||
|
||||
public void Set(string s, LuaType obj)
|
||||
{
|
||||
EvaluationScope.Set(new VariableSymbol(s, obj.Type, false), obj);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,6 +56,8 @@ namespace Upsilon.Binder
|
|||
return BindReturnStatement((ReturnStatementSyntax) s);
|
||||
case SyntaxKind.FunctionAssignmentStatement:
|
||||
return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s);
|
||||
case SyntaxKind.TableAssignmentStatement:
|
||||
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(s.Kind.ToString());
|
||||
|
@ -217,52 +219,64 @@ namespace Upsilon.Binder
|
|||
|
||||
private BoundStatement BindAssignmentStatement(AssignmentExpressionSyntax e)
|
||||
{
|
||||
var name = e.Identifier.Name;
|
||||
var boundExpression = BindExpression(e.Expression);
|
||||
if (e.Identifier.Kind == SyntaxKind.VariableExpression)
|
||||
{
|
||||
var variableExpression = (VariableExpressionSyntax) e.Identifier;
|
||||
var name = variableExpression.Identifier.Name;
|
||||
var boundExpression = BindExpression(e.Expression);
|
||||
|
||||
var isLocal = e.LocalToken != null;
|
||||
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
||||
{
|
||||
if (boundExpression.Type == Type.Table)
|
||||
var isLocal = e.LocalToken != null;
|
||||
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
||||
{
|
||||
var tableExpression = (BoundTableExpression) boundExpression;
|
||||
variable = new TableVariableSymbol(name, tableExpression.ValueType, isLocal);
|
||||
}
|
||||
else
|
||||
{
|
||||
variable = new VariableSymbol(name, boundExpression.Type, isLocal);
|
||||
}
|
||||
if (isLocal)
|
||||
Scope.SetVariable(variable);
|
||||
else
|
||||
Scope.SetGlobalVariable(variable);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
|
||||
if (boundExpression.Type != variable.Type)
|
||||
{
|
||||
if (variable.Type == Type.Nil || boundExpression.Type == Type.Nil)
|
||||
if (boundExpression.Type == Type.Table)
|
||||
{
|
||||
variable.Type = boundExpression.Type;
|
||||
}
|
||||
else if (variable.Type == Type.Unknown)
|
||||
{
|
||||
variable.Type = boundExpression.Type;
|
||||
}
|
||||
else if (boundExpression.Type == Type.Unknown && boundExpression is BoundVariableExpression v)
|
||||
{
|
||||
v.Variable.Type = variable.Type;
|
||||
var tableExpression = (BoundTableExpression) boundExpression;
|
||||
variable = new TableVariableSymbol(name, tableExpression.ValueType, isLocal);
|
||||
}
|
||||
else
|
||||
{
|
||||
_diagnostics.LogCannotConvert(boundExpression.Type, variable.Type, e.Span);
|
||||
return new BoundExpressionStatement(boundExpression);
|
||||
variable = new VariableSymbol(name, boundExpression.Type, isLocal);
|
||||
}
|
||||
|
||||
if (isLocal)
|
||||
Scope.SetVariable(variable);
|
||||
else
|
||||
Scope.SetGlobalVariable(variable);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
|
||||
if (boundExpression.Type != variable.Type)
|
||||
{
|
||||
if (variable.Type == Type.Nil || boundExpression.Type == Type.Nil)
|
||||
{
|
||||
variable.Type = boundExpression.Type;
|
||||
}
|
||||
else if (variable.Type == Type.Unknown)
|
||||
{
|
||||
variable.Type = boundExpression.Type;
|
||||
}
|
||||
else if (boundExpression.Type == Type.Unknown && boundExpression is BoundVariableExpression v)
|
||||
{
|
||||
v.Variable.Type = variable.Type;
|
||||
}
|
||||
else
|
||||
{
|
||||
_diagnostics.LogCannotConvert(boundExpression.Type, variable.Type, e.Span);
|
||||
return new BoundExpressionStatement(boundExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BoundVariableAssignment(variable, boundExpression);
|
||||
}
|
||||
|
||||
return new BoundVariableAssignment(variable, boundExpression);
|
||||
if (e.Identifier.Kind == SyntaxKind.IndexExpression)
|
||||
{
|
||||
throw new Exception("this");
|
||||
}
|
||||
|
||||
return new BoundExpressionStatement(new BoundLiteralExpression(new LuaNull()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -419,6 +433,11 @@ namespace Upsilon.Binder
|
|||
var expression = BindExpression(e.Expression);
|
||||
|
||||
var index = BindExpression(e.Index);
|
||||
if (index.Type != Type.Number && index.Type != Type.String && index.Type != Type.Unknown)
|
||||
{
|
||||
_diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span);
|
||||
return new BoundLiteralExpression(new LuaNull());
|
||||
}
|
||||
switch (expression.Type)
|
||||
{
|
||||
case Type.Table:
|
||||
|
@ -432,5 +451,12 @@ namespace Upsilon.Binder
|
|||
return new BoundLiteralExpression(new LuaNull());
|
||||
}
|
||||
}
|
||||
|
||||
private BoundStatement BindTableAssignmentStatement(TableAssigmentStatementSyntax e)
|
||||
{
|
||||
var tableIndexExpression = (BoundIndexExpression)BindExpression(e.TableExpression);
|
||||
var value = BindExpression(e.Expression);
|
||||
return new BoundTableAssigmentStatement(tableIndexExpression, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ namespace Upsilon.Binder
|
|||
BoundFunctionExpression,
|
||||
BoundPromise,
|
||||
BoundReturnStatement,
|
||||
BoundFunctionAssignmentStatement
|
||||
BoundFunctionAssignmentStatement,
|
||||
BoundTableAssigmentStatement
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
namespace Upsilon.Binder
|
||||
{
|
||||
public class BoundTableAssigmentStatement : BoundStatement
|
||||
{
|
||||
public BoundIndexExpression TableIndexExpression { get; }
|
||||
public BoundExpression Value { get; }
|
||||
|
||||
public BoundTableAssigmentStatement(BoundIndexExpression tableIndexExpression, BoundExpression value)
|
||||
{
|
||||
TableIndexExpression = tableIndexExpression;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override BoundKind Kind => BoundKind.BoundTableAssigmentStatement;
|
||||
}
|
||||
}
|
|
@ -34,6 +34,10 @@ namespace Upsilon
|
|||
{
|
||||
LogError($"Invalid character found. Expected: '{expectedToken}'", location);
|
||||
}
|
||||
public void LogBadCharacter(TextSpan location)
|
||||
{
|
||||
LogError($"Invalid character found.", location);
|
||||
}
|
||||
|
||||
public void LogUnknownVariable(TextSpan span, string variable)
|
||||
{
|
||||
|
|
|
@ -15,26 +15,17 @@ namespace Upsilon.Evaluator
|
|||
private LuaType _returnValue;
|
||||
internal EvaluationScope Scope { get; }
|
||||
private bool HasReturned { get; set; }
|
||||
public int Identifier { get; }
|
||||
|
||||
internal Evaluator(Diagnostics diagnostics, Dictionary<string, LuaType> variables)
|
||||
{
|
||||
_diagnostics = diagnostics;
|
||||
Scope = new EvaluationScope(variables);
|
||||
Identifier = new Random().Next();
|
||||
}
|
||||
|
||||
private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope)
|
||||
{
|
||||
_diagnostics = diagnostics;
|
||||
Scope = new EvaluationScope(parentScope);
|
||||
Identifier = new Random().Next();
|
||||
}
|
||||
private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope, int identifier)
|
||||
{
|
||||
_diagnostics = diagnostics;
|
||||
Scope = new EvaluationScope(parentScope);
|
||||
Identifier = identifier;
|
||||
}
|
||||
|
||||
public LuaType Evaluate(BoundScript e)
|
||||
|
@ -90,6 +81,7 @@ namespace Upsilon.Evaluator
|
|||
case BoundKind.BoundFunctionAssignmentStatement:
|
||||
case BoundKind.BoundPromise:
|
||||
case BoundKind.BoundReturnStatement:
|
||||
case BoundKind.BoundTableAssigmentStatement:
|
||||
EvaluateStatement((BoundStatement) b);
|
||||
break;
|
||||
default:
|
||||
|
@ -119,6 +111,9 @@ namespace Upsilon.Evaluator
|
|||
case BoundKind.BoundFunctionAssignmentStatement:
|
||||
EvaluateBoundFunctionAssigmentStatement((BoundFunctionAssignmentStatement) e);
|
||||
break;
|
||||
case BoundKind.BoundTableAssigmentStatement:
|
||||
EvaluateTableAssignmentStatement((BoundTableAssigmentStatement) e);
|
||||
break;
|
||||
default:
|
||||
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
||||
break;
|
||||
|
@ -354,5 +349,19 @@ namespace Upsilon.Evaluator
|
|||
var indexer = evaluator.EvaluateExpression(e.Index);
|
||||
return indexable.Get(indexer.ToString(), scope);
|
||||
}
|
||||
|
||||
private void EvaluateTableAssignmentStatement(BoundTableAssigmentStatement e)
|
||||
{
|
||||
var table = EvaluateExpression(e.TableIndexExpression.Identifier);
|
||||
var index = EvaluateExpression(e.TableIndexExpression.Index);
|
||||
var value = EvaluateExpression(e.Value);
|
||||
if (table.Type != Type.Table)
|
||||
{
|
||||
throw new Exception("Not a table");
|
||||
}
|
||||
|
||||
var t = (LuaTable) table;
|
||||
t.Set(index.ToString(), value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,7 +83,6 @@ namespace Upsilon.Parser
|
|||
return ParseFunctionAssignmentStatement();
|
||||
}
|
||||
|
||||
|
||||
return ParseExpressionStatement();
|
||||
}
|
||||
|
||||
|
@ -109,7 +108,7 @@ namespace Upsilon.Parser
|
|||
case SyntaxKind.ElseIfKeyword:
|
||||
var nextElseIf =
|
||||
new ElseIfStatementSyntax((IfStatementSyntax) ParseIfStatement(SyntaxKind.ElseIfKeyword));
|
||||
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block,
|
||||
return new IfStatementSyntax(ifToken, (ExpressionStatementSyntax) condition, thenToken, (BlockStatementSyntax) block,
|
||||
nextElseIf);
|
||||
case SyntaxKind.ElseKeyword:
|
||||
{
|
||||
|
@ -117,11 +116,11 @@ namespace Upsilon.Parser
|
|||
var elseBlock = ParseBlockStatement(new[]{SyntaxKind.EndKeyword});
|
||||
var endEndToken = MatchToken(SyntaxKind.EndKeyword);
|
||||
var elseStatement = new ElseStatementSyntax(elseToken, (BlockStatementSyntax) elseBlock, endEndToken);
|
||||
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, elseStatement);
|
||||
return new IfStatementSyntax(ifToken, (ExpressionStatementSyntax) condition, thenToken, (BlockStatementSyntax) block, elseStatement);
|
||||
}
|
||||
case SyntaxKind.EndKeyword:
|
||||
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
||||
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, endToken);
|
||||
return new IfStatementSyntax(ifToken, (ExpressionStatementSyntax) condition, thenToken, (BlockStatementSyntax) block, endToken);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
@ -172,9 +171,13 @@ namespace Upsilon.Parser
|
|||
return new FunctionAssignmentStatementSyntax(localToken, identifier, functionExpression);
|
||||
}
|
||||
|
||||
private ExpressionStatementSyntax ParseExpressionStatement()
|
||||
private StatementSyntax ParseExpressionStatement()
|
||||
{
|
||||
var expression = ParseExpression();
|
||||
if (expression.Kind == SyntaxKind.IndexExpression && Current.Kind == SyntaxKind.Equals)
|
||||
{
|
||||
return ParseTableAssignmentExpression(expression);
|
||||
}
|
||||
return new ExpressionStatementSyntax(expression);
|
||||
}
|
||||
|
||||
|
@ -213,12 +216,21 @@ namespace Upsilon.Parser
|
|||
{
|
||||
localKeyword = MatchToken(SyntaxKind.LocalKeyword);
|
||||
}
|
||||
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
|
||||
|
||||
var identifier = ParseExpression();
|
||||
var assignmentToken = MatchToken(SyntaxKind.Equals);
|
||||
var expression = ParseExpression();
|
||||
return new AssignmentExpressionSyntax(localKeyword, identifier, assignmentToken, expression);
|
||||
}
|
||||
|
||||
private StatementSyntax ParseTableAssignmentExpression(ExpressionSyntax tableExpression)
|
||||
{
|
||||
var assignmentToken = MatchToken(SyntaxKind.Equals);
|
||||
var expression = ParseExpression();
|
||||
return new TableAssigmentStatementSyntax(tableExpression, assignmentToken, expression);
|
||||
}
|
||||
|
||||
|
||||
private ExpressionSyntax ParseBinaryExpression(SyntaxKindPrecedence.Precedence parentPrecedence = SyntaxKindPrecedence.Precedence.None)
|
||||
{
|
||||
ExpressionSyntax left;
|
||||
|
@ -278,7 +290,7 @@ namespace Upsilon.Parser
|
|||
expression = new LiteralExpressionSyntax(nilToken, null);
|
||||
break;
|
||||
default:
|
||||
_diagnostics.LogBadCharacter(new TextSpan(_position, 1), SyntaxKind.Identifier);
|
||||
_diagnostics.LogBadCharacter(new TextSpan(_position, 1));
|
||||
NextToken();
|
||||
expression = new BadExpressionSyntax();
|
||||
break;
|
||||
|
|
|
@ -5,11 +5,11 @@ namespace Upsilon.Parser
|
|||
{
|
||||
public sealed class AssignmentExpressionSyntax : StatementSyntax
|
||||
{
|
||||
public AssignmentExpressionSyntax(SyntaxToken localToken, IdentifierToken identifier, SyntaxToken equalsToken,
|
||||
public AssignmentExpressionSyntax(SyntaxToken localToken, ExpressionSyntax identifyExpression, SyntaxToken equalsToken,
|
||||
ExpressionSyntax expression)
|
||||
{
|
||||
LocalToken = localToken;
|
||||
Identifier = identifier;
|
||||
Identifier = identifyExpression;
|
||||
EqualsToken = equalsToken;
|
||||
Expression = expression;
|
||||
var start = LocalToken?.Span.Start ?? Identifier.Span.Start;
|
||||
|
@ -19,7 +19,7 @@ namespace Upsilon.Parser
|
|||
public override SyntaxKind Kind => SyntaxKind.AssignmentStatement;
|
||||
|
||||
public SyntaxToken LocalToken { get; }
|
||||
public IdentifierToken Identifier { get; }
|
||||
public ExpressionSyntax Identifier { get; }
|
||||
public SyntaxToken EqualsToken { get; }
|
||||
public ExpressionSyntax Expression { get; }
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public class TableAssigmentStatementSyntax : StatementSyntax
|
||||
{
|
||||
public ExpressionSyntax TableExpression { get; }
|
||||
public SyntaxToken AssignmentToken { get; }
|
||||
public ExpressionSyntax Expression { get; }
|
||||
|
||||
public TableAssigmentStatementSyntax(ExpressionSyntax tableExpression, SyntaxToken assignmentToken,
|
||||
ExpressionSyntax expression)
|
||||
{
|
||||
TableExpression = tableExpression;
|
||||
AssignmentToken = assignmentToken;
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public override SyntaxKind Kind => SyntaxKind.TableAssignmentStatement;
|
||||
|
||||
public override IEnumerable<SyntaxNode> ChildNodes()
|
||||
{
|
||||
yield return TableExpression;
|
||||
yield return AssignmentToken;
|
||||
yield return Expression;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public enum SyntaxKind
|
||||
|
@ -67,6 +68,7 @@ namespace Upsilon.Parser
|
|||
ElseStatement,
|
||||
FunctionExpression,
|
||||
ReturnStatement,
|
||||
FunctionAssignmentStatement
|
||||
FunctionAssignmentStatement,
|
||||
TableAssignmentStatement
|
||||
}
|
||||
}
|
|
@ -153,6 +153,21 @@ table = {
|
|||
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);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AssignToTable()
|
||||
{
|
||||
const string input = @"
|
||||
table = {}
|
||||
table[1] = 400
|
||||
return table[1]
|
||||
";
|
||||
var script = new Script(input);
|
||||
Assert.Empty(script.Diagnostics.Messages);
|
||||
|
|
Loading…
Reference in New Issue