diff --git a/ConditionalTranslation.md b/ConditionalTranslation.md index 8f34c67..178eb30 100644 --- a/ConditionalTranslation.md +++ b/ConditionalTranslation.md @@ -113,35 +113,36 @@ Refer to the [updated grammar appendix](#appendix-updated-grammar) for the list * function call statements * const assertion statements -> [!WARNING] -> We added attributes on assignment and increment/decrement statements. This change makes the WESL grammar no longer [LR(1)](https://en.wikipedia.org/wiki/LR_parser). See [`wesl-rs#162`](https://github.com/webgpu-tools/wesl-rs/issues/162) for details. -> -> Due to this limitation, *wesl-rs* does not allow attributes on assignment, increment and decrement statements starting with a `(`. Concretely, this is not supported by *wesl-rs*: `@if(FOO) (x)++`. +## `@if` attribute family +The `@if`, `@elif` and `@else` *translate-time attributes* are introduced. The decorated node is only kept if the branch is truthy. -## `@if` attribute -The `@if` *translate-time attribute* is introduced. It takes a single parameter. It marks the decorated node for removal if the parameter evaluates to `false`. - -A syntax node may at most have a single `@if` attribute. This keeps the way open for a `@else` attribute introduction in the future. +A syntax node may at most have a single `@if`, `@elif` or `@else` attribute. Checking for multiple features is done with an `&&` ```wgsl @if(feature1 && feature2) const decl: u32 = 0; ``` -> *See the [possible future extensions](#possible-future-extensions) for the attributes `@elif` and `@else`. -> They may be introduced in the specification in a future version if deemed useful.* +- An `@if` attribute takes a single parameter. It marks the decorated node for removal if the parameter evaluates to `false`. + +- An `@elif` attribute decorates the next sibling of a syntax node decorated by a `@if` or an `@elif`. It takes one parameter. +It marks the decorated node for removal if its parameter evaluates to `false` OR if any of the previous `@if` and `@elif` attribute parameters evaluate to `true`. + +- An `@else` attribute decorates the next sibling of a syntax node decorated by a `@if` or an `@elif`. It does not take any parameter. +It marks the decorated node for removal if any of the previous `@if` and `@elif` attribute parameters evaluate to `true`. Example: ```wgsl @if(feature_1 && (!feature_2 || feature_3)) fn f() { ... } -@if(!feature_1) // corresponds to @elif(!feature_1) +@elif(!feature_1) fn f() { ... } -@if(feature_1 && !(!feature_2 || feature_3)) // corresponds to @else +@else fn f() { ... } ``` + ## Execution of the conditional translation phase 1. The *WESL translator* is invoked with the list of features to *enable* or *disable*. @@ -168,65 +169,18 @@ If the *WESL translator* does not support incremental translation, it is a *link > *It is not an error to provide unused feature flags to the linker. However, an implementation may choose to display a warning in that case.* -## Possible future extensions -> *This section is non-normative* - -* `@else` and `@elif` attributes: - * An `@elif` attribute decorates the next sibling of a syntax node decorated by a `@if` or an `@elif`. It takes one parameter. - It marks the decorated node for removal if its parameter evaluates to `false` OR if any of the previous `@if` and `@elif` attribute parameters evaluate to `true`. - * An `@else` attribute decorates the next sibling of a syntax node decorated by a `@if` or an `@elif`. It does not take any parameter. - It marks the decorated node for removal if any of the previous `@if` and `@elif` attribute parameters evaluate to `true`. - - The `@else` attribute has the nice property that all cases lead to generating a node, and *could* therefore be used in places where the node is required (e.g. expressions) - - Example: - - ```wgsl - @if(feature_1 && (!feature_2 || feature_3)) - fn f() { ... } - @elif(!feature_1) - fn f() { ... } - @else - fn f() { ... } - ``` - -* High-complexity *translate-time expressions*: if we end up implementing other *translate-time attributes*, such as loops (e.g. `@for`, `@repeat`), or [translate-time-evaluable](https://zig.guide/language-basics/comptime/) expressions, then we would need to extend the grammar of *translate-time expressions*. It would also affect this proposal. - - *Example* - - ```wgsl - @comptime - fn response_to_the_ultimate_question() -> u32 { - return 42; - } - - @if(response_to_the_ultimate_question() == 42) - fn f() { ... } - ``` - -* Decorating other WESL language extensions: import statements could be decorated with *translate-time attributes* too. - - *Example* - - ```wgsl - @if(use_bvh) - import accel/bvh_acceleration_structure as scene_struct; - @else - import accel/default_acceleration_structure as scene_struct; - ``` - ## Appendix: Updated grammar -The following non-terminals are added or modified: +The following non-terminals are added or modified. Global declarations get extended to handle general attributes to support future experiments such as `@deprecated`. Everything else is extended with the more restricted `unambiguous_attribute`: ```grammar diagnostic_directive : - attribute * 'diagnostic' diagnostic_control ';' + unambiguous_attribute * 'diagnostic' diagnostic_control ';' enable_directive : - attribute * 'enable' enable_extension_list ';' + unambiguous_attribute * 'enable' enable_extension_list ';' requires_directive : - attribute * 'requires' software_extension_list ';' + unambiguous_attribute * 'requires' software_extension_list ';' struct_decl : attribute * 'struct' ident struct_body_decl @@ -235,20 +189,17 @@ The following non-terminals are added or modified: attribute * 'alias' ident '=' type_specifier variable_or_value_statement : - variable_decl - | variable_decl '=' expression - | attribute * 'let' optionally_typed_ident '=' expression - | attribute * 'const' optionally_typed_ident '=' expression + unambiguous_attribute * variable_decl + | unambiguous_attribute * variable_decl '=' expression + | unambiguous_attribute * 'let' optionally_typed_ident '=' expression + | unambiguous_attribute * 'const' optionally_typed_ident '=' expression variable_decl : - attribute * 'var' _disambiguate_template template_list ? optionally_typed_ident + 'var' _disambiguate_template template_list ? optionally_typed_ident - global_variable_decl : - variable_decl ( '=' expression ) ? - global_value_decl : attribute * 'const' optionally_typed_ident '=' expression - | attribute * 'override' optionally_typed_ident ( '=' expression ) ? + | ... case_clause : attribute * 'case' case_selectors ':' ? compound_statement @@ -257,77 +208,91 @@ The following non-terminals are added or modified: attribute * 'default' ':' ? compound_statement assignment_statement : - attribute * lhs_expression ( '=' | compound_assignment_operator ) expression - | attribute * '_' '=' expression + unambiguous_attribute * lhs_expression ( '=' | compound_assignment_operator ) expression + | unambiguous_attribute * '_' '=' expression increment_statement : - attribute * lhs_expression '++' + unambiguous_attribute * lhs_expression '++' decrement_statement : - attribute * lhs_expression '--' + unambiguous_attribute * lhs_expression '--' break_statement : - attribute * 'break' + unambiguous_attribute * 'break' break_if_statement : - attribute * 'break' 'if' expression ';' + unambiguous_attribute * 'break' 'if' expression ';' continue_statement : - attribute * 'continue' + unambiguous_attribute * 'continue' continuing_statement : - attribute * 'continuing' continuing_compound_statement + unambiguous_attribute * 'continuing' continuing_compound_statement return_statement : - attribute * 'return' expression ? + unambiguous_attribute * 'return' expression ? discard_statement: - attribute * 'discard' + unambiguous_attribute * 'discard' func_call_statement : - attribute * call_phrase + unambiguous_attribute * call_phrase - const_assert_statement : - attribute * 'const_assert' expression + global_assert : + unambiguous_attribute * const_assert + + assert_statement : + unambiguous_attribute * const_assert statement : ';' - | return_statement ';' - | if_statement - | switch_statement - | loop_statement - | for_statement - | while_statement - | func_call_statement ';' - | variable_or_value_statement ';' - | break_statement ';' - | continue_statement ';' + | ... | discard_statement ';' - | variable_updating_statement ';' - | compound_statement - | const_assert_statement ';' - + | ... + + unambiguous_attribute: + '@' ident_pattern_token argument_expression_list + | '@' 'if' '(' expression ',' ? ')' + | '@' 'elif' '(' expression ',' ? ')' + | '@' 'else' + attribute : - '@' ident_pattern_token argument_expression_list ? + '@' ident_pattern_token + | unambiguous_attribute | align_attr - | binding_attr - | blend_src_attr - | builtin_attr - | const_attr - | diagnostic_attr - | group_attr - | id_attr - | interpolate_attr - | invariant_attr - | location_attr - | must_use_attr - | size_attr - | workgroup_size_attr - | vertex_attr - | fragment_attr - | compute_attr - | if_attr - - if_attr: - '@' 'if' '(' expression ',' ? ')' + | ... ``` + +> [!NOTE] +> No alternative of `unambiguous_attribute` has an *optional* argument list, so a parser always knows where the attribute ends. `attribute` lets the argument list be omitted, which is ambiguous before a statement: in `@foo (x)++`, the `(x)` could be `@foo`'s argument or the start of the statement. Using `unambiguous_attribute` keeps the grammar [LR(1)](https://en.wikipedia.org/wiki/LR_parser) and allows`@if(FOO) (x)++`. + +### Possible extensions + +* The `@else` attribute has the nice property that all cases lead to generating a node, and *could* therefore be used in places where the node is required (e.g. expressions) + + + +* High-complexity *translate-time expressions*: if we end up implementing other *translate-time attributes*, such as loops (e.g. `@for`, `@repeat`), or [translate-time-evaluable](https://zig.guide/language-basics/comptime/) expressions, then we would need to extend the grammar of *translate-time expressions*. It would also affect this proposal. + + *Example* + + ```wgsl + @comptime + fn response_to_the_ultimate_question() -> u32 { + return 42; + } + + @if(response_to_the_ultimate_question() == 42) + fn f() { ... } + ``` + +* Decorating other WESL language extensions: import statements could be decorated with *translate-time attributes* too. + + *Example* + + ```wgsl + @if(use_bvh) + import accel/bvh_acceleration_structure as scene_struct; + @else + import accel/default_acceleration_structure as scene_struct; + ``` \ No newline at end of file