---
title: Improve readability of the C++ grammar by adding a syntax for groups and repetitions
document: P3891R1
date: 2026-05-11
audience: CWG, LWG
reply-to:
  - "Jan Schultke < <janschultke@gmail.com>"
paper-type: proposal
---

Purely editorial changes should be made to the C++ grammar to improve readability, such as adding a syntax for groups and repetitions.



### Changes since R0

- In §4.3.3. Alternative brackets for groups, also mentioned DOUBLE ANGLE QUOTATION MARK characters

## Introduction

The current C++ syntax notation as specified in [[syntax]](https://eel.is/c++draft/syntax) and summarized in [[gram]](https://eel.is/c++draft/gram) has only a handful of features:

- concatenation, such as pp-number identifier-continue,
- alternatives or unions, sometimes specified using "one of", and
- optional expansions, such as long-suffix<sub>opt</sub>.

Two notably absent features are grouping and repetition. This leads to many cases of low expressiveness and grammatical bloat, like our many X-seq and X-list rules:

**declaration-seq:**
: declaration declaration-seq<sub>opt</sub>
**template-parameter-list:**
: template-parameter
: template-parameter-list `,` template-parameter

In plain English, we say:

> A declaration-seq is a declaration followed by an optional declaration-seq.
> 
> A template-parameter-list is either a single template-parameter or a template-parameter-list, followed by a comma token, followed by a template-parameter.

No reasonable person should teach the language syntax in those words, but that is what the grammar says. This means that any reader (or author of grammar changes) has to mentally deobfuscate the grammar into something intuitive, like:

> A declaration-seq is one or more declarations.
> 
> A template-parameter-list is one or more template-parameters, separated by a comma token.

This proposal adds grouping and repetition, which obsoletes all X-seq nonterminals and simplifies the specification in many places.

## Motivating example

With these new features, more concise grammar is possible:

| Before | After |
| --- | --- |
| compound-statement:
{ statement-seq<sub>opt</sub> label-seq<sub>opt</sub> }
statement-seq:
statement statement-seq<sub>opt</sub>
label-seq:
label label-seq<sub>opt</sub> | compound-statement:
{ statement<sub>seq opt</sub> label<sub>seq opt</sub> } |
| initializer-list:
initializer-clause ...<sub>opt</sub>
initializer-list , initializer-clause ...<sub>opt</sub> | initializer-list:
initializer-clause ...<sub>opt</sub> ⟪ , initializer-clause ...<sub>opt</sub> ⟫<sub>seq opt</sub> |
| identifier:
identifier-start
identifier identifier-continue | identifier:
identifier-start identifier-continue<sub>seq opt</sub> |

See §5. Core wording and §6. Library wording for many more concrete examples.

Notably, the boilerplate X-seq rules are eliminated. The amount of recursion necessary is also greatly reduced.

## Design

### Design constraints

- The new grammar syntax needs to be easily writable both in proposals that contain grammatical changes as well as the standard itself. Note that authors frequently use strikethrough or underline text to indicate insertions and deletions, in addition to color. Those styles should be avoided.
- The C++ grammar already contains all sorts of tokens without any delimiters like quotes, which we probably want to keep that way due to familiarity. This means that any brackets and punctuation may visually conflict with C++ tokens.
- Some users may rely on assistive technology such as screen readers. This means that if font choice is the only distinction between e.g. C++ tokens and the new grammar features, the standard would be inaccessible to those users.

### New syntax

**X<sub>seq</sub>**
: One or more repetitions of X.
    This replaces X-seq.
    The new syntax is similar to <sub>opt</sub>, so it is obviously feasible in the standard draft.
    Proposal authors can use subscript text.
    Screen readers would pronounce "seq" uninterrupted (ignoring subscript),
    which is fine as long as "seq" is *only* used in this operator form.
**X<sub>seq opt</sub>**
: Zero or more repetitions of X.
    This replaces X-seq<sub>opt</sub>.
**⟪ X yyy ⟫**
: Groups X and yyy,
    which allows applying <sub>opt</sub>, <sub>seq</sub>, and <sub>seq opt</sub> to multiple elements.
    The characters used here are
    U+27EA MATHEMATICAL LEFT DOUBLE ANGLE BRACKET and
    U+27EB MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET.
    These get pronounced distinct from other brackets by screen readers,
    are handled well by many fonts,
    and are sufficiently visually distinct from (, [, {,
    and <.
    LaTeX packages like MnSymbol provide these characters.
    Paper authors can copy and paste these Unicode characters,
    use HTML character references such as `&#x27EA;`,
    or use text editor extensions like
    [Insert Unicode](https://marketplace.visualstudio.com/items?itemName=brunnerh.insert-unicode)
    for typing these.

### Alternatives considered

#### Alternative to <sub>seq</sub> and <sub>seq opt</sub>

An alternatively syntax to <sub>seq</sub> and <sub>seq opt</sub> briefly considered was superscript ＋ and superscript 🞰 for one-or-more and zero-or-more repetitions, respectively. These characters are widely used in regular expression with this meaning, and superscript asterisks denote the [Kleene Star](https://en.wikipedia.org/wiki/Kleene_star) in computer science papers. However, these characters can be hard to distinguish based on font weight and font family. Replacing X-seq with X<sub>seq</sub> also feels like a more natural transition for C++, and means that existing teaching resource referencing the C++ grammar would be easy to relate to the new format.

#### <sub>seq</sub> <sub>opt</sub> vs. <sub>opt</sub> <sub>seq</sub>

Both notations are equivalent in the sense that the same inputs would be matched. <sub>opt</sub> <sub>seq</sub> could be argued to be "more natural" because it translates to "optional sequence".

However, if we consider <sub>seq</sub> and <sub>opt</sub> to be postfix unary operators, then X<sub>opt</sub> <sub>seq</sub> would be a sequence where every X is individually optional. This is more complex, and the parallel to our existing X-seq<sub>opt</sub> uses in the grammar is less obvious. It also results in an infinitely ambiguous concrete syntax tree: is the declaration<sub>opt</sub> <sub>seq</sub> `int x;` a declaration followed by an infinite sequence of absent declarations, or is it one absent declaration, followed by one declaration, followed by an infinite sequence of absent declarations? The answer is: it doesn't matter because implementations will figure out how to match declaration<sub>opt</sub> <sub>seq</sub> either way, but we should prefer the syntax that doesn't raise such questions in the first place.

#### Alternative brackets for groups

There are many possible [Unicode bracket characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[[%3ABidi_Paired_Bracket_Type%3DOpen%3A]|[%3ABidi_Paired_Bracket_Type%3DClose%3A]]) that could have been used instead. However,

- some are too exotic, like ⟅ S-SHAPED BAG DELIMITERS ⟆,
- some are just font variations of existing brackets, like FULLWIDTH or WHITE (which means that users with visual impairment could mistake these too easily, especially with bad choice of font),
- some are used too commonly for mathematical operations, like ⌈ CEILING ⌉,
- some rely too much on "good fonts" and become too visually similar to parentheses for bold font weight, like ⟬ MATHEMATICAL WHITE TORTOISE SHELL BRACKETS ⟭,
- some are too pointy in most fonts and may be confused with bitwise shift operators, like « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK and » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK,
- etc.

The chosen ⟪ MATHEMATICAL DOUBLE ANGLE BRACKETS ⟫ do not suffer from any of these issues, although they are visually similar to 《 DOUBLE ANGLE BRACKETS 》 used in [Chinese punctuation for proper nouns](https://en.wikipedia.org/wiki/Chinese_punctuation_for_proper_nouns).

## Core wording

The changes are relative to [[N5014]](https://wg21%2elink/n5014).

### [syntax]

Change [[syntax]](https://eel.is/c++draft/syntax) as follows:

1 In the syntax notation used in this document, <del>syntactic categories</del> <ins>non-terminal symbols</ins> are indicated by italic, sans-serif type, and <del>literal words and characters</del> <ins>terminal symbols</ins> in constant width type. <ins>A syntactic element is a terminal symbol, non-terminal symbol, or a group of syntactic elements. A group of syntactic elements is delimited by ⟪ and ⟫. Consecutive syntactic elements are listed from left to right.</ins> Alternatives are listed on separate lines except in a few cases where a <del>long</del> set of alternatives is marked by the phrase one of. If the text of an alternative is too long to fit on a line, the text is continued on subsequent lines indented from the first one.

<ins>2</ins> An optional <del>terminal or non-terminal symbol</del> <ins>syntactic element</ins> is indicated by the <ins>postfix</ins> subscript <sub>opt</sub><del>, so</del> <ins>.</ins>

: <del>{ expression<sub>opt</sub> }</del>

<del>indicates an optional expression enclosed in braces.</del>

<ins>One or more repetitions of a syntactic element are indicated by the postfix subscript <sub>seq</sub>.</ins>

<ins>[*Example*:</ins>

**<ins>initializer-list:</ins>**
: <ins>initializer-clause ...<sub>opt</sub> ⟪ , initializer-clause ...<sub>opt</sub> ⟫<sub>seq opt</sub></ins>

<ins>This notation means that the non-terminal symbol initializer-list is matched by an initializer-clause, optionally followed by ..., followed by zero or more repetitions of `,`, initializer-clause, and optionally .... — *end example*]</ins>

<del>2</del> <ins>3 <ins>[*Note*:</ins></ins> Names for syntactic categories have generally been chosen according to the following rules:

- X-name is a use of an identifier in a context that determines its meaning (e.g., class-name, typedef-name).
- X-id is an identifier with no context-dependent meaning (e.g., qualified-id).
- <del>X-seq is one or more Xs without intervening delimiters (e.g., declaration-seq is a sequence of declarations).</del>
- X-list is one or more Xs separated by intervening commas (e.g., identifier-list is a sequence of identifiers separated by commas).

<ins>— *end note*]</ins>

### Bulk operations

Please read the editorial notes below. This diff looks small, but it's a *massive* change with huge implications for the document.

Replace the following non-terminals in the document with X<sub>seq</sub> :

<del>n-char-sequence</del> <ins>n-char<sub>seq</sub></ins> <del>simple-hexadecimal-digit-sequence</del> <ins>hexadecimal-digit<sub>seq</sub></ins> <del>h-char-sequence</del> <ins>h-char<sub>seq</sub></ins> <del>q-char-sequence</del> <ins>q-char<sub>seq</sub></ins> <del>c-char-sequence</del> <ins>c-char<sub>seq</sub></ins> <del>simple-octal-digit-sequence</del> <ins>octal-digit<sub>seq</sub></ins> <del>s-char-sequence</del> <ins>s-char<sub>seq</sub></ins> <del>r-char-sequence</del> <ins>r-char<sub>seq</sub></ins> <del>d-char-sequence</del> <ins>d-char<sub>seq</sub></ins> <del>declaration-seq</del> <ins>declaration<sub>seq</sub></ins> <del>attribute-specifier-seq</del> <ins>attribute-specifier<sub>seq</sub></ins> <del>function-contract-specifier-seq</del> <ins>function-contract-specifier<sub>seq</sub></ins> <del>lambda-specifier-seq</del> <ins>lambda-specifier<sub>seq</sub></ins> <del>requirement-seq</del> <ins>requirement<sub>seq</sub></ins> <del>statement-seq</del> <ins>statement<sub>seq</sub></ins> <del>label-seq</del> <ins>label<sub>seq</sub></ins> <del>cv-qualifier-seq</del> <ins>cv-qualifier<sub>seq</sub></ins> <del>virt-specifier-seq</del> <ins>virt-specifier<sub>seq</sub></ins> <del>balanced-token-seq</del> <ins>balanced-token<sub>seq</sub></ins> <del>class-property-specifier-seq</del> <ins>class-property-specifier<sub>seq</sub></ins> <del>handler-seq</del> <ins>handler<sub>seq</sub></ins> <del>embed-parameter-seq</del> <ins>embed-parameter<sub>seq</sub></ins> <del>pp-balanced-token-seq</del> <ins>pp-balanced-token<sub>seq</sub></ins>

Remove all definitions of the replaced non-terminals. These are all of the form:

**X-seq :**
: X X-seq<sub>opt</sub>

This bulk edit results in many places where these syntactic elements are referenced in prose, such as in [[lex.universal.char] paragraph 3](https://eel.is/c++draft/lex.universal.char#3):

A universal-character-name that is a named-universal-character designates the corresponding character in the Unicode Standard (chapter 4.8 Name) if the <del>n-char-sequence</del> <ins>n-char<sub>seq</sub></ins> is equal to its character name […]

In my opinion, this is acceptable. Referring to X<sub>seq</sub> as a single construct isn't necessarily wrong, even though we've historically tried to reference non-terminal symbols as much as possible. That practice isn't feasible anyway when the grammar is much more powerful and there are far fewer non-terminals.

From an English perspective, both X-seq and X<sub>seq</sub> are pronounced "X sequence" or "X seq.", so no problem is caused.

Due to this wording choice, we talk about an X<sub>seq</sub> as if it still was an X-seq non-terminal. For example, we say

[…] from any declaration in the <del>declaration-seq</del> <ins>declaration<sub>seq</sub></ins> of the translation-unit.

in [[module.global.frag] paragraph 4](https://eel.is/c++draft/module.global.frag#4), rather than considering the declaration to belong directly to a translation-unit. That is, the middle-man in translation-unit → declaration-seq → declaration is eliminated. This would allow using (direct) of instead of (indirect) in, if we wanted to.

In my opinion, simply applying the bulk edit is fine; pointing out the indirection through <sub>seq</sub> would be an optional and redundant wording choice, but it wouldn't be incorrect.

Perhaps, CWG could decide on a consistent policy so that we *always* call out the <sub>seq</sub> indirection, or *never* do so.

### [lex.name]

Change [[lex.name] identifier](https://eel.is/c++draft/lex.name#nt:identifier) as follows:

**identifier:**
: identifier-start <ins>identifier-continue<sub>seq opt</sub></ins>
: <del>identifier identifier-continue</del>

### [lex.icon]

Change [[lex.icon] binary-literal](https://eel.is/c++draft/lex.icon#nt:binary-literal) as follows:

**binary-literal:**
: <ins>⟪ one of</ins> 0b <ins>0B ⟫</ins> binary-digit <ins>⟪ `'`<sub>opt</sub> binary-digit ⟫<sub>seq opt</sub></ins>
: <del>0B binary-digit</del>
: <del>binary-literal `'`<sub>opt</sub> binary-digit</del>

Change [[lex.icon] octal-literal](https://eel.is/c++draft/lex.icon#nt:octal-literal) as follows:

**octal-literal:**
: 0 <ins>⟪ `'`<sub>opt</sub> octal-digit ⟫<sub>seq opt</sub></ins>
: <del>octal-literal `'`<sub>opt</sub> octal-digit</del>

Change [[lex.icon] decimal-literal](https://eel.is/c++draft/lex.icon#nt:decimal-literal) as follows:

**decimal-literal:**
: nonzero-digit <ins>⟪ `'`<sub>opt</sub> digit ⟫<sub>seq opt</sub></ins>
: <del>decimal-literal `'`<sub>opt</sub> digit</del>

Do not change [[lex.icon] hexadecimal-literal](https://eel.is/c++draft/lex.icon#nt:hexadecimal-literal):

> **hexadecimal-literal:**
> : hexadecimal-prefix hexadecimal-digit-sequence

Change [[lex.icon] hexadecimal-digit-sequence](https://eel.is/c++draft/lex.icon#nt:hexadecimal-digit-sequence) as follows:

**hexadecimal-digit-sequence:**
: hexadecimal-digit <ins>⟪ '<sub>opt</sub> hexadecimal-digit ⟫<sub>seq opt</sub></ins>
: <del>hexadecimal-digit-sequence '<sub>opt</sub> hexadecimal-digit</del>

hexadecimal-digit-sequence is used in so many places that it would be inconvenient to expand it (into hexadecimal-literal and other non-terminals). See also [[lex.fcon]](https://eel.is/c++draft/lex.fcon).

### [lex.fcon]

Change [[lex.fcon] exponent-part](https://eel.is/c++draft/lex.fcon#nt:exponent-part) as follows:

**exponent-part:**
: <ins>⟪ one of</ins> e <ins>E ⟫</ins> sign<sub>opt</sub> digit-sequence
: <del>E sign<sub>opt</sub> digit-sequence</del>

Change [[lex.fcon] binary-exponent-part](https://eel.is/c++draft/lex.fcon#nt:binary-exponent-part) as follows:

**binary-exponent-part:**
: <ins>⟪ one of</ins> p <ins>P ⟫</ins> sign<sub>opt</sub> digit-sequence
: <del>P sign<sub>opt</sub> digit-sequence</del>

Change [[lex.fcon] digit-sequence](https://eel.is/c++draft/lex.fcon#nt:digit-sequence) as follows:

**digit-sequence:**
: digit <ins>⟪ '<sub>opt</sub> digit ⟫<sub>seq opt</sub></ins>
: <del>digit-sequence '<sub>opt</sub> digit</del>

### [lex.string]

Change the grammar in [[lex.string]](https://eel.is/c++draft/lex.string) as follows:

**[…]**
**raw-string:**
: " d-char<del>-sequence</del> <ins><sub>seq</sub></ins> <sub>opt</sub> ( r-char<del>-sequence</del> <ins><sub>seq</sub></ins> <sub>opt</sub> d-char<del>-sequence</del> <ins><sub>seq</sub></ins> <sub>opt</sub> ) "
**<del>r-char-sequence:</del>**
: <del>r-char r-char-sequence<sub>opt</sub></del>
**r-char:**
: any member of the translation character set,
    except a U+0029 RIGHT PARENTHESIS
    followed by the initial
    <del>d-char-sequence (which may be empty)</del>
<ins>d-char<sub>seq opt</sub></ins>
    followed by U+0022 QUOTATION MARK
**[…]**

The are further changes in this area resulting from the § Bulk operations. The important change in this area is the redefinition of r-char.

The current "(which may be empty)" seems incorrect anyway; a d-char-sequence cannot be empty, but it is optional. The usual wording is (found in *many* places)

> The optional attribute-specifier-seq […]

For consistency, we could say

> […] by the initial optional d-seq<sub>seq</sub> followed by U+0022 QUOTATION MARK

However, "initial optional" feels a bit clunky.

### [dcl.spec.general]

Change [[dcl.spec.general] decl-specifier-seq](https://eel.is/c++draft/dcl.spec.general#nt:decl-specifier-seq) as follows:

**<del>decl-specifier-seq</del> <ins>decl-specifiers-and-attributes</ins>:**
: decl-specifier<ins><sub>seq</sub></ins> attribute-specifier<del>-seq</del> <ins><sub>seq</sub></ins> <sub>opt</sub>
: <del>decl-specifier decl-specifier-seq</del>

Replace all occurrences of <del>decl-specifier-seq</del> with <ins>decl-specifiers-and-attributes</ins>.

### [dcl.type.general]

Change [[dcl.type.general] type-specifier-seq](https://eel.is/c++draft/dcl.type.general#nt:type-specifier-seq) as follows:

**<del>type-specifier-seq</del> <ins>type-specifiers-and-attributes</ins>:**
: type-specifier<ins><sub>seq</sub></ins> attribute-specifier<del>-seq</del> <ins><sub>seq</sub></ins> <sub>opt</sub>
: <del>type-specifier type-specifier-seq</del>

Replace all occurrences of <del>type-specifier-seq</del> with <ins>type-specifiers-and-attributes</ins>.

Change [[dcl.type.general] defining-type-specifier-seq](https://eel.is/c++draft/dcl.type.general#nt:defining-type-specifier-seq) as follows:

**<del>defining-type-specifier-seq</del> <ins>defining-type-specifiers-and-attributes</ins>:**
: defining-type-specifier<ins><sub>seq</sub></ins> attribute-specifier<del>-seq</del> <ins><sub>seq</sub></ins> <sub>opt</sub>
: <del>defining-type-specifier type-specifier-seq</del>

Replace all occurrences of <del>defining-type-specifier-seq</del> with <ins>defining-type-specifiers-and-attributes</ins>.

### [dcl.decl.general]

Change [[dcl.decl.general] ptr-declarator](https://eel.is/c++draft/dcl.decl.general#nt:ptr-declarator) as follows:

**ptr-declarator:**
: <ins>ptr-operator<sub>seq opt</sub></ins> noptr-declarator
: <del>ptr-operator ptr-declarator</del>

### [dcl.fct]

Change [[dcl.fct] parameter-declaration-list](https://eel.is/c++draft/dcl.fct#nt:parameter-declaration-list) as follows:

**parameter-declaration-list:**
: parameter-declaration <ins>⟪ , parameter-declaration ⟫<sub>seq opt</sub></ins>
: <del>parameter-declaration-list , parameter-declaration</del>

### [dcl.init.general]

Change [[dcl.fct] initializer-list](https://eel.is/c++draft/dcl.fct#nt:initializer-list) as follows:

**initializer-list:**
: initializer-clause ...<sub>opt</sub> <ins>⟪ , initializer-clause ...<sub>opt</sub> ⟫<sub>seq opt</sub></ins>
: <del>initializer-list , initializer-clause</del> ...<sub>opt</sub>

Change [[dcl.fct] designated-initializer-list](https://eel.is/c++draft/dcl.fct#nt:designated-initializer-list) as follows:

**designated-initializer-list:**
: designated-initializer-clause <ins>⟪ , designated-initializer-clause ⟫<sub>seq opt</sub></ins>
: <del>designated-initializer-list , designated-initializer-clause</del>

### [dcl.enum]

Change [[dcl.enum] enumerator-list](https://eel.is/c++draft/dcl.enum#nt:enumerator-list) as follows:

**enumerator-list:**
: enumerator-definition <ins>⟪ , enumerator-definition ⟫<sub>seq opt</sub></ins>
: <del>enumerator-list , enumerator-definition</del>

Change [[dcl.enum] enumerator-definition](https://eel.is/c++draft/dcl.enum#nt:enumerator-definition) as follows:

**enumerator-definition:**
: <del>enumerator</del>
: enumerator <ins>⟪</ins> = constant-expression <ins>⟫<sub>opt</sub></ins>

### [namespace.udecl]

Change [[namespace.udecl] using-declarator-list](https://eel.is/c++draft/namespace.udecl#nt:using-declarator-list) as follows:

**using-declarator-list:**
: using-declarator ...<sub>opt</sub> <ins>⟪ , using-declarator ...<sub>opt</sub> ⟫<sub>seq opt</sub></ins>
: <del>using-declarator-list , using-declarator ...<sub>opt</sub></del>

### [dcl.attr.grammar]

Do not change [[dcl.attr.grammar] attribute-list](https://eel.is/c++draft/dcl.attr.grammar#nt:attribute-list):

> **attribute-list:**
> : attribute<sub>opt</sub>
> : attribute-list , attribute<sub>opt</sub>
> : attribute ...
> : attribute-list , attribute ...

What makes attribute-list particularly difficult to change is that each-comma separated element can either be empty, an attribute, or ⟪ attribute ... ⟫, but not just ....

I did not want to factor out a new attribute-clause non-terminal, which seems necessary to make attribute-list non-recursive in a simple way. We can always make that change later.

Change [[dcl.fct] annotation-list](https://eel.is/c++draft/dcl.fct#nt:annotation-list) as follows:

**annotation-list:**
: annotation ...<sub>opt</sub> <ins>⟪ , annotation ...<sub>opt</sub> ⟫<sub>seq opt</sub></ins>
: <del>annotation-list , annotation ...<sub>opt</sub></del>

### [stmt.select.general]

Change [[stmt.select.general] [selection], [statement]](https://eel.is/c++draft/stmt.select.general#selection-statement) as follows:

**selection-statement:**
: `if` `constexpr`<sub>opt</sub> ( init-statement<sub>opt</sub> condition ) statement <ins>⟪ `else` statement ⟫<sub>opt</sub></ins>
: <del>`if` `constexpr`<sub>opt</sub> ( init-statement<sub>opt</sub> condition ) statement `else` statement</del>
: `if` !<sub>opt</sub> `consteval` compound-statement <ins>⟪ `else` statement ⟫<sub>opt</sub></ins>
: <del>`if` !<sub>opt</sub> `consteval` compound-statement `else` statement</del>
: `switch` ( init-statement<sub>opt</sub> condition ) statement

### [stmt.if]

Change [[stmt.if] paragraph 1](https://eel.is/c++draft/stmt.if#1) as follows:

If the condition ([[stmt.pre]](https://eel.is/c++draft/stmt.pre)) yields `true`, the first substatement is executed. If the `else` <del>part</del> <ins>statement</ins> of the <del>selection statement</del> <ins>selection-statement</ins> is present and the condition yields `false`, the second substatement is executed. If the first substatement is reached via a label, the condition is not evaluated and the second substatement is not executed. <del>In the second form of `if` statement (the one including `else`),</del> <ins>In an `if` statement where the `else` statement of the selection-statement is present,</ins> if the first substatement is also an `if` statement <ins>,</ins> then that inner `if` statement shall contain an `else` <del>part</del> <ins>statement</ins>.

`if consteval` is not relevant to this change because it can only contain a compound-statement as its first substatement.

"Part" is removed because we never define what an "`else` part" is, so this wording smells a bit fishy.

### [dcl.pre]

Change [[dcl.pre] sb-identifier-list](https://eel.is/c++draft/dcl.pre#nt:sb-identifier-list) as follows:

**sb-identifier-list:**
: sb-identifier <ins>⟪ , sb-identifier ⟫<sub>seq opt</sub></ins>
: <del>sb-identifier-list , sb-identifier</del>

Change [[dcl.pre] static_assert-declaration](https://eel.is/c++draft/dcl.pre#nt:static_assert-declaration) as follows:

**static_assert-declaration:**
: `static_assert` ( constant-expression <ins>⟪ , static_assert-message ⟫<sub>opt</sub></ins> )
: <del>`static_assert` ( constant-expression , static_assert-message )</del>

## Library wording

The changes are relative to [[N5014]](https://wg21%2elink/n5014).

### [locale.numpunct.general]

Change [[locale.numpunct.general] paragraph 2](https://eel.is/c++draft/locale.numpunct.general#2) as follows:

[…] Integer values have the format:

**units:**
: <del>digits</del>
: <del>digits thousands-sep units</del>
: <ins>digit<sub>seq</sub> ⟪ thousands-sep digit<sub>seq</sub> ⟫<sub>seq opt</sub></ins>
**<del>digits:</del>**
: <del>digit digits<sub>opt</sub></del>

and floating-point values have:

**floatval:**
: sign<sub>opt</sub> units fractional<sub>opt</sub> exponent<sub>opt</sub>
: sign<sub>opt</sub> decimal-point <del>digits</del> <ins>digit<sub>seq</sub></ins> exponent<sub>opt</sub>
**fractional:**
: decimal-point <del>digits</del> <ins>digit<sub>seq</sub></ins> <sub>opt</sub>
**exponent:**
: <del>e</del> <ins>⟪ one of e E ⟫</ins> sign<sub>opt</sub> <del>digits</del> <ins>digit<sub>seq</sub></ins>
**<del>e:</del>**
: <del>e</del>
: <del>E</del>

where the number of digits between thousands-seps is as specified by do_grouping(). For parsing, if the <del>digits</del> <ins>digit<sub>seq</sub></ins> portion contains no thousands-separators, no grouping constraint is applied.

### [locale.moneypunct.general]

Change [[locale.moneypunct.general] paragraph 3](https://eel.is/c++draft/locale.moneypunct.general#3) as follows:

The format of the numeric monetary value is a decimal number:

**value:**
: units fractional<sub>opt</sub>
: decimal-point <del>digits</del> <ins>adigit<sub>seq</sub></ins>
**fractional:**
: decimal-point <del>digits</del> <ins>adigit<sub>seq</sub></ins> <sub>opt</sub>

if frac_digits() returns a positive value, or

**value:**
: units

otherwise. The symbol decimal-point indicates the character returned by decimal_point(). The other symbols are defined as follows:

**units:**
: <del>digits</del>
: <del>digits thousands-sep units</del>
: <ins>adigit<sub>seq</sub> ⟪ thousands-sep adigit<sub>seq</sub> ⟫<sub>seq opt</sub></ins>

In the syntax specification, the symbol adigit is any of the values ct.widen(c) for c in the range `'0'` through `'9'` (inclusive) and `ct` is a reference of type `const ctype<charT>&` […]

### [format.string.general]

Change the grammar in [[format.string.general]](https://eel.is/c++draft/format.string.general) as follows:

**positive-integer:**
: nonzero-digit <ins>digit<sub>seq opt</sub></ins>
: <del>positive-integer digit</del>
**nonnegative-integer:**
: digit<ins><sub>seq</sub></ins>
: <del>nonnegative-integer digit</del>

### [time.format]

Do not change [[time.format] chrono-specs](https://eel.is/c++draft/time.format#nt:chrono-specs):

> **chrono-specs:**
> : conversion-spec
> : chrono-specs conversion-spec
> : chrono-specs literal-char

Even with the new features, this is about as compact as it can be. A chrono-specs is matched by a conversion-spec, followed by zero or more conversion-specs or literal-char. There is no good way to express that on one line.

### [fs.path.generic]

Change the grammar in [[fs.path.generic]](https://eel.is/c++draft/fs.path.generic) as follows:

**[…]**
**relative-path:**
: <del>filename</del>
: <del>filename directory-separator relative-path</del>
: <del>an empty path</del>
: <ins><ins>⟪ filename directory-separator ⟫<sub>seq opt</sub> filename<sub>opt</sub></ins></ins>
**filename:**
: non-empty sequence of characters other than
    directory-separator characters
**directory-separator:**
: preferred-separator directory-separator<sub>opt</sub>
: fallback-separator directory-separator<sub>opt</sub>
**[…]**

A more ambitious change would be to replace directory-separator with a new directory-separator-char<sub>seq</sub>. However, directory-separator is used *a lot* in subsequent wording, so this would have massive blast radius.

## References

[N5014]

Thomas Köppe.

Working Draft, Programming Languages — C++

2025-08-05

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/n5014.pdf
