Skip to content
239 changes: 137 additions & 102 deletions source/preprocessor.tex
Original file line number Diff line number Diff line change
Expand Up @@ -47,60 +47,22 @@

\begin{bnf}
\nontermdef{control-line}\br
\terminal{\# include} pp-tokens new-line\br
include-directive\br
pp-import\br
\terminal{\# embed \ } pp-tokens new-line\br
\terminal{\# define } identifier replacement-list new-line\br
\terminal{\# define } identifier lparen \opt{identifier-list} \terminal{)} replacement-list new-line\br
\terminal{\# define } identifier lparen \terminal{... )} replacement-list new-line\br
\terminal{\# define } identifier lparen identifier-list \terminal{, ... )} replacement-list new-line\br
\terminal{\# undef \ } identifier new-line\br
embed-directive\br
define-directive\br
undef-directive\br
line-directive\br
\terminal{\# error \ } \opt{pp-tokens} new-line\br
\terminal{\# warning} \opt{pp-tokens} new-line\br
\terminal{\# pragma } \opt{pp-tokens} new-line\br
\terminal{\# }new-line
diagnostic-directive\br
pragma-directive\br
null-directive
\end{bnf}

\begin{bnf}
\nontermdef{line-directives}\br
line-directive \opt{line-directives}
\end{bnf}

\begin{bnf}
\nontermdef{if-section}\br
if-group \opt{elif-groups} \opt{else-group} endif-line
\end{bnf}

\begin{bnf}
\nontermdef{if-group}\br
\terminal{\# if \ \ \ \ } constant-expression new-line \opt{group}\br
\terminal{\# ifdef \ } identifier new-line \opt{group}\br
\terminal{\# ifndef } identifier new-line \opt{group}
\end{bnf}

\begin{bnf}
\nontermdef{elif-groups}\br
elif-group \opt{elif-groups}
\end{bnf}

\begin{bnf}
\nontermdef{elif-group}\br
\terminal{\# elif \ \ \ } constant-expression new-line \opt{group}\br
\terminal{\# elifdef } identifier new-line \opt{group}\br
\terminal{\# elifndef} identifier new-line \opt{group}
\end{bnf}

\begin{bnf}
\nontermdef{else-group}\br
\terminal{\# else \ \ } new-line \opt{group}
\end{bnf}

\begin{bnf}
\nontermdef{endif-line}\br
\terminal{\# endif \ } new-line
\end{bnf}

\begin{bnf}
\nontermdef{text-line}\br
\opt{pp-tokens} new-line
Expand All @@ -111,68 +73,11 @@
pp-tokens new-line
\end{bnf}

\begin{bnf}
\nontermdef{lparen}\br
\descr{a \terminal{(} character not immediately preceded by whitespace}
\end{bnf}

\begin{bnf}
\nontermdef{identifier-list}\br
identifier\br
identifier-list \terminal{,} identifier
\end{bnf}

\begin{bnf}
\nontermdef{replacement-list}\br
\opt{pp-tokens}
\end{bnf}

\begin{bnf}
\nontermdef{pp-tokens}\br
preprocessing-token \opt{pp-tokens}
\end{bnf}

\begin{bnf}
\nontermdef{embed-parameter-seq}\br
embed-parameter \opt{embed-parameter-seq}
\end{bnf}

\begin{bnf}
\nontermdef{embed-parameter}\br
embed-standard-parameter\br
embed-prefixed-parameter
\end{bnf}

\begin{bnf}
\nontermdef{embed-standard-parameter}\br
\terminal{limit} \terminal{(} pp-balanced-token-seq \terminal{)}\br
\terminal{prefix} \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}\br
\terminal{suffix} \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}\br
\terminal{if_empty} \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}
\end{bnf}

\begin{bnf}
\nontermdef{embed-prefixed-parameter}\br
identifier :: identifier\br
identifier :: identifier \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}
\end{bnf}

\begin{bnf}
\nontermdef{pp-balanced-token-seq}\br
pp-balanced-token \opt{pp-balanced-token-seq}
\end{bnf}

\begin{bnf}
\nontermdef{pp-balanced-token}\br
\terminal{(} \opt{pp-balanced-token-seq} \terminal{)}\br
\terminal{[} \opt{pp-balanced-token-seq} \terminal{]}\br
\terminal{\{} \opt{pp-balanced-token-seq} \terminal{\}}\br
\textnormal{any} pp-token \textnormal{except:}\br
\bnfindent\textnormal{parenthesis (\unicode{0028}{left parenthesis} and \unicode{0029}{right parenthesis}),}\br
\bnfindent\textnormal{bracket (\unicode{005b}{left square bracket} and \unicode{005d}{right square bracket}), or}\br
\bnfindent\textnormal{brace (\unicode{007b}{left curly bracket} and \unicode{007d}{right curly bracket}).}
\end{bnf}

\begin{bnf}
\nontermdef{new-line}\br
\descr{the new-line character}
Expand Down Expand Up @@ -330,6 +235,40 @@
\indextext{preprocessing directive!conditional inclusion}%
\indextext{inclusion!conditional|see{preprocessing directive, conditional inclusion}}

\begin{bnf}
\nontermdef{if-section}\br
if-group \opt{elif-groups} \opt{else-group} endif-line
\end{bnf}

\begin{bnf}
\nontermdef{if-group}\br
\terminal{\# if \ \ \ \ } constant-expression new-line \opt{group}\br
\terminal{\# ifdef \ } identifier new-line \opt{group}\br
\terminal{\# ifndef } identifier new-line \opt{group}
\end{bnf}

\begin{bnf}
\nontermdef{elif-groups}\br
elif-group \opt{elif-groups}
\end{bnf}

\begin{bnf}
\nontermdef{elif-group}\br
\terminal{\# elif \ \ \ } constant-expression new-line \opt{group}\br
\terminal{\# elifdef } identifier new-line \opt{group}\br
\terminal{\# elifndef} identifier new-line \opt{group}
\end{bnf}

\begin{bnf}
\nontermdef{else-group}\br
\terminal{\# else \ \ } new-line \opt{group}
\end{bnf}

\begin{bnf}
\nontermdef{endif-line}\br
\terminal{\# endif \ } new-line
\end{bnf}

\indextext{\idxcode{defined}}%
\begin{bnf}
\nontermdef{defined-macro-expression}\br
Expand Down Expand Up @@ -685,6 +624,11 @@
\indextext{inclusion!source file|see{preprocessing directive, source-file inclusion}}%
\indextext{\idxcode{\#include}}%

\begin{bnf}
\nontermdef{include-directive}\br
\terminal{\# include} pp-tokens new-line\br
\end{bnf}

\pnum
A \defnadj{header}{search} for a sequence of characters
searches a sequence of places for a header
Expand Down Expand Up @@ -838,6 +782,52 @@
\indextext{preprocessing directive!embed a resource}
\indextext{\idxcode{\#embed}}%

\begin{bnf}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets us a "hanging paragraph" in ISO parlance (text directly in a subsection that also has further sub-subsections).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I based the diff on related existing code. There's for example a recent commit that did the same thing, I believe:

commit 2c473cc8744fc5b2077349add2283826061ce881
Author: Jens Maurer <Jens.Maurer@gmx.net>
Date:   2025-11-09 20:46:24 +0100

    P3868R1 Allow #line before module declarations
    
    Fixes NB US 55-102 (C++26 CD).
    
    Editorial note:
    * Retained position of #line alternative within control-line.

diff --git a/source/preprocessor.tex b/source/preprocessor.tex
index 487c0360..e449e830 100644
--- a/source/preprocessor.tex
+++ b/source/preprocessor.tex
@@ -16,11 +16,11 @@
     module-file
 \end{bnf}
 
 \begin{bnf}
 \nontermdef{module-file}\br
-    \opt{pp-global-module-fragment} pp-module \opt{group} \opt{pp-private-module-fragment}
+    \opt{line-directives} \opt{pp-global-module-fragment} pp-module \opt{group} \opt{pp-private-module-fragment}
 \end{bnf}
 
 \begin{bnf}
 \nontermdef{pp-global-module-fragment}\br
     \keyword{module} \terminal{;} new-line \opt{group}
@@ -53,17 +53,22 @@
     \terminal{\# define } identifier replacement-list new-line\br
     \terminal{\# define } identifier lparen \opt{identifier-list} \terminal{)} replacement-list new-line\br
     \terminal{\# define } identifier lparen \terminal{... )} replacement-list new-line\br
     \terminal{\# define } identifier lparen identifier-list \terminal{, ... )} replacement-list new-line\br
     \terminal{\# undef \ } identifier new-line\br
-    \terminal{\# line \ \ } pp-tokens new-line\br
+    line-directive\br
     \terminal{\# error \ } \opt{pp-tokens} new-line\br
     \terminal{\# warning} \opt{pp-tokens} new-line\br
     \terminal{\# pragma } \opt{pp-tokens} new-line\br
     \terminal{\# }new-line
 \end{bnf}
 
+\begin{bnf}
+\nontermdef{line-directives}\br
+    line-directive \opt{line-directives}
+\end{bnf}
+
 \begin{bnf}
 \nontermdef{if-section}\br
     if-group \opt{elif-groups} \opt{else-group} endif-line
 \end{bnf}
 
@@ -2060,10 +2065,15 @@ a macro name.
 
 \rSec1[cpp.line]{Line control}%
 \indextext{preprocessing directive!line control}%
 \indextext{\idxcode{\#line}|see{preprocessing directive, line control}}
 
+\begin{bnf}
+\nontermdef{line-directive}\br
+    \terminal{\# line} pp-tokens new-line
+\end{bnf}
+
 \pnum
 The \grammarterm{string-literal} of a
 \tcode{\#line}
 directive, if present,
 shall be a character string literal.

Am I missing something?

Copy link
Copy Markdown
Author

@alejandro-colomar alejandro-colomar May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, now I see. Embed has subsections, while the others don't. What do you suggest? I don't know what should be done here. Is there any part of the standard that I could imitate for this?

Maybe put it in General (cpp.embed.gen)?

\nontermdef{embed-directive}\br
\terminal{\# embed \ } pp-tokens new-line\br
\end{bnf}

\begin{bnf}
\nontermdef{embed-parameter-seq}\br
embed-parameter \opt{embed-parameter-seq}
\end{bnf}

\begin{bnf}
\nontermdef{embed-parameter}\br
embed-standard-parameter\br
embed-prefixed-parameter
\end{bnf}

\begin{bnf}
\nontermdef{embed-standard-parameter}\br
\terminal{limit} \terminal{(} pp-balanced-token-seq \terminal{)}\br
\terminal{prefix} \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}\br
\terminal{suffix} \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}\br
\terminal{if_empty} \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}
\end{bnf}

\begin{bnf}
\nontermdef{embed-prefixed-parameter}\br
identifier :: identifier\br
identifier :: identifier \terminal{(} \opt{pp-balanced-token-seq} \terminal{)}
\end{bnf}

\begin{bnf}
\nontermdef{pp-balanced-token-seq}\br
pp-balanced-token \opt{pp-balanced-token-seq}
\end{bnf}

\begin{bnf}
\nontermdef{pp-balanced-token}\br
\terminal{(} \opt{pp-balanced-token-seq} \terminal{)}\br
\terminal{[} \opt{pp-balanced-token-seq} \terminal{]}\br
\terminal{\{} \opt{pp-balanced-token-seq} \terminal{\}}\br
\textnormal{any} pp-token \textnormal{except:}\br
\bnfindent\textnormal{parenthesis (\unicode{0028}{left parenthesis} and \unicode{0029}{right parenthesis}),}\br
\bnfindent\textnormal{bracket (\unicode{005b}{left square bracket} and \unicode{005d}{right square bracket}), or}\br
\bnfindent\textnormal{brace (\unicode{007b}{left curly bracket} and \unicode{007d}{right curly bracket}).}
\end{bnf}

\rSec2[cpp.embed.gen]{General}

\pnum
Expand Down Expand Up @@ -1483,6 +1473,35 @@
\indextext{replacement!macro|see{macro, replacement}}%
\indextext{preprocessing directive!macro replacement|see{macro, replacement}}

\begin{bnf}
\nontermdef{define-directive}\br
\terminal{\# define } identifier replacement-list new-line\br
\terminal{\# define } identifier lparen \opt{identifier-list} \terminal{)} replacement-list new-line\br
\terminal{\# define } identifier lparen \terminal{... )} replacement-list new-line\br
\terminal{\# define } identifier lparen identifier-list \terminal{, ... )} replacement-list new-line\br
\end{bnf}

\begin{bnf}
\nontermdef{undef-directive}\br
\terminal{\# undef \ } identifier new-line\br
\end{bnf}

\begin{bnf}
\nontermdef{lparen}\br
\descr{a \terminal{(} character not immediately preceded by whitespace}
\end{bnf}

\begin{bnf}
\nontermdef{identifier-list}\br
identifier\br
identifier-list \terminal{,} identifier
\end{bnf}

\begin{bnf}
\nontermdef{replacement-list}\br
\opt{pp-tokens}
\end{bnf}

\pnum
\indextext{macro!replacement list}%
Two replacement lists are identical if and only if
Expand Down Expand Up @@ -2172,6 +2191,12 @@
\indextext{preprocessing directive!warning}%
\indextext{\idxcode{\#error}|see{preprocessing directive, error}}

\begin{bnf}
\nontermdef{diagnostic-directive}\br
\terminal{\# error \ } \opt{pp-tokens} new-line\br
\terminal{\# warning} \opt{pp-tokens} new-line\br
\end{bnf}

\pnum
A preprocessing directive of the form
\begin{ncsimplebnf}
Expand All @@ -2194,6 +2219,11 @@
\indextext{preprocessing directive!pragma}%
\indextext{\idxcode{\#pragma}|see{preprocessing directive, pragma}}

\begin{bnf}
\nontermdef{pragma-directive}\br
\terminal{\# pragma } \opt{pp-tokens} new-line\br
\end{bnf}

\pnum
A preprocessing directive of the form
\begin{ncsimplebnf}
Expand All @@ -2208,6 +2238,11 @@
\rSec1[cpp.null]{Null directive}%
\indextext{preprocessing directive!null}

\begin{bnf}
\nontermdef{null-directive}\br
\terminal{\# }new-line
\end{bnf}

\pnum
A preprocessing directive of the form
\begin{ncsimplebnf}
Expand Down
Loading