Add Numeric For Loops

This commit is contained in:
Deukhoofd 2018-11-23 14:38:45 +01:00
parent 1928979b40
commit d2c14d213c
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
21 changed files with 256 additions and 22 deletions

View File

@ -5,7 +5,7 @@ namespace Upsilon.BaseTypes.Number
{
internal class NumberLong : Number
{
public long Value { get; }
public long Value { get; set; }
protected override bool IsFloat { get; } = false;
public NumberLong(long val)

View File

@ -68,6 +68,8 @@ namespace Upsilon.Binder
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
case SyntaxKind.MultiAssignmentStatement:
return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s);
case SyntaxKind.NumericForStatement:
return BindNumericForStatement((NumericForStatementSyntax) s);
}
throw new NotImplementedException(s.Kind.ToString());
@ -515,5 +517,21 @@ namespace Upsilon.Binder
var value = BindExpression(e.Expression);
return new BoundTableAssigmentStatement(indexableExpression, value);
}
private BoundStatement BindNumericForStatement(NumericForStatementSyntax e)
{
var variableName = e.Identifier.Name;
Scope = new BoundScope(Scope);
var variable = new VariableSymbol(variableName, Type.Number, true);
Scope.SetVariable(variable);
var boundStart = BindExpression(e.StartExpression);
var boundStop = BindExpression(e.StopExpression);
BoundExpression boundStep = null;
if (e.StepExpression != null)
boundStep = BindExpression(e.StepExpression);
var block = BindBlockStatement((BlockStatementSyntax) e.Block);
Scope = Scope.ParentScope;
return new BoundNumericForStatement(variable, boundStart, boundStop, boundStep, (BoundBlockStatement) block);
}
}
}

View File

@ -24,6 +24,7 @@ namespace Upsilon.Binder
BoundFunctionAssignmentStatement,
BoundTableAssigmentStatement,
BoundFullstopIndexExpression,
BoundMultiAssignmentStatement
BoundMultiAssignmentStatement,
BoundNumericForStatement
}
}

View File

@ -0,0 +1,23 @@
namespace Upsilon.Binder
{
public class BoundNumericForStatement : BoundStatement
{
public VariableSymbol Variable { get; }
public BoundExpression BoundStart { get; }
public BoundExpression BoundStop { get; }
public BoundExpression BoundStep { get; }
public BoundBlockStatement Block { get; }
public BoundNumericForStatement(VariableSymbol variable, BoundExpression boundStart,
BoundExpression boundStop, BoundExpression boundStep, BoundBlockStatement block)
{
Variable = variable;
BoundStart = boundStart;
BoundStop = boundStop;
BoundStep = boundStep;
Block = block;
}
public override BoundKind Kind => BoundKind.BoundNumericForStatement;
}
}

View File

@ -93,6 +93,7 @@ namespace Upsilon.Evaluator
case BoundKind.BoundReturnStatement:
case BoundKind.BoundTableAssigmentStatement:
case BoundKind.BoundMultiAssignmentStatement:
case BoundKind.BoundNumericForStatement:
EvaluateStatement((BoundStatement) b);
break;
default:
@ -128,6 +129,9 @@ namespace Upsilon.Evaluator
case BoundKind.BoundMultiAssignmentStatement:
EvaluateMultiAssignmentStatement((BoundMultiAssignmentStatement) e);
break;
case BoundKind.BoundNumericForStatement:
EvaluateNumericForStatement((BoundNumericForStatement) e);
break;
default:
EvaluateExpressionStatement((BoundExpressionStatement) e);
break;
@ -485,5 +489,34 @@ namespace Upsilon.Evaluator
}
indexable.Set(_diagnostics, e.Span, index.ToString().ToLuaType(), value);
}
private void EvaluateNumericForStatement(BoundNumericForStatement e)
{
var innerEvaluator = new Evaluator(_diagnostics, Scope);
var startVal = (NumberLong)innerEvaluator.EvaluateExpression(e.BoundStart);
innerEvaluator.Scope.Set(e.Variable, startVal);
var stopVal = (NumberLong)innerEvaluator.EvaluateExpression(e.BoundStop);
long step = 1;
if (e.BoundStep != null)
{
var stepVal = (NumberLong)innerEvaluator.EvaluateExpression(e.BoundStep);
step = stepVal.Value;
}
if (step > 0)
{
for (; startVal.Value <= stopVal.Value; startVal.Value = startVal.Value + step)
{
innerEvaluator.EvaluateBoundBlockStatement(e.Block);
}
}
else if (step < 0)
{
for (; startVal.Value >= stopVal.Value; startVal.Value = startVal.Value + step)
{
innerEvaluator.EvaluateBoundBlockStatement(e.Block);
}
}
}
}
}

