Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/QsCompiler/DataStructures/Diagnostics.fs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type ErrorCode =
| InvalidOperationCharacteristics = 3115
| MissingOperationCharacteristics = 3116
| ExpectingUpdateExpression = 3117
| InvalidAttribute = 3118

| InvalidIdentifierDeclaration = 3201
| MissingIdentifierDeclaration = 3202
Expand Down Expand Up @@ -103,6 +104,8 @@ type ErrorCode =
| MissingUdtItemDeclaration = 3234
| InvalidUdtItemNameDeclaration = 3235
| MissingUdtItemNameDeclaration = 3236
| MissingClosingAttributeSign = 3237
| MissingAttributeArgs = 3238

| EmptyValueArray = 3300
| InvalidValueArray = 3301
Expand Down Expand Up @@ -134,6 +137,7 @@ type ErrorCode =
| InvertControlledGenerator = 4110
| ControlledGenArgMismatch = 4111
| ControlledAdjointGenArgMismatch = 4112
| MissingFollowingDeclaration = 4113

| MissingExprInArray = 5001
| MultipleTypesInArray = 5002
Expand Down Expand Up @@ -356,6 +360,7 @@ type DiagnosticItem =
| ErrorCode.ExcessContinuation -> "Unexpected code fragment."
| ErrorCode.NonCallExprAsStatement -> "An expression used as a statement must be a call expression."

| ErrorCode.InvalidAttribute -> "Syntax error in attribute."
| ErrorCode.InvalidExpression -> "Syntax error in expression."
| ErrorCode.MissingExpression -> "Expecting expression."
| ErrorCode.InvalidIdentifierName -> "Identifiers need to start with an ASCII letter or an underscore, and need to contain at least one non-underscore character."
Expand Down Expand Up @@ -441,7 +446,10 @@ type DiagnosticItem =
| ErrorCode.InvertControlledGenerator -> "Invalid generator for controlled specialization. Valid generators are \"distributed\" and \"auto\"."
| ErrorCode.ControlledGenArgMismatch -> "The argument to a user-defined controlled specialization must must be of the form \"(ctlQsName, ...)\"."
| ErrorCode.ControlledAdjointGenArgMismatch -> "The argument to a user-defined controlled-adjoint specialization must must be of the form \"(ctlQsName, ...)\"."

| ErrorCode.MissingFollowingDeclaration -> "The attribute must be above another attribute, a type definition, an operator declaration, or a function declaration."
| ErrorCode.MissingClosingAttributeSign -> "Missing closing sign for attribute, did you mean to type @ ?"
| ErrorCode.MissingAttributeArgs -> "Missing arguments for specified attribute."

| ErrorCode.MissingExprInArray -> "Underscores cannot be used to denote missing array elements."
| ErrorCode.MultipleTypesInArray -> "Array items must have a common base type."
| ErrorCode.InvalidArrayItemIndex -> "Expecting an expression of type Int or Range. Got an expression of type {0}."
Expand Down
5 changes: 4 additions & 1 deletion src/QsCompiler/DataStructures/SyntaxTokens.fs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ type QsFragmentKind =
| AdjointDeclaration of QsSpecializationGenerator
| ControlledDeclaration of QsSpecializationGenerator
| ControlledAdjointDeclaration of QsSpecializationGenerator
| AttributeDeclaration of QsExpression * QsExpression
| OperationDeclaration of QsSymbol * CallableSignature
| FunctionDeclaration of QsSymbol * CallableSignature
| TypeDefinition of QsSymbol * QsTuple<QsSymbol * QsType>
Expand All @@ -223,7 +224,8 @@ with
/// returns the error code for an invalid fragment of the given kind
member this.ErrorCode =
match this with
| ExpressionStatement _ -> ErrorCode.InvalidExpressionStatement
| ExpressionStatement _ -> ErrorCode.InvalidExpressionStatement
| AttributeDeclaration _ -> ErrorCode.InvalidAttribute
| ReturnStatement _ -> ErrorCode.InvalidReturnStatement
| FailStatement _ -> ErrorCode.InvalidFailStatement
| ImmutableBinding _ -> ErrorCode.InvalidImmutableBinding
Expand Down Expand Up @@ -252,6 +254,7 @@ with
/// returns the error code for an invalid fragment ending on the given kind
member this.InvalidEnding =
match this with
| AttributeDeclaration _ -> ErrorCode.UnexpectedFragmentDelimiter
| ExpressionStatement _
| ReturnStatement _
| FailStatement _
Expand Down
8 changes: 8 additions & 0 deletions src/QsCompiler/SyntaxProcessor/ContextVerification.fs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ let private verifyDeclaration (context : SyntaxTokenContext) =
| _ -> errMsg
context.Parents |> Array.toList |> isNamespace

/// Used by attributes to check if it is followed by another attribute, a function, an operation, or type definition/declaration.
let private followedByNamespaceDeclaration (context : SyntaxTokenContext) =
match context.Next with
| Value (FunctionDeclaration _) | Value (OperationDeclaration _) | Value (TypeDefinition _) | Value (AttributeDeclaration _) -> verifyDeclaration context
| Value InvalidFragment -> false, [||]
| _ -> false, [| (ErrorCode.MissingFollowingDeclaration |> Error, context.Range) |]

