Reduce unneeded parentheses in JS and Python.

This commit is contained in:
Neil Fraser
2016-06-20 17:34:36 -07:00
parent 6502ea5026
commit 4b319d461d
8 changed files with 107 additions and 68 deletions

View File

@@ -77,6 +77,12 @@ Blockly.Generator.prototype.INDENT = ' ';
*/
Blockly.Generator.prototype.COMMENT_WRAP = 60;
/**
* List of outer-inner pairings that do NOT require parentheses.
* @type {!Array.<!Array.<number>>}
*/
Blockly.Generator.prototype.ORDER_OVERRIDES = [];
/**
* Generate code for all blocks in the workspace to the specified language.
* @param {Blockly.Workspace} workspace Workspace to generate code from.
@@ -198,13 +204,13 @@ Blockly.Generator.prototype.blockToCode = function(block) {
* Generate code representing the specified value input.
* @param {!Blockly.Block} block The block containing the input.
* @param {string} name The name of the input.
* @param {number} order The maximum binding strength (minimum order value)
* @param {number} outerOrder The maximum binding strength (minimum order value)
* of any operators adjacent to "block".
* @return {string} Generated code or '' if no blocks are connected or the
* specified input does not exist.
*/
Blockly.Generator.prototype.valueToCode = function(block, name, order) {
if (isNaN(order)) {
Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) {
if (isNaN(outerOrder)) {
goog.asserts.fail('Expecting valid order from block "%s".', block.type);
}
var targetBlock = block.getInputTargetBlock(name);
@@ -226,8 +232,17 @@ Blockly.Generator.prototype.valueToCode = function(block, name, order) {
goog.asserts.fail('Expecting valid order from value block "%s".',
targetBlock.type);
}
if (code && order <= innerOrder) {
if (order == innerOrder && (order == 0 || order == 99)) {
if (!code) {
return '';
}
// Add parentheses if needed.
var parensNeeded = false;
var outerOrderClass = Math.floor(outerOrder);
var innerOrderClass = Math.floor(innerOrder);
if (outerOrderClass <= innerOrderClass) {
if (outerOrderClass == innerOrderClass &&
(outerOrderClass == 0 || outerOrderClass == 99)) {
// Don't generate parens around NONE-NONE and ATOMIC-ATOMIC pairs.
// 0 is the atomic order, 99 is the none order. No parentheses needed.
// In all known languages multiple such code blocks are not order
@@ -236,11 +251,22 @@ Blockly.Generator.prototype.valueToCode = function(block, name, order) {
// The operators outside this code are stonger than the operators
// inside this code. To prevent the code from being pulled apart,
// wrap the code in parentheses.
// Technically, this should be handled on a language-by-language basis.
// However all known (sane) languages use parentheses for grouping.
code = '(' + code + ')';
parensNeeded = true;
// Check for special exceptions.
for (var i = 0; i < this.ORDER_OVERRIDES.length; i++) {
if (this.ORDER_OVERRIDES[i][0] == outerOrder &&
this.ORDER_OVERRIDES[i][1] == innerOrder) {
parensNeeded = false;
break;
}
}
}
}
if (parensNeeded) {
// Technically, this should be handled on a language-by-language basis.
// However all known (sane) languages use parentheses for grouping.
code = '(' + code + ')';
}
return code;
};

View File

@@ -90,12 +90,8 @@ Blockly.Dart['lists_getIndex'] = function(block) {
var where = block.getFieldValue('WHERE') || 'FROM_START';
var at = Blockly.Dart.valueToCode(block, 'AT',
Blockly.Dart.ORDER_UNARY_PREFIX) || '1';
// Special case to avoid wrapping function calls in unneeded parenthesis.
// func()[0] is prefered over (func())[0]
var valueBlock = this.getInputTargetBlock('VALUE');
var order = (valueBlock && valueBlock.type == 'procedures_callreturn') ?
Blockly.Dart.ORDER_NONE : Blockly.Dart.ORDER_UNARY_POSTFIX;
var list = Blockly.Dart.valueToCode(block, 'VALUE', order) || '[]';
var list = Blockly.Dart.valueToCode(block, 'VALUE',
Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
if (where == 'FIRST') {
if (mode == 'GET') {

View File

@@ -70,38 +70,61 @@ Blockly.JavaScript.addReservedWords(
* Order of operation ENUMs.
* https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence
*/
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; // (...)
Blockly.JavaScript.ORDER_ATOMIC = 0; // 0 "" ...
Blockly.JavaScript.ORDER_MEMBER = 1.1; // . []
Blockly.JavaScript.ORDER_NEW = 1.2; // new
Blockly.JavaScript.ORDER_FUNCTION_CALL = 2; // ()
Blockly.JavaScript.ORDER_INCREMENT = 3; // ++
Blockly.JavaScript.ORDER_DECREMENT = 3; // --
Blockly.JavaScript.ORDER_LOGICAL_NOT = 4.1; // !
Blockly.JavaScript.ORDER_BITWISE_NOT = 4.2; // ~
Blockly.JavaScript.ORDER_UNARY_PLUS = 4.3; // +
Blockly.JavaScript.ORDER_UNARY_NEGATION = 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_MULTIPLICATION = 5.1; // *
Blockly.JavaScript.ORDER_DIVISION = 5.2; // /
Blockly.JavaScript.ORDER_MODULUS = 5.3; // %
Blockly.JavaScript.ORDER_ADDITION = 6.1; // +
Blockly.JavaScript.ORDER_SUBTRACTION = 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_COMMA = 17; // ,
Blockly.JavaScript.ORDER_NONE = 99; // (...)
/**
* List of outer-inner pairings that do NOT require parentheses.
* @type {!Array.<!Array.<number>>}
*/
Blockly.JavaScript.ORDER_OVERRIDES = [
// (foo()).bar() -> foo().bar()
// (foo())[0] -> foo()[0]
[Blockly.JavaScript.ORDER_FUNCTION_CALL, Blockly.JavaScript.ORDER_MEMBER],
// (foo[0])[1] -> foo[0][1]
// (foo.bar).baz -> foo.bar.baz
[Blockly.JavaScript.ORDER_MEMBER, Blockly.JavaScript.ORDER_MEMBER],
// !(!foo) -> !!foo
[Blockly.JavaScript.ORDER_LOGICAL_NOT, Blockly.JavaScript.ORDER_LOGICAL_NOT],
// a * (b * c) -> a * b * c
[Blockly.JavaScript.ORDER_MULTIPLICATION, Blockly.JavaScript.ORDER_MULTIPLICATION],
// a + (b + c) -> a + b + c
[Blockly.JavaScript.ORDER_ADDITION, Blockly.JavaScript.ORDER_ADDITION],
// a && (b && c) -> a && b && c
[Blockly.JavaScript.ORDER_LOGICAL_AND, Blockly.JavaScript.ORDER_LOGICAL_AND],
// a || (b || c) -> a || b || c
[Blockly.JavaScript.ORDER_LOGICAL_OR, Blockly.JavaScript.ORDER_LOGICAL_OR]
];
/**
* Allow for switching between one and zero based indexing, one based by

View File

@@ -98,12 +98,8 @@ Blockly.JavaScript['lists_getIndex'] = function(block) {
var where = block.getFieldValue('WHERE') || 'FROM_START';
var at = Blockly.JavaScript.valueToCode(block, 'AT',
Blockly.JavaScript.ORDER_UNARY_NEGATION) || '1';
// Special case to avoid wrapping function calls in unneeded parenthesis.
// func()[0] is prefered over (func())[0]
var valueBlock = this.getInputTargetBlock('VALUE');
var order = (valueBlock && valueBlock.type == 'procedures_callreturn') ?
Blockly.JavaScript.ORDER_NONE : Blockly.JavaScript.ORDER_MEMBER;
var list = Blockly.JavaScript.valueToCode(block, 'VALUE', order) || '[]';
var list = Blockly.JavaScript.valueToCode(block, 'VALUE',
Blockly.JavaScript.ORDER_MEMBER) || '[]';
if (where == 'FIRST') {
if (mode == 'GET') {

View File

@@ -160,11 +160,7 @@ Blockly.Lua['lists_getIndex'] = function(block) {
var at = Blockly.Lua.valueToCode(block, 'AT',
Blockly.Lua.ORDER_ADDITIVE) || '1';
if (mode == 'GET') {
// Special case to avoid wrapping function calls in unneeded parenthesis.
// func()[0] is prefered over (func())[0]
var valueBlock = this.getInputTargetBlock('VALUE');
var order = (valueBlock && valueBlock.type == 'procedures_callreturn') ?
Blockly.Lua.ORDER_NONE : Blockly.Lua.ORDER_HIGH;
var order = Blockly.Lua.ORDER_HIGH;
} else {
// List will be an argument in a function call.
var order = Blockly.Lua.ORDER_NONE;

View File

@@ -133,11 +133,7 @@ Blockly.PHP['lists_getIndex'] = function(block) {
var at = Blockly.PHP.valueToCode(block, 'AT',
Blockly.PHP.ORDER_UNARY_NEGATION) || '1';
if (mode == 'GET') {
// Special case to avoid wrapping function calls in unneeded parenthesis.
// func()[0] is prefered over (func())[0]
var valueBlock = this.getInputTargetBlock('VALUE');
var order = (valueBlock && valueBlock.type == 'procedures_callreturn') ?
Blockly.PHP.ORDER_NONE : Blockly.PHP.ORDER_FUNCTION_CALL;
var order = Blockly.PHP.ORDER_FUNCTION_CALL;
} else {
// List will be an argument in a function call.
var order = Blockly.PHP.ORDER_COMMA;

View File

@@ -79,6 +79,16 @@ Blockly.Python.ORDER_CONDITIONAL = 15; // if else
Blockly.Python.ORDER_LAMBDA = 16; // lambda
Blockly.Python.ORDER_NONE = 99; // (...)
/**
* List of outer-inner pairings that do NOT require parentheses.
* @type {!Array.<!Array.<number>>}
*/
Blockly.Python.ORDER_OVERRIDES = [
// (foo()).bar() -> foo().bar()
// (foo())[0] -> foo()[0]
[Blockly.Python.ORDER_FUNCTION_CALL, Blockly.Python.ORDER_MEMBER]
];
/**
* Initialise the database of variable names.
* @param {!Blockly.Workspace} workspace Workspace to generate code from.

View File

@@ -105,12 +105,8 @@ Blockly.Python['lists_getIndex'] = function(block) {
var where = block.getFieldValue('WHERE') || 'FROM_START';
var at = Blockly.Python.valueToCode(block, 'AT',
Blockly.Python.ORDER_UNARY_SIGN) || '1';
// Special case to avoid wrapping function calls in unneeded parenthesis.
// func()[0] is prefered over (func())[0]
var valueBlock = this.getInputTargetBlock('VALUE');
var order = (valueBlock && valueBlock.type == 'procedures_callreturn') ?
Blockly.Python.ORDER_NONE : Blockly.Python.ORDER_MEMBER;
var list = Blockly.Python.valueToCode(block, 'VALUE', order) || '[]';
var list = Blockly.Python.valueToCode(block, 'VALUE',
Blockly.Python.ORDER_MEMBER) || '[]';
if (where == 'FIRST') {
if (mode == 'GET') {