<p>I want the expression <code>a &amp;&amp; b = c</code> in my language to mean that the value of <code>c</code> is assigned to <code>b</code> when <code>a</code> is true, and nothing is done otherwise. In other words, the <code>&amp;&amp;</code> operator short-circuits, and the expression <code>b = c</code> on the right-hand side is evaluated only when the left-hand side is true.</p>
<p>How can I define and implement this grammar rule in a programming language?</p>

<parsing><expressions><grammar>
How to support syntax like `a && b = c` for a conditional assignment expression?
CC BY-SA 4.0
2024-02-09T10:18:47.317Z
Just give `&&` and `=` the same level of priority, but right-associative, so that `a && b = c` is parsed as `a && (b = c)` and `a = b && c` is parsed as `a = (b && c)`
Stef
CC BY-SA 4.0
2024-02-09T10:19:10.850Z
C's operator precedence says that && binds tighter than =.  Thus, your expression is the same as (a&&b)=c, which doesn't work.  To fix, simply add ()'s where needed, e.g. a&&(b=c).
Erik Eidt
CC BY-SA 4.0
2024-02-09T10:26:03.177Z
@ErikEidt I see. I went through the expressions chapter again and see that the right side of logical-AND-expr don't accept a non-terminal 'expression' term unless it's bracketted in `()` as specified in primary-expression.
DannyNiu
CC BY-SA 4.0
2024-02-09T16:36:12.807Z
In short, in C, we parse that as `(a&&b)=c`, which is valid (and recognizable) syntax yet later a semantic error b/c assignment requires something assignable.  This is not something to be fixed by parsing, since something assignable can be an expression of considerable complexity.
Erik Eidt
CC BY-SA 4.0
2024-02-09T19:55:35.737Z
The title of this question is incorrect and confusing, because "successfully parse the expression `a && b = c` in C" means "parse `a && b = c` as meaning `(a && b) = c`." If your question is "How can we parse a && b = c as meaning 'if a is true, set b to c' in a C-like language?" then that's what the title should say.
Sophie Swett
CC BY-SA 4.0
2024-02-10T12:21:30.080Z
Technically, assigning meaning to this expression is not done in the parser; the parser only figures out the lexical structure (i.e. it's an `&&` operator with a simple name `a` on the left, and a `=` operator on the right with simple names `b` and `c` for its left and right sides).
kaya3
CC BY-SA 4.0
2024-02-12T04:51:57.853Z
I tried to rewrite the question to get at the part of the core idea that would be on topic for this site. Asking about what's allowed in C is a question for Stack Overflow; and asking about whether a C behaviour is caused by "the grammar" or "the compiler" makes no sense **because there are many possible compilers**. Of course it's the grammar, unless there's an actual bug in the compiler; if every compiler works the same way then surely it isn't a bug. If you don't know, then doing the testing is your responsibility. **If you do find a compiler bug, file a bug report** instead of asking here.
Karl Knechtel
CC BY-SA 4.0
2024-03-23T17:06:14.373Z
Personally that is less readable than just `if(a) b = c;`
CPlus
<p>PHP supports this:</p>
<pre class="lang-php prettyprint-override"><code>&lt;?php
<span class="math-container">$a = true; $</span>a &amp;&amp; <span class="math-container">$b = 1; echo $</span>b;
</code></pre>
<p>Output:</p>
<pre><code>1
</code></pre>
<p><a href="https://tio.run/##K8go@P/fxr4go4BLJVHBVqGkqDTVWgHIVFNTUEkCChhaK6QmZ@QDOdb//wMA" rel="nofollow noreferrer" title="PHP – Try It Online">Try it online!</a></p>
<p>Unfortunately, <a href="https://www.php.net/manual/en/language.operators.precedence.php" rel="nofollow noreferrer">the documentation</a> only says it is a special case in a note:</p>
<blockquote>
<p>Note: Although <code>=</code> has a lower precedence than most other operators, PHP will still allow expressions similar to the following: <code>if (!$a = foo())</code>, in which case the return value of <code>foo()</code> is put into <code>$a</code>.</p>
</blockquote>
<p>But this could be done by using different left and right operator precedence, just like how you usually implement brackets, where it has a high precedence outside but low precedence inside.</p>
<p>If you use a formal grammar, the number of symbols seems to explode:</p>
<pre><code>level5 = identifier | constant | &quot;(&quot; level0 &quot;)&quot;
level4 = level5
level3 = level3 (&quot;*&quot; | &quot;/&quot;) level4 | level4
level2 = level2 (&quot;+&quot; | &quot;-&quot;) level3 | level3
level1 = level2 | level2_1
level4_1 = level4 &quot;=&quot; level1
level3_1 = level3 (&quot;*&quot; | &quot;/&quot;) level4_1 | level4_1
level2_1 = level2 (&quot;+&quot; | &quot;-&quot;) level3_1 | level3_1
level0 = level0 &quot;;&quot; level1 | level1
</code></pre>
<p>This may conflict with the Python way of assigning multiple variables:</p>
<pre class="lang-python prettyprint-override"><code>a, b = 1, 2
</code></pre>

CC BY-SA 4.0
2024-02-09T10:46:45.890Z
2024-02-09T10:46:45.890Z
user23013
CC BY-SA 4.0
2024-02-09T11:35:09.850Z
Looking at [the PHP parser](https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y) there's no obvious explicit "special case" to make this assignment work. I don't understand parser theory well enough, but I think it may just be that there is a production `expr: variable '=' expr` but no `expr: expr '=' expr`, so `$a && $b = 1;` can't result in `expr(expr(variable '&&' variable) '=' scalar))`, it  has to be interpreted as `expr(variable '&&' expr(variable '=' scalar))`
IMSoP
<p>Aggregating from comments (credits goes to @Stef and @ErikEidt).</p>
<p>Q1: The parser doesn't help in this case. There are 3 ways out of it:</p>
<ol>
<li><p>add the <code>expresion</code> non-terminal to the <code>primary-expression</code> non-terminal, (and this would yield an ambiguous grammar).</p>
</li>
<li><p>Give logical operators same precedance as assignment operators. This is a breaking change to all C programs (and any program written in one of the C-family language).</p>
</li>
<li><p>Borrow from Perl, use literal <code>and</code> and <code>or</code> for lower precedance.</p>
</li>
</ol>
<p>Q2: that of the grammar.</p>

CC BY-SA 4.0
2024-02-09T10:29:14.983Z
2024-02-09T10:29:14.983Z
DannyNiu
CC BY-SA 4.0
2024-02-09T10:43:04.830Z
To be fair it's a bit hard to understand what you're asking exactly. `a && b = c` is definitely an error in C, but doesn't have to be an error in another language. It's not a "failure" so much as a definition. The parser in a C compiler successfully parses `a && b = c` as `(a && b) = c`, which results in an error because `(a && b)` is an rvalue whereas `=` requires an lvalue on the left of `=`. You want `a && b = c` to be parsed as `a && (b = c)`, which is totally possible, just not C.
Stef
CC BY-SA 4.0
2024-02-09T10:45:30.767Z
As an example of a different language making different choices: `a < b < c` is parsed as `(a < b) < c` in C, but as syntactic sugar for `(a < b) && (b < c)` in python.
Stef
<p>I am not an expert, so feel free to correct me if I've missed something fundamental here, but looking at <a href="https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y" rel="nofollow noreferrer">the Bison parser for PHP</a>, the way it is resolved there is not related to <em>precedence</em>, but to the productions defined.</p>
<p>Specifically, most binary operators (including <code>&amp;&amp;</code>) are defined as accepting any expression on the left-hand side; but <em>assignment</em> is defined as accepting a smaller set of syntax on the left-hand side.</p>
<p>In PHP's case, the token allowed on the left is called <code>variable</code>, but it might more generally be called an <code>lvalue</code>. The key limitations are:</p>
<ul>
<li>You need to distinguish in the grammar between &quot;assignment operators&quot; and other &quot;binary operators&quot;. This may make a difference in a language with liberal operator overloading.</li>
<li>You need to define in the grammar some subset of expressions which <em>may</em> be valid &quot;lvalues&quot;. This can still include cases which fail at a later compiler phase, or even at run-time. It can even include <code>'(' expression ')'</code> so that <code>(a &amp;&amp; b) = c</code> compiles, but means something different from <code>a &amp;&amp; b = c</code>.</li>
</ul>
<p>In the case of PHP:</p>
<ul>
<li>PHP does not allow operator overloading or custom operators, but does define combined assignment operators such as <code>+=</code>; these are handled with similar rules.</li>
<li>It also has array destructuring (<code>[$a, $b] = $array</code>) which is handled as an additional rule for <code>=</code> only.</li>
<li>The <code>variable</code> rule includes not just <code>$foo</code> but also other variable-like things, such as <code>$object-&gt;property</code> and <code>SomeClass::$staticProperty</code>.</li>
<li>In fact, it also includes things which can't be assigned to, because of how it's reused in other parts of the parser: <code>foo() = 42;</code> gives a compilation error, but not a parser error.</li>
<li>However, crucially, it <em>doesn't</em> include anything that matches <code>$a &amp;&amp; $b</code>.</li>
</ul>
<p>Here's an edited excerpt of PHP's Bison rules (without the target code which generates AST nodes):</p>
<pre><code>expr:
        variable
    |   '[' array_pair_list ']' '=' expr
    |   variable '=' expr
    |   variable T_PLUS_EQUAL expr
    |   expr T_BOOLEAN_AND expr

variable:
        simple_variable
    |   static_member
    |   array_object_dereferenceable T_OBJECT_OPERATOR property_name
    |   array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name
</code></pre>
<p>Let's examine a simplified version with just assignment and logical and:</p>
<pre><code>expression: variable | assignment | and_expression
assignment: variable '=' expression
and_expression: expression '&amp;&amp;' expression
</code></pre>
<p>Given the input <code>$a &amp;&amp; $b = $c</code>, the stack will proceed as follows</p>
<ul>
<li><code>$a</code> - shift - <code>variable</code></li>
<li><code>&amp;&amp;</code> - reduce, then shift - <code>expression '&amp;&amp;'</code></li>
<li><code>$b</code> - shift - <code>expression '&amp;&amp;' variable</code></li>
<li><code>=</code> - shift (because <code>and_expression '='</code> does not match any rule) - <code>expression '&amp;&amp;' variable '='</code></li>
<li><code>$c</code> - shift - <code>expression '&amp;&amp;' variable '=' variable</code></li>
<li>reduce - <code>expression '&amp;&amp;' assignment</code></li>
<li>reduce - <code>expression '&amp;&amp;' expression</code></li>
<li>reduce - <code>and_expression</code></li>
<li>reduce - <code>expression</code></li>
</ul>
<p>The resulting AST is equivalent to <code>$a &amp;&amp; ($b = $c)</code></p>

CC BY-SA 4.0
2024-02-09T12:37:17.763Z
2024-02-09T21:38:25.970Z
2024-02-09T21:38:25.970Z
IMSoP
CC BY-SA 4.0
2024-02-09T12:47:24.990Z
Interesting! This effectively builds a more tolerant parser; `$a && $b = $c` is parsed as `$a && ($b = $c)` because the parser knows that `($a && $b) = $c` wouldn't make sense. In C, `a && b = c` results in an error because it is parsed as `(a && b) = c` and then recognised as not making sense. And in some languages, PHP's rules would be a nightmare. Consider C++ and its overloading of operators. You wouldn't want the evaluation of `a && b = c` to depend on whether `&&` returns an lvalue or not. (Now I'm tempted to try to write a C++ program that compiles with `a && b = c`...)
Stef
CC BY-SA 4.0
2024-02-09T12:54:02.763Z
@Stef Indeed, it relies on having a fairly conservative definition of "lvalue", and not treating `=` as a generic "operator" token. In PHP, it's just three very explicit rules: the basic `variable '=' expr` plus `T_LIST '(' array_pair_list ')' '=' expr` and `'[' array_pair_list ']' '=' expr` for two styles of array destructuring (`list($a, $b) = $array` and `[$a, $b] = $array`).
IMSoP
CC BY-SA 4.0
2024-02-09T13:28:54.980Z
@Stef I guess if you wanted run-time lvalues, you could trade off by requiring parentheses around them - `lvalue = variable | '(' expr ')'` Then `($a && $b) = $c` would compile, and attempt to create an lvalue at run-time, but `$a && $b = $c` would still be equivalent to `$a && ($b = $c)`
IMSoP
CC BY-SA 4.0
2024-02-09T16:35:13.310Z
If you had C's indirection operator, then `*p=5` would parse as `*(p=5)`.  And what of `a[i]=5` or `f()->g=5`?
Erik Eidt
CC BY-SA 4.0
2024-02-09T17:16:43.370Z
@ErikEidt It would be no problem to include rules for all of those in the definition of "lvalue"; in fact, although PHP calls the rule "variable", it includes the equivalent of all of them - `$$p`, `$a[$]`, and `f()->g` would all be reduced as "variable" in the parser. I've edited my answer to make that clear, as well as incorporating my comment above about allowing `( expression )` as an lvalue.
IMSoP
CC BY-SA 4.0
2024-02-09T19:34:42.143Z
PHP's array destructuring isn't only in assignment, you can also write `foreach ($array as [$v1, $v2])`
Barmar
CC BY-SA 4.0
2024-02-09T19:58:06.487Z
@Barmar Indeed; that's handled in a completely separate rule, `foreach_variable`: https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L670 The two variants on both places all share an inner rule, `array_pair_list`
IMSoP
# How to support syntax like `a && b = c` for a conditional assignment expression?
Posted by: username_0 on 2024-02-09
I want the expression `a && b = c` in my language to mean that the value of `c` is assigned to `b` when `a` is true, and nothing is done otherwise. In other words, the `&&` operator short-circuits, and the expression `b = c` on the right-hand side is evaluated only when the left-hand side is true.

How can I define and implement this grammar rule in a programming language?
**Tags:** parsing expressions grammar
> **Comments:**
> **username_1**: Just give `&&` and `=` the same level of priority, but right-associative, so that `a && b = c` is parsed as `a && (b = c)` and `a = b && c` is parsed as `a = (b && c)`  
> **username_2**: C's operator precedence says that && binds tighter than =.  Thus, your expression is the same as (a&&b)=c, which doesn't work.  To fix, simply add ()'s where needed, e.g. a&&(b=c).  
> **username_0**: @ErikEidt I see. I went through the expressions chapter again and see that the right side of logical-AND-expr don't accept a non-terminal 'expression' term unless it's bracketted in `()` as specified in primary-expression.  
> **username_2**: In short, in C, we parse that as `(a&&b)=c`, which is valid (and recognizable) syntax yet later a semantic error b/c assignment requires something assignable.  This is not something to be fixed by parsing, since something assignable can be an expression of considerable complexity.  
> **username_3**: The title of this question is incorrect and confusing, because "successfully parse the expression `a && b = c` in C" means "parse `a && b = c` as meaning `(a && b) = c`." If your question is "How can we parse a && b = c as meaning 'if a is true, set b to c' in a C-like language?" then that's what the title should say.  
> **username_4**: Technically, assigning meaning to this expression is not done in the parser; the parser only figures out the lexical structure (i.e. it's an `&&` operator with a simple name `a` on the left, and a `=` operator on the right with simple names `b` and `c` for its left and right sides).  
> **username_5**: I tried to rewrite the question to get at the part of the core idea that would be on topic for this site. Asking about what's allowed in C is a question for Stack Overflow; and asking about whether a C behaviour is caused by "the grammar" or "the compiler" makes no sense **because there are many possible compilers**. Of course it's the grammar, unless there's an actual bug in the compiler; if every compiler works the same way then surely it isn't a bug. If you don't know, then doing the testing is your responsibility. **If you do find a compiler bug, file a bug report** instead of asking here.  
> **username_6**: Personally that is less readable than just `if(a) b = c;`  
---
## 3 answers
### Answer by username_8 on 2024-02-09. Score: 4 | Accepted answer
I am not an expert, so feel free to correct me if I've missed something fundamental here, but looking at the Bison parser for PHP, the way it is resolved there is not related to precedence, but to the productions defined.
Specifically, most binary operators (including &&) are defined as accepting any expression on the left-hand side; but assignment is defined as accepting a smaller set of syntax on the left-hand side.
In PHP's case, the token allowed on the left is called variable, but it might more generally be called an lvalue. The key limitations are:

You need to distinguish in the grammar between "assignment operators" and other "binary operators". This may make a difference in a language with liberal operator overloading.
You need to define in the grammar some subset of expressions which may be valid "lvalues". This can still include cases which fail at a later compiler phase, or even at run-time. It can even include '(' expression ')' so that (a && b) = c compiles, but means something different from a && b = c.

In the case of PHP:

PHP does not allow operator overloading or custom operators, but does define combined assignment operators such as +=; these are handled with similar rules.
It also has array destructuring ([$a, $b] = $array) which is handled as an additional rule for = only.
The variable rule includes not just $foo but also other variable-like things, such as $object->property and SomeClass::$staticProperty.
In fact, it also includes things which can't be assigned to, because of how it's reused in other parts of the parser: foo() = 42; gives a compilation error, but not a parser error.
However, crucially, it doesn't include anything that matches $a && $b.

Here's an edited excerpt of PHP's Bison rules (without the target code which generates AST nodes):
expr:
        variable
    |   '[' array_pair_list ']' '=' expr
    |   variable '=' expr
    |   variable T_PLUS_EQUAL expr
    |   expr T_BOOLEAN_AND expr

variable:
        simple_variable
    |   static_member
    |   array_object_dereferenceable T_OBJECT_OPERATOR property_name
    |   array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name

Let's examine a simplified version with just assignment and logical and:
expression: variable | assignment | and_expression
assignment: variable '=' expression
and_expression: expression '&&' expression

Given the input $a && $b = $c, the stack will proceed as follows

$a - shift - variable
&& - reduce, then shift - expression '&&'
$b - shift - expression '&&' variable
= - shift (because and_expression '=' does not match any rule) - expression '&&' variable '='
$c - shift - expression '&&' variable '=' variable
reduce - expression '&&' assignment
reduce - expression '&&' expression
reduce - and_expression
reduce - expression

The resulting AST is equivalent to $a && ($b = $c)
> **Comments:**
> **username_1** (2024-02-09): Interesting! This effectively builds a more tolerant parser; `$a && $b = $c` is parsed as `$a && ($b = $c)` because the parser knows that `($a && $b) = $c` wouldn't make sense. In C, `a && b = c` results in an error because it is parsed as `(a && b) = c` and then recognised as not making sense. And in some languages, PHP's rules would be a nightmare. Consider C++ and its overloading of operators. You wouldn't want the evaluation of `a && b = c` to depend on whether `&&` returns an lvalue or not. (Now I'm tempted to try to write a C++ program that compiles with `a && b = c`...)  
> **username_8** (2024-02-09): @Stef Indeed, it relies on having a fairly conservative definition of "lvalue", and not treating `=` as a generic "operator" token. In PHP, it's just three very explicit rules: the basic `variable '=' expr` plus `T_LIST '(' array_pair_list ')' '=' expr` and `'[' array_pair_list ']' '=' expr` for two styles of array destructuring (`list($a, $b) = $array` and `[$a, $b] = $array`).  
> **username_8** (2024-02-09): @Stef I guess if you wanted run-time lvalues, you could trade off by requiring parentheses around them - `lvalue = variable | '(' expr ')'` Then `($a && $b) = $c` would compile, and attempt to create an lvalue at run-time, but `$a && $b = $c` would still be equivalent to `$a && ($b = $c)`  
> **username_2** (2024-02-09): If you had C's indirection operator, then `*p=5` would parse as `*(p=5)`.  And what of `a[i]=5` or `f()->g=5`?  
> **username_8** (2024-02-09): @ErikEidt It would be no problem to include rules for all of those in the definition of "lvalue"; in fact, although PHP calls the rule "variable", it includes the equivalent of all of them - `$$p`, `$a[$]`, and `f()->g` would all be reduced as "variable" in the parser. I've edited my answer to make that clear, as well as incorporating my comment above about allowing `( expression )` as an lvalue.  
> **username_9** (2024-02-09): PHP's array destructuring isn't only in assignment, you can also write `foreach ($array as [$v1, $v2])`  
> **username_8** (2024-02-09): @Barmar Indeed; that's handled in a completely separate rule, `foreach_variable`: https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L670 The two variants on both places all share an inner rule, `array_pair_list`  
---
### Answer by username_7 on 2024-02-09. Score: 1
PHP supports this:
$a = true; $a && $b = 1; echo $b;

