Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
44 changes: 44 additions & 0 deletions src/QsFmt/Formatter.Tests/Examples.fs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,50 @@ let ``Removes For-Loop Parens`` =
}
}"""

[<Example(ExampleKind.Update)>]
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, ...) {
}
}
}"""

[<Example(ExampleKind.Update)>]
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 (...) {}
}
}"""

[<Example(ExampleKind.Update)>]
let ``Allows size as an Identifier`` =
"""namespace Foo {
Expand Down
1 change: 1 addition & 0 deletions src/QsFmt/Formatter/Formatter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ let versionToUpdateRules (version: Version option) =
simpleRule qubitBindingUpdate
simpleRule unitUpdate
simpleRule forParensUpdate
simpleRule specializationUpdate
simpleRule arraySyntaxUpdate
simpleRule booleanOperatorUpdate
]
Expand Down
12 changes: 11 additions & 1 deletion src/QsFmt/Formatter/ParseTree/Namespace.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,18 @@ type SpecializationGeneratorVisitor(tokens) =
toBuiltIn context.intrinsic context.semicolon

override _.VisitProvidedGenerator context =
let tospecializationParameters tokens (context: QSharpParser.SpecializationParameterTupleContext) =
let parameters = context._parameters |> Seq.map (Node.toUnknown tokens)
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
Expand Down
55 changes: 55 additions & 0 deletions src/QsFmt/Formatter/Rules.fs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,61 @@ let forParensUpdate =
}
}

/// <summary>
/// Make sure that a <see cref="SequenceItem"/> contains a comma.
/// </summary>
let ensureComma (item: 'a SequenceItem) =
match item.Comma with
| None -> { Item = item.Item; Comma = { Prefix = []; Text = "," } |> Some }
| Some _ -> item

/// <summary>
/// Prepends the <paramref name="parameters"/> with an ellipsis <see cref="Terminal"/> item if it does not already contain one.
/// </summary>
let ensureEllipsis (parameters: Terminal Tuple) =
let ellipsis nspace =
{ Prefix = [ spaces nspace ]; Text = "..." }

let ellipsisItem nspace =
{ Item = ellipsis nspace |> Some; 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) =
let emptyTuple =
{
OpenParen = { Prefix = [ spaces 1 ]; Text = "(" }
Items = []
CloseParen = { Prefix = []; Text = ")" }
}

match generator with
| Provided (parameters, statements) ->
Provided(
parameters =
(match parameters with
// Replace, e.g., `body` with `body (...)`
| None -> ensureEllipsis emptyTuple |> Some
// Replace, e.g., `body ()` with `body (...)`
| Some par -> ensureEllipsis par |> Some),
statements = statements
)
| _ -> generator
}

let arraySyntaxUpdate =

let getBuiltInDefault builtIn =
Expand Down
6 changes: 5 additions & 1 deletion src/QsFmt/Formatter/Rules.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ 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 <Type>[n]` array syntax to the new `[val, size = n]` array syntax.
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
2 changes: 1 addition & 1 deletion src/QsFmt/Formatter/SyntaxTree/Namespace.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type TypeParameterBinding =

type SpecializationGenerator =
| BuiltIn of name: Terminal * semicolon: Terminal
| Provided of parameters: Terminal option * statements: Statement Block
| Provided of parameters: Terminal Tuple option * statements: Statement Block

type Specialization = { Names: Terminal list; Generator: SpecializationGenerator }

Expand Down
2 changes: 1 addition & 1 deletion src/QsFmt/Formatter/SyntaxTree/Namespace.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type internal SpecializationGenerator =
| BuiltIn of name: Terminal * semicolon: Terminal

/// A provided specialization.
| Provided of parameters: Terminal option * statements: Statement Block
| Provided of parameters: Terminal Tuple option * statements: Statement Block

/// A specialization.
type internal Specialization =
Expand Down
2 changes: 1 addition & 1 deletion src/QsFmt/Formatter/SyntaxTree/Reducer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/QsFmt/Formatter/SyntaxTree/Rewriter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)

Expand Down
3 changes: 2 additions & 1 deletion src/QsFmt/Parser/QSharpParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ specializationGenerator

providedSpecialization : parameters=specializationParameterTuple? block=scope;

specializationParameterTuple : '(' (specializationParameter (',' specializationParameter)*)? ')';
specializationParameterTuple
: openParen='(' (parameters+=specializationParameter (commas+=',' parameters+=specializationParameter)*)? closeParen=')';

specializationParameter
: Identifier
Expand Down
27 changes: 14 additions & 13 deletions src/QsFmt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,29 @@ dotnet build ./App/App.fsproj

## Usage

Updates the source code in input files:
&nbsp;&nbsp;&nbsp;&nbsp;`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:
&nbsp;&nbsp;&nbsp;&nbsp;`qsfmt update --input Path\To\My\File1.qs Path\To\My\File2.qs`
Updates the source code in project:
&nbsp;&nbsp;&nbsp;&nbsp;`qsfmt update --project Path\To\My\Project.csproj`

Command Line Options:
&nbsp;&nbsp;&nbsp;&nbsp;`-i`, `--input`: Required. Files or folders to update.
&nbsp;&nbsp;&nbsp;&nbsp;`-p`, `--project`: Required. The project file for the project to update.
&nbsp;&nbsp;&nbsp;&nbsp;`-b`, `--backup`: Option to create backup files of input files.
&nbsp;&nbsp;&nbsp;&nbsp;`-r`, `--recurse`: Option to process input folders recursively.
&nbsp;&nbsp;&nbsp;&nbsp;`--qsharp-version`: Option to provide a Q# version to the tool.
&nbsp;&nbsp;&nbsp;&nbsp;`--help`: Display this help screen.
Command Line Options:
&nbsp;&nbsp;&nbsp;&nbsp;`-i`, `--input`: Required. Files or folders to update.
&nbsp;&nbsp;&nbsp;&nbsp;`-p`, `--project`: Required. The project file for the project to update.
&nbsp;&nbsp;&nbsp;&nbsp;`-b`, `--backup`: Option to create backup files of input files.
&nbsp;&nbsp;&nbsp;&nbsp;`-r`, `--recurse`: Option to process input folders recursively.
&nbsp;&nbsp;&nbsp;&nbsp;`--qsharp-version`: Option to provide a Q# version to the tool.
&nbsp;&nbsp;&nbsp;&nbsp;`--help`: Display this help screen.
&nbsp;&nbsp;&nbsp;&nbsp;`--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
Expand All @@ -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.
Expand Down