Add Numeric For Loops
This commit is contained in:
parent
1928979b40
commit
d2c14d213c
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ namespace Upsilon.Binder
|
|||
BoundFunctionAssignmentStatement,
|
||||
BoundTableAssigmentStatement,
|
||||
BoundFullstopIndexExpression,
|
||||
BoundMultiAssignmentStatement
|
||||
BoundMultiAssignmentStatement,
|
||||
BoundNumericForStatement
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Upsilon.Evaluator;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class BasicMathExpressions : TestClass
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
using Upsilon.BaseTypes.Number;
|
||||
using Upsilon.Evaluator;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class ScopeTests : TestClass
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Upsilon.Evaluator;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class StringTests : TestClass
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Upsilon.Evaluator;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class TableTests : TestClass
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||
using Upsilon.Evaluator;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class UserDataDictionaryTests : TestClass
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||
using Upsilon.Evaluator;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class UserDataListTests : TestClass
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue