创建自定义块 - 运算符优先级
title: 创建自定义块 - 运算符优先级
代码生成器用于将Blockly的程序转换为JavaScript,Python,PHP,Lua,Dart等。为新块创建代码生成器时最具挑战性的问题是处理操作的顺序,以便生成的代码按预期执行。
坑爹的括号
考虑以下块的装配。
如果生成器不知道运算符优先级,则生成的JavaScript代码将是:
alert(2 * 3 + 4);
这显然是不正确的,因为乘法运算符分割加法,先乘以3。一个解决方案是将每个值块的结果包裹在括号中:
alert(((2) * ((3) + (4)));
这个解决方案完美地工作,但它导致非常凌乱的代码与大量的冗余括号。对于一些使用情况,这不是一个问题。如果人眼永远不会看到生成的代码,那么这是可以接受的。但是,Blockly经常被用作介绍编程的教育工具,一种依赖于生成人类可读代码的用例。
Good Parentheses
为了生成没有不合理数量的括号的正确代码,每个语言生成器都提供了有序的优先级列表。这里是JavaScript的列表:
Blockly.JavaScript.ORDER_ATOMIC = 0; // 0 "" ...
Blockly.JavaScript.ORDER_MEMBER = 1; // . []
Blockly.JavaScript.ORDER_NEW = 1; // new
Blockly.JavaScript.ORDER_FUNCTION_CALL = 2; // ()
Blockly.JavaScript.ORDER_INCREMENT = 3; // ++
Blockly.JavaScript.ORDER_DECREMENT = 3; // --
Blockly.JavaScript.ORDER_LOGICAL_NOT = 4; // !
Blockly.JavaScript.ORDER_BITWISE_NOT = 4; // ~
Blockly.JavaScript.ORDER_UNARY_PLUS = 4; // +
Blockly.JavaScript.ORDER_UNARY_NEGATION = 4; // -
Blockly.JavaScript.ORDER_TYPEOF = 4; // typeof
Blockly.JavaScript.ORDER_VOID = 4; // void
Blockly.JavaScript.ORDER_DELETE = 4; // delete
Blockly.JavaScript.ORDER_MULTIPLICATION = 5; // \*
Blockly.JavaScript.ORDER_DIVISION = 5; // /
Blockly.JavaScript.ORDER_MODULUS = 5; // %
Blockly.JavaScript.ORDER_ADDITION = 6; // +
Blockly.JavaScript.ORDER_SUBTRACTION = 6; // -
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_COMMA = 17; // ,
Blockly.JavaScript.ORDER_NONE = 99; // (...)
此列表的大部分直接取自JavaScript的语言规范language spec,其中ORDER_ATOMIC添加到开始,ORDER_NONE添加到结尾。
应用这些规则会在每个块的生成器中的两个地方使用到。第一个地方是从连接的值块获取生成的代码。在这种情况下,我们传递常数,该常数表示与子块生成的代码相邻的任何运算符的最大绑定强度。例如:
var arg0 = Blockly.JavaScript.valueToCode(this, 'NUM1', Blockly.JavaScript.ORDER_DIVISION);
第二个是从值块返回生成的代码。在这种情况下,我们传递常量,它代表块生成代码中任何运算符的最小绑定强度。例如:
return [arg0 + ' / ' + arg1, Blockly.JavaScript.ORDER_DIVISION];
如果子块返回的顺序值弱于或等于父块的顺序参数的顺序值,那么valueToCode函数将自动将子块代码的内容括在括号中,以防止它由父块的代码割裂开。
下面是一些更多的例子。在每种情况下,块具有被表示为“X”的一个连接子块('X'的内容是未知的并且无关紧要)。第二列列出可能分裂“X”的最强运算符。第三列列出了块的最终代码中最弱的运算符。
生成的代码 | Max strength against X | Min strength of block |
---|---|---|
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作为每个值块上最终返回语句的顺序。结果代码将被不必要的括号侵蚀,但是保证是正确的。