Output:
1

Try it online!
Unfortunately, the documentation only says it is a special case in a note:

Note: Although = has a lower precedence than most other operators, PHP will still allow expressions similar to the following: if (!$a = foo()), in which case the return value of foo() is put into $a.

But this could be done by using different left and right operator precedence, just like how you usually implement brackets, where it has a high precedence outside but low precedence inside.
If you use a formal grammar, the number of symbols seems to explode:
level5 = identifier | constant | "(" level0 ")"
level4 = level5
level3 = level3 ("*" | "/") level4 | level4
level2 = level2 ("+" | "-") level3 | level3
level1 = level2 | level2_1
level4_1 = level4 "=" level1
level3_1 = level3 ("*" | "/") level4_1 | level4_1
level2_1 = level2 ("+" | "-") level3_1 | level3_1
level0 = level0 ";" level1 | level1

This may conflict with the Python way of assigning multiple variables:
a, b = 1, 2
> **Comments:**
> **username_8** (2024-02-09): Looking at [the PHP parser](https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y) there's no obvious explicit "special case" to make this assignment work. I don't understand parser theory well enough, but I think it may just be that there is a production `expr: variable '=' expr` but no `expr: expr '=' expr`, so `$a && $b = 1;` can't result in `expr(expr(variable '&&' variable) '=' scalar))`, it  has to be interpreted as `expr(variable '&&' expr(variable '=' scalar))`  
---
### Answer by username_0 on 2024-02-09. Score: 0
Aggregating from comments (credits goes to @Stef and @ErikEidt).

Q1: The parser doesn't help in this case. There are 3 ways out of it:

1. add the `expresion` non-terminal to the `primary-expression` non-terminal, (and this would yield an ambiguous grammar).
2. Give logical operators same precedance as assignment operators. This is a breaking change to all C programs (and any program written in one of the C-family language).
3. Borrow from Perl, use literal `and` and `or` for lower precedance.

Q2: that of the grammar.
> **Comments:**
> **username_1** (2024-02-09): To be fair it's a bit hard to understand what you're asking exactly. `a && b = c` is definitely an error in C, but doesn't have to be an error in another language. It's not a "failure" so much as a definition. The parser in a C compiler successfully parses `a && b = c` as `(a && b) = c`, which results in an error because `(a && b)` is an rvalue whereas `=` requires an lvalue on the left of `=`. You want `a && b = c` to be parsed as `a && (b = c)`, which is totally possible, just not C.  
> **username_1** (2024-02-09): As an example of a different language making different choices: `a < b < c` is parsed as `(a < b) < c` in C, but as syntactic sugar for `(a < b) && (b < c)` in python.  
---
