diff --git a/core/field_angle.js b/core/field_angle.js index 00b0c16a4..9cb5675f4 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -154,15 +154,15 @@ Blockly.FieldAngle.prototype.showEditor_ = function() { 'y1': Blockly.FieldAngle.HALF, 'class': 'blocklyAngleLine'}, svg); // Draw markers around the edge. - for (var a = 0; a < 360; a += 15) { + for (var angle = 0; angle < 360; angle += 15) { Blockly.createSvgElement('line', { 'x1': Blockly.FieldAngle.HALF + Blockly.FieldAngle.RADIUS, 'y1': Blockly.FieldAngle.HALF, 'x2': Blockly.FieldAngle.HALF + Blockly.FieldAngle.RADIUS - - (a % 45 == 0 ? 10 : 5), + (angle % 45 == 0 ? 10 : 5), 'y2': Blockly.FieldAngle.HALF, 'class': 'blocklyAngleMarks', - 'transform': 'rotate(' + a + ',' + + 'transform': 'rotate(' + angle + ',' + Blockly.FieldAngle.HALF + ',' + Blockly.FieldAngle.HALF + ')' }, svg); } diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 1e62cf50c..ec3dd4f5a 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -122,9 +122,9 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() { var menu = new goog.ui.Menu(); menu.setRightToLeft(this.sourceBlock_.RTL); var options = this.getOptions_(); - for (var x = 0; x < options.length; x++) { - var text = options[x][0]; // Human-readable text. - var value = options[x][1]; // Language-neutral value. + for (var i = 0; i < options.length; i++) { + var text = options[i][0]; // Human-readable text. + var value = options[i][1]; // Language-neutral value. var menuItem = new goog.ui.MenuItem(text); menuItem.setRightToLeft(this.sourceBlock_.RTL); menuItem.setValue(value); @@ -223,11 +223,11 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() { } // Remove the prefix and suffix from the options. var newOptions = []; - for (var x = 0; x < options.length; x++) { - var text = options[x][0]; - var value = options[x][1]; + for (var i = 0; i < options.length; i++) { + var text = options[i][0]; + var value = options[i][1]; text = text.substring(prefixLength, text.length - suffixLength); - newOptions[x] = [text, value]; + newOptions[i] = [text, value]; } this.menuGenerator_ = newOptions; }; @@ -268,10 +268,10 @@ Blockly.FieldDropdown.prototype.setValue = function(newValue) { this.value_ = newValue; // Look up and display the human-readable text. var options = this.getOptions_(); - for (var x = 0; x < options.length; x++) { + for (var i = 0; i < options.length; i++) { // Options are tuples of human-readable text and language-neutral values. - if (options[x][1] == newValue) { - this.setText(options[x][0]); + if (options[i][1] == newValue) { + this.setText(options[i][0]); return; } } diff --git a/core/field_variable.js b/core/field_variable.js index cf903c04c..3c6491794 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -113,8 +113,8 @@ Blockly.FieldVariable.dropdownCreate = function() { // Variables are not language-specific, use the name as both the user-facing // text and the internal representation. var options = []; - for (var x = 0; x < variableList.length; x++) { - options[x] = [variableList[x], variableList[x]]; + for (var i = 0; i < variableList.length; i++) { + options[i] = [variableList[i], variableList[i]]; } return options; }; diff --git a/core/generator.js b/core/generator.js index 8ee110f86..fecc35531 100644 --- a/core/generator.js +++ b/core/generator.js @@ -132,7 +132,7 @@ Blockly.Generator.prototype.workspaceToCode = function(workspace) { * @return {string} The prefixed lines of code. */ Blockly.Generator.prototype.prefixLines = function(text, prefix) { - return prefix + text.replace(/\n(.)/g, '\n' + prefix + '$1'); + return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix); }; /** @@ -143,8 +143,8 @@ Blockly.Generator.prototype.prefixLines = function(text, prefix) { Blockly.Generator.prototype.allNestedComments = function(block) { var comments = []; var blocks = block.getDescendants(); - for (var x = 0; x < blocks.length; x++) { - var comment = blocks[x].getCommentText(); + for (var i = 0; i < blocks.length; i++) { + var comment = blocks[i].getCommentText(); if (comment) { comments.push(comment); } diff --git a/core/input.js b/core/input.js index 346b053cb..4b4eb1df9 100644 --- a/core/input.js +++ b/core/input.js @@ -222,8 +222,8 @@ Blockly.Input.prototype.init = function() { if (!this.sourceBlock_.workspace.rendered) { return; // Headless blocks don't need fields initialized. } - for (var x = 0; x < this.fieldRow.length; x++) { - this.fieldRow[x].init(); + for (var i = 0; i < this.fieldRow.length; i++) { + this.fieldRow[i].init(); } }; diff --git a/core/procedures.js b/core/procedures.js index f98fbd92a..34419fb54 100644 --- a/core/procedures.js +++ b/core/procedures.js @@ -197,9 +197,9 @@ Blockly.Procedures.flyoutCategory = function(workspace) { var mutation = goog.dom.createDom('mutation'); mutation.setAttribute('name', name); block.appendChild(mutation); - for (var t = 0; t < args.length; t++) { + for (var j = 0; j < args.length; j++) { var arg = goog.dom.createDom('arg'); - arg.setAttribute('name', args[t]); + arg.setAttribute('name', args[j]); mutation.appendChild(arg); } xmlList.push(block); diff --git a/core/rendered_connection.js b/core/rendered_connection.js index ef7130dc6..552248526 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -234,8 +234,8 @@ Blockly.RenderedConnection.prototype.unhideAll = function() { // Show all connections of this block. connections = block.getConnections_(true); } - for (var c = 0; c < connections.length; c++) { - renderList.push.apply(renderList, connections[c].unhideAll()); + for (var i = 0; i < connections.length; i++) { + renderList.push.apply(renderList, connections[i].unhideAll()); } if (!renderList.length) { // Leaf block. @@ -275,17 +275,17 @@ Blockly.RenderedConnection.prototype.hideAll = function() { this.setHidden(true); if (this.targetConnection) { var blocks = this.targetBlock().getDescendants(); - for (var b = 0; b < blocks.length; b++) { - var block = blocks[b]; + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; // Hide all connections of all children. var connections = block.getConnections_(true); - for (var c = 0; c < connections.length; c++) { - connections[c].setHidden(true); + for (var j = 0; j < connections.length; j++) { + connections[j].setHidden(true); } // Close all bubbles of all children. var icons = block.getIcons(); - for (var i = 0; i < icons.length; i++) { - icons[i].setVisible(false); + for (var j = 0; j < icons.length; j++) { + icons[j].setVisible(false); } } } diff --git a/core/variables.js b/core/variables.js index 9399177fe..450d33fcf 100644 --- a/core/variables.js +++ b/core/variables.js @@ -54,10 +54,10 @@ Blockly.Variables.allVariables = function(root) { } var variableHash = Object.create(null); // Iterate through every block and add each variable to the hash. - for (var x = 0; x < blocks.length; x++) { - var blockVariables = blocks[x].getVars(); - for (var y = 0; y < blockVariables.length; y++) { - var varName = blockVariables[y]; + for (var i = 0; i < blocks.length; i++) { + var blockVariables = blocks[i].getVars(); + for (var j = 0; j < blockVariables.length; j++) { + var varName = blockVariables[j]; // Variable name may be null if the block is only half-built. if (varName) { variableHash[varName.toLowerCase()] = varName; diff --git a/demos/blockfactory/blocks.js b/demos/blockfactory/blocks.js index 5008e6a20..012c76fe0 100644 --- a/demos/blockfactory/blocks.js +++ b/demos/blockfactory/blocks.js @@ -505,13 +505,13 @@ Blockly.Blocks['type_group'] = { // Parse XML to restore the group of types. this.typeCount_ = parseInt(container.getAttribute('types'), 10); this.updateShape_(); - for (var x = 0; x < this.typeCount_; x++) { - this.removeInput('TYPE' + x); + for (var i = 0; i < this.typeCount_; i++) { + this.removeInput('TYPE' + i); } - for (var x = 0; x < this.typeCount_; x++) { - var input = this.appendValueInput('TYPE' + x) + for (var i = 0; i < this.typeCount_; i++) { + var input = this.appendValueInput('TYPE' + i) .setCheck('Type'); - if (x == 0) { + if (i == 0) { input.appendField('any of'); } } diff --git a/demos/blockfactory/factory.js b/demos/blockfactory/factory.js index 3e1cf16a0..1bfdf6ce9 100644 --- a/demos/blockfactory/factory.js +++ b/demos/blockfactory/factory.js @@ -501,8 +501,8 @@ function getTypesFrom_(block, name) { types = [escapeString(typeBlock.getFieldValue('TYPE'))]; } else if (typeBlock.type == 'type_group') { types = []; - for (var n = 0; n < typeBlock.typeCount_; n++) { - types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + n)); + for (var i = 0; i < typeBlock.typeCount_; i++) { + types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + i)); } // Remove duplicates. var hash = Object.create(null); diff --git a/generators/dart.js b/generators/dart.js index f0d7715ea..6fb1236d4 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -45,16 +45,28 @@ Blockly.Dart = new Blockly.Generator('Dart'); Blockly.Dart.addReservedWords( // https://www.dartlang.org/docs/spec/latest/dart-language-specification.pdf // Section 16.1.1 - 'assert,break,case,catch,class,const,continue,default,do,else,enum,extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,switch,this,throw,true,try,var,void,while,with,' + + 'assert,break,case,catch,class,const,continue,default,do,else,enum,' + + 'extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,' + + 'switch,this,throw,true,try,var,void,while,with,' + // https://api.dartlang.org/dart_core.html - 'print,identityHashCode,identical,BidirectionalIterator,Comparable,double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,ArgumentError,AssertionError,CastError,ConcurrentModificationError,CyclicInitializationError,Error,Exception,FallThroughError,FormatException,IntegerDivisionByZeroException,NoSuchMethodError,NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,StateError,TypeError,UnimplementedError,UnsupportedError'); + 'print,identityHashCode,identical,BidirectionalIterator,Comparable,' + + 'double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,' + + 'Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,' + + 'Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,' + + 'StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,' + + 'ArgumentError,AssertionError,CastError,ConcurrentModificationError,' + + 'CyclicInitializationError,Error,Exception,FallThroughError,' + + 'FormatException,IntegerDivisionByZeroException,NoSuchMethodError,' + + 'NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,' + + 'StateError,TypeError,UnimplementedError,UnsupportedError' +); /** * Order of operation ENUMs. * https://www.dartlang.org/docs/dart-up-and-running/ch02.html#operator_table */ Blockly.Dart.ORDER_ATOMIC = 0; // 0 "" ... -Blockly.Dart.ORDER_UNARY_POSTFIX = 1; // expr++ expr-- () [] . +Blockly.Dart.ORDER_UNARY_POSTFIX = 1; // expr++ expr-- () [] . ?. Blockly.Dart.ORDER_UNARY_PREFIX = 2; // -expr !expr ~expr ++expr --expr Blockly.Dart.ORDER_MULTIPLICATIVE = 3; // * / % ~/ Blockly.Dart.ORDER_ADDITIVE = 4; // + - @@ -66,11 +78,18 @@ Blockly.Dart.ORDER_RELATIONAL = 9; // >= > <= < as is is! Blockly.Dart.ORDER_EQUALITY = 10; // == != Blockly.Dart.ORDER_LOGICAL_AND = 11; // && Blockly.Dart.ORDER_LOGICAL_OR = 12; // || -Blockly.Dart.ORDER_CONDITIONAL = 13; // expr ? expr : expr -Blockly.Dart.ORDER_CASCADE = 14; // .. -Blockly.Dart.ORDER_ASSIGNMENT = 15; // = *= /= ~/= %= += -= <<= >>= &= ^= |= +Blockly.Dart.ORDER_IF_NULL = 13; // ?? +Blockly.Dart.ORDER_CONDITIONAL = 14; // expr ? expr : expr +Blockly.Dart.ORDER_CASCADE = 15; // .. +Blockly.Dart.ORDER_ASSIGNMENT = 16; // = *= /= ~/= %= += -= <<= >>= &= ^= |= Blockly.Dart.ORDER_NONE = 99; // (...) +/** + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. + */ +Blockly.Dart.ONE_BASED_INDEXING = true; + /** * Initialise the database of variable names. * @param {!Blockly.Workspace} workspace Workspace to generate code from. @@ -182,9 +201,9 @@ Blockly.Dart.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.Dart.allNestedComments(childBlock); if (comment) { @@ -198,3 +217,63 @@ Blockly.Dart.scrub_ = function(block, code) { var nextCode = Blockly.Dart.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ +Blockly.Dart.getAdjusted = function(block, atId, opt_delta, opt_negate, + opt_order) { + var delta = opt_delta || 0; + var order = opt_order || Blockly.Dart.ORDER_NONE; + if (Blockly.Dart.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.Dart.ONE_BASED_INDEXING ? '1' : '0'; + if (delta) { + var at = Blockly.Dart.valueToCode(block, atId, + Blockly.Dart.ORDER_ADDITIVE) || defaultAtIndex; + } else if (opt_negate) { + var at = Blockly.Dart.valueToCode(block, atId, + Blockly.Dart.ORDER_UNARY_PREFIX) || defaultAtIndex; + } else { + var at = Blockly.Dart.valueToCode(block, atId, order) || + defaultAtIndex; + } + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseInt(at, 10) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + var innerOrder = Blockly.Dart.ORDER_ADDITIVE; + } else if (delta < 0) { + at = at + ' - ' + -delta; + var innerOrder = Blockly.Dart.ORDER_ADDITIVE; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + var innerOrder = Blockly.Dart.ORDER_UNARY_PREFIX; + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; + } + } + return at; +}; diff --git a/generators/dart/colour.js b/generators/dart/colour.js index d73415522..7ded91419 100644 --- a/generators/dart/colour.js +++ b/generators/dart/colour.js @@ -43,13 +43,13 @@ Blockly.Dart['colour_random'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'colour_random', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' String hex = \'0123456789abcdef\';', - ' var rnd = new Math.Random();', - ' return \'#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', - ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', - ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\';', - '}']); + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', + ' String hex = \'0123456789abcdef\';', + ' var rnd = new Math.Random();', + ' return \'#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', + ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', + ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\';', + '}']); var code = functionName + '()'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; @@ -67,22 +67,22 @@ Blockly.Dart['colour_rgb'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'colour_rgb', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num r, num g, num b) {', - ' num rn = (Math.max(Math.min(r, 1), 0) * 255).round();', - ' String rs = rn.toInt().toRadixString(16);', - ' rs = \'0$rs\';', - ' rs = rs.substring(rs.length - 2);', - ' num gn = (Math.max(Math.min(g, 1), 0) * 255).round();', - ' String gs = gn.toInt().toRadixString(16);', - ' gs = \'0$gs\';', - ' gs = gs.substring(gs.length - 2);', - ' num bn = (Math.max(Math.min(b, 1), 0) * 255).round();', - ' String bs = bn.toInt().toRadixString(16);', - ' bs = \'0$bs\';', - ' bs = bs.substring(bs.length - 2);', - ' return \'#$rs$gs$bs\';', - '}']); + ' num rn = (Math.max(Math.min(r, 1), 0) * 255).round();', + ' String rs = rn.toInt().toRadixString(16);', + ' rs = \'0$rs\';', + ' rs = rs.substring(rs.length - 2);', + ' num gn = (Math.max(Math.min(g, 1), 0) * 255).round();', + ' String gs = gn.toInt().toRadixString(16);', + ' gs = \'0$gs\';', + ' gs = gs.substring(gs.length - 2);', + ' num bn = (Math.max(Math.min(b, 1), 0) * 255).round();', + ' String bs = bn.toInt().toRadixString(16);', + ' bs = \'0$bs\';', + ' bs = bs.substring(bs.length - 2);', + ' return \'#$rs$gs$bs\';', + '}']); var code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; @@ -100,29 +100,29 @@ Blockly.Dart['colour_blend'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'colour_blend', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String c1, String c2, num ratio) {', - ' ratio = Math.max(Math.min(ratio, 1), 0);', - ' int r1 = int.parse(\'0x${c1.substring(1, 3)}\');', - ' int g1 = int.parse(\'0x${c1.substring(3, 5)}\');', - ' int b1 = int.parse(\'0x${c1.substring(5, 7)}\');', - ' int r2 = int.parse(\'0x${c2.substring(1, 3)}\');', - ' int g2 = int.parse(\'0x${c2.substring(3, 5)}\');', - ' int b2 = int.parse(\'0x${c2.substring(5, 7)}\');', - ' num rn = (r1 * (1 - ratio) + r2 * ratio).round();', - ' String rs = rn.toInt().toRadixString(16);', - ' num gn = (g1 * (1 - ratio) + g2 * ratio).round();', - ' String gs = gn.toInt().toRadixString(16);', - ' num bn = (b1 * (1 - ratio) + b2 * ratio).round();', - ' String bs = bn.toInt().toRadixString(16);', - ' rs = \'0$rs\';', - ' rs = rs.substring(rs.length - 2);', - ' gs = \'0$gs\';', - ' gs = gs.substring(gs.length - 2);', - ' bs = \'0$bs\';', - ' bs = bs.substring(bs.length - 2);', - ' return \'#$rs$gs$bs\';', - '}']); + ' ratio = Math.max(Math.min(ratio, 1), 0);', + ' int r1 = int.parse(\'0x${c1.substring(1, 3)}\');', + ' int g1 = int.parse(\'0x${c1.substring(3, 5)}\');', + ' int b1 = int.parse(\'0x${c1.substring(5, 7)}\');', + ' int r2 = int.parse(\'0x${c2.substring(1, 3)}\');', + ' int g2 = int.parse(\'0x${c2.substring(3, 5)}\');', + ' int b2 = int.parse(\'0x${c2.substring(5, 7)}\');', + ' num rn = (r1 * (1 - ratio) + r2 * ratio).round();', + ' String rs = rn.toInt().toRadixString(16);', + ' num gn = (g1 * (1 - ratio) + g2 * ratio).round();', + ' String gs = gn.toInt().toRadixString(16);', + ' num bn = (b1 * (1 - ratio) + b2 * ratio).round();', + ' String bs = bn.toInt().toRadixString(16);', + ' rs = \'0$rs\';', + ' rs = rs.substring(rs.length - 2);', + ' gs = \'0$gs\';', + ' gs = gs.substring(gs.length - 2);', + ' bs = \'0$bs\';', + ' bs = bs.substring(bs.length - 2);', + ' return \'#$rs$gs$bs\';', + '}']); var code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/lists.js b/generators/dart/lists.js index 24fcd2243..ae5ecc20e 100644 --- a/generators/dart/lists.js +++ b/generators/dart/lists.js @@ -38,49 +38,52 @@ Blockly.Dart['lists_create_empty'] = function(block) { Blockly.Dart['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Dart.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Dart.valueToCode(block, 'ADD' + i, Blockly.Dart.ORDER_NONE) || 'null'; } - code = '[' + code.join(', ') + ']'; + var code = '[' + elements.join(', ') + ']'; return [code, Blockly.Dart.ORDER_ATOMIC]; }; Blockly.Dart['lists_repeat'] = function(block) { // Create a list with one element repeated. - var argument0 = Blockly.Dart.valueToCode(block, 'ITEM', - Blockly.Dart.ORDER_NONE) || 'null'; - var argument1 = Blockly.Dart.valueToCode(block, 'NUM', - Blockly.Dart.ORDER_NONE) || '0'; - var code = 'new List.filled(' + argument1 + ', ' + argument0 + ')'; + var element = Blockly.Dart.valueToCode(block, 'ITEM', + Blockly.Dart.ORDER_NONE) || 'null'; + var repeatCount = Blockly.Dart.valueToCode(block, 'NUM', + Blockly.Dart.ORDER_NONE) || '0'; + var code = 'new List.filled(' + repeatCount + ', ' + element + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var list = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; - return [argument0 + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [list + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var list = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; - return [argument0 + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [list + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_indexOf'] = function(block) { // Find an item in the list. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.Dart.valueToCode(block, 'FIND', + var item = Blockly.Dart.valueToCode(block, 'FIND', Blockly.Dart.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Dart.valueToCode(block, 'VALUE', + var list = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Dart.ORDER_ADDITIVE]; + var code = list + '.' + operator + '(' + item + ')'; + if (Blockly.Dart.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE]; + } + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_getIndex'] = function(block) { @@ -88,94 +91,156 @@ Blockly.Dart['lists_getIndex'] = function(block) { // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Dart.valueToCode(block, 'AT', - Blockly.Dart.ORDER_UNARY_PREFIX) || '1'; - var list = Blockly.Dart.valueToCode(block, 'VALUE', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; + var listOrder = (where == 'RANDOM' || where == 'FROM_END') ? + Blockly.Dart.ORDER_NONE : Blockly.Dart.ORDER_UNARY_POSTFIX; + var list = Blockly.Dart.valueToCode(block, 'VALUE', listOrder) || '[]'; + // Cache non-trivial values to variables to prevent repeated look-ups. + // Closure, which accesses and modifies 'list'. + function cacheList() { + var listVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_list', Blockly.Variables.NAME_TYPE); + var code = 'List ' + listVar + ' = ' + list + ';\n'; + list = listVar; + return code; + } + // If `list` would be evaluated more than once (which is the case for + // RANDOM REMOVE and FROM_END) and is non-trivial, make sure to access it + // only once. + if (((where == 'RANDOM' && mode == 'REMOVE') || where == 'FROM_END') && + !list.match(/^\w+$/)) { + // `list` is an expression, so we may not evaluate it more than once. + if (where == 'RANDOM') { + Blockly.Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + // We can use multiple statements. + var code = cacheList(); + var xVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list + + '.length);\n'; + code += list + '.removeAt(' + xVar + ');\n'; + return code; + } else { // where == 'FROM_END' + if (mode == 'REMOVE') { + // We can use multiple statements. + var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + var code = cacheList(); + code += list + '.removeAt(' + list + '.length' + ' - ' + at + ');\n'; + return code; - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '.first'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.removeAt(0)'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return list + '.removeAt(0);\n'; - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = list + '.last'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.removeLast()'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return list + '.removeLast();\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.removeAt(' + at + ')'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return list + '.removeAt(' + at + ');\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var functionName = Blockly.Dart.provideFunction_( - 'lists_get_from_end', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List myList, num x) {', - ' x = myList.length - x;', - ' return myList.removeAt(x);', - '}']); - code = functionName + '(' + list + ', ' + at + ')'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { - var functionName = Blockly.Dart.provideFunction_( - 'lists_remove_from_end', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List myList, num x) {', - ' x = myList.length - x;', - ' return myList.removeAt(x);', - '}']); - code = functionName + '(' + list + ', ' + at + ')'; - if (mode == 'GET_REMOVE') { + } else if (mode == 'GET') { + var at = Blockly.Dart.getAdjusted(block, 'AT', 1); + // We need to create a procedure to avoid reevaluating values. + var functionName = Blockly.Dart.provideFunction_( + 'lists_get_from_end', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list, num x) {', + ' x = my_list.length - x;', + ' return my_list[x];', + '}']); + var code = functionName + '(' + list + ', ' + at + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var at = Blockly.Dart.getAdjusted(block, 'AT', 1); + // We need to create a procedure to avoid reevaluating values. + var functionName = Blockly.Dart.provideFunction_( + 'lists_remove_from_end', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list, num x) {', + ' x = my_list.length - x;', + ' return my_list.removeAt(x);', + '}']); + var code = functionName + '(' + list + ', ' + at + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return code + ';\n'; } } - } else if (where == 'RANDOM') { - Blockly.Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - var functionName = Blockly.Dart.provideFunction_( - 'lists_get_random_item', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List myList, bool remove) {', - ' int x = new Math.Random().nextInt(myList.length);', - ' if (remove) {', - ' return myList.removeAt(x);', - ' } else {', - ' return myList[x];', - ' }', - '}']); - code = functionName + '(' + list + ', ' + (mode != 'GET') + ')'; - if (mode == 'GET' || mode == 'GET_REMOVE') { - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else if (mode == 'REMOVE') { - return code + ';\n'; + } else { + // Either `list` is a simple variable, or we only need to refer to `list` + // once. + switch (where) { + case 'FIRST': + if (mode == 'GET') { + var code = list + '.first'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.removeAt(0)'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return list + '.removeAt(0);\n'; + } + break; + case 'LAST': + if (mode == 'GET') { + var code = list + '.last'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.removeLast()'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return list + '.removeLast();\n'; + } + break; + case 'FROM_START': + var at = Blockly.Dart.getAdjusted(block, 'AT'); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.removeAt(' + at + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return list + '.removeAt(' + at + ');\n'; + } + break; + case 'FROM_END': + var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + if (mode == 'GET') { + var code = list + '[' + list + '.length - ' + at + ']'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + var code = list + '.removeAt(' + list + '.length - ' + at + ')'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'REMOVE') { + return code + ';\n'; + } + } + break; + case 'RANDOM': + Blockly.Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + if (mode == 'REMOVE') { + // We can use multiple statements. + var xVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + var code = 'int ' + xVar + ' = new Math.Random().nextInt(' + list + + '.length);\n'; + code += list + '.removeAt(' + xVar + ');\n'; + return code; + } else if (mode == 'GET') { + var functionName = Blockly.Dart.provideFunction_( + 'lists_get_random_item', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list) {', + ' int x = new Math.Random().nextInt(my_list.length);', + ' return my_list[x];', + '}']); + var code = functionName + '(' + list + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } else if (mode == 'GET_REMOVE') { + var functionName = Blockly.Dart.provideFunction_( + 'lists_remove_random_item', + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(List my_list) {', + ' int x = new Math.Random().nextInt(my_list.length);', + ' return my_list.removeAt(x);', + '}']); + var code = functionName + '(' + list + ')'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + } + break; } } throw 'Unhandled combination (lists_getIndex).'; @@ -184,12 +249,10 @@ Blockly.Dart['lists_getIndex'] = function(block) { Blockly.Dart['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. - var list = Blockly.Dart.valueToCode(block, 'LIST', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Dart.valueToCode(block, 'AT', - Blockly.Dart.ORDER_ADDITIVE) || '1'; + var list = Blockly.Dart.valueToCode(block, 'LIST', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var value = Blockly.Dart.valueToCode(block, 'TO', Blockly.Dart.ORDER_ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. @@ -204,59 +267,61 @@ Blockly.Dart['lists_setIndex'] = function(block) { list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.insert(0, ' + value + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { + switch (where) { + case 'FIRST': + if (mode == 'SET') { + return list + '[0] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.insert(0, ' + value + ');\n'; + } + break; + case 'LAST': + if (mode == 'SET') { + var code = cacheList(); + code += list + '[' + list + '.length - 1] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + return list + '.add(' + value + ');\n'; + } + break; + case 'FROM_START': + var at = Blockly.Dart.getAdjusted(block, 'AT'); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.insert(' + at + ', ' + value + ');\n'; + } + break; + case 'FROM_END': + var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false, + Blockly.Dart.ORDER_ADDITIVE); var code = cacheList(); - code += list + '[' + list + '.length - 1] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - return list + '.add(' + value + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.insert(' + at + ', ' + value + ');\n'; - } - } else if (where == 'FROM_END') { - var code = cacheList(); - if (mode == 'SET') { - code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.insert(' + list + '.length - ' + at + ', ' + - value + ');\n'; - return code; - } - } else if (where == 'RANDOM') { - Blockly.Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - var code = cacheList(); - var xVar = Blockly.Dart.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += 'int ' + xVar + - ' = new Math.Random().nextInt(' + list + '.length);'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.insert(' + xVar + ', ' + value + ');\n'; - return code; - } + if (mode == 'SET') { + code += list + '[' + list + '.length - ' + at + '] = ' + value + + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.insert(' + list + '.length - ' + at + ', ' + + value + ');\n'; + return code; + } + break; + case 'RANDOM': + Blockly.Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + var code = cacheList(); + var xVar = Blockly.Dart.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += 'int ' + xVar + + ' = new Math.Random().nextInt(' + list + '.length);\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.insert(' + xVar + ', ' + value + ');\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; @@ -267,43 +332,67 @@ Blockly.Dart['lists_getSublist'] = function(block) { Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Dart.valueToCode(block, 'AT1', - Blockly.Dart.ORDER_NONE) || '1'; - var at2 = Blockly.Dart.valueToCode(block, 'AT2', - Blockly.Dart.ORDER_NONE) || '1'; - if ((where1 == 'FIRST' || where1 == 'FROM_START' && Blockly.isNumber(at1)) && - (where2 == 'LAST' || where2 == 'FROM_START' && Blockly.isNumber(at2))) { - // Simple case that can be done inline. - at1 = where1 == 'FIRST' ? 0 : parseInt(at1, 10) - 1; + if (list.match(/^\w+$/) || (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the list is a is a variable or doesn't require a call for length, + // don't generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + at1 = list + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 0, false, + Blockly.Dart.ORDER_ADDITIVE); + at2 = list + '.length - ' + at2; + break; + case 'LAST': + // There is no second index if LAST option is chosen. + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } if (where2 == 'LAST') { - code = list + '.sublist(' + at1 + ')'; + var code = list + '.sublist(' + at1 + ')'; } else { - at2 = parseInt(at2, 10); - code = list + '.sublist(' + at1 + ', ' + at2 + ')'; + var code = list + '.sublist(' + at1 + ', ' + at2 + ')'; } } else { + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + var at2 = Blockly.Dart.getAdjusted(block, 'AT2'); var functionName = Blockly.Dart.provideFunction_( 'lists_get_sublist', - [ 'List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(list, where1, at1, where2, at2) {', - ' int getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = list.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = list.length - 1;', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return list.sublist(at1, at2);', - '}']); + ' int getAt(where, at) {', + ' if (where == \'FROM_END\') {', + ' at = list.length - 1 - at;', + ' } else if (where == \'FIRST\') {', + ' at = 0;', + ' } else if (where == \'LAST\') {', + ' at = list.length - 1;', + ' } else if (where != \'FROM_START\') {', + ' throw \'Unhandled option (lists_getSublist).\';', + ' }', + ' return at;', + ' }', + ' at1 = getAt(where1, at1);', + ' at2 = getAt(where2, at2) + 1;', + ' return list.sublist(at1, at2);', + '}']); var code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -312,51 +401,52 @@ Blockly.Dart['lists_getSublist'] = function(block) { Blockly.Dart['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = Blockly.Dart.valueToCode(block, 'LIST', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]'; + var list = Blockly.Dart.valueToCode(block, 'LIST', + Blockly.Dart.ORDER_NONE) || '[]'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var sortFunctionName = Blockly.Dart.provideFunction_( - 'lists_sort', - ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(list, type, direction) {', - ' var compareFuncs = {', - ' "NUMERIC": (a, b) => direction * a.compareTo(b),', - ' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),', - ' "IGNORE_CASE": ', - ' (a, b) => direction * ', - ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())', - ' };', - ' list = new List.from(list);', // Clone the list. - ' var compare = compareFuncs[type];', - ' list.sort(compare);', - ' return list;', - '}']); - return [sortFunctionName + '(' + listCode + ', ' + + 'lists_sort', + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + '(list, type, direction) {', + ' var compareFuncs = {', + ' "NUMERIC": (a, b) => direction * a.compareTo(b),', + ' "TEXT": (a, b) => direction * ' + + 'a.toString().compareTo(b.toString()),', + ' "IGNORE_CASE": ', + ' (a, b) => direction * ', + ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())', + ' };', + ' list = new List.from(list);', // Clone the list. + ' var compare = compareFuncs[type];', + ' list.sort(compare);', + ' return list;', + '}']); + return [sortFunctionName + '(' + list + ', ' + '"' + type + '", ' + direction + ')', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - var value_input = Blockly.Dart.valueToCode(block, 'INPUT', + var input = Blockly.Dart.valueToCode(block, 'INPUT', Blockly.Dart.ORDER_UNARY_POSTFIX); - var value_delim = Blockly.Dart.valueToCode(block, 'DELIM', + var delimiter = Blockly.Dart.valueToCode(block, 'DELIM', Blockly.Dart.ORDER_NONE) || '\'\''; var mode = block.getFieldValue('MODE'); if (mode == 'SPLIT') { - if (!value_input) { - value_input = '\'\''; + if (!input) { + input = '\'\''; } var functionName = 'split'; } else if (mode == 'JOIN') { - if (!value_input) { - value_input = '[]'; + if (!input) { + input = '[]'; } var functionName = 'join'; } else { throw 'Unknown mode: ' + mode; } - var code = value_input + '.' + functionName + '(' + value_delim + ')'; + var code = input + '.' + functionName + '(' + delimiter + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/math.js b/generators/dart/math.js index 2f7c8027a..867822beb 100644 --- a/generators/dart/math.js +++ b/generators/dart/math.js @@ -200,25 +200,25 @@ Blockly.Dart['math_number_property'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_isPrime', - [ 'bool ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', - ' return true;', - ' }', - ' // False if n is null, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + + ['bool ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if (n == 2 || n == 3) {', + ' return true;', + ' }', + ' // False if n is null, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + ' n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', + ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); code = functionName + '(' + number_to_check + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; } @@ -275,12 +275,12 @@ Blockly.Dart['math_on_list'] = function(block) { case 'SUM': var functionName = Blockly.Dart.provideFunction_( 'math_sum', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' num sumVal = 0;', - ' myList.forEach((num entry) {sumVal += entry;});', - ' return sumVal;', - '}']); + ' num sumVal = 0;', + ' myList.forEach((num entry) {sumVal += entry;});', + ' return sumVal;', + '}']); code = functionName + '(' + list + ')'; break; case 'MIN': @@ -288,14 +288,14 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_min', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' if (myList.isEmpty) return null;', - ' num minVal = myList[0];', - ' myList.forEach((num entry) ' + + ' if (myList.isEmpty) return null;', + ' num minVal = myList[0];', + ' myList.forEach((num entry) ' + '{minVal = Math.min(minVal, entry);});', - ' return minVal;', - '}']); + ' return minVal;', + '}']); code = functionName + '(' + list + ')'; break; case 'MAX': @@ -303,14 +303,14 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_max', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' if (myList.isEmpty) return null;', - ' num maxVal = myList[0];', - ' myList.forEach((num entry) ' + - '{maxVal = Math.max(maxVal, entry);});', - ' return maxVal;', - '}']); + ' if (myList.isEmpty) return null;', + ' num maxVal = myList[0];', + ' myList.forEach((num entry) ' + + '{maxVal = Math.max(maxVal, entry);});', + ' return maxVal;', + '}']); code = functionName + '(' + list + ')'; break; case 'AVERAGE': @@ -318,38 +318,38 @@ Blockly.Dart['math_on_list'] = function(block) { // math_mean([null,null,"aString",1,9]) == 5.0. var functionName = Blockly.Dart.provideFunction_( 'math_mean', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only.', - ' List localList = new List.from(myList);', - ' localList.removeMatching((a) => a is! num);', - ' if (localList.isEmpty) return null;', - ' num sumVal = 0;', - ' localList.forEach((num entry) {sumVal += entry;});', - ' return sumVal / localList.length;', - '}']); + ' // First filter list for numbers only.', + ' List localList = new List.from(myList);', + ' localList.removeWhere((a) => a is! num);', + ' if (localList.isEmpty) return null;', + ' num sumVal = 0;', + ' localList.forEach((num entry) {sumVal += entry;});', + ' return sumVal / localList.length;', + '}']); code = functionName + '(' + list + ')'; break; case 'MEDIAN': var functionName = Blockly.Dart.provideFunction_( 'math_median', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only, then sort, ' + + ' // First filter list for numbers only, then sort, ' + 'then return middle value', - ' // or the average of two middle values if list has an ' + + ' // or the average of two middle values if list has an ' + 'even number of elements.', - ' List localList = new List.from(myList);', - ' localList.removeMatching((a) => a is! num);', - ' if (localList.isEmpty) return null;', - ' localList.sort((a, b) => (a - b));', - ' int index = localList.length ~/ 2;', - ' if (localList.length % 2 == 1) {', - ' return localList[index];', - ' } else {', - ' return (localList[index - 1] + localList[index]) / 2;', - ' }', - '}']); + ' List localList = new List.from(myList);', + ' localList.removeWhere((a) => a is! num);', + ' if (localList.isEmpty) return null;', + ' localList.sort((a, b) => (a - b));', + ' int index = localList.length ~/ 2;', + ' if (localList.length % 2 == 1) {', + ' return localList[index];', + ' } else {', + ' return (localList[index - 1] + localList[index]) / 2;', + ' }', + '}']); code = functionName + '(' + list + ')'; break; case 'MODE': @@ -360,35 +360,35 @@ Blockly.Dart['math_on_list'] = function(block) { // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.Dart.provideFunction_( 'math_modes', - [ 'List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List values) {', - ' List modes = [];', - ' List counts = [];', - ' int maxCount = 0;', - ' for (int i = 0; i < values.length; i++) {', - ' var value = values[i];', - ' bool found = false;', - ' int thisCount;', - ' for (int j = 0; j < counts.length; j++) {', - ' if (counts[j][0] == value) {', - ' thisCount = ++counts[j][1];', - ' found = true;', - ' break;', - ' }', - ' }', - ' if (!found) {', - ' counts.add([value, 1]);', - ' thisCount = 1;', - ' }', - ' maxCount = Math.max(thisCount, maxCount);', - ' }', - ' for (int j = 0; j < counts.length; j++) {', - ' if (counts[j][1] == maxCount) {', - ' modes.add(counts[j][0]);', - ' }', - ' }', - ' return modes;', - '}']); + ' List modes = [];', + ' List counts = [];', + ' int maxCount = 0;', + ' for (int i = 0; i < values.length; i++) {', + ' var value = values[i];', + ' bool found = false;', + ' int thisCount;', + ' for (int j = 0; j < counts.length; j++) {', + ' if (counts[j][0] == value) {', + ' thisCount = ++counts[j][1];', + ' found = true;', + ' break;', + ' }', + ' }', + ' if (!found) {', + ' counts.add([value, 1]);', + ' thisCount = 1;', + ' }', + ' maxCount = Math.max(thisCount, maxCount);', + ' }', + ' for (int j = 0; j < counts.length; j++) {', + ' if (counts[j][1] == maxCount) {', + ' modes.add(counts[j][0]);', + ' }', + ' }', + ' return modes;', + '}']); code = functionName + '(' + list + ')'; break; case 'STD_DEV': @@ -396,21 +396,21 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_standard_deviation', - [ 'num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['num ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only.', - ' List numbers = new List.from(myList);', - ' numbers.removeMatching((a) => a is! num);', - ' if (numbers.isEmpty) return null;', - ' num n = numbers.length;', - ' num sum = 0;', - ' numbers.forEach((x) => sum += x);', - ' num mean = sum / n;', - ' num sumSquare = 0;', - ' numbers.forEach((x) => sumSquare += ' + - 'Math.pow(x - mean, 2));', - ' return Math.sqrt(sumSquare / n);', - '}']); + ' // First filter list for numbers only.', + ' List numbers = new List.from(myList);', + ' numbers.removeWhere((a) => a is! num);', + ' if (numbers.isEmpty) return null;', + ' num n = numbers.length;', + ' num sum = 0;', + ' numbers.forEach((x) => sum += x);', + ' num mean = sum / n;', + ' num sumSquare = 0;', + ' numbers.forEach((x) => sumSquare += ' + + 'Math.pow(x - mean, 2));', + ' return Math.sqrt(sumSquare / n);', + '}']); code = functionName + '(' + list + ')'; break; case 'RANDOM': @@ -418,11 +418,11 @@ Blockly.Dart['math_on_list'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'math_random_item', - [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' int x = new Math.Random().nextInt(myList.length);', - ' return myList[x];', - '}']); + ' int x = new Math.Random().nextInt(myList.length);', + ' return myList[x];', + '}']); code = functionName + '(' + list + ')'; break; default: @@ -466,15 +466,15 @@ Blockly.Dart['math_random_int'] = function(block) { Blockly.Dart.ORDER_NONE) || '0'; var functionName = Blockly.Dart.provideFunction_( 'math_random_int', - [ 'int ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num a, num b) {', - ' if (a > b) {', - ' // Swap a and b to ensure a is smaller.', - ' num c = a;', - ' a = b;', - ' b = c;', - ' }', - ' return new Math.Random().nextInt(b - a + 1) + a;', - '}']); + ['int ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num a, num b) {', + ' if (a > b) {', + ' // Swap a and b to ensure a is smaller.', + ' num c = a;', + ' a = b;', + ' b = c;', + ' }', + ' return new Math.Random().nextInt(b - a + 1) + a;', + '}']); var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/procedures.js b/generators/dart/procedures.js index 0085c6bf6..ad2550cc7 100644 --- a/generators/dart/procedures.js +++ b/generators/dart/procedures.js @@ -50,8 +50,8 @@ Blockly.Dart['procedures_defreturn'] = function(block) { } var returnType = returnValue ? 'dynamic' : 'void'; var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Dart.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Dart.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = returnType + ' ' + funcName + '(' + args.join(', ') + ') {\n' + @@ -71,8 +71,8 @@ Blockly.Dart['procedures_callreturn'] = function(block) { var funcName = Blockly.Dart.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Dart.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Dart.valueToCode(block, 'ARG' + i, Blockly.Dart.ORDER_NONE) || 'null'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -84,8 +84,8 @@ Blockly.Dart['procedures_callnoreturn'] = function(block) { var funcName = Blockly.Dart.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Dart.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Dart.valueToCode(block, 'ARG' + i, Blockly.Dart.ORDER_NONE) || 'null'; } var code = funcName + '(' + args.join(', ') + ');\n'; diff --git a/generators/dart/text.js b/generators/dart/text.js index ba5a75cb0..5cdbb03ec 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.js @@ -39,22 +39,22 @@ Blockly.Dart['text'] = function(block) { Blockly.Dart['text_join'] = function(block) { // Create a string made up of any number of elements of any type. - var code; - if (block.itemCount_ == 0) { - return ['\'\'', Blockly.Dart.ORDER_ATOMIC]; - } else if (block.itemCount_ == 1) { - var argument0 = Blockly.Dart.valueToCode(block, 'ADD0', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - code = argument0 + '.toString()'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; - } else { - code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Dart.valueToCode(block, 'ADD' + n, - Blockly.Dart.ORDER_NONE) || '\'\''; - } - code = '[' + code.join(',') + '].join()'; - return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + switch (block.itemCount_) { + case 0: + return ['\'\'', Blockly.Dart.ORDER_ATOMIC]; + case 1: + var element = Blockly.Dart.valueToCode(block, 'ADD0', + Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; + var code = element + '.toString()'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; + default: + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Dart.valueToCode(block, 'ADD' + i, + Blockly.Dart.ORDER_NONE) || '\'\''; + } + var code = '[' + elements.join(',') + '].join()'; + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; } }; @@ -62,43 +62,44 @@ Blockly.Dart['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.Dart.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return varName + ' = [' + varName + ', ' + argument0 + '].join();\n'; + var value = Blockly.Dart.valueToCode(block, 'TEXT', + Blockly.Dart.ORDER_NONE) || '\'\''; + return varName + ' = [' + varName + ', ' + value + '].join();\n'; }; Blockly.Dart['text_length'] = function(block) { // String or array length. - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return [argument0 + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [text + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Dart.valueToCode(block, 'VALUE', + var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return [argument0 + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [text + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_indexOf'] = function(block) { // Search the text for a substring. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.Dart.valueToCode(block, 'FIND', + var substring = Blockly.Dart.valueToCode(block, 'FIND', Blockly.Dart.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Dart.valueToCode(block, 'VALUE', + var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Dart.ORDER_ADDITIVE]; + var code = text + '.' + operator + '(' + substring + ')'; + if (Blockly.Dart.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE]; + } + return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Dart.valueToCode(block, 'AT', - Blockly.Dart.ORDER_NONE) || '1'; var text = Blockly.Dart.valueToCode(block, 'VALUE', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; switch (where) { @@ -106,26 +107,20 @@ Blockly.Dart['text_charAt'] = function(block) { var code = text + '[0]'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; case 'FROM_START': - // Blockly uses one-based indicies. - if (at.match(/^-?\d+$/)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } + var at = Blockly.Dart.getAdjusted(block, 'AT'); var code = text + '[' + at + ']'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; case 'LAST': at = 1; // Fall through. case 'FROM_END': + var at = Blockly.Dart.getAdjusted(block, 'AT', 1); var functionName = Blockly.Dart.provideFunction_( 'text_get_from_end', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String text, num x) {', - ' return text[text.length - x];', - '}']); + ' return text[text.length - x];', + '}']); code = functionName + '(' + text + ', ' + at + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; case 'RANDOM': @@ -133,11 +128,11 @@ Blockly.Dart['text_charAt'] = function(block) { 'import \'dart:math\' as Math;'; var functionName = Blockly.Dart.provideFunction_( 'text_random_letter', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String text) {', - ' int x = new Math.Random().nextInt(text.length);', - ' return text[x];', - '}']); + ' int x = new Math.Random().nextInt(text.length);', + ' return text[x];', + '}']); code = functionName + '(' + text + ')'; return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; } @@ -150,35 +145,69 @@ Blockly.Dart['text_getSubstring'] = function(block) { Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Dart.valueToCode(block, 'AT1', - Blockly.Dart.ORDER_NONE) || '1'; - var at2 = Blockly.Dart.valueToCode(block, 'AT2', - Blockly.Dart.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = text; + } else if (text.match(/^'?\w+'?$/) || + (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the text is a variable or literal or doesn't require a call for + // length, don't generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.Dart.getAdjusted(block, 'AT1', 1, false, + Blockly.Dart.ORDER_ADDITIVE); + at1 = text + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 0, false, + Blockly.Dart.ORDER_ADDITIVE); + at2 = text + '.length - ' + at2; + break; + case 'LAST': + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + if (where2 == 'LAST') { + var code = text + '.substring(' + at1 + ')'; + } else { + var code = text + '.substring(' + at1 + ', ' + at2 + ')'; + } } else { + var at1 = Blockly.Dart.getAdjusted(block, 'AT1'); + var at2 = Blockly.Dart.getAdjusted(block, 'AT2'); var functionName = Blockly.Dart.provideFunction_( 'text_get_substring', - [ 'function ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(text, where1, at1, where2, at2) {', - ' function getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = text.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = text.length - 1;', - ' } else {', - ' throw \'Unhandled option (text_getSubstring).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return text.substring(at1, at2);', - '}']); + ' int getAt(where, at) {', + ' if (where == \'FROM_END\') {', + ' at = text.length - 1 - at;', + ' } else if (where == \'FIRST\') {', + ' at = 0;', + ' } else if (where == \'LAST\') {', + ' at = text.length - 1;', + ' } else if (where != \'FROM_START\') {', + ' throw \'Unhandled option (text_getSubstring).\';', + ' }', + ' return at;', + ' }', + ' at1 = getAt(where1, at1);', + ' at2 = getAt(where2, at2) + 1;', + ' return text.substring(at1, at2);', + '}']); var code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -193,34 +222,32 @@ Blockly.Dart['text_changeCase'] = function(block) { 'TITLECASE': null }; var operator = OPERATORS[block.getFieldValue('CASE')]; - var code; + var textOrder = operator ? Blockly.Dart.ORDER_UNARY_POSTFIX : + Blockly.Dart.ORDER_NONE; + var text = Blockly.Dart.valueToCode(block, 'TEXT', textOrder) || '\'\''; if (operator) { // Upper and lower case are functions built into Dart. - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', - Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - code = argument0 + operator; + var code = text + operator; } else { // Title case is not a native Dart function. Define one. var functionName = Blockly.Dart.provideFunction_( 'text_toTitleCase', - [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + + ['String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String str) {', - ' RegExp exp = new RegExp(r\'\\b\');', - ' List list = str.split(exp);', - ' final title = new StringBuffer();', - ' for (String part in list) {', - ' if (part.length > 0) {', - ' title.write(part[0].toUpperCase());', - ' if (part.length > 0) {', - ' title.write(part.substring(1).toLowerCase());', - ' }', - ' }', - ' }', - ' return title.toString();', - '}']); - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', - Blockly.Dart.ORDER_NONE) || '\'\''; - code = functionName + '(' + argument0 + ')'; + ' RegExp exp = new RegExp(r\'\\b\');', + ' List list = str.split(exp);', + ' final title = new StringBuffer();', + ' for (String part in list) {', + ' if (part.length > 0) {', + ' title.write(part[0].toUpperCase());', + ' if (part.length > 0) {', + ' title.write(part.substring(1).toLowerCase());', + ' }', + ' }', + ' }', + ' return title.toString();', + '}']); + var code = functionName + '(' + text + ')'; } return [code, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; @@ -233,16 +260,16 @@ Blockly.Dart['text_trim'] = function(block) { 'BOTH': '.trim()' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', + var text = Blockly.Dart.valueToCode(block, 'TEXT', Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\''; - return [argument0 + operator, Blockly.Dart.ORDER_UNARY_POSTFIX]; + return [text + operator, Blockly.Dart.ORDER_UNARY_POSTFIX]; }; Blockly.Dart['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.Dart.valueToCode(block, 'TEXT', + var msg = Blockly.Dart.valueToCode(block, 'TEXT', Blockly.Dart.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ');\n'; + return 'print(' + msg + ');\n'; }; Blockly.Dart['text_prompt_ext'] = function(block) { diff --git a/generators/javascript.js b/generators/javascript.js index dcd1f146d..a81b62b0c 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -135,8 +135,8 @@ Blockly.JavaScript.ORDER_OVERRIDES = [ ]; /** - * Allow for switching between one and zero based indexing, one based by - * default. + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. */ Blockly.JavaScript.ONE_BASED_INDEXING = true; @@ -258,3 +258,66 @@ Blockly.JavaScript.scrub_ = function(block, code) { var nextCode = Blockly.JavaScript.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ +Blockly.JavaScript.getAdjusted = function(block, atId, opt_delta, opt_negate, + opt_order) { + var delta = opt_delta || 0; + var order = opt_order || Blockly.JavaScript.ORDER_NONE; + if (Blockly.JavaScript.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.JavaScript.ONE_BASED_INDEXING ? '1' : '0'; + if (delta > 0) { + var at = Blockly.JavaScript.valueToCode(block, atId, + Blockly.JavaScript.ORDER_ADDITION) || defaultAtIndex; + } else if (delta < 0) { + var at = Blockly.JavaScript.valueToCode(block, atId, + Blockly.JavaScript.ORDER_SUBTRACTION) || defaultAtIndex; + } else if (opt_negate) { + var at = Blockly.JavaScript.valueToCode(block, atId, + Blockly.JavaScript.ORDER_UNARY_NEGATION) || defaultAtIndex; + } else { + var at = Blockly.JavaScript.valueToCode(block, atId, order) || + defaultAtIndex; + } + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseFloat(at) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + var innerOrder = Blockly.JavaScript.ORDER_ADDITION; + } else if (delta < 0) { + at = at + ' - ' + -delta; + var innerOrder = Blockly.JavaScript.ORDER_SUBTRACTION; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + var innerOrder = Blockly.JavaScript.ORDER_UNARY_NEGATION; + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; + } + } + return at; +}; diff --git a/generators/javascript/colour.js b/generators/javascript/colour.js index b8e290360..21b372e9c 100644 --- a/generators/javascript/colour.js +++ b/generators/javascript/colour.js @@ -39,7 +39,7 @@ Blockly.JavaScript['colour_random'] = function(block) { // Generate a random colour. var functionName = Blockly.JavaScript.provideFunction_( 'colourRandom', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', ' var num = Math.floor(Math.random() * Math.pow(2, 24));', ' return \'#\' + (\'00000\' + num.toString(16)).substr(-6);', '}']); @@ -57,16 +57,16 @@ Blockly.JavaScript['colour_rgb'] = function(block) { Blockly.JavaScript.ORDER_COMMA) || 0; var functionName = Blockly.JavaScript.provideFunction_( 'colourRgb', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b) {', - ' r = Math.max(Math.min(Number(r), 100), 0) * 2.55;', - ' g = Math.max(Math.min(Number(g), 100), 0) * 2.55;', - ' b = Math.max(Math.min(Number(b), 100), 0) * 2.55;', - ' r = (\'0\' + (Math.round(r) || 0).toString(16)).slice(-2);', - ' g = (\'0\' + (Math.round(g) || 0).toString(16)).slice(-2);', - ' b = (\'0\' + (Math.round(b) || 0).toString(16)).slice(-2);', - ' return \'#\' + r + g + b;', - '}']); + ' r = Math.max(Math.min(Number(r), 100), 0) * 2.55;', + ' g = Math.max(Math.min(Number(g), 100), 0) * 2.55;', + ' b = Math.max(Math.min(Number(b), 100), 0) * 2.55;', + ' r = (\'0\' + (Math.round(r) || 0).toString(16)).slice(-2);', + ' g = (\'0\' + (Math.round(g) || 0).toString(16)).slice(-2);', + ' b = (\'0\' + (Math.round(b) || 0).toString(16)).slice(-2);', + ' return \'#\' + r + g + b;', + '}']); var code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; @@ -81,23 +81,23 @@ Blockly.JavaScript['colour_blend'] = function(block) { Blockly.JavaScript.ORDER_COMMA) || 0.5; var functionName = Blockly.JavaScript.provideFunction_( 'colourBlend', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(c1, c2, ratio) {', - ' ratio = Math.max(Math.min(Number(ratio), 1), 0);', - ' var r1 = parseInt(c1.substring(1, 3), 16);', - ' var g1 = parseInt(c1.substring(3, 5), 16);', - ' var b1 = parseInt(c1.substring(5, 7), 16);', - ' var r2 = parseInt(c2.substring(1, 3), 16);', - ' var g2 = parseInt(c2.substring(3, 5), 16);', - ' var b2 = parseInt(c2.substring(5, 7), 16);', - ' var r = Math.round(r1 * (1 - ratio) + r2 * ratio);', - ' var g = Math.round(g1 * (1 - ratio) + g2 * ratio);', - ' var b = Math.round(b1 * (1 - ratio) + b2 * ratio);', - ' r = (\'0\' + (r || 0).toString(16)).slice(-2);', - ' g = (\'0\' + (g || 0).toString(16)).slice(-2);', - ' b = (\'0\' + (b || 0).toString(16)).slice(-2);', - ' return \'#\' + r + g + b;', - '}']); + ' ratio = Math.max(Math.min(Number(ratio), 1), 0);', + ' var r1 = parseInt(c1.substring(1, 3), 16);', + ' var g1 = parseInt(c1.substring(3, 5), 16);', + ' var b1 = parseInt(c1.substring(5, 7), 16);', + ' var r2 = parseInt(c2.substring(1, 3), 16);', + ' var g2 = parseInt(c2.substring(3, 5), 16);', + ' var b2 = parseInt(c2.substring(5, 7), 16);', + ' var r = Math.round(r1 * (1 - ratio) + r2 * ratio);', + ' var g = Math.round(g1 * (1 - ratio) + g2 * ratio);', + ' var b = Math.round(b1 * (1 - ratio) + b2 * ratio);', + ' r = (\'0\' + (r || 0).toString(16)).slice(-2);', + ' g = (\'0\' + (g || 0).toString(16)).slice(-2);', + ' b = (\'0\' + (b || 0).toString(16)).slice(-2);', + ' return \'#\' + r + g + b;', + '}']); var code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index 4bb6aac20..d1e66a0ed 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -36,12 +36,12 @@ Blockly.JavaScript['lists_create_empty'] = function(block) { Blockly.JavaScript['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.JavaScript.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.JavaScript.valueToCode(block, 'ADD' + i, Blockly.JavaScript.ORDER_COMMA) || 'null'; } - code = '[' + code.join(', ') + ']'; + var code = '[' + elements.join(', ') + ']'; return [code, Blockly.JavaScript.ORDER_ATOMIC]; }; @@ -49,46 +49,49 @@ Blockly.JavaScript['lists_repeat'] = function(block) { // Create a list with one element repeated. var functionName = Blockly.JavaScript.provideFunction_( 'listsRepeat', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(value, n) {', - ' var array = [];', - ' for (var i = 0; i < n; i++) {', - ' array[i] = value;', - ' }', - ' return array;', - '}']); - var argument0 = Blockly.JavaScript.valueToCode(block, 'ITEM', + ' var array = [];', + ' for (var i = 0; i < n; i++) {', + ' array[i] = value;', + ' }', + ' return array;', + '}']); + var element = Blockly.JavaScript.valueToCode(block, 'ITEM', Blockly.JavaScript.ORDER_COMMA) || 'null'; - var argument1 = Blockly.JavaScript.valueToCode(block, 'NUM', + var repeatCount = Blockly.JavaScript.valueToCode(block, 'NUM', Blockly.JavaScript.ORDER_COMMA) || '0'; - var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + var code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_FUNCTION_CALL) || '[]'; - return [argument0 + '.length', Blockly.JavaScript.ORDER_MEMBER]; + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', + Blockly.JavaScript.ORDER_MEMBER) || '[]'; + return [list + '.length', Blockly.JavaScript.ORDER_MEMBER]; }; Blockly.JavaScript['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; - return ['!' + argument0 + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; + return ['!' + list + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; }; Blockly.JavaScript['lists_indexOf'] = function(block) { // Find an item in the list. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.JavaScript.valueToCode(block, 'FIND', + var item = Blockly.JavaScript.valueToCode(block, 'FIND', Blockly.JavaScript.ORDER_NONE) || '\'\''; - var argument1 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.JavaScript.ORDER_ADDITION]; + var code = list + '.' + operator + '(' + item + ')'; + if (Blockly.JavaScript.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION]; + } + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_getIndex'] = function(block) { @@ -96,86 +99,76 @@ Blockly.JavaScript['lists_getIndex'] = function(block) { // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.JavaScript.valueToCode(block, 'AT', - Blockly.JavaScript.ORDER_UNARY_NEGATION) || '1'; - var list = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_MEMBER) || '[]'; + var listOrder = (where == 'RANDOM') ? Blockly.JavaScript.ORDER_COMMA : + Blockly.JavaScript.ORDER_MEMBER; + var list = Blockly.JavaScript.valueToCode(block, 'VALUE', listOrder) || '[]'; - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '[0]'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.shift()'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'REMOVE') { - return list + '.shift();\n'; - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = list + '.slice(-1)[0]'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.pop()'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'REMOVE') { - return list + '.pop();\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.JavaScript.ORDER_MEMBER]; - } else if (mode == 'GET_REMOVE') { - var code = list + '.splice(' + at + ', 1)[0]'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return list + '.splice(' + at + ', 1);\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var code = list + '.slice(-' + at + ')[0]'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + switch (where) { + case ('FIRST'): + if (mode == 'GET') { + var code = list + '[0]'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.shift()'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'REMOVE') { + return list + '.shift();\n'; + } + break; + case ('LAST'): + if (mode == 'GET') { + var code = list + '.slice(-1)[0]'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop()'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'REMOVE') { + return list + '.pop();\n'; + } + break; + case ('FROM_START'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT'); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.JavaScript.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.splice(' + at + ', 1)[0]'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return list + '.splice(' + at + ', 1);\n'; + } + break; + case ('FROM_END'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, true); + if (mode == 'GET') { + var code = list + '.slice(' + at + ')[0]'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.splice(' + at + ', 1)[0]'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return list + '.splice(' + at + ', 1);'; + } + break; + case ('RANDOM'): var functionName = Blockly.JavaScript.provideFunction_( - 'listsRemoveFromEnd', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, x) {', - ' x = list.length - x;', - ' return list.splice(x, 1)[0];', - '}']); - code = functionName + '(' + list + ', ' + at + ')'; - if (mode == 'GET_REMOVE') { + 'listsGetRandomItem', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(list, remove) {', + ' var x = Math.floor(Math.random() * list.length);', + ' if (remove) {', + ' return list.splice(x, 1)[0];', + ' } else {', + ' return list[x];', + ' }', + '}']); + code = functionName + '(' + list + ', ' + (mode != 'GET') + ')'; + if (mode == 'GET' || mode == 'GET_REMOVE') { return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { return code + ';\n'; } - } - } else if (where == 'RANDOM') { - var functionName = Blockly.JavaScript.provideFunction_( - 'listsGetRandomItem', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, remove) {', - ' var x = Math.floor(Math.random() * list.length);', - ' if (remove) {', - ' return list.splice(x, 1)[0];', - ' } else {', - ' return list[x];', - ' }', - '}']); - code = functionName + '(' + list + ', ' + (mode != 'GET') + ')'; - if (mode == 'GET' || mode == 'GET_REMOVE') { - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return code + ';\n'; - } + break; } throw 'Unhandled combination (lists_getIndex).'; }; @@ -187,8 +180,6 @@ Blockly.JavaScript['lists_setIndex'] = function(block) { Blockly.JavaScript.ORDER_MEMBER) || '[]'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.JavaScript.valueToCode(block, 'AT', - Blockly.JavaScript.ORDER_NONE) || '1'; var value = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. @@ -198,155 +189,206 @@ Blockly.JavaScript['lists_setIndex'] = function(block) { return ''; } var listVar = Blockly.JavaScript.variableDB_.getDistinctName( - 'tmp_list', Blockly.Variables.NAME_TYPE); + 'tmpList', Blockly.Variables.NAME_TYPE); var code = 'var ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.unshift(' + value + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { + switch (where) { + case ('FIRST'): + if (mode == 'SET') { + return list + '[0] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.unshift(' + value + ');\n'; + } + break; + case ('LAST'): + if (mode == 'SET') { + var code = cacheList(); + code += list + '[' + list + '.length - 1] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + return list + '.push(' + value + ');\n'; + } + break; + case ('FROM_START'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT'); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + return list + '.splice(' + at + ', 0, ' + value + ');\n'; + } + break; + case ('FROM_END'): + var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, false, + Blockly.JavaScript.ORDER_SUBTRACTION); var code = cacheList(); - code += list + '[' + list + '.length - 1] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - return list + '.push(' + value + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return list + '.splice(' + at + ', 0, ' + value + ');\n'; - } - } else if (where == 'FROM_END') { - var code = cacheList(); - if (mode == 'SET') { - code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.splice(' + list + '.length - ' + at + ', 0, ' + value + - ');\n'; - return code; - } - } else if (where == 'RANDOM') { - var code = cacheList(); - var xVar = Blockly.JavaScript.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + - '.length);\n'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.splice(' + xVar + ', 0, ' + value + ');\n'; - return code; - } + if (mode == 'SET') { + code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.splice(' + list + '.length - ' + at + ', 0, ' + value + + ');\n'; + return code; + } + break; + case ('RANDOM'): + var code = cacheList(); + var xVar = Blockly.JavaScript.variableDB_.getDistinctName( + 'tmpX', Blockly.Variables.NAME_TYPE); + code += 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + + '.length);\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.splice(' + xVar + ', 0, ' + value + ');\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; +/** + * Returns an expression calculating the index into a list. + * @private + * @param {string} listName Name of the list, used to calculate length. + * @param {string} where The method of indexing, selected by dropdown in Blockly + * @param {string=} opt_at The optional offset when indexing from start/end. + * @return {string} Index expression. + */ +Blockly.JavaScript.lists.getIndex_ = function(listName, where, opt_at) { + if (where == 'FIRST') { + return '0'; + } else if (where == 'FROM_END') { + return listName + '.length - 1 - ' + opt_at; + } else if (where == 'LAST') { + return listName + '.length - 1'; + } else { + return opt_at; + } +}; + Blockly.JavaScript['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_MEMBER) || '[]'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.JavaScript.valueToCode(block, 'AT1', - Blockly.JavaScript.ORDER_NONE) || '1'; - var at2 = Blockly.JavaScript.valueToCode(block, 'AT2', - Blockly.JavaScript.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { - var code = list + '.concat()'; + var code = list + '.slice(0)'; + } else if (list.match(/^\w+$/) || + (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the list is a variable or doesn't require a call for length, don't + // generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at1 = list + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at2 = list + '.length - ' + at2; + break; + case 'LAST': + var at2 = list + '.length'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + code = list + '.slice(' + at1 + ', ' + at2 + ')'; } else { + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + var getIndex_ = Blockly.JavaScript.lists.getIndex_; + var wherePascalCase = {'FIRST': 'First', 'LAST': 'Last', + 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'}; var functionName = Blockly.JavaScript.provideFunction_( - 'listsGetSublist', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, where1, at1, where2, at2) {', - ' function getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = list.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = list.length - 1;', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return list.slice(at1, at2);', + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(sequence' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') + + ') {', + ' var start = ' + getIndex_('sequence', where1, 'at1') + ';', + ' var end = ' + getIndex_('sequence', where2, 'at2') + ' + 1;', + ' return sequence.slice(start, end);', '}']); - var code = functionName + '(' + list + ', \'' + - where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; + var code = functionName + '(' + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') + + ')'; } return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = Blockly.JavaScript.valueToCode( - block, 'LIST', + var list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '[]'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var getCompareFunctionName = Blockly.JavaScript.provideFunction_( - 'listsGetSortCompare', - ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(type, direction) {', - ' var compareFuncs = {', - ' "NUMERIC": function(a, b) {', - ' return parseFloat(a) - parseFloat(b); },', - ' "TEXT": function(a, b) {', - ' return a.toString() > b.toString() ? 1 : -1; },', - ' "IGNORE_CASE": function(a, b) {', - ' return a.toString().toLowerCase() > ' + - 'b.toString().toLowerCase() ? 1 : -1; },', - ' };', - ' var compare = compareFuncs[type];', - ' return function(a, b) { return compare(a, b) * direction; }', - '}']); - return ['(' + listCode + ').slice().sort(' + + 'listsGetSortCompare', + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(type, direction) {', + ' var compareFuncs = {', + ' "NUMERIC": function(a, b) {', + ' return parseFloat(a) - parseFloat(b); },', + ' "TEXT": function(a, b) {', + ' return a.toString() > b.toString() ? 1 : -1; },', + ' "IGNORE_CASE": function(a, b) {', + ' return a.toString().toLowerCase() > ' + + 'b.toString().toLowerCase() ? 1 : -1; },', + ' };', + ' var compare = compareFuncs[type];', + ' return function(a, b) { return compare(a, b) * direction; }', + '}']); + return [list + '.slice().sort(' + getCompareFunctionName + '("' + type + '", ' + direction + '))', Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - var value_input = Blockly.JavaScript.valueToCode(block, 'INPUT', + var input = Blockly.JavaScript.valueToCode(block, 'INPUT', Blockly.JavaScript.ORDER_MEMBER); - var value_delim = Blockly.JavaScript.valueToCode(block, 'DELIM', + var delimiter = Blockly.JavaScript.valueToCode(block, 'DELIM', Blockly.JavaScript.ORDER_NONE) || '\'\''; var mode = block.getFieldValue('MODE'); if (mode == 'SPLIT') { - if (!value_input) { - value_input = '\'\''; + if (!input) { + input = '\'\''; } var functionName = 'split'; } else if (mode == 'JOIN') { - if (!value_input) { - value_input = '[]'; + if (!input) { + input = '[]'; } var functionName = 'join'; } else { throw 'Unknown mode: ' + mode; } - var code = value_input + '.' + functionName + '(' + value_delim + ')'; + var code = input + '.' + functionName + '(' + delimiter + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/math.js b/generators/javascript/math.js index 0a7d14621..a31b94392 100644 --- a/generators/javascript/math.js +++ b/generators/javascript/math.js @@ -168,25 +168,25 @@ Blockly.JavaScript['math_number_property'] = function(block) { // Prime is a special case as it is not a one-liner test. var functionName = Blockly.JavaScript.provideFunction_( 'mathIsPrime', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', - ' return true;', - ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if (n == 2 || n == 3) {', + ' return true;', + ' }', + ' // False if n is NaN, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + ' n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', + ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); code = functionName + '(' + number_to_check + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } @@ -254,7 +254,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // mathMean([null,null,1,3]) == 2.0. var functionName = Blockly.JavaScript.provideFunction_( 'mathMean', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(myList) {', ' return myList.reduce(function(x, y) {return x + y;}) / ' + 'myList.length;', @@ -267,7 +267,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // mathMedian([null,null,1,3]) == 2.0. var functionName = Blockly.JavaScript.provideFunction_( 'mathMedian', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(myList) {', ' var localList = myList.filter(function (x) ' + '{return typeof x == \'number\';});', @@ -290,7 +290,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.JavaScript.provideFunction_( 'mathModes', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(values) {', ' var modes = [];', ' var counts = [];', @@ -326,7 +326,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { case 'STD_DEV': var functionName = Blockly.JavaScript.provideFunction_( 'mathStandardDeviation', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(numbers) {', ' var n = numbers.length;', ' if (!n) return null;', @@ -345,7 +345,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { case 'RANDOM': var functionName = Blockly.JavaScript.provideFunction_( 'mathRandomList', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(list) {', ' var x = Math.floor(Math.random() * list.length);', ' return list[x];', @@ -391,16 +391,16 @@ Blockly.JavaScript['math_random_int'] = function(block) { Blockly.JavaScript.ORDER_COMMA) || '0'; var functionName = Blockly.JavaScript.provideFunction_( 'mathRandomInt', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(a, b) {', - ' if (a > b) {', - ' // Swap a and b to ensure a is smaller.', - ' var c = a;', - ' a = b;', - ' b = c;', - ' }', - ' return Math.floor(Math.random() * (b - a + 1) + a);', - '}']); + ' if (a > b) {', + ' // Swap a and b to ensure a is smaller.', + ' var c = a;', + ' a = b;', + ' b = c;', + ' }', + ' return Math.floor(Math.random() * (b - a + 1) + a);', + '}']); var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 86befb9ef..9ecfdc387 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -37,29 +37,29 @@ Blockly.JavaScript['text'] = function(block) { Blockly.JavaScript['text_join'] = function(block) { // Create a string made up of any number of elements of any type. - var code; - if (block.itemCount_ == 0) { - return ['\'\'', Blockly.JavaScript.ORDER_ATOMIC]; - } else if (block.itemCount_ == 1) { - var argument0 = Blockly.JavaScript.valueToCode(block, 'ADD0', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - code = 'String(' + argument0 + ')'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; - } else if (block.itemCount_ == 2) { - var argument0 = Blockly.JavaScript.valueToCode(block, 'ADD0', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - var argument1 = Blockly.JavaScript.valueToCode(block, 'ADD1', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - code = 'String(' + argument0 + ') + String(' + argument1 + ')'; - return [code, Blockly.JavaScript.ORDER_ADDITION]; - } else { - code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.JavaScript.valueToCode(block, 'ADD' + n, - Blockly.JavaScript.ORDER_COMMA) || '\'\''; - } - code = '[' + code.join(',') + '].join(\'\')'; - return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + switch (block.itemCount_) { + case 0: + return ['\'\'', Blockly.JavaScript.ORDER_ATOMIC]; + case 1: + var element = Blockly.JavaScript.valueToCode(block, 'ADD0', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var code = 'String(' + element + ')'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + case 2: + var element0 = Blockly.JavaScript.valueToCode(block, 'ADD0', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var element1 = Blockly.JavaScript.valueToCode(block, 'ADD1', + Blockly.JavaScript.ORDER_NONE) || '\'\''; + var code = 'String(' + element0 + ') + String(' + element1 + ')'; + return [code, Blockly.JavaScript.ORDER_ADDITION]; + default: + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.JavaScript.valueToCode(block, 'ADD' + i, + Blockly.JavaScript.ORDER_COMMA) || '\'\''; + } + var code = '[' + elements.join(',') + '].join(\'\')'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } }; @@ -67,45 +67,49 @@ Blockly.JavaScript['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', + var value = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; - return varName + ' = String(' + varName + ') + String(' + argument0 + ');\n'; + return varName + ' = String(' + varName + ') + String(' + value + ');\n'; }; Blockly.JavaScript['text_length'] = function(block) { // String or array length. - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\''; - return [argument0 + '.length', Blockly.JavaScript.ORDER_MEMBER]; + return [text + '.length', Blockly.JavaScript.ORDER_MEMBER]; }; Blockly.JavaScript['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - return ['!' + argument0 + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; + return ['!' + text + '.length', Blockly.JavaScript.ORDER_LOGICAL_NOT]; }; Blockly.JavaScript['text_indexOf'] = function(block) { // Search the text for a substring. var operator = block.getFieldValue('END') == 'FIRST' ? 'indexOf' : 'lastIndexOf'; - var argument0 = Blockly.JavaScript.valueToCode(block, 'FIND', + var substring = Blockly.JavaScript.valueToCode(block, 'FIND', Blockly.JavaScript.ORDER_NONE) || '\'\''; - var argument1 = Blockly.JavaScript.valueToCode(block, 'VALUE', + var text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.JavaScript.ORDER_ADDITION]; + var code = text + '.' + operator + '(' + substring + ')'; + // Adjust index if using one-based indices. + if (Blockly.JavaScript.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION]; + } + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.JavaScript.valueToCode(block, 'AT', - Blockly.JavaScript.ORDER_UNARY_NEGATION) || '1'; + var textOrder = (where == 'RANDOM') ? Blockly.JavaScript.ORDER_NONE : + Blockly.JavaScript.ORDER_MEMBER; var text = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + textOrder) || '\'\''; switch (where) { case 'FIRST': var code = text + '.charAt(0)'; @@ -114,70 +118,117 @@ Blockly.JavaScript['text_charAt'] = function(block) { var code = text + '.slice(-1)'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'FROM_START': - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } + var at = Blockly.JavaScript.getAdjusted(block, 'AT'); + // Adjust index if using one-based indices. var code = text + '.charAt(' + at + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'FROM_END': - var code = text + '.slice(-' + at + ').charAt(0)'; + var at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, true); + var code = text + '.slice(' + at + ').charAt(0)'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'RANDOM': var functionName = Blockly.JavaScript.provideFunction_( 'textRandomLetter', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(text) {', - ' var x = Math.floor(Math.random() * text.length);', - ' return text[x];', - '}']); - code = functionName + '(' + text + ')'; + ' var x = Math.floor(Math.random() * text.length);', + ' return text[x];', + '}']); + var code = functionName + '(' + text + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } throw 'Unhandled option (text_charAt).'; }; +/** + * Returns an expression calculating the index into a string. + * @private + * @param {string} stringName Name of the string, used to calculate length. + * @param {string} where The method of indexing, selected by dropdown in Blockly + * @param {string=} opt_at The optional offset when indexing from start/end. + * @return {string} Index expression. + */ +Blockly.JavaScript.text.getIndex_ = function(stringName, where, opt_at) { + if (where == 'FIRST') { + return '0'; + } else if (where == 'FROM_END') { + return stringName + '.length - 1 - ' + opt_at; + } else if (where == 'LAST') { + return stringName + '.length - 1'; + } else { + return opt_at; + } +}; + Blockly.JavaScript['text_getSubstring'] = function(block) { // Get substring. var text = Blockly.JavaScript.valueToCode(block, 'STRING', - Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.JavaScript.valueToCode(block, 'AT1', - Blockly.JavaScript.ORDER_NONE) || '1'; - var at2 = Blockly.JavaScript.valueToCode(block, 'AT2', - Blockly.JavaScript.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = text; + } else if (text.match(/^'?\w+'?$/) || + (where1 != 'FROM_END' && where1 != 'LAST' && + where2 != 'FROM_END' && where2 != 'LAST')) { + // If the text is a variable or literal or doesn't require a call for + // length, don't generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at1 = text + '.length - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false, + Blockly.JavaScript.ORDER_SUBTRACTION); + at2 = text + '.length - ' + at2; + break; + case 'LAST': + var at2 = text + '.length'; + break; + default: + throw 'Unhandled option (text_getSubstring).'; + } + code = text + '.slice(' + at1 + ', ' + at2 + ')'; } else { + var at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + var at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + var getIndex_ = Blockly.JavaScript.text.getIndex_; + var wherePascalCase = {'FIRST': 'First', 'LAST': 'Last', + 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'}; var functionName = Blockly.JavaScript.provideFunction_( - 'textGetSubstring', - [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(text, where1, at1, where2, at2) {', - ' function getAt(where, at) {', - ' if (where == \'FROM_START\') {', - ' at--;', - ' } else if (where == \'FROM_END\') {', - ' at = text.length - at;', - ' } else if (where == \'FIRST\') {', - ' at = 0;', - ' } else if (where == \'LAST\') {', - ' at = text.length - 1;', - ' } else {', - ' throw \'Unhandled option (text_getSubstring).\';', - ' }', - ' return at;', - ' }', - ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', - ' return text.slice(at1, at2);', + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(sequence' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') + + ') {', + ' var start = ' + getIndex_('sequence', where1, 'at1') + ';', + ' var end = ' + getIndex_('sequence', where2, 'at2') + ' + 1;', + ' return sequence.slice(start, end);', '}']); - var code = functionName + '(' + text + ', \'' + - where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; + var code = functionName + '(' + text + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') + + ')'; } return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; @@ -190,25 +241,24 @@ Blockly.JavaScript['text_changeCase'] = function(block) { 'TITLECASE': null }; var operator = OPERATORS[block.getFieldValue('CASE')]; - var code; + var textOrder = operator ? Blockly.JavaScript.ORDER_MEMBER : + Blockly.JavaScript.ORDER_NONE; + var text = Blockly.JavaScript.valueToCode(block, 'TEXT', + textOrder) || '\'\''; if (operator) { // Upper and lower case are functions built into JavaScript. - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', - Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - code = argument0 + operator; + var code = text + operator; } else { // Title case is not a native JavaScript function. Define one. var functionName = Blockly.JavaScript.provideFunction_( 'textToTitleCase', - [ 'function ' + - Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(str) {', - ' return str.replace(/\\S+/g,', - ' function(txt) {return txt[0].toUpperCase() + ' + - 'txt.substring(1).toLowerCase();});', - '}']); - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', - Blockly.JavaScript.ORDER_NONE) || '\'\''; - code = functionName + '(' + argument0 + ')'; + ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + + '(str) {', + ' return str.replace(/\\S+/g,', + ' function(txt) {return txt[0].toUpperCase() + ' + + 'txt.substring(1).toLowerCase();});', + '}']); + var code = functionName + '(' + text + ')'; } return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; @@ -221,16 +271,16 @@ Blockly.JavaScript['text_trim'] = function(block) { 'BOTH': '.trim()' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', + var text = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; - return [argument0 + operator, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + return [text + operator, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; Blockly.JavaScript['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.JavaScript.valueToCode(block, 'TEXT', + var msg = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; - return 'window.alert(' + argument0 + ');\n'; + return 'window.alert(' + msg + ');\n'; }; Blockly.JavaScript['text_prompt_ext'] = function(block) { diff --git a/generators/lua.js b/generators/lua.js index 60a6c0e14..3f7df6b1c 100644 --- a/generators/lua.js +++ b/generators/lua.js @@ -77,7 +77,7 @@ Blockly.Lua.ORDER_ATOMIC = 0; // literals // The next level was not explicit in documentation and inferred by Ellen. Blockly.Lua.ORDER_HIGH = 1; // Function calls, tables[] Blockly.Lua.ORDER_EXPONENTIATION = 2; // ^ -Blockly.Lua.ORDER_UNARY = 3; // not # - () +Blockly.Lua.ORDER_UNARY = 3; // not # - ~ Blockly.Lua.ORDER_MULTIPLICATIVE = 4; // * / % Blockly.Lua.ORDER_ADDITIVE = 5; // + - Blockly.Lua.ORDER_CONCATENATION = 6; // .. @@ -86,6 +86,12 @@ Blockly.Lua.ORDER_AND = 8; // and Blockly.Lua.ORDER_OR = 9; // or Blockly.Lua.ORDER_NONE = 99; +/** + * Lua is not supporting zero-indexing since the language itself is one-indexed, + * so there is not flag for ONE_BASED_INDEXING to indicate which indexing is + * used for lists and text. + */ + /** * Initialise the database of variable names. * @param {!Blockly.Workspace} workspace Workspace to generate code from. @@ -170,9 +176,9 @@ Blockly.Lua.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { comment = Blockly.Lua.allNestedComments(childBlock); if (comment) { diff --git a/generators/lua/lists.js b/generators/lua/lists.js index 8c89a1bd3..5b7711dbb 100644 --- a/generators/lua/lists.js +++ b/generators/lua/lists.js @@ -31,18 +31,17 @@ goog.require('Blockly.Lua'); Blockly.Lua['lists_create_empty'] = function(block) { // Create an empty list. - // List literals must be parenthesized before indexing into. - return ['({})', Blockly.Lua.ORDER_ATOMIC]; + return ['{}', Blockly.Lua.ORDER_ATOMIC]; }; Blockly.Lua['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Lua.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Lua.valueToCode(block, 'ADD' + i, Blockly.Lua.ORDER_NONE) || 'None'; } - code = '({' + code.join(', ') + '})'; + var code = '{' + elements.join(', ') + '}'; return [code, Blockly.Lua.ORDER_ATOMIC]; }; @@ -57,38 +56,37 @@ Blockly.Lua['lists_repeat'] = function(block) { ' end', ' return t', 'end']); - var argument0 = Blockly.Lua.valueToCode(block, 'ITEM', + var element = Blockly.Lua.valueToCode(block, 'ITEM', Blockly.Lua.ORDER_NONE) || 'None'; - var argument1 = Blockly.Lua.valueToCode(block, 'NUM', + var repeatCount = Blockly.Lua.valueToCode(block, 'NUM', Blockly.Lua.ORDER_NONE) || '0'; - var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + var code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; Blockly.Lua['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '({})'; - return ['#' + argument0, Blockly.Lua.ORDER_HIGH]; + var list = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '{}'; + return ['#' + list, Blockly.Lua.ORDER_UNARY]; }; Blockly.Lua['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '({})'; - var code = '#' + argument0 + ' == 0'; + var list = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '{}'; + var code = '#' + list + ' == 0'; return [code, Blockly.Lua.ORDER_RELATIONAL]; }; Blockly.Lua['lists_indexOf'] = function(block) { // Find an item in the list. - var argument0 = Blockly.Lua.valueToCode(block, 'FIND', + var item = Blockly.Lua.valueToCode(block, 'FIND', Blockly.Lua.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_NONE) || '({})'; - var functionName; + var list = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_NONE) || '{}'; if (block.getFieldValue('END') == 'FIRST') { - functionName = Blockly.Lua.provideFunction_( + var functionName = Blockly.Lua.provideFunction_( 'first_index', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, elem)', ' for k, v in ipairs(t) do', @@ -99,7 +97,7 @@ Blockly.Lua['lists_indexOf'] = function(block) { ' return 0', 'end']); } else { - functionName = Blockly.Lua.provideFunction_( + var functionName = Blockly.Lua.provideFunction_( 'last_index', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, elem)', ' for i = #t, 1, -1 do', @@ -110,64 +108,40 @@ Blockly.Lua['lists_indexOf'] = function(block) { ' return 0', 'end']); } - var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + var code = functionName + '(' + list + ', ' + item + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; /** * Returns an expression calculating the index into a list. * @private - * @param {string} listname Name of the list, used to calculate length. + * @param {string} listName Name of the list, used to calculate length. * @param {string} where The method of indexing, selected by dropdown in Blockly * @param {string=} opt_at The optional offset when indexing from start/end. * @return {string} Index expression. */ -Blockly.Lua.lists.getIndex_ = function(listname, where, opt_at) { +Blockly.Lua.lists.getIndex_ = function(listName, where, opt_at) { if (where == 'FIRST') { return '1'; } else if (where == 'FROM_END') { - return '#' + listname + ' + 1 - ' + opt_at; + return '#' + listName + ' + 1 - ' + opt_at; } else if (where == 'LAST') { - return '#' + listname; + return '#' + listName; } else if (where == 'RANDOM') { - return 'math.random(#' + listname + ')'; + return 'math.random(#' + listName + ')'; } else { return opt_at; } }; -/** - * Counter for generating unique symbols. - * @private - * @type {number} - */ -Blockly.Lua.lists.gensym_counter_ = 0; - -/** - * Generate a unique symbol. - * @private - * @return {string} unique symbol, eg 'G123' - */ -Blockly.Lua.lists.gensym_ = function() { - return 'G' + Blockly.Lua.lists.gensym_counter_++; -}; - Blockly.Lua['lists_getIndex'] = function(block) { // Get element at index. // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Lua.valueToCode(block, 'AT', - Blockly.Lua.ORDER_ADDITIVE) || '1'; - if (mode == 'GET') { - var order = Blockly.Lua.ORDER_HIGH; - } else { - // List will be an argument in a function call. - var order = Blockly.Lua.ORDER_NONE; - } - var list = Blockly.Lua.valueToCode(block, 'VALUE', order) || '({})'; + var list = Blockly.Lua.valueToCode(block, 'VALUE', Blockly.Lua.ORDER_HIGH) || + '{}'; var getIndex_ = Blockly.Lua.lists.getIndex_; - var gensym_ = Blockly.Lua.lists.gensym_; // If `list` would be evaluated more than once (which is the case for LAST, // FROM_END, and RANDOM) and is non-trivial, make sure to access it only once. @@ -176,46 +150,59 @@ Blockly.Lua['lists_getIndex'] = function(block) { // `list` is an expression, so we may not evaluate it more than once. if (mode == 'REMOVE') { // We can use multiple statements. + var atOrder = (where == 'FROM_END') ? Blockly.Lua.ORDER_ADDITIVE : + Blockly.Lua.ORDER_NONE; + var at = Blockly.Lua.valueToCode(block, 'AT', atOrder) || '1'; var listVar = Blockly.Lua.variableDB_.getDistinctName( 'tmp_list', Blockly.Variables.NAME_TYPE); + at = getIndex_(listVar, where, at); var code = listVar + ' = ' + list + '\n' + - 'table.remove(' + listVar + ', ' + getIndex_(listVar, where, at) + - ')\n'; + 'table.remove(' + listVar + ', ' + at + ')\n'; return code; } else { // We need to create a procedure to avoid reevaluating values. + var at = Blockly.Lua.valueToCode(block, 'AT', Blockly.Lua.ORDER_NONE) || + '1'; if (mode == 'GET') { - // Note that getIndex_() ignores `at` when `where` == 'LAST' or - // 'RANDOM', so we only need one procedure for each of those 'where' - // values. The value for 'FROM_END' depends on `at`, so we will - // generate a unique procedure (name) each time. var functionName = Blockly.Lua.provideFunction_( - 'list_get_' + where.toLowerCase() + - (where == 'FROM_END' ? '_' + gensym_() : ''), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' return t[' + getIndex_('t', where, at) + ']', + 'list_get_' + where.toLowerCase(), + ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where == 'FROM_END' || where == 'FROM_START') ? + ', at)' : ')'), + ' return t[' + getIndex_('t', where, 'at') + ']', 'end']); - } else { // mode == 'GET_REMOVE' - // We need to create a procedure. + } else { // mode == 'GET_REMOVE' var functionName = Blockly.Lua.provideFunction_( - 'list_remove_' + where.toLowerCase() + - (where == 'FROM_END' ? '_' + gensym_() : ''), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' return table.remove(t, ' + getIndex_('t', where, at) + ')', + 'list_remove_' + where.toLowerCase(), + ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where == 'FROM_END' || where == 'FROM_START') ? + ', at)' : ')'), + ' return table.remove(t, ' + getIndex_('t', where, 'at') + ')', 'end']); } - var code = functionName + '(' + list + ')'; + var code = functionName + '(' + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where == 'FROM_END' || where == 'FROM_START') ? ', ' + at : '') + + ')'; return [code, Blockly.Lua.ORDER_HIGH]; } } else { // Either `list` is a simple variable, or we only need to refer to `list` // once. + var atOrder = (mode == 'GET' && where == 'FROM_END') ? + Blockly.Lua.ORDER_ADDITIVE : Blockly.Lua.ORDER_NONE; + var at = Blockly.Lua.valueToCode(block, 'AT', atOrder) || '1'; + at = getIndex_(list, where, at); if (mode == 'GET') { - var code = list + '[' + getIndex_(list, where, at) + ']'; + var code = list + '[' + at + ']'; return [code, Blockly.Lua.ORDER_HIGH]; } else { - var code = 'table.remove(' + list + ', ' + getIndex_(list, where, at) + - ')'; + var code = 'table.remove(' + list + ', ' + at + ')'; if (mode == 'GET_REMOVE') { return [code, Blockly.Lua.ORDER_HIGH]; } else { // `mode` == 'REMOVE' @@ -229,7 +216,7 @@ Blockly.Lua['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. var list = Blockly.Lua.valueToCode(block, 'LIST', - Blockly.Lua.ORDER_HIGH) || '({})'; + Blockly.Lua.ORDER_HIGH) || '{}'; var mode = block.getFieldValue('MODE') || 'SET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; var at = Blockly.Lua.valueToCode(block, 'AT', @@ -238,97 +225,71 @@ Blockly.Lua['lists_setIndex'] = function(block) { Blockly.Lua.ORDER_NONE) || 'None'; var getIndex_ = Blockly.Lua.lists.getIndex_; + var code = ''; // If `list` would be evaluated more than once (which is the case for LAST, // FROM_END, and RANDOM) and is non-trivial, make sure to access it only once. if ((where == 'LAST' || where == 'FROM_END' || where == 'RANDOM') && !list.match(/^\w+$/)) { // `list` is an expression, so we may not evaluate it more than once. - if (where == 'RANDOM' || where == 'LAST') { - // In these cases, `at` is implicit. getIndex_() ignores its value. - if (mode == 'SET') { - var functionName = Blockly.Lua.provideFunction_( - 'list_set_' + where.toLowerCase(), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, val)', - ' t[' + getIndex_('t', where, at) + '] = val', - 'end']); - } else { // `mode` == 'INSERT' - var functionName = Blockly.Lua.provideFunction_( - 'list_insert_' + where.toLowerCase(), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, val)', - ' table.insert(t, ' + - // LAST is a special case, because we want to insert - // *after* not *before*, the existing last element. - getIndex_('t', where, at) + (where == 'LAST' ? ' + 1' : '') + - ', val)', - 'end']); - } - var code = functionName + '(' + list + ', ' + value + ')\n'; - return code; - } else { // `where` = 'FROM_END' - if (mode == 'SET') { - var functionName = Blockly.Lua.provideFunction_( - 'list_set_from_end', - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(t, index, val)', - ' t[#t + 1 - index] = val', - 'end']); - } else { // `mode` == 'INSERT' - var functionName = Blockly.Lua.provideFunction_( - 'list_insert_from_end', - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(t, index, val)', - ' table.insert(t, #t + 1 - index, val)', - 'end']); - } - var code = functionName + '(' + list + ', ' + at + ', ' + value + ')\n'; - return code; - } - } else { - // It's okay to have multiple references to the list. - if (mode == 'SET') { - var code = list + '[' + getIndex_(list, where, at) + '] = ' + value; - } else { // `mode` == 'INSERT' - // LAST is a special case, because we want to insert - // *after* not *before*, the existing last element. - var code = 'table.insert(' + list + ', ' + - (getIndex_(list, where, at) + (where == 'LAST' ? ' + 1' : '')) + - ', ' + value + ')'; - } - return code + '\n'; + // We can use multiple statements. + var listVar = Blockly.Lua.variableDB_.getDistinctName( + 'tmp_list', Blockly.Variables.NAME_TYPE); + code = listVar + ' = ' + list + '\n'; + list = listVar; } + if (mode == 'SET') { + code += list + '[' + getIndex_(list, where, at) + '] = ' + value; + } else { // `mode` == 'INSERT' + // LAST is a special case, because we want to insert + // *after* not *before*, the existing last element. + code += 'table.insert(' + list + ', ' + + (getIndex_(list, where, at) + (where == 'LAST' ? ' + 1' : '')) + + ', ' + value + ')'; + } + return code + '\n'; }; Blockly.Lua['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.Lua.valueToCode(block, 'LIST', - Blockly.Lua.ORDER_HIGH) || '({})'; + Blockly.Lua.ORDER_NONE) || '{}'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); var at1 = Blockly.Lua.valueToCode(block, 'AT1', - Blockly.Lua.ORDER_ADDITIVE) || '1'; + Blockly.Lua.ORDER_NONE) || '1'; var at2 = Blockly.Lua.valueToCode(block, 'AT2', - Blockly.Lua.ORDER_ADDITIVE) || '1'; + Blockly.Lua.ORDER_NONE) || '1'; var getIndex_ = Blockly.Lua.lists.getIndex_; var functionName = Blockly.Lua.provideFunction_( - 'list_sublist_' + Blockly.Lua.lists.gensym_(), - ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(source)', + 'list_sublist_' + where1.toLowerCase() + '_' + where2.toLowerCase(), + ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(source' + + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', at1' : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', at2' : '') + + ')', ' local t = {}', - ' local start = ' + getIndex_('source', where1, at1), - ' local finish = ' + getIndex_('source', where2, at2), + ' local start = ' + getIndex_('source', where1, 'at1'), + ' local finish = ' + getIndex_('source', where2, 'at2'), ' for i = start, finish do', ' table.insert(t, source[i])', ' end', ' return t', 'end']); - var code = functionName + '(' + list + ')'; + var code = functionName + '(' + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + ((where1 == 'FROM_END' || where1 == 'FROM_START') ? ', ' + at1 : '') + + ((where2 == 'FROM_END' || where2 == 'FROM_START') ? ', ' + at2 : '') + + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; Blockly.Lua['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = Blockly.Lua.valueToCode( - block, 'LIST', Blockly.Lua.ORDER_HIGH) || '({})'; + var list = Blockly.Lua.valueToCode( + block, 'LIST', Blockly.Lua.ORDER_NONE) || '{}'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); @@ -357,26 +318,26 @@ Blockly.Lua['lists_sort'] = function(block) { 'end']); var code = functionName + - '(' + listCode + ',"' + type + '", ' + direction + ')'; + '(' + list + ',"' + type + '", ' + direction + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; Blockly.Lua['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. - var value_input = Blockly.Lua.valueToCode(block, 'INPUT', + var input = Blockly.Lua.valueToCode(block, 'INPUT', Blockly.Lua.ORDER_NONE); - var value_delim = Blockly.Lua.valueToCode(block, 'DELIM', + var delimiter = Blockly.Lua.valueToCode(block, 'DELIM', Blockly.Lua.ORDER_NONE) || '\'\''; var mode = block.getFieldValue('MODE'); var functionName; if (mode == 'SPLIT') { - if (!value_input) { - value_input = '\'\''; + if (!input) { + input = '\'\''; } functionName = Blockly.Lua.provideFunction_( 'list_string_split', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(input, delim)', + '(input, delim)', ' local t = {}', ' local pos = 1', ' while true do', @@ -392,13 +353,13 @@ Blockly.Lua['lists_split'] = function(block) { ' return t', 'end']); } else if (mode == 'JOIN') { - if (!value_input) { - value_input = '({})'; + if (!input) { + input = '{}'; } functionName = 'table.concat'; } else { throw 'Unknown mode: ' + mode; } - var code = functionName + '(' + value_input + ', ' + value_delim + ')'; + var code = functionName + '(' + input + ', ' + delimiter + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; diff --git a/generators/lua/procedures.js b/generators/lua/procedures.js index 9bdcc416b..b6d6cdda1 100644 --- a/generators/lua/procedures.js +++ b/generators/lua/procedures.js @@ -51,8 +51,8 @@ Blockly.Lua['procedures_defreturn'] = function(block) { branch = ''; } var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Lua.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Lua.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = 'function ' + funcName + '(' + args.join(', ') + ')\n' + @@ -73,8 +73,8 @@ Blockly.Lua['procedures_callreturn'] = function(block) { var funcName = Blockly.Lua.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Lua.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Lua.valueToCode(block, 'ARG' + i, Blockly.Lua.ORDER_NONE) || 'nil'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -86,8 +86,8 @@ Blockly.Lua['procedures_callnoreturn'] = function(block) { var funcName = Blockly.Lua.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Lua.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Lua.valueToCode(block, 'ARG' + i, Blockly.Lua.ORDER_NONE) || 'nil'; } var code = funcName + '(' + args.join(', ') + ')\n'; diff --git a/generators/lua/text.js b/generators/lua/text.js index 8a6a02702..74efba4a2 100644 --- a/generators/lua/text.js +++ b/generators/lua/text.js @@ -40,24 +40,24 @@ Blockly.Lua['text_join'] = function(block) { if (block.itemCount_ == 0) { return ['\'\'', Blockly.Lua.ORDER_ATOMIC]; } else if (block.itemCount_ == 1) { - var argument0 = Blockly.Lua.valueToCode(block, 'ADD0', + var element = Blockly.Lua.valueToCode(block, 'ADD0', Blockly.Lua.ORDER_NONE) || '\'\''; - var code = argument0; + var code = 'tostring(' + element + ')'; return [code, Blockly.Lua.ORDER_HIGH]; } else if (block.itemCount_ == 2) { - var argument0 = Blockly.Lua.valueToCode(block, 'ADD0', - Blockly.Lua.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Lua.valueToCode(block, 'ADD1', - Blockly.Lua.ORDER_NONE) || '\'\''; - var code = argument0 + ' .. ' + argument1; - return [code, Blockly.Lua.ORDER_UNARY]; + var element0 = Blockly.Lua.valueToCode(block, 'ADD0', + Blockly.Lua.ORDER_CONCATENATION) || '\'\''; + var element1 = Blockly.Lua.valueToCode(block, 'ADD1', + Blockly.Lua.ORDER_CONCATENATION) || '\'\''; + var code = element0 + ' .. ' + element1; + return [code, Blockly.Lua.ORDER_CONCATENATION]; } else { - var code = []; - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Lua.valueToCode(block, 'ADD' + n, + var elements = []; + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Lua.valueToCode(block, 'ADD' + i, Blockly.Lua.ORDER_NONE) || '\'\''; } - code = 'table.concat({' + code.join(', ') + '})'; + var code = 'table.concat({' + elements.join(', ') + '})'; return [code, Blockly.Lua.ORDER_HIGH]; } }; @@ -66,32 +66,30 @@ Blockly.Lua['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.Lua.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.Lua.valueToCode(block, 'TEXT', - Blockly.Lua.ORDER_NONE) || '\'\''; - return varName + ' = ' + varName + ' .. ' + argument0 + '\n'; + var value = Blockly.Lua.valueToCode(block, 'TEXT', + Blockly.Lua.ORDER_CONCATENATION) || '\'\''; + return varName + ' = ' + varName + ' .. ' + value + '\n'; }; Blockly.Lua['text_length'] = function(block) { // String or array length. - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '\'\''; - return ['#' + argument0, Blockly.Lua.ORDER_HIGH]; + var text = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '\'\''; + return ['#' + text, Blockly.Lua.ORDER_UNARY]; }; Blockly.Lua['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Lua.valueToCode(block, 'VALUE', - Blockly.Lua.ORDER_HIGH) || '\'\''; - return ['#' + argument0 + ' == 0', Blockly.Lua.ORDER_RELATIONAL]; + var text = Blockly.Lua.valueToCode(block, 'VALUE', + Blockly.Lua.ORDER_UNARY) || '\'\''; + return ['#' + text + ' == 0', Blockly.Lua.ORDER_RELATIONAL]; }; Blockly.Lua['text_indexOf'] = function(block) { // Search the text for a substring. - var operator = block.getFieldValue('END') == 'FIRST' ? - 'indexOf' : 'lastIndexOf'; - var substr = Blockly.Lua.valueToCode(block, 'FIND', + var substring = Blockly.Lua.valueToCode(block, 'FIND', Blockly.Lua.ORDER_NONE) || '\'\''; - var str = Blockly.Lua.valueToCode(block, 'VALUE', + var text = Blockly.Lua.valueToCode(block, 'VALUE', Blockly.Lua.ORDER_NONE) || '\'\''; if (block.getFieldValue('END') == 'FIRST') { var functionName = Blockly.Lua.provideFunction_( @@ -118,7 +116,7 @@ Blockly.Lua['text_indexOf'] = function(block) { ' return 0', 'end']); } - var code = functionName + '(' + str + ', ' + substr + ')'; + var code = functionName + '(' + text + ', ' + substring + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; @@ -126,8 +124,9 @@ Blockly.Lua['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Lua.valueToCode(block, 'AT', - Blockly.Lua.ORDER_UNARY) || '1'; + var atOrder = (where == 'FROM_END') ? Blockly.Lua.ORDER_UNARY : + Blockly.Lua.ORDER_NONE; + var at = Blockly.Lua.valueToCode(block, 'AT', atOrder) || '1'; var text = Blockly.Lua.valueToCode(block, 'VALUE', Blockly.Lua.ORDER_NONE) || '\'\''; var code; @@ -176,8 +175,9 @@ Blockly.Lua['text_getSubstring'] = function(block) { // Get start index. var where1 = block.getFieldValue('WHERE1'); - var at1 = Blockly.Lua.valueToCode(block, 'AT1', - Blockly.Lua.ORDER_UNARY) || '1'; + var at1Order = (where1 == 'FROM_END') ? Blockly.Lua.ORDER_UNARY : + Blockly.Lua.ORDER_NONE; + var at1 = Blockly.Lua.valueToCode(block, 'AT1', at1Order) || '1'; if (where1 == 'FIRST') { var start = 1; } else if (where1 == 'FROM_START') { @@ -190,8 +190,9 @@ Blockly.Lua['text_getSubstring'] = function(block) { // Get end index. var where2 = block.getFieldValue('WHERE2'); - var at2 = Blockly.Lua.valueToCode(block, 'AT2', - Blockly.Lua.ORDER_UNARY) || '1'; + var at2Order = (where2 == 'FROM_END') ? Blockly.Lua.ORDER_UNARY : + Blockly.Lua.ORDER_NONE; + var at2 = Blockly.Lua.valueToCode(block, 'AT2', at2Order) || '1'; if (where2 == 'LAST') { var end = -1; } else if (where2 == 'FROM_START') { @@ -208,7 +209,7 @@ Blockly.Lua['text_getSubstring'] = function(block) { Blockly.Lua['text_changeCase'] = function(block) { // Change capitalization. var operator = block.getFieldValue('CASE'); - var argument0 = Blockly.Lua.valueToCode(block, 'TEXT', + var text = Blockly.Lua.valueToCode(block, 'TEXT', Blockly.Lua.ORDER_NONE) || '\'\''; if (operator == 'UPPERCASE') { var functionName = 'string.upper'; @@ -238,7 +239,7 @@ Blockly.Lua['text_changeCase'] = function(block) { ' return table.concat(buf)', 'end']); } - var code = functionName + '(' + argument0 + ')'; + var code = functionName + '(' + text + ')'; return [code, Blockly.Lua.ORDER_HIGH]; }; @@ -258,9 +259,9 @@ Blockly.Lua['text_trim'] = function(block) { Blockly.Lua['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.Lua.valueToCode(block, 'TEXT', + var msg = Blockly.Lua.valueToCode(block, 'TEXT', Blockly.Lua.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ')\n'; + return 'print(' + msg + ')\n'; }; Blockly.Lua['text_prompt_ext'] = function(block) { diff --git a/generators/php.js b/generators/php.js index d935cda1b..02e89e809 100644 --- a/generators/php.js +++ b/generators/php.js @@ -44,44 +44,98 @@ Blockly.PHP = new Blockly.Generator('PHP'); */ Blockly.PHP.addReservedWords( // http://php.net/manual/en/reserved.keywords.php - '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,clone,const,continue,declare,default,die,do,echo,else,elseif,empty,enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,extends,final,for,foreach,function,global,goto,if,implements,include,include_once,instanceof,insteadof,interface,isset,list,namespace,new,or,print,private,protected,public,require,require_once,return,static,switch,throw,trait,try,unset,use,var,while,xor,' + + '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,' + + 'clone,const,continue,declare,default,die,do,echo,else,elseif,empty,' + + 'enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,extends,' + + 'final,for,foreach,function,global,goto,if,implements,include,' + + 'include_once,instanceof,insteadof,interface,isset,list,namespace,new,or,' + + 'print,private,protected,public,require,require_once,return,static,' + + 'switch,throw,trait,try,unset,use,var,while,xor,' + // http://php.net/manual/en/reserved.constants.php - 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,PHP_SYSCONFDIR,PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,PHP_CONFIG_FILE_SCAN_DIR,PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,E_NOTICE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING,E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE,E_DEPRECATED,E_USER_DEPRECATED,E_ALL,E_STRICT,__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,__FILE__,__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__'); + 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,' + + 'PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,' + + 'PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,' + + 'PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,' + + 'PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,PHP_SYSCONFDIR,' + + 'PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,PHP_CONFIG_FILE_SCAN_DIR,' + + 'PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,E_NOTICE,E_CORE_ERROR,' + + 'E_CORE_WARNING,E_COMPILE_ERROR,E_COMPILE_WARNING,E_USER_ERROR,' + + 'E_USER_WARNING,E_USER_NOTICE,E_DEPRECATED,E_USER_DEPRECATED,E_ALL,' + + 'E_STRICT,__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,' + + '__FILE__,__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__' +); /** * Order of operation ENUMs. * http://php.net/manual/en/language.operators.precedence.php */ -Blockly.PHP.ORDER_ATOMIC = 0; // 0 "" ... -Blockly.PHP.ORDER_CLONE = 1; // clone -Blockly.PHP.ORDER_NEW = 1; // new -Blockly.PHP.ORDER_MEMBER = 2; // () -Blockly.PHP.ORDER_FUNCTION_CALL = 2; // () -Blockly.PHP.ORDER_INCREMENT = 3; // ++ -Blockly.PHP.ORDER_DECREMENT = 3; // -- -Blockly.PHP.ORDER_LOGICAL_NOT = 4; // ! -Blockly.PHP.ORDER_BITWISE_NOT = 4; // ~ -Blockly.PHP.ORDER_UNARY_PLUS = 4; // + -Blockly.PHP.ORDER_UNARY_NEGATION = 4; // - -Blockly.PHP.ORDER_MULTIPLICATION = 5; // * -Blockly.PHP.ORDER_DIVISION = 5; // / -Blockly.PHP.ORDER_MODULUS = 5; // % -Blockly.PHP.ORDER_ADDITION = 6; // + -Blockly.PHP.ORDER_SUBTRACTION = 6; // - -Blockly.PHP.ORDER_BITWISE_SHIFT = 7; // << >> >>> -Blockly.PHP.ORDER_RELATIONAL = 8; // < <= > >= -Blockly.PHP.ORDER_IN = 8; // in -Blockly.PHP.ORDER_INSTANCEOF = 8; // instanceof -Blockly.PHP.ORDER_EQUALITY = 9; // == != === !== -Blockly.PHP.ORDER_BITWISE_AND = 10; // & -Blockly.PHP.ORDER_BITWISE_XOR = 11; // ^ -Blockly.PHP.ORDER_BITWISE_OR = 12; // | -Blockly.PHP.ORDER_CONDITIONAL = 13; // ?: -Blockly.PHP.ORDER_ASSIGNMENT = 14; // = += -= *= /= %= <<= >>= ... -Blockly.PHP.ORDER_LOGICAL_AND = 15; // && -Blockly.PHP.ORDER_LOGICAL_OR = 16; // || -Blockly.PHP.ORDER_COMMA = 17; // , -Blockly.PHP.ORDER_NONE = 99; // (...) +Blockly.PHP.ORDER_ATOMIC = 0; // 0 "" ... +Blockly.PHP.ORDER_CLONE = 1; // clone +Blockly.PHP.ORDER_NEW = 1; // new +Blockly.PHP.ORDER_MEMBER = 2.1; // [] +Blockly.PHP.ORDER_FUNCTION_CALL = 2.2; // () +Blockly.PHP.ORDER_POWER = 3; // ** +Blockly.PHP.ORDER_INCREMENT = 4; // ++ +Blockly.PHP.ORDER_DECREMENT = 4; // -- +Blockly.PHP.ORDER_BITWISE_NOT = 4; // ~ +Blockly.PHP.ORDER_CAST = 4; // (int) (float) (string) (array) ... +Blockly.PHP.ORDER_SUPPRESS_ERROR = 4; // @ +Blockly.PHP.ORDER_INSTANCEOF = 5; // instanceof +Blockly.PHP.ORDER_LOGICAL_NOT = 6; // ! +Blockly.PHP.ORDER_UNARY_PLUS = 7.1; // + +Blockly.PHP.ORDER_UNARY_NEGATION = 7.2; // - +Blockly.PHP.ORDER_MULTIPLICATION = 8.1; // * +Blockly.PHP.ORDER_DIVISION = 8.2; // / +Blockly.PHP.ORDER_MODULUS = 8.3; // % +Blockly.PHP.ORDER_ADDITION = 9.1; // + +Blockly.PHP.ORDER_SUBTRACTION = 9.2; // - +Blockly.PHP.ORDER_STRING_CONCAT = 9.3; // . +Blockly.PHP.ORDER_BITWISE_SHIFT = 10; // << >> +Blockly.PHP.ORDER_RELATIONAL = 11; // < <= > >= +Blockly.PHP.ORDER_EQUALITY = 12; // == != === !== <> <=> +Blockly.PHP.ORDER_REFERENCE = 13; // & +Blockly.PHP.ORDER_BITWISE_AND = 13; // & +Blockly.PHP.ORDER_BITWISE_XOR = 14; // ^ +Blockly.PHP.ORDER_BITWISE_OR = 15; // | +Blockly.PHP.ORDER_LOGICAL_AND = 16; // && +Blockly.PHP.ORDER_LOGICAL_OR = 17; // || +Blockly.PHP.ORDER_IF_NULL = 18; // ?? +Blockly.PHP.ORDER_CONDITIONAL = 19; // ?: +Blockly.PHP.ORDER_ASSIGNMENT = 20; // = += -= *= /= %= <<= >>= ... +Blockly.PHP.ORDER_LOGICAL_AND_WEAK = 21; // and +Blockly.PHP.ORDER_LOGICAL_XOR = 22; // xor +Blockly.PHP.ORDER_LOGICAL_OR_WEAK = 23; // or +Blockly.PHP.ORDER_COMMA = 24; // , +Blockly.PHP.ORDER_NONE = 99; // (...) + +/** + * List of outer-inner pairings that do NOT require parentheses. + * @type {!Array.>} + */ +Blockly.PHP.ORDER_OVERRIDES = [ + // (foo()).bar() -> foo().bar() + // (foo())[0] -> foo()[0] + [Blockly.PHP.ORDER_MEMBER, Blockly.PHP.ORDER_FUNCTION_CALL], + // (foo[0])[1] -> foo[0][1] + // (foo.bar).baz -> foo.bar.baz + [Blockly.PHP.ORDER_MEMBER, Blockly.PHP.ORDER_MEMBER], + // !(!foo) -> !!foo + [Blockly.PHP.ORDER_LOGICAL_NOT, Blockly.PHP.ORDER_LOGICAL_NOT], + // a * (b * c) -> a * b * c + [Blockly.PHP.ORDER_MULTIPLICATION, Blockly.PHP.ORDER_MULTIPLICATION], + // a + (b + c) -> a + b + c + [Blockly.PHP.ORDER_ADDITION, Blockly.PHP.ORDER_ADDITION], + // a && (b && c) -> a && b && c + [Blockly.PHP.ORDER_LOGICAL_AND, Blockly.PHP.ORDER_LOGICAL_AND], + // a || (b || c) -> a || b || c + [Blockly.PHP.ORDER_LOGICAL_OR, Blockly.PHP.ORDER_LOGICAL_OR] +]; + +/** + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. + */ +Blockly.PHP.ONE_BASED_INDEXING = true; /** * Initialise the database of variable names. @@ -173,9 +227,9 @@ Blockly.PHP.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.PHP.allNestedComments(childBlock); if (comment) { @@ -189,3 +243,66 @@ Blockly.PHP.scrub_ = function(block, code) { var nextCode = Blockly.PHP.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value while taking into account indexing. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @param {number=} opt_order The highest order acting on this value. + * @return {string|number} + */ +Blockly.PHP.getAdjusted = function(block, atId, opt_delta, opt_negate, + opt_order) { + var delta = opt_delta || 0; + var order = opt_order || Blockly.PHP.ORDER_NONE; + if (Blockly.PHP.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.PHP.ONE_BASED_INDEXING ? '1' : '0'; + if (delta > 0) { + var at = Blockly.PHP.valueToCode(block, atId, + Blockly.PHP.ORDER_ADDITION) || defaultAtIndex; + } else if (delta < 0) { + var at = Blockly.PHP.valueToCode(block, atId, + Blockly.PHP.ORDER_SUBTRACTION) || defaultAtIndex; + } else if (opt_negate) { + var at = Blockly.PHP.valueToCode(block, atId, + Blockly.PHP.ORDER_UNARY_NEGATION) || defaultAtIndex; + } else { + var at = Blockly.PHP.valueToCode(block, atId, order) || + defaultAtIndex; + } + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseFloat(at) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = at + ' + ' + delta; + var innerOrder = Blockly.PHP.ORDER_ADDITION; + } else if (delta < 0) { + at = at + ' - ' + -delta; + var innerOrder = Blockly.PHP.ORDER_SUBTRACTION; + } + if (opt_negate) { + if (delta) { + at = '-(' + at + ')'; + } else { + at = '-' + at; + } + var innerOrder = Blockly.PHP.ORDER_UNARY_NEGATION; + } + innerOrder = Math.floor(innerOrder); + order = Math.floor(order); + if (innerOrder && order >= innerOrder) { + at = '(' + at + ')'; + } + } + return at; +}; diff --git a/generators/php/colour.js b/generators/php/colour.js index bc1789c35..e73c17a95 100644 --- a/generators/php/colour.js +++ b/generators/php/colour.js @@ -39,10 +39,10 @@ Blockly.PHP['colour_random'] = function(block) { // Generate a random colour. var functionName = Blockly.PHP.provideFunction_( 'colour_random', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' return \'#\' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), ' + - '6, \'0\', STR_PAD_LEFT);', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', + ' return \'#\' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), ' + + '6, \'0\', STR_PAD_LEFT);', + '}']); var code = functionName + '()'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -57,17 +57,17 @@ Blockly.PHP['colour_rgb'] = function(block) { Blockly.PHP.ORDER_COMMA) || 0; var functionName = Blockly.PHP.provideFunction_( 'colour_rgb', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($r, $g, $b) {', - ' $r = round(max(min($r, 100), 0) * 2.55);', - ' $g = round(max(min($g, 100), 0) * 2.55);', - ' $b = round(max(min($b, 100), 0) * 2.55);', - ' $hex = "#";', - ' $hex .= str_pad(dechex($r), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($g), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($b), 2, "0", STR_PAD_LEFT);', - ' return $hex;', - '}']); + ' $r = round(max(min($r, 100), 0) * 2.55);', + ' $g = round(max(min($g, 100), 0) * 2.55);', + ' $b = round(max(min($b, 100), 0) * 2.55);', + ' $hex = \'#\';', + ' $hex .= str_pad(dechex($r), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($g), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($b), 2, \'0\', STR_PAD_LEFT);', + ' return $hex;', + '}']); var code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -82,24 +82,24 @@ Blockly.PHP['colour_blend'] = function(block) { Blockly.PHP.ORDER_COMMA) || 0.5; var functionName = Blockly.PHP.provideFunction_( 'colour_blend', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($c1, $c2, $ratio) {', - ' $ratio = max(min($ratio, 1), 0);', - ' $r1 = hexdec(substr($c1, 1, 2));', - ' $g1 = hexdec(substr($c1, 3, 2));', - ' $b1 = hexdec(substr($c1, 5, 2));', - ' $r2 = hexdec(substr($c2, 1, 2));', - ' $g2 = hexdec(substr($c2, 3, 2));', - ' $b2 = hexdec(substr($c2, 5, 2));', - ' $r = round($r1 * (1 - $ratio) + $r2 * $ratio);', - ' $g = round($g1 * (1 - $ratio) + $g2 * $ratio);', - ' $b = round($b1 * (1 - $ratio) + $b2 * $ratio);', - ' $hex = "#";', - ' $hex .= str_pad(dechex($r), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($g), 2, "0", STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($b), 2, "0", STR_PAD_LEFT);', - ' return $hex;', - '}']); + ' $ratio = max(min($ratio, 1), 0);', + ' $r1 = hexdec(substr($c1, 1, 2));', + ' $g1 = hexdec(substr($c1, 3, 2));', + ' $b1 = hexdec(substr($c1, 5, 2));', + ' $r2 = hexdec(substr($c2, 1, 2));', + ' $g2 = hexdec(substr($c2, 3, 2));', + ' $b2 = hexdec(substr($c2, 5, 2));', + ' $r = round($r1 * (1 - $ratio) + $r2 * $ratio);', + ' $g = round($g1 * (1 - $ratio) + $g2 * $ratio);', + ' $b = round($b1 * (1 - $ratio) + $b2 * $ratio);', + ' $hex = \'#\';', + ' $hex .= str_pad(dechex($r), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($g), 2, \'0\', STR_PAD_LEFT);', + ' $hex .= str_pad(dechex($b), 2, \'0\', STR_PAD_LEFT);', + ' return $hex;', + '}']); var code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/lists.js b/generators/php/lists.js index a406aa6d2..449c31a5e 100644 --- a/generators/php/lists.js +++ b/generators/php/lists.js @@ -22,6 +22,17 @@ * @fileoverview Generating PHP for list blocks. * @author daarond@gmail.com (Daaron Dwyer) */ + +/* + * Lists in PHP are known to break when non-variables are passed into blocks + * that require a list. PHP, unlike other languages, passes arrays as reference + * value instead of value so we are unable to support it to the extent we can + * for the other languages. + * For example, a ternary operator with two arrays will return the array by + * value and that cannot be passed into any of the built-in array functions for + * PHP (because only variables can be passed by reference). + * ex: end(true ? list1 : list2) + */ 'use strict'; goog.provide('Blockly.PHP.lists'); @@ -31,37 +42,37 @@ goog.require('Blockly.PHP'); Blockly.PHP['lists_create_empty'] = function(block) { // Create an empty list. - return ['array()', Blockly.PHP.ORDER_ATOMIC]; + return ['array()', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.PHP.valueToCode(block, 'ADD' + n, + for (var i = 0; i < block.itemCount_; i++) { + code[i] = Blockly.PHP.valueToCode(block, 'ADD' + i, Blockly.PHP.ORDER_COMMA) || 'null'; } code = 'array(' + code.join(', ') + ')'; - return [code, Blockly.PHP.ORDER_ATOMIC]; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_repeat'] = function(block) { // Create a list with one element repeated. var functionName = Blockly.PHP.provideFunction_( 'lists_repeat', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value, $count) {', - ' $array = array();', - ' for ($index = 0; $index < $count; $index++) {', - ' $array[] = $value;', - ' }', - ' return $array;', - '}']); - var argument0 = Blockly.PHP.valueToCode(block, 'ITEM', + ' $array = array();', + ' for ($index = 0; $index < $count; $index++) {', + ' $array[] = $value;', + ' }', + ' return $array;', + '}']); + var element = Blockly.PHP.valueToCode(block, 'ITEM', Blockly.PHP.ORDER_COMMA) || 'null'; - var argument1 = Blockly.PHP.valueToCode(block, 'NUM', + var repeatCount = Blockly.PHP.valueToCode(block, 'NUM', Blockly.PHP.ORDER_COMMA) || '0'; - var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + var code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -69,16 +80,16 @@ Blockly.PHP['lists_length'] = function(block) { // String or array length. var functionName = Blockly.PHP.provideFunction_( 'length', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', - ' if (is_string($value)) {', - ' return strlen($value);', - ' } else {', - ' return count($value);', - ' }', - '}']); - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - return [functionName + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', + ' if (is_string($value)) {', + ' return strlen($value);', + ' } else {', + ' return count($value);', + ' }', + '}']); + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + return [functionName + '(' + list + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['lists_isEmpty'] = function(block) { @@ -90,36 +101,42 @@ Blockly.PHP['lists_isEmpty'] = function(block) { Blockly.PHP['lists_indexOf'] = function(block) { // Find an item in the list. - var operator = block.getFieldValue('END') == 'FIRST' ? - 'indexOf' : 'lastIndexOf'; var argument0 = Blockly.PHP.valueToCode(block, 'FIND', Blockly.PHP.ORDER_NONE) || '\'\''; var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', Blockly.PHP.ORDER_MEMBER) || '[]'; - var functionName; - if (block.getFieldValue('END') == 'FIRST'){ + if (Blockly.PHP.ONE_BASED_INDEXING) { + var errorIndex = ' 0'; + var indexAdjustment = ' + 1'; + } else { + var errorIndex = ' -1'; + var indexAdjustment = ''; + } + if (block.getFieldValue('END') == 'FIRST') { // indexOf - functionName = Blockly.PHP.provideFunction_( + var functionName = Blockly.PHP.provideFunction_( 'indexOf', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($haystack, $needle) {', - ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) return $index + 1;', - ' }', - ' return 0;', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($haystack, $needle) {', + ' for ($index = 0; $index < count($haystack); $index++) {', + ' if ($haystack[$index] == $needle) return $index' + + indexAdjustment + ';', + ' }', + ' return ' + errorIndex + ';', + '}']); } else { // lastIndexOf - functionName = Blockly.PHP.provideFunction_( + var functionName = Blockly.PHP.provideFunction_( 'lastIndexOf', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($haystack, $needle) {', - ' $last = 0;', - ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) $last = $index + 1;', - ' }', - ' return $last;', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($haystack, $needle) {', + ' $last = ' + errorIndex + ';', + ' for ($index = 0; $index < count($haystack); $index++) {', + ' if ($haystack[$index] == $needle) $last = $index' + + indexAdjustment + ';', + ' }', + ' return $last;', + '}']); } var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; @@ -130,97 +147,113 @@ Blockly.PHP['lists_getIndex'] = function(block) { // Get element at index. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.PHP.valueToCode(block, 'AT', - Blockly.PHP.ORDER_UNARY_NEGATION) || '1'; - if (mode == 'GET') { - var order = Blockly.PHP.ORDER_FUNCTION_CALL; - } else { - // List will be an argument in a function call. - var order = Blockly.PHP.ORDER_COMMA; - } - var list = Blockly.PHP.valueToCode(block, 'VALUE', order) || 'array()'; - - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '[0]'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE') { - var code = 'array_shift(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return 'array_shift(' + list + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = 'end(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE') { - var code = 'array_pop(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return 'array_pop(' + list + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE') { - var code = 'array_splice(' + list + ', ' + at + ', 1)[0]'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return 'array_splice(' + list + ', ' + at + ', 1);\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var code = 'array_slice(' + list + ', -' + at + ', 1)[0]'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { - code = 'array_splice(' + list + - ', count(' + list + ') - ' + at + ', 1)[0]'; - if (mode == 'GET_REMOVE') { + switch (where) { + case 'FIRST': + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + var code = list + '[0]'; + return [code, Blockly.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var code = 'array_shift(' + list + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + ';\n'; + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + return 'array_shift(' + list + ');\n'; } - } - } else if (where == 'RANDOM') { - if (mode == 'GET'){ - var functionName = Blockly.PHP.provideFunction_( - 'lists_get_random_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($list) {', - ' return $list[rand(0,count($list)-1)];', - '}']); - code = functionName + '(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'GET_REMOVE'){ - var functionName = Blockly.PHP.provideFunction_( - 'lists_get_remove_random_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list) {', - ' $x = rand(0,count($list)-1);', - ' unset($list[$x]);', - ' return array_values($list);', - '}']); - code = functionName + '(' + list + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_remove_random_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list) {', - ' unset($list[rand(0,count($list)-1)]);', - '}']); - return functionName + '(' + list + ');\n'; - } + break; + case 'LAST': + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var code = 'end(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var code = 'array_pop(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + return 'array_pop(' + list + ');\n'; + } + break; + case 'FROM_START': + var at = Blockly.PHP.getAdjusted(block, 'AT'); + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + var code = list + '[' + at + ']'; + return [code, Blockly.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || 'array()'; + var code = 'array_splice(' + list + ', ' + at + ', 1)[0]'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || 'array()'; + return 'array_splice(' + list + ', ' + at + ', 1);\n'; + } + break; + case 'FROM_END': + if (mode == 'GET') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || 'array()'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1, true); + var code = 'array_slice(' + list + ', ' + at + ', 1)[0]'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1, false, + Blockly.PHP.ORDER_SUBTRACTION); + code = 'array_splice(' + list + + ', count(' + list + ') - ' + at + ', 1)[0]'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + ';\n'; + } + } + break; + case 'RANDOM': + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'array()'; + if (mode == 'GET') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_random_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list) {', + ' return $list[rand(0,count($list)-1)];', + '}']); + code = functionName + '(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_remove_random_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list) {', + ' $x = rand(0,count($list)-1);', + ' unset($list[$x]);', + ' return array_values($list);', + '}']); + code = functionName + '(' + list + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_remove_random_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list) {', + ' unset($list[rand(0,count($list)-1)]);', + '}']); + return functionName + '(' + list + ');\n'; + } + break; } throw 'Unhandled combination (lists_getIndex).'; }; @@ -228,18 +261,14 @@ Blockly.PHP['lists_getIndex'] = function(block) { Blockly.PHP['lists_setIndex'] = function(block) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. - var list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || 'array()'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.PHP.valueToCode(block, 'AT', - Blockly.PHP.ORDER_NONE) || '1'; var value = Blockly.PHP.valueToCode(block, 'TO', Blockly.PHP.ORDER_ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. // Closure, which accesses and modifies 'list'. function cacheList() { - if (list.match(/^\w+$/)) { + if (list.match(/^\$\w+$/)) { return ''; } var listVar = Blockly.PHP.variableDB_.getDistinctName( @@ -248,68 +277,83 @@ Blockly.PHP['lists_setIndex'] = function(block) { list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return 'array_unshift(' + list + ', ' + value + ');\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_set_last_item', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $value) {', - ' $list[count($list) - 1] = $value;', - '}']); - return functionName + '(' + list + ', ' + value + ');\n'; - } else if (mode == 'INSERT') { - return 'array_push(' + list + ', ' + value + ');\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + ';\n'; - } else if (mode == 'INSERT') { - return 'array_splice(' + list + ', ' + at + ', 0, ' + value + ');\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'SET') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_set_from_end', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $at, $value) {', - ' $list[count($list) - $at] = $value;', - '}']); - return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; - } else if (mode == 'INSERT') { - var functionName = Blockly.PHP.provideFunction_( - 'lists_insert_from_end', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $at, $value) {', - ' return array_splice($list, count($list) - $at, 0, $value);', - '}']); - return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; - } - } else if (where == 'RANDOM') { - var code = cacheList(); - var xVar = Blockly.PHP.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += xVar + ' = rand(0, count(' + list + ')-1);\n'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + ';\n'; - return code; - } else if (mode == 'INSERT') { - code += 'array_splice(' + list + ', ' + xVar + ', 0, ' + value + ');\n'; - return code; - } + switch (where) { + case 'FIRST': + if (mode == 'SET') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + return list + '[0] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + return 'array_unshift(' + list + ', ' + value + ');\n'; + } + break; + case 'LAST': + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + if (mode == 'SET') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_set_last_item', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list, $value) {', + ' $list[count($list) - 1] = $value;', + '}']); + return functionName + '(' + list + ', ' + value + ');\n'; + } else if (mode == 'INSERT') { + return 'array_push(' + list + ', ' + value + ');\n'; + } + break; + case 'FROM_START': + var at = Blockly.PHP.getAdjusted(block, 'AT'); + if (mode == 'SET') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + return list + '[' + at + '] = ' + value + ';\n'; + } else if (mode == 'INSERT') { + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + return 'array_splice(' + list + ', ' + at + ', 0, ' + value + ');\n'; + } + break; + case 'FROM_END': + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1); + if (mode == 'SET') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_set_from_end', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list, $at, $value) {', + ' $list[count($list) - $at] = $value;', + '}']); + return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; + } else if (mode == 'INSERT') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_insert_from_end', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(&$list, $at, $value) {', + ' return array_splice($list, count($list) - $at, 0, $value);', + '}']); + return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; + } + break; + case 'RANDOM': + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_REFERENCE) || 'array()'; + var code = cacheList(); + var xVar = Blockly.PHP.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += xVar + ' = rand(0, count(' + list + ')-1);\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + ';\n'; + return code; + } else if (mode == 'INSERT') { + code += 'array_splice(' + list + ', ' + xVar + ', 0, ' + value + + ');\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; @@ -317,44 +361,90 @@ Blockly.PHP['lists_setIndex'] = function(block) { Blockly.PHP['lists_getSublist'] = function(block) { // Get sublist. var list = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_MEMBER) || 'array()'; + Blockly.PHP.ORDER_COMMA) || 'array()'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.PHP.valueToCode(block, 'AT1', - Blockly.PHP.ORDER_NONE) || '1'; - var at2 = Blockly.PHP.valueToCode(block, 'AT2', - Blockly.PHP.ORDER_NONE) || '1'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = list; + } else if (list.match(/^\$\w+$/) || + (where1 != 'FROM_END' && where2 == 'FROM_START')) { + // If the list is a simple value or doesn't require a call for length, don't + // generate a helper function. + switch (where1) { + case 'FROM_START': + var at1 = Blockly.PHP.getAdjusted(block, 'AT1'); + break; + case 'FROM_END': + var at1 = Blockly.PHP.getAdjusted(block, 'AT1', 1, false, + Blockly.PHP.ORDER_SUBTRACTION); + at1 = 'count(' + list + ') - ' + at1; + break; + case 'FIRST': + var at1 = '0'; + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + switch (where2) { + case 'FROM_START': + var at2 = Blockly.PHP.getAdjusted(block, 'AT2', 0, false, + Blockly.PHP.ORDER_SUBTRACTION); + var length = at2 + ' - '; + if (Blockly.isNumber(String(at1)) || String(at1).match(/^\(.+\)$/)) { + length += at1; + } else { + length += '(' + at1 + ')'; + } + length += ' + 1'; + break; + case 'FROM_END': + var at2 = Blockly.PHP.getAdjusted(block, 'AT2', 0, false, + Blockly.PHP.ORDER_SUBTRACTION); + var length = 'count(' + list + ') - ' + at2 + ' - '; + if (Blockly.isNumber(String(at1)) || String(at1).match(/^\(.+\)$/)) { + length += at1; + } else { + length += '(' + at1 + ')'; + } + break; + case 'LAST': + var length = 'count(' + list + ') - '; + if (Blockly.isNumber(String(at1)) || String(at1).match(/^\(.+\)$/)) { + length += at1; + } else { + length += '(' + at1 + ')'; + } + break; + default: + throw 'Unhandled option (lists_getSublist).'; + } + code = 'array_slice(' + list + ', ' + at1 + ', ' + length + ')'; } else { + var at1 = Blockly.PHP.getAdjusted(block, 'AT1'); + var at2 = Blockly.PHP.getAdjusted(block, 'AT2'); var functionName = Blockly.PHP.provideFunction_( 'lists_get_sublist', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list, $where1, $at1, $where2, $at2) {', - ' if ($where2 == \'FROM_START\') {', - ' $at2--;', - ' } else if ($where2 == \'FROM_END\') {', - ' $at2 = $at2 - $at1;', - ' } else if ($where2 == \'FIRST\') {', - ' $at2 = 0;', - ' } else if ($where2 == \'LAST\') {', - ' $at2 = count($list);', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' if ($where1 == \'FROM_START\') {', - ' $at1--;', - ' } else if ($where1 == \'FROM_END\') {', - ' $at1 = count($list) - $at1;', - ' } else if ($where1 == \'FIRST\') {', - ' $at1 = 0;', - ' } else if ($where1 == \'LAST\') {', - ' $at1 = count($list) - 1;', - ' } else {', - ' throw \'Unhandled option (lists_getSublist).\';', - ' }', - ' return array_slice($list, $at1, $at2);', - '}']); + ' if ($where1 == \'FROM_END\') {', + ' $at1 = count($list) - 1 - $at1;', + ' } else if ($where1 == \'FIRST\') {', + ' $at1 = 0;', + ' } else if ($where1 != \'FROM_START\'){', + ' throw new Exception(\'Unhandled option (lists_get_sublist).\');', + ' }', + ' $length = 0;', + ' if ($where2 == \'FROM_START\') {', + ' $length = $at2 - $at1 + 1;', + ' } else if ($where2 == \'FROM_END\') {', + ' $length = count($list) - $at1 - $at2;', + ' } else if ($where2 == \'LAST\') {', + ' $length = count($list) - $at1;', + ' } else {', + ' throw new Exception(\'Unhandled option (lists_get_sublist).\');', + ' }', + ' return array_slice($list, $at1, $length);', + '}']); var code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -364,26 +454,26 @@ Blockly.PHP['lists_getSublist'] = function(block) { Blockly.PHP['lists_sort'] = function(block) { // Block for sorting a list. var listCode = Blockly.PHP.valueToCode(block, 'LIST', - Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; + Blockly.PHP.ORDER_COMMA) || 'array()'; var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var functionName = Blockly.PHP.provideFunction_( - 'lists_sort', [ - 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($list, $type, $direction) {', - ' $sortCmpFuncs = array(', - ' "NUMERIC" => "strnatcasecmp",', - ' "TEXT" => "strcmp",', - ' "IGNORE_CASE" => "strcasecmp"', - ' );', - ' $sortCmp = $sortCmpFuncs[$type];', - ' $list2 = $list;', // Clone list. - ' usort($list2, $sortCmp);', - ' if ($direction == -1) {', - ' $list2 = array_reverse($list2);', - ' }', - ' return $list2;', - '}']); + 'lists_sort', + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list, $type, $direction) {', + ' $sortCmpFuncs = array(', + ' "NUMERIC" => "strnatcasecmp",', + ' "TEXT" => "strcmp",', + ' "IGNORE_CASE" => "strcasecmp"', + ' );', + ' $sortCmp = $sortCmpFuncs[$type];', + ' $list2 = $list;', // Clone list. + ' usort($list2, $sortCmp);', + ' if ($direction == -1) {', + ' $list2 = array_reverse($list2);', + ' }', + ' return $list2;', + '}']); var sortCode = functionName + '(' + listCode + ', "' + type + '", ' + direction + ')'; return [sortCode, Blockly.PHP.ORDER_FUNCTION_CALL]; @@ -392,9 +482,9 @@ Blockly.PHP['lists_sort'] = function(block) { Blockly.PHP['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. var value_input = Blockly.PHP.valueToCode(block, 'INPUT', - Blockly.PHP.ORDER_MEMBER); + Blockly.PHP.ORDER_COMMA); var value_delim = Blockly.PHP.valueToCode(block, 'DELIM', - Blockly.PHP.ORDER_NONE) || '\'\''; + Blockly.PHP.ORDER_COMMA) || '\'\''; var mode = block.getFieldValue('MODE'); if (mode == 'SPLIT') { if (!value_input) { diff --git a/generators/php/math.js b/generators/php/math.js index f0bb9fa79..7789ba8fe 100644 --- a/generators/php/math.js +++ b/generators/php/math.js @@ -47,20 +47,14 @@ Blockly.PHP['math_arithmetic'] = function(block) { 'MINUS': [' - ', Blockly.PHP.ORDER_SUBTRACTION], 'MULTIPLY': [' * ', Blockly.PHP.ORDER_MULTIPLICATION], 'DIVIDE': [' / ', Blockly.PHP.ORDER_DIVISION], - 'POWER': [null, Blockly.PHP.ORDER_COMMA] // Handle power separately. + 'POWER': [' ** ', Blockly.PHP.ORDER_POWER] }; var tuple = OPERATORS[block.getFieldValue('OP')]; var operator = tuple[0]; var order = tuple[1]; var argument0 = Blockly.PHP.valueToCode(block, 'A', order) || '0'; var argument1 = Blockly.PHP.valueToCode(block, 'B', order) || '0'; - var code; - // Power in PHP requires a special case since it has no operator. - if (!operator) { - code = 'pow(' + argument0 + ', ' + argument1 + ')'; - return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; - } - code = argument0 + operator + argument1; + var code = argument0 + operator + argument1; return [code, order]; }; @@ -172,25 +166,25 @@ Blockly.PHP['math_number_property'] = function(block) { // Prime is a special case as it is not a one-liner test. var functionName = Blockly.PHP.provideFunction_( 'math_isPrime', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if ($n == 2 || $n == 3) {', - ' return true;', - ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + - ' $n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', - ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if ($n == 2 || $n == 3) {', + ' return true;', + ' }', + ' // False if n is NaN, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + + ' $n % 3 == 0) {', + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', + ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); code = functionName + '(' + number_to_check + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } @@ -256,10 +250,10 @@ Blockly.PHP['math_on_list'] = function(block) { case 'AVERAGE': var functionName = Blockly.PHP.provideFunction_( 'math_mean', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($myList) {', - ' return array_sum($myList) / count($myList);', - '}']); + ' return array_sum($myList) / count($myList);', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || 'array()'; code = functionName + '(' + list + ')'; @@ -267,12 +261,13 @@ Blockly.PHP['math_on_list'] = function(block) { case 'MEDIAN': var functionName = Blockly.PHP.provideFunction_( 'math_median', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($arr) {', - ' sort($arr,SORT_NUMERIC);', - ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ', - ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2) - 1]) / 2;', - '}']); + ' sort($arr,SORT_NUMERIC);', + ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ', + ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2)' + + ' - 1]) / 2;', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -283,13 +278,14 @@ Blockly.PHP['math_on_list'] = function(block) { // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.PHP.provideFunction_( 'math_modes', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($values) {', - ' $v = array_count_values($values);', - ' arsort($v);', - ' foreach($v as $k => $v){$total = $k; break;}', - ' return array($total);', - '}']); + ' if (empty($values)) return array();', + ' $counts = array_count_values($values);', + ' arsort($counts); // Sort counts in descending order', + ' $modes = array_keys($counts, current($counts), true);', + ' return $modes;', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -297,14 +293,15 @@ Blockly.PHP['math_on_list'] = function(block) { case 'STD_DEV': var functionName = Blockly.PHP.provideFunction_( 'math_standard_deviation', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($numbers) {', - ' $n = count($numbers);', - ' if (!$n) return null;', - ' $mean = array_sum($numbers) / count($numbers);', - ' foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2);', - ' return sqrt(array_sum($devs) / (count($devs) - 1));', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($numbers) {', + ' $n = count($numbers);', + ' if (!$n) return null;', + ' $mean = array_sum($numbers) / count($numbers);', + ' foreach($numbers as $key => $num) $devs[$key] = ' + + 'pow($num - $mean, 2);', + ' return sqrt(array_sum($devs) / (count($devs) - 1));', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -312,11 +309,11 @@ Blockly.PHP['math_on_list'] = function(block) { case 'RANDOM': var functionName = Blockly.PHP.provideFunction_( 'math_random_list', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {', - ' $x = rand(0, count($list)-1);', - ' return $list[$x];', - '}']); + ' $x = rand(0, count($list)-1);', + ' return $list[$x];', + '}']); list = Blockly.PHP.valueToCode(block, 'LIST', Blockly.PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -358,13 +355,13 @@ Blockly.PHP['math_random_int'] = function(block) { Blockly.PHP.ORDER_COMMA) || '0'; var functionName = Blockly.PHP.provideFunction_( 'math_random_int', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($a, $b) {', - ' if ($a > $b) {', - ' return rand($b, $a);', - ' }', - ' return rand($a, $b);', - '}']); + ' if ($a > $b) {', + ' return rand($b, $a);', + ' }', + ' return rand($a, $b);', + '}']); var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/procedures.js b/generators/php/procedures.js index 1fd4cc872..0dc56d7cb 100644 --- a/generators/php/procedures.js +++ b/generators/php/procedures.js @@ -63,8 +63,8 @@ Blockly.PHP['procedures_defreturn'] = function(block) { returnValue = ' return ' + returnValue + ';\n'; } var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.PHP.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + @@ -85,8 +85,8 @@ Blockly.PHP['procedures_callreturn'] = function(block) { var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.PHP.valueToCode(block, 'ARG' + i, Blockly.PHP.ORDER_COMMA) || 'null'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -98,8 +98,8 @@ Blockly.PHP['procedures_callnoreturn'] = function(block) { var funcName = Blockly.PHP.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.PHP.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.PHP.valueToCode(block, 'ARG' + i, Blockly.PHP.ORDER_COMMA) || 'null'; } var code = funcName + '(' + args.join(', ') + ');\n'; diff --git a/generators/php/text.js b/generators/php/text.js index 3960ab011..efc0f2de3 100644 --- a/generators/php/text.js +++ b/generators/php/text.js @@ -37,28 +37,27 @@ Blockly.PHP['text'] = function(block) { Blockly.PHP['text_join'] = function(block) { // Create a string made up of any number of elements of any type. - var code; if (block.itemCount_ == 0) { return ['\'\'', Blockly.PHP.ORDER_ATOMIC]; } else if (block.itemCount_ == 1) { - var argument0 = Blockly.PHP.valueToCode(block, 'ADD0', + var element = Blockly.PHP.valueToCode(block, 'ADD0', Blockly.PHP.ORDER_NONE) || '\'\''; - code = argument0; + var code = element; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } else if (block.itemCount_ == 2) { - var argument0 = Blockly.PHP.valueToCode(block, 'ADD0', + var element0 = Blockly.PHP.valueToCode(block, 'ADD0', Blockly.PHP.ORDER_NONE) || '\'\''; - var argument1 = Blockly.PHP.valueToCode(block, 'ADD1', + var element1 = Blockly.PHP.valueToCode(block, 'ADD1', Blockly.PHP.ORDER_NONE) || '\'\''; - code = argument0 + ' . ' + argument1; + var code = element0 + ' . ' + element1; return [code, Blockly.PHP.ORDER_ADDITION]; } else { - code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.PHP.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.PHP.valueToCode(block, 'ADD' + i, Blockly.PHP.ORDER_COMMA) || '\'\''; } - code = 'implode(\'\', array(' + code.join(',') + '))'; + var code = 'implode(\'\', array(' + elements.join(',') + '))'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } }; @@ -67,89 +66,89 @@ Blockly.PHP['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_NONE) || '\'\''; - return varName + ' .= ' + argument0 + ';\n'; + var value = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_ASSIGNMENT) || '\'\''; + return varName + ' .= ' + value + ';\n'; }; Blockly.PHP['text_length'] = function(block) { // String or array length. var functionName = Blockly.PHP.provideFunction_( 'length', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', - ' if (is_string($value)) {', - ' return strlen($value);', - ' } else {', - ' return count($value);', - ' }', - '}']); - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - return [functionName + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', + ' if (is_string($value)) {', + ' return strlen($value);', + ' } else {', + ' return count($value);', + ' }', + '}']); + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + return [functionName + '(' + text + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + return ['empty(' + text + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_indexOf'] = function(block) { // Search the text for a substring. var operator = block.getFieldValue('END') == 'FIRST' ? 'strpos' : 'strrpos'; - var argument0 = Blockly.PHP.valueToCode(block, 'FIND', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - + var substring = Blockly.PHP.valueToCode(block, 'FIND', + Blockly.PHP.ORDER_NONE) || '\'\''; + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || '\'\''; + if (Blockly.PHP.ONE_BASED_INDEXING) { + var errorIndex = ' 0'; + var indexAdjustment = ' + 1'; + } else { + var errorIndex = ' -1'; + var indexAdjustment = ''; + } var functionName = Blockly.PHP.provideFunction_( block.getFieldValue('END') == 'FIRST' ? 'text_indexOf' : 'text_lastIndexOf', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text, $search) {', - ' $pos = ' + operator + '($text, $search);', - ' return $pos === false ? 0 : $pos + 1;', - '}']); - var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + ' $pos = ' + operator + '($text, $search);', + ' return $pos === false ? ' + errorIndex + ' : $pos' + + indexAdjustment + ';', + '}']); + var code = functionName + '(' + text + ', ' + substring + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_charAt'] = function(block) { // Get letter at index. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.PHP.valueToCode(block, 'AT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '0'; - var text = Blockly.PHP.valueToCode(block, 'VALUE', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + var textOrder = (where == 'RANDOM') ? Blockly.PHP.ORDER_NONE : + Blockly.PHP.ORDER_COMMA; + var text = Blockly.PHP.valueToCode(block, 'VALUE', textOrder) || '\'\''; switch (where) { case 'FIRST': - var code = text + '[0]'; + var code = 'substr(' + text + ', 0, 1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'LAST': - var code = 'substr(' + text + ', -1, 1)'; + var code = 'substr(' + text + ', -1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'FROM_START': - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseFloat(at) - 1; - } else { - // If the index is dynamic, decrement it in code. - at += ' - 1'; - } + var at = Blockly.PHP.getAdjusted(block, 'AT'); var code = 'substr(' + text + ', ' + at + ', 1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'FROM_END': - var code = 'substr(' + text + ', -' + at + ', 1)'; + var at = Blockly.PHP.getAdjusted(block, 'AT', 1, true); + var code = 'substr(' + text + ', ' + at + ', 1)'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; case 'RANDOM': var functionName = Blockly.PHP.provideFunction_( 'text_random_letter', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text) {', - ' return $text[rand(0, strlen($text) - 1)];', - '}']); + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text) {', + ' return $text[rand(0, strlen($text) - 1)];', + '}']); code = functionName + '(' + text + ')'; return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; } @@ -162,37 +161,34 @@ Blockly.PHP['text_getSubstring'] = function(block) { Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.PHP.valueToCode(block, 'AT1', - Blockly.PHP.ORDER_FUNCTION_CALL) || '0'; - var at2 = Blockly.PHP.valueToCode(block, 'AT2', - Blockly.PHP.ORDER_FUNCTION_CALL) || '0'; if (where1 == 'FIRST' && where2 == 'LAST') { var code = text; } else { + var at1 = Blockly.PHP.getAdjusted(block, 'AT1'); + var at2 = Blockly.PHP.getAdjusted(block, 'AT2'); var functionName = Blockly.PHP.provideFunction_( 'text_get_substring', - [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text, $where1, $at1, $where2, $at2) {', - ' if ($where2 == \'FROM_START\') {', - ' $at2--;', - ' } else if ($where2 == \'FROM_END\') {', - ' $at2 = $at2 - $at1;', - ' } else if ($where2 == \'FIRST\') {', - ' $at2 = 0;', - ' } else if ($where2 == \'LAST\') {', - ' $at2 = strlen($text);', - ' } else { $at2 = 0; }', - ' if ($where1 == \'FROM_START\') {', - ' $at1--;', - ' } else if ($where1 == \'FROM_END\') {', - ' $at1 = strlen($text) - $at1;', - ' } else if ($where1 == \'FIRST\') {', - ' $at1 = 0;', - ' } else if ($where1 == \'LAST\') {', - ' $at1 = strlen($text) - 1;', - ' } else { $at1 = 0; }', - ' return substr($text, $at1, $at2);', - '}']); + ' if ($where1 == \'FROM_END\') {', + ' $at1 = strlen($text) - 1 - $at1;', + ' } else if ($where1 == \'FIRST\') {', + ' $at1 = 0;', + ' } else if ($where1 != \'FROM_START\'){', + ' throw new Exception(\'Unhandled option (text_get_substring).\');', + ' }', + ' $length = 0;', + ' if ($where2 == \'FROM_START\') {', + ' $length = $at2 - $at1 + 1;', + ' } else if ($where2 == \'FROM_END\') {', + ' $length = strlen($text) - $at1 - $at2;', + ' } else if ($where2 == \'LAST\') {', + ' $length = strlen($text) - $at1;', + ' } else {', + ' throw new Exception(\'Unhandled option (text_get_substring).\');', + ' }', + ' return substr($text, $at1, $length);', + '}']); var code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -201,19 +197,14 @@ Blockly.PHP['text_getSubstring'] = function(block) { Blockly.PHP['text_changeCase'] = function(block) { // Change capitalization. - var code; + var text = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_NONE) || '\'\''; if (block.getFieldValue('CASE') == 'UPPERCASE') { - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'strtoupper(' + argument0 + ')'; + var code = 'strtoupper(' + text + ')'; } else if (block.getFieldValue('CASE') == 'LOWERCASE') { - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'strtolower(' + argument0 + ')'; + var code = 'strtolower(' + text + ')'; } else if (block.getFieldValue('CASE') == 'TITLECASE') { - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', - Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; - code = 'ucwords(strtolower(' + argument0 + '))'; + var code = 'ucwords(strtolower(' + text + '))'; } return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; }; @@ -226,16 +217,16 @@ Blockly.PHP['text_trim'] = function(block) { 'BOTH': 'trim' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + var text = Blockly.PHP.valueToCode(block, 'TEXT', Blockly.PHP.ORDER_NONE) || '\'\''; - return [ operator + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; + return [operator + '(' + text + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; }; Blockly.PHP['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + var msg = Blockly.PHP.valueToCode(block, 'TEXT', Blockly.PHP.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ');\n'; + return 'print(' + msg + ');\n'; }; Blockly.PHP['text_prompt_ext'] = function(block) { diff --git a/generators/php/variables.js b/generators/php/variables.js index dd68f4ead..466774b3f 100644 --- a/generators/php/variables.js +++ b/generators/php/variables.js @@ -43,4 +43,4 @@ Blockly.PHP['variables_set'] = function(block) { var varName = Blockly.PHP.variableDB_.getName( block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); return varName + ' = ' + argument0 + ';\n'; -}; \ No newline at end of file +}; diff --git a/generators/python.js b/generators/python.js index 2e77c0753..679093db1 100644 --- a/generators/python.js +++ b/generators/python.js @@ -46,11 +46,22 @@ Blockly.Python.addReservedWords( // import keyword // print ','.join(keyword.kwlist) // http://docs.python.org/reference/lexical_analysis.html#keywords - 'and,as,assert,break,class,continue,def,del,elif,else,except,exec,finally,for,from,global,if,import,in,is,lambda,not,or,pass,print,raise,return,try,while,with,yield,' + + 'and,as,assert,break,class,continue,def,del,elif,else,except,exec,' + + 'finally,for,from,global,if,import,in,is,lambda,not,or,pass,print,raise,' + + 'return,try,while,with,yield,' + //http://docs.python.org/library/constants.html - 'True,False,None,NotImplemented,Ellipsis,__debug__,quit,exit,copyright,license,credits,' + + 'True,False,None,NotImplemented,Ellipsis,__debug__,quit,exit,copyright,' + + 'license,credits,' + // http://docs.python.org/library/functions.html - 'abs,divmod,input,open,staticmethod,all,enumerate,int,ord,str,any,eval,isinstance,pow,sum,basestring,execfile,issubclass,print,super,bin,file,iter,property,tuple,bool,filter,len,range,type,bytearray,float,list,raw_input,unichr,callable,format,locals,reduce,unicode,chr,frozenset,long,reload,vars,classmethod,getattr,map,repr,xrange,cmp,globals,max,reversed,zip,compile,hasattr,memoryview,round,__import__,complex,hash,min,set,apply,delattr,help,next,setattr,buffer,dict,hex,object,slice,coerce,dir,id,oct,sorted,intern'); + 'abs,divmod,input,open,staticmethod,all,enumerate,int,ord,str,any,eval,' + + 'isinstance,pow,sum,basestring,execfile,issubclass,print,super,bin,file,' + + 'iter,property,tuple,bool,filter,len,range,type,bytearray,float,list,' + + 'raw_input,unichr,callable,format,locals,reduce,unicode,chr,frozenset,' + + 'long,reload,vars,classmethod,getattr,map,repr,xrange,cmp,globals,max,' + + 'reversed,zip,compile,hasattr,memoryview,round,__import__,complex,hash,' + + 'min,set,apply,delattr,help,next,setattr,buffer,dict,hex,object,slice,' + + 'coerce,dir,id,oct,sorted,intern' +); /** * Order of operation ENUMs. @@ -79,6 +90,12 @@ Blockly.Python.ORDER_CONDITIONAL = 15; // if else Blockly.Python.ORDER_LAMBDA = 16; // lambda Blockly.Python.ORDER_NONE = 99; // (...) +/** + * Allow for switching between one and zero based indexing for lists and text, + * one based by default. + */ +Blockly.Python.ONE_BASED_INDEXING = true; + /** * List of outer-inner pairings that do NOT require parentheses. * @type {!Array.>} @@ -213,9 +230,9 @@ Blockly.Python.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.Python.allNestedComments(childBlock); if (comment) { @@ -229,3 +246,45 @@ Blockly.Python.scrub_ = function(block, code) { var nextCode = Blockly.Python.blockToCode(nextBlock); return commentCode + code + nextCode; }; + +/** + * Gets a property and adjusts the value, taking into account indexing, and + * casts to an integer. + * @param {!Blockly.Block} block The block. + * @param {string} atId The property ID of the element to get. + * @param {number=} opt_delta Value to add. + * @param {boolean=} opt_negate Whether to negate the value. + * @return {string|number} + */ +Blockly.Python.getAdjustedInt = function(block, atId, opt_delta, opt_negate) { + var delta = opt_delta || 0; + if (Blockly.Python.ONE_BASED_INDEXING) { + delta--; + } + var defaultAtIndex = Blockly.Python.ONE_BASED_INDEXING ? '1' : '0'; + var atOrder = delta ? Blockly.Python.ORDER_ADDITIVE : + Blockly.Python.ORDER_NONE; + var at = Blockly.Python.valueToCode(block, atId, atOrder) || defaultAtIndex; + + if (Blockly.isNumber(at)) { + // If the index is a naked number, adjust it right now. + at = parseInt(at, 10) + delta; + if (opt_negate) { + at = -at; + } + } else { + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = 'int(' + at + ' + ' + delta + ')'; + } else if (delta < 0) { + at = 'int(' + at + ' - ' + -delta + ')'; + } else { + at = 'int(' + at + ')'; + } + if (opt_negate) { + at = '-' + at; + } + } + return at; +}; + diff --git a/generators/python/colour.js b/generators/python/colour.js index 49505ac86..68666a89b 100644 --- a/generators/python/colour.js +++ b/generators/python/colour.js @@ -46,11 +46,11 @@ Blockly.Python['colour_rgb'] = function(block) { // Compose a colour from RGB components expressed as percentages. var functionName = Blockly.Python.provideFunction_( 'colour_rgb', - [ 'def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b):', - ' r = round(min(100, max(0, r)) * 2.55)', - ' g = round(min(100, max(0, g)) * 2.55)', - ' b = round(min(100, max(0, b)) * 2.55)', - ' return \'#%02x%02x%02x\' % (r, g, b)']); + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b):', + ' r = round(min(100, max(0, r)) * 2.55)', + ' g = round(min(100, max(0, g)) * 2.55)', + ' b = round(min(100, max(0, b)) * 2.55)', + ' return \'#%02x%02x%02x\' % (r, g, b)']); var r = Blockly.Python.valueToCode(block, 'RED', Blockly.Python.ORDER_NONE) || 0; var g = Blockly.Python.valueToCode(block, 'GREEN', diff --git a/generators/python/lists.js b/generators/python/lists.js index e43e31d5e..b831d4d2f 100644 --- a/generators/python/lists.js +++ b/generators/python/lists.js @@ -36,66 +36,75 @@ Blockly.Python['lists_create_empty'] = function(block) { Blockly.Python['lists_create_with'] = function(block) { // Create a list with any number of elements of any type. - var code = new Array(block.itemCount_); - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Python.valueToCode(block, 'ADD' + n, + var elements = new Array(block.itemCount_); + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Python.valueToCode(block, 'ADD' + i, Blockly.Python.ORDER_NONE) || 'None'; } - code = '[' + code.join(', ') + ']'; + var code = '[' + elements.join(', ') + ']'; return [code, Blockly.Python.ORDER_ATOMIC]; }; Blockly.Python['lists_repeat'] = function(block) { // Create a list with one element repeated. - var argument0 = Blockly.Python.valueToCode(block, 'ITEM', + var item = Blockly.Python.valueToCode(block, 'ITEM', Blockly.Python.ORDER_NONE) || 'None'; - var argument1 = Blockly.Python.valueToCode(block, 'NUM', + var times = Blockly.Python.valueToCode(block, 'NUM', Blockly.Python.ORDER_MULTIPLICATIVE) || '0'; - var code = '[' + argument0 + '] * ' + argument1; + var code = '[' + item + '] * ' + times; return [code, Blockly.Python.ORDER_MULTIPLICATIVE]; }; Blockly.Python['lists_length'] = function(block) { // String or array length. - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var list = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '[]'; - return ['len(' + argument0 + ')', Blockly.Python.ORDER_FUNCTION_CALL]; + return ['len(' + list + ')', Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['lists_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var list = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '[]'; - var code = 'not len(' + argument0 + ')'; + var code = 'not len(' + list + ')'; return [code, Blockly.Python.ORDER_LOGICAL_NOT]; }; Blockly.Python['lists_indexOf'] = function(block) { // Find an item in the list. - var argument0 = Blockly.Python.valueToCode(block, 'FIND', + var item = Blockly.Python.valueToCode(block, 'FIND', Blockly.Python.ORDER_NONE) || '[]'; - var argument1 = Blockly.Python.valueToCode(block, 'VALUE', - Blockly.Python.ORDER_MEMBER) || '\'\''; - var code; + var list = Blockly.Python.valueToCode(block, 'VALUE', + Blockly.Python.ORDER_NONE) || '\'\''; + if (Blockly.Python.ONE_BASED_INDEXING) { + var errorIndex = ' 0'; + var firstIndexAdjustment = ' + 1'; + var lastIndexAdjustment = ''; + } else { + var errorIndex = ' -1'; + var firstIndexAdjustment = ''; + var lastIndexAdjustment = ' - 1'; + } if (block.getFieldValue('END') == 'FIRST') { var functionName = Blockly.Python.provideFunction_( 'first_index', - ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList, elem):', - ' try: theIndex = myList.index(elem) + 1', - ' except: theIndex = 0', - ' return theIndex']); - code = functionName + '(' + argument1 + ', ' + argument0 + ')'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else { - var functionName = Blockly.Python.provideFunction_( - 'last_index', - ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList, elem):', - ' try: theIndex = len(myList) - myList[::-1].index(elem)', - ' except: theIndex = 0', - ' return theIndex']); - code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + + '(my_list, elem):', + ' try: index = my_list.index(elem)' + firstIndexAdjustment, + ' except: index =' + errorIndex, + ' return index']); + var code = functionName + '(' + list + ', ' + item + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } + var functionName = Blockly.Python.provideFunction_( + 'last_index', + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(my_list, elem):', + ' try: index = len(my_list) - my_list[::-1].index(elem)' + + lastIndexAdjustment, + ' except: index =' + errorIndex, + ' return index']); + var code = functionName + '(' + list + ', ' + item + ')'; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['lists_getIndex'] = function(block) { @@ -103,85 +112,76 @@ Blockly.Python['lists_getIndex'] = function(block) { // Note: Until January 2013 this block did not have MODE or WHERE inputs. var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Python.valueToCode(block, 'AT', - Blockly.Python.ORDER_UNARY_SIGN) || '1'; - var list = Blockly.Python.valueToCode(block, 'VALUE', - Blockly.Python.ORDER_MEMBER) || '[]'; + var listOrder = (where == 'RANDOM') ? Blockly.Python.ORDER_NONE : + Blockly.Python.ORDER_MEMBER; + var list = Blockly.Python.valueToCode(block, 'VALUE', listOrder) || '[]'; - if (where == 'FIRST') { - if (mode == 'GET') { - var code = list + '[0]'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop(0)'; - if (mode == 'GET_REMOVE') { + switch (where) { + case 'FIRST': + if (mode == 'GET') { + var code = list + '[0]'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop(0)'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop(0)\n'; } - } - } else if (where == 'LAST') { - if (mode == 'GET') { - var code = list + '[-1]'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop()'; - if (mode == 'GET_REMOVE') { + break; + case 'LAST': + if (mode == 'GET') { + var code = list + '[-1]'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop()'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop()\n'; } - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at = 'int(' + at + ' - 1)'; - } - if (mode == 'GET') { - var code = list + '[' + at + ']'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop(' + at + ')'; - if (mode == 'GET_REMOVE') { + break; + case 'FROM_START': + var at = Blockly.Python.getAdjustedInt(block, 'AT'); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop(' + at + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop(' + at + ')\n'; } - } - } else if (where == 'FROM_END') { - if (mode == 'GET') { - var code = list + '[-' + at + ']'; - return [code, Blockly.Python.ORDER_MEMBER]; - } else { - var code = list + '.pop(-' + at + ')'; - if (mode == 'GET_REMOVE') { + break; + case'FROM_END': + var at = Blockly.Python.getAdjustedInt(block, 'AT', 1, true); + if (mode == 'GET') { + var code = list + '[' + at + ']'; + return [code, Blockly.Python.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop(' + at + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } else if (mode == 'REMOVE') { - return code + '\n'; + return list + '.pop(' + at + ')\n'; } - } - } else if (where == 'RANDOM') { - Blockly.Python.definitions_['import_random'] = 'import random'; - if (mode == 'GET') { - code = 'random.choice(' + list + ')'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else { - var functionName = Blockly.Python.provideFunction_( - 'lists_remove_random_item', - ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', - ' x = int(random.random() * len(myList))', - ' return myList.pop(x)']); - code = functionName + '(' + list + ')'; - if (mode == 'GET_REMOVE') { + break; + case 'RANDOM': + Blockly.Python.definitions_['import_random'] = 'import random'; + if (mode == 'GET') { + code = 'random.choice(' + list + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else if (mode == 'REMOVE') { - return code + '\n'; + } else { + var functionName = Blockly.Python.provideFunction_( + 'lists_remove_random_item', + ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', + ' x = int(random.random() * len(myList))', + ' return myList.pop(x)']); + code = functionName + '(' + list + ')'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + '\n'; + } } - } + break; } throw 'Unhandled combination (lists_getIndex).'; }; @@ -193,8 +193,6 @@ Blockly.Python['lists_setIndex'] = function(block) { Blockly.Python.ORDER_MEMBER) || '[]'; var mode = block.getFieldValue('MODE') || 'GET'; var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Python.valueToCode(block, 'AT', - Blockly.Python.ORDER_NONE) || '1'; var value = Blockly.Python.valueToCode(block, 'TO', Blockly.Python.ORDER_NONE) || 'None'; // Cache non-trivial values to variables to prevent repeated look-ups. @@ -209,51 +207,52 @@ Blockly.Python['lists_setIndex'] = function(block) { list = listVar; return code; } - if (where == 'FIRST') { - if (mode == 'SET') { - return list + '[0] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.insert(0, ' + value + ')\n'; - } - } else if (where == 'LAST') { - if (mode == 'SET') { - return list + '[-1] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.append(' + value + ')\n'; - } - } else if (where == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at = 'int(' + at + ' - 1)'; - } - if (mode == 'SET') { - return list + '[' + at + '] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.insert(' + at + ', ' + value + ')\n'; - } - } else if (where == 'FROM_END') { - if (mode == 'SET') { - return list + '[-' + at + '] = ' + value + '\n'; - } else if (mode == 'INSERT') { - return list + '.insert(-' + at + ', ' + value + ')\n'; - } - } else if (where == 'RANDOM') { - Blockly.Python.definitions_['import_random'] = 'import random'; - var code = cacheList(); - var xVar = Blockly.Python.variableDB_.getDistinctName( - 'tmp_x', Blockly.Variables.NAME_TYPE); - code += xVar + ' = int(random.random() * len(' + list + '))\n'; - if (mode == 'SET') { - code += list + '[' + xVar + '] = ' + value + '\n'; - return code; - } else if (mode == 'INSERT') { - code += list + '.insert(' + xVar + ', ' + value + ')\n'; - return code; - } + + switch (where) { + case 'FIRST': + if (mode == 'SET') { + return list + '[0] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.insert(0, ' + value + ')\n'; + } + break; + case 'LAST': + if (mode == 'SET') { + return list + '[-1] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.append(' + value + ')\n'; + } + break; + case 'FROM_START': + var at = Blockly.Python.getAdjustedInt(block, 'AT'); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.insert(' + at + ', ' + value + ')\n'; + } + break; + case 'FROM_END': + var at = Blockly.Python.getAdjustedInt(block, 'AT', 1, true); + if (mode == 'SET') { + return list + '[' + at + '] = ' + value + '\n'; + } else if (mode == 'INSERT') { + return list + '.insert(' + at + ', ' + value + ')\n'; + } + break; + case 'RANDOM': + Blockly.Python.definitions_['import_random'] = 'import random'; + var code = cacheList(); + var xVar = Blockly.Python.variableDB_.getDistinctName( + 'tmp_x', Blockly.Variables.NAME_TYPE); + code += xVar + ' = int(random.random() * len(' + list + '))\n'; + if (mode == 'SET') { + code += list + '[' + xVar + '] = ' + value + '\n'; + return code; + } else if (mode == 'INSERT') { + code += list + '.insert(' + xVar + ', ' + value + ')\n'; + return code; + } + break; } throw 'Unhandled combination (lists_setIndex).'; }; @@ -264,49 +263,42 @@ Blockly.Python['lists_getSublist'] = function(block) { Blockly.Python.ORDER_MEMBER) || '[]'; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Python.valueToCode(block, 'AT1', - Blockly.Python.ORDER_ADDITIVE) || '1'; - var at2 = Blockly.Python.valueToCode(block, 'AT2', - Blockly.Python.ORDER_ADDITIVE) || '1'; - if (where1 == 'FIRST' || (where1 == 'FROM_START' && at1 == '1')) { - at1 = ''; - } else if (where1 == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at1)) { - // If the index is a naked number, decrement it right now. - at1 = parseInt(at1, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at1 = 'int(' + at1 + ' - 1)'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at1)) { - at1 = -parseInt(at1, 10); - } else { - at1 = '-int(' + at1 + ')'; - } + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1'); + if (at1 == '0') { + at1 = ''; + } + break; + case 'FROM_END': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1', 1, true); + break; + case 'FIRST': + var at1 = ''; + break; + default: + throw 'Unhandled option (lists_getSublist)'; } - if (where2 == 'LAST' || (where2 == 'FROM_END' && at2 == '1')) { - at2 = ''; - } else if (where1 == 'FROM_START') { - if (Blockly.isNumber(at2)) { - at2 = parseInt(at2, 10); - } else { - at2 = 'int(' + at2 + ')'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at2)) { - // If the index is a naked number, increment it right now. - // Add special case for -0. - at2 = 1 - parseInt(at2, 10); - if (at2 == 0) { + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 0, true); + // Ensure that if the result calculated is 0 that sub-sequence will + // include all elements as expected. + if (!Blockly.isNumber(String(at2))) { + Blockly.Python.definitions_['import_sys'] = 'import sys'; + at2 += ' or sys.maxsize'; + } else if (at2 == '0') { at2 = ''; } - } else { - // If the index is dynamic, increment it in code. - Blockly.Python.definitions_['import_sys'] = 'import sys'; - at2 = 'int(1 - ' + at2 + ') or sys.maxsize'; - } + break; + case 'LAST': + var at2 = ''; + break; + default: + throw 'Unhandled option (lists_getSublist)'; } var code = list + '[' + at1 + ' : ' + at2 + ']'; return [code, Blockly.Python.ORDER_MEMBER]; @@ -314,30 +306,30 @@ Blockly.Python['lists_getSublist'] = function(block) { Blockly.Python['lists_sort'] = function(block) { // Block for sorting a list. - var listCode = (Blockly.Python.valueToCode(block, 'LIST', - Blockly.Python.ORDER_MEMBER) || '[]'); + var list = (Blockly.Python.valueToCode(block, 'LIST', + Blockly.Python.ORDER_NONE) || '[]'); var type = block.getFieldValue('TYPE'); var reverse = block.getFieldValue('DIRECTION') === '1' ? 'False' : 'True'; var sortFunctionName = Blockly.Python.provideFunction_('lists_sort', ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + - '(listv, type, reversev):', - ' def tryfloat(s):', + '(my_list, type, reverse):', + ' def try_float(s):', ' try:', ' return float(s)', ' except:', ' return 0', - ' keyFuncts = {', - ' "NUMERIC": tryfloat,', + ' key_funcs = {', + ' "NUMERIC": try_float,', ' "TEXT": str,', ' "IGNORE_CASE": lambda s: str(s).lower()', ' }', - ' keyv = keyFuncts[type]', - ' tmp_list = list(listv)', // Clone the list. - ' return sorted(tmp_list, key=keyv, reverse=reversev)' + ' key_func = key_funcs[type]', + ' list_cpy = list(my_list)', // Clone the list. + ' return sorted(list_cpy, key=key_func, reverse=reverse)' ]); var code = sortFunctionName + - '(' + listCode + ', "' + type + '", ' + reverse + ')'; + '(' + list + ', "' + type + '", ' + reverse + ')'; return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; diff --git a/generators/python/procedures.js b/generators/python/procedures.js index daa7386aa..b90f38d5b 100644 --- a/generators/python/procedures.js +++ b/generators/python/procedures.js @@ -65,8 +65,8 @@ Blockly.Python['procedures_defreturn'] = function(block) { branch = Blockly.Python.PASS; } var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Python.variableDB_.getName(block.arguments_[x], + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Python.variableDB_.getName(block.arguments_[i], Blockly.Variables.NAME_TYPE); } var code = 'def ' + funcName + '(' + args.join(', ') + '):\n' + @@ -87,8 +87,8 @@ Blockly.Python['procedures_callreturn'] = function(block) { var funcName = Blockly.Python.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Python.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Python.valueToCode(block, 'ARG' + i, Blockly.Python.ORDER_NONE) || 'None'; } var code = funcName + '(' + args.join(', ') + ')'; @@ -100,8 +100,8 @@ Blockly.Python['procedures_callnoreturn'] = function(block) { var funcName = Blockly.Python.variableDB_.getName(block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); var args = []; - for (var x = 0; x < block.arguments_.length; x++) { - args[x] = Blockly.Python.valueToCode(block, 'ARG' + x, + for (var i = 0; i < block.arguments_.length; i++) { + args[i] = Blockly.Python.valueToCode(block, 'ARG' + i, Blockly.Python.ORDER_NONE) || 'None'; } var code = funcName + '(' + args.join(', ') + ')\n'; diff --git a/generators/python/text.js b/generators/python/text.js index 3ae0ffc8e..84e19872a 100644 --- a/generators/python/text.js +++ b/generators/python/text.js @@ -38,32 +38,35 @@ Blockly.Python['text'] = function(block) { Blockly.Python['text_join'] = function(block) { // Create a string made up of any number of elements of any type. //Should we allow joining by '-' or ',' or any other characters? - var code; - if (block.itemCount_ == 0) { - return ['\'\'', Blockly.Python.ORDER_ATOMIC]; - } else if (block.itemCount_ == 1) { - var argument0 = Blockly.Python.valueToCode(block, 'ADD0', - Blockly.Python.ORDER_NONE) || '\'\''; - code = 'str(' + argument0 + ')'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; - } else if (block.itemCount_ == 2) { - var argument0 = Blockly.Python.valueToCode(block, 'ADD0', - Blockly.Python.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Python.valueToCode(block, 'ADD1', - Blockly.Python.ORDER_NONE) || '\'\''; - var code = 'str(' + argument0 + ') + str(' + argument1 + ')'; - return [code, Blockly.Python.ORDER_UNARY_SIGN]; - } else { - var code = []; - for (var n = 0; n < block.itemCount_; n++) { - code[n] = Blockly.Python.valueToCode(block, 'ADD' + n, - Blockly.Python.ORDER_NONE) || '\'\''; - } - var tempVar = Blockly.Python.variableDB_.getDistinctName('temp_value', - Blockly.Variables.NAME_TYPE); - code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + - code.join(', ') + ']])'; - return [code, Blockly.Python.ORDER_FUNCTION_CALL]; + switch (block.itemCount_) { + case 0: + return ['\'\'', Blockly.Python.ORDER_ATOMIC]; + break; + case 1: + var element = Blockly.Python.valueToCode(block, 'ADD0', + Blockly.Python.ORDER_NONE) || '\'\''; + var code = 'str(' + element + ')'; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; + break; + case 2: + var element0 = Blockly.Python.valueToCode(block, 'ADD0', + Blockly.Python.ORDER_NONE) || '\'\''; + var element1 = Blockly.Python.valueToCode(block, 'ADD1', + Blockly.Python.ORDER_NONE) || '\'\''; + var code = 'str(' + element0 + ') + str(' + element1 + ')'; + return [code, Blockly.Python.ORDER_ADDITIVE]; + break; + default: + var elements = []; + for (var i = 0; i < block.itemCount_; i++) { + elements[i] = Blockly.Python.valueToCode(block, 'ADD' + i, + Blockly.Python.ORDER_NONE) || '\'\''; + } + var tempVar = Blockly.Python.variableDB_.getDistinctName('x', + Blockly.Variables.NAME_TYPE); + var code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + + elements.join(', ') + ']])'; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; } }; @@ -71,23 +74,23 @@ Blockly.Python['text_append'] = function(block) { // Append to a variable in place. var varName = Blockly.Python.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var value = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_NONE) || '\'\''; - return varName + ' = str(' + varName + ') + str(' + argument0 + ')\n'; + return varName + ' = str(' + varName + ') + str(' + value + ')\n'; }; Blockly.Python['text_length'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '\'\''; - return ['len(' + argument0 + ')', Blockly.Python.ORDER_FUNCTION_CALL]; + return ['len(' + text + ')', Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_isEmpty'] = function(block) { // Is the string null or array empty? - var argument0 = Blockly.Python.valueToCode(block, 'VALUE', + var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_NONE) || '\'\''; - var code = 'not len(' + argument0 + ')'; + var code = 'not len(' + text + ')'; return [code, Blockly.Python.ORDER_LOGICAL_NOT]; }; @@ -95,20 +98,21 @@ Blockly.Python['text_indexOf'] = function(block) { // Search the text for a substring. // Should we allow for non-case sensitive??? var operator = block.getFieldValue('END') == 'FIRST' ? 'find' : 'rfind'; - var argument0 = Blockly.Python.valueToCode(block, 'FIND', + var substring = Blockly.Python.valueToCode(block, 'FIND', Blockly.Python.ORDER_NONE) || '\'\''; - var argument1 = Blockly.Python.valueToCode(block, 'VALUE', + var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_MEMBER) || '\'\''; - var code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; - return [code, Blockly.Python.ORDER_ADDITIVE]; + var code = text + '.' + operator + '(' + substring + ')'; + if (Blockly.Python.ONE_BASED_INDEXING) { + return [code + ' + 1', Blockly.Python.ORDER_ADDITIVE]; + } + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_charAt'] = function(block) { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. var where = block.getFieldValue('WHERE') || 'FROM_START'; - var at = Blockly.Python.valueToCode(block, 'AT', - Blockly.Python.ORDER_UNARY_SIGN) || '1'; var text = Blockly.Python.valueToCode(block, 'VALUE', Blockly.Python.ORDER_MEMBER) || '\'\''; switch (where) { @@ -119,18 +123,12 @@ Blockly.Python['text_charAt'] = function(block) { var code = text + '[-1]'; return [code, Blockly.Python.ORDER_MEMBER]; case 'FROM_START': - // Blockly uses one-based indicies. - if (Blockly.isNumber(at)) { - // If the index is a naked number, decrement it right now. - at = parseInt(at, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at = 'int(' + at + ' - 1)'; - } + var at = Blockly.Python.getAdjustedInt(block, 'AT'); var code = text + '[' + at + ']'; return [code, Blockly.Python.ORDER_MEMBER]; case 'FROM_END': - var code = text + '[-' + at + ']'; + var at = Blockly.Python.getAdjustedInt(block, 'AT', 1, true); + var code = text + '[' + at + ']'; return [code, Blockly.Python.ORDER_MEMBER]; case 'RANDOM': Blockly.Python.definitions_['import_random'] = 'import random'; @@ -147,53 +145,46 @@ Blockly.Python['text_charAt'] = function(block) { Blockly.Python['text_getSubstring'] = function(block) { // Get substring. - var text = Blockly.Python.valueToCode(block, 'STRING', - Blockly.Python.ORDER_MEMBER) || '\'\''; var where1 = block.getFieldValue('WHERE1'); var where2 = block.getFieldValue('WHERE2'); - var at1 = Blockly.Python.valueToCode(block, 'AT1', - Blockly.Python.ORDER_ADDITIVE) || '1'; - var at2 = Blockly.Python.valueToCode(block, 'AT2', - Blockly.Python.ORDER_ADDITIVE) || '1'; - if (where1 == 'FIRST' || (where1 == 'FROM_START' && at1 == '1')) { - at1 = ''; - } else if (where1 == 'FROM_START') { - // Blockly uses one-based indicies. - if (Blockly.isNumber(at1)) { - // If the index is a naked number, decrement it right now. - at1 = parseInt(at1, 10) - 1; - } else { - // If the index is dynamic, decrement it in code. - at1 = 'int(' + at1 + ' - 1)'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at1)) { - at1 = -parseInt(at1, 10); - } else { - at1 = '-int(' + at1 + ')'; - } + var text = Blockly.Python.valueToCode(block, 'STRING', + Blockly.Python.ORDER_MEMBER) || '\'\''; + switch (where1) { + case 'FROM_START': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1'); + if (at1 == '0') { + at1 = ''; + } + break; + case 'FROM_END': + var at1 = Blockly.Python.getAdjustedInt(block, 'AT1', 1, true); + break; + case 'FIRST': + var at1 = ''; + break; + default: + throw 'Unhandled option (text_getSubstring)'; } - if (where2 == 'LAST' || (where2 == 'FROM_END' && at2 == '1')) { - at2 = ''; - } else if (where1 == 'FROM_START') { - if (Blockly.isNumber(at2)) { - at2 = parseInt(at2, 10); - } else { - at2 = 'int(' + at2 + ')'; - } - } else if (where1 == 'FROM_END') { - if (Blockly.isNumber(at2)) { - // If the index is a naked number, increment it right now. - at2 = 1 - parseInt(at2, 10); - if (at2 == 0) { + switch (where2) { + case 'FROM_START': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 1); + break; + case 'FROM_END': + var at2 = Blockly.Python.getAdjustedInt(block, 'AT2', 0, true); + // Ensure that if the result calculated is 0 that sub-sequence will + // include all elements as expected. + if (!Blockly.isNumber(String(at2))) { + Blockly.Python.definitions_['import_sys'] = 'import sys'; + at2 += ' or sys.maxsize'; + } else if (at2 == '0') { at2 = ''; } - } else { - // If the index is dynamic, increment it in code. - // Add special case for -0. - Blockly.Python.definitions_['import_sys'] = 'import sys'; - at2 = 'int(1 - ' + at2 + ') or sys.maxsize'; - } + break; + case 'LAST': + var at2 = ''; + break; + default: + throw 'Unhandled option (text_getSubstring)'; } var code = text + '[' + at1 + ' : ' + at2 + ']'; return [code, Blockly.Python.ORDER_MEMBER]; @@ -207,10 +198,10 @@ Blockly.Python['text_changeCase'] = function(block) { 'TITLECASE': '.title()' }; var operator = OPERATORS[block.getFieldValue('CASE')]; - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var text = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_MEMBER) || '\'\''; - var code = argument0 + operator; - return [code, Blockly.Python.ORDER_MEMBER]; + var code = text + operator; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_trim'] = function(block) { @@ -221,17 +212,17 @@ Blockly.Python['text_trim'] = function(block) { 'BOTH': '.strip()' }; var operator = OPERATORS[block.getFieldValue('MODE')]; - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var text = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_MEMBER) || '\'\''; - var code = argument0 + operator; - return [code, Blockly.Python.ORDER_MEMBER]; + var code = text + operator; + return [code, Blockly.Python.ORDER_FUNCTION_CALL]; }; Blockly.Python['text_print'] = function(block) { // Print statement. - var argument0 = Blockly.Python.valueToCode(block, 'TEXT', + var msg = Blockly.Python.valueToCode(block, 'TEXT', Blockly.Python.ORDER_NONE) || '\'\''; - return 'print(' + argument0 + ')\n'; + return 'print(' + msg + ')\n'; }; Blockly.Python['text_prompt_ext'] = function(block) { diff --git a/tests/generators/colour.xml b/tests/generators/colour.xml index 78ac58516..bae853d4d 100644 --- a/tests/generators/colour.xml +++ b/tests/generators/colour.xml @@ -3,7 +3,11 @@ test colour picker - static colour + + + static colour + + #ff6600 @@ -21,7 +25,11 @@ test rgb - from rgb + + + from rgb + + @@ -88,7 +96,11 @@ - test name + + + test name + + @@ -105,7 +117,11 @@ - test name + + + test name + + @@ -137,7 +153,11 @@ - test name + + + test name + + TRUE @@ -192,7 +212,11 @@ test blend - blend + + + blend + + diff --git a/tests/generators/functions.xml b/tests/generators/functions.xml index ce450b11a..b189ed355 100644 --- a/tests/generators/functions.xml +++ b/tests/generators/functions.xml @@ -8,7 +8,11 @@ - test recurse + + + test recurse + + @@ -53,7 +57,11 @@ - procedure with global + + + procedure with global + + proc z @@ -84,7 +92,11 @@ - procedure no return + + + procedure no return + + TRUE @@ -111,7 +123,11 @@ - procedure return + + + procedure return + + FALSE @@ -192,7 +208,11 @@ test function - function with arguments + + + function with arguments + + @@ -218,7 +238,11 @@ - function with side effect + + + function with side effect + + func z @@ -247,7 +271,11 @@ - function with global + + + function with global + + @@ -267,7 +295,11 @@ - function with scope + + + function with scope + + func a @@ -280,7 +312,11 @@ - function return + + + function return + + TRUE @@ -296,7 +332,11 @@ - function no return + + + function no return + + FALSE diff --git a/tests/generators/index.html b/tests/generators/index.html index 5ae75e553..a5003ca51 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -153,17 +153,23 @@ function toXml() { } function toJavaScript() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.JavaScript.ONE_BASED_INDEXING = oneBasedIndexing; var code = '\'use strict\';\n\n' code += Blockly.JavaScript.workspaceToCode(workspace); setOutput(code); } function toPython() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.Python.ONE_BASED_INDEXING = oneBasedIndexing; var code = Blockly.Python.workspaceToCode(workspace); setOutput(code); } function toPhp() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.PHP.ONE_BASED_INDEXING = oneBasedIndexing; var code = Blockly.PHP.workspaceToCode(workspace); setOutput(code); } @@ -174,6 +180,8 @@ function toLua() { } function toDart() { + var oneBasedIndexing = document.getElementById('indexing').checked; + Blockly.Dart.ONE_BASED_INDEXING = oneBasedIndexing; var code = Blockly.Dart.workspaceToCode(workspace); setOutput(code); } @@ -212,9 +220,22 @@ h1 {