Skip to content

Commit 9556bb1

Browse files
RexJaeschkeBillWagner
authored andcommitted
Add support for pattern additions
Add support for pattern additions fix md and example update headings fix link warnings
1 parent d68b04d commit 9556bb1

2 files changed

Lines changed: 167 additions & 10 deletions

File tree

standard/lexical-structure.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ These productions occur in contexts where a value can occur in an expression, an
7878
7979
If a sequence of tokens can be parsed, in context, as one of the disambiguated productions including an optional *type_argument_list* ([§8.4.2](types.md#842-type-arguments)), then the token immediately following the closing `>token shall be examined and if it is:
8080
81-
- one of `( ) ] } : ; , . ? == != | ^ && || & [`; or
81+
- one of `( ) ] } : ; , . ? == != | ^ && || & [ =>`; or
8282
- one of the relational operators `< <= >= is as`; or
8383
- a contextual query keyword appearing inside a query expression.
84+
- In certain contexts, *identifier* is treated as a disambiguating token. Those contexts are where the sequence of tokens being disambiguated is immediately preceded by one of the keywords `is`, `case` or `out`, or arises while parsing the first element of a tuple literal (in which case the tokens are preceded by `(` or `:` and the identifier is followed by a `,`) or a subsequent element of a tuple literal.
8485
8586
then the *type_argument_list* shall be retained as part of the disambiguated production and any other possible parse of the sequence of tokens discarded. Otherwise, the tokens parsed as a *type_argument_list* shall not be considered to be part of the disambiguated production, even if there is no other possible parse of those tokens.
8687
@@ -606,12 +607,13 @@ A ***contextual keyword*** is an identifier-like sequence of characters that has
606607

607608
```ANTLR
608609
contextual_keyword
609-
: 'add' | 'alias' | 'ascending' | 'async' | 'await'
610-
| 'by' | 'descending' | 'dynamic' | 'equals' | 'from'
611-
| 'get' | 'global' | 'group' | 'into' | 'join'
612-
| 'let' | 'nameof' | 'notnull' | 'on' | 'orderby'
613-
| 'partial' | 'remove' | 'select' | 'set' | 'unmanaged'
614-
| 'value' | 'var' | 'when' | 'where' | 'yield'
610+
: 'add' | 'alias' | 'and' | 'ascending' | 'async'
611+
| 'await' | 'by' | 'descending' | 'dynamic' | 'equals'
612+
| 'from' | 'get' | 'global' | 'group' | 'into'
613+
| 'join' | 'let' | 'nameof' | 'not' | 'notnull'
614+
| 'on' | 'or' | 'orderby' | 'partial' | 'remove'
615+
| 'select' | 'set' | 'unmanaged' | 'value' | 'var'
616+
| 'when' | 'where' | 'yield'
615617
;
616618
```
617619

standard/patterns.md

Lines changed: 158 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 11.1 General
44

5-
A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.14.12](expressions.md#121412-the-is-operator)), in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)), and in a *switch_expression* ([§12.11](expressions.md#1211-switch-expression)) to express the shape of data against which incoming data is to be compared. Patterns may be recursive, so that parts of the data may be matched against ***sub-patterns***.
5+
A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.14.12](expressions.md#121412-the-is-operator)), in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)), and in a *switch_expression* ([§12.11](expressions.md#1211-switch-expression)) to express the shape of data against which incoming data is to be compared. Patterns may be recursive, so that parts of the data may be matched against ***sub-patterns***.
66

77
A pattern is tested against a value in a number of contexts:
88

@@ -11,7 +11,7 @@ A pattern is tested against a value in a number of contexts:
1111
- In a switch expression, the *pattern* of a *switch_expression_arm* is tested against the expression on the switch-expression’s left-hand-side.
1212
- In nested contexts, the *sub-pattern* is tested against values retrieved from properties, fields, or indexed from other input values, depending on the pattern form.
1313

14-
The value against which a pattern is tested is called the ***pattern input value***.
14+
The value against which a pattern is tested is called the ***pattern input value***. Patterns may be combined using Boolean logic.
1515

1616
## 11.2 Pattern forms
1717

@@ -21,12 +21,16 @@ A pattern may have one of the following forms:
2121

2222
```ANTLR
2323
pattern
24-
: declaration_pattern
24+
: '(' pattern ')'
25+
| declaration_pattern
2526
| constant_pattern
2627
| var_pattern
2728
| positional_pattern
2829
| property_pattern
2930
| discard_pattern
31+
| type_pattern
32+
| relational_pattern
33+
| logical_pattern
3034
;
3135
```
3236

@@ -407,6 +411,157 @@ It is a compile-time error to use a discard pattern in a *relational_expression*
407411
> Here, a discard pattern is used to handle `null` and any integer value that does not have the corresponding member of the `DayOfWeek` enumeration. That guarantees that the `switch` expression handles all possible input values.
408412
> *end example*
409413
414+
### §type-pattern-new-clause Type pattern
415+
416+
A *type_pattern* is used to test that the pattern input value ([§11.1](patterns.md#111-general)) has a given type.
417+
418+
```ANTLR
419+
type_pattern
420+
: type
421+
;
422+
```
423+
424+
The runtime type of the value is tested against *type* using the same rules specified in the is-type operator ([§12.14.12.1](expressions.md#1214121-the-is-type-operator)). If the test succeeds, the pattern matches that value. It is a compile-time error if the *type* is a nullable type. This pattern form never matches a `null` value.
425+
426+
### §relational-pattern-new-clause Relational pattern
427+
428+
A *relational_pattern* is used to relationally test the pattern input value ([§11.1](patterns.md#111-general)) against a constant value.
429+
430+
```ANTLR
431+
relational_pattern
432+
: '<' constant_expression
433+
| '<=' constant_expression
434+
| '>' constant_expression
435+
| '>=' constant_expression
436+
;
437+
```
438+
439+
Relational patterns support the relational operators `<`, `<=`, `>`, and `>=` on all of the built-in types that support such binary relational operators with both operands having the same type: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `nint`, `nuint`, and enums.
440+
441+
It is a compile-time error if `constant_expression`is `double.NaN`, `float.NaN`, or `null_literal`.
442+
443+
When the input value has a type for which a suitable built-in binary relational operator is defined, the evaluation of that operator is taken as the meaning of the relational pattern. Otherwise, the input value is converted to the type of `constant_expression` using an explicit nullable or unboxing conversion. It is a compile-time error if no such conversion exists. The pattern is considered to not match if the conversion fails. If the conversion succeeds, the result of the pattern-matching operation is the result of evaluating the expression `e «op» v` where `e` is the converted input, «op» is the relational operator, and `v` is the `constant_expression`.
444+
445+
> *Example*:
446+
>
447+
> <!-- Example: {template:"standalone-console", name:"RelationalPattern1", inferOutput:true} -->
448+
> ```csharp
449+
> Console.WriteLine(Classify(13));
450+
> Console.WriteLine(Classify(double.NaN));
451+
> Console.WriteLine(Classify(2.4));
452+
>
453+
> static string Classify(double measurement) => measurement switch
454+
> {
455+
> < -4.0 => "Too low",
456+
> > 10.0 => "Too high",
457+
> double.NaN => "Unknown",
458+
> _ => "Acceptable",
459+
> };
460+
> ```
461+
>
462+
> The output produced is
463+
>
464+
> ```console
465+
> Too high
466+
> Unknown
467+
> Acceptable
468+
> ```
469+
>
470+
> *end example*
471+
472+
### §logical-pattern-new-clause Logical pattern
473+
474+
A *logical_pattern* is used to negate a pattern input value ([§11.1](patterns.md#111-general)) or to combine that value with a pattern using a Boolean operator.
475+
476+
```ANTLR
477+
logical_pattern
478+
: disjunctive_pattern
479+
;
480+
481+
disjunctive_pattern
482+
: disjunctive_pattern 'or' conjunctive_pattern
483+
| conjunctive_pattern
484+
;
485+
486+
conjunctive_pattern
487+
: conjunctive_pattern 'and' negated_pattern
488+
| negated_pattern
489+
;
490+
491+
negated_pattern
492+
: 'not' negated_pattern
493+
| pattern
494+
;
495+
```
496+
497+
`not`, `and`, and `or` are collectively called ***pattern operators***.
498+
499+
A *negated_pattern* matches if the pattern being negated does not match, and vice versa. A *conjunctive_pattern* requires both patterns to match. A *disjunctive_pattern* requires either pattern to match. Unlike their language operator counterparts, `&&` and `||`, `and` and `or` are *not* short-circuiting operators.
500+
501+
> *Note*: As indicated by the grammar, `not` has precedence over `and`, which has precedence over `or`. This can be explicitly indicated or overridden by using parentheses. *end note*
502+
503+
When a *pattern* is used with `is`, any pattern operators in that *pattern* have higher precedence than their logical operator counterparts. Otherwise, those pattern operators have lower precedence.
504+
505+
> *Example*:
506+
>
507+
> <!-- Example: {template:"standalone-console", name:"LogicalPattern1", inferOutput:true} -->
508+
> ```csharp
509+
> Console.WriteLine(Classify(13));
510+
> Console.WriteLine(Classify(-100));
511+
> Console.WriteLine(Classify(5.7));
512+
>
513+
> static string Classify(double measurement) => measurement switch
514+
> {
515+
> < -40.0 => "Too low",
516+
> >= -40.0 and < 0 => "Low",
517+
> >= 0 and < 10.0 => "Acceptable",
518+
> >= 10.0 and < 20.0 => "High",
519+
> >= 20.0 => "Too high",
520+
> double.NaN => "Unknown",
521+
> };
522+
> ```
523+
>
524+
> The output produced is
525+
>
526+
> ```console
527+
> High
528+
> Too low
529+
> Acceptable
530+
> ```
531+
>
532+
> *end example*
533+
<!-- markdownlint-disable MD028 -->
534+
535+
<!-- markdownlint-enable MD028 -->
536+
> *Example*:
537+
>
538+
> <!-- Example: {template:"standalone-console", name:"LogicalPattern2", inferOutput:true} -->
539+
> ```csharp
540+
> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));
541+
> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));
542+
> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));
543+
>
544+
> static string GetCalendarSeason(DateTime date) => date.Month switch
545+
> {
546+
> 3 or 4 or 5 => "spring",
547+
> 6 or 7 or 8 => "summer",
548+
> 9 or 10 or 11 => "autumn",
549+
> 12 or 1 or 2 => "winter",
550+
> _ => throw new ArgumentOutOfRangeException(nameof(date),
551+
> $"Date with unexpected month: {date.Month}."),
552+
> };
553+
> ```
554+
>
555+
> The output produced is
556+
>
557+
> ```console
558+
> winter
559+
> autumn
560+
> spring
561+
> ```
562+
>
563+
> *end example*
564+
410565
## 11.3 Pattern subsumption
411566
412567
In a switch statement, it is an error if a cases pattern is *subsumed* by the preceding set of unguarded cases ([§13.8.3](statements.md#1383-the-switch-statement)).

0 commit comments

Comments
 (0)