From 582f30852268af4f1409c0ead57f79632da3ccfd Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Wed, 27 Oct 2021 13:58:37 +0800 Subject: [PATCH 1/8] Change the defination of Provided in Namespace.fsi Also update parsing, rewritting, and reducing rules. --- src/QsFmt/Formatter/ParseTree/Namespace.fs | 15 ++++++++++++++- src/QsFmt/Formatter/SyntaxTree/Namespace.fs | 4 +++- src/QsFmt/Formatter/SyntaxTree/Namespace.fsi | 5 ++++- src/QsFmt/Formatter/SyntaxTree/Reducer.fs | 2 +- src/QsFmt/Formatter/SyntaxTree/Rewriter.fs | 2 +- src/QsFmt/Parser/QSharpParser.g4 | 3 ++- 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/QsFmt/Formatter/ParseTree/Namespace.fs b/src/QsFmt/Formatter/ParseTree/Namespace.fs index 6074e419b0..7f81e7290c 100644 --- a/src/QsFmt/Formatter/ParseTree/Namespace.fs +++ b/src/QsFmt/Formatter/ParseTree/Namespace.fs @@ -33,8 +33,21 @@ type SpecializationGeneratorVisitor(tokens) = toBuiltIn context.intrinsic context.semicolon override _.VisitProvidedGenerator context = + let tospecializationParameters tokens (context: QSharpParser.SpecializationParameterTupleContext) = + let toSpecializationParameter (context: QSharpParser.SpecializationParameterContext) = + { Prefix = Node.prefix tokens context.Start.TokenIndex; Text = context.GetText() } + + let parameters = context._parameters |> Seq.map toSpecializationParameter + let commas = context._commas |> Seq.map (Node.toTerminal tokens) + + { + OpenParen = context.openParen |> Node.toTerminal tokens + Items = Node.tupleItems parameters commas + CloseParen = context.closeParen |> Node.toTerminal tokens + } + Provided( - parameters = (Option.ofObj context.provided.parameters |> Option.map (Node.toUnknown tokens)), + parameters = (Option.ofObj context.provided.parameters |> Option.map (tospecializationParameters tokens)), statements = { OpenBrace = context.provided.block.openBrace |> Node.toTerminal tokens diff --git a/src/QsFmt/Formatter/SyntaxTree/Namespace.fs b/src/QsFmt/Formatter/SyntaxTree/Namespace.fs index 5f5f1e1339..aec2372e6b 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Namespace.fs +++ b/src/QsFmt/Formatter/SyntaxTree/Namespace.fs @@ -12,9 +12,11 @@ type TypeParameterBinding = CloseBracket: Terminal } +type SpecializationParameter = Terminal + type SpecializationGenerator = | BuiltIn of name: Terminal * semicolon: Terminal - | Provided of parameters: Terminal option * statements: Statement Block + | Provided of parameters: SpecializationParameter Tuple option * statements: Statement Block type Specialization = { Names: Terminal list; Generator: SpecializationGenerator } diff --git a/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi b/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi index 51e673d71d..7811db9b09 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi +++ b/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi @@ -26,13 +26,16 @@ type internal TypeParameterBinding = CloseBracket: Terminal } +/// A specialization parameter +type internal SpecializationParameter = Terminal + /// A specialization generator. type internal SpecializationGenerator = /// A built-in generator. | BuiltIn of name: Terminal * semicolon: Terminal /// A provided specialization. - | Provided of parameters: Terminal option * statements: Statement Block + | Provided of parameters: SpecializationParameter Tuple option * statements: Statement Block /// A specialization. type internal Specialization = diff --git a/src/QsFmt/Formatter/SyntaxTree/Reducer.fs b/src/QsFmt/Formatter/SyntaxTree/Reducer.fs index 87a1569125..076d020c1e 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Reducer.fs +++ b/src/QsFmt/Formatter/SyntaxTree/Reducer.fs @@ -152,7 +152,7 @@ type internal 'result Reducer() as reducer = match generator with | BuiltIn (name, semicolon) -> [ reducer.Terminal name; reducer.Terminal semicolon ] |> reduce | Provided (parameters, statements) -> - (parameters |> Option.map reducer.Terminal |> Option.toList) + (parameters |> Option.map (curry reducer.Tuple reducer.Terminal) |> Option.toList) @ [ reducer.Block(reducer.Statement, statements) ] |> reduce diff --git a/src/QsFmt/Formatter/SyntaxTree/Rewriter.fs b/src/QsFmt/Formatter/SyntaxTree/Rewriter.fs index 996dfc6fbf..8203931054 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Rewriter.fs +++ b/src/QsFmt/Formatter/SyntaxTree/Rewriter.fs @@ -151,7 +151,7 @@ type 'context Rewriter() = BuiltIn(name = rewriter.Terminal(context, name), semicolon = rewriter.Terminal(context, semicolon)) | Provided (parameters, statements) -> Provided( - parameters = (parameters |> Option.map (curry rewriter.Terminal context)), + parameters = (parameters |> Option.map (curry3 rewriter.Tuple context rewriter.Terminal)), statements = rewriter.Block(context, rewriter.Statement, statements) ) diff --git a/src/QsFmt/Parser/QSharpParser.g4 b/src/QsFmt/Parser/QSharpParser.g4 index f1f6c0ecab..4aeb82a134 100644 --- a/src/QsFmt/Parser/QSharpParser.g4 +++ b/src/QsFmt/Parser/QSharpParser.g4 @@ -109,7 +109,8 @@ specializationGenerator providedSpecialization : parameters=specializationParameterTuple? block=scope; -specializationParameterTuple : '(' (specializationParameter (',' specializationParameter)*)? ')'; +specializationParameterTuple + : openParen='(' (parameters+=specializationParameter (commas+=',' parameters+=specializationParameter)*)? closeParen=')'; specializationParameter : Identifier From d01719152fdffc6399874c062fd2110ce3d902c6 Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Wed, 27 Oct 2021 17:17:33 +0800 Subject: [PATCH 2/8] Add update rule specializationUpdate to formatter This rule updates deprecated specialization declarations to add a `...` parameter. --- src/QsFmt/Formatter.Tests/Examples.fs | 28 ++++++++++++++++ src/QsFmt/Formatter/Formatter.fs | 1 + src/QsFmt/Formatter/Rules.fs | 46 +++++++++++++++++++++++++++ src/QsFmt/Formatter/Rules.fsi | 3 ++ 4 files changed, 78 insertions(+) diff --git a/src/QsFmt/Formatter.Tests/Examples.fs b/src/QsFmt/Formatter.Tests/Examples.fs index 95ecd8ace2..61a39fe539 100644 --- a/src/QsFmt/Formatter.Tests/Examples.fs +++ b/src/QsFmt/Formatter.Tests/Examples.fs @@ -245,6 +245,34 @@ let ``Removes For-Loop Parens`` = } }""" +[] +let ``Update Specialization Declaration`` = + """namespace Foo { + operation Bar() : Unit is Ctl + Adj { + body () { + } + adjoint () { + } + controlled (q) { + } + controlled adjoint (q) { + } + } +}""", + + """namespace Foo { + operation Bar() : Unit is Ctl + Adj { + body (...) { + } + adjoint (...) { + } + controlled (q, ...) { + } + controlled adjoint (q, ...) { + } + } +}""" + [] let ``Allows size as an Identifier`` = """namespace Foo { diff --git a/src/QsFmt/Formatter/Formatter.fs b/src/QsFmt/Formatter/Formatter.fs index 2c93a95e08..373898a006 100644 --- a/src/QsFmt/Formatter/Formatter.fs +++ b/src/QsFmt/Formatter/Formatter.fs @@ -84,6 +84,7 @@ let versionToUpdateRules (version: Version option) = simpleRule qubitBindingUpdate simpleRule unitUpdate simpleRule forParensUpdate + simpleRule specializationUpdate simpleRule arraySyntaxUpdate ] diff --git a/src/QsFmt/Formatter/Rules.fs b/src/QsFmt/Formatter/Rules.fs index 2b87ea8f2b..a94b8a515e 100644 --- a/src/QsFmt/Formatter/Rules.fs +++ b/src/QsFmt/Formatter/Rules.fs @@ -160,6 +160,52 @@ let forParensUpdate = } } +/// +/// Make sure that a contains a comma. +/// +let ensureComma (item: 'a SequenceItem) = + match item.Comma with + | None -> { Item = item.Item; Comma = Some({ Prefix = []; Text = "," }) } + | Some _ -> item + +/// +/// Prepends the with an ellipsis item if it does not already contain one. +/// +let ensureEllipsis (parameters: SpecializationParameter Tuple) = + let ellipsis nspace = + { Prefix = [ spaces nspace ]; Text = "..." } + + let ellipsisItem nspace = + { Item = Some(ellipsis nspace); Comma = None } + + { parameters with + Items = + match parameters.Items with + // Replace, e.g., `body()` with `body(...)` + | [] -> [ ellipsisItem 0 ] + // Replace, e.g., `controlled (q)` with `controlled (q, ...)` + | [ x ] -> + match Option.get(x.Item).Text with + | "..." -> [ x ] + | _ -> [ ensureComma x; ellipsisItem 1 ] + | _ -> parameters.Items + } + +let specializationUpdate = + { new Rewriter<_>() with + override _.SpecializationGenerator((), generator) = + match generator with + | Provided (parameters, statements) -> + Provided( + parameters = + (match parameters with + | None -> parameters + | Some par -> Some(ensureEllipsis par)), + statements = statements + ) + | _ -> generator + } + let arraySyntaxUpdate = let getBuiltInDefault builtIn = diff --git a/src/QsFmt/Formatter/Rules.fsi b/src/QsFmt/Formatter/Rules.fsi index c5d3915ab7..12a373dc04 100644 --- a/src/QsFmt/Formatter/Rules.fsi +++ b/src/QsFmt/Formatter/Rules.fsi @@ -29,6 +29,9 @@ val unitUpdate : unit Rewriter /// Updates for-loops to remove deprecated parentheses. val forParensUpdate: unit Rewriter +/// Updates deprecated specialization declarations to add a `...` parameter. +val specializationUpdate: unit Rewriter + /// Updates the `new [n]` array syntax to the new `[val, size = n]` array syntax. val arraySyntaxUpdate: unit Rewriter From d5a2095bd62c0575a9dc32b9283ae4b6c9038dcc Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Thu, 28 Oct 2021 09:22:38 +0800 Subject: [PATCH 3/8] Add `internal` to `SpecializationParameter` --- src/QsFmt/Formatter/SyntaxTree/Namespace.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsFmt/Formatter/SyntaxTree/Namespace.fs b/src/QsFmt/Formatter/SyntaxTree/Namespace.fs index aec2372e6b..831100628d 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Namespace.fs +++ b/src/QsFmt/Formatter/SyntaxTree/Namespace.fs @@ -12,7 +12,7 @@ type TypeParameterBinding = CloseBracket: Terminal } -type SpecializationParameter = Terminal +type internal SpecializationParameter = Terminal type SpecializationGenerator = | BuiltIn of name: Terminal * semicolon: Terminal From ef789d3c3244e7e230f22253db0364f452cf251f Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Sat, 30 Oct 2021 14:46:33 +0800 Subject: [PATCH 4/8] Remove the type SpecializationParameter --- src/QsFmt/Formatter/Rules.fs | 2 +- src/QsFmt/Formatter/Rules.fsi | 3 ++- src/QsFmt/Formatter/SyntaxTree/Namespace.fs | 4 +--- src/QsFmt/Formatter/SyntaxTree/Namespace.fsi | 5 +---- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/QsFmt/Formatter/Rules.fs b/src/QsFmt/Formatter/Rules.fs index fb2a9bf518..8cb2f0fcc6 100644 --- a/src/QsFmt/Formatter/Rules.fs +++ b/src/QsFmt/Formatter/Rules.fs @@ -171,7 +171,7 @@ let ensureComma (item: 'a SequenceItem) = /// /// Prepends the with an ellipsis item if it does not already contain one. /// -let ensureEllipsis (parameters: SpecializationParameter Tuple) = +let ensureEllipsis (parameters: Terminal Tuple) = let ellipsis nspace = { Prefix = [ spaces nspace ]; Text = "..." } diff --git a/src/QsFmt/Formatter/Rules.fsi b/src/QsFmt/Formatter/Rules.fsi index c8d20c16b7..c260c5b15b 100644 --- a/src/QsFmt/Formatter/Rules.fsi +++ b/src/QsFmt/Formatter/Rules.fsi @@ -38,5 +38,6 @@ val arraySyntaxUpdate: unit Rewriter /// Provides warnings for deprecated array syntax still in the syntax tree. val checkArraySyntax: string -> Document -> string list -/// Replaces `&&`, `||`, and `!` with `and`, `or`, and `not`, respectively. +/// Replaces deprecated use of boolean operators `&&`, `||`, and `!` with their keyword +/// equivalence `and`, `or`, and `not` respectively. val booleanOperatorUpdate : unit Rewriter diff --git a/src/QsFmt/Formatter/SyntaxTree/Namespace.fs b/src/QsFmt/Formatter/SyntaxTree/Namespace.fs index 831100628d..acf5013000 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Namespace.fs +++ b/src/QsFmt/Formatter/SyntaxTree/Namespace.fs @@ -12,11 +12,9 @@ type TypeParameterBinding = CloseBracket: Terminal } -type internal SpecializationParameter = Terminal - type SpecializationGenerator = | BuiltIn of name: Terminal * semicolon: Terminal - | Provided of parameters: SpecializationParameter Tuple option * statements: Statement Block + | Provided of parameters: Terminal Tuple option * statements: Statement Block type Specialization = { Names: Terminal list; Generator: SpecializationGenerator } diff --git a/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi b/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi index 7811db9b09..6dfe50b94d 100644 --- a/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi +++ b/src/QsFmt/Formatter/SyntaxTree/Namespace.fsi @@ -26,16 +26,13 @@ type internal TypeParameterBinding = CloseBracket: Terminal } -/// A specialization parameter -type internal SpecializationParameter = Terminal - /// A specialization generator. type internal SpecializationGenerator = /// A built-in generator. | BuiltIn of name: Terminal * semicolon: Terminal /// A provided specialization. - | Provided of parameters: SpecializationParameter Tuple option * statements: Statement Block + | Provided of parameters: Terminal Tuple option * statements: Statement Block /// A specialization. type internal Specialization = From 9ea67d4a0636a1a1ea5b956718009c409b11df9b Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Tue, 2 Nov 2021 10:18:27 +0800 Subject: [PATCH 5/8] Also update SpecializationGenerator with no parens A new test case is added to demonstrate this type of updating. --- src/QsFmt/Formatter.Tests/Examples.fs | 16 ++++++++++++++++ src/QsFmt/Formatter/Rules.fs | 13 +++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/QsFmt/Formatter.Tests/Examples.fs b/src/QsFmt/Formatter.Tests/Examples.fs index feeca53f2e..3056117530 100644 --- a/src/QsFmt/Formatter.Tests/Examples.fs +++ b/src/QsFmt/Formatter.Tests/Examples.fs @@ -273,6 +273,22 @@ let ``Update Specialization Declaration`` = } }""" +[] +let ``Update Specialization Declaration No Parens`` = + """namespace Foo { + operation Bar() : Unit is Ctl + Adj { + body {} + adjoint {} + } +}""", + + """namespace Foo { + operation Bar() : Unit is Ctl + Adj { + body (...) {} + adjoint (...) {} + } +}""" + [] let ``Allows size as an Identifier`` = """namespace Foo { diff --git a/src/QsFmt/Formatter/Rules.fs b/src/QsFmt/Formatter/Rules.fs index 8cb2f0fcc6..370e549567 100644 --- a/src/QsFmt/Formatter/Rules.fs +++ b/src/QsFmt/Formatter/Rules.fs @@ -181,7 +181,7 @@ let ensureEllipsis (parameters: Terminal Tuple) = { parameters with Items = match parameters.Items with - // Replace, e.g., `body()` with `body(...)` + // Replace, e.g., `body ()` with `body (...)` | [] -> [ ellipsisItem 0 ] // Replace, e.g., `controlled (q)` with `controlled (q, ...)` | [ x ] -> @@ -194,12 +194,21 @@ let ensureEllipsis (parameters: Terminal Tuple) = let specializationUpdate = { new Rewriter<_>() with override _.SpecializationGenerator((), generator) = + let emptyTuple = + { + OpenParen = { Prefix = [ spaces 1 ]; Text = "(" } + Items = [] + CloseParen = { Prefix = []; Text = ")" } + } + match generator with | Provided (parameters, statements) -> Provided( parameters = (match parameters with - | None -> parameters + // Replace, e.g., `body` with `body (...)` + | None -> Some(ensureEllipsis emptyTuple) + // Replace, e.g., `body ()` with `body (...)` | Some par -> Some(ensureEllipsis par)), statements = statements ) From cf97c77ff5e63d7d1ea495664966acb113ff4e92 Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Tue, 2 Nov 2021 11:12:19 +0800 Subject: [PATCH 6/8] Improve coding style Use `Some` with dataflow connection instead of function calling. --- src/QsFmt/Formatter/Rules.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QsFmt/Formatter/Rules.fs b/src/QsFmt/Formatter/Rules.fs index 370e549567..961feb5cb7 100644 --- a/src/QsFmt/Formatter/Rules.fs +++ b/src/QsFmt/Formatter/Rules.fs @@ -165,7 +165,7 @@ let forParensUpdate = /// let ensureComma (item: 'a SequenceItem) = match item.Comma with - | None -> { Item = item.Item; Comma = Some({ Prefix = []; Text = "," }) } + | None -> { Item = item.Item; Comma = { Prefix = []; Text = "," } |> Some } | Some _ -> item /// @@ -176,7 +176,7 @@ let ensureEllipsis (parameters: Terminal Tuple) = { Prefix = [ spaces nspace ]; Text = "..." } let ellipsisItem nspace = - { Item = Some(ellipsis nspace); Comma = None } + { Item = ellipsis nspace |> Some; Comma = None } { parameters with Items = @@ -207,9 +207,9 @@ let specializationUpdate = parameters = (match parameters with // Replace, e.g., `body` with `body (...)` - | None -> Some(ensureEllipsis emptyTuple) + | None -> ensureEllipsis emptyTuple |> Some // Replace, e.g., `body ()` with `body (...)` - | Some par -> Some(ensureEllipsis par)), + | Some par -> ensureEllipsis par |> Some), statements = statements ) | _ -> generator From bfdef7eee5889a28e1d1bc6bebe1c54f68807758 Mon Sep 17 00:00:00 2001 From: Jintao YU Date: Wed, 3 Nov 2021 18:42:37 +0800 Subject: [PATCH 7/8] Update src/QsFmt/Formatter/ParseTree/Namespace.fs Co-authored-by: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> --- src/QsFmt/Formatter/ParseTree/Namespace.fs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/QsFmt/Formatter/ParseTree/Namespace.fs b/src/QsFmt/Formatter/ParseTree/Namespace.fs index 7f81e7290c..ed8fbb3fc7 100644 --- a/src/QsFmt/Formatter/ParseTree/Namespace.fs +++ b/src/QsFmt/Formatter/ParseTree/Namespace.fs @@ -34,10 +34,7 @@ type SpecializationGeneratorVisitor(tokens) = override _.VisitProvidedGenerator context = let tospecializationParameters tokens (context: QSharpParser.SpecializationParameterTupleContext) = - let toSpecializationParameter (context: QSharpParser.SpecializationParameterContext) = - { Prefix = Node.prefix tokens context.Start.TokenIndex; Text = context.GetText() } - - let parameters = context._parameters |> Seq.map toSpecializationParameter + let parameters = context._parameters |> Seq.map (Node.toUnknown tokens) let commas = context._commas |> Seq.map (Node.toTerminal tokens) { From c47e5f358f245f43ded22f2c27078448b6c6abce Mon Sep 17 00:00:00 2001 From: Jintaoyu Date: Wed, 3 Nov 2021 19:02:47 +0800 Subject: [PATCH 8/8] Add specialization declaration update to README --- src/QsFmt/README.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/QsFmt/README.md b/src/QsFmt/README.md index 91a7c7ac20..9cd6d8ce56 100644 --- a/src/QsFmt/README.md +++ b/src/QsFmt/README.md @@ -15,29 +15,29 @@ dotnet build ./App/App.fsproj ## Usage -Updates the source code in input files: -    `qsfmt update --input Path\To\My\File1.qs Path\To\My\File2.qs` -Updates the source code in project: +Updates the source code in input files: +    `qsfmt update --input Path\To\My\File1.qs Path\To\My\File2.qs` +Updates the source code in project:     `qsfmt update --project Path\To\My\Project.csproj` -Command Line Options: -    `-i`, `--input`: Required. Files or folders to update. -    `-p`, `--project`: Required. The project file for the project to update. -    `-b`, `--backup`: Option to create backup files of input files. -    `-r`, `--recurse`: Option to process input folders recursively. -    `--qsharp-version`: Option to provide a Q# version to the tool. -    `--help`: Display this help screen. +Command Line Options: +    `-i`, `--input`: Required. Files or folders to update. +    `-p`, `--project`: Required. The project file for the project to update. +    `-b`, `--backup`: Option to create backup files of input files. +    `-r`, `--recurse`: Option to process input folders recursively. +    `--qsharp-version`: Option to provide a Q# version to the tool. +    `--help`: Display this help screen.     `--version`: Display version information. -Either the `--input` option or the `--project` must be used to specify the input files to the tool. +Either the `--input` option or the `--project` must be used to specify the input files to the tool. The `--recurse` and `--qsharp-version` options can only be used with the `--input` option. ## Input and Output -Input to the formatter can be specified in one of two ways. +Input to the formatter can be specified in one of two ways. Individual files or folders can be specified with the `--input` command-line argument. Multiple files and folders can be specified after the argument, but at least one is expected. .qs extension directly under the folder will be processed. If the `--recurse` option is -specified, subfolders will be processed recursively for Q# files. +specified, subfolders will be processed recursively for Q# files. The other method of providing input to the formatter is by specifying a Q# project file with the `--project` command-line argument. When this method is used, exactly one project file is expected after the argument, and the tool will use MSBuild to determine all applicable Q# source @@ -57,6 +57,7 @@ Updating rules currently in use: - Array Syntax - [proposal](https://github.com/microsoft/qsharp-language/blob/main/Approved/2-enhanced-array-literals.md) - Using and Borrowing Syntax - [proposal](https://github.com/microsoft/qsharp-language/blob/main/Approved/1-implicitly-scoped-qubit-allocation.md) - Parentheses in For Loop Syntax - Removes deprecated parentheses around for-loop range expressions. + - Specialization Declaration Syntax - Add `...` to deprecated forms of the parameter list in specialization declarations. - Unit Syntax - Replaces deprecated unit syntax `()` for `Unit`. - Boolean Operator Syntax - Replaces deprecated use of boolean operators `&&`, `||`, and `!` with their keyword equivalence `and`, `or`, and `not` respectively.