# This is a C++ grammar from the C++ standard [1].
#
# The grammar is a superset of the true grammar requring semantic constraints to
# resolve ambiguities. 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 EOF
_ := statement-seq EOF
_ := declaration-seq EOF
# gram.key
#! we don't distinguish between namespaces and namespace aliases, as it's hard
#! and uninteresting.
namespace-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 := :: [guard]
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 ; [guard]
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 := SHORT
simple-type-specifier := LONG
simple-type-specifier := SIGNED
simple-type-specifier := UNSIGNED
simple-type-specifier := builtin-type
#! builtin-type added to aid in classifying which specifiers may combined.
builtin-type := CHAR
builtin-type := CHAR8_T
builtin-type := CHAR16_T
builtin-type := CHAR32_T
builtin-type := WCHAR_T
builtin-type := BOOL
builtin-type := INT
builtin-type := FLOAT
builtin-type := DOUBLE
builtin-type := VOID
#! Unlike C++ standard grammar, we don't distinguish the underlying type (class,
#! enum, typedef) of the IDENTIFIER, as these ambiguities are "local" and don't
#! affect the final parse tree. Eliminating them gives a significant performance
#! boost to the parser.
type-name := IDENTIFIER
type-name := simple-template-id
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 type-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 member-declarator-list_opt ;
member-declaration := member-declarator-list ;
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 := function-declarator virt-specifier-seq_opt pure-specifier_opt
member-declarator := function-declarator requires-clause
member-declarator := non-function-declarator brace-or-equal-initializer_opt
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__