Skip to content

Commit

Permalink
comment for Pattern (#991)
Browse files Browse the repository at this point in the history
* update

* update

* DeclarationPatternSyntax

* update

* Update Contract_Pattern.cs

* Apply suggestions from code review

---------

Co-authored-by: Shargon <shargon@gmail.com>
  • Loading branch information
chenzhitong and shargon committed Mar 14, 2024
1 parent 4661dd2 commit 31bedde
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 1 deletion.
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)"/>
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:
//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

0 comments on commit 31bedde

Please sign in to comment.