Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

comment for Pattern #991

Merged
merged 6 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ private void ConvertExpression(SemanticModel model, ExpressionSyntax syntax, Syn
ConvertTupleExpression(model, expression);
break;
default:
//Example: typeof(Transaction);
throw new CompilationException(syntax, DiagnosticId.SyntaxNotSupported, $"Unsupported syntax: {syntax}");
}
}
Expand Down
77 changes: 77 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Pattern/BinaryPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet a binary pattern to OpCodes.
/// </summary>
/// <param name="model">The semantic model providing context and information about binary pattern.</param>
/// <param name="pattern">The binary pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <see cref="ConvertAndPattern(SemanticModel, PatternSyntax, PatternSyntax, byte)"/>
/// <see cref="ConvertOrPattern(SemanticModel, PatternSyntax, PatternSyntax, byte)"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

examples

private void ConvertBinaryPattern(SemanticModel model, BinaryPatternSyntax pattern, byte localIndex)
{
switch (pattern.OperatorToken.ValueText)
Expand All @@ -33,29 +41,98 @@ private void ConvertBinaryPattern(SemanticModel model, BinaryPatternSyntax patte
}
}

/// <summary>
/// Convet a "and" pattern to OpCodes.
/// </summary>
/// <remarks>
/// Conjunctive "and" pattern that matches an expression when both patterns match the expression.
/// </remarks>
/// <param name="model">The semantic model providing context and information about "and" pattern.</param>
/// <param name="left">The left pattern to be converted.</param>
/// <param name="right">The right pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <example>
/// The following example shows how you can combine relational patterns to check if a value is in a certain range:
/// <code>
/// public static string Classify(int measurement) => measurement switch
/// {
/// < -40 => "Too low",
/// >= -40 and < 0 => "Low",
/// >= 0 and < 10 => "Acceptable",
/// >= 10 and < 20 => "High",
/// >= 20 => "Too high"
/// };
/// </code>
/// </example>
private void ConvertAndPattern(SemanticModel model, PatternSyntax left, PatternSyntax right, byte localIndex)
{
// Define jump targets for the right pattern and the end of the conversion process
JumpTarget rightTarget = new();
JumpTarget endTarget = new();

// Convert the left pattern
ConvertPattern(model, left, localIndex);

// Jump to the right pattern if the left pattern matches
Jump(OpCode.JMPIF_L, rightTarget);

// Push 'false' onto the evaluation stack and jump to the end if the left pattern does not match
Push(false);
Jump(OpCode.JMP_L, endTarget);

// Define an instruction for the right pattern and convert it
rightTarget.Instruction = AddInstruction(OpCode.NOP);
ConvertPattern(model, right, localIndex);

// Define an instruction for the end of the conversion process
endTarget.Instruction = AddInstruction(OpCode.NOP);
}

/// <summary>
/// Convet a "or" pattern to OpCodes.
/// </summary>
/// <remarks>
/// Disjunctive "or" pattern that matches an expression when either pattern matches the expression.
/// </remarks>
/// <param name="model">The semantic model providing context and information about "or" pattern.</param>
/// <param name="left">The left pattern to be converted.</param>
/// <param name="right">The right pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <example>
/// As the following example shows:
/// <code>
/// public static string GetCalendarSeason(int month) => month switch
/// {
/// 3 or 4 or 5 => "spring",
/// 6 or 7 or 8 => "summer",
/// 9 or 10 or 11 => "autumn",
/// 12 or 1 or 2 => "winter",
/// _ => throw new Exception($"Unexpected month: {month}."),
/// };
/// </code>
/// As the preceding example shows, you can repeatedly use the pattern combinators in a pattern.
/// </example>
private void ConvertOrPattern(SemanticModel model, PatternSyntax left, PatternSyntax right, byte localIndex)
{
// Define jump targets for the right pattern and the end of the conversion process
JumpTarget rightTarget = new();
JumpTarget endTarget = new();

// Convert the left pattern
ConvertPattern(model, left, localIndex);

// Jump to the right pattern if the left pattern does not match
Jump(OpCode.JMPIFNOT_L, rightTarget);

// Push 'true' onto the evaluation stack and jump to the end if the left pattern matches
Push(true);
Jump(OpCode.JMP_L, endTarget);

// Define an instruction for the right pattern and convert it
rightTarget.Instruction = AddInstruction(OpCode.NOP);
ConvertPattern(model, right, localIndex);

// Define an instruction for the end of the conversion process
endTarget.Instruction = AddInstruction(OpCode.NOP);
}
}
24 changes: 24 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Pattern/ConstantPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet a constant pattern to OpCodes.
/// </summary>
/// <param name="model">The semantic model providing context and information about constant pattern.</param>
/// <param name="pattern">The constant pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <remarks>
/// You use a constant pattern to test if an expression result equals a specified constant.
/// In a constant pattern, you can use any constant expression, such as:
/// integer, char, string, Boolean value true or false, enum value, the name of a declared const field or local, null
/// </remarks>
/// <example>
/// <code>
/// public static int GetGroupTicketPrice(int visitorCount) => visitorCount switch
/// {
/// 1 => 12,
/// 2 => 20,
/// 3 => 27,
/// 4 => 32,
/// 0 => 0,
/// _ => throw new Exception($"Not supported number of visitors: {visitorCount}"),
/// };
/// </code>
/// </example>
private void ConvertConstantPattern(SemanticModel model, ConstantPatternSyntax pattern, byte localIndex)
{
AccessSlot(OpCode.LDLOC, localIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet declaration pattern to OpCodes.
/// </summary>
/// <param name="model">The semantic model providing context and information about declaration pattern.</param>
/// <param name="pattern">The declaration pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <example>
/// With a declaration pattern, you can also declare a new local variable.
/// When a declaration pattern matches an expression, that variable is assigned a converted expression result,
/// as the following example shows:
/// <code>
/// object greeting = "Hello, World!";
/// if (greeting is string message)
/// {
/// Runtime.Log(message);
/// }
/// object greeting2 = "Hello, World!";
/// if (greeting2 is string _)
/// {
/// Runtime.Log("greeting2 is string");
/// }
/// </code>
/// <c>string message</c> is DiscardDesignationSyntax, <c>string _</c> is SingleVariableDesignationSyntax.
/// </example>
private void ConvertDeclarationPattern(SemanticModel model, DeclarationPatternSyntax pattern, byte localIndex)
{
ITypeSymbol type = model.GetTypeInfo(pattern.Type).Type!;
Expand Down
18 changes: 18 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Pattern/NotPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet a "not" pattern to OpCodes.
/// </summary>
/// <remarks>
/// Negation "not" pattern that matches an expression when the negated pattern doesn't match the expression.
/// </remarks>
/// <param name="model">The semantic model providing context and information about "not" pattern.</param>
/// <param name="pattern">The "not" pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <example>
/// The following example shows how you can negate a constant null pattern to check if an expression is non-null:
/// <code>
/// if (input is not null)
/// {
/// // ...
/// }
/// </code>
/// </example>
private void ConvertNotPattern(SemanticModel model, UnaryPatternSyntax pattern, byte localIndex)
{
ConvertPattern(model, pattern.Pattern, localIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet a parenthesized pattern to OpCodes.
/// </summary>
/// <param name="model">The semantic model providing context and information about parenthesized pattern.</param>
/// <param name="pattern">The parenthesized pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <remarks>
/// You can put parentheses around any pattern.
/// Typically, you do that to emphasize or change the precedence in logical patterns,
/// as the following example shows:
/// </remarks>
/// <example>
/// <c>return value is (> 1 and < 100);</c>
/// </example>
private void ConvertParenthesizedPatternSyntax(SemanticModel model, ParenthesizedPatternSyntax pattern, byte localIndex)
{
ConvertPattern(model, pattern.Pattern, localIndex);
Expand Down
42 changes: 42 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Pattern/Pattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,77 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convert pattern to OpCodes.
/// </summary>
/// <param name="model"></param>
/// <param name="pattern"></param>
/// <param name="localIndex"></param>
/// <exception cref="CompilationException"></exception>
/// <seealso href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#logical-patterns">
/// Pattern matching - the is and switch expressions, and operators and, or and not in patterns
/// </seealso>
private void ConvertPattern(SemanticModel model, PatternSyntax pattern, byte localIndex)
{
switch (pattern)
{
//Convet "and" / "or" pattern to OpCodes.
//Example: return value is > 1 and < 100;
//Example: return value is >= 80 or <= 20;
case BinaryPatternSyntax binaryPattern:
ConvertBinaryPattern(model, binaryPattern, localIndex);
break;
//Convet constant pattern to OpCodes.
//Example: return value is > 1;
//Example: return value is null;
case ConstantPatternSyntax constantPattern:
ConvertConstantPattern(model, constantPattern, localIndex);
break;
//Convet declaration pattern to OpCodes.
//Example: if (greeting is string message)
case DeclarationPatternSyntax declarationPattern:
ConvertDeclarationPattern(model, declarationPattern, localIndex);
break;
//Convet discard pattern (_) to OpCodes.
//Example: if (greeting2 is string _)
case DiscardPatternSyntax:
Push(true);
break;
//Convet relational pattern to OpCodes.
//Example: return value is > 1;
case RelationalPatternSyntax relationalPattern:
ConvertRelationalPattern(model, relationalPattern, localIndex);
break;
//Convert type pattern to OpCodes.
//Example:
//switch (o1)
//{
// case byte[]: break;
// case string: break;
//}
case TypePatternSyntax typePattern:
ConvertTypePattern(model, typePattern, localIndex);
break;
//Convet "not" pattern to OpCodes.
//Example: return value is not null;
case UnaryPatternSyntax unaryPattern when unaryPattern.OperatorToken.ValueText == "not":
ConvertNotPattern(model, unaryPattern, localIndex);
break;
//Convet parenthesized to OpCodes.
//Example: return value is (> 1 and < 100);
case ParenthesizedPatternSyntax parenthesizedPattern:
ConvertParenthesizedPatternSyntax(model, parenthesizedPattern, localIndex);
break;
default:
//Example:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default?

//object greeting = "Hello, World!";
//if (greeting3 is var message) { }
//Example:
//public static void M(object o1, object o2)
//{
// var t = (o1, o2);
// if (t is (int, string)) { }
//}
throw new CompilationException(pattern, DiagnosticId.SyntaxNotSupported, $"Unsupported pattern: {pattern}");
}
}
Expand Down
26 changes: 26 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Pattern/RelationalPattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,32 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet relational pattern to OpCodes.
/// </summary>
/// <param name="model">The semantic model providing context and information about convert pattern.</param>
/// <param name="pattern">The convert pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <exception cref="CompilationException"></exception>
/// <remarks>
/// In a relational pattern, you can use any of the relational operators <![CDATA[<, >, <=, or >=]]>.
/// The right-hand part of a relational pattern must be a constant expression.
/// The constant expression can be of an integer, char, or enum type.
/// To check if an expression result is in a certain range, match it against a <see cref="ConvertBinaryPattern(SemanticModel, BinaryPatternSyntax, byte)">conjunctive and pattern</see>.
/// </remarks>
/// <example>
/// You use a relational pattern to compare an expression result with a constant,
/// as the following example shows:
/// <code>
/// int a = 1;
/// var b = a switch
/// {
/// > 1 => true,
/// <= 1 => false
/// };
/// </code>
/// <c>> 1</c> and <c><= 1</c> is RelationalPatternSyntax;
/// </example>
private void ConvertRelationalPattern(SemanticModel model, RelationalPatternSyntax pattern, byte localIndex)
{
AccessSlot(OpCode.LDLOC, localIndex);
Expand Down
24 changes: 24 additions & 0 deletions src/Neo.Compiler.CSharp/MethodConvert/Pattern/TypePattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ namespace Neo.Compiler;

partial class MethodConvert
{
/// <summary>
/// Convet type pattern to OpCodes.
/// </summary>
/// <param name="model">The semantic model providing context and information about type pattern.</param>
/// <param name="pattern">The type pattern to be converted.</param>
/// <param name="localIndex">The index of the local variable.</param>
/// <example>
/// <code>
/// public void M(object o1)
/// {
/// switch (o1)
/// {
/// case byte[]: break;
/// case string: break;
/// }
/// }
/// </code>
/// <c>byte[]</c> and <c>string</c> is TypePatternSyntax.
/// </example>
/// <remarks>
/// Only few type judgments are supported, such as: bool, byte[], string,
/// Not supported ByteString, BigInteger.
/// <see cref="Helper.GetPatternType(ITypeSymbol)"/>
/// </remarks>
private void ConvertTypePattern(SemanticModel model, TypePatternSyntax pattern, byte localIndex)
{
ITypeSymbol type = model.GetTypeInfo(pattern.Type).Type!;
Expand Down
Loading
Loading