From f0c837bb2d77d86efa4d091c20513e8fbfdabd7c Mon Sep 17 00:00:00 2001 From: daarond Date: Sat, 9 May 2015 16:52:36 -0500 Subject: [PATCH] starting php support addition --- generators/php.js | 193 ++++++++++++++++++++ generators/php/colour.js | 101 ++++++++++ generators/php/lists.js | 324 ++++++++++++++++++++++++++++++++ generators/php/logic.js | 126 +++++++++++++ generators/php/loops.js | 180 ++++++++++++++++++ generators/php/math.js | 345 +++++++++++++++++++++++++++++++++++ generators/php/procedures.js | 108 +++++++++++ generators/php/text.js | 247 +++++++++++++++++++++++++ generators/php/variables.js | 46 +++++ tests/playground.html | 11 ++ 10 files changed, 1681 insertions(+) create mode 100644 generators/php.js create mode 100644 generators/php/colour.js create mode 100644 generators/php/lists.js create mode 100644 generators/php/logic.js create mode 100644 generators/php/loops.js create mode 100644 generators/php/math.js create mode 100644 generators/php/procedures.js create mode 100644 generators/php/text.js create mode 100644 generators/php/variables.js diff --git a/generators/php.js b/generators/php.js new file mode 100644 index 000000000..cf6334138 --- /dev/null +++ b/generators/php.js @@ -0,0 +1,193 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Helper functions for generating PHP for blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP'); + +goog.require('Blockly.Generator'); + + +/** + * PHP code generator. + * @type !Blockly.Generator + */ +Blockly.PHP = new Blockly.Generator('PHP'); + +/** + * List of illegal variable names. + * This is not intended to be a security feature. Blockly is 100% client-side, + * so bypassing this list is trivial. This is intended to prevent users from + * accidentally clobbering a built-in object or function. + * @private + */ +Blockly.PHP.addReservedWords( + 'Blockly,' + // In case JS is evaled in the current window. + // 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,' + + // 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__'); + // there are more than 9,000 internal functions in http://php.net/manual/en/indexes.functions.php + // do we really need to list them here? + +/** + * Order of operation ENUMs. + * https://developer.mozilla.org/en/PHP/Reference/Operators/Operator_Precedence + */ +Blockly.PHP.ORDER_ATOMIC = 0; // 0 "" ... +Blockly.PHP.ORDER_CLONE = 1; // clone +Blockly.PHP.ORDER_NEW = 1; // new +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_TYPEOF = 4; // typeof +Blockly.PHP.ORDER_VOID = 4; // void +Blockly.PHP.ORDER_DELETE = 4; // delete +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; // (...) + +/** + * Initialise the database of variable names. + * @param {!Blockly.Workspace} workspace Workspace to generate code from. + */ +Blockly.PHP.init = function(workspace) { + // Create a dictionary of definitions to be printed before the code. + Blockly.PHP.definitions_ = Object.create(null); + // Create a dictionary mapping desired function names in definitions_ + // to actual function names (to avoid collisions with user functions). + Blockly.PHP.functionNames_ = Object.create(null); + + if (!Blockly.PHP.variableDB_) { + Blockly.PHP.variableDB_ = + new Blockly.Names(Blockly.PHP.RESERVED_WORDS_); + } else { + Blockly.PHP.variableDB_.reset(); + } + + var defvars = []; + var variables = Blockly.Variables.allVariables(workspace); + for (var x = 0; x < variables.length; x++) { + defvars[x] = 'var ' + + Blockly.PHP.variableDB_.getName(variables[x], + Blockly.Variables.NAME_TYPE) + ';'; + } + Blockly.PHP.definitions_['variables'] = defvars.join('\n'); +}; + +/** + * Prepend the generated code with the variable definitions. + * @param {string} code Generated code. + * @return {string} Completed code. + */ +Blockly.PHP.finish = function(code) { + // Convert the definitions dictionary into a list. + var definitions = []; + for (var name in Blockly.PHP.definitions_) { + definitions.push(Blockly.PHP.definitions_[name]); + } + return definitions.join('\n\n') + '\n\n\n' + code; +}; + +/** + * Naked values are top-level blocks with outputs that aren't plugged into + * anything. A trailing semicolon is needed to make this legal. + * @param {string} line Line of generated code. + * @return {string} Legal line of code. + */ +Blockly.PHP.scrubNakedValue = function(line) { + return line + ';\n'; +}; + +/** + * Encode a string as a properly escaped PHP string, complete with + * quotes. + * @param {string} string Text to encode. + * @return {string} PHP string. + * @private + */ +Blockly.PHP.quote_ = function(string) { + // TODO: This is a quick hack. Replace with goog.string.quote + string = string.replace(/\\/g, '\\\\') + .replace(/\n/g, '\\\n') + .replace(/'/g, '\\\''); + return '\'' + string + '\''; +}; + +/** + * Common tasks for generating PHP from blocks. + * Handles comments for the specified block and any connected value blocks. + * Calls any statements following this block. + * @param {!Blockly.Block} block The current block. + * @param {string} code The PHP code created for this block. + * @return {string} PHP code with comments and subsequent blocks added. + * @private + */ +Blockly.PHP.scrub_ = function(block, code) { + var commentCode = ''; + // Only collect comments for blocks that aren't inline. + if (!block.outputConnection || !block.outputConnection.targetConnection) { + // Collect comment for this block. + var comment = block.getCommentText(); + if (comment) { + commentCode += Blockly.PHP.prefixLines(comment, '// ') + '\n'; + } + // 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(); + if (childBlock) { + var comment = Blockly.PHP.allNestedComments(childBlock); + if (comment) { + commentCode += Blockly.PHP.prefixLines(comment, '// '); + } + } + } + } + } + var nextBlock = block.nextConnection && block.nextConnection.targetBlock(); + var nextCode = Blockly.PHP.blockToCode(nextBlock); + return commentCode + code + nextCode; +}; diff --git a/generators/php/colour.js b/generators/php/colour.js new file mode 100644 index 000000000..341690d3f --- /dev/null +++ b/generators/php/colour.js @@ -0,0 +1,101 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for colour blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.colour'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['colour_picker'] = function(block) { + // Colour picker. + var code = '\'' + block.getFieldValue('COLOUR') + '\''; + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; + +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);', + '}']); + var code = functionName + '()'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['colour_rgb'] = function(block) { + // Compose a colour from RGB components expressed as percentages. + var red = Blockly.PHP.valueToCode(block, 'RED', + Blockly.PHP.ORDER_COMMA) || 0; + var green = Blockly.PHP.valueToCode(block, 'GREEN', + Blockly.PHP.ORDER_COMMA) || 0; + var blue = Blockly.PHP.valueToCode(block, 'BLUE', + Blockly.PHP.ORDER_COMMA) || 0; + var functionName = Blockly.PHP.provideFunction_( + 'colour_rgb', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($r, $g, $b) {', + ' $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]; +}; + +Blockly.PHP['colour_blend'] = function(block) { + // Blend two colours together. + var c1 = Blockly.PHP.valueToCode(block, 'COLOUR1', + Blockly.PHP.ORDER_COMMA) || '\'#000000\''; + var c2 = Blockly.PHP.valueToCode(block, 'COLOUR2', + Blockly.PHP.ORDER_COMMA) || '\'#000000\''; + var ratio = Blockly.PHP.valueToCode(block, 'RATIO', + Blockly.PHP.ORDER_COMMA) || 0.5; + var functionName = Blockly.PHP.provideFunction_( + 'colour_blend', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($c1, $c2, $ratio) {', + ' $ratio = max(min($ratio, 1), 0);', + ' $r1 = hexdec(substr($c1,0,2));', + ' $g1 = hexdec(substr($c1,2,2));', + ' $b1 = hexdec(substr($c1,4,2));', + ' $r2 = hexdec(substr($c2,0,2));', + ' $g2 = hexdec(substr($c2,2,2));', + ' $b2 = hexdec(substr($c2,4,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 new file mode 100644 index 000000000..8d66fd969 --- /dev/null +++ b/generators/php/lists.js @@ -0,0 +1,324 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for list blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.lists'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['lists_create_empty'] = function(block) { + // Create an empty list. + return ['array()', Blockly.PHP.ORDER_ATOMIC]; +}; + +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, + Blockly.PHP.ORDER_COMMA) || 'null'; + } + code = 'array(' + code.join(', ') + ')'; + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; + +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_ + + '($value, $n) {', + ' $array = array();', + ' for ($i = 0; $i < $n; $i++) {', + ' $array[i] = $value;', + ' }', + ' return $array;', + '}']); + var argument0 = Blockly.PHP.valueToCode(block, 'ITEM', + Blockly.PHP.ORDER_COMMA) || 'null'; + var argument1 = Blockly.PHP.valueToCode(block, 'NUM', + Blockly.PHP.ORDER_COMMA) || '0'; + var code = functionName + '($' + argument0 + ', $' + argument1 + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['lists_length'] = function(block) { + // List length. + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_FUNCTION_CALL) || '[]'; + return ['count(' + argument0 + ')', Blockly.PHP.ORDER_MEMBER]; +}; + +Blockly.PHP['lists_isEmpty'] = function(block) { + // Is the list empty? + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || '[]'; + return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_LOGICAL_NOT]; +}; + +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 code = argument1 + '.' + operator + '(' + argument0 + ') + 1'; + return [code, Blockly.PHP.ORDER_MEMBER]; +}; + +Blockly.PHP['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.PHP.valueToCode(block, 'AT', + Blockly.PHP.ORDER_UNARY_NEGATION) || '1'; + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || '[]'; + + if (where == 'FIRST') { + if (mode == 'GET') { + var code = list + '[0]'; + return [code, Blockly.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.shift()'; + return [code, Blockly.PHP.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.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.pop()'; + return [code, Blockly.PHP.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.PHP.ORDER_MEMBER]; + } else if (mode == 'GET_REMOVE') { + var code = list + '.splice(' + at + ', 1)[0]'; + return [code, Blockly.PHP.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.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_remove_from_end', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '(list, x) {', + ' x = list.length - x;', + ' return list.splice(x, 1)[0];', + '}']); + code = functionName + '(' + list + ', ' + at + ')'; + if (mode == 'GET_REMOVE') { + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + ';\n'; + } + } + } else if (where == 'RANDOM') { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_random_item', + [ 'function ' + Blockly.PHP.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.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + ';\n'; + } + } + throw 'Unhandled combination (lists_getIndex).'; +}; + +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) || '[]'; + 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+$/)) { + return ''; + } + var listVar = Blockly.PHP.variableDB_.getDistinctName( + 'tmp_list', 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') { + 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.PHP.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; + } + } + throw 'Unhandled combination (lists_setIndex).'; +}; + +Blockly.PHP['lists_getSublist'] = function(block) { + // Get sublist. + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || '[]'; + 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 + '.concat()'; + } else { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_sublist', + [ 'function ' + Blockly.PHP.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);', + '}']); + var code = functionName + '(' + list + ', \'' + + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +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); + var value_delim = Blockly.PHP.valueToCode(block, 'DELIM', + Blockly.PHP.ORDER_NONE) || '\'\''; + var mode = block.getFieldValue('MODE'); + if (mode == 'SPLIT') { + if (!value_input) { + value_input = '\'\''; + } + var functionName = 'split'; + } else if (mode == 'JOIN') { + if (!value_input) { + value_input = '[]'; + } + var functionName = 'join'; + } else { + throw 'Unknown mode: ' + mode; + } + var code = value_input + '.' + functionName + '(' + value_delim + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; diff --git a/generators/php/logic.js b/generators/php/logic.js new file mode 100644 index 000000000..b9e3c8f27 --- /dev/null +++ b/generators/php/logic.js @@ -0,0 +1,126 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for logic blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.logic'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['controls_if'] = function(block) { + // If/elseif/else condition. + var n = 0; + var argument = Blockly.PHP.valueToCode(block, 'IF' + n, + Blockly.PHP.ORDER_NONE) || 'false'; + var branch = Blockly.PHP.statementToCode(block, 'DO' + n); + var code = 'if (' + argument + ') {\n' + branch + '}'; + for (n = 1; n <= block.elseifCount_; n++) { + argument = Blockly.PHP.valueToCode(block, 'IF' + n, + Blockly.PHP.ORDER_NONE) || 'false'; + branch = Blockly.PHP.statementToCode(block, 'DO' + n); + code += ' else if (' + argument + ') {\n' + branch + '}'; + } + if (block.elseCount_) { + branch = Blockly.PHP.statementToCode(block, 'ELSE'); + code += ' else {\n' + branch + '}'; + } + return code + '\n'; +}; + +Blockly.PHP['logic_compare'] = function(block) { + // Comparison operator. + var OPERATORS = { + 'EQ': '==', + 'NEQ': '!=', + 'LT': '<', + 'LTE': '<=', + 'GT': '>', + 'GTE': '>=' + }; + var operator = OPERATORS[block.getFieldValue('OP')]; + var order = (operator == '==' || operator == '!=') ? + Blockly.PHP.ORDER_EQUALITY : Blockly.PHP.ORDER_RELATIONAL; + var argument0 = Blockly.PHP.valueToCode(block, 'A', order) || '0'; + var argument1 = Blockly.PHP.valueToCode(block, 'B', order) || '0'; + var code = argument0 + ' ' + operator + ' ' + argument1; + return [code, order]; +}; + +Blockly.PHP['logic_operation'] = function(block) { + // Operations 'and', 'or'. + var operator = (block.getFieldValue('OP') == 'AND') ? '&&' : '||'; + var order = (operator == '&&') ? Blockly.PHP.ORDER_LOGICAL_AND : + Blockly.PHP.ORDER_LOGICAL_OR; + var argument0 = Blockly.PHP.valueToCode(block, 'A', order); + var argument1 = Blockly.PHP.valueToCode(block, 'B', order); + if (!argument0 && !argument1) { + // If there are no arguments, then the return value is false. + argument0 = 'false'; + argument1 = 'false'; + } else { + // Single missing arguments have no effect on the return value. + var defaultArgument = (operator == '&&') ? 'true' : 'false'; + if (!argument0) { + argument0 = defaultArgument; + } + if (!argument1) { + argument1 = defaultArgument; + } + } + var code = argument0 + ' ' + operator + ' ' + argument1; + return [code, order]; +}; + +Blockly.PHP['logic_negate'] = function(block) { + // Negation. + var order = Blockly.PHP.ORDER_LOGICAL_NOT; + var argument0 = Blockly.PHP.valueToCode(block, 'BOOL', order) || + 'true'; + var code = '!' + argument0; + return [code, order]; +}; + +Blockly.PHP['logic_boolean'] = function(block) { + // Boolean values true and false. + var code = (block.getFieldValue('BOOL') == 'TRUE') ? 'true' : 'false'; + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; + +Blockly.PHP['logic_null'] = function(block) { + // Null data type. + return ['null', Blockly.PHP.ORDER_ATOMIC]; +}; + +Blockly.PHP['logic_ternary'] = function(block) { + // Ternary operator. + var value_if = Blockly.PHP.valueToCode(block, 'IF', + Blockly.PHP.ORDER_CONDITIONAL) || 'false'; + var value_then = Blockly.PHP.valueToCode(block, 'THEN', + Blockly.PHP.ORDER_CONDITIONAL) || 'null'; + var value_else = Blockly.PHP.valueToCode(block, 'ELSE', + Blockly.PHP.ORDER_CONDITIONAL) || 'null'; + var code = value_if + ' ? ' + value_then + ' : ' + value_else; + return [code, Blockly.PHP.ORDER_CONDITIONAL]; +}; diff --git a/generators/php/loops.js b/generators/php/loops.js new file mode 100644 index 000000000..4583e75a4 --- /dev/null +++ b/generators/php/loops.js @@ -0,0 +1,180 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for loop blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.loops'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['controls_repeat'] = function(block) { + // Repeat n times (internal number). + var repeats = Number(block.getFieldValue('TIMES')); + var branch = Blockly.PHP.statementToCode(block, 'DO'); + branch = Blockly.PHP.addLoopTrap(branch, block.id); + var loopVar = Blockly.PHP.variableDB_.getDistinctName( + 'count', Blockly.Variables.NAME_TYPE); + var code = 'for ($' + loopVar + ' = 0; $' + + loopVar + ' < ' + repeats + '; $' + + loopVar + '++) {\n' + + branch + '}\n'; + return code; +}; + +Blockly.PHP['controls_repeat_ext'] = function(block) { + // Repeat n times (external number). + var repeats = Blockly.PHP.valueToCode(block, 'TIMES', + Blockly.PHP.ORDER_ASSIGNMENT) || '0'; + var branch = Blockly.PHP.statementToCode(block, 'DO'); + branch = Blockly.PHP.addLoopTrap(branch, block.id); + var code = ''; + var loopVar = Blockly.PHP.variableDB_.getDistinctName( + 'count', Blockly.Variables.NAME_TYPE); + var endVar = repeats; + if (!repeats.match(/^\w+$/) && !Blockly.isNumber(repeats)) { + var endVar = Blockly.PHP.variableDB_.getDistinctName( + 'repeat_end', Blockly.Variables.NAME_TYPE); + code += 'var ' + endVar + ' = ' + repeats + ';\n'; + } + code += 'for ($' + loopVar + ' = 0; $' + + loopVar + ' < $' + endVar + '; $' + + loopVar + '++) {\n' + + branch + '}\n'; + return code; +}; + +Blockly.PHP['controls_whileUntil'] = function(block) { + // Do while/until loop. + var until = block.getFieldValue('MODE') == 'UNTIL'; + var argument0 = Blockly.PHP.valueToCode(block, 'BOOL', + until ? Blockly.PHP.ORDER_LOGICAL_NOT : + Blockly.PHP.ORDER_NONE) || 'false'; + var branch = Blockly.PHP.statementToCode(block, 'DO'); + branch = Blockly.PHP.addLoopTrap(branch, block.id); + if (until) { + argument0 = '!' + argument0; + } + return 'while (' + argument0 + ') {\n' + branch + '}\n'; +}; + +Blockly.PHP['controls_for'] = function(block) { + // For loop. + var variable0 = Blockly.PHP.variableDB_.getName( + block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + var argument0 = Blockly.PHP.valueToCode(block, 'FROM', + Blockly.PHP.ORDER_ASSIGNMENT) || '0'; + var argument1 = Blockly.PHP.valueToCode(block, 'TO', + Blockly.PHP.ORDER_ASSIGNMENT) || '0'; + var increment = Blockly.PHP.valueToCode(block, 'BY', + Blockly.PHP.ORDER_ASSIGNMENT) || '1'; + var branch = Blockly.PHP.statementToCode(block, 'DO'); + branch = Blockly.PHP.addLoopTrap(branch, block.id); + var code; + if (Blockly.isNumber(argument0) && Blockly.isNumber(argument1) && + Blockly.isNumber(increment)) { + // All arguments are simple numbers. + var up = parseFloat(argument0) <= parseFloat(argument1); + code = 'for ($' + variable0 + ' = ' + argument0 + '; $' + + variable0 + (up ? ' <= ' : ' >= ') + argument1 + '; $' + + variable0; + var step = Math.abs(parseFloat(increment)); + if (step == 1) { + code += up ? '++' : '--'; + } else { + code += (up ? ' += ' : ' -= ') + step; + } + code += ') {\n' + branch + '}\n'; + } else { + code = ''; + // Cache non-trivial values to variables to prevent repeated look-ups. + var startVar = argument0; + if (!argument0.match(/^\w+$/) && !Blockly.isNumber(argument0)) { + startVar = Blockly.PHP.variableDB_.getDistinctName( + variable0 + '_start', Blockly.Variables.NAME_TYPE); + code += '$' + startVar + ' = ' + argument0 + ';\n'; + } + var endVar = argument1; + if (!argument1.match(/^\w+$/) && !Blockly.isNumber(argument1)) { + var endVar = Blockly.PHP.variableDB_.getDistinctName( + variable0 + '_end', Blockly.Variables.NAME_TYPE); + code += '$' + endVar + ' = ' + argument1 + ';\n'; + } + // Determine loop direction at start, in case one of the bounds + // changes during loop execution. + var incVar = Blockly.PHP.variableDB_.getDistinctName( + variable0 + '_inc', Blockly.Variables.NAME_TYPE); + code += '$' + incVar + ' = '; + if (Blockly.isNumber(increment)) { + code += Math.abs(increment) + ';\n'; + } else { + code += 'abs(' + increment + ');\n'; + } + code += 'if ($' + startVar + ' > $' + endVar + ') {\n'; + code += Blockly.PHP.INDENT + '$' + incVar + ' = -$' + incVar + ';\n'; + code += '}\n'; + code += 'for (' + variable0 + ' = $' + startVar + ';\n' + + ' ' + incVar + ' >= 0 ? $' + + variable0 + ' <= ' + endVar + ' : $' + + variable0 + ' >= ' + endVar + ';\n' + + ' ' + variable0 + ' += $' + incVar + ') {\n' + + branch + '}\n'; + } + return code; +}; + +Blockly.PHP['controls_forEach'] = function(block) { + // For each loop. + var variable0 = Blockly.PHP.variableDB_.getName( + block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + var argument0 = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_ASSIGNMENT) || '[]'; + var branch = Blockly.PHP.statementToCode(block, 'DO'); + branch = Blockly.PHP.addLoopTrap(branch, block.id); + var code = ''; + // Cache non-trivial values to variables to prevent repeated look-ups. + var listVar = argument0; + if (!argument0.match(/^\w+$/)) { + listVar = Blockly.PHP.variableDB_.getDistinctName( + variable0 + '_list', Blockly.Variables.NAME_TYPE); + code += '$' + listVar + ' = ' + argument0 + ';\n'; + } + var indexVar = Blockly.PHP.variableDB_.getDistinctName( + variable0 + '_index', Blockly.Variables.NAME_TYPE); + branch = Blockly.PHP.INDENT + variable0 + ' = ' + + listVar + '[' + indexVar + '];\n' + branch; + code += 'foreach ($' + listVar + ' as $' + indexVar + ') {\n' + branch + '}\n'; + return code; +}; + +Blockly.PHP['controls_flow_statements'] = function(block) { + // Flow statements: continue, break. + switch (block.getFieldValue('FLOW')) { + case 'BREAK': + return 'break;\n'; + case 'CONTINUE': + return 'continue;\n'; + } + throw 'Unknown flow statement.'; +}; diff --git a/generators/php/math.js b/generators/php/math.js new file mode 100644 index 000000000..c808f1c23 --- /dev/null +++ b/generators/php/math.js @@ -0,0 +1,345 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for math blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.math'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['math_number'] = function(block) { + // Numeric value. + var code = parseFloat(block.getFieldValue('NUM')); + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; + +Blockly.PHP['math_arithmetic'] = function(block) { + // Basic arithmetic operators, and power. + var OPERATORS = { + 'ADD': [' + ', Blockly.PHP.ORDER_ADDITION], + 'MINUS': [' - ', Blockly.PHP.ORDER_SUBTRACTION], + 'MULTIPLY': [' * ', Blockly.PHP.ORDER_MULTIPLICATION], + 'DIVIDE': [' / ', Blockly.PHP.ORDER_DIVISION], + 'POWER': [null, Blockly.PHP.ORDER_COMMA] // Handle power separately. + }; + 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 = 'Math.pow(' + argument0 + ', ' + argument1 + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } + code = argument0 + operator + argument1; + return [code, order]; +}; + +Blockly.PHP['math_single'] = function(block) { + // Math operators with single operand. + var operator = block.getFieldValue('OP'); + var code; + var arg; + if (operator == 'NEG') { + // Negation is a special case given its different operator precedence. + arg = Blockly.PHP.valueToCode(block, 'NUM', + Blockly.PHP.ORDER_UNARY_NEGATION) || '0'; + if (arg[0] == '-') { + // --3 is not legal in JS. + arg = ' ' + arg; + } + code = '-' + arg; + return [code, Blockly.PHP.ORDER_UNARY_NEGATION]; + } + if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') { + arg = Blockly.PHP.valueToCode(block, 'NUM', + Blockly.PHP.ORDER_DIVISION) || '0'; + } else { + arg = Blockly.PHP.valueToCode(block, 'NUM', + Blockly.PHP.ORDER_NONE) || '0'; + } + // First, handle cases which generate values that don't need parentheses + // wrapping the code. + switch (operator) { + case 'ABS': + code = 'abs(' + arg + ')'; + break; + case 'ROOT': + code = 'sqrt(' + arg + ')'; + break; + case 'LN': + code = 'log(' + arg + ')'; + break; + case 'EXP': + code = 'exp(' + arg + ')'; + break; + case 'POW10': + code = 'pow(10,' + arg + ')'; + break; + case 'ROUND': + code = 'round(' + arg + ')'; + break; + case 'ROUNDUP': + code = 'ceil(' + arg + ')'; + break; + case 'ROUNDDOWN': + code = 'floor(' + arg + ')'; + break; + case 'SIN': + code = 'sin(' + arg + ' / 180 * pi())'; + break; + case 'COS': + code = 'cos(' + arg + ' / 180 * pi())'; + break; + case 'TAN': + code = 'tan(' + arg + ' / 180 * pi())'; + break; + } + if (code) { + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } + // Second, handle cases which generate values that may need parentheses + // wrapping the code. + switch (operator) { + case 'LOG10': + code = 'log(' + arg + ') / log(10)'; + break; + case 'ASIN': + code = 'asin(' + arg + ') / pi() * 180'; + break; + case 'ACOS': + code = 'acos(' + arg + ') / pi() * 180'; + break; + case 'ATAN': + code = 'atan(' + arg + ') / pi() * 180'; + break; + default: + throw 'Unknown math operator: ' + operator; + } + return [code, Blockly.PHP.ORDER_DIVISION]; +}; + +Blockly.PHP['math_constant'] = function(block) { + // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. + var CONSTANTS = { + 'PI': ['pi()', Blockly.PHP.ORDER_MEMBER], + 'E': ['exp', Blockly.PHP.ORDER_FUNCTION_CALL], + 'GOLDEN_RATIO': + ['(1 + sqrt(5)) / 2', Blockly.PHP.ORDER_DIVISION], + 'SQRT2': ['sqrt', Blockly.PHP.ORDER_MEMBER], + 'SQRT1_2': ['sqrt', Blockly.PHP.ORDER_MEMBER], + 'INFINITY': ['Infinity', Blockly.PHP.ORDER_ATOMIC] + }; + return CONSTANTS[block.getFieldValue('CONSTANT')]; +}; + +Blockly.PHP['math_number_property'] = function(block) { + // Check if a number is even, odd, prime, whole, positive, or negative + // or if it is divisible by certain number. Returns true or false. + var number_to_check = Blockly.PHP.valueToCode(block, 'NUMBER_TO_CHECK', + Blockly.PHP.ORDER_MODULUS) || '0'; + var dropdown_property = block.getFieldValue('PROPERTY'); + var code; + + switch (dropdown_property) { + case 'EVEN': + code = number_to_check + ' % 2 == 0'; + break; + case 'ODD': + code = number_to_check + ' % 2 == 1'; + break; + case 'PRIME': + code = 'mp_prob_prime('+number_to_check + ')'; + break; + case 'WHOLE': + code = number_to_check + ' % 1 == 0'; + break; + case 'POSITIVE': + code = number_to_check + ' > 0'; + break; + case 'NEGATIVE': + code = number_to_check + ' < 0'; + break; + case 'DIVISIBLE_BY': + var divisor = Blockly.PHP.valueToCode(block, 'DIVISOR', + Blockly.PHP.ORDER_MODULUS) || '0'; + code = number_to_check + ' % ' + divisor + ' == 0'; + break; + } + return [code, Blockly.PHP.ORDER_EQUALITY]; +}; + +Blockly.PHP['math_change'] = function(block) { + // Add to a variable in place. + var argument0 = Blockly.PHP.valueToCode(block, 'DELTA', + Blockly.PHP.ORDER_ADDITION) || '0'; + var varName = Blockly.PHP.variableDB_.getName( + block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + return varName + ' = (typeof ' + varName + ' == \'number\' ? ' + varName + + ' : 0) + ' + argument0 + ';\n'; +}; + +// Rounding functions have a single operand. +Blockly.PHP['math_round'] = Blockly.PHP['math_single']; +// Trigonometry functions have a single operand. +Blockly.PHP['math_trig'] = Blockly.PHP['math_single']; + +Blockly.PHP['math_on_list'] = function(block) { + // Math functions for lists. + var func = block.getFieldValue('OP'); + var list, code; + switch (func) { + case 'SUM': + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + code = 'array_sum(' + list + ')'; + break; + case 'MIN': + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + code = 'min(' + list + ')'; + break; + case 'MAX': + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_COMMA) || 'array()'; + code = 'max(' + list + ')'; + break; + case 'AVERAGE': + // math_median([null,null,1,3]) == 2.0. + var functionName = Blockly.PHP.provideFunction_( + 'math_mean', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($myList) {', + ' return array_sum($myList) / count($myList);', + '}']); + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_NONE) || 'array()'; + code = functionName + '(' + list + ')'; + break; + case 'MEDIAN': + // math_median([null,null,1,3]) == 2.0. + var functionName = Blockly.PHP.provideFunction_( + 'math_median', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($myList) {', + ' rsort($myList);', + ' $middle = round(count($myList) / 2);', + ' return $myList[$middle-1]; ', + '}']); + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_NONE) || '[]'; + code = functionName + '(' + list + ')'; + break; + case 'MODE': + // As a list of numbers can contain more than one mode, + // the returned result is provided as an array. + // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. + var functionName = Blockly.PHP.provideFunction_( + 'math_modes', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($values) {', + ' $v = array_count_values($values);', + ' arsort($v);', + ' foreach($v as $k => $v){$total = $k; break;}', + ' return $total;', + '}']); + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_NONE) || '[]'; + code = functionName + '(' + list + ')'; + break; + case 'STD_DEV': + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || 'array()'; + code = 'stats_standard_deviation(' + list + ')'; + break; + case 'RANDOM': + var functionName = Blockly.PHP.provideFunction_( + 'math_random_list', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list) {', + ' $x = floor(rand() * count($list));', + ' return $list[$x];', + '}']); + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_NONE) || '[]'; + code = functionName + '(' + list + ')'; + break; + default: + throw 'Unknown operator: ' + func; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['math_modulo'] = function(block) { + // Remainder computation. + var argument0 = Blockly.PHP.valueToCode(block, 'DIVIDEND', + Blockly.PHP.ORDER_MODULUS) || '0'; + var argument1 = Blockly.PHP.valueToCode(block, 'DIVISOR', + Blockly.PHP.ORDER_MODULUS) || '0'; + var code = argument0 + ' % ' + argument1; + return [code, Blockly.PHP.ORDER_MODULUS]; +}; + +Blockly.PHP['math_constrain'] = function(block) { + // Constrain a number between two limits. + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_COMMA) || '0'; + var argument1 = Blockly.PHP.valueToCode(block, 'LOW', + Blockly.PHP.ORDER_COMMA) || '0'; + var argument2 = Blockly.PHP.valueToCode(block, 'HIGH', + Blockly.PHP.ORDER_COMMA) || 'Infinity'; + var code = 'min(max(' + argument0 + ', ' + argument1 + '), ' + + argument2 + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['math_random_int'] = function(block) { + // Random integer between [X] and [Y]. + var argument0 = Blockly.PHP.valueToCode(block, 'FROM', + Blockly.PHP.ORDER_COMMA) || '0'; + var argument1 = Blockly.PHP.valueToCode(block, 'TO', + Blockly.PHP.ORDER_COMMA) || '0'; + var functionName = Blockly.PHP.provideFunction_( + 'math_random_int', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($a, $b) {', + ' if ($a > $b) {', + ' // Swap a and b to ensure a is smaller.', + ' $c = $a;', + ' $a = $b;', + ' $b = $c;', + ' }', + ' return rand($a, $b);', + '}']); + var code = functionName + '(' + argument0 + ', ' + argument1 + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['math_random_float'] = function(block) { + // Random fraction between 0 and 1. + return ['(float)rand()/(float)getrandmax()', Blockly.PHP.ORDER_FUNCTION_CALL]; +}; diff --git a/generators/php/procedures.js b/generators/php/procedures.js new file mode 100644 index 000000000..d532dde12 --- /dev/null +++ b/generators/php/procedures.js @@ -0,0 +1,108 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for procedure blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.procedures'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['procedures_defreturn'] = function(block) { + // Define a procedure with a return value. + var funcName = Blockly.PHP.variableDB_.getName( + block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); + var branch = Blockly.PHP.statementToCode(block, 'STACK'); + if (Blockly.PHP.STATEMENT_PREFIX) { + branch = Blockly.PHP.prefixLines( + Blockly.PHP.STATEMENT_PREFIX.replace(/%1/g, + '\'' + block.id + '\''), Blockly.PHP.INDENT) + branch; + } + if (Blockly.PHP.INFINITE_LOOP_TRAP) { + branch = Blockly.PHP.INFINITE_LOOP_TRAP.replace(/%1/g, + '\'' + block.id + '\'') + branch; + } + var returnValue = Blockly.PHP.valueToCode(block, 'RETURN', + Blockly.PHP.ORDER_NONE) || ''; + if (returnValue) { + returnValue = ' return ' + returnValue + ';\n'; + } + var args = []; + for (var x = 0; x < block.arguments_.length; x++) { + args[x] = Blockly.PHP.variableDB_.getName(block.arguments_[x], + Blockly.Variables.NAME_TYPE); + } + var code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + + branch + returnValue + '}'; + code = Blockly.PHP.scrub_(block, code); + Blockly.PHP.definitions_[funcName] = code; + return null; +}; + +// Defining a procedure without a return value uses the same generator as +// a procedure with a return value. +Blockly.PHP['procedures_defnoreturn'] = + Blockly.PHP['procedures_defreturn']; + +Blockly.PHP['procedures_callreturn'] = function(block) { + // Call a procedure with a return value. + 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, + Blockly.PHP.ORDER_COMMA) || 'null'; + } + var code = funcName + '(' + args.join(', ') + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['procedures_callnoreturn'] = function(block) { + // Call a procedure with no return value. + 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, + Blockly.PHP.ORDER_COMMA) || 'null'; + } + var code = funcName + '(' + args.join(', ') + ');\n'; + return code; +}; + +Blockly.PHP['procedures_ifreturn'] = function(block) { + // Conditionally return value from a procedure. + var condition = Blockly.PHP.valueToCode(block, 'CONDITION', + Blockly.PHP.ORDER_NONE) || 'false'; + var code = 'if (' + condition + ') {\n'; + if (block.hasReturnValue_) { + var value = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_NONE) || 'null'; + code += ' return ' + value + ';\n'; + } else { + code += ' return;\n'; + } + code += '}\n'; + return code; +}; diff --git a/generators/php/text.js b/generators/php/text.js new file mode 100644 index 000000000..6d4e69e52 --- /dev/null +++ b/generators/php/text.js @@ -0,0 +1,247 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for text blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.texts'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['text'] = function(block) { + // Text value. + var code = Blockly.PHP.quote_(block.getFieldValue('TEXT')); + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; + +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', + Blockly.PHP.ORDER_NONE) || '\'\''; + code = argument0; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (block.itemCount_ == 2) { + var argument0 = Blockly.PHP.valueToCode(block, 'ADD0', + Blockly.PHP.ORDER_NONE) || '\'\''; + var argument1 = Blockly.PHP.valueToCode(block, 'ADD1', + Blockly.PHP.ORDER_NONE) || '\'\''; + code = argument0 + ' . ' + argument1; + 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, + Blockly.PHP.ORDER_COMMA) || '\'\''; + } + code = 'implode(\',\'' + code.join(',') + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } +}; + +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 + ' = ' + varName + ' . ' + argument0 + ';\n'; +}; + +Blockly.PHP['text_length'] = function(block) { + // String length. + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + return ['strlen(' + argument0 + ')', Blockly.PHP.ORDER_MEMBER]; +}; + +Blockly.PHP['text_isEmpty'] = function(block) { + // Is the string null? + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_LOGICAL_NOT]; +}; + +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_NONE) || '\'\''; + var argument1 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + var code = operator + '(' + argument0 + ', ' + argument1 + ') + 1'; + return [code, Blockly.PHP.ORDER_MEMBER]; +}; + +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_UNARY_NEGATION) || '1'; + var text = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + switch (where) { + case 'FIRST': + var code = text + '[0]'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + case 'LAST': + var code = 'substr(' + text + ',-1,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 code = 'substr(' + text + ', ' + at + ', 1)'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + case 'FROM_END': + 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)];', + '}']); + code = functionName + '(' + text + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } + throw 'Unhandled option (text_charAt).'; +}; + +Blockly.PHP['text_getSubstring'] = function(block) { + // Get substring. + var text = Blockly.PHP.valueToCode(block, 'STRING', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + 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 = text; + } else { + var functionName = Blockly.PHP.provideFunction_( + 'text_get_substring', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($text, $where1, $at1, $where2, $at2) {', + ' if ($where1 == \'FROM_START\') {', + ' $at1--;', + ' } else if ($where1 == \'FROM_END\') {', + ' $at1 = strlen($text) - $at;', + ' } else if ($where1 == \'FIRST\') {', + ' $at1 = 0;', + ' } else if (where1 == \'LAST\') {', + ' $at1 = strlen($text) - 1;', + ' } else { $at1 = 0; }', + ' if ($where2 == \'FROM_START\') {', + ' $at2--;', + ' } else if ($where2 == \'FROM_END\') {', + ' $at2 = strlen($text) - $at;', + ' } else if ($where2 == \'FIRST\') {', + ' $at2 = 0;', + ' } else if (where2 == \'LAST\') {', + ' $at2 = strlen($text) - 1;', + ' } else { $at2 = 0; }', + ' $at1 = getAt($where1, $at1);', + ' $at2 = getAt($where2, $at2) + 1;', + ' return substr($text, $at1, $at2);', + '}']); + var code = functionName + '(' + text + ', \'' + + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['text_changeCase'] = function(block) { + // Change capitalization. + var code; + if (block.getFieldValue('CASE')=='UPPERCASE') { + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + code = 'strtoupper(' + argument0 + ')'; + } else if (block.getFieldValue('CASE')=='LOWERCASE') { + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + code = 'strtolower(' + argument0 + ')'; + } else if (block.getFieldValue('CASE')=='TITLECASE') { + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + code = 'ucwords(' + argument0 + ')'; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['text_trim'] = function(block) { + // Trim spaces. + var OPERATORS = { + 'LEFT': 'ltrim', + 'RIGHT': 'rtrim', + 'BOTH': 'trim' + }; + var operator = OPERATORS[block.getFieldValue('MODE')]; + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_MEMBER) || '\'\''; + return [ operator + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['text_print'] = function(block) { + // Print statement. + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_NONE) || '\'\''; + return 'print(' + argument0 + ');\n'; +}; + +Blockly.PHP['text_prompt'] = function(block) { + // Prompt function (internal message). + var msg = Blockly.PHP.quote_(block.getFieldValue('TEXT')); + var code = 'readline(' + msg + ')'; + var toNumber = block.getFieldValue('TYPE') == 'NUMBER'; + if (toNumber) { + code = 'floatval(' + code + ')'; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['text_prompt_ext'] = function(block) { + // Prompt function (external message). + var msg = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_NONE) || '\'\''; + var code = 'readline(' + msg + ')'; + var toNumber = block.getFieldValue('TYPE') == 'NUMBER'; + if (toNumber) { + code = 'floatval(' + code + ')'; + } + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; diff --git a/generators/php/variables.js b/generators/php/variables.js new file mode 100644 index 000000000..7857cf4b8 --- /dev/null +++ b/generators/php/variables.js @@ -0,0 +1,46 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Generating PHP for variable blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +goog.provide('Blockly.PHP.variables'); + +goog.require('Blockly.PHP'); + + +Blockly.PHP['variables_get'] = function(block) { + // Variable getter. + var code = Blockly.PHP.variableDB_.getName(block.getFieldValue('VAR'), + Blockly.Variables.NAME_TYPE); + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; + +Blockly.PHP['variables_set'] = function(block) { + // Variable setter. + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_ASSIGNMENT) || '0'; + var varName = Blockly.PHP.variableDB_.getName( + block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + return '$' + varName + ' = ' + argument0 + ';\n'; +}; diff --git a/tests/playground.html b/tests/playground.html index da90cdb1e..1f73b4cb3 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -13,6 +13,15 @@ + + + + + + + + + @@ -436,6 +445,8 @@ h1 {
  + +