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
|
internal class NumberLong : Number
|
||||||
{
|
{
|
||||||
public long Value { get; }
|
public long Value { get; set; }
|
||||||
protected override bool IsFloat { get; } = false;
|
protected override bool IsFloat { get; } = false;
|
||||||
|
|
||||||
public NumberLong(long val)
|
public NumberLong(long val)
|
||||||
|
|
|
@ -68,6 +68,8 @@ namespace Upsilon.Binder
|
||||||
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
|
return BindTableAssignmentStatement((TableAssigmentStatementSyntax) s);
|
||||||
case SyntaxKind.MultiAssignmentStatement:
|
case SyntaxKind.MultiAssignmentStatement:
|
||||||
return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s);
|
return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s);
|
||||||
|
case SyntaxKind.NumericForStatement:
|
||||||
|
return BindNumericForStatement((NumericForStatementSyntax) s);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotImplementedException(s.Kind.ToString());
|
throw new NotImplementedException(s.Kind.ToString());
|
||||||
|
@ -515,5 +517,21 @@ namespace Upsilon.Binder
|
||||||
var value = BindExpression(e.Expression);
|
var value = BindExpression(e.Expression);
|
||||||
return new BoundTableAssigmentStatement(indexableExpression, value);
|
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,
|
BoundFunctionAssignmentStatement,
|
||||||
BoundTableAssigmentStatement,
|
BoundTableAssigmentStatement,
|
||||||
BoundFullstopIndexExpression,
|
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.BoundReturnStatement:
|
||||||
case BoundKind.BoundTableAssigmentStatement:
|
case BoundKind.BoundTableAssigmentStatement:
|
||||||
case BoundKind.BoundMultiAssignmentStatement:
|
case BoundKind.BoundMultiAssignmentStatement:
|
||||||
|
case BoundKind.BoundNumericForStatement:
|
||||||
EvaluateStatement((BoundStatement) b);
|
EvaluateStatement((BoundStatement) b);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -128,6 +129,9 @@ namespace Upsilon.Evaluator
|
||||||
case BoundKind.BoundMultiAssignmentStatement:
|
case BoundKind.BoundMultiAssignmentStatement:
|
||||||
EvaluateMultiAssignmentStatement((BoundMultiAssignmentStatement) e);
|
EvaluateMultiAssignmentStatement((BoundMultiAssignmentStatement) e);
|
||||||
break;
|
break;
|
||||||
|
case BoundKind.BoundNumericForStatement:
|
||||||
|
EvaluateNumericForStatement((BoundNumericForStatement) e);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
||||||
break;
|
break;
|
||||||
|
@ -485,5 +489,34 @@ namespace Upsilon.Evaluator
|
||||||
}
|
}
|
||||||
indexable.Set(_diagnostics, e.Span, index.ToString().ToLuaType(), value);
|
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();
|
return ParseFunctionAssignmentStatement();
|
||||||
}
|
}
|
||||||
|
if (Current.Kind == SyntaxKind.ForKeyword)
|
||||||
|
{
|
||||||
|
return ParseForStatement();
|
||||||
|
}
|
||||||
|
|
||||||
return ParseExpressionStatement();
|
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()
|
private ExpressionSyntax ParseFunctionExpression()
|
||||||
{
|
{
|
||||||
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);
|
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;
|
return SyntaxKind.FunctionKeyword;
|
||||||
case "return":
|
case "return":
|
||||||
return SyntaxKind.ReturnKeyword;
|
return SyntaxKind.ReturnKeyword;
|
||||||
|
case "for":
|
||||||
|
return SyntaxKind.ForKeyword;
|
||||||
|
case "in":
|
||||||
|
return SyntaxKind.InKeyword;
|
||||||
|
case "do":
|
||||||
|
return SyntaxKind.DoKeyword;
|
||||||
default:
|
default:
|
||||||
return SyntaxKind.Identifier;
|
return SyntaxKind.Identifier;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,9 @@ namespace Upsilon.Parser
|
||||||
NilKeyword,
|
NilKeyword,
|
||||||
FunctionKeyword,
|
FunctionKeyword,
|
||||||
ReturnKeyword,
|
ReturnKeyword,
|
||||||
|
ForKeyword,
|
||||||
|
InKeyword,
|
||||||
|
DoKeyword,
|
||||||
|
|
||||||
Identifier,
|
Identifier,
|
||||||
|
|
||||||
|
@ -73,5 +76,6 @@ namespace Upsilon.Parser
|
||||||
ReturnStatement,
|
ReturnStatement,
|
||||||
FunctionAssignmentStatement,
|
FunctionAssignmentStatement,
|
||||||
TableAssignmentStatement,
|
TableAssignmentStatement,
|
||||||
|
NumericForStatement
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class BasicMathExpressions : TestClass
|
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 Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class FunctionTests : TestClass
|
public class FunctionTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
using Upsilon.BaseTypes;
|
|
||||||
using Upsilon.BaseTypes.Number;
|
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class IfTests : TestClass
|
public class IfTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
using Upsilon.BaseTypes.Number;
|
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Parser;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class MathPrecedence : TestClass
|
public class MathPrecedence : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
using Upsilon.BaseTypes.Number;
|
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class ScopeTests : TestClass
|
public class ScopeTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class StringTests : TestClass
|
public class StringTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class TableTests : TestClass
|
public class TableTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class UserDataDictionaryTests : TestClass
|
public class UserDataDictionaryTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class UserDataListTests : TestClass
|
public class UserDataListTests : TestClass
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,10 +2,11 @@ using System;
|
||||||
using Upsilon.BaseTypes.UserData;
|
using Upsilon.BaseTypes.UserData;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Local
|
// ReSharper disable UnusedMember.Local
|
||||||
// ReSharper disable ClassNeverInstantiated.Global
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class UserDataOperatorTests : TestClass, IClassFixture<UserDataOperatorTests.UserDataOperatorTestsFixture>
|
public class UserDataOperatorTests : TestClass, IClassFixture<UserDataOperatorTests.UserDataOperatorTestsFixture>
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,10 @@ using System;
|
||||||
using Upsilon.BaseTypes.UserData;
|
using Upsilon.BaseTypes.UserData;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Local
|
// ReSharper disable UnusedMember.Local
|
||||||
|
|
||||||
namespace UpsilonTests
|
namespace UpsilonTests.GeneralTests
|
||||||
{
|
{
|
||||||
public class UserDataTests : TestClass, IClassFixture<UserDataTests.UserDataTestsFixture>
|
public class UserDataTests : TestClass, IClassFixture<UserDataTests.UserDataTestsFixture>
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue