<p>The most obvious way of supporting chained comparisons operators, such as <code>a &lt; b &lt; c</code>, is to rewrite it as <code>(a &lt; b) and (b &lt; c)</code>. That's what I've done <a href="https://github.com/FlatAssembler/AECforWebAssembly/blob/29cf9e2101b4e110618bafa42a1538a2d5db2f3e/compiler.cpp#L1361C1-L1383C6" rel="nofollow noreferrer">in my AEC-to-WebAssembly compiler</a>, written in C++:</p>
<pre class="lang-cpp prettyprint-override"><code>  } else if (text == &quot;&lt;&quot; or text == &quot;&gt;&quot; or text == &quot;&lt;=&quot; or text == &quot;&gt;=&quot;) {
    if (children.at(0).text ==
        text) // Chained comparisons, such as `a &lt; b &lt; c`.
    {
      TreeNode andNode(&quot;and&quot;, lineNumber, columnNumber),
          secondChild(text, lineNumber, columnNumber);
      secondChild.children = {children.at(0).children.at(1), children.at(1)};
      andNode.children = {children.at(0), secondChild};
      std::cerr
          &lt;&lt; &quot;Line &quot; &lt;&lt; lineNumber &lt;&lt; &quot;, Column &quot; &lt;&lt; columnNumber
          &lt;&lt; &quot;, Compiler warning: Chained comparisons are not implemented &quot;
             &quot;correctly in this compiler. The middle term, in this case \&quot;&quot;
          &lt;&lt; children.at(0).children.at(1).text
          &lt;&lt; &quot;\&quot;, will be evaluated twice, possibly leading to unintended side &quot;
             &quot;effects. I am sorry about that, but, thus far, there does not &quot;
             &quot;seem to be a simple solution given the way the compiler is &quot;
             &quot;structured. I've started a StackExchange thread about that &quot;
             &quot;problem: https://langdev.stackexchange.com/q/3755/330&quot;
          &lt;&lt; std::endl;
      return &quot;;;Chained comparison, converting &quot; + getLispExpression() +
             &quot; to &quot; + andNode.getLispExpression() + &quot;\n&quot; +
             andNode.compile(context);
    }
</code></pre>
<p>However, there is a problem with that. Suppose that <code>b</code> is a function which has side-effects. As I've written it, the compiled code would call it twice instead of once.</p>
<p>For example, this <a href="https://github.com/FlatAssembler/AECforWebAssembly/blob/master/chainedComparisonWithSideEffects/chainedComparisonWithSideEffects.aec" rel="nofollow noreferrer">program in AEC</a>:</p>
<pre><code>#target WASI

Integer16 counter := 0;

Function b() Which Returns Integer32 Does
    counter += 1;
    Return counter;
EndFunction

Function test() Which Returns Integer32 Does
    // By common sense, this should return 1. However, because of the
    // current AEC semantics, this returns 2.
    counter := 0;
    Integer16 a := 0, c := 2;
    Integer16 resultOfComparison := a &lt;= b() &lt;= c;
    Return counter;
EndFunction
</code></pre>
<p>If invoked with <a href="https://github.com/FlatAssembler/AECforWebAssembly/blob/master/chainedComparisonWithSideEffects/chainedComparisonWithSideEffects.js" rel="nofollow noreferrer">this program in JavaScript</a>:</p>
<pre class="lang-js prettyprint-override"><code>const fs = require(&quot;fs&quot;);
const buffer = fs.readFileSync(&quot;chainedComparisonWithSideEffects.wasm&quot;);
WebAssembly.instantiate(buffer).then((results) =&gt; {
  const exports = results.instance.exports;
  console.log(&quot;The test returns: &quot; + exports.test());
});
</code></pre>
<p>It outputs <code>The test returns: 2</code> instead of <code>The test returns: 1</code>.</p>
<p>So, how do you compile it correctly?</p>

<operators><comparison>
How do you correctly compile the chained comparison operators like ones that exist in Python (`a < b < c`), if `b` might have side-effects?
CC BY-SA 4.0
2024-04-29T12:32:44.990Z
Evaluate the expression once and use it twice? Why do you need to have a tree rather than a DAG?
user1937198
CC BY-SA 4.0
2024-04-29T12:35:04.860Z
If you really need to use an expression tree at this stage rather than a DAG, you can postpone that to whenever you lower to the webasm DAG using something along the lines of `let temp = b in a < temp and temp < c` if you can create an anonymous temporary in your expression tree.
user1937198
CC BY-SA 4.0
2024-04-29T13:29:58.760Z
For correct semantics you (presumably) must evaluate `a` before `b`, so you must use locals for both. CPython can avoid locals by using the stack instructions `DUP_TOP` and `ROT_THREE`, but unfortunately WebAssembly doesn't have those.
kaya3
CC BY-SA 4.0
2024-05-01T07:24:42.687Z
@user1937198 What is DAG?
FlatAssembler
CC BY-SA 4.0
2024-05-01T10:44:40.443Z
@FlatAssembler Directed Acyclic Graph: https://en.wikipedia.org/wiki/Directed_acyclic_graph Which allows representing that values may be consumed multiple times, which is an extreamly common desire when de-sugaring.
user1937198
<p>So this one depends on your operational semantics, in particular on whether your language exhibits Lazy or Eager Evaluation.<br />
That said, please bear in mind that even in most eagerly-evaluated languages, the Boolean conjunction and disjunction operators (<code>and</code> and <code>or</code>) <strong>are</strong> exhibiting lazy evaluation.</p>
<p><code>x ∧ y</code> sometimes mean that <code>false ∧ (x/0)</code> is legal and equal to <code>false</code>, while sometimes it is illegal.<br />
This will become important in what follows.</p>
<h2>Memoising And Rewriting</h2>
<p>That one is simple enough: you just rewrite comparison chains to conjunctions or disjunctions.</p>
<p><code>x &lt; y &lt; z</code> becomes <code>x &lt; y ∧ y &lt; z</code>.<br />
Then, assuming Lazy Evaluation, this further gets lowered to <code>force x &lt; force y ∧ force y &lt; force z</code> where each term is memoised.<br />
Assuming Eager Evaluation, the uses of <code>force</code> are explicitated using temporaries as so:<br />
<code>let x' = /*force*/ x;; y' = /*force*/ y in x' &lt; y' ∧ y' &lt; z</code>.</p>
<p>Now, what <code>3 &lt; 0 &lt; explode()</code> is supposed to do is up to whether Boolean conjunction is lazy or not.</p>
<h2>Binary Comparisons Do Not Result In Booleans</h2>
<p>That one is a bit more funky but works in existing languages and relies on the binary comparison operators returning a type that can both be turned into Booleans and be used in comparison chains.<br />
It's simple: it holds a reference to its RHS and has the operators compare against the RHS.</p>
<p>Here is the approach illustrated in C++:</p>
<pre class="lang-cpp prettyprint-override"><code>template&lt;typename T&gt;
struct ComparisonVariable
{
    const bool r;
    const T &amp;rhs;

    operator bool() const
    {
        return r;
    }

    ComparisonVariable&lt;T&gt; operator &lt;(const T &amp;arg) const
    {
        return ComparisonVariable&lt;T&gt; { r &amp;&amp; (bool) (rhs &lt; arg), arg };
    }
};
struct Int
{
    int val;
    ComparisonVariable&lt;Int&gt; operator &lt;(const Int &amp;arg) const
    {
        return ComparisonVariable&lt;Int&gt; { val &lt; arg.val, arg };
    }
};
/* ... */

extern &quot;C&quot; int printf(const char*, ...);
int main(int argc, char *argv[])
{
    Int x = { 1 }, y = { 2 }, z = { 3 };
    bool fst = x &lt; y &lt; z;
    bool snd = z &lt; y &lt; x;
    printf(&quot;%d %d&quot;, fst, snd);
}
</code></pre>
<p><code>x &lt; y &lt; z</code> is <code>(x.Int::operator &lt;(y)).ComparisonVariable&lt;Int&gt;::operator &lt;(z)</code>, where, as you can see, each operand only appears once.</p>

CC BY-SA 4.0
2024-04-29T17:37:44.330Z
2024-04-29T20:58:06.030Z
2024-04-29T20:58:06.030Z
Longinus
<p>You've got some good answers already but I'll just point out that I've had to do this twice, and done it in two different ways; both work.</p>
<p>Method one:</p>
<p>When I was writing a Python-to-Python compiler at Facebook we rewrote <code>r = a() &lt; b() &lt; c()</code> via the following steps:</p>
<ul>
<li><p>Does the first operand possibly have a side effect? If no, we're done. If yes, rewrite to <code>_a = a() ; r = _a &lt; b() &lt; c()</code>.</p>
</li>
<li><p>Does the second operand possibly have a side effect? Rewrite similarly, so now we have <code>_a = a() ; _b = b(); r = _a &lt; _b &lt; c()</code></p>
</li>
<li><p>Is there more than one operator? If yes, it is now safe to rewrite the assignment as <code>r = _a &lt; _b and _b &lt; c()</code>.</p>
</li>
</ul>
<p>Method two:</p>
<p>C# does not have this kind of chained comparison but it has many other expressions where the specification says &quot;... but the expression is only evaluated once&quot;.  Our intermediate form after parsing but before IL generation has a pair of special nodes: one means &quot;evaluate the referred-to expression and store it to a temp&quot;, the other means &quot;fetch the value of the temp in the referred-to store expression&quot;. That makes the lowered form a DAG rather than a tree but that's not a big deal. The IL generator then decides whether to turn the temp into an unnamed local or a dup instruction or whatever.</p>
<p>Both work. In the first case I was already writing a Python-to-Python compiler so it made sense to do the lowering entirely in Python, in the second case it was just easier to have a custom intermediate format that we could special-case later; do whatever works best for your application.</p>

CC BY-SA 4.0
2024-04-30T19:48:13.653Z
2024-05-01T00:46:35.437Z
2024-05-01T00:46:35.437Z
Eric Lippert
<p>You can translate it to</p>
<pre><code>(a &lt; (temp_b := b)) and (temp_b &lt; c)
</code></pre>
<p>before code generation. <code>temp_b</code> should be a unique identifier generated by the compiler. Repeat this replacement for each step in the chained comparisons.</p>

CC BY-SA 4.0
2024-04-30T20:53:33.137Z
2024-04-30T20:53:33.137Z
Barmar
<p>The trite answer is that if evaluating <code>b</code> twice is wrong, then it should only be evaluated once...</p>
<p><em>Disclaimer: I don't know AEC, so I'm going to make up syntax.</em></p>
<h3>Desugaring or not</h3>
<p>For the purpose of this answer, I'll define desugaring of AEC as a transformation to semantically equivalent AEC code.</p>
<p>The first question I would ask is whether you should desugar or not.</p>
<p>Desugaring has the advantage of removing constructs early, thereby potentially simplifying the rest of the compiler, but it does come with its own challenges: diagnostics should refer to the original source, the meaning shouldn't change, and sometimes it's just plain impossible to express the target semantics in the language.</p>
<p>So, if you can't desugar for any reason, don't. Move the problem to a later part of the compilation pipeline which will not suffer from the limitations that desugaring faces.</p>
<p>If you do wish to desugar, read on.</p>
<h3>Variables</h3>
<p>The desugaring requires introducing variables, which should definitely NOT shadow any existing variable.</p>
<p>A trick which may help is to cheat. Reserving a prefix for the &quot;implementation&quot;, as C and C++ do, allows to use <code>@1</code>, <code>@2</code>, etc... when nobody else is allowed to. Otherwise, you need the set of in-scope variable names, and you need to avoid them.</p>
<h3>With a Block Expression</h3>
<p>If the language has block expressions<sup>1</sup>, you essentially want to replace <code>a’ &lt; b’ &lt; c’</code> by the following:</p>
<pre class="lang-rust prettyprint-override"><code>{
    let @1 = a’;
    let @2 = b’;

    @1 &lt; @2 &amp;&amp; @2 &lt; c’
}
</code></pre>
<p><em>(where <code>a’</code> and co are place-holders for expressions)</em></p>
<p>If there are more elements, nest:</p>
<pre class="lang-rust prettyprint-override"><code>{
    let @1 = a’;
    let @2 = b’;

    @1 &lt; @2 &amp;&amp; {
        let @3 = c’;

        @2 &lt; @3 &amp;&amp; {
            let @4 = d’;

            @3 &lt; @4
        }
    }
}
</code></pre>
<p>Block expressions are the best in that even in languages such as Rust where typing and borrowing are so strict, they avoid having to spell out the types, borrowing modes, etc...</p>
<p><sup>1</sup> <em>Block expressions are the abilities to use a block as an expression</em>.</p>
<h3>With a Closure</h3>
<p>In the absence of block expressions, the easiest is to introduce a closure:</p>
<pre><code>def closure_42():
    @1 = a’
    @2 = b’

    if not (@1 &lt; @2):
        return False

    @3 = c’

    if not (@2 &lt; @3):
        return False

    # Possibly further expressions

    return True
</code></pre>
<p>The closure is then substituted in place of the replaced expression.</p>
<h3>With a function</h3>
<p>In the absence of closures, you can emulate it...</p>
<p>You essentially create a function which takes as input the union of all the variables referenced in the expression to replace. This works well in dynamically typed languages, or languages with templates.</p>
<pre class="lang-py prettyprint-override"><code>def generated_42(v_0’, v_1’, v_2’, v_3’):
    @1 = a’
    @2 = b’

    if not (@1 &lt; @2):
        return False

    @3 = c’

    if not (@2 &lt; @3):
        return False

    # Possibly further expressions

    return True
</code></pre>
<p>Where v_0’, v_1’, v_2’, ... are the variables of the scope in which a’, b’, and c’ are defined that are referenced by a’, b’, and c’. Note that each reference variable need only be passed once, and they should have the same name as they had before.</p>
<h3>In-situ</h3>
<p>The last and least solution is an in-situ rewrite of the <em>entire</em> condition.</p>
<p>It's my least favorite because it requires rewriting the entire expression, not just the sub-expression <code>a’ &lt; b’ &lt; c’</code>, or at the very least rewriting the prefix of the expression up to the last sub-expression to rewrite. This makes the rewrite less local, so more annoying to do correctly, and it gets worse when you have multiple sub-expressions to rewrite in a single expression.</p>
<p>And on top of that it can't use short-circuiting.</p>
<p>Before:</p>
<pre class="lang-py prettyprint-override"><code>    if foo() and a’ &lt; b’ &lt; c’ ... and bar():
        # Original block.
</code></pre>
<p>After:</p>
<pre class="lang-py prettyprint-override"><code>    @condition = foo()

    if @condition:
        @1 = a’
        @2 = b’

        @condition = @1 &lt; @2

    if @condition:
        @3 = c’

        @condition = @2 &lt; @3

    # further chaining

    if @condition and bar():
        # Original block.
</code></pre>
<p>I do note the unfortunate issue of having to rewrite the entire condition, or rather, at least, the <em>prefix</em> of the condition if you feel fancy.</p>

CC BY-SA 4.0
2024-04-29T17:17:37.580Z
2024-05-01T12:35:06.580Z
2024-05-01T12:35:06.580Z
Matthieu M.
CC BY-SA 4.0
2024-04-29T17:28:28.180Z
Even if the language doesn't have block expressions,  it may make sense to desugar to an extended intermediate representation that does have them.
user1937198
CC BY-SA 4.0
2024-04-30T02:15:06.643Z
`a` is only evaluated once either way. Why do you include it in the list of variables that are introduced to prevent double-evaluation?
amalloy
CC BY-SA 4.0
2024-04-30T06:00:55.943Z
@amalloy: Because it needs to be evaluated before `let b = b';`, but used afterwards.
user2357112
CC BY-SA 4.0
2024-04-30T13:51:38.210Z
Your 'function' case misses the potential short circuiting that would skip evaluating `c`, since its value would be passed as an argument
Phil Miller
CC BY-SA 4.0
2024-04-30T14:01:05.923Z
@PhilMiller: Not at all :) But then I didn't elaborate so I can see why you'd be confused. The idea of the function case is to pass as argument the _variables_ referenced in the expressions a', b', c', ... It's essentially trying to be a closure in the absence of closure, manually passing the "environment".
Matthieu M.
CC BY-SA 4.0
2024-04-30T14:04:35.327Z
For a questioner potentially new to PL implementation, based on asking this question, I think that needed to be spelled out. Alternately, function encapsulation works fine with lazy evaluation, or pass-by-name parameters
Phil Miller
CC BY-SA 4.0
2024-04-30T14:42:41.440Z
@PhilMiller: Not convinced it's needed, to be honest.... but it doesn't take too much space so why not.
Matthieu M.
CC BY-SA 4.0
2024-05-01T17:30:19.107Z
I know neither AEC nor WebAssembly, but I imagine it could be problematic if the variable for `a` keeps the value around until the end? Imagine it for example keeps some file open and that file is supposed to be flushed+closed after the first comparison and before the second. I'm just a Python person, made a [question](https://stackoverflow.com/q/78414881/16759116) about that.
no comment
CC BY-SA 4.0
2024-05-01T17:36:14.120Z
@nocomment: Oh, that's a nice one. Yes, if destruction matters it could get tricky really quickly.
Matthieu M.
CC BY-SA 4.0
2024-05-01T17:58:56.100Z
I also posted an [answer](https://stackoverflow.com/a/78415224/16759116) there now. No idea how to do it in WebAssembly or whether it matters here :-)
no comment
# How do you correctly compile the chained comparison operators like ones that exist in Python (`a < b < c`), if `b` might have side-effects?
Posted by: username_0 on 2024-04-29
The most obvious way of supporting chained comparisons operators, such as a < b < c, is to rewrite it as (a < b) and (b < c). That's what I've done in my AEC-to-WebAssembly compiler, written in C++:
  } else if (text == "<" or text == ">" or text == "<=" or text == ">=") {
    if (children.at(0).text ==
        text) // Chained comparisons, such as `a < b < c`.
    {
      TreeNode andNode("and", lineNumber, columnNumber),
          secondChild(text, lineNumber, columnNumber);
      secondChild.children = {children.at(0).children.at(1), children.at(1)};
      andNode.children = {children.at(0), secondChild};
      std::cerr
          << "Line " << lineNumber << ", Column " << columnNumber
          << ", Compiler warning: Chained comparisons are not implemented "
             "correctly in this compiler. The middle term, in this case \""
          << children.at(0).children.at(1).text
          << "\", will be evaluated twice, possibly leading to unintended side "
             "effects. I am sorry about that, but, thus far, there does not "
             "seem to be a simple solution given the way the compiler is "
             "structured. I've started a StackExchange thread about that "
             "problem: https://langdev.stackexchange.com/q/3755/330"
          << std::endl;
      return ";;Chained comparison, converting " + getLispExpression() +
             " to " + andNode.getLispExpression() + "\n" +
             andNode.compile(context);
    }

However, there is a problem with that. Suppose that b is a function which has side-effects. As I've written it, the compiled code would call it twice instead of once.
For example, this program in AEC:
#target WASI

Integer16 counter := 0;

Function b() Which Returns Integer32 Does
    counter += 1;
    Return counter;
EndFunction

Function test() Which Returns Integer32 Does
    // By common sense, this should return 1. However, because of the
    // current AEC semantics, this returns 2.
    counter := 0;
    Integer16 a := 0, c := 2;
    Integer16 resultOfComparison := a <= b() <= c;
    Return counter;
EndFunction

If invoked with this program in JavaScript:
const fs = require("fs");
const buffer = fs.readFileSync("chainedComparisonWithSideEffects.wasm");
WebAssembly.instantiate(buffer).then((results) => {
  const exports = results.instance.exports;
  console.log("The test returns: " + exports.test());
});

It outputs The test returns: 2 instead of The test returns: 1.
So, how do you compile it correctly?
**Tags:** operators comparison
> **Comments:**
> **username_1**: Evaluate the expression once and use it twice? Why do you need to have a tree rather than a DAG?  
> **username_1**: If you really need to use an expression tree at this stage rather than a DAG, you can postpone that to whenever you lower to the webasm DAG using something along the lines of `let temp = b in a < temp and temp < c` if you can create an anonymous temporary in your expression tree.  
> **username_2**: For correct semantics you (presumably) must evaluate `a` before `b`, so you must use locals for both. CPython can avoid locals by using the stack instructions `DUP_TOP` and `ROT_THREE`, but unfortunately WebAssembly doesn't have those.  
> **username_0**: @user1937198 What is DAG?  
> **username_1**: @FlatAssembler Directed Acyclic Graph: https://en.wikipedia.org/wiki/Directed_acyclic_graph Which allows representing that values may be consumed multiple times, which is an extreamly common desire when de-sugaring.  
---
## 4 answers
### Answer by username_6 on 2024-04-29. Score: 12 | Accepted answer
The trite answer is that if evaluating b twice is wrong, then it should only be evaluated once...
Disclaimer: I don't know AEC, so I'm going to make up syntax.
Desugaring or not
For the purpose of this answer, I'll define desugaring of AEC as a transformation to semantically equivalent AEC code.
The first question I would ask is whether you should desugar or not.
Desugaring has the advantage of removing constructs early, thereby potentially simplifying the rest of the compiler, but it does come with its own challenges: diagnostics should refer to the original source, the meaning shouldn't change, and sometimes it's just plain impossible to express the target semantics in the language.
So, if you can't desugar for any reason, don't. Move the problem to a later part of the compilation pipeline which will not suffer from the limitations that desugaring faces.
If you do wish to desugar, read on.
Variables
The desugaring requires introducing variables, which should definitely NOT shadow any existing variable.
A trick which may help is to cheat. Reserving a prefix for the "implementation", as C and C++ do, allows to use @1, @2, etc... when nobody else is allowed to. Otherwise, you need the set of in-scope variable names, and you need to avoid them.
With a Block Expression
If the language has block expressions1, you essentially want to replace a’ < b’ < c’ by the following:
{
    let @1 = a’;
    let @2 = b’;

    @1 < @2 && @2 < c’
}

(where a’ and co are place-holders for expressions)
If there are more elements, nest:
{
    let @1 = a’;
    let @2 = b’;

    @1 < @2 && {
        let @3 = c’;

        @2 < @3 && {
            let @4 = d’;

            @3 < @4
        }
    }
}

Block expressions are the best in that even in languages such as Rust where typing and borrowing are so strict, they avoid having to spell out the types, borrowing modes, etc...
1 Block expressions are the abilities to use a block as an expression.
With a Closure
In the absence of block expressions, the easiest is to introduce a closure:
def closure_42():
    @1 = a’
    @2 = b’

    if not (@1 < @2):
        return False

    @3 = c’

    if not (@2 < @3):
        return False

    # Possibly further expressions

    return True

The closure is then substituted in place of the replaced expression.
With a function
In the absence of closures, you can emulate it...
You essentially create a function which takes as input the union of all the variables referenced in the expression to replace. This works well in dynamically typed languages, or languages with templates.
def generated_42(v_0’, v_1’, v_2’, v_3’):
    @1 = a’
    @2 = b’

    if not (@1 < @2):
        return False

    @3 = c’

    if not (@2 < @3):
        return False

    # Possibly further expressions

    return True

Where v_0’, v_1’, v_2’, ... are the variables of the scope in which a’, b’, and c’ are defined that are referenced by a’, b’, and c’. Note that each reference variable need only be passed once, and they should have the same name as they had before.
In-situ
The last and least solution is an in-situ rewrite of the entire condition.
It's my least favorite because it requires rewriting the entire expression, not just the sub-expression a’ < b’ < c’, or at the very least rewriting the prefix of the expression up to the last sub-expression to rewrite. This makes the rewrite less local, so more annoying to do correctly, and it gets worse when you have multiple sub-expressions to rewrite in a single expression.
And on top of that it can't use short-circuiting.
Before:
    if foo() and a’ < b’ < c’ ... and bar():
        # Original block.

After:
    @condition = foo()

    if @condition:
        @1 = a’
        @2 = b’

        @condition = @1 < @2

    if @condition:
        @3 = c’

        @condition = @2 < @3

    # further chaining

    if @condition and bar():
        # Original block.

I do note the unfortunate issue of having to rewrite the entire condition, or rather, at least, the prefix of the condition if you feel fancy.
> **Comments:**
> **username_1** (2024-04-29): Even if the language doesn't have block expressions,  it may make sense to desugar to an extended intermediate representation that does have them.  
> **username_7** (2024-04-30): `a` is only evaluated once either way. Why do you include it in the list of variables that are introduced to prevent double-evaluation?  
> **username_8** (2024-04-30): @amalloy: Because it needs to be evaluated before `let b = b';`, but used afterwards.  
> **username_9** (2024-04-30): Your 'function' case misses the potential short circuiting that would skip evaluating `c`, since its value would be passed as an argument  
> **username_6** (2024-04-30): @PhilMiller: Not at all :) But then I didn't elaborate so I can see why you'd be confused. The idea of the function case is to pass as argument the _variables_ referenced in the expressions a', b', c', ... It's essentially trying to be a closure in the absence of closure, manually passing the "environment".  
> **username_9** (2024-04-30): For a questioner potentially new to PL implementation, based on asking this question, I think that needed to be spelled out. Alternately, function encapsulation works fine with lazy evaluation, or pass-by-name parameters  
> **username_6** (2024-04-30): @PhilMiller: Not convinced it's needed, to be honest.... but it doesn't take too much space so why not.  
> **username_10** (2024-05-01): I know neither AEC nor WebAssembly, but I imagine it could be problematic if the variable for `a` keeps the value around until the end? Imagine it for example keeps some file open and that file is supposed to be flushed+closed after the first comparison and before the second. I'm just a Python person, made a [question](https://stackoverflow.com/q/78414881/16759116) about that.  
> **username_6** (2024-05-01): @nocomment: Oh, that's a nice one. Yes, if destruction matters it could get tricky really quickly.  
> **username_10** (2024-05-01): I also posted an [answer](https://stackoverflow.com/a/78415224/16759116) there now. No idea how to do it in WebAssembly or whether it matters here :-)  
---
### Answer by username_3 on 2024-04-29. Score: 5
So this one depends on your operational semantics, in particular on whether your language exhibits Lazy or Eager Evaluation.
That said, please bear in mind that even in most eagerly-evaluated languages, the Boolean conjunction and disjunction operators (and and or) are exhibiting lazy evaluation.
x ∧ y sometimes mean that false ∧ (x/0) is legal and equal to false, while sometimes it is illegal.
This will become important in what follows.
Memoising And Rewriting
That one is simple enough: you just rewrite comparison chains to conjunctions or disjunctions.
x < y < z becomes x < y ∧ y < z.
Then, assuming Lazy Evaluation, this further gets lowered to force x < force y ∧ force y < force z where each term is memoised.
Assuming Eager Evaluation, the uses of force are explicitated using temporaries as so:
let x' = /*force*/ x;; y' = /*force*/ y in x' < y' ∧ y' < z.
Now, what 3 < 0 < explode() is supposed to do is up to whether Boolean conjunction is lazy or not.
Binary Comparisons Do Not Result In Booleans
That one is a bit more funky but works in existing languages and relies on the binary comparison operators returning a type that can both be turned into Booleans and be used in comparison chains.
It's simple: it holds a reference to its RHS and has the operators compare against the RHS.
Here is the approach illustrated in C++:
template
struct ComparisonVariable
{
    const bool r;
    const T &rhs

    operator bool() const
    {
        return r;
    }

    ComparisonVariable operator <(const T &arg) const
    {
        return ComparisonVariable { r && (bool) (rhs < arg), arg };
    }
};
struct Int
{
    int val;
    ComparisonVariable operator <(const Int &arg) const
    {
        return ComparisonVariable { val < arg.val, arg };
    }
};
/* ... */

extern "C" int printf(const char*, ...);
int main(int argc, char *argv[])
{
    Int x = { 1 }, y = { 2 }, z = { 3 };
    bool fst = x < y < z;
    bool snd = z < y < x;
    printf("%d %d", fst, snd);
}

x < y < z is (x.Int::operator <(y)).ComparisonVariable::operator <(z), where, as you can see, each operand only appears once.
---
### Answer by username_4 on 2024-04-30. Score: 4
You've got some good answers already but I'll just point out that I've had to do this twice, and done it in two different ways; both work.

Method one:

When I was writing a Python-to-Python compiler at Facebook we rewrote `r = a() < b() < c()` via the following steps:

* Does the first operand possibly have a side effect? If no, we're done. If yes, rewrite to `_a = a() ; r = _a < b() < c()`.
* Does the second operand possibly have a side effect? Rewrite similarly, so now we have `_a = a() ; _b = b(); r = _a < _b < c()`
* Is there more than one operator? If yes, it is now safe to rewrite the assignment as `r = _a < _b and _b < c()`.

Method two:

C# does not have this kind of chained comparison but it has many other expressions where the specification says "... but the expression is only evaluated once". Our intermediate form after parsing but before IL generation has a pair of special nodes: one means "evaluate the referred-to expression and store it to a temp", the other means "fetch the value of the temp in the referred-to store expression". That makes the lowered form a DAG rather than a tree but that's not a big deal. The IL generator then decides whether to turn the temp into an unnamed local or a dup instruction or whatever.

Both work. In the first case I was already writing a Python-to-Python compiler so it made sense to do the lowering entirely in Python, in the second case it was just easier to have a custom intermediate format that we could special-case later; do whatever works best for your application.
---
### Answer by username_5 on 2024-04-30. Score: 1
You can translate it to
(a < (temp_b := b)) and (temp_b < c)

before code generation. temp_b should be a unique identifier generated by the compiler. Repeat this replacement for each step in the chained comparisons.
---
