# 操作符优先级

代码生成器用于将Blockly的程序转换为JavaScript,Python,PHP,Lua,Dart等。在为新块编写代码生成器时,最具挑战性的问题是处理操作顺序,以使生成的代码按预期执行。

# 坏括号

考虑下面的块组装。

如果生成器不知道运算符的优先级,则生成的 JavaScript 代码将是:

alert(2*3+4);

这显然是不正确的,因为乘法运算符会撕裂加法,自己获取 “3”。一种解决方案是将每个值块的结果括在括号中:

alert(((2)*((3)+(4)))

该解决方案可以完美地工作,但是会导致代码非常混乱,并带有大量的多余括号。对于某些用例,这不是问题。如果人眼永远看不到生成的代码,那么这是可以接受的。但是,Blockly 通常用作介绍编程的教育工具,该用例依赖于生成人类可读的代码。

# 好括号

为了生成正确的代码而没有过多的括号,每个语言生成器都提供有序的优先级列表。这是 JavaScript 的列表:

Blockly.JavaScript.ORDER_ATOMIC = 0;           // 0 "" ...
Blockly.JavaScript.ORDER_NEW = 1.1;            // new
Blockly.JavaScript.ORDER_MEMBER = 1.2;         // . []
Blockly.JavaScript.ORDER_FUNCTION_CALL = 2;    // ()
Blockly.JavaScript.ORDER_INCREMENT = 3;        // ++
Blockly.JavaScript.ORDER_DECREMENT = 3;        // --
Blockly.JavaScript.ORDER_BITWISE_NOT = 4.1;    // ~
Blockly.JavaScript.ORDER_UNARY_PLUS = 4.2;     // +
Blockly.JavaScript.ORDER_UNARY_NEGATION = 4.3; // -
Blockly.JavaScript.ORDER_LOGICAL_NOT = 4.4;    // !
Blockly.JavaScript.ORDER_TYPEOF = 4.5;         // typeof
Blockly.JavaScript.ORDER_VOID = 4.6;           // void
Blockly.JavaScript.ORDER_DELETE = 4.7;         // delete
Blockly.JavaScript.ORDER_AWAIT = 4.8;          // await
Blockly.JavaScript.ORDER_EXPONENTIATION = 5.0; // **
Blockly.JavaScript.ORDER_MULTIPLICATION = 5.1; // *
Blockly.JavaScript.ORDER_DIVISION = 5.2;       // /
Blockly.JavaScript.ORDER_MODULUS = 5.3;        // %
Blockly.JavaScript.ORDER_SUBTRACTION = 6.1;    // -
Blockly.JavaScript.ORDER_ADDITION = 6.2;       // +
Blockly.JavaScript.ORDER_BITWISE_SHIFT = 7;    // << >> >>>
Blockly.JavaScript.ORDER_RELATIONAL = 8;       // < <= > >=
Blockly.JavaScript.ORDER_IN = 8;               // in
Blockly.JavaScript.ORDER_INSTANCEOF = 8;       // instanceof
Blockly.JavaScript.ORDER_EQUALITY = 9;         // == != === !==
Blockly.JavaScript.ORDER_BITWISE_AND = 10;     // &
Blockly.JavaScript.ORDER_BITWISE_XOR = 11;     // ^
Blockly.JavaScript.ORDER_BITWISE_OR = 12;      // |
Blockly.JavaScript.ORDER_LOGICAL_AND = 13;     // &&
Blockly.JavaScript.ORDER_LOGICAL_OR = 14;      // ||
Blockly.JavaScript.ORDER_CONDITIONAL = 15;     // ?:
Blockly.JavaScript.ORDER_ASSIGNMENT = 16;      // = += -= **= *= /= %= <<= >>= ...
Blockly.JavaScript.ORDER_YIELD = 16.5;         // yield
Blockly.JavaScript.ORDER_COMMA = 17;           // ,
Blockly.JavaScript.ORDER_NONE = 99;            // (...)

此列表的大部分直接取自 JavaScript 的 语言规范 (opens new window),并将 ORDER_ATOMIC 添加到开头,将 ORDER_NONE 添加到结尾。

应用这些命令发生在每个块生成器的两个位置。首先是从连接的值块中获取生成的代码时。在这种情况下,我们传递常数,该常数代表与子块的生成代码相邻的所有运算符的最大绑定强度。例如:

var arg0 = Blockly.JavaScript.valueToCode(this, 'NUM1', Blockly.JavaScript.ORDER_DIVISION);

第二地方是从值块返回生成的代码时。在这种情况下,我们传递常数,该常数表示块生成的代码中任何运算符的最小绑定强度。例如:

return [arg0 + ' / ' + arg1, Blockly.JavaScript.ORDER_DIVISION];

如果子块返回的顺序值小于或等于父块的 order 参数的顺序值,则该 valueToCode 函数将自动将子块的代码内容包装在括号中,以防止其被撕破除了父块的代码。

以下是更多示例。在每种情况下,该块都有一个连接的子块,表示为“X”(“X”的内容未知且无关紧要)。第二列列出了可能会拆分“X”的最强运算符。第三列列出了该块最终代码中最弱的运算符。

生成的代码 最大抗X强度 最小块强度
X + 1 ORDER_ADDITION ORDER_ADDITION
Math.sqrt(X) ORDER_NONE ORDER_MEMBER
!X && false ORDER_LOGICAL_NOT ORDER_LOGICAL_AND
foo[X % 60] ORDER_MODULUS ORDER_MEMBER

# 数学很难

还是不明白? 没问题。 只需将 ORDER_ATOMIC 用作每次对 valueToCode 的调用的顺序,并将 ORDER_NONE 用作每个值块的最终返回语句的顺序。 产生的代码将带有不必要的括号,但可以保证它是正确的。