/* Apache V2 usage with no significant modifications
   Source: https://github.com/openqasm/openqasm/blob/429781bb9c95ef15944861f306ac6b9e4ff0abf0/source/grammar/qasm3Parser.g4
   License: https://github.com/openqasm/openqasm/blob/main/LICENSE
*/

parser grammar qasm3Parser;

options {
    tokenVocab = qasm3Lexer;
}

program: version? statement* EOF;
version: OPENQASM VersionSpecifier SEMICOLON;

// A statement is any valid single statement of an OpenQASM 3 program, with the
// exception of the version-definition statement (which must be unique, and the
// first statement of the file if present).  This file just defines rules for
// parsing; we leave semantic analysis and rejection of invalid scopes for
// compiler implementations.
statement:
    pragma
    // All the actual statements of the language.
    | annotation* (
        aliasDeclarationStatement
        | assignmentStatement
        | barrierStatement
        | boxStatement
        | breakStatement
        | calibrationGrammarStatement
        | classicalDeclarationStatement
        | constDeclarationStatement
        | continueStatement
        | defStatement
        | defcalStatement
        | delayStatement
        | endStatement
        | expressionStatement
        | externStatement
        | forStatement
        | gateCallStatement
        | gateStatement
        | ifStatement
        | includeStatement
        | ioDeclarationStatement
        | measureArrowAssignmentStatement
        | oldStyleDeclarationStatement
        | quantumDeclarationStatement
        | resetStatement
        | returnStatement
        | whileStatement
    )
;
annotation: AnnotationKeyword RemainingLineContent?;
scope: LBRACE statement* RBRACE;
pragma: PRAGMA RemainingLineContent;

statementOrScope: statement | scope;


/* Start top-level statement definitions. */

// Inclusion statements.
calibrationGrammarStatement: DEFCALGRAMMAR StringLiteral SEMICOLON;
includeStatement: INCLUDE StringLiteral SEMICOLON;

// Control-flow statements.
breakStatement: BREAK SEMICOLON;
continueStatement: CONTINUE SEMICOLON;
endStatement: END SEMICOLON;
forStatement: FOR scalarType Identifier IN (setExpression | LBRACKET rangeExpression RBRACKET | Identifier) body=statementOrScope;
ifStatement: IF LPAREN expression RPAREN if_body=statementOrScope (ELSE else_body=statementOrScope)?;
returnStatement: RETURN (expression | measureExpression)? SEMICOLON;
whileStatement: WHILE LPAREN expression RPAREN body=statementOrScope;

// Quantum directive statements.
barrierStatement: BARRIER gateOperandList? SEMICOLON;
boxStatement: BOX designator? scope;
delayStatement: DELAY designator gateOperandList? SEMICOLON;
/* `gateCallStatement`  is split in two to avoid a potential ambiguity with an
 * `expressionStatement` that consists of a single function call.  The only
 * "gate" that can have no operands is `gphase` with no control modifiers, and
 * `gphase(pi);` looks grammatically identical to `fn(pi);`.  We disambiguate by
 * having `gphase` be its own token, and requiring that all other gate calls
 * grammatically have at least one qubit.  Strictly, as long as `gphase` is a
 * separate token, ANTLR can disambiguate the statements by the definition
 * order, but this is more robust. */
gateCallStatement:
    gateModifier* Identifier (LPAREN expressionList? RPAREN)? designator? gateOperandList SEMICOLON
    | gateModifier* GPHASE (LPAREN expressionList? RPAREN)? designator? gateOperandList? SEMICOLON
;
// measureArrowAssignmentStatement also permits the case of not assigning the
// result to any classical value too.
measureArrowAssignmentStatement: measureExpression (ARROW indexedIdentifier)? SEMICOLON;
resetStatement: RESET gateOperand SEMICOLON;

// Primitive declaration statements.
aliasDeclarationStatement: LET Identifier EQUALS aliasExpression SEMICOLON;
classicalDeclarationStatement: (scalarType | arrayType) Identifier (EQUALS declarationExpression)? SEMICOLON;
constDeclarationStatement: CONST scalarType Identifier EQUALS declarationExpression SEMICOLON;
ioDeclarationStatement: (INPUT | OUTPUT) (scalarType | arrayType) Identifier SEMICOLON;
oldStyleDeclarationStatement: (CREG | QREG) Identifier designator? SEMICOLON;
quantumDeclarationStatement: qubitType Identifier SEMICOLON;

// Declarations and definitions of higher-order objects.
defStatement: DEF Identifier LPAREN argumentDefinitionList? RPAREN returnSignature? scope;
externStatement: EXTERN Identifier LPAREN externArgumentList? RPAREN returnSignature? SEMICOLON;
gateStatement: GATE Identifier (LPAREN params=identifierList? RPAREN)? qubits=identifierList scope;

// Non-declaration assignments and calculations.
assignmentStatement: indexedIdentifier op=(EQUALS | CompoundAssignmentOperator) (expression | measureExpression) SEMICOLON;
expressionStatement: expression SEMICOLON;

