From e451960683ab8167dd409c41c0211597ebc173a5 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Sat, 12 Aug 2023 16:07:11 +0200 Subject: [PATCH 01/14] Patterns C# 7.0 - 11.0 --- grammars/csharp.tmLanguage | 868 +++++++++++++++++++++----------- grammars/csharp.tmLanguage.cson | 586 +++++++++++++-------- src/csharp.tmLanguage.yml | 424 +++++++++------- test/expressions.tests.ts | 59 ++- test/utils/tokenize.ts | 3 +- 5 files changed, 1206 insertions(+), 734 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 9870517..d9800d3 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -289,10 +289,6 @@ include #else-part - - include - #switch-statement - include #goto-statement @@ -421,7 +417,7 @@ include - #switch-expression + #switch-statement-or-expression include @@ -2963,7 +2959,7 @@ end - (?<=\})|(?=;) + (?<=})|(?=;) patterns @@ -3014,7 +3010,7 @@ end - (?<=\})|(?=;) + (?<=})|(?=;) patterns @@ -3023,10 +3019,10 @@ - switch-statement + switch-statement-or-expression begin - (?<!\.)\b(switch)\b\s*(?=\() + (?<!\.)\b(switch)\b beginCaptures 1 @@ -3036,12 +3032,48 @@ end - (?<=\}) + (?<=})|(?=}) patterns include - #comment + #intrusive + + + begin + (?=\() + end + (?<=\})|(?=\}) + patterns + + + include + #switch-statement + + + + + begin + (?=\{) + end + (?<=\})|(?=\}) + patterns + + + include + #switch-expression + + + + + + switch-statement + + patterns + + + include + #intrusive begin @@ -3107,407 +3139,685 @@ - switch-expression + switch-label begin - (?x) (?<!\.)\b(switch)\b + \b(case|default)\b beginCaptures 1 name - keyword.control.switch.cs + keyword.control.$1.cs end - (?<=\}) + (:)|(?=}) endCaptures - 0 + 1 name - punctuation.curlybrace.close.cs + punctuation.separator.colon.cs patterns begin - \{ - beginCaptures - - 0 + (?!\s|\bwhen\b) + end + (?=\bwhen\b|:|}) + patterns + - name - punctuation.curlybrace.open.cs + include + #pattern - - end - \} - endCaptures + + + + begin + \b(when)\b + beginCaptures - 0 + 1 name - punctuation.curlybrace.close.cs + keyword.control.when.cs + end + (?=:|}) patterns include - #comment - - - include - #literal - - - include - #switch-var-pattern + #case-guard + + + + + switch-expression + + begin + \{ + beginCaptures + + 0 + + name + punctuation.curlybrace.open.cs + + + end + \} + endCaptures + + 0 + + name + punctuation.curlybrace.close.cs + + + patterns + + + begin + (?!\s|\bwhen\b|=>|,) + end + (?=\bwhen\b|=>|,|}) + patterns + include - #switch-property-expression + #pattern + + + + begin + \b(when)\b + beginCaptures + + 1 - include - #member-access-expression + name + keyword.control.when.cs + + end + (?==>|,|}) + patterns + include - #switch-pattern + #case-guard + + + + begin + => + beginCaptures + + 0 - include - #expression-body + name + keyword.operator.arrow.cs + + end + (?=,|}) + patterns + include - #punctuation-comma + #expression + + include + #punctuation-comma + - with-expression + case-guard + + patterns + + + include + #parenthesized-expression + + + include + #expression + + + + is-expression begin - (?<!\.)\b(with)\b\s*(?=\{|//|/\*|$) + (?<!\.)\b(is)\b beginCaptures 1 name - keyword.other.with.cs + keyword.other.is.cs end - (?<=\}) + (?=[)}\];:?=&|^]|!=) patterns include - #comment + #pattern + + + pattern + + patterns + include - #initializer-expression + #intrusive + + + include + #combinator-pattern + + + include + #discard-pattern + + + include + #constant-pattern + + + include + #relational-pattern + + + include + #var-pattern + + + include + #type-pattern + + + include + #positional-pattern + + + include + #property-pattern + + + include + #list-pattern + + + include + #slice-pattern + + + + combinator-pattern + + match + \b(and|or|not)\b + name + keyword.operator.word.$1.cs + + discard-pattern + + match + _(?![_[:alnum:]]) + name + variable.language.discard.cs + + constant-pattern + + patterns + + + include + #boolean-literal + + + include + #null-literal + + + include + #numeric-literal + + + include + #char-literal + + + include + #string-literal + + + include + #raw-string-literal + + + include + #verbatim-string-literal - switch-pattern + relational-pattern begin - (?x) # e.g. int x OR var x -(?<type_name> - (?: - (?: - (?:(?<identifier>@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification - (?<name_and_type_args> # identifier + type arguments (if any) - \g<identifier>\s* - (?<type_args>\s*<(?:[^<>]|\g<type_args>)+>\s*)? - ) - (?:\s*\.\s*\g<name_and_type_args>)* | # Are there any more names being dotted into? - (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) - ) - (?:\s*\?\s*)? # nullable suffix? - (?:\s* # array suffix? - \[ - (?:\s*,\s*)* # commata for multi-dimensional arrays - \] - \s* - (?:\?)? # arrays can be nullable reference types - \s* - )* - ) -)\s+ -(\g<identifier>)\b\s* + <=?|>=? + beginCaptures + + 0 + + name + keyword.operator.relational.cs + + + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + + include + #expression + + + + var-pattern + + begin + \b(var)\b beginCaptures 1 + name + keyword.other.var.cs + + + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + + include + #designation-pattern + + + + designation-pattern + + patterns + + + include + #intrusive + + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + patterns include - #type + #punctuation-comma + + + include + #designation-pattern - 2 + include + #simple-designation-pattern + + + + simple-designation-pattern + + patterns + + + include + #discard-pattern + + + match + @?[_[:alpha:]][_[:alnum:]]* name entity.name.variable.local.cs - + + + type-pattern + + begin + (?=@?[_[:alpha:]][_[:alnum:]]*) end - (?==>) + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) patterns - include - #comment + begin + \G + end + (?=\s|[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + + include + #type-builtin + + + include + #type-name + + + include + #type-arguments + + + include + #type-array-suffix + + + include + #type-nullable-suffix + + - include - #switch-when-clause + begin + (?=\s) + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + + include + #intrusive + + + include + #positional-pattern + + + include + #property-pattern + + + include + #simple-designation-pattern + + - switch-property-expression + positional-pattern begin - (?x) # e.g. int x OR var x -(?<type_name> - (?: - (?: - (?:(?<identifier>@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification - (?<name_and_type_args> # identifier + type arguments (if any) - \g<identifier>\s* - (?<type_args>\s*<(?:[^<>]|\g<type_args>)+>\s*)? - ) - (?:\s*\.\s*\g<name_and_type_args>)* | # Are there any more names being dotted into? - (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) - ) - (?:\s*\?\s*)? # nullable suffix? - (?:\s* # array suffix? - \[ - (?:\s*,\s*)* # commata for multi-dimensional arrays - \] - \s* - (?:\?)? # arrays can be nullable reference types - \s* - )* - ) -)?\s* -(\{) - beginCaptures - - 1 + (?=\() + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + begin + \( + beginCaptures + + 0 + + name + punctuation.parenthesis.open.cs + + + end + \) + endCaptures + + 0 + + name + punctuation.parenthesis.close.cs + + patterns include - #type + #subpattern + + + include + #punctuation-comma + + + + + begin + (?<=\)) + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + + include + #intrusive + + + include + #property-pattern + + + include + #simple-designation-pattern - 6 - - name - punctuation.curlybrace.open.cs - - + + + property-pattern + + begin + (?={) end - \} - endCaptures - - 0 - - name - punctuation.curlybrace.close.cs - - + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) patterns - match - (@?[_[:alpha:]][_[:alnum:]]*)\s*(:) - captures + begin + \{ + beginCaptures - 1 + 0 name - variable.other.object.property.cs + punctuation.curlybrace.open.cs - 2 + + end + \} + endCaptures + + 0 name - punctuation.separator.colon.cs + punctuation.curlybrace.close.cs - - - include - #expression - - - include - #punctuation-comma - - - - switch-var-pattern - - begin - (?x) # match foreach (var (x, y) in ...) -(?:\b(var)\b\s*) -(?<tuple>\((?:[^\(\)]|\g<tuple>)+\))\s* - beginCaptures - - 1 - - name - keyword.other.var.cs - - 2 - patterns include - #tuple-declaration-deconstruction-element-list + #subpattern + + + include + #punctuation-comma - - end - (?==>) - patterns - - - include - #comment - - include - #switch-when-clause + begin + (?<=\}) + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + + + include + #intrusive + + + include + #simple-designation-pattern + + - switch-when-clause + subpattern - begin - (?<!\.)\b(when)\b\s*(\()? - beginCaptures - - 1 - - name - keyword.control.try.when.cs - - 2 - - name - punctuation.parenthesis.open.cs - - - end - (?==>) patterns - - include - #comment - - - include - #expression - - - include - #punctuation-comma - match - \( + (@?[_[:alpha:]][_[:alnum:]]*(?:\s*\.\s*@?[_[:alpha:]][_[:alnum:]]*)*)\s*(:) captures - 0 + 1 - name - punctuation.parenthesis.open.cs + patterns + + + match + \@?[_[:alpha:]][_[:alnum:]]* + name + variable.other.object.property.cs + + + include + #punctuation-accessor + + - - - - match - \) - captures - - 0 + 2 name - punctuation.parenthesis.close.cs + punctuation.separator.colon.cs + + include + #pattern + - switch-label + list-pattern + begin + (?=\[) + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) patterns begin - (?<!\.)\b(case)\b\s+ + \[ beginCaptures - 1 + 0 name - keyword.control.case.cs + punctuation.squarebracket.open.cs end - : + \] endCaptures 0 name - punctuation.separator.colon.cs + punctuation.squarebracket.close.cs patterns include - #expression + #pattern + + + include + #punctuation-comma - match - (?<!\.)\b(default)\b\s*(:) - captures - - 1 + begin + (?<=\]) + end + (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + patterns + - name - keyword.control.default.cs + include + #intrusive - 2 - name - punctuation.separator.colon.cs + include + #simple-designation-pattern - + + slice-pattern + + match + \.\. + name + keyword.operator.range.cs + do-statement begin @@ -3963,7 +4273,7 @@ 1 name - keyword.control.try.when.cs + keyword.control.when.cs 2 @@ -5930,20 +6240,20 @@ - switch-literal + with-expression - name - constant.language.null.cs - match - (?<!\.)\bnull\b + begin + (?<!\.)\b(with)\b\s*(?=\{|//|/\*|$) beginCaptures 1 name - constant.language.null.cs + keyword.other.with.cs + end + (?<=\}) patterns @@ -5952,11 +6262,7 @@ include - #punctuation-comma - - - include - #expression-body + #initializer-expression @@ -6152,52 +6458,6 @@ - is-expression - - match - (?x) -(?<!\.)\b(is)\b\s* -(?<type_name> - (?: - (?: - (?:(?<identifier>@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification - (?<name_and_type_args> # identifier + type arguments (if any) - \g<identifier>\s* - (?<type_args>\s*<(?:[^<>]|\g<type_args>)+>\s*)? - ) - (?:\s*\.\s*\g<name_and_type_args>)* | # Are there any more names being dotted into? - (?<tuple>\s*\((?:[^\(\)]|\g<tuple>)+\)) - ) - (?:\s*\?\s*)? # nullable suffix? - (?:\s* # array suffix? - \[ - (?:\s*,\s*)* # commata for multi-dimensional arrays - \] - \s* - (?:\?)? # arrays can be nullable reference types - \s* - )* - ) -)? - captures - - 1 - - name - keyword.other.is.cs - - 2 - - patterns - - - include - #type - - - - - language-variable patterns @@ -7586,8 +7846,6 @@ type - name - meta.type.cs patterns @@ -7822,10 +8080,6 @@ patterns - - include - #comment - include #type @@ -7860,6 +8114,10 @@ patterns + + include + #intrusive + include #punctuation-comma @@ -7907,6 +8165,20 @@ match \. + intrusive + + patterns + + + include + #preprocessor + + + include + #comment + + + preprocessor name diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index b3085f4..85ded10 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -201,9 +201,6 @@ repository: { include: "#else-part" } - { - include: "#switch-statement" - } { include: "#goto-statement" } @@ -298,7 +295,7 @@ repository: include: "#language-variable" } { - include: "#switch-expression" + include: "#switch-statement-or-expression" } { include: "#with-expression" @@ -1852,7 +1849,7 @@ repository: beginCaptures: "1": name: "keyword.control.conditional.if.cs" - end: "(?<=\\})|(?=;)" + end: "(?<=})|(?=;)" patterns: [ { begin: "\\(" @@ -1878,21 +1875,45 @@ repository: beginCaptures: "1": name: "keyword.control.conditional.else.cs" - end: "(?<=\\})|(?=;)" + end: "(?<=})|(?=;)" patterns: [ { include: "#statement" } ] - "switch-statement": - begin: "(?|,)" + end: "(?=\\bwhen\\b|=>|,|})" + patterns: [ { - include: "#switch-pattern" + include: "#pattern" } + ] + } + { + begin: "\\b(when)\\b" + beginCaptures: + "1": + name: "keyword.control.when.cs" + end: "(?==>|,|})" + patterns: [ { - include: "#expression-body" + include: "#case-guard" } + ] + } + { + begin: "=>" + beginCaptures: + "0": + name: "keyword.operator.arrow.cs" + end: "(?=,|})" + patterns: [ { - include: "#punctuation-comma" + include: "#expression" } ] } + { + include: "#punctuation-comma" + } ] - "with-expression": - begin: "(? - (?: - (?: - (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification - (? # identifier + type arguments (if any) - \\g\\s* - (?\\s*<(?:[^<>]|\\g)+>\\s*)? - ) - (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into? - (?\\s*\\((?:[^\\(\\)]|\\g)+\\)) - ) - (?:\\s*\\?\\s*)? # nullable suffix? - (?:\\s* # array suffix? - \\[ - (?:\\s*,\\s*)* # commata for multi-dimensional arrays - \\] - \\s* - (?:\\?)? # arrays can be nullable reference types - \\s* - )* - ) - )\\s+ - (\\g)\\b\\s* - ''' + "is-expression": + begin: "(?)" + name: "keyword.other.is.cs" + end: "(?=[)}\\];:?=&|^]|!=)" patterns: [ { - include: "#comment" + include: "#pattern" + } + ] + pattern: + patterns: [ + { + include: "#intrusive" + } + { + include: "#combinator-pattern" + } + { + include: "#discard-pattern" } { - include: "#switch-when-clause" + include: "#constant-pattern" + } + { + include: "#relational-pattern" + } + { + include: "#var-pattern" + } + { + include: "#type-pattern" + } + { + include: "#positional-pattern" + } + { + include: "#property-pattern" + } + { + include: "#list-pattern" + } + { + include: "#slice-pattern" } ] - "switch-property-expression": - begin: ''' - (?x) # e.g. int x OR var x - (? - (?: - (?: - (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification - (? # identifier + type arguments (if any) - \\g\\s* - (?\\s*<(?:[^<>]|\\g)+>\\s*)? - ) - (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into? - (?\\s*\\((?:[^\\(\\)]|\\g)+\\)) - ) - (?:\\s*\\?\\s*)? # nullable suffix? - (?:\\s* # array suffix? - \\[ - (?:\\s*,\\s*)* # commata for multi-dimensional arrays - \\] - \\s* - (?:\\?)? # arrays can be nullable reference types - \\s* - )* - ) - )?\\s* - (\\{) - ''' - beginCaptures: - "1": - patterns: [ - { - include: "#type" - } - ] - "6": - name: "punctuation.curlybrace.open.cs" - end: "\\}" - endCaptures: - "0": - name: "punctuation.curlybrace.close.cs" + "combinator-pattern": + match: "\\b(and|or|not)\\b" + name: "keyword.operator.word.$1.cs" + "discard-pattern": + match: "_(?![_[:alnum:]])" + name: "variable.language.discard.cs" + "constant-pattern": patterns: [ { - match: "(@?[_[:alpha:]][_[:alnum:]]*)\\s*(:)" - captures: - "1": - name: "variable.other.object.property.cs" - "2": - name: "punctuation.separator.colon.cs" + include: "#boolean-literal" } { - include: "#expression" + include: "#null-literal" } { - include: "#punctuation-comma" + include: "#numeric-literal" + } + { + include: "#char-literal" + } + { + include: "#string-literal" + } + { + include: "#raw-string-literal" + } + { + include: "#verbatim-string-literal" } ] - "switch-var-pattern": - begin: ''' - (?x) # match foreach (var (x, y) in ...) - (?:\\b(var)\\b\\s*) - (?\\((?:[^\\(\\)]|\\g)+\\))\\s* - ''' + "relational-pattern": + begin: "<=?|>=?" + beginCaptures: + "0": + name: "keyword.operator.relational.cs" + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ + { + include: "#expression" + } + ] + "var-pattern": + begin: "\\b(var)\\b" beginCaptures: "1": name: "keyword.other.var.cs" - "2": + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ + { + include: "#designation-pattern" + } + ] + "designation-pattern": + patterns: [ + { + include: "#intrusive" + } + { + begin: "\\(" + beginCaptures: + "0": + name: "punctuation.parenthesis.open.cs" + end: "\\)" + endCaptures: + "0": + name: "punctuation.parenthesis.close.cs" patterns: [ { - include: "#tuple-declaration-deconstruction-element-list" + include: "#punctuation-comma" + } + { + include: "#designation-pattern" } ] - end: "(?==>)" - patterns: [ - { - include: "#comment" } { - include: "#switch-when-clause" + include: "#simple-designation-pattern" } ] - "switch-when-clause": - begin: "(?)" + "simple-designation-pattern": patterns: [ { - include: "#comment" + include: "#discard-pattern" } { - include: "#expression" + match: "@?[_[:alpha:]][_[:alnum:]]*" + name: "entity.name.variable.local.cs" } + ] + "type-pattern": + begin: "(?=@?[_[:alpha:]][_[:alnum:]]*)" + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ { - include: "#punctuation-comma" + begin: "\\G" + end: "(?=\\s|[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ + { + include: "#type-builtin" + } + { + include: "#type-name" + } + { + include: "#type-arguments" + } + { + include: "#type-array-suffix" + } + { + include: "#type-nullable-suffix" + } + ] } { - match: "\\(" - captures: - "0": - name: "punctuation.parenthesis.open.cs" + begin: "(?=\\s)" + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ + { + include: "#intrusive" + } + { + include: "#positional-pattern" + } + { + include: "#property-pattern" + } + { + include: "#simple-designation-pattern" + } + ] } + ] + "positional-pattern": + begin: "(?=\\()" + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ { - match: "\\)" - captures: + begin: "\\(" + beginCaptures: + "0": + name: "punctuation.parenthesis.open.cs" + end: "\\)" + endCaptures: "0": name: "punctuation.parenthesis.close.cs" + patterns: [ + { + include: "#subpattern" + } + { + include: "#punctuation-comma" + } + ] + } + { + begin: "(?<=\\))" + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ + { + include: "#intrusive" + } + { + include: "#property-pattern" + } + { + include: "#simple-designation-pattern" + } + ] } ] - "switch-label": + "property-pattern": + begin: "(?={)" + end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { - begin: "(? - (?: - (?: - (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification - (? # identifier + type arguments (if any) - \\g\\s* - (?\\s*<(?:[^<>]|\\g)+>\\s*)? - ) - (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into? - (?\\s*\\((?:[^\\(\\)]|\\g)+\\)) - ) - (?:\\s*\\?\\s*)? # nullable suffix? - (?:\\s* # array suffix? - \\[ - (?:\\s*,\\s*)* # commata for multi-dimensional arrays - \\] - \\s* - (?:\\?)? # arrays can be nullable reference types - \\s* - )* - ) - )? - ''' - captures: - "1": - name: "keyword.other.is.cs" - "2": - patterns: [ - { - include: "#type" - } - ] "language-variable": patterns: [ { @@ -4592,7 +4732,6 @@ repository: "7": name: "entity.name.variable.parameter.cs" type: - name: "meta.type.cs" patterns: [ { include: "#comment" @@ -4727,9 +4866,6 @@ repository: "0": name: "punctuation.definition.typeparameters.end.cs" patterns: [ - { - include: "#comment" - } { include: "#type" } @@ -4747,6 +4883,9 @@ repository: "0": name: "punctuation.squarebracket.close.cs" patterns: [ + { + include: "#intrusive" + } { include: "#punctuation-comma" } @@ -4768,6 +4907,15 @@ repository: "punctuation-accessor": name: "punctuation.accessor.cs" match: "\\." + intrusive: + patterns: [ + { + include: "#preprocessor" + } + { + include: "#comment" + } + ] preprocessor: name: "meta.preprocessor.cs" begin: "^\\s*(\\#)\\s*" diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index 67d4200..c70f344 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -86,7 +86,7 @@ repository: - include: '#foreach-statement' - include: '#if-statement' - include: '#else-part' - - include: '#switch-statement' + # - include: '#switch-statement' - include: '#goto-statement' - include: '#return-statement' - include: '#break-or-continue-statement' @@ -120,7 +120,7 @@ repository: - include: '#verbatim-interpolated-string' - include: '#type-builtin' - include: '#language-variable' - - include: '#switch-expression' + - include: '#switch-statement-or-expression' - include: '#with-expression' - include: '#conditional-operator' - include: '#expression-operators' @@ -1103,7 +1103,7 @@ repository: begin: (?|,) + end: (?=\bwhen\b|=>|,|}) + patterns: + - include: '#pattern' + - begin: \b(when)\b + beginCaptures: + 1: { name: keyword.control.when.cs } + end: (?==>|,|}) + patterns: + - include: '#case-guard' + - begin: => + beginCaptures: + 0: { name: keyword.operator.arrow.cs } + end: (?=,|}) + patterns: + # - include: '#ref-modifier' # not supported yet + - include: '#expression' + - include: '#punctuation-comma' - switch-pattern: - begin: |- - (?x) # e.g. int x OR var x - (? - (?: - (?: - (?:(?@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification - (? # identifier + type arguments (if any) - \g\s* - (?\s*<(?:[^<>]|\g)+>\s*)? - ) - (?:\s*\.\s*\g)* | # Are there any more names being dotted into? - (?\s*\((?:[^\(\)]|\g)+\)) - ) - (?:\s*\?\s*)? # nullable suffix? - (?:\s* # array suffix? - \[ - (?:\s*,\s*)* # commata for multi-dimensional arrays - \] - \s* - (?:\?)? # arrays can be nullable reference types - \s* - )* - ) - )\s+ - (\g)\b\s* + case-guard: + patterns: + # conditional_expression should not contain lambda_expression -> consume ( early + - include: '#parenthesized-expression' + - include: '#expression' + + is-expression: + begin: (?) + 1: { name: keyword.other.is.cs } + end: (?=[)}\];:?=&|^]|!=) patterns: - - include: '#comment' - - include: '#switch-when-clause' + - include: '#pattern' - switch-property-expression: - begin: |- - (?x) # e.g. int x OR var x - (? - (?: - (?: - (?:(?@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification - (? # identifier + type arguments (if any) - \g\s* - (?\s*<(?:[^<>]|\g)+>\s*)? - ) - (?:\s*\.\s*\g)* | # Are there any more names being dotted into? - (?\s*\((?:[^\(\)]|\g)+\)) - ) - (?:\s*\?\s*)? # nullable suffix? - (?:\s* # array suffix? - \[ - (?:\s*,\s*)* # commata for multi-dimensional arrays - \] - \s* - (?:\?)? # arrays can be nullable reference types - \s* - )* - ) - )?\s* - (\{) + pattern: + patterns: + - include: '#intrusive' + - include: '#combinator-pattern' + - include: '#discard-pattern' + - include: '#constant-pattern' + - include: '#relational-pattern' + - include: '#var-pattern' + - include: '#type-pattern' + - include: '#positional-pattern' + - include: '#property-pattern' + - include: '#list-pattern' + - include: '#slice-pattern' + + combinator-pattern: + match: \b(and|or|not)\b + name: keyword.operator.word.$1.cs + + discard-pattern: + match: _(?![_[:alnum:]]) + name: variable.language.discard.cs + + constant-pattern: + patterns: + - include: '#boolean-literal' + - include: '#null-literal' + - include: '#numeric-literal' + - include: '#char-literal' + - include: '#string-literal' + - include: '#raw-string-literal' + - include: '#verbatim-string-literal' + + relational-pattern: + begin: <=?|>=? beginCaptures: - '1': - patterns: - - include: '#type' - # '': { name: punctuation.curlybrace.open.cs } - '6': { name: punctuation.curlybrace.open.cs } - end: \} - endCaptures: - '0': { name: punctuation.curlybrace.close.cs } + 0: { name: keyword.operator.relational.cs } + end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) patterns: - - match: (@?[_[:alpha:]][_[:alnum:]]*)\s*(:) - captures: - '1': { name: variable.other.object.property.cs } - '2': { name: punctuation.separator.colon.cs } - include: '#expression' - - include: '#punctuation-comma' - switch-var-pattern: - begin: |- - (?x) # match foreach (var (x, y) in ...) - (?:\b(var)\b\s*) - (?\((?:[^\(\)]|\g)+\))\s* + var-pattern: + begin: \b(var)\b beginCaptures: - '1': { name: keyword.other.var.cs } - '2': - patterns: - - include: '#tuple-declaration-deconstruction-element-list' - end: (?==>) + 1: { name: keyword.other.var.cs } + end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) patterns: - - include: '#comment' - - include: '#switch-when-clause' + - include: '#designation-pattern' - switch-when-clause: - begin: (?) + designation-pattern: patterns: - - include: '#comment' - - include: '#expression' - - include: '#punctuation-comma' - - match: \( - captures: - '0': { name: punctuation.parenthesis.open.cs } - - match: \) - captures: - '0': { name: punctuation.parenthesis.close.cs } + - include: '#intrusive' + - begin: \( + beginCaptures: + 0: { name: punctuation.parenthesis.open.cs } + end: \) + endCaptures: + 0: { name: punctuation.parenthesis.close.cs } + patterns: + - include: '#punctuation-comma' + - include: '#designation-pattern' + - include: '#simple-designation-pattern' - switch-label: + simple-designation-pattern: + patterns: + - include: '#discard-pattern' + - match: '@?[_[:alpha:]][_[:alnum:]]*' + name: entity.name.variable.local.cs + + type-pattern: + begin: (?=@?[_[:alpha:]][_[:alnum:]]*) + end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) patterns: - - begin: (? - (?: - (?: - (?:(?@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification - (? # identifier + type arguments (if any) - \g\s* - (?\s*<(?:[^<>]|\g)+>\s*)? - ) - (?:\s*\.\s*\g)* | # Are there any more names being dotted into? - (?\s*\((?:[^\(\)]|\g)+\)) - ) - (?:\s*\?\s*)? # nullable suffix? - (?:\s* # array suffix? - \[ - (?:\s*,\s*)* # commata for multi-dimensional arrays - \] - \s* - (?:\?)? # arrays can be nullable reference types - \s* - )* - ) - )? - captures: - '1': { name: keyword.other.is.cs } - '2': - patterns: - - include: '#type' - language-variable: patterns: - name: variable.language.$1.cs @@ -3066,7 +3108,6 @@ repository: '7': { name: entity.name.variable.parameter.cs } type: - name: meta.type.cs patterns: - include: '#comment' - include: '#ref-modifier' @@ -3163,7 +3204,6 @@ repository: endCaptures: '0': { name: punctuation.definition.typeparameters.end.cs } patterns: - - include: '#comment' - include: '#type' - include: '#punctuation-comma' @@ -3175,6 +3215,7 @@ repository: endCaptures: '0': { name: punctuation.squarebracket.close.cs } patterns: + - include: '#intrusive' - include: '#punctuation-comma' type-nullable-suffix: @@ -3198,6 +3239,11 @@ repository: name: punctuation.accessor.cs match: \. + intrusive: + patterns: + - include: '#preprocessor' + - include: '#comment' + preprocessor: name: meta.preprocessor.cs begin: ^\s*(\#)\s* diff --git a/test/expressions.tests.ts b/test/expressions.tests.ts index 3751def..15b2d57 100644 --- a/test/expressions.tests.ts +++ b/test/expressions.tests.ts @@ -4144,6 +4144,7 @@ select x.Key1;`); Token.Punctuation.CloseParen, Token.Punctuation.CloseParen, Token.Punctuation.Comma, + Token.Variables.Discard, Token.Operators.Arrow, Token.Literals.Numeric.Decimal("3"), Token.Literals.Numeric.Other.Separator.Decimals, @@ -4187,9 +4188,9 @@ select x.Key1;`); Token.Variables.ReadWrite("colorBand"), Token.Keywords.Control.Switch, Token.Punctuation.OpenBrace, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Red"), + Token.Type("Red"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4204,9 +4205,9 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("00"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Orange"), + Token.Type("Orange"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4221,9 +4222,9 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("00"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Yellow"), + Token.Type("Yellow"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4238,9 +4239,9 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("00"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Green"), + Token.Type("Green"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4255,9 +4256,9 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("00"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Blue"), + Token.Type("Blue"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4272,9 +4273,9 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("FF"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Indigo"), + Token.Type("Indigo"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4289,9 +4290,9 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("82"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, - Token.Variables.Object("Rainbow"), + Token.Type("Rainbow"), Token.Punctuation.Accessor, - Token.Variables.Property("Violet"), + Token.Type("Violet"), Token.Operators.Arrow, Token.Keywords.New, Token.Type("RGBColor"), @@ -4306,6 +4307,7 @@ select x.Key1;`); Token.Literals.Numeric.Hexadecimal("D3"), Token.Punctuation.CloseParen, Token.Punctuation.Comma, + Token.Variables.Discard, Token.Operators.Arrow, Token.Keywords.Control.Throw, Token.Keywords.New, @@ -4342,7 +4344,7 @@ select x.Key1;`); };`); const tokens = await tokenize(input); tokens.should.deep.equal([ - { text: "public", type: "storage.modifier.cs" }, + Token.Keywords.Modifiers.Public, Token.Keywords.Modifiers.Static, Token.PrimitiveType.Decimal, Token.Identifiers.MethodName("ComputeSalesTax"), @@ -4405,6 +4407,7 @@ select x.Key1;`); Token.Comment.LeadingWhitespace(" "), Token.Comment.SingleLine.Start, Token.Comment.SingleLine.Text(" other cases removed for brevity..."), + Token.Variables.Discard, Token.Operators.Arrow, Token.Literals.Numeric.Decimal("0"), Token.Literals.Numeric.Other.Suffix("M"), @@ -4531,9 +4534,9 @@ select x.Key1;`); Token.Punctuation.String.End, Token.Punctuation.Comma, Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("_"), + Token.Variables.Discard, Token.Punctuation.Comma, - Token.Variables.ReadWrite("_"), + Token.Variables.Discard, Token.Punctuation.CloseParen, Token.Operators.Arrow, Token.Punctuation.String.Begin, @@ -4581,9 +4584,9 @@ select x.Key1;`); Token.Punctuation.Comma, Token.Keywords.Var, Token.Punctuation.OpenParen, - Token.Identifiers.TupleElementName("x"), + Token.Identifiers.LocalName("x"), Token.Punctuation.Comma, - Token.Identifiers.TupleElementName("y"), + Token.Identifiers.LocalName("y"), Token.Punctuation.CloseParen, Token.Keywords.Control.When, Token.Variables.ReadWrite("x"), @@ -4600,9 +4603,9 @@ select x.Key1;`); Token.Punctuation.Comma, Token.Keywords.Var, Token.Punctuation.OpenParen, - Token.Identifiers.TupleElementName("x"), + Token.Identifiers.LocalName("x"), Token.Punctuation.Comma, - Token.Identifiers.TupleElementName("y"), + Token.Identifiers.LocalName("y"), Token.Punctuation.CloseParen, Token.Keywords.Control.When, Token.Variables.ReadWrite("x"), @@ -4619,9 +4622,9 @@ select x.Key1;`); Token.Punctuation.Comma, Token.Keywords.Var, Token.Punctuation.OpenParen, - Token.Identifiers.TupleElementName("x"), + Token.Identifiers.LocalName("x"), Token.Punctuation.Comma, - Token.Identifiers.TupleElementName("y"), + Token.Identifiers.LocalName("y"), Token.Punctuation.CloseParen, Token.Keywords.Control.When, Token.Variables.ReadWrite("x"), @@ -4638,9 +4641,9 @@ select x.Key1;`); Token.Punctuation.Comma, Token.Keywords.Var, Token.Punctuation.OpenParen, - Token.Identifiers.TupleElementName("x"), + Token.Identifiers.LocalName("x"), Token.Punctuation.Comma, - Token.Identifiers.TupleElementName("y"), + Token.Identifiers.LocalName("y"), Token.Punctuation.CloseParen, Token.Keywords.Control.When, Token.Variables.ReadWrite("x"), @@ -4657,15 +4660,16 @@ select x.Key1;`); Token.Punctuation.Comma, Token.Keywords.Var, Token.Punctuation.OpenParen, - Token.Identifiers.TupleElementName("_"), + Token.Variables.Discard, Token.Punctuation.Comma, - Token.Identifiers.TupleElementName("_"), + Token.Variables.Discard, Token.Punctuation.CloseParen, Token.Operators.Arrow, Token.Variables.Object("Quadrant"), Token.Punctuation.Accessor, Token.Variables.Property("OnBorder"), Token.Punctuation.Comma, + Token.Variables.Discard, Token.Operators.Arrow, Token.Variables.Object("Quadrant"), Token.Punctuation.Accessor, @@ -4754,6 +4758,7 @@ select x.Key1;`); Token.Literals.Numeric.Decimal("50"), Token.Literals.Numeric.Other.Suffix("m"), Token.Punctuation.Comma, + Token.Variables.Discard, Token.Operators.Arrow, Token.Literals.Numeric.Decimal("2"), Token.Literals.Numeric.Other.Separator.Decimals, diff --git a/test/utils/tokenize.ts b/test/utils/tokenize.ts index 1458a5b..17e8bd2 100644 --- a/test/utils/tokenize.ts +++ b/test/utils/tokenize.ts @@ -278,7 +278,7 @@ export namespace Token { export const Switch = createToken('switch', 'keyword.control.switch.cs'); export const Throw = createToken('throw', 'keyword.control.flow.throw.cs'); export const Try = createToken('try', 'keyword.control.try.cs'); - export const When = createToken('when', 'keyword.control.try.when.cs'); + export const When = createToken('when', 'keyword.control.when.cs'); export const While = createToken('while', 'keyword.control.loop.while.cs'); export const Yield = createToken('yield', 'keyword.control.flow.yield.cs'); } @@ -571,6 +571,7 @@ export namespace Token { export const Property = (text: string) => createToken(text, 'variable.other.object.property.cs'); export const ReadWrite = (text: string) => createToken(text, 'variable.other.readwrite.cs'); export const Base = createToken('base', 'variable.language.base.cs'); + export const Discard = createToken('_', 'variable.language.discard.cs'); export const This = createToken('this', 'variable.language.this.cs'); export const Value = createToken('value', 'variable.other.value.cs'); } From a030a327ae0a41eadfb8ceeaa65d3d1757d205a7 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Sat, 12 Aug 2023 16:31:09 +0200 Subject: [PATCH 02/14] Patterns C# 7.0 - 11.0 WIP --- src/csharp.tmLanguage.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index c70f344..478c32c 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -1168,16 +1168,16 @@ repository: endCaptures: 1: { name: punctuation.separator.colon.cs } patterns: - - begin: (?!\s|\bwhen\b) - end: (?=\bwhen\b|:|}) - patterns: - - include: '#pattern' - begin: \b(when)\b beginCaptures: 1: { name: keyword.control.when.cs } end: (?=:|}) patterns: - include: '#case-guard' + - begin: (?!\s) + end: (?=\bwhen\b|:|}) + patterns: + - include: '#pattern' switch-expression: begin: \{ @@ -1187,24 +1187,24 @@ repository: endCaptures: 0: { name: punctuation.curlybrace.close.cs } patterns: - - begin: (?!\s|\bwhen\b|=>|,) - end: (?=\bwhen\b|=>|,|}) + - include: '#punctuation-comma' + - begin: => + beginCaptures: + 0: { name: keyword.operator.arrow.cs } + end: (?=,|}) patterns: - - include: '#pattern' + # - include: '#ref-modifier' # not supported yet + - include: '#expression' - begin: \b(when)\b beginCaptures: 1: { name: keyword.control.when.cs } end: (?==>|,|}) patterns: - include: '#case-guard' - - begin: => - beginCaptures: - 0: { name: keyword.operator.arrow.cs } - end: (?=,|}) + - begin: (?!\s) + end: (?=\bwhen\b|=>|,|}) patterns: - # - include: '#ref-modifier' # not supported yet - - include: '#expression' - - include: '#punctuation-comma' + - include: '#pattern' case-guard: patterns: From 83f972a4cfe7daa42dc067f90d4a51acf76ad1e6 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Sat, 12 Aug 2023 17:00:22 +0200 Subject: [PATCH 03/14] Add pattern combinator tokens to test --- test/utils/tokenize.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/utils/tokenize.ts b/test/utils/tokenize.ts index 17e8bd2..c4c4af5 100644 --- a/test/utils/tokenize.ts +++ b/test/utils/tokenize.ts @@ -482,6 +482,12 @@ export namespace Token { export const GreaterThanOrEqual = createToken('>=', 'keyword.operator.relational.cs'); } + export namespace Word { + export const And = createToken('and', 'keyword.operator.word.and.cs'); + export const Not = createToken('not', 'keyword.operator.word.not.cs'); + export const Or = createToken('or', 'keyword.operator.word.or.cs'); + } + export const Arrow = createToken('=>', 'keyword.operator.arrow.cs'); export const Assignment = createToken('=', 'keyword.operator.assignment.cs'); export const Decrement = createToken('--', 'keyword.operator.decrement.cs'); From 69cc1a1623e5bb0cc8f932bbcbff9c2eccf51c07 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Sun, 13 Aug 2023 23:01:54 +0200 Subject: [PATCH 04/14] WIP add pattern tests --- test/expressions.tests.ts | 1025 ------------------------------------ test/patterns.tests.ts | 1041 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1041 insertions(+), 1025 deletions(-) create mode 100644 test/patterns.tests.ts diff --git a/test/expressions.tests.ts b/test/expressions.tests.ts index 15b2d57..0105367 100644 --- a/test/expressions.tests.ts +++ b/test/expressions.tests.ts @@ -4029,1031 +4029,6 @@ select x.Key1;`); }); }); - describe("Switch Expressions", () => { - it("simple switch expression", async () => { - const input = Input.InClass(` - public decimal Calculate(object thing) => - thing switch - { - Car c => 2.00m - 1.0m, - Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m, - Bus b => 5.00m, - { } => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), - null => throw new ArgumentNullException(nameof(vehicle)), - _ => 3.50m - 1.00m, - }; - `); - const tokens = await tokenize(input); - - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Public, - Token.PrimitiveType.Decimal, - Token.Identifiers.MethodName("Calculate"), - Token.Punctuation.OpenParen, - Token.PrimitiveType.Object, - Token.Identifiers.ParameterName("thing"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.ReadWrite("thing"), - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Type("Car"), - Token.Identifiers.LocalName("c"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("1"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Bus"), - Token.Identifiers.LocalName("b"), - Token.Keywords.Control.When, - Token.Punctuation.OpenParen, - Token.Punctuation.OpenParen, - Token.PrimitiveType.Double, - Token.Punctuation.CloseParen, - Token.Variables.Object("b"), - Token.Punctuation.Accessor, - Token.Variables.Property("Riders"), - Token.Operators.Arithmetic.Division, - Token.Punctuation.OpenParen, - Token.PrimitiveType.Double, - Token.Punctuation.CloseParen, - Token.Variables.Object("b"), - Token.Punctuation.Accessor, - Token.Variables.Property("Capacity"), - Token.Punctuation.CloseParen, - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("5"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Addition, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Bus"), - Token.Identifiers.LocalName("b"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("5"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Punctuation.OpenBrace, - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Keywords.Control.Throw, - Token.Keywords.New, - Token.Type("ArgumentException"), - Token.Punctuation.OpenParen, - Token.Identifiers.ParameterName("message"), - Token.Punctuation.Colon, - Token.Punctuation.String.Begin, - Token.Literals.String("Not a known vehicle type"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Identifiers.ParameterName("paramName"), - Token.Punctuation.Colon, - Token.Keywords.NameOf, - Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("vehicle"), - Token.Punctuation.CloseParen, - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Literals.Null, - Token.Operators.Arrow, - Token.Keywords.Control.Throw, - Token.Keywords.New, - Token.Type("ArgumentNullException"), - Token.Punctuation.OpenParen, - Token.Keywords.NameOf, - Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("vehicle"), - Token.Punctuation.CloseParen, - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Variables.Discard, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("3"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("1"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Punctuation.CloseBrace, - Token.Punctuation.Semicolon - ]); - }); - - it("works with enum example", async () => { - const input = Input.InClass(`public static RGBColor FromRainbow(Rainbow colorBand) => - colorBand switch - { - Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00), - Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00), - Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00), - Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00), - Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF), - Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82), - Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3), - _ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)), - };`); - const tokens = await tokenize(input); - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Public, - Token.Keywords.Modifiers.Static, - Token.Type("RGBColor"), - Token.Identifiers.MethodName("FromRainbow"), - Token.Punctuation.OpenParen, - Token.Type("Rainbow"), - Token.Identifiers.ParameterName("colorBand"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.ReadWrite("colorBand"), - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Red"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("FF"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Orange"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("FF"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("7F"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Yellow"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("FF"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("FF"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Green"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("FF"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Blue"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("FF"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Indigo"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("4B"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("82"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Type("Rainbow"), - Token.Punctuation.Accessor, - Token.Type("Violet"), - Token.Operators.Arrow, - Token.Keywords.New, - Token.Type("RGBColor"), - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("94"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("00"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), - Token.Literals.Numeric.Hexadecimal("D3"), - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Variables.Discard, - Token.Operators.Arrow, - Token.Keywords.Control.Throw, - Token.Keywords.New, - Token.Type("ArgumentException"), - Token.Punctuation.OpenParen, - Token.Identifiers.ParameterName("message"), - Token.Punctuation.Colon, - Token.Punctuation.String.Begin, - Token.Literals.String("invalid enum value"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Identifiers.ParameterName("paramName"), - Token.Punctuation.Colon, - Token.Keywords.NameOf, - Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("colorBand"), - Token.Punctuation.CloseParen, - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Punctuation.CloseBrace, - Token.Punctuation.Semicolon - ]); - }); - - it("works with property pattern", async () => { - const input = Input.InClass(`public static decimal ComputeSalesTax(Address location, decimal salePrice) => - location switch - { - { State: "WA" } => salePrice * 0.06M, - { State: "MN" } => salePrice * 0.75M, - { State: "MI" } => salePrice * 0.05M, - // other cases removed for brevity... - _ => 0M - };`); - const tokens = await tokenize(input); - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Public, - Token.Keywords.Modifiers.Static, - Token.PrimitiveType.Decimal, - Token.Identifiers.MethodName("ComputeSalesTax"), - Token.Punctuation.OpenParen, - Token.Type("Address"), - Token.Identifiers.ParameterName("location"), - Token.Punctuation.Comma, - Token.PrimitiveType.Decimal, - Token.Identifiers.ParameterName("salePrice"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.ReadWrite("location"), - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Punctuation.OpenBrace, - Token.Variables.Property("State"), - Token.Punctuation.Colon, - Token.Punctuation.String.Begin, - Token.Literals.String("WA"), - Token.Punctuation.String.End, - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Variables.ReadWrite("salePrice"), - Token.Operators.Arithmetic.Multiplication, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("06"), - Token.Literals.Numeric.Other.Suffix("M"), - Token.Punctuation.Comma, - Token.Punctuation.OpenBrace, - Token.Variables.Property("State"), - Token.Punctuation.Colon, - Token.Punctuation.String.Begin, - Token.Literals.String("MN"), - Token.Punctuation.String.End, - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Variables.ReadWrite("salePrice"), - Token.Operators.Arithmetic.Multiplication, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("75"), - Token.Literals.Numeric.Other.Suffix("M"), - Token.Punctuation.Comma, - Token.Punctuation.OpenBrace, - Token.Variables.Property("State"), - Token.Punctuation.Colon, - Token.Punctuation.String.Begin, - Token.Literals.String("MI"), - Token.Punctuation.String.End, - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Variables.ReadWrite("salePrice"), - Token.Operators.Arithmetic.Multiplication, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("05"), - Token.Literals.Numeric.Other.Suffix("M"), - Token.Punctuation.Comma, - Token.Comment.LeadingWhitespace(" "), - Token.Comment.SingleLine.Start, - Token.Comment.SingleLine.Text(" other cases removed for brevity..."), - Token.Variables.Discard, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Suffix("M"), - Token.Punctuation.CloseBrace, - Token.Punctuation.Semicolon - ]); - }); - - it("works with tuple pattern", async () => { - const input = Input.InClass(`public static string RockPaperScissors(string first, string second) - => (first, second) switch - { - ("rock", "paper") => "rock is covered by paper. Paper wins.", - ("rock", "scissors") => "rock breaks scissors. Rock wins.", - ("paper", "rock") => "paper covers rock. Paper wins.", - ("paper", "scissors") => "paper is cut by scissors. Scissors wins.", - ("scissors", "rock") => "scissors is broken by rock. Rock wins.", - ("scissors", "paper") => "scissors cuts paper. Scissors wins.", - (_, _) => "tie" - };`); - const tokens = await tokenize(input); - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Public, - Token.Keywords.Modifiers.Static, - Token.PrimitiveType.String, - Token.Identifiers.MethodName("RockPaperScissors"), - Token.Punctuation.OpenParen, - Token.PrimitiveType.String, - Token.Identifiers.ParameterName("first"), - Token.Punctuation.Comma, - Token.PrimitiveType.String, - Token.Identifiers.ParameterName("second"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("first"), - Token.Punctuation.Comma, - Token.Variables.ReadWrite("second"), - Token.Punctuation.CloseParen, - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Punctuation.OpenParen, - Token.Punctuation.String.Begin, - Token.Literals.String("rock"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.String.Begin, - Token.Literals.String("paper"), - Token.Punctuation.String.End, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("rock is covered by paper. Paper wins."), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.OpenParen, - Token.Punctuation.String.Begin, - Token.Literals.String("rock"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.String.Begin, - Token.Literals.String("scissors"), - Token.Punctuation.String.End, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("rock breaks scissors. Rock wins."), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.OpenParen, - Token.Punctuation.String.Begin, - Token.Literals.String("paper"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.String.Begin, - Token.Literals.String("rock"), - Token.Punctuation.String.End, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("paper covers rock. Paper wins."), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.OpenParen, - Token.Punctuation.String.Begin, - Token.Literals.String("paper"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.String.Begin, - Token.Literals.String("scissors"), - Token.Punctuation.String.End, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("paper is cut by scissors. Scissors wins."), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.OpenParen, - Token.Punctuation.String.Begin, - Token.Literals.String("scissors"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.String.Begin, - Token.Literals.String("rock"), - Token.Punctuation.String.End, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("scissors is broken by rock. Rock wins."), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.OpenParen, - Token.Punctuation.String.Begin, - Token.Literals.String("scissors"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.String.Begin, - Token.Literals.String("paper"), - Token.Punctuation.String.End, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("scissors cuts paper. Scissors wins."), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Punctuation.OpenParen, - Token.Variables.Discard, - Token.Punctuation.Comma, - Token.Variables.Discard, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Punctuation.String.Begin, - Token.Literals.String("tie"), - Token.Punctuation.String.End, - Token.Punctuation.CloseBrace, - Token.Punctuation.Semicolon - ]); - }); - - it("works with positional pattern", async () => { - const input = Input.InClass(`public static Quadrant GetQuadrant(Point point) => point switch - { - (0, 0) => Quadrant.Origin, - var (x, y) when x > 0 && y > 0 => Quadrant.One, - var (x, y) when x < 0 && y > 0 => Quadrant.Two, - var (x, y) when x < 0 && y < 0 => Quadrant.Three, - var (x, y) when x > 0 && y < 0 => Quadrant.Four, - var (_, _) => Quadrant.OnBorder, - _ => Quadrant.Unknown - };`); - const tokens = await tokenize(input); - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Public, - Token.Keywords.Modifiers.Static, - Token.Type("Quadrant"), - Token.Identifiers.MethodName("GetQuadrant"), - Token.Punctuation.OpenParen, - Token.Type("Point"), - Token.Identifiers.ParameterName("point"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.ReadWrite("point"), - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Punctuation.OpenParen, - Token.Literals.Numeric.Decimal("0"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Decimal("0"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("Origin"), - Token.Punctuation.Comma, - Token.Keywords.Var, - Token.Punctuation.OpenParen, - Token.Identifiers.LocalName("x"), - Token.Punctuation.Comma, - Token.Identifiers.LocalName("y"), - Token.Punctuation.CloseParen, - Token.Keywords.Control.When, - Token.Variables.ReadWrite("x"), - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Logical.And, - Token.Variables.ReadWrite("y"), - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("One"), - Token.Punctuation.Comma, - Token.Keywords.Var, - Token.Punctuation.OpenParen, - Token.Identifiers.LocalName("x"), - Token.Punctuation.Comma, - Token.Identifiers.LocalName("y"), - Token.Punctuation.CloseParen, - Token.Keywords.Control.When, - Token.Variables.ReadWrite("x"), - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Logical.And, - Token.Variables.ReadWrite("y"), - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("Two"), - Token.Punctuation.Comma, - Token.Keywords.Var, - Token.Punctuation.OpenParen, - Token.Identifiers.LocalName("x"), - Token.Punctuation.Comma, - Token.Identifiers.LocalName("y"), - Token.Punctuation.CloseParen, - Token.Keywords.Control.When, - Token.Variables.ReadWrite("x"), - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Logical.And, - Token.Variables.ReadWrite("y"), - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("Three"), - Token.Punctuation.Comma, - Token.Keywords.Var, - Token.Punctuation.OpenParen, - Token.Identifiers.LocalName("x"), - Token.Punctuation.Comma, - Token.Identifiers.LocalName("y"), - Token.Punctuation.CloseParen, - Token.Keywords.Control.When, - Token.Variables.ReadWrite("x"), - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Logical.And, - Token.Variables.ReadWrite("y"), - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("Four"), - Token.Punctuation.Comma, - Token.Keywords.Var, - Token.Punctuation.OpenParen, - Token.Variables.Discard, - Token.Punctuation.Comma, - Token.Variables.Discard, - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("OnBorder"), - Token.Punctuation.Comma, - Token.Variables.Discard, - Token.Operators.Arrow, - Token.Variables.Object("Quadrant"), - Token.Punctuation.Accessor, - Token.Variables.Property("Unknown"), - Token.Punctuation.CloseBrace, - Token.Punctuation.Semicolon - ]); - }); - - it("works with nested expressions", async () => { - const input = Input.InClass(`public decimal CalculateToll(object vehicle) => - vehicle switch - { - Car c => c.Passengers switch - { - 0 => 2.00m + 0.50m, - 1 => 2.0m, - 2 => 2.0m - 0.50m, - _ => 2.00m - 1.0m, - }, - - Taxi { Fares: 0 } => 3.50m + 1.00m, - Taxi { Fares: 1 } => 3.50m, - Taxi { Fares: 2 } => 3.50m - 0.50m, - Taxi t => 3.50m - 1.00m, - - Bus b when (double)b.Riders / (double)b.Capacity < 0.50 => 5.00m + 2.00m, - Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m, - Bus b => 5.00m, - - DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m, - DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m, - DeliveryTruck t => 10.00m, - null => throw new ArgumentNullException(nameof(vehicle)), - { } => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), - };`); - const tokens = await tokenize(input); - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Public, - Token.PrimitiveType.Decimal, - Token.Identifiers.MethodName("CalculateToll"), - Token.Punctuation.OpenParen, - Token.PrimitiveType.Object, - Token.Identifiers.ParameterName("vehicle"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Variables.ReadWrite("vehicle"), - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Type("Car"), - Token.Identifiers.LocalName("c"), - Token.Operators.Arrow, - Token.Variables.Object("c"), - Token.Punctuation.Accessor, - Token.Variables.Property("Passengers"), - Token.Keywords.Control.Switch, - Token.Punctuation.OpenBrace, - Token.Literals.Numeric.Decimal("0"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Addition, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Decimal("1"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Literals.Numeric.Decimal("2"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Variables.Discard, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("1"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Punctuation.CloseBrace, - Token.Punctuation.Comma, - Token.Type("Taxi"), - Token.Punctuation.OpenBrace, - Token.Variables.Property("Fares"), - Token.Punctuation.Colon, - Token.Literals.Numeric.Decimal("0"), - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("3"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Addition, - Token.Literals.Numeric.Decimal("1"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Taxi"), - Token.Punctuation.OpenBrace, - Token.Variables.Property("Fares"), - Token.Punctuation.Colon, - Token.Literals.Numeric.Decimal("1"), - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("3"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Taxi"), - Token.Punctuation.OpenBrace, - Token.Variables.Property("Fares"), - Token.Punctuation.Colon, - Token.Literals.Numeric.Decimal("2"), - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("3"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Taxi"), - Token.Identifiers.LocalName("t"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("3"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("1"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Bus"), - Token.Identifiers.LocalName("b"), - Token.Keywords.Control.When, - Token.Punctuation.OpenParen, - Token.PrimitiveType.Double, - Token.Punctuation.CloseParen, - Token.Variables.Object("b"), - Token.Punctuation.Accessor, - Token.Variables.Property("Riders"), - Token.Operators.Arithmetic.Division, - Token.Punctuation.OpenParen, - Token.PrimitiveType.Double, - Token.Punctuation.CloseParen, - Token.Variables.Object("b"), - Token.Punctuation.Accessor, - Token.Variables.Property("Capacity"), - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("50"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("5"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Addition, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Bus"), - Token.Identifiers.LocalName("b"), - Token.Keywords.Control.When, - Token.Punctuation.OpenParen, - Token.Punctuation.OpenParen, - Token.PrimitiveType.Double, - Token.Punctuation.CloseParen, - Token.Variables.Object("b"), - Token.Punctuation.Accessor, - Token.Variables.Property("Riders"), - Token.Operators.Arithmetic.Division, - Token.Punctuation.OpenParen, - Token.PrimitiveType.Double, - Token.Punctuation.CloseParen, - Token.Variables.Object("b"), - Token.Punctuation.Accessor, - Token.Variables.Property("Capacity"), - Token.Punctuation.CloseParen, - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("0"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("90"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("5"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("1"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("Bus"), - Token.Identifiers.LocalName("b"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("5"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("DeliveryTruck"), - Token.Identifiers.LocalName("t"), - Token.Keywords.Control.When, - Token.Punctuation.OpenParen, - Token.Variables.Object("t"), - Token.Punctuation.Accessor, - Token.Variables.Property("GrossWeightClass"), - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("5000"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("10"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Addition, - Token.Literals.Numeric.Decimal("5"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("DeliveryTruck"), - Token.Identifiers.LocalName("t"), - Token.Keywords.Control.When, - Token.Punctuation.OpenParen, - Token.Variables.Object("t"), - Token.Punctuation.Accessor, - Token.Variables.Property("GrossWeightClass"), - Token.Operators.Relational.LessThan, - Token.Literals.Numeric.Decimal("3000"), - Token.Punctuation.CloseParen, - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("10"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Operators.Arithmetic.Subtraction, - Token.Literals.Numeric.Decimal("2"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Type("DeliveryTruck"), - Token.Identifiers.LocalName("t"), - Token.Operators.Arrow, - Token.Literals.Numeric.Decimal("10"), - Token.Literals.Numeric.Other.Separator.Decimals, - Token.Literals.Numeric.Decimal("00"), - Token.Literals.Numeric.Other.Suffix("m"), - Token.Punctuation.Comma, - Token.Literals.Null, - Token.Operators.Arrow, - Token.Keywords.Control.Throw, - Token.Keywords.New, - Token.Type("ArgumentNullException"), - Token.Punctuation.OpenParen, - Token.Keywords.NameOf, - Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("vehicle"), - Token.Punctuation.CloseParen, - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Punctuation.OpenBrace, - Token.Punctuation.CloseBrace, - Token.Operators.Arrow, - Token.Keywords.Control.Throw, - Token.Keywords.New, - Token.Type("ArgumentException"), - Token.Punctuation.OpenParen, - Token.Identifiers.ParameterName("message"), - Token.Punctuation.Colon, - Token.Punctuation.String.Begin, - Token.Literals.String("Not a known vehicle type"), - Token.Punctuation.String.End, - Token.Punctuation.Comma, - Token.Identifiers.ParameterName("paramName"), - Token.Punctuation.Colon, - Token.Keywords.NameOf, - Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("vehicle"), - Token.Punctuation.CloseParen, - Token.Punctuation.CloseParen, - Token.Punctuation.Comma, - Token.Punctuation.CloseBrace, - Token.Punctuation.Semicolon - ]); - }); - - it("does not highlight variable names starting with 'switch' (issue #208)", async () => { - const input = Input.InClass(`private bool switchedOn = false; - - private bool DecodeSwitchString(string switchString) { - var switchOn = false; - - if (!this.switchedOn) - { - switchOn = switchString.ToUpper() == "ON"; - } - - return switchOn; - }`); - const tokens = await tokenize(input); - tokens.should.deep.equal([ - Token.Keywords.Modifiers.Private, - Token.PrimitiveType.Bool, - Token.Identifiers.FieldName("switchedOn"), - Token.Operators.Assignment, - Token.Literals.Boolean.False, - Token.Punctuation.Semicolon, - Token.Keywords.Modifiers.Private, - Token.PrimitiveType.Bool, - Token.Identifiers.MethodName("DecodeSwitchString"), - Token.Punctuation.OpenParen, - Token.PrimitiveType.String, - Token.Identifiers.ParameterName("switchString"), - Token.Punctuation.CloseParen, - Token.Punctuation.OpenBrace, - Token.Keywords.Var, - Token.Identifiers.LocalName("switchOn"), - Token.Operators.Assignment, - Token.Literals.Boolean.False, - Token.Punctuation.Semicolon, - Token.Keywords.Control.If, - Token.Punctuation.OpenParen, - Token.Operators.Logical.Not, - Token.Variables.This, - Token.Punctuation.Accessor, - Token.Variables.Property("switchedOn"), - Token.Punctuation.CloseParen, - Token.Punctuation.OpenBrace, - Token.Variables.ReadWrite("switchOn"), - Token.Operators.Assignment, - Token.Variables.Object("switchString"), - Token.Punctuation.Accessor, - Token.Identifiers.MethodName("ToUpper"), - Token.Punctuation.OpenParen, - Token.Punctuation.CloseParen, - Token.Operators.Relational.Equals, - Token.Punctuation.String.Begin, - Token.Literals.String("ON"), - Token.Punctuation.String.End, - Token.Punctuation.Semicolon, - Token.Punctuation.CloseBrace, - Token.Keywords.Control.Return, - Token.Variables.ReadWrite("switchOn"), - Token.Punctuation.Semicolon, - Token.Punctuation.CloseBrace - ]); - }); - }); - describe("With expression", () => { it("single line", async () => { const input = Input.InMethod(`var p2 = p1 with { X = 5 };`); diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts new file mode 100644 index 0000000..8d73cc5 --- /dev/null +++ b/test/patterns.tests.ts @@ -0,0 +1,1041 @@ +import { should } from "chai"; +import { tokenize, Input, Token } from "./utils/tokenize"; + +describe("Patterns", () => { + before(should); + + describe("is pattern", () => { + + }); + + describe("switch case pattern", () => { + + }); + + describe("switch expression pattern", () => { + it("simple", async () => { + const input = Input.InClass(` +public decimal Calculate(object thing) => + thing switch + { + Car c => 2.00m - 1.0m, + Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m, + Bus b => 5.00m, + { } => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), + null => throw new ArgumentNullException(nameof(vehicle)), + _ => 3.50m - 1.00m, + };`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Public, + Token.PrimitiveType.Decimal, + Token.Identifiers.MethodName("Calculate"), + Token.Punctuation.OpenParen, + Token.PrimitiveType.Object, + Token.Identifiers.ParameterName("thing"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.ReadWrite("thing"), + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Type("Car"), + Token.Identifiers.LocalName("c"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("1"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Bus"), + Token.Identifiers.LocalName("b"), + Token.Keywords.Control.When, + Token.Punctuation.OpenParen, + Token.Punctuation.OpenParen, + Token.PrimitiveType.Double, + Token.Punctuation.CloseParen, + Token.Variables.Object("b"), + Token.Punctuation.Accessor, + Token.Variables.Property("Riders"), + Token.Operators.Arithmetic.Division, + Token.Punctuation.OpenParen, + Token.PrimitiveType.Double, + Token.Punctuation.CloseParen, + Token.Variables.Object("b"), + Token.Punctuation.Accessor, + Token.Variables.Property("Capacity"), + Token.Punctuation.CloseParen, + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("5"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Addition, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Bus"), + Token.Identifiers.LocalName("b"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("5"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Keywords.Control.Throw, + Token.Keywords.New, + Token.Type("ArgumentException"), + Token.Punctuation.OpenParen, + Token.Identifiers.ParameterName("message"), + Token.Punctuation.Colon, + Token.Punctuation.String.Begin, + Token.Literals.String("Not a known vehicle type"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Identifiers.ParameterName("paramName"), + Token.Punctuation.Colon, + Token.Keywords.NameOf, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("vehicle"), + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Literals.Null, + Token.Operators.Arrow, + Token.Keywords.Control.Throw, + Token.Keywords.New, + Token.Type("ArgumentNullException"), + Token.Punctuation.OpenParen, + Token.Keywords.NameOf, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("vehicle"), + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("3"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("1"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Punctuation.CloseBrace, + Token.Punctuation.Semicolon + ]); + }); + + it("works with enum example", async () => { + const input = Input.InClass(` +public static RGBColor FromRainbow(Rainbow colorBand) => colorBand switch +{ + Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00), + Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00), + Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00), + Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00), + Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF), + Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82), + Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3), + _ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)), +};`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Public, + Token.Keywords.Modifiers.Static, + Token.Type("RGBColor"), + Token.Identifiers.MethodName("FromRainbow"), + Token.Punctuation.OpenParen, + Token.Type("Rainbow"), + Token.Identifiers.ParameterName("colorBand"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.ReadWrite("colorBand"), + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Red"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("FF"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Orange"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("FF"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("7F"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Yellow"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("FF"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("FF"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Green"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("FF"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Blue"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("FF"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Indigo"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("4B"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("82"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Type("Rainbow"), + Token.Punctuation.Accessor, + Token.Type("Violet"), + Token.Operators.Arrow, + Token.Keywords.New, + Token.Type("RGBColor"), + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("94"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("00"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Other.Preffix.Hexadecimal("0x"), + Token.Literals.Numeric.Hexadecimal("D3"), + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Operators.Arrow, + Token.Keywords.Control.Throw, + Token.Keywords.New, + Token.Type("ArgumentException"), + Token.Punctuation.OpenParen, + Token.Identifiers.ParameterName("message"), + Token.Punctuation.Colon, + Token.Punctuation.String.Begin, + Token.Literals.String("invalid enum value"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Identifiers.ParameterName("paramName"), + Token.Punctuation.Colon, + Token.Keywords.NameOf, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("colorBand"), + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Punctuation.CloseBrace, + Token.Punctuation.Semicolon + ]); + }); + + it("works with property pattern", async () => { + const input = Input.InClass(` +public static decimal ComputeSalesTax(Address location, decimal salePrice) => + location switch + { + { State: "WA" } => salePrice * 0.06M, + { State: "MN" } => salePrice * 0.75M, + { State: "MI" } => salePrice * 0.05M, + // other cases removed for brevity... + _ => 0M + };`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Public, + Token.Keywords.Modifiers.Static, + Token.PrimitiveType.Decimal, + Token.Identifiers.MethodName("ComputeSalesTax"), + Token.Punctuation.OpenParen, + Token.Type("Address"), + Token.Identifiers.ParameterName("location"), + Token.Punctuation.Comma, + Token.PrimitiveType.Decimal, + Token.Identifiers.ParameterName("salePrice"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.ReadWrite("location"), + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Punctuation.OpenBrace, + Token.Variables.Property("State"), + Token.Punctuation.Colon, + Token.Punctuation.String.Begin, + Token.Literals.String("WA"), + Token.Punctuation.String.End, + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Variables.ReadWrite("salePrice"), + Token.Operators.Arithmetic.Multiplication, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("06"), + Token.Literals.Numeric.Other.Suffix("M"), + Token.Punctuation.Comma, + Token.Punctuation.OpenBrace, + Token.Variables.Property("State"), + Token.Punctuation.Colon, + Token.Punctuation.String.Begin, + Token.Literals.String("MN"), + Token.Punctuation.String.End, + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Variables.ReadWrite("salePrice"), + Token.Operators.Arithmetic.Multiplication, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("75"), + Token.Literals.Numeric.Other.Suffix("M"), + Token.Punctuation.Comma, + Token.Punctuation.OpenBrace, + Token.Variables.Property("State"), + Token.Punctuation.Colon, + Token.Punctuation.String.Begin, + Token.Literals.String("MI"), + Token.Punctuation.String.End, + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Variables.ReadWrite("salePrice"), + Token.Operators.Arithmetic.Multiplication, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("05"), + Token.Literals.Numeric.Other.Suffix("M"), + Token.Punctuation.Comma, + Token.Comment.LeadingWhitespace(" "), + Token.Comment.SingleLine.Start, + Token.Comment.SingleLine.Text(" other cases removed for brevity..."), + Token.Variables.Discard, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Suffix("M"), + Token.Punctuation.CloseBrace, + Token.Punctuation.Semicolon + ]); + }); + + it("works with tuple pattern", async () => { + const input = Input.InClass(` +public static string RockPaperScissors(string first, string second) + => (first, second) switch + { + ("rock", "paper") => "rock is covered by paper. Paper wins.", + ("rock", "scissors") => "rock breaks scissors. Rock wins.", + ("paper", "rock") => "paper covers rock. Paper wins.", + ("paper", "scissors") => "paper is cut by scissors. Scissors wins.", + ("scissors", "rock") => "scissors is broken by rock. Rock wins.", + ("scissors", "paper") => "scissors cuts paper. Scissors wins.", + (_, _) => "tie" + };`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Public, + Token.Keywords.Modifiers.Static, + Token.PrimitiveType.String, + Token.Identifiers.MethodName("RockPaperScissors"), + Token.Punctuation.OpenParen, + Token.PrimitiveType.String, + Token.Identifiers.ParameterName("first"), + Token.Punctuation.Comma, + Token.PrimitiveType.String, + Token.Identifiers.ParameterName("second"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("first"), + Token.Punctuation.Comma, + Token.Variables.ReadWrite("second"), + Token.Punctuation.CloseParen, + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Punctuation.OpenParen, + Token.Punctuation.String.Begin, + Token.Literals.String("rock"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.String.Begin, + Token.Literals.String("paper"), + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("rock is covered by paper. Paper wins."), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.OpenParen, + Token.Punctuation.String.Begin, + Token.Literals.String("rock"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.String.Begin, + Token.Literals.String("scissors"), + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("rock breaks scissors. Rock wins."), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.OpenParen, + Token.Punctuation.String.Begin, + Token.Literals.String("paper"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.String.Begin, + Token.Literals.String("rock"), + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("paper covers rock. Paper wins."), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.OpenParen, + Token.Punctuation.String.Begin, + Token.Literals.String("paper"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.String.Begin, + Token.Literals.String("scissors"), + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("paper is cut by scissors. Scissors wins."), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.OpenParen, + Token.Punctuation.String.Begin, + Token.Literals.String("scissors"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.String.Begin, + Token.Literals.String("rock"), + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("scissors is broken by rock. Rock wins."), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.OpenParen, + Token.Punctuation.String.Begin, + Token.Literals.String("scissors"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.String.Begin, + Token.Literals.String("paper"), + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("scissors cuts paper. Scissors wins."), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Punctuation.OpenParen, + Token.Variables.Discard, + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Punctuation.String.Begin, + Token.Literals.String("tie"), + Token.Punctuation.String.End, + Token.Punctuation.CloseBrace, + Token.Punctuation.Semicolon + ]); + }); + + it("works with positional pattern", async () => { + const input = Input.InClass(` +public static Quadrant GetQuadrant(Point point) => point switch + { + (0, 0) => Quadrant.Origin, + var (x, y) when x > 0 && y > 0 => Quadrant.One, + var (x, y) when x < 0 && y > 0 => Quadrant.Two, + var (x, y) when x < 0 && y < 0 => Quadrant.Three, + var (x, y) when x > 0 && y < 0 => Quadrant.Four, + var (_, _) => Quadrant.OnBorder, + _ => Quadrant.Unknown + };`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Public, + Token.Keywords.Modifiers.Static, + Token.Type("Quadrant"), + Token.Identifiers.MethodName("GetQuadrant"), + Token.Punctuation.OpenParen, + Token.Type("Point"), + Token.Identifiers.ParameterName("point"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.ReadWrite("point"), + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Punctuation.OpenParen, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("Origin"), + Token.Punctuation.Comma, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("x"), + Token.Punctuation.Comma, + Token.Identifiers.LocalName("y"), + Token.Punctuation.CloseParen, + Token.Keywords.Control.When, + Token.Variables.ReadWrite("x"), + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Logical.And, + Token.Variables.ReadWrite("y"), + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("One"), + Token.Punctuation.Comma, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("x"), + Token.Punctuation.Comma, + Token.Identifiers.LocalName("y"), + Token.Punctuation.CloseParen, + Token.Keywords.Control.When, + Token.Variables.ReadWrite("x"), + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Logical.And, + Token.Variables.ReadWrite("y"), + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("Two"), + Token.Punctuation.Comma, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("x"), + Token.Punctuation.Comma, + Token.Identifiers.LocalName("y"), + Token.Punctuation.CloseParen, + Token.Keywords.Control.When, + Token.Variables.ReadWrite("x"), + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Logical.And, + Token.Variables.ReadWrite("y"), + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("Three"), + Token.Punctuation.Comma, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("x"), + Token.Punctuation.Comma, + Token.Identifiers.LocalName("y"), + Token.Punctuation.CloseParen, + Token.Keywords.Control.When, + Token.Variables.ReadWrite("x"), + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Logical.And, + Token.Variables.ReadWrite("y"), + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("Four"), + Token.Punctuation.Comma, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Variables.Discard, + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("OnBorder"), + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Operators.Arrow, + Token.Variables.Object("Quadrant"), + Token.Punctuation.Accessor, + Token.Variables.Property("Unknown"), + Token.Punctuation.CloseBrace, + Token.Punctuation.Semicolon + ]); + }); + + it("works with nested expressions", async () => { + const input = Input.InClass(` +public decimal CalculateToll(object vehicle) => + vehicle switch + { + Car c => c.Passengers switch + { + 0 => 2.00m + 0.50m, + 1 => 2.0m, + 2 => 2.0m - 0.50m, + _ => 2.00m - 1.0m, + }, + + Taxi { Fares: 0 } => 3.50m + 1.00m, + Taxi { Fares: 1 } => 3.50m, + Taxi { Fares: 2 } => 3.50m - 0.50m, + Taxi t => 3.50m - 1.00m, + + Bus b when (double)b.Riders / (double)b.Capacity < 0.50 => 5.00m + 2.00m, + Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m, + Bus b => 5.00m, + + DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m, + DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m, + DeliveryTruck t => 10.00m, + null => throw new ArgumentNullException(nameof(vehicle)), + { } => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)), + };`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Public, + Token.PrimitiveType.Decimal, + Token.Identifiers.MethodName("CalculateToll"), + Token.Punctuation.OpenParen, + Token.PrimitiveType.Object, + Token.Identifiers.ParameterName("vehicle"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Variables.ReadWrite("vehicle"), + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Type("Car"), + Token.Identifiers.LocalName("c"), + Token.Operators.Arrow, + Token.Variables.Object("c"), + Token.Punctuation.Accessor, + Token.Variables.Property("Passengers"), + Token.Keywords.Control.Switch, + Token.Punctuation.OpenBrace, + Token.Literals.Numeric.Decimal("0"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Addition, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("1"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("2"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("1"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Punctuation.CloseBrace, + Token.Punctuation.Comma, + Token.Type("Taxi"), + Token.Punctuation.OpenBrace, + Token.Variables.Property("Fares"), + Token.Punctuation.Colon, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("3"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Addition, + Token.Literals.Numeric.Decimal("1"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Taxi"), + Token.Punctuation.OpenBrace, + Token.Variables.Property("Fares"), + Token.Punctuation.Colon, + Token.Literals.Numeric.Decimal("1"), + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("3"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Taxi"), + Token.Punctuation.OpenBrace, + Token.Variables.Property("Fares"), + Token.Punctuation.Colon, + Token.Literals.Numeric.Decimal("2"), + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("3"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Taxi"), + Token.Identifiers.LocalName("t"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("3"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("1"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Bus"), + Token.Identifiers.LocalName("b"), + Token.Keywords.Control.When, + Token.Punctuation.OpenParen, + Token.PrimitiveType.Double, + Token.Punctuation.CloseParen, + Token.Variables.Object("b"), + Token.Punctuation.Accessor, + Token.Variables.Property("Riders"), + Token.Operators.Arithmetic.Division, + Token.Punctuation.OpenParen, + Token.PrimitiveType.Double, + Token.Punctuation.CloseParen, + Token.Variables.Object("b"), + Token.Punctuation.Accessor, + Token.Variables.Property("Capacity"), + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("50"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("5"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Addition, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Bus"), + Token.Identifiers.LocalName("b"), + Token.Keywords.Control.When, + Token.Punctuation.OpenParen, + Token.Punctuation.OpenParen, + Token.PrimitiveType.Double, + Token.Punctuation.CloseParen, + Token.Variables.Object("b"), + Token.Punctuation.Accessor, + Token.Variables.Property("Riders"), + Token.Operators.Arithmetic.Division, + Token.Punctuation.OpenParen, + Token.PrimitiveType.Double, + Token.Punctuation.CloseParen, + Token.Variables.Object("b"), + Token.Punctuation.Accessor, + Token.Variables.Property("Capacity"), + Token.Punctuation.CloseParen, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("90"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("5"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("1"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("Bus"), + Token.Identifiers.LocalName("b"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("5"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("DeliveryTruck"), + Token.Identifiers.LocalName("t"), + Token.Keywords.Control.When, + Token.Punctuation.OpenParen, + Token.Variables.Object("t"), + Token.Punctuation.Accessor, + Token.Variables.Property("GrossWeightClass"), + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("5000"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("10"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Addition, + Token.Literals.Numeric.Decimal("5"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("DeliveryTruck"), + Token.Identifiers.LocalName("t"), + Token.Keywords.Control.When, + Token.Punctuation.OpenParen, + Token.Variables.Object("t"), + Token.Punctuation.Accessor, + Token.Variables.Property("GrossWeightClass"), + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("3000"), + Token.Punctuation.CloseParen, + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("10"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Operators.Arithmetic.Subtraction, + Token.Literals.Numeric.Decimal("2"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Type("DeliveryTruck"), + Token.Identifiers.LocalName("t"), + Token.Operators.Arrow, + Token.Literals.Numeric.Decimal("10"), + Token.Literals.Numeric.Other.Separator.Decimals, + Token.Literals.Numeric.Decimal("00"), + Token.Literals.Numeric.Other.Suffix("m"), + Token.Punctuation.Comma, + Token.Literals.Null, + Token.Operators.Arrow, + Token.Keywords.Control.Throw, + Token.Keywords.New, + Token.Type("ArgumentNullException"), + Token.Punctuation.OpenParen, + Token.Keywords.NameOf, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("vehicle"), + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + Token.Operators.Arrow, + Token.Keywords.Control.Throw, + Token.Keywords.New, + Token.Type("ArgumentException"), + Token.Punctuation.OpenParen, + Token.Identifiers.ParameterName("message"), + Token.Punctuation.Colon, + Token.Punctuation.String.Begin, + Token.Literals.String("Not a known vehicle type"), + Token.Punctuation.String.End, + Token.Punctuation.Comma, + Token.Identifiers.ParameterName("paramName"), + Token.Punctuation.Colon, + Token.Keywords.NameOf, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("vehicle"), + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.Comma, + Token.Punctuation.CloseBrace, + Token.Punctuation.Semicolon + ]); + }); + + it("does not highlight variable names starting with 'switch' (issue #208)", async () => { + const input = Input.InClass(` +private bool switchedOn = false; + +private bool DecodeSwitchString(string switchString) { + var switchOn = false; + if (!this.switchedOn) + { + switchOn = switchString.ToUpper() == "ON"; + } + return switchOn; +}`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Modifiers.Private, + Token.PrimitiveType.Bool, + Token.Identifiers.FieldName("switchedOn"), + Token.Operators.Assignment, + Token.Literals.Boolean.False, + Token.Punctuation.Semicolon, + Token.Keywords.Modifiers.Private, + Token.PrimitiveType.Bool, + Token.Identifiers.MethodName("DecodeSwitchString"), + Token.Punctuation.OpenParen, + Token.PrimitiveType.String, + Token.Identifiers.ParameterName("switchString"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Keywords.Var, + Token.Identifiers.LocalName("switchOn"), + Token.Operators.Assignment, + Token.Literals.Boolean.False, + Token.Punctuation.Semicolon, + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Operators.Logical.Not, + Token.Variables.This, + Token.Punctuation.Accessor, + Token.Variables.Property("switchedOn"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Variables.ReadWrite("switchOn"), + Token.Operators.Assignment, + Token.Variables.Object("switchString"), + Token.Punctuation.Accessor, + Token.Identifiers.MethodName("ToUpper"), + Token.Punctuation.OpenParen, + Token.Punctuation.CloseParen, + Token.Operators.Relational.Equals, + Token.Punctuation.String.Begin, + Token.Literals.String("ON"), + Token.Punctuation.String.End, + Token.Punctuation.Semicolon, + Token.Punctuation.CloseBrace, + Token.Keywords.Control.Return, + Token.Variables.ReadWrite("switchOn"), + Token.Punctuation.Semicolon, + Token.Punctuation.CloseBrace + ]); + }); + }); +}); \ No newline at end of file From 0fce07365678269f0e321922ff7b5b19c5781dd5 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Sun, 13 Aug 2023 23:30:20 +0200 Subject: [PATCH 05/14] WIP add pattern tests --- grammars/csharp.tmLanguage | 62 ++++++++++++++++----------------- grammars/csharp.tmLanguage.cson | 42 +++++++++++----------- test/patterns.tests.ts | 54 +++++++++++++++++++++++++--- 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index d9800d3..905f1c4 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -3163,19 +3163,6 @@ patterns - - begin - (?!\s|\bwhen\b) - end - (?=\bwhen\b|:|}) - patterns - - - include - #pattern - - - begin \b(when)\b @@ -3197,6 +3184,19 @@ + + begin + (?!\s) + end + (?=\bwhen\b|:|}) + patterns + + + include + #pattern + + + switch-expression @@ -3223,16 +3223,28 @@ patterns + + include + #punctuation-comma + begin - (?!\s|\bwhen\b|=>|,) + => + beginCaptures + + 0 + + name + keyword.operator.arrow.cs + + end - (?=\bwhen\b|=>|,|}) + (?=,|}) patterns include - #pattern + #expression @@ -3259,29 +3271,17 @@ begin - => - beginCaptures - - 0 - - name - keyword.operator.arrow.cs - - + (?!\s) end - (?=,|}) + (?=\bwhen\b|=>|,|}) patterns include - #expression + #pattern - - include - #punctuation-comma - case-guard diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 85ded10..72c5344 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -1959,15 +1959,6 @@ repository: "1": name: "punctuation.separator.colon.cs" patterns: [ - { - begin: "(?!\\s|\\bwhen\\b)" - end: "(?=\\bwhen\\b|:|})" - patterns: [ - { - include: "#pattern" - } - ] - } { begin: "\\b(when)\\b" beginCaptures: @@ -1980,6 +1971,15 @@ repository: } ] } + { + begin: "(?!\\s)" + end: "(?=\\bwhen\\b|:|})" + patterns: [ + { + include: "#pattern" + } + ] + } ] "switch-expression": begin: "\\{" @@ -1992,11 +1992,17 @@ repository: name: "punctuation.curlybrace.close.cs" patterns: [ { - begin: "(?!\\s|\\bwhen\\b|=>|,)" - end: "(?=\\bwhen\\b|=>|,|})" + include: "#punctuation-comma" + } + { + begin: "=>" + beginCaptures: + "0": + name: "keyword.operator.arrow.cs" + end: "(?=,|})" patterns: [ { - include: "#pattern" + include: "#expression" } ] } @@ -2013,20 +2019,14 @@ repository: ] } { - begin: "=>" - beginCaptures: - "0": - name: "keyword.operator.arrow.cs" - end: "(?=,|})" + begin: "(?!\\s)" + end: "(?=\\bwhen\\b|=>|,|})" patterns: [ { - include: "#expression" + include: "#pattern" } ] } - { - include: "#punctuation-comma" - } ] "case-guard": patterns: [ diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index 8d73cc5..c950c52 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -4,15 +4,61 @@ import { tokenize, Input, Token } from "./utils/tokenize"; describe("Patterns", () => { before(should); - describe("is pattern", () => { + describe("is operator", () => { + it("relational pattern", async () => { + }); + + it("constant pattern", async () => { + + }); + + it("declaration pattern", async () => { + + }); + + it("var pattern", async () => { + + }); + + it("type pattern", async () => { + + }); + + it("positional pattern", async () => { + + }); + + it("property pattern", async () => { + + }); + + it("discard pattern", async () => { + + }); + + it("list pattern", async () => { + + }); + + it("slice pattern", async () => { + + }); + + it("pattern combinators", async () => { + + }); + + it("parenthesized pattern", async () => { + + }); }); - describe("switch case pattern", () => { + describe("switch statement", () => { }); - describe("switch expression pattern", () => { + describe("switch expression", () => { it("simple", async () => { const input = Input.InClass(` public decimal Calculate(object thing) => @@ -387,7 +433,7 @@ public static decimal ComputeSalesTax(Address location, decimal salePrice) => Token.Literals.Numeric.Decimal("05"), Token.Literals.Numeric.Other.Suffix("M"), Token.Punctuation.Comma, - Token.Comment.LeadingWhitespace(" "), + Token.Comment.LeadingWhitespace(" "), Token.Comment.SingleLine.Start, Token.Comment.SingleLine.Text(" other cases removed for brevity..."), Token.Variables.Discard, From 56a1d5843cf7732916594bef38f39a3ca853fb12 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Mon, 14 Aug 2023 13:23:03 +0000 Subject: [PATCH 06/14] WIP --- grammars/csharp.tmLanguage | 54 +++-- grammars/csharp.tmLanguage.cson | 41 ++-- src/csharp.tmLanguage.yml | 34 +-- test/patterns.tests.ts | 354 ++++++++++++++++++++++++++++++-- 4 files changed, 435 insertions(+), 48 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 905f1c4..23a0fa7 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -427,6 +427,10 @@ include #conditional-operator + + include + #assignment-expression + include #expression-operators @@ -3311,7 +3315,7 @@ end - (?=[)}\];:?=&|^]|!=) + (?=[)}\],;:?=&|^]|!=) patterns @@ -3431,7 +3435,7 @@ end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3453,7 +3457,7 @@ end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3530,14 +3534,14 @@ begin (?=@?[_[:alpha:]][_[:alnum:]]*) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns begin \G end - (?=\s|[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=\s|[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3566,7 +3570,7 @@ begin (?=\s) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3594,7 +3598,7 @@ begin (?=\() end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3634,7 +3638,7 @@ begin (?<=\)) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3658,7 +3662,7 @@ begin (?={) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3698,7 +3702,7 @@ begin (?<=\}) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3756,7 +3760,7 @@ begin (?=\[) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3796,7 +3800,7 @@ begin (?<=\]) end - (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -6156,6 +6160,32 @@ + assignment-expression + + begin + (=)(?!=|>) + beginCaptures + + 1 + + name + keyword.operator.assignment.cs + + + end + (?=[,\)\];}]) + patterns + + + include + #ref-modifier + + + include + #expression + + + expression-operators patterns diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 72c5344..63907e9 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -303,6 +303,9 @@ repository: { include: "#conditional-operator" } + { + include: "#assignment-expression" + } { include: "#expression-operators" } @@ -2042,7 +2045,7 @@ repository: beginCaptures: "1": name: "keyword.other.is.cs" - end: "(?=[)}\\];:?=&|^]|!=)" + end: "(?=[)}\\],;:?=&|^]|!=)" patterns: [ { include: "#pattern" @@ -2119,7 +2122,7 @@ repository: beginCaptures: "0": name: "keyword.operator.relational.cs" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#expression" @@ -2130,7 +2133,7 @@ repository: beginCaptures: "1": name: "keyword.other.var.cs" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#designation-pattern" @@ -2175,11 +2178,11 @@ repository: ] "type-pattern": begin: "(?=@?[_[:alpha:]][_[:alnum:]]*)" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { begin: "\\G" - end: "(?=\\s|[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=\\s|[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#type-builtin" @@ -2200,7 +2203,7 @@ repository: } { begin: "(?=\\s)" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#intrusive" @@ -2219,7 +2222,7 @@ repository: ] "positional-pattern": begin: "(?=\\()" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { begin: "\\(" @@ -2241,7 +2244,7 @@ repository: } { begin: "(?<=\\))" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#intrusive" @@ -2257,7 +2260,7 @@ repository: ] "property-pattern": begin: "(?={)" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { begin: "\\{" @@ -2279,7 +2282,7 @@ repository: } { begin: "(?<=\\})" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#intrusive" @@ -2314,7 +2317,7 @@ repository: ] "list-pattern": begin: "(?=\\[)" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { begin: "\\[" @@ -2336,7 +2339,7 @@ repository: } { begin: "(?<=\\])" - end: "(?=[)}\\];:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#intrusive" @@ -3660,6 +3663,20 @@ repository: endCaptures: "0": name: "punctuation.separator.colon.cs" + "assignment-expression": + begin: "(=)(?!=|>)" + beginCaptures: + "1": + name: "keyword.operator.assignment.cs" + end: "(?=[,\\)\\];}])" + patterns: [ + { + include: "#ref-modifier" + } + { + include: "#expression" + } + ] "expression-operators": patterns: [ { diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index 478c32c..41fd2cc 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -123,6 +123,7 @@ repository: - include: '#switch-statement-or-expression' - include: '#with-expression' - include: '#conditional-operator' + - include: '#assignment-expression' - include: '#expression-operators' - include: '#await-expression' - include: '#query-expression' @@ -1216,7 +1217,7 @@ repository: begin: (?=? beginCaptures: 0: { name: keyword.operator.relational.cs } - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#expression' @@ -1264,7 +1265,7 @@ repository: begin: \b(var)\b beginCaptures: 1: { name: keyword.other.var.cs } - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#designation-pattern' @@ -1290,10 +1291,10 @@ repository: type-pattern: begin: (?=@?[_[:alpha:]][_[:alnum:]]*) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - begin: \G - end: (?=\s|[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=\s|[)}\],;:?=&|^]|!=|\b(and|or|when)\b) # hacky solution - can't have whitespace/comments/preprocessor around dot patterns: # - include: '#intrusive' @@ -1303,7 +1304,7 @@ repository: - include: '#type-array-suffix' - include: '#type-nullable-suffix' - begin: (?=\s) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#intrusive' - include: '#positional-pattern' @@ -1312,7 +1313,7 @@ repository: positional-pattern: begin: (?=\() - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - begin: \( beginCaptures: @@ -1324,7 +1325,7 @@ repository: - include: '#subpattern' - include: '#punctuation-comma' - begin: (?<=\)) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#intrusive' - include: '#property-pattern' @@ -1332,7 +1333,7 @@ repository: property-pattern: begin: (?={) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - begin: \{ beginCaptures: @@ -1344,7 +1345,7 @@ repository: - include: '#subpattern' - include: '#punctuation-comma' - begin: (?<=\}) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#intrusive' - include: '#simple-designation-pattern' @@ -1363,7 +1364,7 @@ repository: list-pattern: begin: (?=\[) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - begin: \[ beginCaptures: @@ -1375,7 +1376,7 @@ repository: - include: '#pattern' - include: '#punctuation-comma' - begin: (?<=\]) - end: (?=[)}\];:?=&|^]|!=|\b(and|or|when)\b) + end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#intrusive' - include: '#simple-designation-pattern' @@ -2327,6 +2328,15 @@ repository: endCaptures: '0': { name: punctuation.separator.colon.cs } + assignment-expression: + begin: (=)(?!=|>) + beginCaptures: + 1: { name: keyword.operator.assignment.cs } + end: (?=[,\)\];}]) + patterns: + - include: '#ref-modifier' + - include: '#expression' + expression-operators: patterns: - name: keyword.operator.assignment.compound.cs diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index c950c52..a4a1bc0 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -5,51 +5,381 @@ describe("Patterns", () => { before(should); describe("is operator", () => { - it("relational pattern", async () => { + it("Discard pattern", async () => { + const input = Input.InMethod(` +if (var is _) { } +`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Variables.Discard, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); }); - it("constant pattern", async () => { + it("Constant pattern", async () => { + const input = Input.InMethod(` +if (var is 0) { } +if (var is null) { } +if (var is "") { } +if (var is true) { } +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Literals.Null, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Punctuation.String.Begin, + Token.Punctuation.String.End, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Literals.Boolean.True, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); }); - it("declaration pattern", async () => { + it("Relational pattern", async () => { + const input = Input.InMethod(` +if (var is > 0) { } +if (var is < 0) { } +if (var is >= 0) { } +if (var is <= 0) { } +`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Operators.Relational.LessThanOrEqual, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); }); - it("var pattern", async () => { + it("Declaration pattern", async () => { + const input = Input.InMethod(` +if (var is string str) { } +if (var is List list) { } +if (var is int[,] arr) { } +if (var is Dictionary> dict) { } +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.PrimitiveType.String, + Token.Identifiers.LocalName("str"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameters.End, + Token.Identifiers.LocalName("list"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.PrimitiveType.Int, + Token.Punctuation.OpenBracket, + Token.Punctuation.Comma, + Token.Punctuation.CloseBracket, + Token.Identifiers.LocalName("arr"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Type("Dictionary"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.String, + Token.Punctuation.Comma, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.TypeParameters.End, + Token.Identifiers.LocalName("dict"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); }); - it("type pattern", async () => { + it("var pattern", async () => { + const input = Input.InMethod(` +if (var is var var) { } +if (var is var (var, var)) { } +if (var is var _) { } +if (var is var (var, _)) { } +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Keywords.Var, + Token.Identifiers.LocalName("var"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("var"), + Token.Punctuation.Comma, + Token.Identifiers.LocalName("var"), + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Keywords.Var, + Token.Variables.Discard, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("var"), + Token.Keywords.Is, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("var"), + Token.Punctuation.Comma, + Token.Variables.Discard, + Token.Punctuation.CloseParen, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); }); - it("positional pattern", async () => { + it("Type pattern", async () => { + const input = Input.InMethod(` +result = obj is string; +result = obj is List; +result = obj is int[,]; +result = obj is Dictionary>; +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("obj"), + Token.Keywords.Is, + Token.PrimitiveType.String, + Token.Punctuation.Semicolon, + + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("obj"), + Token.Keywords.Is, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.Semicolon, + + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("obj"), + Token.Keywords.Is, + Token.PrimitiveType.Int, + Token.Punctuation.OpenBracket, + Token.Punctuation.Comma, + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon, + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("obj"), + Token.Keywords.Is, + Token.Type("Dictionary"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.String, + Token.Punctuation.Comma, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.Semicolon, + ]); }); - it("property pattern", async () => { + it("Positional sub-pattern", async () => { + const input = Input.InMethod(` +result = (a, b, c, d, e) is +( + _, + null, + c: > 3, + string str, + e: List, +); +`); + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("a"), + Token.Punctuation.Comma, + Token.Variables.ReadWrite("b"), + Token.Punctuation.Comma, + Token.Variables.ReadWrite("c"), + Token.Punctuation.Comma, + Token.Variables.ReadWrite("d"), + Token.Punctuation.Comma, + Token.Variables.ReadWrite("e"), + Token.Punctuation.CloseParen, + Token.Keywords.Is, + Token.Punctuation.OpenParen, + + Token.Variables.Discard, + Token.Punctuation.Comma, + + Token.Literals.Null, + Token.Punctuation.Comma, + + Token.Variables.Property("c"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Punctuation.Comma, + + Token.PrimitiveType.String, + Token.Identifiers.LocalName("str"), + Token.Punctuation.Comma, + + Token.Variables.Property("e"), + Token.Punctuation.Colon, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Object, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.Comma, + + Token.Punctuation.CloseParen, + Token.Punctuation.Semicolon, + ]); }); - it("discard pattern", async () => { + it("Property pattern", async () => { }); - it("list pattern", async () => { + it("List pattern", async () => { }); - it("slice pattern", async () => { + it("Slice pattern", async () => { }); - it("pattern combinators", async () => { + it("Pattern combinators", async () => { }); - it("parenthesized pattern", async () => { + it("Parenthesized pattern", async () => { }); }); From 62aaff9c11608c5a0415e8f356b773f990a7e5d1 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Mon, 14 Aug 2023 13:36:34 +0000 Subject: [PATCH 07/14] Allow only expressions after assignments otherwise x = obj is ... treats obj is as a local variable declaration of type 'obj' and name 'is' --- grammars/csharp.tmLanguage | 11 ++++++++--- grammars/csharp.tmLanguage.cson | 8 ++++++-- src/csharp.tmLanguage.yml | 6 ++++-- test/patterns.tests.ts | 6 +++--- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 23a0fa7..3844bec 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -6163,13 +6163,18 @@ assignment-expression begin - (=)(?!=|>) + (\*=|/=|%=|\+=|-=|\?\?=|\&=|\^=|<<=|>>>?=|\|=|=)(?!=|>) beginCaptures 1 - name - keyword.operator.assignment.cs + patterns + + + include + #expression-operators + + end diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 63907e9..2d08e9d 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -3664,10 +3664,14 @@ repository: "0": name: "punctuation.separator.colon.cs" "assignment-expression": - begin: "(=)(?!=|>)" + begin: "(\\*=|/=|%=|\\+=|-=|\\?\\?=|\\&=|\\^=|<<=|>>>?=|\\|=|=)(?!=|>)" beginCaptures: "1": - name: "keyword.operator.assignment.cs" + patterns: [ + { + include: "#expression-operators" + } + ] end: "(?=[,\\)\\];}])" patterns: [ { diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index 41fd2cc..97dc9c3 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -2329,9 +2329,11 @@ repository: '0': { name: punctuation.separator.colon.cs } assignment-expression: - begin: (=)(?!=|>) + begin: (\*=|/=|%=|\+=|-=|\?\?=|\&=|\^=|<<=|>>>?=|\|=|=)(?!=|>) beginCaptures: - 1: { name: keyword.operator.assignment.cs } + 1: + patterns: + - include: '#expression-operators' end: (?=[,\)\];}]) patterns: - include: '#ref-modifier' diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index a4a1bc0..aeb395c 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -339,13 +339,13 @@ result = (a, b, c, d, e) is Token.Literals.Null, Token.Punctuation.Comma, - + Token.Variables.Property("c"), Token.Punctuation.Colon, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("3"), Token.Punctuation.Comma, - + Token.PrimitiveType.String, Token.Identifiers.LocalName("str"), Token.Punctuation.Comma, @@ -1414,4 +1414,4 @@ private bool DecodeSwitchString(string switchString) { ]); }); }); -}); \ No newline at end of file +}); From 994e129c88d90e42f5e495fe66c426d8f9cc81f3 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 14 Aug 2023 17:50:18 -0700 Subject: [PATCH 08/14] Add additional tests for patterns --- test/patterns.tests.ts | 417 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index aeb395c..abb3913 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -364,28 +364,445 @@ result = (a, b, c, d, e) is }); it("Property pattern", async () => { + const input = Input.InMethod(` +if (o is string { Length: 5 } s) { } +if (f is System.Diagnostics.FileVersionInfo { IsPreRelease: false, FileName.Length: >3 and <10 }) { } +if (v is System.Version { Major: >= 10 }) { } +` + ); + + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("o"), + Token.Keywords.Is, + Token.PrimitiveType.String, + Token.Punctuation.OpenBrace, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Literals.Numeric.Decimal("5"), + Token.Punctuation.CloseBrace, + Token.Identifiers.LocalName("s"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("f"), + Token.Keywords.Is, + Token.Type("System"), + Token.Punctuation.Accessor, + Token.Type("Diagnostics"), + Token.Punctuation.Accessor, + Token.Type("FileVersionInfo"), + Token.Punctuation.OpenBrace, + Token.Variables.Property("IsPreRelease"), + Token.Punctuation.Colon, + Token.Literals.Boolean.False, + Token.Punctuation.Comma, + Token.Variables.Property("FileName"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Operators.Word.And, + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("10"), + Token.Punctuation.CloseBrace, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("v"), + Token.Keywords.Is, + Token.Type("System"), + Token.Punctuation.Accessor, + Token.Type("Version"), + Token.Punctuation.OpenBrace, + Token.Variables.Property("Major"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Literals.Numeric.Decimal("10"), + Token.Punctuation.CloseBrace, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); }); it("List pattern", async () => { + const input = Input.InMethod(` +result = array is [1, 2, 3]; +` + ); + + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("array"), + Token.Keywords.Is, + Token.Punctuation.OpenBracket, + Token.Literals.Numeric.Decimal("1"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("2"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("3"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon, + ]); }); it("Slice pattern", async () => { + const input = Input.InMethod(` +result = array is [_, 1, ..]; +` + ); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("array"), + Token.Keywords.Is, + Token.Punctuation.OpenBracket, + Token.Variables.Discard, + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("1"), + Token.Punctuation.Comma, + Token.Operators.Range, + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon, + ]); }); it("Pattern combinators", async () => { + const input = Input.InMethod(` +result = c is + >= 'a' and + <= 'z' or + >= 'A' and + <= 'Z'; +` + ); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("c"), + Token.Keywords.Is, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("a"), + Token.Punctuation.Char.End, + Token.Operators.Word.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("z"), + Token.Punctuation.Char.End, + Token.Operators.Word.Or, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("A"), + Token.Punctuation.Char.End, + Token.Operators.Word.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("Z"), + Token.Punctuation.Char.End, + Token.Punctuation.Semicolon, + ]); }); it("Parenthesized pattern", async () => { + const input = Input.InMethod(` +result = c is + (>= 'a' and <= 'z') or + (>= 'A' and <= 'Z'); +` + ); + + const tokens = await tokenize(input); + tokens.should.deep.equal([ + Token.Variables.ReadWrite("result"), + Token.Operators.Assignment, + Token.Variables.ReadWrite("c"), + Token.Keywords.Is, + Token.Punctuation.OpenParen, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("a"), + Token.Punctuation.Char.End, + Token.Operators.Word.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("z"), + Token.Punctuation.Char.End, + Token.Punctuation.CloseParen, + Token.Operators.Word.Or, + Token.Punctuation.OpenParen, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("A"), + Token.Punctuation.Char.End, + Token.Operators.Word.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("Z"), + Token.Punctuation.Char.End, + Token.Punctuation.CloseParen, + Token.Punctuation.Semicolon, + ]); }); }); describe("switch statement", () => { + it("supports patterns", async () => { + const input = Input.InMethod(` +switch (a) +{ + case string str when str.Length is >3 and not >8: break; + case "" or null or 0: break; + case { Length: >8 or <=2 }: break; + case (first: int, { A: not null, B.C: string }): break; + case _: break; +} +` + ); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.Switch, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("a"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Keywords.Control.Case, + Token.PrimitiveType.String, + Token.Identifiers.LocalName("str"), + Token.Keywords.Control.When, + Token.Variables.Object("str"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), + Token.Keywords.Is, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Operators.Word.And, + Token.Operators.Word.Not, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("8"), + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Punctuation.String.Begin, + Token.Punctuation.String.End, + Token.Operators.Word.Or, + Token.Literals.Null, + Token.Operators.Word.Or, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Punctuation.OpenBrace, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("8"), + Token.Operators.Word.Or, + Token.Operators.Relational.LessThanOrEqual, + Token.Literals.Numeric.Decimal("2"), + Token.Punctuation.CloseBrace, + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Punctuation.OpenParen, + Token.Variables.Property("first"), + Token.Punctuation.Colon, + Token.PrimitiveType.Int, + Token.Punctuation.Comma, + Token.Punctuation.OpenBrace, + Token.Variables.Property("A"), + Token.Punctuation.Colon, + Token.Operators.Word.Not, + Token.Literals.Null, + Token.Punctuation.Comma, + Token.Variables.Property("B"), + Token.Punctuation.Accessor, + Token.Variables.Property("C"), + Token.Punctuation.Colon, + Token.PrimitiveType.String, + Token.Punctuation.CloseBrace, + Token.Punctuation.CloseParen, + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Variables.Discard, + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Punctuation.CloseBrace, + ]); + }); + + it("handles line breaks", async () => { + const input = Input.InMethod(` +switch (a) +{ + case + string + str + when + str + .Length + is + > + 3 + and + not + > + 8 + : break; + case + "" + or + null + or + 0 + : break; + case + { + Length: + > + 8 + or + <= + 2 + } + : break; + case + ( + first: + int, + { + A: + not + null, + B.C: + string + } + ) + : break; + case + _ + : break; +} +` + ); + + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.Switch, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("a"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Keywords.Control.Case, + Token.PrimitiveType.String, + Token.Identifiers.LocalName("str"), + Token.Keywords.Control.When, + Token.Variables.ReadWrite("str"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), + Token.Keywords.Is, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Operators.Word.And, + Token.Operators.Word.Not, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("8"), + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Punctuation.String.Begin, + Token.Punctuation.String.End, + Token.Operators.Word.Or, + Token.Literals.Null, + Token.Operators.Word.Or, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Punctuation.OpenBrace, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("8"), + Token.Operators.Word.Or, + Token.Operators.Relational.LessThanOrEqual, + Token.Literals.Numeric.Decimal("2"), + Token.Punctuation.CloseBrace, + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Punctuation.OpenParen, + Token.Variables.Property("first"), + Token.Punctuation.Colon, + Token.PrimitiveType.Int, + Token.Punctuation.Comma, + Token.Punctuation.OpenBrace, + Token.Variables.Property("A"), + Token.Punctuation.Colon, + Token.Operators.Word.Not, + Token.Literals.Null, + Token.Punctuation.Comma, + Token.Variables.Property("B"), + Token.Punctuation.Accessor, + Token.Variables.Property("C"), + Token.Punctuation.Colon, + Token.PrimitiveType.String, + Token.Punctuation.CloseBrace, + Token.Punctuation.CloseParen, + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Keywords.Control.Case, + Token.Variables.Discard, + Token.Punctuation.Colon, + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, + + Token.Punctuation.CloseBrace, + ]); + }); }); describe("switch expression", () => { From ad11f972f3a31d97b4324c3e04e0a95648268820 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Wed, 16 Aug 2023 12:25:48 +0000 Subject: [PATCH 09/14] Add test pattern tokens --- test/expressions.tests.ts | 4 ++-- test/utils/tokenize.ts | 10 +++++----- test/verbatim-indentifier.tests.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/expressions.tests.ts b/test/expressions.tests.ts index 0105367..63ddbe2 100644 --- a/test/expressions.tests.ts +++ b/test/expressions.tests.ts @@ -1645,7 +1645,7 @@ var x = new // comment Token.Identifiers.LocalName("x"), Token.Operators.Assignment, Token.Variables.ReadWrite("o"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("List"), Token.Punctuation.TypeParameters.Begin, Token.Type("Lazy"), @@ -1668,7 +1668,7 @@ var x = new // comment Token.Identifiers.MethodName("M"), Token.Punctuation.OpenParen, Token.Punctuation.CloseParen, - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("List"), Token.Punctuation.TypeParameters.Begin, Token.Type("Lazy"), diff --git a/test/utils/tokenize.ts b/test/utils/tokenize.ts index c4c4af5..52f1229 100644 --- a/test/utils/tokenize.ts +++ b/test/utils/tokenize.ts @@ -366,7 +366,6 @@ export namespace Token { export const Implicit = createToken('implicit', 'keyword.other.implicit.cs'); export const Init = createToken('init', 'keyword.other.init.cs'); export const Interface = createToken('interface', 'keyword.other.interface.cs'); - export const Is = createToken('is', 'keyword.other.is.cs'); export const Lock = createToken('lock', 'keyword.other.lock.cs'); export const NameOf = createToken('nameof', 'keyword.other.nameof.cs'); export const Namespace = createToken('namespace', 'keyword.other.namespace.cs'); @@ -482,10 +481,11 @@ export namespace Token { export const GreaterThanOrEqual = createToken('>=', 'keyword.operator.relational.cs'); } - export namespace Word { - export const And = createToken('and', 'keyword.operator.word.and.cs'); - export const Not = createToken('not', 'keyword.operator.word.not.cs'); - export const Or = createToken('or', 'keyword.operator.word.or.cs'); + export namespace Pattern { + export const And = createToken('and', 'keyword.operator.word.pattern.combinator.and.cs'); + export const Is = createToken('is', 'keyword.operator.word.pattern.is.cs'); + export const Not = createToken('not', 'keyword.operator.word.pattern.combinator.not.cs'); + export const Or = createToken('or', 'keyword.operator.word.pattern.combinator.or.cs'); } export const Arrow = createToken('=>', 'keyword.operator.arrow.cs'); diff --git a/test/verbatim-indentifier.tests.ts b/test/verbatim-indentifier.tests.ts index 17ace50..5501f04 100644 --- a/test/verbatim-indentifier.tests.ts +++ b/test/verbatim-indentifier.tests.ts @@ -580,7 +580,7 @@ const string @class = obj.@class;`); Token.Identifiers.LocalName("x"), Token.Operators.Assignment, Token.Variables.ReadWrite("@variable"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("@class"), Token.Punctuation.Semicolon ]); From b92d1ce184a6f975433fa57105bec89bd6174836 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Wed, 16 Aug 2023 12:26:03 +0000 Subject: [PATCH 10/14] WIP add more pattern tests --- grammars/csharp.tmLanguage | 4 +- grammars/csharp.tmLanguage.cson | 4 +- src/csharp.tmLanguage.yml | 4 +- test/patterns.tests.ts | 322 +++++++++++++++++++------------- 4 files changed, 200 insertions(+), 134 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 3844bec..6bfbfea 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -3311,7 +3311,7 @@ 1 name - keyword.other.is.cs + keyword.operator.word.pattern.is.cs end @@ -3379,7 +3379,7 @@ match \b(and|or|not)\b name - keyword.operator.word.$1.cs + keyword.operator.word.pattern.combinator.$1.cs discard-pattern diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 2d08e9d..f0e986c 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2044,7 +2044,7 @@ repository: begin: "(?> dict) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.PrimitiveType.String, Token.Identifiers.LocalName("str"), Token.Punctuation.CloseParen, @@ -147,7 +147,7 @@ if (var is Dictionary> dict) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("List"), Token.Punctuation.TypeParameters.Begin, Token.PrimitiveType.Int, @@ -160,7 +160,7 @@ if (var is Dictionary> dict) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.PrimitiveType.Int, Token.Punctuation.OpenBracket, Token.Punctuation.Comma, @@ -173,7 +173,7 @@ if (var is Dictionary> dict) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("Dictionary"), Token.Punctuation.TypeParameters.Begin, Token.PrimitiveType.String, @@ -203,7 +203,7 @@ if (var is var (var, _)) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Keywords.Var, Token.Identifiers.LocalName("var"), Token.Punctuation.CloseParen, @@ -213,7 +213,7 @@ if (var is var (var, _)) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Keywords.Var, Token.Punctuation.OpenParen, Token.Identifiers.LocalName("var"), @@ -227,7 +227,7 @@ if (var is var (var, _)) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Keywords.Var, Token.Variables.Discard, Token.Punctuation.CloseParen, @@ -237,7 +237,7 @@ if (var is var (var, _)) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("var"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Keywords.Var, Token.Punctuation.OpenParen, Token.Identifiers.LocalName("var"), @@ -263,14 +263,14 @@ result = obj is Dictionary>; Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("obj"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.PrimitiveType.String, Token.Punctuation.Semicolon, Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("obj"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("List"), Token.Punctuation.TypeParameters.Begin, Token.PrimitiveType.Int, @@ -280,7 +280,7 @@ result = obj is Dictionary>; Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("obj"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.PrimitiveType.Int, Token.Punctuation.OpenBracket, Token.Punctuation.Comma, @@ -290,7 +290,7 @@ result = obj is Dictionary>; Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("obj"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("Dictionary"), Token.Punctuation.TypeParameters.Begin, Token.PrimitiveType.String, @@ -331,7 +331,7 @@ result = (a, b, c, d, e) is Token.Punctuation.Comma, Token.Variables.ReadWrite("e"), Token.Punctuation.CloseParen, - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Punctuation.OpenParen, Token.Variables.Discard, @@ -363,21 +363,86 @@ result = (a, b, c, d, e) is ]); }); + it("Positional pattern", async () => { + const input = Input.InMethod(` +if (x is Foo.Bar (int, not null) { Data.Length: > 0 } y) { } +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("x"), + Token.Operators.Pattern.Is, + Token.Type("Foo"), + Token.Punctuation.Accessor, + Token.Type("Bar"), + Token.Punctuation.OpenParen, + Token.PrimitiveType.Int, + Token.Punctuation.Comma, + Token.Operators.Pattern.Not, + Token.Literals.Null, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Variables.Property("Data"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseBrace, + Token.Identifiers.LocalName("y"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); + }); + + it("Property sub-pattern", async () => { + const input = Input.InMethod(` +if (f is { IsPreRelease: false, FileName.Length: > 3 and < 10 }) { } +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("f"), + Token.Operators.Pattern.Is, + Token.Punctuation.OpenBrace, + Token.Variables.Property("IsPreRelease"), + Token.Punctuation.Colon, + Token.Literals.Boolean.False, + Token.Punctuation.Comma, + Token.Variables.Property("FileName"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Operators.Pattern.And, + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("10"), + Token.Punctuation.CloseBrace, + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, + ]); + }); + it("Property pattern", async () => { const input = Input.InMethod(` if (o is string { Length: 5 } s) { } -if (f is System.Diagnostics.FileVersionInfo { IsPreRelease: false, FileName.Length: >3 and <10 }) { } +if (f is System.Diagnostics.FileVersionInfo { IsPreRelease: false, FileName.Length: > 3 and < 10 }) { } if (v is System.Version { Major: >= 10 }) { } -` - ); - +`); const tokens = await tokenize(input); tokens.should.deep.equal([ Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("o"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.PrimitiveType.String, Token.Punctuation.OpenBrace, Token.Variables.Property("Length"), @@ -392,7 +457,7 @@ if (v is System.Version { Major: >= 10 }) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("f"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("System"), Token.Punctuation.Accessor, Token.Type("Diagnostics"), @@ -409,7 +474,7 @@ if (v is System.Version { Major: >= 10 }) { } Token.Punctuation.Colon, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("3"), - Token.Operators.Word.And, + Token.Operators.Pattern.And, Token.Operators.Relational.LessThan, Token.Literals.Numeric.Decimal("10"), Token.Punctuation.CloseBrace, @@ -420,7 +485,7 @@ if (v is System.Version { Major: >= 10 }) { } Token.Keywords.Control.If, Token.Punctuation.OpenParen, Token.Variables.ReadWrite("v"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Type("System"), Token.Punctuation.Accessor, Token.Type("Version"), @@ -438,17 +503,15 @@ if (v is System.Version { Major: >= 10 }) { } it("List pattern", async () => { const input = Input.InMethod(` -result = array is [1, 2, 3]; -` - ); - +if (array is [1, 2, 3] x) { } +`); const tokens = await tokenize(input); tokens.should.deep.equal([ - Token.Variables.ReadWrite("result"), - Token.Operators.Assignment, + Token.Keywords.Control.If, + Token.Punctuation.OpenParen, Token.Variables.ReadWrite("array"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Punctuation.OpenBracket, Token.Literals.Numeric.Decimal("1"), Token.Punctuation.Comma, @@ -456,29 +519,36 @@ result = array is [1, 2, 3]; Token.Punctuation.Comma, Token.Literals.Numeric.Decimal("3"), Token.Punctuation.CloseBracket, - Token.Punctuation.Semicolon, + Token.Identifiers.LocalName("x"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace, ]); }); it("Slice pattern", async () => { const input = Input.InMethod(` -result = array is [_, 1, ..]; -` - ); - +result = array is [_, 1, ..{ Length: > 0 }]; +`); const tokens = await tokenize(input); tokens.should.deep.equal([ Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("array"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Punctuation.OpenBracket, Token.Variables.Discard, Token.Punctuation.Comma, Token.Literals.Numeric.Decimal("1"), Token.Punctuation.Comma, Token.Operators.Range, + Token.Punctuation.OpenBrace, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseBrace, Token.Punctuation.CloseBracket, Token.Punctuation.Semicolon, ]); @@ -491,31 +561,29 @@ result = c is <= 'z' or >= 'A' and <= 'Z'; -` - ); - +`); const tokens = await tokenize(input); tokens.should.deep.equal([ Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("c"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Operators.Relational.GreaterThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("a"), Token.Punctuation.Char.End, - Token.Operators.Word.And, + Token.Operators.Pattern.And, Token.Operators.Relational.LessThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("z"), Token.Punctuation.Char.End, - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Operators.Relational.GreaterThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("A"), Token.Punctuation.Char.End, - Token.Operators.Word.And, + Token.Operators.Pattern.And, Token.Operators.Relational.LessThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("Z"), @@ -529,34 +597,32 @@ result = c is result = c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z'); -` - ); - +`); const tokens = await tokenize(input); tokens.should.deep.equal([ Token.Variables.ReadWrite("result"), Token.Operators.Assignment, Token.Variables.ReadWrite("c"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Punctuation.OpenParen, Token.Operators.Relational.GreaterThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("a"), Token.Punctuation.Char.End, - Token.Operators.Word.And, + Token.Operators.Pattern.And, Token.Operators.Relational.LessThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("z"), Token.Punctuation.Char.End, Token.Punctuation.CloseParen, - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Punctuation.OpenParen, Token.Operators.Relational.GreaterThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("A"), Token.Punctuation.Char.End, - Token.Operators.Word.And, + Token.Operators.Pattern.And, Token.Operators.Relational.LessThanOrEqual, Token.Punctuation.Char.Begin, Token.Literals.Char("Z"), @@ -568,19 +634,21 @@ result = c is }); describe("switch statement", () => { - it("supports patterns", async () => { + it("Supports patterns", async () => { + const input = Input.InMethod(` +`); + }) + + it("when clause", async () => { const input = Input.InMethod(` switch (a) { - case string str when str.Length is >3 and not >8: break; + case string str when str.Length is > 3 and not > 8: break; case "" or null or 0: break; - case { Length: >8 or <=2 }: break; + case { Length: > 8 or <= 2 }: break; case (first: int, { A: not null, B.C: string }): break; case _: break; -} -` - ); - +}`); const tokens = await tokenize(input); tokens.should.deep.equal([ @@ -596,11 +664,11 @@ switch (a) Token.Variables.Object("str"), Token.Punctuation.Accessor, Token.Variables.Property("Length"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("3"), - Token.Operators.Word.And, - Token.Operators.Word.Not, + Token.Operators.Pattern.And, + Token.Operators.Pattern.Not, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("8"), Token.Punctuation.Colon, @@ -610,9 +678,9 @@ switch (a) Token.Keywords.Control.Case, Token.Punctuation.String.Begin, Token.Punctuation.String.End, - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Literals.Null, - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Literals.Numeric.Decimal("0"), Token.Punctuation.Colon, Token.Keywords.Control.Break, @@ -624,7 +692,7 @@ switch (a) Token.Punctuation.Colon, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("8"), - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Operators.Relational.LessThanOrEqual, Token.Literals.Numeric.Decimal("2"), Token.Punctuation.CloseBrace, @@ -641,7 +709,7 @@ switch (a) Token.Punctuation.OpenBrace, Token.Variables.Property("A"), Token.Punctuation.Colon, - Token.Operators.Word.Not, + Token.Operators.Pattern.Not, Token.Literals.Null, Token.Punctuation.Comma, Token.Variables.Property("B"), @@ -669,57 +737,54 @@ switch (a) const input = Input.InMethod(` switch (a) { - case - string - str - when - str - .Length - is - > - 3 - and - not - > - 8 - : break; - case - "" - or - null - or - 0 - : break; - case + case + string + str + when + str + .Length + is + > + 3 + and + not + > + 8 + : break; + case + "" + or + null + or + 0 + : break; + case + { + Length: + > + 8 + or + <= + 2 + } + : break; + case + ( + first: + int, { - Length: - > - 8 - or - <= - 2 - } - : break; - case - ( - first: - int, - { - A: - not - null, - B.C: - string - } - ) - : break; - case - _ - : break; -} -` - ); - + A: + not + null, + B.C: + string + } + ) + : break; + case + _ + : break; +}`); const tokens = await tokenize(input); tokens.should.deep.equal([ @@ -728,6 +793,7 @@ switch (a) Token.Variables.ReadWrite("a"), Token.Punctuation.CloseParen, Token.Punctuation.OpenBrace, + Token.Keywords.Control.Case, Token.PrimitiveType.String, Token.Identifiers.LocalName("str"), @@ -735,11 +801,11 @@ switch (a) Token.Variables.ReadWrite("str"), Token.Punctuation.Accessor, Token.Variables.Property("Length"), - Token.Keywords.Is, + Token.Operators.Pattern.Is, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("3"), - Token.Operators.Word.And, - Token.Operators.Word.Not, + Token.Operators.Pattern.And, + Token.Operators.Pattern.Not, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("8"), Token.Punctuation.Colon, @@ -749,9 +815,9 @@ switch (a) Token.Keywords.Control.Case, Token.Punctuation.String.Begin, Token.Punctuation.String.End, - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Literals.Null, - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Literals.Numeric.Decimal("0"), Token.Punctuation.Colon, Token.Keywords.Control.Break, @@ -763,7 +829,7 @@ switch (a) Token.Punctuation.Colon, Token.Operators.Relational.GreaterThan, Token.Literals.Numeric.Decimal("8"), - Token.Operators.Word.Or, + Token.Operators.Pattern.Or, Token.Operators.Relational.LessThanOrEqual, Token.Literals.Numeric.Decimal("2"), Token.Punctuation.CloseBrace, @@ -780,7 +846,7 @@ switch (a) Token.Punctuation.OpenBrace, Token.Variables.Property("A"), Token.Punctuation.Colon, - Token.Operators.Word.Not, + Token.Operators.Pattern.Not, Token.Literals.Null, Token.Punctuation.Comma, Token.Variables.Property("B"), From abb21ef056933ab66688390bf06ff5c5fe3a3d90 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Wed, 16 Aug 2023 12:46:22 +0000 Subject: [PATCH 11/14] Add switch statement pattern tests --- test/patterns.tests.ts | 264 +++++++++++++++++++++++++++++++++-------- 1 file changed, 214 insertions(+), 50 deletions(-) diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index fa9b57a..6f64c5e 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -313,8 +313,7 @@ result = (a, b, c, d, e) is c: > 3, string str, e: List, -); -`); +);`); const tokens = await tokenize(input); tokens.should.deep.equal([ @@ -634,97 +633,262 @@ result = c is }); describe("switch statement", () => { - it("Supports patterns", async () => { - const input = Input.InMethod(` -`); - }) - - it("when clause", async () => { + it("Supports all patterns", async () => { const input = Input.InMethod(` -switch (a) +switch (allPatterns) { - case string str when str.Length is > 3 and not > 8: break; - case "" or null or 0: break; - case { Length: > 8 or <= 2 }: break; - case (first: int, { A: not null, B.C: string }): break; - case _: break; + case (>= 'a' and <= 'z') or (>= 'A' and <= 'Z'): + case >= 0 and <= 9 or >= 20 and <= 29: + case [_, 1, ..{ Length: > 0 }]: + case [1, 2, 3] x: + case string { Length: 5 } s: + case { IsPreRelease: false, FileName.Length: > 3 and < 10 }: + case Foo.Bar (int, not null) { Data.Length: > 0 } y: + case (_, null, c: > 3, string str, e: List, ): + case Dictionary>: + case int[,] arr: + case > 0: + case null: + case _: + break; }`); const tokens = await tokenize(input); tokens.should.deep.equal([ Token.Keywords.Control.Switch, Token.Punctuation.OpenParen, - Token.Variables.ReadWrite("a"), + Token.Variables.ReadWrite("allPatterns"), Token.Punctuation.CloseParen, Token.Punctuation.OpenBrace, + Token.Keywords.Control.Case, - Token.PrimitiveType.String, - Token.Identifiers.LocalName("str"), - Token.Keywords.Control.When, - Token.Variables.Object("str"), - Token.Punctuation.Accessor, - Token.Variables.Property("Length"), - Token.Operators.Pattern.Is, - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("3"), + Token.Punctuation.OpenParen, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("a"), + Token.Punctuation.Char.End, Token.Operators.Pattern.And, - Token.Operators.Pattern.Not, - Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("8"), + Token.Operators.Relational.LessThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("z"), + Token.Punctuation.Char.End, + Token.Punctuation.CloseParen, + Token.Operators.Pattern.Or, + Token.Punctuation.OpenParen, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("A"), + Token.Punctuation.Char.End, + Token.Operators.Pattern.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Punctuation.Char.Begin, + Token.Literals.Char("Z"), + Token.Punctuation.Char.End, + Token.Punctuation.CloseParen, Token.Punctuation.Colon, - Token.Keywords.Control.Break, - Token.Punctuation.Semicolon, Token.Keywords.Control.Case, - Token.Punctuation.String.Begin, - Token.Punctuation.String.End, - Token.Operators.Pattern.Or, - Token.Literals.Null, - Token.Operators.Pattern.Or, + Token.Operators.Relational.GreaterThanOrEqual, Token.Literals.Numeric.Decimal("0"), + Token.Operators.Pattern.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Literals.Numeric.Decimal("9"), + Token.Operators.Pattern.Or, + Token.Operators.Relational.GreaterThanOrEqual, + Token.Literals.Numeric.Decimal("20"), + Token.Operators.Pattern.And, + Token.Operators.Relational.LessThanOrEqual, + Token.Literals.Numeric.Decimal("29"), Token.Punctuation.Colon, - Token.Keywords.Control.Break, - Token.Punctuation.Semicolon, Token.Keywords.Control.Case, + Token.Punctuation.OpenBracket, + Token.Variables.Discard, + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("1"), + Token.Punctuation.Comma, + Token.Operators.Range, Token.Punctuation.OpenBrace, Token.Variables.Property("Length"), Token.Punctuation.Colon, Token.Operators.Relational.GreaterThan, - Token.Literals.Numeric.Decimal("8"), - Token.Operators.Pattern.Or, - Token.Operators.Relational.LessThanOrEqual, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseBrace, + Token.Punctuation.CloseBracket, + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Punctuation.OpenBracket, + Token.Literals.Numeric.Decimal("1"), + Token.Punctuation.Comma, Token.Literals.Numeric.Decimal("2"), + Token.Punctuation.Comma, + Token.Literals.Numeric.Decimal("3"), + Token.Punctuation.CloseBracket, + Token.Identifiers.LocalName("x"), + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.PrimitiveType.String, + Token.Punctuation.OpenBrace, + Token.Variables.Property("Length"), + Token.Punctuation.Colon, + Token.Literals.Numeric.Decimal("5"), Token.Punctuation.CloseBrace, + Token.Identifiers.LocalName("s"), Token.Punctuation.Colon, - Token.Keywords.Control.Break, - Token.Punctuation.Semicolon, Token.Keywords.Control.Case, - Token.Punctuation.OpenParen, - Token.Variables.Property("first"), + Token.Punctuation.OpenBrace, + Token.Variables.Property("IsPreRelease"), + Token.Punctuation.Colon, + Token.Literals.Boolean.False, + Token.Punctuation.Comma, + Token.Variables.Property("FileName"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Operators.Pattern.And, + Token.Operators.Relational.LessThan, + Token.Literals.Numeric.Decimal("10"), + Token.Punctuation.CloseBrace, + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Type("Foo"), + Token.Punctuation.Accessor, + Token.Type("Bar"), + Token.Punctuation.OpenParen, Token.PrimitiveType.Int, Token.Punctuation.Comma, + Token.Operators.Pattern.Not, + Token.Literals.Null, + Token.Punctuation.CloseParen, Token.Punctuation.OpenBrace, - Token.Variables.Property("A"), + Token.Variables.Property("Data"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), Token.Punctuation.Colon, - Token.Operators.Pattern.Not, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.CloseBrace, + Token.Identifiers.LocalName("y"), + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Punctuation.OpenParen, + Token.Variables.Discard, + Token.Punctuation.Comma, Token.Literals.Null, Token.Punctuation.Comma, - Token.Variables.Property("B"), - Token.Punctuation.Accessor, - Token.Variables.Property("C"), + Token.Variables.Property("c"), Token.Punctuation.Colon, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Punctuation.Comma, Token.PrimitiveType.String, + Token.Identifiers.LocalName("str"), + Token.Punctuation.Comma, + Token.Variables.Property("e"), + Token.Punctuation.Colon, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Object, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.Comma, + Token.Punctuation.CloseParen, + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Type("Dictionary"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.String, + Token.Punctuation.Comma, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.PrimitiveType.Int, + Token.Punctuation.OpenBracket, + Token.Punctuation.Comma, + Token.Punctuation.CloseBracket, + Token.Identifiers.LocalName("arr"), + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("0"), + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Literals.Null, + Token.Punctuation.Colon, + + Token.Keywords.Control.Case, + Token.Variables.Discard, + Token.Punctuation.Colon, + + Token.Keywords.Control.Break, + Token.Punctuation.Semicolon, Token.Punctuation.CloseBrace, + ]); + }) + + it("when clause", async () => { + const input = Input.InMethod(` +switch (a) +{ + case string str when str.Length is > 3 and not > 8: break; + case var (x, y) when x is not null && y is int: break; +}`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Control.Switch, + Token.Punctuation.OpenParen, + Token.Variables.ReadWrite("a"), Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Keywords.Control.Case, + Token.PrimitiveType.String, + Token.Identifiers.LocalName("str"), + Token.Keywords.Control.When, + Token.Variables.Object("str"), + Token.Punctuation.Accessor, + Token.Variables.Property("Length"), + Token.Operators.Pattern.Is, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("3"), + Token.Operators.Pattern.And, + Token.Operators.Pattern.Not, + Token.Operators.Relational.GreaterThan, + Token.Literals.Numeric.Decimal("8"), Token.Punctuation.Colon, Token.Keywords.Control.Break, Token.Punctuation.Semicolon, Token.Keywords.Control.Case, - Token.Variables.Discard, + Token.Keywords.Var, + Token.Punctuation.OpenParen, + Token.Identifiers.LocalName("x"), + Token.Punctuation.Comma, + Token.Identifiers.LocalName("y"), + Token.Punctuation.CloseParen, + Token.Keywords.Control.When, + Token.Variables.ReadWrite("x"), + Token.Operators.Pattern.Is, + Token.Operators.Pattern.Not, + Token.Literals.Null, + Token.Operators.Logical.And, + Token.Variables.ReadWrite("y"), + Token.Operators.Pattern.Is, + Token.PrimitiveType.Int, Token.Punctuation.Colon, Token.Keywords.Control.Break, Token.Punctuation.Semicolon, @@ -733,7 +897,7 @@ switch (a) ]); }); - it("handles line breaks", async () => { + it("Handles line breaks", async () => { const input = Input.InMethod(` switch (a) { From 2bc01d186363646ff9d5ed5f2510f643617048ec Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Wed, 16 Aug 2023 14:20:52 +0000 Subject: [PATCH 12/14] Allow line breaks in type-pattern --- grammars/csharp.tmLanguage | 97 ++++++++++++++++++++++++++++----- grammars/csharp.tmLanguage.cson | 63 +++++++++++++++++---- src/csharp.tmLanguage.yml | 38 ++++++++++--- test/patterns.tests.ts | 33 +++++++++-- 4 files changed, 194 insertions(+), 37 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 6bfbfea..cee4347 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -3541,36 +3541,72 @@ begin \G end - (?=\s|[)}\],;:?=&|^]|!=|\b(and|or|when)\b) + (?!\G[@_[:alpha:]])(?=[\({@_[:alpha:])}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns include - #type-builtin + #intrusive include - #type-name + #type-subpattern + + + + begin + (?=[\({@_[:alpha:]]) + end + (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) + patterns + include - #type-arguments + #intrusive + + + include + #positional-pattern include - #type-array-suffix + #property-pattern include - #type-nullable-suffix + #simple-designation-pattern + + + type-subpattern + + patterns + + + include + #type-builtin + begin - (?=\s) + (@?[_[:alpha:]][_[:alnum:]]*)\s*(::) + beginCaptures + + 1 + + name + entity.name.type.alias.cs + + 2 + + name + punctuation.separator.coloncolon.cs + + end - (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) + (?<=[_[:alnum:]])|(?=[.<\[\({)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns @@ -3578,19 +3614,54 @@ #intrusive - include - #positional-pattern + match + \@?[_[:alpha:]][_[:alnum:]]* + name + entity.name.type.cs + + + + match + \@?[_[:alpha:]][_[:alnum:]]* + name + entity.name.type.cs + + + begin + \. + beginCaptures + + 0 - include - #property-pattern + name + punctuation.accessor.cs + + end + (?<=[_[:alnum:]])|(?=[<\[\({)}\],;:?=&|^]|!=|\b(and|or|when)\b) + patterns + include - #simple-designation-pattern + #intrusive + + + match + \@?[_[:alpha:]][_[:alnum:]]* + name + entity.name.type.cs + + include + #type-arguments + + + include + #type-array-suffix + positional-pattern diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index f0e986c..07aa31d 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2182,43 +2182,84 @@ repository: patterns: [ { begin: "\\G" - end: "(?=\\s|[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?!\\G[@_[:alpha:]])(?=[\\({@_[:alpha:])}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { - include: "#type-builtin" + include: "#intrusive" } { - include: "#type-name" + include: "#type-subpattern" } + ] + } + { + begin: "(?=[\\({@_[:alpha:]])" + end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ { - include: "#type-arguments" + include: "#intrusive" } { - include: "#type-array-suffix" + include: "#positional-pattern" } { - include: "#type-nullable-suffix" + include: "#property-pattern" + } + { + include: "#simple-designation-pattern" } ] } + ] + "type-subpattern": + patterns: [ { - begin: "(?=\\s)" - end: "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" + include: "#type-builtin" + } + { + begin: "(@?[_[:alpha:]][_[:alnum:]]*)\\s*(::)" + beginCaptures: + "1": + name: "entity.name.type.alias.cs" + "2": + name: "punctuation.separator.coloncolon.cs" + end: "(?<=[_[:alnum:]])|(?=[.<\\[\\({)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#intrusive" } { - include: "#positional-pattern" + match: "\\@?[_[:alpha:]][_[:alnum:]]*" + name: "entity.name.type.cs" } + ] + } + { + match: "\\@?[_[:alpha:]][_[:alnum:]]*" + name: "entity.name.type.cs" + } + { + begin: "\\." + beginCaptures: + "0": + name: "punctuation.accessor.cs" + end: "(?<=[_[:alnum:]])|(?=[<\\[\\({)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" + patterns: [ { - include: "#property-pattern" + include: "#intrusive" } { - include: "#simple-designation-pattern" + match: "\\@?[_[:alpha:]][_[:alnum:]]*" + name: "entity.name.type.cs" } ] } + { + include: "#type-arguments" + } + { + include: "#type-array-suffix" + } ] "positional-pattern": begin: "(?=\\()" diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index a0bc617..71b6222 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -1294,16 +1294,11 @@ repository: end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - begin: \G - end: (?=\s|[)}\],;:?=&|^]|!=|\b(and|or|when)\b) - # hacky solution - can't have whitespace/comments/preprocessor around dot + end: (?!\G[@_[:alpha:]])(?=[\({@_[:alpha:])}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - # - include: '#intrusive' - - include: '#type-builtin' - - include: '#type-name' - - include: '#type-arguments' - - include: '#type-array-suffix' - - include: '#type-nullable-suffix' - - begin: (?=\s) + - include: '#intrusive' + - include: '#type-subpattern' + - begin: (?=[\({@_[:alpha:]]) end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) patterns: - include: '#intrusive' @@ -1311,6 +1306,31 @@ repository: - include: '#property-pattern' - include: '#simple-designation-pattern' + type-subpattern: + patterns: + - include: '#type-builtin' + - begin: (@?[_[:alpha:]][_[:alnum:]]*)\s*(::) + beginCaptures: + 1: { name: entity.name.type.alias.cs } + 2: { name: punctuation.separator.coloncolon.cs } + end: (?<=[_[:alnum:]])|(?=[.<\[\({)}\],;:?=&|^]|!=|\b(and|or|when)\b) + patterns: + - include: '#intrusive' + - match: \@?[_[:alpha:]][_[:alnum:]]* + name: entity.name.type.cs + - match: \@?[_[:alpha:]][_[:alnum:]]* + name: entity.name.type.cs + - begin: \. + beginCaptures: + 0: { name: punctuation.accessor.cs } + end: (?<=[_[:alnum:]])|(?=[<\[\({)}\],;:?=&|^]|!=|\b(and|or|when)\b) + patterns: + - include: '#intrusive' + - match: \@?[_[:alpha:]][_[:alnum:]]* + name: entity.name.type.cs + - include: '#type-arguments' + - include: '#type-array-suffix' + positional-pattern: begin: (?=\() end: (?=[)}\],;:?=&|^]|!=|\b(and|or|when)\b) diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index 6f64c5e..b823132 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -2,7 +2,7 @@ import { should } from "chai"; import { tokenize, Input, Token } from "./utils/tokenize"; describe("Patterns", () => { - before(should); + before(() => { should(); }); describe("is operator", () => { it("Discard pattern", async () => { @@ -364,7 +364,7 @@ result = (a, b, c, d, e) is it("Positional pattern", async () => { const input = Input.InMethod(` -if (x is Foo.Bar (int, not null) { Data.Length: > 0 } y) { } +if (x is Foo.Bar(int, not null) { Data.Length: > 0 } y) { } `); const tokens = await tokenize(input); @@ -946,7 +946,19 @@ switch (a) ) : break; case - _ + global :: + System + . + Collections + . + Generic + . + List + < + int + > + [ + ] : break; }`); const tokens = await tokenize(input); @@ -1025,7 +1037,20 @@ switch (a) Token.Punctuation.Semicolon, Token.Keywords.Control.Case, - Token.Variables.Discard, + Token.Identifiers.AliasName("global"), + Token.Punctuation.ColonColon, + Token.Type("System"), + Token.Punctuation.Accessor, + Token.Type("Collections"), + Token.Punctuation.Accessor, + Token.Type("Generic"), + Token.Punctuation.Accessor, + Token.Type("List"), + Token.Punctuation.TypeParameters.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameters.End, + Token.Punctuation.OpenBracket, + Token.Punctuation.CloseBracket, Token.Punctuation.Colon, Token.Keywords.Control.Break, Token.Punctuation.Semicolon, From 4dbeb62316c180584d23a9dc8cf058bc1cc97b16 Mon Sep 17 00:00:00 2001 From: Daniel Jeong Date: Wed, 16 Aug 2023 14:36:33 +0000 Subject: [PATCH 13/14] Allow nullable in type-pattern --- grammars/csharp.tmLanguage | 8 +++++++- grammars/csharp.tmLanguage.cson | 6 +++++- src/csharp.tmLanguage.yml | 4 +++- test/patterns.tests.ts | 3 ++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index cee4347..c84610c 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -3541,7 +3541,7 @@ begin \G end - (?!\G[@_[:alpha:]])(?=[\({@_[:alpha:])}\],;:?=&|^]|!=|\b(and|or|when)\b) + (?!\G[@_[:alpha:]])(?=[\({@_[:alpha:])}\],;:=&|^]|(?:\s|^)\?|!=|\b(and|or|when)\b) patterns @@ -3662,6 +3662,12 @@ include #type-array-suffix + + match + (?<!\s)\? + name + punctuation.separator.question-mark.cs + positional-pattern diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 07aa31d..3b5f49d 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2182,7 +2182,7 @@ repository: patterns: [ { begin: "\\G" - end: "(?!\\G[@_[:alpha:]])(?=[\\({@_[:alpha:])}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)" + end: "(?!\\G[@_[:alpha:]])(?=[\\({@_[:alpha:])}\\],;:=&|^]|(?:\\s|^)\\?|!=|\\b(and|or|when)\\b)" patterns: [ { include: "#intrusive" @@ -2260,6 +2260,10 @@ repository: { include: "#type-array-suffix" } + { + match: "(? Date: Wed, 16 Aug 2023 14:44:37 +0000 Subject: [PATCH 14/14] switch expression tests --- test/patterns.tests.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/patterns.tests.ts b/test/patterns.tests.ts index a953728..1870ad9 100644 --- a/test/patterns.tests.ts +++ b/test/patterns.tests.ts @@ -1062,7 +1062,7 @@ switch (a) }); describe("switch expression", () => { - it("simple", async () => { + it("Declaration, constant, discard patterns", async () => { const input = Input.InClass(` public decimal Calculate(object thing) => thing switch @@ -1192,7 +1192,7 @@ public decimal Calculate(object thing) => ]); }); - it("works with enum example", async () => { + it("Type pattern", async () => { const input = Input.InClass(` public static RGBColor FromRainbow(Rainbow colorBand) => colorBand switch { @@ -1363,7 +1363,7 @@ public static RGBColor FromRainbow(Rainbow colorBand) => colorBand switch ]); }); - it("works with property pattern", async () => { + it("Property pattern", async () => { const input = Input.InClass(` public static decimal ComputeSalesTax(Address location, decimal salePrice) => location switch @@ -1448,7 +1448,7 @@ public static decimal ComputeSalesTax(Address location, decimal salePrice) => ]); }); - it("works with tuple pattern", async () => { + it("Positional pattern", async () => { const input = Input.InClass(` public static string RockPaperScissors(string first, string second) => (first, second) switch @@ -1580,7 +1580,7 @@ public static string RockPaperScissors(string first, string second) ]); }); - it("works with positional pattern", async () => { + it("var pattern", async () => { const input = Input.InClass(` public static Quadrant GetQuadrant(Point point) => point switch { @@ -1713,7 +1713,7 @@ public static Quadrant GetQuadrant(Point point) => point switch ]); }); - it("works with nested expressions", async () => { + it("when clause", async () => { const input = Input.InClass(` public decimal CalculateToll(object vehicle) => vehicle switch