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:
|
case SyntaxKind.ExpressionStatement:
|
||||||
return BindExpressionStatement((ExpressionStatementSyntax) s);
|
return BindExpressionStatement((ExpressionStatementSyntax) s);
|
||||||
case SyntaxKind.AssignmentStatement:
|
case SyntaxKind.AssignmentStatement:
|
||||||
return BindAssignmentStatement((AssignmentExpressionSyntax) s);
|
return BindAssignmentStatement((AssignmentStatementSyntax) s);
|
||||||
case SyntaxKind.BlockStatement:
|
case SyntaxKind.BlockStatement:
|
||||||
return BindBlockStatement((BlockStatementSyntax) s);
|
return BindBlockStatement((BlockStatementSyntax) s);
|
||||||
case SyntaxKind.IfStatement:
|
case SyntaxKind.IfStatement:
|
||||||
|
@ -59,6 +59,8 @@ namespace Upsilon.Binder
|
||||||
return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s);
|
return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s);
|
||||||
case SyntaxKind.TableAssignmentStatement:
|
case SyntaxKind.TableAssignmentStatement:
|
||||||
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
|
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
|
||||||
|
case SyntaxKind.MultiAssignmentStatement:
|
||||||
|
return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotImplementedException(s.Kind.ToString());
|
throw new NotImplementedException(s.Kind.ToString());
|
||||||
|
@ -220,25 +222,18 @@ namespace Upsilon.Binder
|
||||||
return new BoundExpressionStatement(exp);
|
return new BoundExpressionStatement(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BoundStatement BindAssignmentStatement(AssignmentExpressionSyntax e)
|
private VariableSymbol TryBindVariable(string name, bool isLocal, BoundExpression assignment)
|
||||||
{
|
{
|
||||||
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 (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
||||||
{
|
{
|
||||||
if (boundExpression.Type == Type.Table)
|
if (assignment.Type == Type.Table)
|
||||||
{
|
{
|
||||||
var tableExpression = (BoundTableExpression) boundExpression;
|
var tableExpression = (BoundTableExpression) assignment;
|
||||||
variable = new TableVariableSymbol(name, tableExpression.ValueType, isLocal);
|
variable = new TableVariableSymbol(name, tableExpression.ValueType, isLocal);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
variable = new VariableSymbol(name, boundExpression.Type, isLocal);
|
variable = new VariableSymbol(name, assignment.Type, isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLocal)
|
if (isLocal)
|
||||||
|
@ -249,39 +244,63 @@ namespace Upsilon.Binder
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
|
// 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 (assignment.Type != variable.Type)
|
||||||
{
|
{
|
||||||
if (variable.Type == Type.Nil || boundExpression.Type == Type.Nil)
|
if (variable.Type == Type.Nil || assignment.Type == Type.Nil)
|
||||||
{
|
{
|
||||||
variable.Type = boundExpression.Type;
|
variable.Type = assignment.Type;
|
||||||
}
|
}
|
||||||
else if (variable.Type == Type.Unknown)
|
else if (variable.Type == Type.Unknown)
|
||||||
{
|
{
|
||||||
variable.Type = boundExpression.Type;
|
variable.Type = assignment.Type;
|
||||||
}
|
}
|
||||||
else if (boundExpression.Type == Type.Unknown && boundExpression is BoundVariableExpression v)
|
else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v)
|
||||||
{
|
{
|
||||||
v.Variable.Type = variable.Type;
|
v.Variable.Type = variable.Type;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_diagnostics.LogCannotConvert(boundExpression.Type, variable.Type, e.Span);
|
_diagnostics.LogCannotConvert(assignment.Type, variable.Type, assignment.Span);
|
||||||
return new BoundExpressionStatement(boundExpression);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
return new BoundVariableAssignment(variable, boundExpression);
|
private BoundStatement BindAssignmentStatement(AssignmentStatementSyntax e)
|
||||||
}
|
|
||||||
|
|
||||||
if (e.Identifier.Kind == SyntaxKind.IndexExpression)
|
|
||||||
{
|
{
|
||||||
throw new Exception("this");
|
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;
|
||||||
|
var boundVariable = TryBindVariable(name, isLocal, boundExpression);
|
||||||
|
if (boundVariable != null)
|
||||||
|
{
|
||||||
|
return new BoundVariableAssignment(boundVariable, boundExpression);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BoundExpressionStatement(new BoundLiteralExpression(new LuaNull()));
|
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)
|
private BoundStatement BindBlockStatement(BlockStatementSyntax e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace Upsilon.Binder
|
||||||
BoundReturnStatement,
|
BoundReturnStatement,
|
||||||
BoundFunctionAssignmentStatement,
|
BoundFunctionAssignmentStatement,
|
||||||
BoundTableAssigmentStatement,
|
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.BoundPromise:
|
||||||
case BoundKind.BoundReturnStatement:
|
case BoundKind.BoundReturnStatement:
|
||||||
case BoundKind.BoundTableAssigmentStatement:
|
case BoundKind.BoundTableAssigmentStatement:
|
||||||
|
case BoundKind.BoundMultiAssignmentStatement:
|
||||||
EvaluateStatement((BoundStatement) b);
|
EvaluateStatement((BoundStatement) b);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -116,6 +117,9 @@ namespace Upsilon.Evaluator
|
||||||
case BoundKind.BoundTableAssigmentStatement:
|
case BoundKind.BoundTableAssigmentStatement:
|
||||||
EvaluateTableAssignmentStatement((BoundTableAssigmentStatement) e);
|
EvaluateTableAssignmentStatement((BoundTableAssigmentStatement) e);
|
||||||
break;
|
break;
|
||||||
|
case BoundKind.BoundMultiAssignmentStatement:
|
||||||
|
EvaluateMultiAssignmentStatement((BoundMultiAssignmentStatement) e);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
||||||
break;
|
break;
|
||||||
|
@ -275,6 +279,31 @@ namespace Upsilon.Evaluator
|
||||||
_lastValue = val;
|
_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)
|
private LuaType EvaluateVariableExpression(BoundVariableExpression e)
|
||||||
{
|
{
|
||||||
if (Scope.TryGet(e.Variable, out var val))
|
if (Scope.TryGet(e.Variable, out var val))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
@ -66,6 +67,10 @@ namespace Upsilon.Parser
|
||||||
{
|
{
|
||||||
return ParseAssignmentExpression();
|
return ParseAssignmentExpression();
|
||||||
}
|
}
|
||||||
|
if (Current.Kind == SyntaxKind.Identifier && Next.Kind == SyntaxKind.Comma)
|
||||||
|
{
|
||||||
|
return ParseAssignmentExpression();
|
||||||
|
}
|
||||||
if (Current.Kind == SyntaxKind.IfKeyword)
|
if (Current.Kind == SyntaxKind.IfKeyword)
|
||||||
{
|
{
|
||||||
return ParseIfStatement(SyntaxKind.IfKeyword);
|
return ParseIfStatement(SyntaxKind.IfKeyword);
|
||||||
|
@ -216,7 +221,7 @@ namespace Upsilon.Parser
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AssignmentExpressionSyntax ParseAssignmentExpression()
|
private StatementSyntax ParseAssignmentExpression()
|
||||||
{
|
{
|
||||||
SyntaxToken localKeyword = null;
|
SyntaxToken localKeyword = null;
|
||||||
if (Current.Kind == SyntaxKind.LocalKeyword)
|
if (Current.Kind == SyntaxKind.LocalKeyword)
|
||||||
|
@ -225,9 +230,28 @@ namespace Upsilon.Parser
|
||||||
}
|
}
|
||||||
|
|
||||||
var identifier = ParseExpression();
|
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 assignmentToken = MatchToken(SyntaxKind.Equals);
|
||||||
var expression = ParseExpression();
|
var expression = ParseExpression();
|
||||||
return new AssignmentExpressionSyntax(localKeyword, identifier, assignmentToken, expression);
|
return new AssignmentStatementSyntax(localKeyword, identifier, assignmentToken, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatementSyntax ParseTableAssignmentExpression(ExpressionSyntax tableExpression)
|
private StatementSyntax ParseTableAssignmentExpression(ExpressionSyntax tableExpression)
|
||||||
|
@ -286,8 +310,7 @@ namespace Upsilon.Parser
|
||||||
expression = ParseString();
|
expression = ParseString();
|
||||||
break;
|
break;
|
||||||
case SyntaxKind.Identifier:
|
case SyntaxKind.Identifier:
|
||||||
var token = MatchToken(SyntaxKind.Identifier);
|
expression = ParseVariableExpression();
|
||||||
expression = new VariableExpressionSyntax((IdentifierToken) token);
|
|
||||||
break;
|
break;
|
||||||
case SyntaxKind.OpenBrace:
|
case SyntaxKind.OpenBrace:
|
||||||
expression = ParseTable();
|
expression = ParseTable();
|
||||||
|
@ -305,6 +328,12 @@ namespace Upsilon.Parser
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ExpressionSyntax ParseVariableExpression()
|
||||||
|
{
|
||||||
|
var token = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
|
||||||
|
return new VariableExpressionSyntax(token);
|
||||||
|
}
|
||||||
|
|
||||||
private ExpressionSyntax ParseFunctionCallExpression(ExpressionSyntax expression)
|
private ExpressionSyntax ParseFunctionCallExpression(ExpressionSyntax expression)
|
||||||
{
|
{
|
||||||
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
||||||
|
|
|
@ -3,9 +3,9 @@ using Upsilon.Text;
|
||||||
|
|
||||||
namespace Upsilon.Parser
|
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)
|
ExpressionSyntax expression)
|
||||||
{
|
{
|
||||||
LocalToken = localToken;
|
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,
|
TableExpression,
|
||||||
IndexExpression,
|
IndexExpression,
|
||||||
FullStopIndexExpression,
|
FullStopIndexExpression,
|
||||||
|
MultiAssignmentStatement,
|
||||||
|
|
||||||
// script unit
|
// script unit
|
||||||
ScriptUnit,
|
ScriptUnit,
|
||||||
|
|
Loading…
Reference in New Issue