| # This is a C++ grammar from the C++ standard [1]. |
| # |
| # The grammar is a superset of the true grammar requring semantic constraints to |
| # resolve ambiguties. The grammar is context-free and ambiguous (beyond the |
| # limit of LR(k)). We use general parsing algorithm (e.g GLR) to handle the |
| # grammar and generate a transition table which is used to drive the parsing. |
| # |
| # It aims to align with the ISO C++ grammar as much as possible. We adjust it |
| # to fit the need for the grammar-based parser: |
| # - attributes are omitted, which will be handled as comments; |
| # - we don't allow nullable nonterminal symbols. There are few nullable |
| # nonterminals in the spec grammar, they are adjusted to be non-nullable; |
| # - the file merely describes the core C++ grammar. Preprocessor directives and |
| # lexical conversions are omitted as we reuse clang's lexer and run a fake |
| # preprocessor; |
| # - grammar rules with the >> token are adjusted, the greatergreater token is |
| # split into two > tokens, to make the GLR parser aware of nested templates |
| # and right shift operator; |
| # |
| # Guidelines: |
| # - nonterminals are lower_case; terminals (aka tokens) correspond to |
| # clang::TokenKind, written as "IDENTIFIER", "USING", "::" etc; |
| # - optional symbols are supported, with a _opt suffix; |
| # |
| # [1] https://isocpp.org/files/papers/N4860.pdf |
| |
| # _ lists all the start-symbols which we support parsing. |
| # |
| # We list important nonterminals as start symbols, rather than doing it for all |
| # nonterminals by default, this reduces the number of states by 30% and LRTable |
| # actions by 16%. |
| _ := translation-unit |
| _ := statement-seq |
| _ := declaration-seq |
| |
| # gram.key |
| typedef-name := IDENTIFIER |
| typedef-name := simple-template-id |
| namespace-name := IDENTIFIER |
| namespace-name := namespace-alias |
| namespace-alias := IDENTIFIER |
| class-name := IDENTIFIER |
| class-name := simple-template-id |
| enum-name := IDENTIFIER |
| template-name := IDENTIFIER |
| |
| # gram.basic |
| #! Custom modifications to eliminate optional declaration-seq |
| translation-unit := declaration-seq |
| translation-unit := global-module-fragment_opt module-declaration declaration-seq_opt private-module-fragment_opt |
| |
| # gram.expr |
| # expr.prim |
| primary-expression := literal |
| primary-expression := THIS |
| primary-expression := ( expression ) |
| primary-expression := id-expression |
| primary-expression := lambda-expression |
| primary-expression := fold-expression |
| primary-expression := requires-expression |
| id-expression := unqualified-id |
| id-expression := qualified-id |
| unqualified-id := IDENTIFIER |
| unqualified-id := operator-function-id |
| unqualified-id := conversion-function-id |
| unqualified-id := literal-operator-id |
| unqualified-id := ~ type-name |
| unqualified-id := ~ decltype-specifier |
| unqualified-id := template-id |
| qualified-id := nested-name-specifier TEMPLATE_opt unqualified-id |
| nested-name-specifier := :: |
| nested-name-specifier := type-name :: |
| nested-name-specifier := namespace-name :: |
| nested-name-specifier := decltype-specifier :: |
| nested-name-specifier := nested-name-specifier IDENTIFIER :: |
| nested-name-specifier := nested-name-specifier TEMPLATE_opt simple-template-id :: |
| lambda-expression := lambda-introducer lambda-declarator_opt compound-statement |
| lambda-expression := lambda-introducer < template-parameter-list > requires-clause_opt lambda-declarator_opt compound-statement |
| #! We allow a capture-default to appear anywhere in a capture-list. |
| # This simplifies the grammar and error recovery. |
| lambda-introducer := [ capture-list_opt ] |
| lambda-declarator := ( parameter-declaration-clause_opt ) decl-specifier-seq_opt noexcept-specifier_opt trailing-return-type_opt requires-clause_opt |
| capture-list := capture |
| capture-list := capture-list , capture |
| capture := capture-default |
| capture := simple-capture |
| capture := init-capture |
| capture-default := & |
| capture-default := = |
| simple-capture := IDENTIFIER ..._opt |
| simple-capture := & IDENTIFIER ..._opt |
| simple-capture := THIS |
| simple-capture := * THIS |
| init-capture := ..._opt IDENTIFIER initializer |
| init-capture := & ..._opt IDENTIFIER initializer |
| fold-expression := ( cast-expression fold-operator ... ) |
| fold-expression := ( ... fold-operator cast-expression ) |
| fold-expression := ( cast-expression fold-operator ... fold-operator cast-expression ) |
| fold-operator := + |
| fold-operator := - |
| fold-operator := * |
| fold-operator := / |
| fold-operator := % |
| fold-operator := ^ |
| fold-operator := | |
| fold-operator := << |
| fold-operator := greatergreater |
| fold-operator := += |
| fold-operator := -= |
| fold-operator := *= |
| fold-operator := /= |
| fold-operator := %= |
| fold-operator := ^= |
| fold-operator := &= |
| fold-operator := |= |
| fold-operator := <<= |
| fold-operator := >>= |
| fold-operator := = |
| fold-operator := == |
| fold-operator := != |
| fold-operator := < |
| fold-operator := > |
| fold-operator := <= |
| fold-operator := >= |
| fold-operator := && |
| fold-operator := || |
| fold-operator := , |
| fold-operator := .* |
| fold-operator := ->* |
| requires-expression := REQUIRES requirement-parameter-list_opt requirement-body |
| requirement-parameter-list := ( parameter-declaration-clause_opt ) |
| requirement-body := { requirement-seq } |
| requirement-seq := requirement |
| requirement-seq := requirement-seq requirement |
| requirement := simple-requirement |
| requirement := type-requirement |
| requirement := compound-requirement |
| requirement := nested-requirement |
| simple-requirement := expression ; |
| type-requirement := TYPENAME nested-name-specifier_opt type-name ; |
| compound-requirement := { expression } NOEXCEPT_opt return-type-requirement_opt ; |
| return-type-requirement := -> type-constraint |
| nested-requirement := REQUIRES constraint-expression ; |
| # expr.post |
| postfix-expression := primary-expression |
| postfix-expression := postfix-expression [ expr-or-braced-init-list ] |
| postfix-expression := postfix-expression ( expression-list_opt ) |
| postfix-expression := simple-type-specifier ( expression-list_opt ) |
| postfix-expression := typename-specifier ( expression-list_opt ) |
| postfix-expression := simple-type-specifier braced-init-list |
| postfix-expression := postfix-expression . TEMPLATE_opt id-expression |
| postfix-expression := postfix-expression -> TEMPLATE_opt id-expression |
| postfix-expression := postfix-expression ++ |
| postfix-expression := postfix-expression -- |
| postfix-expression := DYNAMIC_CAST < type-id > ( expression ) |
| postfix-expression := STATIC_CAST < type-id > ( expression ) |
| postfix-expression := REINTERPRET_CAST < type-id > ( expression ) |
| postfix-expression := CONST_CAST < type-id > ( expression ) |
| postfix-expression := TYPEID ( expression ) |
| postfix-expression := TYPEID ( type-id ) |
| #! Standard defines expression-list in terms of initializer-list, but our |
| # initializer-list allows designators. |
| expression-list := initializer-clause ..._opt |
| expression-list := expression-list , initializer-clause ..._opt |
| # expr.unary |
| unary-expression := postfix-expression |
| unary-expression := unary-operator cast-expression |
| unary-expression := ++ cast-expression |
| unary-expression := -- cast-expression |
| unary-expression := await-expression |
| unary-expression := SIZEOF unary-expression |
| unary-expression := SIZEOF ( type-id ) |
| unary-expression := SIZEOF ... ( IDENTIFIER ) |
| unary-expression := ALIGNOF ( type-id ) |
| unary-expression := noexcept-expression |
| unary-expression := new-expression |
| unary-expression := delete-expression |
| unary-operator := * |
| unary-operator := & |
| unary-operator := + |
| unary-operator := - |
| unary-operator := ! |
| unary-operator := ~ |
| await-expression := CO_AWAIT cast-expression |
| noexcept-expression := NOEXCEPT ( expression ) |
| new-expression := ::_opt NEW new-placement_opt new-type-id new-initializer_opt |
| new-expression := ::_opt NEW new-placement_opt ( type-id ) new-initializer_opt |
| new-placement := ( expression-list ) |
| new-type-id := type-specifier-seq new-declarator_opt |
| new-declarator := ptr-operator new-declarator_opt |
| new-declarator := noptr-new-declarator |
| noptr-new-declarator := [ expression_opt ] |
| noptr-new-declarator := noptr-new-declarator [ constant-expression ] |
| new-initializer := ( expression-list_opt ) |
| new-initializer := braced-init-list |
| delete-expression := ::_opt DELETE cast-expression |
| delete-expression := ::_opt DELETE [ ] cast-expression |
| cast-expression := unary-expression |
| cast-expression := ( type-id ) cast-expression |
| # expr.mptr.oper |
| pm-expression := cast-expression |
| pm-expression := pm-expression .* cast-expression |
| pm-expression := pm-expression ->* cast-expression |
| # expr.mul |
| multiplicative-expression := pm-expression |
| multiplicative-expression := multiplicative-expression * pm-expression |
| multiplicative-expression := multiplicative-expression / pm-expression |
| multiplicative-expression := multiplicative-expression % pm-expression |
| # expr.add |
| additive-expression := multiplicative-expression |
| additive-expression := additive-expression + multiplicative-expression |
| additive-expression := additive-expression - multiplicative-expression |
| # expr.shift |
| shift-expression := additive-expression |
| shift-expression := shift-expression << additive-expression |
| shift-expression := shift-expression greatergreater additive-expression |
| # expr.spaceship |
| compare-expression := shift-expression |
| compare-expression := compare-expression <=> shift-expression |
| # expr.rel |
| relational-expression := compare-expression |
| relational-expression := relational-expression < compare-expression |
| relational-expression := relational-expression > compare-expression |
| relational-expression := relational-expression <= compare-expression |
| relational-expression := relational-expression >= compare-expression |
| # expr.eq |
| equality-expression := relational-expression |
| equality-expression := equality-expression == relational-expression |
| equality-expression := equality-expression != relational-expression |
| # expr.bit.and |
| and-expression := equality-expression |
| and-expression := and-expression & equality-expression |
| # expr.xor |
| exclusive-or-expression := and-expression |
| exclusive-or-expression := exclusive-or-expression ^ and-expression |
| # expr.or |
| inclusive-or-expression := exclusive-or-expression |
| inclusive-or-expression := inclusive-or-expression | exclusive-or-expression |
| # expr.log.and |
| logical-and-expression := inclusive-or-expression |
| logical-and-expression := logical-and-expression && inclusive-or-expression |
| # expr.log.or |
| logical-or-expression := logical-and-expression |
| logical-or-expression := logical-or-expression || logical-and-expression |
| # expr.cond |
| conditional-expression := logical-or-expression |
| conditional-expression := logical-or-expression ? expression : assignment-expression |
| # expr.ass |
| yield-expression := CO_YIELD assignment-expression |
| yield-expression := CO_YIELD braced-init-list |
| throw-expression := THROW assignment-expression_opt |
| assignment-expression := conditional-expression |
| assignment-expression := yield-expression |
| assignment-expression := throw-expression |
| assignment-expression := logical-or-expression assignment-operator initializer-clause |
| assignment-operator := = |
| assignment-operator := *= |
| assignment-operator := /= |
| assignment-operator := %= |
| assignment-operator := += |
| assignment-operator := -= |
| assignment-operator := >>= |
| assignment-operator := <<= |
| assignment-operator := &= |
| assignment-operator := ^= |
| assignment-operator := |= |
| # expr.comma |
| expression := assignment-expression |
| expression := expression , assignment-expression |
| # expr.const |
| constant-expression := conditional-expression |
| |
| # gram.stmt |
| statement := labeled-statement |
| statement := expression-statement |
| statement := compound-statement |
| statement := selection-statement |
| statement := iteration-statement |
| statement := jump-statement |
| statement := declaration-statement |
| statement := try-block |
| init-statement := expression-statement |
| init-statement := simple-declaration |
| condition := expression |
| condition := decl-specifier-seq declarator brace-or-equal-initializer |
| labeled-statement := IDENTIFIER : statement |
| labeled-statement := CASE constant-expression : statement |
| labeled-statement := DEFAULT : statement |
| expression-statement := expression_opt ; |
| compound-statement := { statement-seq_opt [recover=Brackets] } |
| statement-seq := statement |
| statement-seq := statement-seq statement |
| selection-statement := IF CONSTEXPR_opt ( init-statement_opt condition ) statement [guard] |
| selection-statement := IF CONSTEXPR_opt ( init-statement_opt condition ) statement ELSE statement |
| selection-statement := SWITCH ( init-statement_opt condition ) statement |
| iteration-statement := WHILE ( condition ) statement |
| iteration-statement := DO statement WHILE ( expression ) ; |
| iteration-statement := FOR ( init-statement condition_opt ; expression_opt ) statement |
| iteration-statement := FOR ( init-statement_opt for-range-declaration : for-range-initializer ) statement |
| for-range-declaration := decl-specifier-seq declarator |
| for-range-declaration := decl-specifier-seq ref-qualifier_opt [ identifier-list ] |
| for-range-initializer := expr-or-braced-init-list |
| jump-statement := BREAK ; |
| jump-statement := CONTINUE ; |
| jump-statement := RETURN expr-or-braced-init-list_opt ; |
| jump-statement := coroutine-return-statement |
| jump-statement := GOTO IDENTIFIER ; |
| coroutine-return-statement := CO_RETURN expr-or-braced-init-list_opt ; |
| declaration-statement := block-declaration |
| |
| # gram.dcl |
| declaration-seq := declaration |
| declaration-seq := declaration-seq declaration |
| declaration := block-declaration |
| declaration := nodeclspec-function-declaration |
| declaration := function-definition |
| declaration := template-declaration |
| declaration := deduction-guide |
| declaration := explicit-instantiation |
| declaration := explicit-specialization |
| declaration := export-declaration |
| declaration := linkage-specification |
| declaration := namespace-definition |
| declaration := empty-declaration |
| declaration := module-import-declaration |
| block-declaration := simple-declaration |
| block-declaration := asm-declaration |
| block-declaration := namespace-alias-definition |
| block-declaration := using-declaration |
| block-declaration := using-enum-declaration |
| block-declaration := using-directive |
| block-declaration := static_assert-declaration |
| block-declaration := alias-declaration |
| block-declaration := opaque-enum-declaration |
| nodeclspec-function-declaration := function-declarator ; |
| alias-declaration := USING IDENTIFIER = defining-type-id ; |
| simple-declaration := decl-specifier-seq init-declarator-list_opt ; |
| simple-declaration := decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ; |
| static_assert-declaration := STATIC_ASSERT ( constant-expression ) ; |
| static_assert-declaration := STATIC_ASSERT ( constant-expression , string-literal ) ; |
| empty-declaration := ; |
| # dcl.spec |
| decl-specifier := storage-class-specifier |
| decl-specifier := defining-type-specifier |
| decl-specifier := function-specifier |
| decl-specifier := FRIEND |
| decl-specifier := TYPEDEF |
| decl-specifier := CONSTEXPR |
| decl-specifier := CONSTEVAL |
| decl-specifier := CONSTINIT |
| decl-specifier := INLINE |
| decl-specifier-seq := decl-specifier |
| decl-specifier-seq := decl-specifier decl-specifier-seq [guard] |
| storage-class-specifier := STATIC |
| storage-class-specifier := THREAD_LOCAL |
| storage-class-specifier := EXTERN |
| storage-class-specifier := MUTABLE |
| function-specifier := VIRTUAL |
| function-specifier := explicit-specifier |
| explicit-specifier := EXPLICIT ( constant-expression ) |
| explicit-specifier := EXPLICIT |
| type-specifier := simple-type-specifier |
| type-specifier := elaborated-type-specifier |
| type-specifier := typename-specifier |
| type-specifier := cv-qualifier |
| type-specifier-seq := type-specifier |
| type-specifier-seq := type-specifier type-specifier-seq [guard] |
| defining-type-specifier := type-specifier |
| defining-type-specifier := class-specifier |
| defining-type-specifier := enum-specifier |
| defining-type-specifier-seq := defining-type-specifier |
| defining-type-specifier-seq := defining-type-specifier defining-type-specifier-seq [guard] |
| simple-type-specifier := nested-name-specifier_opt type-name |
| simple-type-specifier := nested-name-specifier TEMPLATE simple-template-id |
| simple-type-specifier := decltype-specifier |
| simple-type-specifier := placeholder-type-specifier |
| simple-type-specifier := nested-name-specifier_opt template-name |
| simple-type-specifier := builtin-type |
| builtin-type := CHAR |
| builtin-type := CHAR8_T |
| builtin-type := CHAR16_T |
| builtin-type := CHAR32_T |
| builtin-type := WCHAR_T |
| builtin-type := BOOL |
| simple-type-specifier := SHORT |
| builtin-type := INT |
| simple-type-specifier := LONG |
| simple-type-specifier := SIGNED |
| simple-type-specifier := UNSIGNED |
| builtin-type := FLOAT |
| builtin-type := DOUBLE |
| builtin-type := VOID |
| type-name := class-name |
| type-name := enum-name |
| type-name := typedef-name |
| elaborated-type-specifier := class-key nested-name-specifier_opt IDENTIFIER |
| elaborated-type-specifier := class-key simple-template-id |
| elaborated-type-specifier := class-key nested-name-specifier TEMPLATE_opt simple-template-id |
| elaborated-type-specifier := elaborated-enum-specifier |
| elaborated-enum-specifier := ENUM nested-name-specifier_opt IDENTIFIER |
| decltype-specifier := DECLTYPE ( expression ) |
| placeholder-type-specifier := type-constraint_opt AUTO |
| placeholder-type-specifier := type-constraint_opt DECLTYPE ( AUTO ) |
| init-declarator-list := init-declarator |
| init-declarator-list := init-declarator-list , init-declarator |
| #! The standard grammar allows: |
| #! 1) an initializer with any declarator, including a function declarator, this |
| #! creates an ambiguity where a function definition is misparsed as a simple |
| #! declaration; |
| #! 2) an function-body with any declarator, includeing a non-function |
| #! declarator, this creates an ambiguity whwere a simple-declaration is |
| #! misparsed as a function-definition; |
| #! We extend the standard declarator to function-declarator and non-function-declarator |
| #! to eliminate these false parses. |
| init-declarator := non-function-declarator initializer_opt |
| init-declarator := function-declarator requires-clause_opt |
| function-declarator := declarator [guard] |
| non-function-declarator := declarator [guard] |
| declarator := ptr-declarator |
| declarator := noptr-declarator parameters-and-qualifiers trailing-return-type |
| ptr-declarator := noptr-declarator |
| ptr-declarator := ptr-operator ptr-declarator |
| noptr-declarator := declarator-id |
| noptr-declarator := noptr-declarator parameters-and-qualifiers |
| noptr-declarator := noptr-declarator [ constant-expression_opt ] |
| noptr-declarator := ( ptr-declarator ) |
| parameters-and-qualifiers := ( parameter-declaration-clause_opt [recover=Brackets] ) cv-qualifier-seq_opt ref-qualifier_opt noexcept-specifier_opt |
| trailing-return-type := -> type-id |
| ptr-operator := * cv-qualifier-seq_opt |
| ptr-operator := & |
| ptr-operator := && |
| ptr-operator := nested-name-specifier * cv-qualifier-seq_opt |
| cv-qualifier-seq := cv-qualifier cv-qualifier-seq_opt |
| cv-qualifier := CONST |
| cv-qualifier := VOLATILE |
| ref-qualifier := & |
| ref-qualifier := && |
| declarator-id := ..._opt id-expression |
| type-id := type-specifier-seq abstract-declarator_opt |
| defining-type-id := defining-type-specifier-seq abstract-declarator_opt |
| abstract-declarator := ptr-abstract-declarator |
| abstract-declarator := noptr-abstract-declarator_opt parameters-and-qualifiers trailing-return-type |
| abstract-declarator := abstract-pack-declarator |
| ptr-abstract-declarator := noptr-abstract-declarator |
| ptr-abstract-declarator := ptr-operator ptr-abstract-declarator_opt |
| noptr-abstract-declarator := noptr-abstract-declarator_opt parameters-and-qualifiers |
| noptr-abstract-declarator := noptr-abstract-declarator_opt [ constant-expression_opt ] |
| noptr-abstract-declarator := ( ptr-abstract-declarator ) |
| abstract-pack-declarator := noptr-abstract-pack-declarator |
| abstract-pack-declarator := ptr-operator abstract-pack-declarator |
| noptr-abstract-pack-declarator := noptr-abstract-pack-declarator parameters-and-qualifiers |
| noptr-abstract-pack-declarator := noptr-abstract-pack-declarator [ constant-expression_opt ] |
| noptr-abstract-pack-declarator := ... |
| #! Custom modifications to avoid nullable clause. |
| parameter-declaration-clause := parameter-declaration-list |
| parameter-declaration-clause := parameter-declaration-list_opt ... |
| parameter-declaration-clause := parameter-declaration-list , ... |
| parameter-declaration-list := parameter-declaration |
| parameter-declaration-list := parameter-declaration-list , parameter-declaration |
| parameter-declaration := decl-specifier-seq declarator |
| parameter-declaration := decl-specifier-seq declarator = initializer-clause |
| parameter-declaration := decl-specifier-seq abstract-declarator_opt |
| parameter-declaration := decl-specifier-seq abstract-declarator_opt = initializer-clause |
| # dcl.init |
| initializer := brace-or-equal-initializer |
| initializer := ( expression-list ) |
| brace-or-equal-initializer := = initializer-clause |
| brace-or-equal-initializer := braced-init-list |
| initializer-clause := assignment-expression |
| initializer-clause := braced-init-list |
| #! Allow mixed designated/non-designated init-list. |
| # This is standard C, and accepted by clang and others as an extension. |
| # FIXME: Decouple recovery from is-there-a-trailing-comma! |
| braced-init-list := { initializer-list [recover=Brackets] } |
| braced-init-list := { initializer-list , } |
| braced-init-list := { } |
| initializer-list := initializer-list-item |
| initializer-list := initializer-list , initializer-list-item |
| initializer-list-item := initializer-clause ..._opt |
| initializer-list-item := designator brace-or-equal-initializer ..._opt |
| designator := . IDENTIFIER |
| #! Array designators are legal in C, and a common extension in C++. |
| designator := [ expression ] |
| expr-or-braced-init-list := expression |
| expr-or-braced-init-list := braced-init-list |
| # dcl.fct |
| function-definition := decl-specifier-seq_opt function-declarator virt-specifier-seq_opt function-body |
| function-definition := decl-specifier-seq_opt function-declarator requires-clause function-body |
| function-body := ctor-initializer_opt compound-statement |
| function-body := function-try-block |
| function-body := = DEFAULT ; |
| function-body := = DELETE ; |
| # dcl.enum |
| enum-specifier := enum-head { enumerator-list_opt } |
| enum-specifier := enum-head { enumerator-list , } |
| enum-head := enum-key enum-head-name_opt enum-base_opt |
| enum-head-name := nested-name-specifier_opt IDENTIFIER |
| opaque-enum-declaration := enum-key enum-head-name enum-base_opt ; |
| enum-key := ENUM |
| enum-key := ENUM CLASS |
| enum-key := ENUM STRUCT |
| enum-base := : type-specifier-seq |
| enumerator-list := enumerator-definition |
| enumerator-list := enumerator-list , enumerator-definition |
| enumerator-definition := enumerator |
| enumerator-definition := enumerator = constant-expression |
| enumerator := IDENTIFIER |
| using-enum-declaration := USING elaborated-enum-specifier ; |
| # basic.namespace |
| namespace-definition := named-namespace-definition |
| namespace-definition := unnamed-namespace-definition |
| namespace-definition := nested-namespace-definition |
| named-namespace-definition := INLINE_opt NAMESPACE IDENTIFIER { namespace-body_opt } |
| unnamed-namespace-definition := INLINE_opt NAMESPACE { namespace-body_opt } |
| nested-namespace-definition := NAMESPACE enclosing-namespace-specifier :: INLINE_opt IDENTIFIER { namespace-body } |
| enclosing-namespace-specifier := IDENTIFIER |
| enclosing-namespace-specifier := enclosing-namespace-specifier :: INLINE_opt IDENTIFIER |
| #! Custom modification to avoid nullable namespace-body. |
| namespace-body := declaration-seq |
| namespace-alias-definition := NAMESPACE IDENTIFIER = qualified-namespace-specifier ; |
| qualified-namespace-specifier := nested-name-specifier_opt namespace-name |
| using-directive := USING NAMESPACE nested-name-specifier_opt namespace-name ; |
| using-declaration := USING using-declarator-list ; |
| using-declarator-list := using-declarator ..._opt |
| using-declarator-list := using-declarator-list , using-declarator ..._opt |
| using-declarator := TYPENAME_opt nested-name-specifier unqualified-id |
| # dcl.asm |
| asm-declaration := ASM ( string-literal ) ; |
| # dcl.link |
| linkage-specification := EXTERN string-literal { declaration-seq_opt } |
| linkage-specification := EXTERN string-literal declaration |
| |
| # gram.module |
| module-declaration := export-keyword_opt module-keyword module-name module-partition_opt ; |
| module-name := module-name-qualifier_opt IDENTIFIER |
| module-partition := : module-name-qualifier_opt IDENTIFIER |
| module-name-qualifier := IDENTIFIER . |
| module-name-qualifier := module-name-qualifier IDENTIFIER . |
| export-declaration := EXPORT declaration |
| export-declaration := EXPORT { declaration-seq_opt } |
| export-declaration := export-keyword module-import-declaration |
| module-import-declaration := import-keyword module-name ; |
| module-import-declaration := import-keyword module-partition ; |
| # FIXME: we don't have header-name in the grammar. Handle these in PP? |
| # module-import-declaration := import-keyword header-name ; |
| global-module-fragment := module-keyword ; declaration-seq_opt |
| private-module-fragment := module-keyword : PRIVATE ; declaration-seq_opt |
| |
| # gram.class |
| class-specifier := class-head { member-specification_opt [recover=Brackets] } |
| class-head := class-key class-head-name class-virt-specifier_opt base-clause_opt |
| class-head := class-key base-clause_opt |
| class-head-name := nested-name-specifier_opt class-name |
| class-virt-specifier := contextual-final |
| class-key := CLASS |
| class-key := STRUCT |
| class-key := UNION |
| member-specification := member-declaration member-specification_opt |
| member-specification := access-specifier : member-specification_opt |
| member-declaration := decl-specifier-seq_opt member-declarator-list_opt ; |
| member-declaration := function-definition |
| member-declaration := using-declaration |
| member-declaration := using-enum-declaration |
| member-declaration := static_assert-declaration |
| member-declaration := template-declaration |
| member-declaration := explicit-specialization |
| member-declaration := deduction-guide |
| member-declaration := alias-declaration |
| member-declaration := opaque-enum-declaration |
| member-declaration := empty-declaration |
| member-declarator-list := member-declarator |
| member-declarator-list := member-declarator-list , member-declarator |
| member-declarator := declarator virt-specifier-seq_opt pure-specifier_opt |
| member-declarator := declarator requires-clause |
| member-declarator := declarator brace-or-equal-initializer |
| member-declarator := IDENTIFIER_opt : constant-expression brace-or-equal-initializer_opt |
| virt-specifier-seq := virt-specifier |
| virt-specifier-seq := virt-specifier-seq virt-specifier |
| virt-specifier := contextual-override |
| virt-specifier := contextual-final |
| pure-specifier := = contextual-zero |
| conversion-function-id := OPERATOR conversion-type-id |
| conversion-type-id := type-specifier-seq conversion-declarator_opt |
| conversion-declarator := ptr-operator conversion-declarator_opt |
| base-clause := : base-specifier-list |
| base-specifier-list := base-specifier ..._opt |
| base-specifier-list := base-specifier-list , base-specifier ..._opt |
| base-specifier := class-or-decltype |
| base-specifier := VIRTUAL access-specifier_opt class-or-decltype |
| base-specifier := access-specifier VIRTUAL_opt class-or-decltype |
| class-or-decltype := nested-name-specifier_opt type-name |
| class-or-decltype := nested-name-specifier TEMPLATE simple-template-id |
| class-or-decltype := decltype-specifier |
| access-specifier := PRIVATE |
| access-specifier := PROTECTED |
| access-specifier := PUBLIC |
| ctor-initializer := : mem-initializer-list |
| mem-initializer-list := mem-initializer ..._opt |
| mem-initializer-list := mem-initializer-list , mem-initializer ..._opt |
| mem-initializer := mem-initializer-id ( expression-list_opt ) |
| mem-initializer := mem-initializer-id braced-init-list |
| mem-initializer-id := class-or-decltype |
| mem-initializer-id := IDENTIFIER |
| |
| # gram.over |
| operator-function-id := OPERATOR operator-name |
| operator-name := NEW |
| operator-name := DELETE |
| operator-name := NEW [ ] |
| operator-name := DELETE [ ] |
| operator-name := CO_AWAIT |
| operator-name := ( ) |
| operator-name := [ ] |
| operator-name := -> |
| operator-name := ->* |
| operator-name := ~ |
| operator-name := ! |
| operator-name := + |
| operator-name := - |
| operator-name := * |
| operator-name := / |
| operator-name := % |
| operator-name := ^ |
| operator-name := & |
| operator-name := | |
| operator-name := = |
| operator-name := += |
| operator-name := -= |
| operator-name := *= |
| operator-name := /= |
| operator-name := %= |
| operator-name := ^= |
| operator-name := &= |
| operator-name := |= |
| operator-name := == |
| operator-name := != |
| operator-name := < |
| operator-name := > |
| operator-name := <= |
| operator-name := >= |
| operator-name := <=> |
| operator-name := ^^ |
| operator-name := || |
| operator-name := << |
| operator-name := greatergreater |
| operator-name := <<= |
| operator-name := >>= |
| operator-name := ++ |
| operator-name := -- |
| operator-name := , |
| literal-operator-id := OPERATOR string-literal IDENTIFIER |
| literal-operator-id := OPERATOR user-defined-string-literal |
| |
| # gram.temp |
| template-declaration := template-head declaration |
| template-declaration := template-head concept-definition |
| template-head := TEMPLATE < template-parameter-list > requires-clause_opt |
| template-parameter-list := template-parameter |
| template-parameter-list := template-parameter-list , template-parameter |
| requires-clause := REQUIRES constraint-logical-or-expression |
| constraint-logical-or-expression := constraint-logical-and-expression |
| constraint-logical-or-expression := constraint-logical-or-expression || constraint-logical-and-expression |
| constraint-logical-and-expression := primary-expression |
| constraint-logical-and-expression := constraint-logical-and-expression && primary-expression |
| template-parameter := type-parameter |
| template-parameter := parameter-declaration |
| type-parameter := type-parameter-key ..._opt IDENTIFIER_opt |
| type-parameter := type-parameter-key IDENTIFIER_opt = type-id |
| type-parameter := type-constraint ..._opt IDENTIFIER_opt |
| type-parameter := type-constraint IDENTIFIER_opt = type-id |
| type-parameter := template-head type-parameter-key ..._opt IDENTIFIER_opt |
| type-parameter := template-head type-parameter-key IDENTIFIER_opt = id-expression |
| type-parameter-key := CLASS |
| type-parameter-key := TYPENAME |
| type-constraint := nested-name-specifier_opt concept-name |
| type-constraint := nested-name-specifier_opt concept-name < template-argument-list_opt > |
| simple-template-id := template-name < template-argument-list_opt > |
| template-id := simple-template-id |
| template-id := operator-function-id < template-argument-list_opt > |
| template-id := literal-operator-id < template-argument-list_opt > |
| template-argument-list := template-argument ..._opt |
| template-argument-list := template-argument-list , template-argument ..._opt |
| template-argument := constant-expression |
| template-argument := type-id |
| template-argument := id-expression |
| constraint-expression := logical-or-expression |
| deduction-guide := explicit-specifier_opt template-name ( parameter-declaration-list_opt ) -> simple-template-id ; |
| concept-definition := CONCEPT concept-name = constraint-expression ; |
| concept-name := IDENTIFIER |
| typename-specifier := TYPENAME nested-name-specifier IDENTIFIER |
| typename-specifier := TYPENAME nested-name-specifier TEMPLATE_opt simple-template-id |
| explicit-instantiation := EXTERN_opt TEMPLATE declaration |
| explicit-specialization := TEMPLATE < > declaration |
| |
| # gram.except |
| try-block := TRY compound-statement handler-seq |
| function-try-block := TRY ctor-initializer_opt compound-statement handler-seq |
| handler-seq := handler handler-seq_opt |
| handler := CATCH ( exception-declaration ) compound-statement |
| exception-declaration := type-specifier-seq declarator |
| exception-declaration := type-specifier-seq abstract-declarator_opt |
| noexcept-specifier := NOEXCEPT ( constant-expression ) |
| noexcept-specifier := NOEXCEPT |
| |
| # gram.cpp |
| identifier-list := IDENTIFIER |
| identifier-list := identifier-list , IDENTIFIER |
| |
| # gram.lex |
| #! As we use clang lexer, most of lexical symbols are not needed, we only add |
| #! needed literals. |
| literal := integer-literal |
| literal := character-literal |
| literal := floating-point-literal |
| literal := string-literal |
| literal := boolean-literal |
| literal := pointer-literal |
| literal := user-defined-literal |
| integer-literal := NUMERIC_CONSTANT [guard] |
| character-literal := CHAR_CONSTANT [guard] |
| character-literal := WIDE_CHAR_CONSTANT [guard] |
| character-literal := UTF8_CHAR_CONSTANT [guard] |
| character-literal := UTF16_CHAR_CONSTANT [guard] |
| character-literal := UTF32_CHAR_CONSTANT [guard] |
| floating-point-literal := NUMERIC_CONSTANT [guard] |
| string-literal-chunk := STRING_LITERAL [guard] |
| string-literal-chunk := WIDE_STRING_LITERAL [guard] |
| string-literal-chunk := UTF8_STRING_LITERAL [guard] |
| string-literal-chunk := UTF16_STRING_LITERAL [guard] |
| string-literal-chunk := UTF32_STRING_LITERAL [guard] |
| #! Technically, string concatenation happens at phase 6 which is before parsing, |
| #! so it doesn't belong to the grammar. However, we extend the grammar to |
| #! support it, to make the pseudoparser fully functional on practical code. |
| string-literal := string-literal-chunk |
| string-literal := string-literal string-literal-chunk |
| user-defined-literal := user-defined-integer-literal |
| user-defined-literal := user-defined-floating-point-literal |
| user-defined-literal := user-defined-string-literal |
| user-defined-literal := user-defined-character-literal |
| user-defined-integer-literal := NUMERIC_CONSTANT [guard] |
| user-defined-string-literal-chunk := STRING_LITERAL [guard] |
| user-defined-string-literal-chunk := WIDE_STRING_LITERAL [guard] |
| user-defined-string-literal-chunk := UTF8_STRING_LITERAL [guard] |
| user-defined-string-literal-chunk := UTF16_STRING_LITERAL [guard] |
| user-defined-string-literal-chunk := UTF32_STRING_LITERAL [guard] |
| user-defined-string-literal := user-defined-string-literal-chunk |
| user-defined-string-literal := string-literal-chunk user-defined-string-literal |
| user-defined-string-literal := user-defined-string-literal string-literal-chunk |
| user-defined-floating-point-literal := NUMERIC_CONSTANT [guard] |
| user-defined-character-literal := CHAR_CONSTANT [guard] |
| user-defined-character-literal := WIDE_CHAR_CONSTANT [guard] |
| user-defined-character-literal := UTF8_CHAR_CONSTANT [guard] |
| user-defined-character-literal := UTF16_CHAR_CONSTANT [guard] |
| user-defined-character-literal := UTF32_CHAR_CONSTANT [guard] |
| boolean-literal := FALSE |
| boolean-literal := TRUE |
| pointer-literal := NULLPTR |
| |
| #! Contextual keywords -- clang lexer always lexes them as identifier tokens. |
| #! Placeholders for literal text in the grammar that lex as other things. |
| contextual-override := IDENTIFIER [guard] |
| contextual-final := IDENTIFIER [guard] |
| contextual-zero := NUMERIC_CONSTANT [guard] |
| module-keyword := IDENTIFIER [guard] |
| import-keyword := IDENTIFIER [guard] |
| export-keyword := IDENTIFIER [guard] |
| |
| #! greatergreater token -- clang lexer always lexes it as a single token, we |
| #! split it into two tokens to make the GLR parser aware of the nested-template |
| #! case. |
| greatergreater := > > |
| |
| #! C++ predefined identifier, __func__ [dcl.fct.def.general] p8 |
| #! FIXME: add other (MSVC, GNU extension) predefined identifiers. |
| primary-expression := predefined-expression |
| predefined-expression := __FUNC__ |