Support for assigning multiple variables from a table
This commit is contained in:
parent
105c40bc05
commit
4ab755d0d2
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Upsilon.Binder
|
|||
BoundReturnStatement,
|
||||
BoundFunctionAssignmentStatement,
|
||||
BoundTableAssigmentStatement,
|
||||
BoundFullstopIndexExpression
|
||||
BoundFullstopIndexExpression,
|
||||
BoundMultiAssignmentStatement
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,6 +58,7 @@ namespace Upsilon.Parser
|
|||
TableExpression,
|
||||
IndexExpression,
|
||||
FullStopIndexExpression,
|
||||
MultiAssignmentStatement,
|
||||
|
||||
// script unit
|
||||
ScriptUnit,
|
||||
|
|
Loading…
Reference in New Issue