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?
let temp = b in a < temp and temp < cif you can create an anonymous temporary in your expression tree. $\endgroup$abeforeb, so you must use locals for both. CPython can avoid locals by using the stack instructionsDUP_TOPandROT_THREE, but unfortunately WebAssembly doesn't have those. $\endgroup$