diff --git a/src/VaVare.Tests/Statements/SelectionStatementTests.cs b/src/VaVare.Tests/Statements/SelectionStatementTests.cs index e4bb632..3bb5c36 100644 --- a/src/VaVare.Tests/Statements/SelectionStatementTests.cs +++ b/src/VaVare.Tests/Statements/SelectionStatementTests.cs @@ -1,7 +1,10 @@ -using NUnit.Framework; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using NUnit.Framework; using VaVare.Generators.Common; using VaVare.Generators.Common.Arguments.ArgumentTypes; using VaVare.Generators.Common.BinaryExpressions; +using VaVare.Generators.Common.Patterns; using VaVare.Models.References; using VaVare.Statements; using Assert = NUnit.Framework.Assert; @@ -106,5 +109,91 @@ public void If_WhenCreatingAnIfWithBinaryExpressionAndExpressionStatement_Should Assert.AreEqual("if(2==3)MyMethod();", conditional.If(new ConditionalBinaryExpression(new ConstantReference(2), new ConstantReference(3), ConditionalStatements.Equal), Statement.Expression.Invoke("MyMethod").AsStatement()).ToString()); } + + [Test] + public void If_ConditionalIsExpression() + { + var expected = "if(a is string){}".Replace(" ", ""); + var sut = conditional.If(new ValueArgument("a", false), new ValueArgument("string", false), ConditionalStatements.Is, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } + + [Test] + public void If_DeclarationPattern() + { + var expected = "if(a is string b){}".Replace(" ", ""); + + var declarationPattern = new DeclarationPattern(SyntaxFactory.ParseTypeName("string"), "b"); + var sut = conditional.If(new ValueArgument("a", false), declarationPattern, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } + + [Test] + public void If_NotTypePattern() + { + var expected = "if(a is not string){}".Replace(" ", ""); + + var typePattern = new TypePattern(SyntaxFactory.ParseTypeName("string")); + var pattern = new NotPattern(typePattern); + var sut = conditional.If(new ValueArgument("a", false), pattern, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } + + [Test] + public void If_NotConstantPattern() + { + var expected = "if(a is not 12){}".Replace(" ", ""); + + var typePattern = new ConstantPattern(new ValueArgument(12)); + var pattern = new NotPattern(typePattern); + var sut = conditional.If(new ValueArgument("a", false), pattern, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } + + [Test] + public void If_NotAndRelationalPattern() + { + var expected = "if(a is not 12 and > 15){}".Replace(" ", ""); + + var typePattern = new ConstantPattern(new ValueArgument(12)); + var relationPattern = new RelationalPattern(ConditionalStatements.GreaterThan, new ValueArgument(15)); + var and = new AndPattern(typePattern, relationPattern); + var pattern = new NotPattern(and); + var sut = conditional.If(new ValueArgument("a", false), pattern, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } + + [Test] + public void If_NotAndRelationalPatternGreaterEquals() + { + var expected = "if(a is not 12 and >= 15){}".Replace(" ", ""); + + var typePattern = new ConstantPattern(new ValueArgument(12)); + var relationPattern = new RelationalPattern(ConditionalStatements.GreaterThanOrEqual, new ValueArgument(15)); + var and = new AndPattern(typePattern, relationPattern); + var pattern = new NotPattern(and); + var sut = conditional.If(new ValueArgument("a", false), pattern, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } + + [Test] + public void If_NotOrRelationalPatternGreaterEquals() + { + var expected = "if(a is not 12 or >= 15){}".Replace(" ", ""); + + var typePattern = new ConstantPattern(new ValueArgument(12)); + var relationPattern = new RelationalPattern(ConditionalStatements.GreaterThanOrEqual, new ValueArgument(15)); + var and = new OrPattern(typePattern, relationPattern); + var pattern = new NotPattern(and); + var sut = conditional.If(new ValueArgument("a", false), pattern, BodyGenerator.Create()); + + Assert.That(sut.ToFullString(), Is.EqualTo(expected)); + } } } diff --git a/src/VaVare/Enums.cs b/src/VaVare/Enums.cs index 613802d..bf833de 100644 --- a/src/VaVare/Enums.cs +++ b/src/VaVare/Enums.cs @@ -36,6 +36,11 @@ public enum ConditionalStatements /// Generate with a less than or equal conditional statement: "<=". /// LessThanOrEqual, + + /// + /// Generate with an is statement: "is". + /// + Is, } /// diff --git a/src/VaVare/Factories/ConditionalFactory.cs b/src/VaVare/Factories/ConditionalFactory.cs index 9bd7f1a..ab970ff 100644 --- a/src/VaVare/Factories/ConditionalFactory.cs +++ b/src/VaVare/Factories/ConditionalFactory.cs @@ -21,6 +21,29 @@ public static SyntaxKind GetSyntaxKind(ConditionalStatements conditional) return SyntaxKind.LessThanExpression; case ConditionalStatements.LessThanOrEqual: return SyntaxKind.LessThanOrEqualExpression; + case ConditionalStatements.Is: + return SyntaxKind.IsExpression; + default: + throw new ArgumentOutOfRangeException(nameof(conditional), conditional, null); + } + } + + public static SyntaxKind GetSyntaxKindToken(ConditionalStatements conditional) + { + switch (conditional) + { + case ConditionalStatements.Equal: + return SyntaxKind.EqualsToken; + case ConditionalStatements.NotEqual: + return SyntaxKind.ExclamationEqualsToken; + case ConditionalStatements.GreaterThan: + return SyntaxKind.GreaterThanToken; + case ConditionalStatements.GreaterThanOrEqual: + return SyntaxKind.GreaterThanEqualsToken; + case ConditionalStatements.LessThan: + return SyntaxKind.LessThanToken; + case ConditionalStatements.LessThanOrEqual: + return SyntaxKind.LessThanEqualsToken; default: throw new ArgumentOutOfRangeException(nameof(conditional), conditional, null); } diff --git a/src/VaVare/Generators/Common/PatternExpressions/IPatternExpression.cs b/src/VaVare/Generators/Common/PatternExpressions/IPatternExpression.cs new file mode 100644 index 0000000..79750f7 --- /dev/null +++ b/src/VaVare/Generators/Common/PatternExpressions/IPatternExpression.cs @@ -0,0 +1,13 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace VaVare.Generators.Common.PatternExpressions +{ + public interface IPatternExpression + { + /// + /// Get the generated pattern expression. + /// + /// The generated pattern expression. + ExpressionSyntax GetPatternExpression(); + } +} diff --git a/src/VaVare/Generators/Common/PatternExpressions/IsPatternExpression.cs b/src/VaVare/Generators/Common/PatternExpressions/IsPatternExpression.cs new file mode 100644 index 0000000..82f9305 --- /dev/null +++ b/src/VaVare/Generators/Common/PatternExpressions/IsPatternExpression.cs @@ -0,0 +1,23 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.PatternExpressions +{ + public class IsPatternExpression : IPatternExpression + { + private readonly ExpressionSyntax _expressionSyntax; + private readonly PatternSyntax _patternSyntax; + + public IsPatternExpression(ExpressionSyntax expressionSyntax, PatternSyntax patternSyntax) + { + _expressionSyntax = expressionSyntax; + _patternSyntax = patternSyntax; + } + + public ExpressionSyntax GetPatternExpression() + { + return IsPatternExpression(_expressionSyntax, _patternSyntax); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/AndPattern.cs b/src/VaVare/Generators/Common/Patterns/AndPattern.cs new file mode 100644 index 0000000..01fb296 --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/AndPattern.cs @@ -0,0 +1,30 @@ +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class AndPattern : Pattern + { + private readonly PatternSyntax _left; + private readonly PatternSyntax _right; + + public AndPattern(PatternSyntax left, PatternSyntax right) + { + _left = left; + _right = right; + } + + public AndPattern(Pattern left, Pattern right) + { + _left = left.GetPatternSyntax(); + _right = right.GetPatternSyntax(); + } + + public override PatternSyntax GetPatternSyntax() + { + return BinaryPattern(SyntaxKind.AndPattern, _left, _right); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/ConstantPattern.cs b/src/VaVare/Generators/Common/Patterns/ConstantPattern.cs new file mode 100644 index 0000000..fbcadaa --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/ConstantPattern.cs @@ -0,0 +1,26 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using VaVare.Generators.Common.Arguments.ArgumentTypes; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class ConstantPattern : Pattern + { + private readonly ExpressionSyntax _expressionSyntax; + + public ConstantPattern(ExpressionSyntax expression) + { + _expressionSyntax = expression; + } + + public ConstantPattern(IArgument argument) + { + _expressionSyntax = argument.GetArgumentSyntax().Expression; + } + + public override PatternSyntax GetPatternSyntax() + { + return ConstantPattern(_expressionSyntax); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/DeclarationPattern.cs b/src/VaVare/Generators/Common/Patterns/DeclarationPattern.cs new file mode 100644 index 0000000..8642fb5 --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/DeclarationPattern.cs @@ -0,0 +1,37 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using VaVare.Factories; +using VaVare.Generators.Common.Arguments.ArgumentTypes; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class DeclarationPattern : Pattern + { + private readonly TypeSyntax _typeSyntax; + private readonly VariableDesignationSyntax _variableDesignationSyntax; + + public DeclarationPattern(TypeSyntax syntaxToken, VariableDesignationSyntax expressionSyntax) + { + _typeSyntax = syntaxToken; + _variableDesignationSyntax = expressionSyntax; + } + + public DeclarationPattern(TypeSyntax syntaxToken, string identifier) + { + _typeSyntax = syntaxToken; + _variableDesignationSyntax = SingleVariableDesignation(Identifier(identifier)); + } + + public DeclarationPattern(TypeSyntax syntaxToken, SyntaxToken identifier) + { + _typeSyntax = syntaxToken; + _variableDesignationSyntax = SingleVariableDesignation(identifier); + } + + public override PatternSyntax GetPatternSyntax() + { + return DeclarationPattern(_typeSyntax, _variableDesignationSyntax); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/DiscardPattern.cs b/src/VaVare/Generators/Common/Patterns/DiscardPattern.cs new file mode 100644 index 0000000..6420bd5 --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/DiscardPattern.cs @@ -0,0 +1,13 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class DiscardPattern : Pattern + { + public override PatternSyntax GetPatternSyntax() + { + return DiscardPattern(); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/IPattern.cs b/src/VaVare/Generators/Common/Patterns/IPattern.cs new file mode 100644 index 0000000..4fbbc5d --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/IPattern.cs @@ -0,0 +1,9 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace VaVare.Generators.Common.Patterns +{ + public interface IPattern + { + PatternSyntax GetPatternSyntax(); + } +} diff --git a/src/VaVare/Generators/Common/Patterns/NotPattern.cs b/src/VaVare/Generators/Common/Patterns/NotPattern.cs new file mode 100644 index 0000000..e827d46 --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/NotPattern.cs @@ -0,0 +1,28 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using VaVare.Factories; +using VaVare.Generators.Common.Arguments.ArgumentTypes; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class NotPattern : Pattern + { + private readonly PatternSyntax _pattern; + + public NotPattern(PatternSyntax pattern) + { + _pattern = pattern; + } + + public NotPattern(Pattern pattern) + { + _pattern = pattern.GetPatternSyntax(); + } + + public override PatternSyntax GetPatternSyntax() + { + return UnaryPattern(_pattern); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/OrPattern.cs b/src/VaVare/Generators/Common/Patterns/OrPattern.cs new file mode 100644 index 0000000..798fb70 --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/OrPattern.cs @@ -0,0 +1,30 @@ +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class OrPattern : Pattern + { + private readonly PatternSyntax _left; + private readonly PatternSyntax _right; + + public OrPattern(PatternSyntax left, PatternSyntax right) + { + _left = left; + _right = right; + } + + public OrPattern(Pattern left, Pattern right) + { + _left = left.GetPatternSyntax(); + _right = right.GetPatternSyntax(); + } + + public override PatternSyntax GetPatternSyntax() + { + return BinaryPattern(SyntaxKind.OrPattern, _left, _right); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/Pattern.cs b/src/VaVare/Generators/Common/Patterns/Pattern.cs new file mode 100644 index 0000000..3ddbde5 --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/Pattern.cs @@ -0,0 +1,19 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace VaVare.Generators.Common.Patterns +{ + /// + /// Provides the base class from which the classes that represent pattern derived. + /// + public abstract class Pattern : IPattern + { + /// + /// Initializes a new instance of the class. + /// + protected Pattern() + { + } + + public abstract PatternSyntax GetPatternSyntax(); + } +} diff --git a/src/VaVare/Generators/Common/Patterns/RelationalPattern.cs b/src/VaVare/Generators/Common/Patterns/RelationalPattern.cs new file mode 100644 index 0000000..f0c2b9a --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/RelationalPattern.cs @@ -0,0 +1,37 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using VaVare.Factories; +using VaVare.Generators.Common.Arguments.ArgumentTypes; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class RelationalPattern : Pattern + { + private readonly SyntaxToken _syntaxToken; + private readonly ExpressionSyntax _expressionSyntax; + + public RelationalPattern(SyntaxToken syntaxToken, ExpressionSyntax expressionSyntax) + { + _syntaxToken = syntaxToken; + _expressionSyntax = expressionSyntax; + } + + public RelationalPattern(ConditionalStatements conditionalStatement, ExpressionSyntax expressionSyntax) + { + _syntaxToken = Token(ConditionalFactory.GetSyntaxKindToken(conditionalStatement)); + _expressionSyntax = expressionSyntax; + } + + public RelationalPattern(ConditionalStatements conditionalStatement, IArgument argument) + { + _syntaxToken = Token(ConditionalFactory.GetSyntaxKindToken(conditionalStatement)); + _expressionSyntax = argument.GetArgumentSyntax().Expression; + } + + public override PatternSyntax GetPatternSyntax() + { + return RelationalPattern(_syntaxToken, _expressionSyntax); + } + } +} diff --git a/src/VaVare/Generators/Common/Patterns/TypePattern.cs b/src/VaVare/Generators/Common/Patterns/TypePattern.cs new file mode 100644 index 0000000..59560cb --- /dev/null +++ b/src/VaVare/Generators/Common/Patterns/TypePattern.cs @@ -0,0 +1,20 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace VaVare.Generators.Common.Patterns +{ + public class TypePattern : Pattern + { + private readonly TypeSyntax _typeSyntax; + + public TypePattern(TypeSyntax typeSyntax) + { + _typeSyntax = typeSyntax; + } + + public override PatternSyntax GetPatternSyntax() + { + return TypePattern(_typeSyntax); + } + } +} diff --git a/src/VaVare/Statements/SelectionStatement.cs b/src/VaVare/Statements/SelectionStatement.cs index c0a9bc4..daed45e 100644 --- a/src/VaVare/Statements/SelectionStatement.cs +++ b/src/VaVare/Statements/SelectionStatement.cs @@ -3,6 +3,8 @@ using VaVare.Factories; using VaVare.Generators.Common.Arguments.ArgumentTypes; using VaVare.Generators.Common.BinaryExpressions; +using VaVare.Generators.Common.PatternExpressions; +using VaVare.Generators.Common.Patterns; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace VaVare.Statements @@ -70,6 +72,60 @@ public StatementSyntax If(IArgument leftArgument, IArgument rightArgument, Condi expressionStatement); } + /// + /// Create the statement syntax for a if-conditional with a single statement. + /// + /// The left argument of the if-statement. + /// The right pattern of the if-statement. + /// Statement inside the if. + /// The declared statement syntax. + public StatementSyntax If(IArgument leftArgument, IPattern pattern, ExpressionStatementSyntax expressionStatement) + { + if (leftArgument == null) + { + throw new ArgumentNullException(nameof(leftArgument)); + } + + if (pattern == null) + { + throw new ArgumentNullException(nameof(pattern)); + } + + return + IfStatement( + IsPatternExpression( + leftArgument.GetArgumentSyntax().Expression, + pattern.GetPatternSyntax()), + expressionStatement); + } + + /// + /// Create the statement syntax for a if-conditional with a single statement. + /// + /// The left argument of the if-statement. + /// The right pattern of the if-statement. + /// The block containing all statements. + /// The declared statement syntax. + public StatementSyntax If(IArgument leftArgument, IPattern pattern, BlockSyntax block) + { + if (leftArgument == null) + { + throw new ArgumentNullException(nameof(leftArgument)); + } + + if (pattern == null) + { + throw new ArgumentNullException(nameof(pattern)); + } + + return + IfStatement( + IsPatternExpression( + leftArgument.GetArgumentSyntax().Expression, + pattern.GetPatternSyntax()), + block); + } + /// /// Create the statement syntax for a if-conditional. /// @@ -83,7 +139,23 @@ public StatementSyntax If(IBinaryExpression binaryExpression, BlockSyntax block) throw new ArgumentNullException(nameof(binaryExpression)); } - return IfStatement(binaryExpression.GetBinaryExpression(), block); + return IfStatement(binaryExpression.GetBinaryExpression(), block); + } + + /// + /// Create the statement syntax for a if-conditional with pattern. + /// + /// The pattern expression to generate. + /// The block containing all statements. + /// The declared statement syntax. + public StatementSyntax If(IPatternExpression patternExpression, BlockSyntax block) + { + if (patternExpression == null) + { + throw new ArgumentNullException(nameof(patternExpression)); + } + + return IfStatement(patternExpression.GetPatternExpression(), block); } /// @@ -101,5 +173,21 @@ public StatementSyntax If(IBinaryExpression binaryExpression, ExpressionStatemen return IfStatement(binaryExpression.GetBinaryExpression(), expressionStatement); } + + /// + /// Create the statement syntax for a if-conditional with a single statement and a pattern expression. + /// + /// The pattern expression to generate. + /// Statement inside the if. + /// The declared statement syntax. + public StatementSyntax If(IPatternExpression patternExpression, ExpressionStatementSyntax expressionStatement) + { + if (patternExpression == null) + { + throw new ArgumentNullException(nameof(patternExpression)); + } + + return IfStatement(patternExpression.GetPatternExpression(), expressionStatement); + } } }