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: 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)
{ {

View File

@ -23,6 +23,7 @@ namespace Upsilon.Binder
BoundReturnStatement, BoundReturnStatement,
BoundFunctionAssignmentStatement, BoundFunctionAssignmentStatement,
BoundTableAssigmentStatement, 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.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))

View File

@ -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);

View File

@ -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;

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, TableExpression,
IndexExpression, IndexExpression,
FullStopIndexExpression, FullStopIndexExpression,
MultiAssignmentStatement,
// script unit // script unit
ScriptUnit, ScriptUnit,