/// Verifies that the given generator is a valid generator for the callable body -
/// i.e. verifies that the generator is either a user defined implementation, or intrinsic.
/// Does *not* verify whether the symbol tuple for a user defined implementation is correct.
Expand Down Expand Up @@ -212,6 +219,7 @@ let VerifySyntaxTokenContext =
| FunctionDeclaration _ -> verifyDeclaration context
| TypeDefinition _ -> verifyDeclaration context
| OpenDirective _ -> verifyOpenDirective context
| AttributeDeclaration _ -> followedByNamespaceDeclaration context
| NamespaceDeclaration _ -> verifyNamespace context
| InvalidFragment _ -> false, [||] // excluded from the compilation
|> fun (kind, tuple) -> kind, tuple |> Array.map (fun (x,y) -> QsCompilerDiagnostic.New (x, []) y)
Expand Down
3 changes: 2 additions & 1 deletion src/QsCompiler/SyntaxProcessor/SyntaxExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ type SymbolInformation = {
[<Extension>]
let public SymbolInformation fragmentKind =
let chooseValues = QsNullable<_>.Choose id >> Seq.toArray
fragmentKind |> function
fragmentKind |> function
| QsFragmentKind.AttributeDeclaration _ -> [||], ([||], [||], [||])
| QsFragmentKind.ExpressionStatement ex -> [||], ([ex] , []) |> collectWith SymbolsFromExpr
| QsFragmentKind.ReturnStatement ex -> [||], ([ex] , []) |> collectWith SymbolsFromExpr
| QsFragmentKind.FailStatement ex -> [||], ([ex] , []) |> collectWith SymbolsFromExpr
Expand Down
7 changes: 7 additions & 0 deletions src/QsCompiler/TextProcessor/QsExpressionParsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,13 @@ let internal callLikeExpr =
attempt ((itemAccessExpr <|> identifier <|> tupledItem expr) .>>. argumentTuple) // identifier needs to come *after* arrayItemExpr
|>> fun (callable, arg) -> applyBinary CallLikeExpression () callable arg

/// Parses a Q# attribute identifier (separated from arguments to better handle errors)
let internal attributeId =
attempt identifier

/// Parses Q# attribute arguments
let internal attributeArgs =
attempt argumentTuple

// processing terms of operator precedence parsers

Expand Down
6 changes: 5 additions & 1 deletion src/QsCompiler/TextProcessor/QsFragmentParsing.fs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ and private namespaceDeclaration =
let invalid = NamespaceDeclaration invalidSymbol
buildFragment namespaceDeclHeader.parse (expectedNamespaceName eof) invalid NamespaceDeclaration

/// Uses buildAttribute to parse a Q# AttributeDeclaration as a QsFragment.
and private attributeDeclaration =
buildAttribute attributeId attributeArgs AttributeDeclaration

// operation and function parsing

Expand Down Expand Up @@ -442,7 +445,8 @@ let private expressionStatement =
let internal codeFragment =
let validFragment =
choice (getFragments() |> List.map snd)
<|> expressionStatement // the expressionStatement needs to be last
<|> attributeDeclaration
<|> expressionStatement// the expressionStatement needs to be last
let invalidFragment =
let valid = fun _ -> InvalidFragment
buildFragment (preturn ()) (fail "invalid syntax") InvalidFragment valid
Expand Down
39 changes: 39 additions & 0 deletions src/QsCompiler/TextProcessor/SyntaxBuilder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -458,5 +458,44 @@ let internal buildFragment header body (invalid : QsFragmentKind) (fragmentKind
header >>. (attempt (validBody state) <|> invalidBody state) >>= build


/// Construts a QsFragment for a Q# attribute.
/// Adds error handling capability to suggest a missing end symbol and missing arguments.
let internal buildAttribute id args (fragmentKind : 'a * QsExpression -> QsFragmentKind) =
let build (kind, (startPos, (text, endPos))) =
getUserState .>> clearDiagnostics
|>> fun diagnostics ->
QsFragment.New(kind, (startPos, endPos), (filterAndAdapt diagnostics (endPos |> QsPositionInfo.New)).ToImmutableArray(), NonNullable<string>.New text)

let delimiters state =
let fragmentEnd =
let allWS = emptySpace .>>? eof
manyCharsTill anyChar (followedBy allWS) .>>. getPosition
(getPosition .>>. fragmentEnd) |> runOnSubstream state

let nextValid =
(skipChar '@') <|> (qsFragmentHeader |>> ignore)

let parseOverError =
skipInvalidUntil nextValid

let checkArgs =
let processMissingArgs = buildError parseOverError ErrorCode.MissingAttributeArgs >>% ()
(lookAhead nextValid >>. processMissingArgs >>. preturn ((InvalidExpr, Null) |> QsExpression.New)) <|> args

let validBody state =
let body = id .>>. checkArgs
(body |>> fragmentKind .>>. delimiters state)

let checkEnding =
let processMissingSymbol = buildError parseOverError ErrorCode.MissingClosingAttributeSign >>% ()
(skipChar '@') <|> processMissingSymbol

let header =
(skipChar '@' |> term)

getCharStreamState >>= fun state ->
header >>. attempt (validBody state) .>> checkEnding >>= build