//===-- lib/Parser/openacc-parsers.cpp ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// Top-level grammar specification for OpenACC 3.1.

#include "basic-parsers.h"
#include "expr-parsers.h"
#include "misc-parsers.h"
#include "stmt-parser.h"
#include "token-parsers.h"
#include "type-parser-implementation.h"
#include "flang/Parser/parse-tree.h"

// OpenACC Directives and Clauses
namespace Fortran::parser {

constexpr auto startAccLine = skipStuffBeforeStatement >> "!$ACC "_sptok;
constexpr auto endAccLine = space >> endOfLine;

// Basic clauses
TYPE_PARSER("AUTO" >> construct<AccClause>(construct<AccClause::Auto>()) ||
    "ASYNC" >> construct<AccClause>(construct<AccClause::Async>(
                   maybe(parenthesized(scalarIntExpr)))) ||
    "ATTACH" >> construct<AccClause>(construct<AccClause::Attach>(
                    parenthesized(Parser<AccObjectList>{}))) ||
    "BIND" >> construct<AccClause>(
                  construct<AccClause::Bind>(Parser<AccBindClause>{})) ||
    "CAPTURE" >> construct<AccClause>(construct<AccClause::Capture>()) ||
    "COLLAPSE" >> construct<AccClause>(construct<AccClause::Collapse>(
                      parenthesized(scalarIntConstantExpr))) ||
    ("COPY"_tok || "PRESENT_OR_COPY"_tok || "PCOPY"_tok) >>
        construct<AccClause>(construct<AccClause::Copy>(
            parenthesized(Parser<AccObjectList>{}))) ||
    ("COPYIN"_tok || "PRESENT_OR_COPYIN"_tok || "PCOPYIN"_tok) >>
        construct<AccClause>(construct<AccClause::Copyin>(
            parenthesized(Parser<AccObjectListWithModifier>{}))) ||
    ("COPYOUT"_tok || "PRESENT_OR_COPYOUT"_tok || "PCOPYOUT"_tok) >>
        construct<AccClause>(construct<AccClause::Copyout>(
            parenthesized(Parser<AccObjectListWithModifier>{}))) ||
    ("CREATE"_tok || "PRESENT_OR_CREATE"_tok || "PCREATE"_tok) >>
        construct<AccClause>(construct<AccClause::Create>(
            parenthesized(Parser<AccObjectListWithModifier>{}))) ||
    "DEFAULT" >> construct<AccClause>(construct<AccClause::Default>(
                     Parser<AccDefaultClause>{})) ||
    "DEFAULT_ASYNC" >> construct<AccClause>(construct<AccClause::DefaultAsync>(
                           parenthesized(scalarIntExpr))) ||
    "DELETE" >> construct<AccClause>(construct<AccClause::Delete>(
                    parenthesized(Parser<AccObjectList>{}))) ||
    "DETACH" >> construct<AccClause>(construct<AccClause::Detach>(
                    parenthesized(Parser<AccObjectList>{}))) ||
    "DEVICE" >> construct<AccClause>(construct<AccClause::Device>(
                    parenthesized(Parser<AccObjectList>{}))) ||
    "DEVICEPTR" >> construct<AccClause>(construct<AccClause::Deviceptr>(
                       parenthesized(Parser<AccObjectList>{}))) ||
    "DEVICE_NUM" >> construct<AccClause>(construct<AccClause::DeviceNum>(
                        parenthesized(scalarIntExpr))) ||
    "DEVICE_RESIDENT" >>
        construct<AccClause>(construct<AccClause::DeviceResident>(
            parenthesized(Parser<AccObjectList>{}))) ||
    ("DEVICE_TYPE"_tok || "DTYPE"_tok) >>
        construct<AccClause>(construct<AccClause::DeviceType>(parenthesized(
            "*" >> construct<std::optional<std::list<ScalarIntExpr>>>()))) ||
    ("DEVICE_TYPE"_tok || "DTYPE"_tok) >>
        construct<AccClause>(construct<AccClause::DeviceType>(
            parenthesized(maybe(nonemptyList(scalarIntExpr))))) ||
    "FINALIZE" >> construct<AccClause>(construct<AccClause::Finalize>()) ||
    "FIRSTPRIVATE" >> construct<AccClause>(construct<AccClause::Firstprivate>(
                          parenthesized(Parser<AccObjectList>{}))) ||
    "GANG" >> construct<AccClause>(construct<AccClause::Gang>(
                  maybe(parenthesized(Parser<AccGangArgument>{})))) ||
    "HOST" >> construct<AccClause>(construct<AccClause::Host>(
                  parenthesized(Parser<AccObjectList>{}))) ||
    "IF" >> construct<AccClause>(
                construct<AccClause::If>(parenthesized(scalarLogicalExpr))) ||
    "IF_PRESENT" >> construct<AccClause>(construct<AccClause::IfPresent>()) ||
    "INDEPENDENT" >>
        construct<AccClause>(construct<AccClause::Independent>()) ||
    "LINK" >> construct<AccClause>(construct<AccClause::Link>(
                  parenthesized(Parser<AccObjectList>{}))) ||
    "NO_CREATE" >> construct<AccClause>(construct<AccClause::NoCreate>(
                       parenthesized(Parser<AccObjectList>{}))) ||
    "NOHOST" >> construct<AccClause>(construct<AccClause::Nohost>()) ||
    "NUM_GANGS" >> construct<AccClause>(construct<AccClause::NumGangs>(
                       parenthesized(scalarIntExpr))) ||
    "NUM_WORKERS" >> construct<AccClause>(construct<AccClause::NumWorkers>(
                         parenthesized(scalarIntExpr))) ||
    "PRESENT" >> construct<AccClause>(construct<AccClause::Present>(
                     parenthesized(Parser<AccObjectList>{}))) ||
    "PRIVATE" >> construct<AccClause>(construct<AccClause::Private>(
                     parenthesized(Parser<AccObjectList>{}))) ||
    "READ" >> construct<AccClause>(construct<AccClause::Read>()) ||
    "REDUCTION" >> construct<AccClause>(construct<AccClause::Reduction>(
                       parenthesized(construct<AccObjectListWithReduction>(
                           Parser<AccReductionOperator>{} / ":",
                           Parser<AccObjectList>{})))) ||
    "SELF" >> construct<AccClause>(
                  construct<AccClause::Self>(Parser<AccSelfClause>{})) ||
    "SEQ" >> construct<AccClause>(construct<AccClause::Seq>()) ||
    "TILE" >> construct<AccClause>(construct<AccClause::Tile>(
                  parenthesized(Parser<AccTileExprList>{}))) ||
    "USE_DEVICE" >> construct<AccClause>(construct<AccClause::UseDevice>(
                        parenthesized(Parser<AccObjectList>{}))) ||
    "VECTOR_LENGTH" >> construct<AccClause>(construct<AccClause::VectorLength>(
                           parenthesized(scalarIntExpr))) ||
    "VECTOR" >>
        construct<AccClause>(construct<AccClause::Vector>(maybe(
            parenthesized(("LENGTH:" >> scalarIntExpr || scalarIntExpr))))) ||
    "WAIT" >> construct<AccClause>(construct<AccClause::Wait>(
                  maybe(parenthesized(Parser<AccWaitArgument>{})))) ||
    "WORKER" >>
        construct<AccClause>(construct<AccClause::Worker>(maybe(
            parenthesized(("NUM:" >> scalarIntExpr || scalarIntExpr))))) ||
    "WRITE" >> construct<AccClause>(construct<AccClause::Auto>()))

TYPE_PARSER(
    construct<AccObject>(designator) || construct<AccObject>("/" >> name / "/"))

TYPE_PARSER(construct<AccObjectList>(nonemptyList(Parser<AccObject>{})))

TYPE_PARSER(construct<AccObjectListWithModifier>(
    maybe(Parser<AccDataModifier>{}), Parser<AccObjectList>{}))

// 2.16.3 (2485) wait-argument is:
//   [devnum : int-expr :] [queues :] int-expr-list
TYPE_PARSER(construct<AccWaitArgument>(maybe("DEVNUM:" >> scalarIntExpr / ":"),
    "QUEUES:" >> nonemptyList(scalarIntExpr) || nonemptyList(scalarIntExpr)))

// 2.9 (1609) size-expr is one of:
//   * (represented as an empty std::optional<ScalarIntExpr>)
//   int-expr
TYPE_PARSER(construct<AccSizeExpr>(scalarIntExpr) ||
    construct<AccSizeExpr>("*" >> construct<std::optional<ScalarIntExpr>>()))
TYPE_PARSER(construct<AccSizeExprList>(nonemptyList(Parser<AccSizeExpr>{})))

// tile size is one of:
//   * (represented as an empty std::optional<ScalarIntExpr>)
//   constant-int-expr
TYPE_PARSER(construct<AccTileExpr>(scalarIntConstantExpr) ||
    construct<AccTileExpr>(
        "*" >> construct<std::optional<ScalarIntConstantExpr>>()))
TYPE_PARSER(construct<AccTileExprList>(nonemptyList(Parser<AccTileExpr>{})))

// 2.9 (1607) gang-arg is:
//   [[num:]int-expr][[,]static:size-expr]
TYPE_PARSER(construct<AccGangArgument>(
    maybe(("NUM:"_tok >> scalarIntExpr || scalarIntExpr)),
    maybe(", STATIC:" >> Parser<AccSizeExpr>{})))

// 2.5.13 Reduction
// Operator for reduction
TYPE_PARSER(sourced(construct<AccReductionOperator>(
    first("+" >> pure(AccReductionOperator::Operator::Plus),
        "*" >> pure(AccReductionOperator::Operator::Multiply),
        "MAX" >> pure(AccReductionOperator::Operator::Max),
        "MIN" >> pure(AccReductionOperator::Operator::Min),
        "IAND" >> pure(AccReductionOperator::Operator::Iand),
        "IOR" >> pure(AccReductionOperator::Operator::Ior),
        "IEOR" >> pure(AccReductionOperator::Operator::Ieor),
        ".AND." >> pure(AccReductionOperator::Operator::And),
        ".OR." >> pure(AccReductionOperator::Operator::Or),
        ".EQV." >> pure(AccReductionOperator::Operator::Eqv),
        ".NEQV." >> pure(AccReductionOperator::Operator::Neqv)))))

// 2.15.1 Bind clause
TYPE_PARSER(sourced(construct<AccBindClause>(parenthesized(name))) ||
    sourced(construct<AccBindClause>(parenthesized(scalarDefaultCharExpr))))

// 2.5.14 Default clause
TYPE_PARSER(construct<AccDefaultClause>(parenthesized(
    first("NONE" >> pure(llvm::acc::DefaultValue::ACC_Default_none),
        "PRESENT" >> pure(llvm::acc::DefaultValue::ACC_Default_present)))))

// SELF clause is either a simple optional condition for compute construct
// or a synonym of the HOST clause for the update directive 2.14.4 holding
// an object list.
TYPE_PARSER(construct<AccSelfClause>(parenthesized(Parser<AccObjectList>{})) ||
    construct<AccSelfClause>(maybe(parenthesized(scalarLogicalExpr))))

// Modifier for copyin, copyout, cache and create
TYPE_PARSER(construct<AccDataModifier>(
    first("ZERO:" >> pure(AccDataModifier::Modifier::Zero),
        "READONLY:" >> pure(AccDataModifier::Modifier::ReadOnly))))

// Combined directives
TYPE_PARSER(sourced(construct<AccCombinedDirective>(
    first("KERNELS LOOP" >> pure(llvm::acc::Directive::ACCD_kernels_loop),
        "PARALLEL LOOP" >> pure(llvm::acc::Directive::ACCD_parallel_loop),
        "SERIAL LOOP" >> pure(llvm::acc::Directive::ACCD_serial_loop)))))

// Block directives
TYPE_PARSER(sourced(construct<AccBlockDirective>(
    first("DATA" >> pure(llvm::acc::Directive::ACCD_data),
        "HOST_DATA" >> pure(llvm::acc::Directive::ACCD_host_data),
        "KERNELS" >> pure(llvm::acc::Directive::ACCD_kernels),
        "PARALLEL" >> pure(llvm::acc::Directive::ACCD_parallel),
        "SERIAL" >> pure(llvm::acc::Directive::ACCD_serial)))))

// Standalone directives
TYPE_PARSER(sourced(construct<AccStandaloneDirective>(
    first("ENTER DATA" >> pure(llvm::acc::Directive::ACCD_enter_data),
        "EXIT DATA" >> pure(llvm::acc::Directive::ACCD_exit_data),
        "INIT" >> pure(llvm::acc::Directive::ACCD_init),
        "SHUTDOWN" >> pure(llvm::acc::Directive::ACCD_shutdown),
        "SET" >> pure(llvm::acc::Directive::ACCD_set),
        "UPDATE" >> pure(llvm::acc::Directive::ACCD_update)))))

// Loop directives
TYPE_PARSER(sourced(construct<AccLoopDirective>(
    first("LOOP" >> pure(llvm::acc::Directive::ACCD_loop)))))

TYPE_PARSER(construct<AccBeginLoopDirective>(
    sourced(Parser<AccLoopDirective>{}), Parser<AccClauseList>{}))

TYPE_PARSER(
    construct<OpenACCLoopConstruct>(sourced(Parser<AccBeginLoopDirective>{})))

// 2.15.1 Routine directive
TYPE_PARSER(sourced(construct<OpenACCRoutineConstruct>(verbatim("ROUTINE"_tok),
    maybe(parenthesized(name)), Parser<AccClauseList>{})))

// 2.10 Cache directive
TYPE_PARSER(sourced(
    construct<OpenACCCacheConstruct>(sourced(construct<Verbatim>("CACHE"_tok)),
        parenthesized(Parser<AccObjectListWithModifier>{}))))

// 2.11 Combined constructs
TYPE_PARSER(construct<AccBeginCombinedDirective>(
    sourced(Parser<AccCombinedDirective>{}), Parser<AccClauseList>{}))

// 2.12 Atomic constructs
TYPE_PARSER(construct<AccEndAtomic>(startAccLine >> "END ATOMIC"_tok))

TYPE_PARSER("ATOMIC" >>
    construct<AccAtomicRead>(verbatim("READ"_tok) / endAccLine,
        statement(assignmentStmt), maybe(Parser<AccEndAtomic>{} / endAccLine)))

TYPE_PARSER("ATOMIC" >>
    construct<AccAtomicWrite>(verbatim("WRITE"_tok) / endAccLine,
        statement(assignmentStmt), maybe(Parser<AccEndAtomic>{} / endAccLine)))

TYPE_PARSER("ATOMIC" >>
    construct<AccAtomicUpdate>(maybe(verbatim("UPDATE"_tok)) / endAccLine,
        statement(assignmentStmt), maybe(Parser<AccEndAtomic>{} / endAccLine)))

TYPE_PARSER("ATOMIC" >>
    construct<AccAtomicCapture>(verbatim("CAPTURE"_tok) / endAccLine,
        statement(assignmentStmt), statement(assignmentStmt),
        Parser<AccEndAtomic>{} / endAccLine))

TYPE_PARSER(
    sourced(construct<OpenACCAtomicConstruct>(Parser<AccAtomicRead>{})) ||
    sourced(construct<OpenACCAtomicConstruct>(Parser<AccAtomicCapture>{})) ||
    sourced(construct<OpenACCAtomicConstruct>(Parser<AccAtomicWrite>{})) ||
    sourced(construct<OpenACCAtomicConstruct>(Parser<AccAtomicUpdate>{})))

// 2.13 Declare constructs
TYPE_PARSER(sourced(construct<AccDeclarativeDirective>(
    first("DECLARE" >> pure(llvm::acc::Directive::ACCD_declare)))))

// [Clause, [Clause], ...]
TYPE_PARSER(sourced(construct<AccClauseList>(
    many(maybe(","_tok) >> sourced(Parser<AccClause>{})))))

// 2.16.3 Wait directive
TYPE_PARSER(sourced(construct<OpenACCWaitConstruct>(
    sourced(construct<Verbatim>("WAIT"_tok)),
    maybe(parenthesized(Parser<AccWaitArgument>{})), Parser<AccClauseList>{})))

// Block Constructs
TYPE_PARSER(sourced(construct<AccBeginBlockDirective>(
    sourced(Parser<AccBlockDirective>{}), Parser<AccClauseList>{})))

TYPE_PARSER(startAccLine >> sourced(construct<AccEndBlockDirective>("END"_tok >>
                                sourced(Parser<AccBlockDirective>{}))))

TYPE_PARSER(construct<OpenACCBlockConstruct>(
    Parser<AccBeginBlockDirective>{} / endAccLine, block,
    Parser<AccEndBlockDirective>{} / endAccLine))

// Standalone constructs
TYPE_PARSER(construct<OpenACCStandaloneConstruct>(
    sourced(Parser<AccStandaloneDirective>{}), Parser<AccClauseList>{}))

// Standalone declarative constructs
TYPE_PARSER(construct<OpenACCStandaloneDeclarativeConstruct>(
    sourced(Parser<AccDeclarativeDirective>{}), Parser<AccClauseList>{}))

TYPE_PARSER(
    startAccLine >> first(sourced(construct<OpenACCDeclarativeConstruct>(
                              Parser<OpenACCStandaloneDeclarativeConstruct>{})),
                        sourced(construct<OpenACCDeclarativeConstruct>(
                            Parser<OpenACCRoutineConstruct>{}))))

// OpenACC constructs
TYPE_CONTEXT_PARSER("OpenACC construct"_en_US,
    startAccLine >>
        first(construct<OpenACCConstruct>(Parser<OpenACCBlockConstruct>{}),
            construct<OpenACCConstruct>(Parser<OpenACCCombinedConstruct>{}),
            construct<OpenACCConstruct>(Parser<OpenACCLoopConstruct>{}),
            construct<OpenACCConstruct>(Parser<OpenACCStandaloneConstruct>{}),
            construct<OpenACCConstruct>(Parser<OpenACCCacheConstruct>{}),
            construct<OpenACCConstruct>(Parser<OpenACCWaitConstruct>{}),
            construct<OpenACCConstruct>(Parser<OpenACCAtomicConstruct>{})))

TYPE_PARSER(startAccLine >> sourced(construct<AccEndCombinedDirective>(sourced(
                                "END"_tok >> Parser<AccCombinedDirective>{}))))

TYPE_PARSER(construct<OpenACCCombinedConstruct>(
    sourced(Parser<AccBeginCombinedDirective>{} / endAccLine)))

} // namespace Fortran::parser