View File

@ -87,6 +87,10 @@ namespace Upsilon.Parser
{
return ParseFunctionAssignmentStatement();
}
if (Current.Kind == SyntaxKind.ForKeyword)
{
return ParseForStatement();
}
return ParseExpressionStatement();
}
@ -131,6 +135,42 @@ namespace Upsilon.Parser
}
}
private StatementSyntax ParseForStatement()
{
var forToken = MatchToken(SyntaxKind.ForKeyword);
if (Next.Kind == SyntaxKind.Equals)
{
return ParseNumericForStatement(forToken);
}
return ParseGenericForStatement(forToken);
}
private StatementSyntax ParseNumericForStatement(SyntaxToken forToken)
{
var identifier = (IdentifierToken)MatchToken(SyntaxKind.Identifier);
var equals = MatchToken(SyntaxKind.Equals);
var v1 = ParseExpression();
var comma1 = MatchToken(SyntaxKind.Comma);
var v2 = ParseExpression();
SyntaxToken comma2 = null;
ExpressionSyntax v3 = null;
if (Current.Kind == SyntaxKind.Comma)
{
comma2 = MatchToken(SyntaxKind.Comma);
v3 = ParseExpression();
}
var doToken = MatchToken(SyntaxKind.DoKeyword);
var block = ParseBlockStatement(new []{SyntaxKind.EndKeyword});
var endToken = MatchToken(SyntaxKind.EndKeyword);
return new NumericForStatementSyntax(forToken, identifier, equals, v1, comma1, v2, comma2, v3, doToken,
block, endToken);
}
private StatementSyntax ParseGenericForStatement(SyntaxToken forToken)
{
throw new NotImplementedException();
}
private ExpressionSyntax ParseFunctionExpression()
{
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
namespace Upsilon.Parser
{
public class NumericForStatementSyntax : StatementSyntax
{
public SyntaxToken ForToken { get; }
public IdentifierToken Identifier { get; }
public SyntaxToken EqualsToken { get; }
public ExpressionSyntax StartExpression { get; }
public SyntaxToken Comma1 { get; }
public ExpressionSyntax StopExpression { get; }
public SyntaxToken Comma2 { get; }
public ExpressionSyntax StepExpression { get; }
public SyntaxToken DoToken { get; }
public StatementSyntax Block { get; }
public SyntaxToken EndToken { get; }
public NumericForStatementSyntax(SyntaxToken forToken, IdentifierToken identifier, SyntaxToken equalsToken,
ExpressionSyntax startExpression, SyntaxToken comma1, ExpressionSyntax stopExpression, SyntaxToken comma2,
ExpressionSyntax stepExpression, SyntaxToken doToken, StatementSyntax block, SyntaxToken endToken)
{
ForToken = forToken;
Identifier = identifier;
EqualsToken = equalsToken;
StartExpression = startExpression;
Comma1 = comma1;
StopExpression = stopExpression;
Comma2 = comma2;
StepExpression = stepExpression;
DoToken = doToken;
Block = block;
EndToken = endToken;
}
public override SyntaxKind Kind => SyntaxKind.NumericForStatement;
public override IEnumerable<SyntaxNode> ChildNodes()
{
yield return ForToken;
yield return Identifier;
yield return EqualsToken;
yield return StartExpression;
yield return Comma1;
yield return StopExpression;
if (Comma2 != null)
yield return Comma2;
if (StepExpression != null)
yield return StepExpression;
yield return DoToken;
yield return Block;
}
}
}

View File

@ -34,6 +34,12 @@ namespace Upsilon.Parser
return SyntaxKind.FunctionKeyword;
case "return":
return SyntaxKind.ReturnKeyword;
case "for":
return SyntaxKind.ForKeyword;
case "in":
return SyntaxKind.InKeyword;
case "do":
return SyntaxKind.DoKeyword;
default:
return SyntaxKind.Identifier;
}

