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 System.Linq;
using Upsilon.Binder;
using Upsilon.Evaluator; using Upsilon.Evaluator;
namespace Upsilon.BaseTypes namespace Upsilon.BaseTypes
@ -28,5 +29,10 @@ namespace Upsilon.BaseTypes
} }
return new LuaNull(); 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); return BindReturnStatement((ReturnStatementSyntax) s);
case SyntaxKind.FunctionAssignmentStatement: case SyntaxKind.FunctionAssignmentStatement:
return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s); return BindFunctionAssignmentStatement((FunctionAssignmentStatementSyntax) s);
case SyntaxKind.TableAssignmentStatement:
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
} }
throw new NotImplementedException(s.Kind.ToString()); throw new NotImplementedException(s.Kind.ToString());
@ -217,52 +219,64 @@ namespace Upsilon.Binder
private BoundStatement BindAssignmentStatement(AssignmentExpressionSyntax e) private BoundStatement BindAssignmentStatement(AssignmentExpressionSyntax e)
{ {
var name = e.Identifier.Name; if (e.Identifier.Kind == SyntaxKind.VariableExpression)
var boundExpression = BindExpression(e.Expression); {
var variableExpression = (VariableExpressionSyntax) e.Identifier;
var name = variableExpression.Identifier.Name;
var boundExpression = BindExpression(e.Expression);
var isLocal = e.LocalToken != null; 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)
{ {
var tableExpression = (BoundTableExpression) boundExpression; if (boundExpression.Type == Type.Table)
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; var tableExpression = (BoundTableExpression) boundExpression;
} variable = new TableVariableSymbol(name, tableExpression.ValueType, isLocal);
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 else
{ {
_diagnostics.LogCannotConvert(boundExpression.Type, variable.Type, e.Span); variable = new VariableSymbol(name, boundExpression.Type, isLocal);
return new BoundExpressionStatement(boundExpression); }
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 expression = BindExpression(e.Expression);
var index = BindExpression(e.Index); 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) switch (expression.Type)
{ {
case Type.Table: case Type.Table:
@ -432,5 +451,12 @@ namespace Upsilon.Binder
return new BoundLiteralExpression(new LuaNull()); 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, BoundFunctionExpression,
BoundPromise, BoundPromise,
BoundReturnStatement, 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); LogError($"Invalid character found. Expected: '{expectedToken}'", location);
} }
public void LogBadCharacter(TextSpan location)
{
LogError($"Invalid character found.", location);
}
public void LogUnknownVariable(TextSpan span, string variable) public void LogUnknownVariable(TextSpan span, string variable)
{ {

View File

@ -15,26 +15,17 @@ namespace Upsilon.Evaluator
private LuaType _returnValue; private LuaType _returnValue;
internal EvaluationScope Scope { get; } internal EvaluationScope Scope { get; }
private bool HasReturned { get; set; } private bool HasReturned { get; set; }
public int Identifier { get; }
internal Evaluator(Diagnostics diagnostics, Dictionary<string, LuaType> variables) internal Evaluator(Diagnostics diagnostics, Dictionary<string, LuaType> variables)
{ {
_diagnostics = diagnostics; _diagnostics = diagnostics;
Scope = new EvaluationScope(variables); Scope = new EvaluationScope(variables);
Identifier = new Random().Next();
} }
private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope) private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope)
{ {
_diagnostics = diagnostics; _diagnostics = diagnostics;
Scope = new EvaluationScope(parentScope); 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) public LuaType Evaluate(BoundScript e)
@ -90,6 +81,7 @@ namespace Upsilon.Evaluator
case BoundKind.BoundFunctionAssignmentStatement: case BoundKind.BoundFunctionAssignmentStatement:
case BoundKind.BoundPromise: case BoundKind.BoundPromise:
case BoundKind.BoundReturnStatement: case BoundKind.BoundReturnStatement:
case BoundKind.BoundTableAssigmentStatement:
EvaluateStatement((BoundStatement) b); EvaluateStatement((BoundStatement) b);
break; break;
default: default:
@ -119,6 +111,9 @@ namespace Upsilon.Evaluator
case BoundKind.BoundFunctionAssignmentStatement: case BoundKind.BoundFunctionAssignmentStatement:
EvaluateBoundFunctionAssigmentStatement((BoundFunctionAssignmentStatement) e); EvaluateBoundFunctionAssigmentStatement((BoundFunctionAssignmentStatement) e);
break; break;
case BoundKind.BoundTableAssigmentStatement:
EvaluateTableAssignmentStatement((BoundTableAssigmentStatement) e);
break;
default: default:
EvaluateExpressionStatement((BoundExpressionStatement) e); EvaluateExpressionStatement((BoundExpressionStatement) e);
break; break;
@ -354,5 +349,19 @@ namespace Upsilon.Evaluator
var indexer = evaluator.EvaluateExpression(e.Index); var indexer = evaluator.EvaluateExpression(e.Index);
return indexable.Get(indexer.ToString(), scope); 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 ParseFunctionAssignmentStatement();
} }
return ParseExpressionStatement(); return ParseExpressionStatement();
} }
@ -109,7 +108,7 @@ namespace Upsilon.Parser
case SyntaxKind.ElseIfKeyword: case SyntaxKind.ElseIfKeyword:
var nextElseIf = var nextElseIf =
new ElseIfStatementSyntax((IfStatementSyntax) ParseIfStatement(SyntaxKind.ElseIfKeyword)); new ElseIfStatementSyntax((IfStatementSyntax) ParseIfStatement(SyntaxKind.ElseIfKeyword));
return new IfStatementSyntax(ifToken, condition, thenToken, (BlockStatementSyntax) block, return new IfStatementSyntax(ifToken, (ExpressionStatementSyntax) condition, thenToken, (BlockStatementSyntax) block,
nextElseIf); nextElseIf);
case SyntaxKind.ElseKeyword: case SyntaxKind.ElseKeyword:
{ {
@ -117,11 +116,11 @@ namespace Upsilon.Parser
var elseBlock = ParseBlockStatement(new[]{SyntaxKind.EndKeyword}); var elseBlock = ParseBlockStatement(new[]{SyntaxKind.EndKeyword});
var endEndToken = MatchToken(SyntaxKind.EndKeyword); var endEndToken = MatchToken(SyntaxKind.EndKeyword);
var elseStatement = new ElseStatementSyntax(elseToken, (BlockStatementSyntax) elseBlock, endEndToken); 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: case SyntaxKind.EndKeyword:
var endToken = MatchToken(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: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
@ -172,9 +171,13 @@ namespace Upsilon.Parser
return new FunctionAssignmentStatementSyntax(localToken, identifier, functionExpression); return new FunctionAssignmentStatementSyntax(localToken, identifier, functionExpression);
} }
private ExpressionStatementSyntax ParseExpressionStatement() private StatementSyntax ParseExpressionStatement()
{ {
var expression = ParseExpression(); var expression = ParseExpression();
if (expression.Kind == SyntaxKind.IndexExpression && Current.Kind == SyntaxKind.Equals)
{
return ParseTableAssignmentExpression(expression);
}
return new ExpressionStatementSyntax(expression); return new ExpressionStatementSyntax(expression);
} }
@ -213,12 +216,21 @@ namespace Upsilon.Parser
{ {
localKeyword = MatchToken(SyntaxKind.LocalKeyword); localKeyword = MatchToken(SyntaxKind.LocalKeyword);
} }
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
var identifier = ParseExpression();
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 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) private ExpressionSyntax ParseBinaryExpression(SyntaxKindPrecedence.Precedence parentPrecedence = SyntaxKindPrecedence.Precedence.None)
{ {
ExpressionSyntax left; ExpressionSyntax left;
@ -278,7 +290,7 @@ namespace Upsilon.Parser
expression = new LiteralExpressionSyntax(nilToken, null); expression = new LiteralExpressionSyntax(nilToken, null);
break; break;
default: default:
_diagnostics.LogBadCharacter(new TextSpan(_position, 1), SyntaxKind.Identifier); _diagnostics.LogBadCharacter(new TextSpan(_position, 1));
NextToken(); NextToken();
expression = new BadExpressionSyntax(); expression = new BadExpressionSyntax();
break; break;

View File

@ -5,11 +5,11 @@ namespace Upsilon.Parser
{ {
public sealed class AssignmentExpressionSyntax : StatementSyntax public sealed class AssignmentExpressionSyntax : StatementSyntax
{ {
public AssignmentExpressionSyntax(SyntaxToken localToken, IdentifierToken identifier, SyntaxToken equalsToken, public AssignmentExpressionSyntax(SyntaxToken localToken, ExpressionSyntax identifyExpression, SyntaxToken equalsToken,
ExpressionSyntax expression) ExpressionSyntax expression)
{ {
LocalToken = localToken; LocalToken = localToken;
Identifier = identifier; Identifier = identifyExpression;
EqualsToken = equalsToken; EqualsToken = equalsToken;
Expression = expression; Expression = expression;
var start = LocalToken?.Span.Start ?? Identifier.Span.Start; var start = LocalToken?.Span.Start ?? Identifier.Span.Start;
@ -19,7 +19,7 @@ namespace Upsilon.Parser
public override SyntaxKind Kind => SyntaxKind.AssignmentStatement; public override SyntaxKind Kind => SyntaxKind.AssignmentStatement;
public SyntaxToken LocalToken { get; } public SyntaxToken LocalToken { get; }
public IdentifierToken Identifier { get; } public ExpressionSyntax Identifier { get; }
public SyntaxToken EqualsToken { get; } public SyntaxToken EqualsToken { get; }
public ExpressionSyntax Expression { 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 namespace Upsilon.Parser
{ {
public enum SyntaxKind public enum SyntaxKind
@ -67,6 +68,7 @@ namespace Upsilon.Parser
ElseStatement, ElseStatement,
FunctionExpression, FunctionExpression,
ReturnStatement, ReturnStatement,
FunctionAssignmentStatement FunctionAssignmentStatement,
TableAssignmentStatement
} }
} }

View File

@ -153,6 +153,21 @@ table = {
end end
} }
return table[1]() 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); var script = new Script(input);
Assert.Empty(script.Diagnostics.Messages); Assert.Empty(script.Diagnostics.Messages);