// TODO: this handling of the defcal block is incorrect, because it is not
// constrained to consume balanced brace blocks.  This needs more attention.
defcalStatement: DEFCAL Identifier (LPAREN argumentDefinitionList? RPAREN)?  defcalArgumentList returnSignature? LBRACE .*? RBRACE;


/* End top-level statement definitions. */
/* Start expression definitions. */


// ANTLR4 can handle direct left-recursive rules, and ambiguities are guaranteed
// to resolve in the order of definition.  This means that the order of rules
// here defines the precedence table, from most tightly binding to least.
expression:
    LPAREN expression RPAREN                                  # parenthesisExpression
    | expression indexOperator                                # indexExpression
    | <assoc=right> expression op=DOUBLE_ASTERISK expression  # powerExpression
    | op=(TILDE | EXCLAMATION_POINT | MINUS) expression       # unaryExpression
    | expression op=(ASTERISK | SLASH | PERCENT) expression   # multiplicativeExpression
    | expression op=(PLUS | MINUS) expression                 # additiveExpression
    | expression op=BitshiftOperator expression               # bitshiftExpression
    | expression op=ComparisonOperator expression             # comparisonExpression
    | expression op=EqualityOperator expression               # equalityExpression
    | expression op=AMPERSAND expression                      # bitwiseAndExpression
    | expression op=CARET expression                          # bitwiseXorExpression
    | expression op=PIPE expression                           # bitwiseOrExpression
    | expression op=DOUBLE_AMPERSAND expression               # logicalAndExpression
    | expression op=DOUBLE_PIPE expression                    # logicalOrExpression
    | (scalarType | arrayType) LPAREN expression RPAREN       # castExpression
    | DURATIONOF LPAREN scope RPAREN                          # durationofExpression
    | Identifier LPAREN expressionList? RPAREN                # callExpression
    | (
        Identifier
        | BinaryIntegerLiteral
        | OctalIntegerLiteral
        | DecimalIntegerLiteral
        | HexIntegerLiteral
        | FloatLiteral
        | ImaginaryLiteral
        | BooleanLiteral
        | BitstringLiteral
        | TimingLiteral
        | HardwareQubit
      )                                                       # literalExpression
;

// Special-case expressions that are only valid in certain contexts.  These are
// not in the expression tree, but can contain elements that are within it.
aliasExpression: expression (DOUBLE_PLUS expression)*;
declarationExpression: arrayLiteral | expression | measureExpression;
measureExpression: MEASURE gateOperand;
rangeExpression: expression? COLON expression? (COLON expression)?;
setExpression: LBRACE expression (COMMA expression)* COMMA? RBRACE;
arrayLiteral: LBRACE (expression | arrayLiteral) (COMMA (expression | arrayLiteral))* COMMA? RBRACE;

// The general form is a comma-separated list of indexing entities.
// `setExpression` is only valid when being used as a single index: registers
// can support it for creating aliases, but arrays cannot.
indexOperator:
    LBRACKET
    (
        setExpression
        | (expression | rangeExpression) (COMMA (expression | rangeExpression))* COMMA?
    )
    RBRACKET;
// Alternative form to `indexExpression` for cases where an obvious l-value is
// better grammatically than a generic expression.  Some current uses of this
// rule may be better as `expression`, leaving the semantic analysis to later
// (for example in gate calls).
indexedIdentifier: Identifier indexOperator*;

/* End expression definitions. */
/* Start type definitions. */

returnSignature: ARROW scalarType;
gateModifier: (
    INV
    | POW LPAREN expression RPAREN
    | (CTRL | NEGCTRL) (LPAREN expression RPAREN)?
) AT;

scalarType:
    BIT designator?
    | INT designator?
    | UINT designator?
    | FLOAT designator?
    | ANGLE designator?
    | BOOL
    | DURATION
    | STRETCH
    | COMPLEX (LBRACKET scalarType RBRACKET)?
;
qubitType: QUBIT designator?;
arrayType: ARRAY LBRACKET scalarType COMMA expressionList RBRACKET;
arrayReferenceType: (CONST | MUTABLE) ARRAY LBRACKET scalarType COMMA (expressionList | DIM EQUALS expression) RBRACKET;

designator: LBRACKET expression RBRACKET;

gateOperand: indexedIdentifier | HardwareQubit;
externArgument: scalarType | arrayReferenceType | CREG designator?;
defcalArgument: HardwareQubit | Identifier;
argumentDefinition:
    scalarType Identifier
    | qubitType Identifier
    | (CREG | QREG) Identifier designator?
    | arrayReferenceType Identifier
;

argumentDefinitionList: argumentDefinition (COMMA argumentDefinition)* COMMA?;
expressionList: expression (COMMA expression)* COMMA?;
defcalArgumentList: defcalArgument (COMMA defcalArgument)* COMMA?;
identifierList: Identifier (COMMA Identifier)* COMMA?;
gateOperandList: gateOperand (COMMA gateOperand)* COMMA?;
externArgumentList: externArgument (COMMA externArgument)* COMMA?;