View File

@ -43,6 +43,9 @@ namespace Upsilon.Parser
NilKeyword,
FunctionKeyword,
ReturnKeyword,
ForKeyword,
InKeyword,
DoKeyword,
Identifier,
@ -73,5 +76,6 @@ namespace Upsilon.Parser
ReturnStatement,
FunctionAssignmentStatement,
TableAssignmentStatement,
NumericForStatement
}
}

View File

@ -1,9 +1,7 @@
using System;
using System.Diagnostics;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class BasicMathExpressions : TestClass
{

View File

@ -0,0 +1,63 @@
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests.GeneralTests
{
public class ForLoopTests : TestClass
{
public ForLoopTests(StaticScriptFixture fix) : base(fix)
{
}
[Fact]
public void BasicNumericForLoopTest()
{
const string input = @"
a = 0
for i=0,5 do
a = a + i
end
return a
";
var script = new Script(input, BoundScope, StaticScope);
Assert.Empty(script.Diagnostics.Messages);
var result = script.Evaluate<long>();
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(15, result);
}
[Fact]
public void NumericForLoopWithDifferentStepTest()
{
const string input = @"
a = 0
for i=0,10,2 do
a = a + i
end
return a
";
var script = new Script(input, BoundScope, StaticScope);
Assert.Empty(script.Diagnostics.Messages);
var result = script.Evaluate<long>();
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(30, result);
}
[Fact]
public void NumericForLoopWithNegativeStepTest()
{
const string input = @"
a = 0
for i=5,0,-1 do
a = a + i
end
return a";
var script = new Script(input, BoundScope, StaticScope);
Assert.Empty(script.Diagnostics.Messages);
var result = script.Evaluate<long>();
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(15, result);
}
}
}

View File

@ -1,9 +1,7 @@
using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class FunctionTests : TestClass
{

View File

@ -1,9 +1,7 @@
using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class IfTests : TestClass
{

View File

@ -1,9 +1,7 @@
using Upsilon.BaseTypes.Number;
using Upsilon.Evaluator;
using Upsilon.Parser;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class MathPrecedence : TestClass
{

View File

@ -1,8 +1,7 @@
using Upsilon.BaseTypes.Number;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class ScopeTests : TestClass
{

View File

@ -1,7 +1,7 @@
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class StringTests : TestClass
{

View File

@ -1,7 +1,7 @@
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class TableTests : TestClass
{

View File

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class UserDataDictionaryTests : TestClass
{

View File

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class UserDataListTests : TestClass
{

View File

@ -2,10 +2,11 @@ using System;
using Upsilon.BaseTypes.UserData;
using Upsilon.Evaluator;
using Xunit;
// ReSharper disable UnusedMember.Local
// ReSharper disable ClassNeverInstantiated.Global
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class UserDataOperatorTests : TestClass, IClassFixture<UserDataOperatorTests.UserDataOperatorTestsFixture>
{

View File

@ -2,9 +2,10 @@ using System;
using Upsilon.BaseTypes.UserData;
using Upsilon.Evaluator;
using Xunit;
// ReSharper disable UnusedMember.Local
namespace UpsilonTests
namespace UpsilonTests.GeneralTests
{
public class UserDataTests : TestClass, IClassFixture<UserDataTests.UserDataTestsFixture>
{