Name n3632, alx-0014r3 - Refactor syntax of directives Category Cosmetic refactor; readability Authors Alejandro Colomar Jens Gustedt History r0 (2025-04-20): - Initial draft. r1 (2025-04-20): - Fix typos. - Move back non-directive to under 'group'. - Define the term standard directive name. r2 (2025-06-29): - s/Description/Rationale/ - Remove obsolete paragraph from Rationale. - Move non-directive above text-line in group-part, since it has preference. - s/anything/any line/ in 6.10.1p2. # is not a directive in the middle of a line: it's either a syntax error or the stringify operator. - Use 6.10.9+1 to refer to the new subsection after 6.10.9. - Reorder this proposal with respect to alx-0013. This one should be applied first. r3 (2025-07-01; n3632): - Fix interaction with other proposals. - Use = instead of * for moved lines. Principles - Keep the language small and simple - Avoid ambiguities Rationale The specification of preprocessing directives is a bit messy. The syntax is largely ad-hoc, and there are patterns that could we could make to simplify the syntax of directives, allowing us to split the huge syntax paragraph into smaller ones for each subsection. This proposal is not definitive, and I believe some further changes can be good, but this is a self-contained net improvement that will allow further refactors in this space, and by being smaller, it should be easier to reason about. I'll propose further improvements once we merge this. That will also reduce conflicts with other proposals. Interaction with other proposals This blocks alx-0003 ("Add directives #def and #enddef") This blocks alx-0013 ("Prohibit non-directives (other than ID directives)"). Proposed wording Based on N3467. Lines that are moved unmodified are marked with '~' for removals and '=' for additions, to distinguish them from changed lines. 6.10.1 Preprocessing directives :: General @@ Syntax, p1 group-part: if-section control-line + non-directive text-line - # non-directive ~if-section: ~ if-group elif-groups(opt) else-group(opt) endif-line ~ ~if-group: ~ # if constant-expression newline group(opt) ~ # ifdef identifier new-line group(opt) ~ # ifndef identifier new-line group(opt) ~ ~elif-groups: ~ elif-group ~ elif-groups elif-group ~ ~elif-group: ~ # elif constant-expression new-line group(opt) ~ # elifdef identifier new-line group(opt) ~ # elifndef identifier new-line group(opt) ~ ~else-group: ~ # else new-line group(opt) ~ ~endif-line: ~ # endif new-line control-line: ~ # include pp-tokens new-line + include-directive ~ # embed pp-tokens new-line + embed-directive ~ # define identifier replacement-list new-line ~ # define identifier lparen identifier-list(opt) ) replacement-list new-line ~ # define identifier lparen ... ) replacement-list new-line ~ # define identifier lparen identifier-list , ... ) replacement-list new-line + define-directive ~ # undef identifier new-line + undef-directive ~ # line pp-tokens new-line + line-directive ~ # error pp-tokens(opt) new-line ~ # warning pp-tokens(opt) new-line + diagnostic-directive ~ # pragma pp-tokens(opt) new-line + pragma-directive ~ # new-line + null-directive text-line: pp-tokens(opt) new-line ~non-directive: - pp-tokens new-line ~lparen: ~ a ( character not immediately preceded by white space ~ ~replacement-list: ~ pp-tokens(opt) ... ~identifier-list: ~ identifier ~ identifier-list , identifier ~pp-parameter: ~ pp-parameter-name pp-parameter-clause(opt) ~ ~pp-parameter-name: ~ pp-standard-parameter ~ pp-prefixed-parameter ~ ~pp-standard-parameter: ~ identifier ~ ~pp-prefixed-parameter ~ identifier :: identifier ~ ~pp-parameter-clause: ~ ( pp-balanced-token-sequence(opt) ) ~ ~pp-balanced-token-sequence: ~ pp-balanced-token ~ pp-balanced-token-sequence pp-balanced-token ~ ~pp-balancced-token: ~ ( pp-balanced-token-sequence(opt) ) ~ [ pp-balanced-token-sequence(opt) ] ~ { pp-balanced-token-sequence(opt) } ~ any pp-token other than a parenthesis, a bracket, or a brace ~ ~embed-parameter-sequence: ~ pp-parameter ~ embed-parameter-sequence pp-parameter @@ Description, p2 -A preprocessing directive -consists of a +Any sequence of preprocessing tokens -that satisfies the following constraints: +that satisfies the following constraints +is a preprocessing directive: ... +That is, +a directive is any line of the form + + # pp-tokens(opt) new-line ## The reword of the paragraph above allows us to remove the ## first sentence of p3, because now we've made it impossible to ## start a text line with a '#', since it now is by definition a ## directive. @@ New p after p2 +A standard directive +is a preprocessing directive where +either the list of preprocessing tokens is empty (see 6.10.9) +or the first preprocessing token +is an identifier token +that is one of the standard directive names. + + define elif elifdef elifndef + else embed error if + ifdef ifndef include line + pragma undef warning ## This new paragraph will allow a clearer rewrite of the second ## sentence of p3. @@ p3 -A text line -shall not begin with a # preprocessing token. -A non-directive shall not begin with -any of the directive names appearing in the syntax. ## The second sentence is replaced by 6.10.9+1p2 (see below). @@ p7 When in a group that is skipped (6.10.2), the directive syntax is relaxed to allow any sequence of preprocessing tokens to occur between -the directive name +a directive name and the following new-line character. ## In some cases, there's not a directive name (think of ## non-directives). 6.10.2 Conditional inclusion @@ Syntax, p1 =if-section: = if-group elif-groups(opt) else-group(opt) endif-line = =if-group: = # if constant-expression newline group(opt) = # ifdef identifier new-line group(opt) = # ifndef identifier new-line group(opt) = =elif-groups: = elif-group = elif-groups elif-group = =elif-group: = # elif constant-expression new-line group(opt) = # elifdef identifier new-line group(opt) = # elifndef identifier new-line group(opt) = =else-group: = # else new-line group(opt) = =endif-line: = # endif new-line = ... 6.10.3 Source file inclusion ## Add 'Syntax' before 'Constraints' @@ Syntax, new p after title +include-directive: = # include pp-tokens new-line 6.10.4.1 Binary resource inclusion :: #embed preprocessing directive ## Add 'Syntax' before 'Description' @@ Syntax, new p after title +embed-directive: = # embed pp-tokens new-line + =pp-parameter: = pp-parameter-name pp-parameter-clause(opt) = =pp-parameter-name: = pp-standard-parameter = pp-prefixed-parameter = =pp-standard-parameter: = identifier = =pp-prefixed-parameter = identifier :: identifier = =pp-parameter-clause: = ( pp-balanced-token-sequence(opt) ) = =pp-balanced-token-sequence: = pp-balanced-token = pp-balanced-token-sequence pp-balanced-token = =pp-balancced-token: = ( pp-balanced-token-sequence(opt) ) = [ pp-balanced-token-sequence(opt) ] = { pp-balanced-token-sequence(opt) } = any pp-token other than a parenthesis, a bracket, or a brace = =embed-parameter-sequence: = pp-parameter = embed-parameter-sequence pp-parameter 6.10.5.1 Macro replacement :: General ## Add 'Syntax' before 'Constraints' @@ Syntax, new p after title +define-directive: = # define identifier replacement-list new-line = # define identifier lparen identifier-list(opt) ) replacement-list new-line = # define identifier lparen ... ) replacement-list new-line = # define identifier lparen identifier-list , ... ) replacement-list new-line + +undef-directive: = # undef identifier new-line + =lparen: = a ( character not immediately preceded by white space = =replacement-list: = pp-tokens(opt) + =identifier-list: = identifier = identifier-list , identifier 6.10.6 Line control ## Add 'Syntax' before 'Constraints' @@ Syntax, new p after title +line-directive: = # line pp-tokens new-line 6.10.7 Diagnostic directives ## Add 'Syntax' before 'Constraints' @@ Syntax, new p after title +diagnostic-directive: = # error pp-tokens(opt) new-line = # warning pp-tokens(opt) new-line 6.10.8 Pragma directive ## Add 'Syntax' before 'Constraints' @@ Syntax, new p after title +pragma-directive: = # pragma pp-tokens(opt) new-line 6.10.9 Null directive ## Add 'Syntax' before 'Constraints' @@ Syntax, new p after title +null-directive: = # new-line 6.10 Preprocessing directives @@ New subsection after 6.10.9 +6.10.<9+1> Non-directive +Syntax +1 = non-directive: + # pp-tokens new-line + +Description +2 + The first token in a non-directive + is none of the standard directive names. + +Semantics +3 + Execution of a non-directive preprocessing directive + results in undefined behavior. ## alx-0013 will replace this p3 by implementation-defined ## behavior for supported extensions, and a constraint for ## others. non-directive will then be renamed to ## extended-directive.