Support for assigning multiple variables from a table

This commit is contained in:
Deukhoofd 2018-11-21 17:18:35 +01:00
parent 105c40bc05
commit 4ab755d0d2
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
8 changed files with 184 additions and 54 deletions

View File

@ -48,7 +48,7 @@ namespace Upsilon.Binder
case SyntaxKind.ExpressionStatement:
return BindExpressionStatement((ExpressionStatementSyntax) s);
case SyntaxKind.AssignmentStatement:
return BindAssignmentStatement((AssignmentExpressionSyntax) s);
return BindAssignmentStatement((AssignmentStatementSyntax) s);
case SyntaxKind.BlockStatement:
return BindBlockStatement((BlockStatementSyntax) s);
case SyntaxKind.IfStatement:
@ -59,6 +59,8 @@ namespace Upsilon.Binder
return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s);
case SyntaxKind.TableAssignmentStatement:
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
case SyntaxKind.MultiAssignmentStatement:
return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s);
}
throw new NotImplementedException(s.Kind.ToString());
@ -220,7 +222,53 @@ namespace Upsilon.Binder
return new BoundExpressionStatement(exp);
}
private BoundStatement BindAssignmentStatement(AssignmentExpressionSyntax e)
private VariableSymbol TryBindVariable(string name, bool isLocal, BoundExpression assignment)
{
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
{
if (assignment.Type == Type.Table)
{
var tableExpression = (BoundTableExpression) assignment;
variable = new TableVariableSymbol(name, tableExpression.ValueType, isLocal);
}
else
{
variable = new VariableSymbol(name, assignment.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 (assignment.Type != variable.Type)
{
if (variable.Type == Type.Nil || assignment.Type == Type.Nil)
{
variable.Type = assignment.Type;
}
else if (variable.Type == Type.Unknown)
{
variable.Type = assignment.Type;
}
else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v)
{
v.Variable.Type = variable.Type;
}
else
{
_diagnostics.LogCannotConvert(assignment.Type, variable.Type, assignment.Span);
return null;
}
}
}
return variable;
}
private BoundStatement BindAssignmentStatement(AssignmentStatementSyntax e)
{
if (e.Identifier.Kind == SyntaxKind.VariableExpression)
{
@ -229,59 +277,30 @@ namespace Upsilon.Binder
var boundExpression = BindExpression(e.Expression);
var isLocal = e.LocalToken != null;
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
var boundVariable = TryBindVariable(name, isLocal, boundExpression);
if (boundVariable != null)
{
if (boundExpression.Type == Type.Table)
{
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)
{
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(boundVariable, boundExpression);
}
return new BoundVariableAssignment(variable, boundExpression);
}
if (e.Identifier.Kind == SyntaxKind.IndexExpression)
{
throw new Exception("this");
}
return new BoundExpressionStatement(new BoundLiteralExpression(new LuaNull()));
}
private BoundStatement BindMultiAssignmentStatement(MultiAssignmentStatementSyntax s)
{
var ls = new List<VariableSymbol>();
var assignment = BindExpression(s.Expression);
var isLocal = s.LocalKeyword != null;
foreach (var identifierToken in s.Identifiers)
{
var boundVariable = TryBindVariable(identifierToken.Name, isLocal, assignment);
ls.Add(boundVariable);
}
return new BoundMultiAssignmentStatement(ls.ToImmutableArray(), assignment);
}
private BoundStatement BindBlockStatement(BlockStatementSyntax e)
{

View File

@ -23,6 +23,7 @@ namespace Upsilon.Binder
BoundReturnStatement,
BoundFunctionAssignmentStatement,
BoundTableAssigmentStatement,
BoundFullstopIndexExpression
BoundFullstopIndexExpression,
BoundMultiAssignmentStatement
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Upsilon.Binder
{
public class BoundMultiAssignmentStatement : BoundStatement
{
public ImmutableArray<VariableSymbol> Variables { get; }
public BoundExpression Assignment { get; }
public BoundMultiAssignmentStatement(ImmutableArray<VariableSymbol> variables, BoundExpression assignment)
{
Variables = variables;
Assignment = assignment;
}
public override BoundKind Kind => BoundKind.BoundMultiAssignmentStatement;
}
}

View File

@ -84,6 +84,7 @@ namespace Upsilon.Evaluator
case BoundKind.BoundPromise:
case BoundKind.BoundReturnStatement:
case BoundKind.BoundTableAssigmentStatement:
case BoundKind.BoundMultiAssignmentStatement:
EvaluateStatement((BoundStatement) b);
break;
default:
@ -116,6 +117,9 @@ namespace Upsilon.Evaluator
case BoundKind.BoundTableAssigmentStatement:
EvaluateTableAssignmentStatement((BoundTableAssigmentStatement) e);
break;
case BoundKind.BoundMultiAssignmentStatement:
EvaluateMultiAssignmentStatement((BoundMultiAssignmentStatement) e);
break;
default:
EvaluateExpressionStatement((BoundExpressionStatement) e);
break;
@ -275,6 +279,31 @@ namespace Upsilon.Evaluator
_lastValue = val;
}
private void EvaluateMultiAssignmentStatement(BoundMultiAssignmentStatement e)
{
var val = EvaluateExpression(e.Assignment);
if (val.Type == Type.Table)
{
var table = (LuaTable) val;
for (var i = 0; i < e.Variables.Length; i++)
{
var variableSymbol = e.Variables[i];
if (variableSymbol == null)
continue;
var value = table.Get(_diagnostics, e.Span, new NumberLong(i + 1), Scope);
if (variableSymbol.Local)
Scope.Set(variableSymbol, value);
else
Scope.SetGlobal(variableSymbol, value);
}
}
else
{
_diagnostics.LogError($"Can't assign type '{val.Type}' to multiple variables.", e.Span);
}
_lastValue = val;
}
private LuaType EvaluateVariableExpression(BoundVariableExpression e)
{
if (Scope.TryGet(e.Variable, out var val))

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Upsilon.Text;
@ -66,6 +67,10 @@ namespace Upsilon.Parser
{
return ParseAssignmentExpression();
}
if (Current.Kind == SyntaxKind.Identifier && Next.Kind == SyntaxKind.Comma)
{
return ParseAssignmentExpression();
}
if (Current.Kind == SyntaxKind.IfKeyword)
{
return ParseIfStatement(SyntaxKind.IfKeyword);
@ -216,7 +221,7 @@ namespace Upsilon.Parser
return expression;
}
private AssignmentExpressionSyntax ParseAssignmentExpression()
private StatementSyntax ParseAssignmentExpression()
{
SyntaxToken localKeyword = null;
if (Current.Kind == SyntaxKind.LocalKeyword)
@ -225,9 +230,28 @@ namespace Upsilon.Parser
}
var identifier = ParseExpression();
if (Current.Kind == SyntaxKind.Comma)
{
if (identifier.Kind != SyntaxKind.VariableExpression)
{
_diagnostics.LogError("Only identifiers can be used for a multi assignment statement.", identifier.Span);
return new ExpressionStatementSyntax(new BadExpressionSyntax());
}
var cast = (VariableExpressionSyntax)identifier;
var ls = new List<IdentifierToken>(){cast.Identifier};
while (Current.Kind == SyntaxKind.Comma)
{
NextToken();
ls.Add((IdentifierToken) MatchToken(SyntaxKind.Identifier));
}
var assignmentTokenMulti = MatchToken(SyntaxKind.Equals);
var expressionMulti = ParseExpression();
return new MultiAssignmentStatementSyntax(localKeyword, ls, assignmentTokenMulti, expressionMulti);
}
var assignmentToken = MatchToken(SyntaxKind.Equals);
var expression = ParseExpression();
return new AssignmentExpressionSyntax(localKeyword, identifier, assignmentToken, expression);
return new AssignmentStatementSyntax(localKeyword, identifier, assignmentToken, expression);
}
private StatementSyntax ParseTableAssignmentExpression(ExpressionSyntax tableExpression)
@ -286,8 +310,7 @@ namespace Upsilon.Parser
expression = ParseString();
break;
case SyntaxKind.Identifier:
var token = MatchToken(SyntaxKind.Identifier);
expression = new VariableExpressionSyntax((IdentifierToken) token);
expression = ParseVariableExpression();
break;
case SyntaxKind.OpenBrace:
expression = ParseTable();
@ -305,6 +328,12 @@ namespace Upsilon.Parser
return expression;
}
private ExpressionSyntax ParseVariableExpression()
{
var token = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
return new VariableExpressionSyntax(token);
}
private ExpressionSyntax ParseFunctionCallExpression(ExpressionSyntax expression)
{
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);

View File

@ -3,9 +3,9 @@ using Upsilon.Text;
namespace Upsilon.Parser
{
public sealed class AssignmentExpressionSyntax : StatementSyntax
public sealed class AssignmentStatementSyntax : StatementSyntax
{
public AssignmentExpressionSyntax(SyntaxToken localToken, ExpressionSyntax identifyExpression, SyntaxToken equalsToken,
public AssignmentStatementSyntax(SyntaxToken localToken, ExpressionSyntax identifyExpression, SyntaxToken equalsToken,
ExpressionSyntax expression)
{
LocalToken = localToken;

View File

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Upsilon.Parser
{
public class MultiAssignmentStatementSyntax : StatementSyntax
{
public SyntaxToken LocalKeyword { get; }
public ImmutableArray<IdentifierToken> Identifiers { get; }
public SyntaxToken AssignmentToken { get; }
public ExpressionSyntax Expression { get; }
public MultiAssignmentStatementSyntax(SyntaxToken localKeyword, IEnumerable<IdentifierToken> identifiers,
SyntaxToken assignmentToken, ExpressionSyntax expression)
{
LocalKeyword = localKeyword;
Identifiers = identifiers.ToImmutableArray();
AssignmentToken = assignmentToken;
Expression = expression;
}
public override SyntaxKind Kind => SyntaxKind.MultiAssignmentStatement;
public override IEnumerable<SyntaxNode> ChildNodes()
{
yield return LocalKeyword;
foreach (var expressionSyntax in Identifiers)
yield return expressionSyntax;
yield return AssignmentToken;
yield return Expression;
}
}
}

View File

@ -58,6 +58,7 @@ namespace Upsilon.Parser
TableExpression,
IndexExpression,
FullStopIndexExpression,
MultiAssignmentStatement,
// script unit
ScriptUnit,