Adds assignment to tables

This commit is contained in:
Deukhoofd 2018-11-19 16:22:13 +01:00
parent f4ae57c550
commit 7d551b6313
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
11 changed files with 176 additions and 57 deletions

View File

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

View File

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

View File

@ -21,6 +21,7 @@ namespace Upsilon.Binder
BoundFunctionExpression,
BoundPromise,
BoundReturnStatement,
BoundFunctionAssignmentStatement
BoundFunctionAssignmentStatement,
BoundTableAssigmentStatement
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
namespace Upsilon.Parser
{
public enum SyntaxKind
@ -67,6 +68,7 @@ namespace Upsilon.Parser
ElseStatement,
FunctionExpression,
ReturnStatement,
FunctionAssignmentStatement
FunctionAssignmentStatement,
TableAssignmentStatement
}
}

View File

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