Skip to content

Commit 4404f2a

Browse files
RexJaeschkeBillWagner
authored andcommitted
Add support for pattern additions
Add support for pattern additions fix md and example update headings
1 parent 1477dfc commit 4404f2a

File tree

2 files changed

+167
-10
lines changed

2 files changed

+167
-10
lines changed

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

@@ -406,6 +410,157 @@ It is a compile-time error to use a discard pattern in a *relational_expression*
406410
> Here, a discard pattern is used to handle `null` and any integer value that doesnt have the corresponding member of the `DayOfWeek` enumeration. That guarantees that the `switch` expression handles all possible input values.
407411
> *end example*
408412
413+
### §type-pattern-new-clause Type pattern
414+
415+
A *type_pattern* is used to test that the pattern input value ([§11.1](patterns.md#111-general)) has a given type.
416+
417+
```ANTLR
418+
type_pattern
419+
: type
420+
;
421+
```
422+
423+
The runtime type of the value is tested against *type* using the same rules specified in the is-type operator ([§12.12.12.1](expressions.md#1212121-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.
424+
425+
### §relational-pattern-new-clause Relational pattern
426+
427+
A *relational_pattern* is used to relationally test the pattern input value ([§11.1](patterns.md#111-general)) against a constant value.
428+
429+
```ANTLR
430+
relational_pattern
431+
: '<' constant_expression
432+
| '<=' constant_expression
433+
| '>' constant_expression
434+
| '>=' constant_expression
435+
;
436+
```
437+
438+
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.
439+
440+
It is a compile-time error if `constant_expression`is `double.NaN`, `float.NaN`, or `null_literal`.
441+
442+
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`.
443+
444+
> *Example*:
445+
>
446+
> <!-- Example: {template:"standalone-console", name:"RelationalPattern1", inferOutput:true} -->
447+
> ```csharp
448+
> Console.WriteLine(Classify(13));
449+
> Console.WriteLine(Classify(double.NaN));
450+
> Console.WriteLine(Classify(2.4));
451+
>
452+
> static string Classify(double measurement) => measurement switch
453+
> {
454+
> < -4.0 => "Too low",
455+
> > 10.0 => "Too high",
456+
> double.NaN => "Unknown",
457+
> _ => "Acceptable",
458+
> };
459+
> ```
460+
>
461+
> The output produced is
462+
>
463+
> ```console
464+
> Too high
465+
> Unknown
466+
> Acceptable
467+
> ```
468+
>
469+
> *end example*
470+
471+
### §logical-pattern-new-clause Logical pattern
472+
473+
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.
474+
475+
```ANTLR
476+
logical_pattern
477+
: disjunctive_pattern
478+
;
479+
480+
disjunctive_pattern
481+
: disjunctive_pattern 'or' conjunctive_pattern
482+
| conjunctive_pattern
483+
;
484+
485+
conjunctive_pattern
486+
: conjunctive_pattern 'and' negated_pattern
487+
| negated_pattern
488+
;
489+
490+
negated_pattern
491+
: 'not' negated_pattern
492+
| pattern
493+
;
494+
```
495+
496+
`not`, `and`, and `or` are collectively called ***pattern operators***.
497+
498+
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.
499+
500+
> *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*
501+
502+
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.
503+
504+
> *Example*:
505+
>
506+
> <!-- Example: {template:"standalone-console", name:"LogicalPattern1", inferOutput:true} -->
507+
> ```csharp
508+
> Console.WriteLine(Classify(13));
509+
> Console.WriteLine(Classify(-100));
510+
> Console.WriteLine(Classify(5.7));
511+
>
512+
> static string Classify(double measurement) => measurement switch
513+
> {
514+
> < -40.0 => "Too low",
515+
> >= -40.0 and < 0 => "Low",
516+
> >= 0 and < 10.0 => "Acceptable",
517+
> >= 10.0 and < 20.0 => "High",
518+
> >= 20.0 => "Too high",
519+
> double.NaN => "Unknown",
520+
> };
521+
> ```
522+
>
523+
> The output produced is
524+
>
525+
> ```console
526+
> High
527+
> Too low
528+
> Acceptable
529+
> ```
530+
>
531+
> *end example*
532+
<!-- markdownlint-disable MD028 -->
533+
534+
<!-- markdownlint-enable MD028 -->
535+
> *Example*:
536+
>
537+
> <!-- Example: {template:"standalone-console", name:"LogicalPattern2", inferOutput:true} -->
538+
> ```csharp
539+
> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));
540+
> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));
541+
> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));
542+
>
543+
> static string GetCalendarSeason(DateTime date) => date.Month switch
544+
> {
545+
> 3 or 4 or 5 => "spring",
546+
> 6 or 7 or 8 => "summer",
547+
> 9 or 10 or 11 => "autumn",
548+
> 12 or 1 or 2 => "winter",
549+
> _ => throw new ArgumentOutOfRangeException(nameof(date),
550+
> $"Date with unexpected month: {date.Month}."),
551+
> };
552+
> ```
553+
>
554+
> The output produced is
555+
>
556+
> ```console
557+
> winter
558+
> autumn
559+
> spring
560+
> ```
561+
>
562+
> *end example*
563+
409564
## 11.3 Pattern subsumption
410565
411566
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)