diff --git a/appengine/README.txt b/appengine/README.txt index f91e2d86a..6ad341cb1 100644 --- a/appengine/README.txt +++ b/appengine/README.txt @@ -30,6 +30,7 @@ blockly/ |- blocks_compressed.js |- dart_compressed.js |- javascript_compressed.js + |- php_compressed.js `- python_compressed.js Instructions for fetching the optional Closure library may be found here: diff --git a/blockly_compressed.js b/blockly_compressed.js index f3da0649b..486ea4450 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -1204,8 +1204,9 @@ Blockly.Generator.prototype.valueToCode=function(a,b,c){if(isNaN(c))throw'Expect Blockly.Generator.prototype.statementToCode=function(a,b){var c=a.getInputTargetBlock(b),d=this.blockToCode(c);if(!goog.isString(d))throw'Expecting code from statement block "'+c.type+'".';d&&(d=this.prefixLines(d,this.INDENT));return d};Blockly.Generator.prototype.addLoopTrap=function(a,b){this.INFINITE_LOOP_TRAP&&(a=this.INFINITE_LOOP_TRAP.replace(/%1/g,"'"+b+"'")+a);this.STATEMENT_PREFIX&&(a+=this.prefixLines(this.STATEMENT_PREFIX.replace(/%1/g,"'"+b+"'"),this.INDENT));return a}; Blockly.Generator.prototype.INDENT=" ";Blockly.Generator.prototype.addReservedWords=function(a){this.RESERVED_WORDS_+=a+","};Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_="{leCUI8hutHZI4480Dc}";Blockly.Generator.prototype.provideFunction_=function(a,b){if(!this.definitions_[a]){var c=this.variableDB_.getDistinctName(a,this.NAME_TYPE);this.functionNames_[a]=c;this.definitions_[a]=b.join("\n").replace(this.FUNCTION_NAME_PLACEHOLDER_REGEXP_,c)}return this.functionNames_[a]}; // Copyright 2012 Google Inc. Apache License 2.0 -Blockly.Names=function(a){this.reservedDict_=Object.create(null);if(a){a=a.split(",");for(var b=0;bd?1:c + diff --git a/demos/code/code.js b/demos/code/code.js index 109378278..d0727fee3 100644 --- a/demos/code/code.js +++ b/demos/code/code.js @@ -243,7 +243,7 @@ Code.LANG = Code.getLang(); * List of tab names. * @private */ -Code.TABS_ = ['blocks', 'javascript', 'python', 'dart', 'xml']; +Code.TABS_ = ['blocks', 'javascript', 'php', 'python', 'dart', 'xml']; Code.selected = 'blocks'; @@ -324,6 +324,14 @@ Code.renderContent = function() { code = prettyPrintOne(code, 'py'); content.innerHTML = code; } + } else if (content.id == 'content_php') { + code = Blockly.PHP.workspaceToCode(Code.workspace); + content.textContent = code; + if (typeof prettyPrintOne == 'function') { + code = content.innerHTML; + code = prettyPrintOne(code, 'php'); + content.innerHTML = code; + } } else if (content.id == 'content_dart') { code = Blockly.Dart.workspaceToCode(Code.workspace); content.textContent = code; diff --git a/demos/code/index.html b/demos/code/index.html index 9b3b22c56..2a42aa617 100644 --- a/demos/code/index.html +++ b/demos/code/index.html @@ -10,6 +10,7 @@ + @@ -36,6 +37,8 @@   Python   + PHP +   Dart   XML @@ -61,6 +64,7 @@

+  

   

   

   
diff --git a/demos/index.html b/demos/index.html
index fafcb1963..8af600662 100644
--- a/demos/index.html
+++ b/demos/index.html
@@ -168,7 +168,7 @@
       
       
         
Code Editor
-
Export a Blockly program into JavaScript, Python, Dart or XML.
+
Export a Blockly program into JavaScript, Python, PHP, Dart or XML.
diff --git a/generators/php.js b/generators/php.js new file mode 100644 index 000000000..8155607b9 --- /dev/null +++ b/generators/php.js @@ -0,0 +1,187 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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( + // 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__'); + +/** + * 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; // (...) + +/** + * 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_, true); + } else { + Blockly.PHP.variableDB_.reset(); + } + + var defvars = []; + var variables = Blockly.Variables.allVariables(workspace); + for (var x = 0; x < variables.length; x++) { + defvars[x] = 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..f04cd0cd3 --- /dev/null +++ b/generators/php/colour.js @@ -0,0 +1,101 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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..78d15c71e --- /dev/null +++ b/generators/php/lists.js @@ -0,0 +1,371 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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, $count) {', + ' $array = array();', + ' for ($index = 0; $index < $count; $index++) {', + ' $array[] = $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) || 'array()'; + return ['count(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +Blockly.PHP['lists_isEmpty'] = function(block) { + // Is the list empty? + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; + return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +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'){ + // indexOf + 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;', + '}']); + } else { + // lastIndexOf + 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;', + '}']); + } + + var code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; +}; + +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'; + var list = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_FUNCTION_CALL) || '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') { + return [code, Blockly.PHP.ORDER_FUNCTION_CALL]; + } else if (mode == 'REMOVE') { + return code + ';\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'; + } + } + 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) || '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+$/)) { + return ''; + } + var listVar = Blockly.PHP.variableDB_.getDistinctName( + 'tmp_list', Blockly.Variables.NAME_TYPE); + var code = listVar + ' = &' + list + ';\n'; + 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; + } + } + throw 'Unhandled combination (lists_setIndex).'; +}; + +Blockly.PHP['lists_getSublist'] = function(block) { + // Get sublist. + var list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_MEMBER) || '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 { + var functionName = Blockly.PHP.provideFunction_( + 'lists_get_sublist', + [ '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);', + '}']); + 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 = 'explode'; + } else if (mode == 'JOIN') { + if (!value_input) { + value_input = 'array()'; + } + var functionName = 'implode'; + } else { + throw 'Unknown mode: ' + mode; + } + var code = functionName + '('+ value_delim + ', ' + value_input + ')'; + 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..59389239f --- /dev/null +++ b/generators/php/logic.js @@ -0,0 +1,126 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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..84ec10414 --- /dev/null +++ b/generators/php/loops.js @@ -0,0 +1,169 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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 += 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 = ''; + code += 'foreach (' + argument0 + ' as ' + variable0 + ') {\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..a10290445 --- /dev/null +++ b/generators/php/math.js @@ -0,0 +1,371 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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 = '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': ['M_PI', Blockly.PHP.ORDER_ATOMIC], + 'E': ['M_E', Blockly.PHP.ORDER_ATOMIC], + 'GOLDEN_RATIO': + ['(1 + sqrt(5)) / 2', Blockly.PHP.ORDER_DIVISION], + 'SQRT2': ['M_SQRT2', Blockly.PHP.ORDER_ATOMIC], + 'SQRT1_2': ['M_SQRT1_2', Blockly.PHP.ORDER_ATOMIC], + 'INFINITY': ['INF', 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; + if (dropdown_property == 'PRIME') { + // 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;', + '}']); + code = functionName + '(' + number_to_check + ')'; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } + switch (dropdown_property) { + case 'EVEN': + code = number_to_check + ' % 2 == 0'; + break; + case 'ODD': + code = number_to_check + ' % 2 == 1'; + break; + case 'WHOLE': + code = 'is_int(' + number_to_check + ')'; + 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 + ' += ' + 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_FUNCTION_CALL) || 'array()'; + code = 'array_sum(' + list + ')'; + break; + case 'MIN': + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; + code = 'min(' + list + ')'; + break; + case 'MAX': + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()'; + code = 'max(' + list + ')'; + break; + case 'AVERAGE': + 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': + var functionName = Blockly.PHP.provideFunction_( + 'math_median', + [ '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;', + '}']); + 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 array($total);', + '}']); + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_NONE) || '[]'; + code = functionName + '(' + list + ')'; + break; + 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));', + '}']); + list = Blockly.PHP.valueToCode(block, 'LIST', + Blockly.PHP.ORDER_NONE) || '[]'; + code = functionName + '(' + list + ')'; + break; + case 'RANDOM': + var functionName = Blockly.PHP.provideFunction_( + 'math_random_list', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($list) {', + ' $x = rand(0, count($list)-1);', + ' 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) {', + ' return rand($b, $a);', + ' }', + ' 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..75903e31c --- /dev/null +++ b/generators/php/procedures.js @@ -0,0 +1,122 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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. + // First, add a 'global' statement for every variable that is assigned. + var globals = Blockly.Variables.allVariables(block); + for (var i = globals.length - 1; i >= 0; i--) { + var varName = globals[i]; + if (block.arguments_.indexOf(varName) == -1) { + globals[i] = Blockly.PHP.variableDB_.getName(varName, + Blockly.Variables.NAME_TYPE); + } else { + // This variable is actually a parameter name. Do not include it in + // the list of globals, thus allowing it be of local scope. + globals.splice(i, 1); + } + } + globals = globals.length ? ' global ' + globals.join(', ') + ';\n' : ''; + + 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' + + globals + 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..1368713f4 --- /dev/null +++ b/generators/php/text.js @@ -0,0 +1,254 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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(\'\', array(' + 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 + ' .= ' + 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_FUNCTION_CALL]; +}; + +Blockly.PHP['text_isEmpty'] = function(block) { + // Is the string null? + var argument0 = Blockly.PHP.valueToCode(block, 'VALUE', + Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + return ['empty(' + argument0 + ')', 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 code = operator + '(' + argument1 + ', ' + argument0 + ') + 1'; + + var functionName = Blockly.PHP.provideFunction_( + block.getFieldValue('END')=='FIRST'?'text_indexOf':'text_lastIndexOf', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($text, $search) {', + ' $pos = ' + operator + '($text, $search);', + ' return $pos===false?0:$pos+1;', + '}']); + code = functionName + '(' + argument1 + ', ' + argument0 + ')'; + 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) || '\'\''; + 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_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 functionName = Blockly.PHP.provideFunction_( + 'text_get_substring', + [ '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);', + '}']); + 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_FUNCTION_CALL) || '\'\''; + code = 'strtoupper(' + argument0 + ')'; + } else if (block.getFieldValue('CASE')=='LOWERCASE') { + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + code = 'strtolower(' + argument0 + ')'; + } else if (block.getFieldValue('CASE')=='TITLECASE') { + var argument0 = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\''; + code = 'ucwords(strtolower(' + 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_ATOMIC) || '\'\''; + return [ operator + '(' + argument0 + ')', Blockly.PHP.ORDER_ATOMIC]; +}; + +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_ATOMIC]; +}; + +Blockly.PHP['text_prompt_ext'] = function(block) { + // Prompt function (external message). + var msg = Blockly.PHP.valueToCode(block, 'TEXT', + Blockly.PHP.ORDER_ATOMIC) || '\'\''; + var code = 'readline(' + msg + ')'; + var toNumber = block.getFieldValue('TYPE') == 'NUMBER'; + if (toNumber) { + code = 'floatval(' + code + ')'; + } + return [code, Blockly.PHP.ORDER_ATOMIC]; +}; diff --git a/generators/php/variables.js b/generators/php/variables.js new file mode 100644 index 000000000..dd68f4ead --- /dev/null +++ b/generators/php/variables.js @@ -0,0 +1,46 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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'; +}; \ No newline at end of file diff --git a/php_compressed.js b/php_compressed.js new file mode 100644 index 000000000..f5ee24777 --- /dev/null +++ b/php_compressed.js @@ -0,0 +1,89 @@ +// Do not edit this file; automatically generated by build.py. +"use strict"; + + +// Copyright 2012 Google Inc. Apache License 2.0 +Blockly.PHP=new Blockly.Generator("PHP");Blockly.PHP.addReservedWords("Blockly,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,enum,export,extends,import,super,implements,interface,let,package,private,protected,public,static,yield,const,null,true,false,Array,ArrayBuffer,Boolean,Date,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Error,eval,EvalError,Float32Array,Float64Array,Function,Infinity,Int16Array,Int32Array,Int8Array,isFinite,isNaN,Iterator,JSON,Math,NaN,Number,Object,parseFloat,parseInt,RangeError,ReferenceError,RegExp,StopIteration,String,SyntaxError,TypeError,Uint16Array,Uint32Array,Uint8Array,Uint8ClampedArray,undefined,uneval,URIError,applicationCache,closed,Components,content,_content,controllers,crypto,defaultStatus,dialogArguments,directories,document,frameElement,frames,fullScreen,globalStorage,history,innerHeight,innerWidth,length,location,locationbar,localStorage,menubar,messageManager,mozAnimationStartTime,mozInnerScreenX,mozInnerScreenY,mozPaintCount,name,navigator,opener,outerHeight,outerWidth,pageXOffset,pageYOffset,parent,performance,personalbar,pkcs11,returnValue,screen,screenX,screenY,scrollbars,scrollMaxX,scrollMaxY,scrollX,scrollY,self,sessionStorage,sidebar,status,statusbar,toolbar,top,URL,window,addEventListener,alert,atob,back,blur,btoa,captureEvents,clearImmediate,clearInterval,clearTimeout,close,confirm,disableExternalCapture,dispatchEvent,dump,enableExternalCapture,escape,find,focus,forward,GeckoActiveXObject,getAttention,getAttentionWithCycleCount,getComputedStyle,getSelection,home,matchMedia,maximize,minimize,moveBy,moveTo,mozRequestAnimationFrame,open,openDialog,postMessage,print,prompt,QueryInterface,releaseEvents,removeEventListener,resizeBy,resizeTo,restore,routeEvent,scroll,scrollBy,scrollByLines,scrollByPages,scrollTo,setCursor,setImmediate,setInterval,setResizable,setTimeout,showModalDialog,sizeToContent,stop,unescape,updateCommands,XPCNativeWrapper,XPCSafeJSObjectWrapper,onabort,onbeforeunload,onblur,onchange,onclick,onclose,oncontextmenu,ondevicemotion,ondeviceorientation,ondragdrop,onerror,onfocus,onhashchange,onkeydown,onkeypress,onkeyup,onload,onmousedown,onmousemove,onmouseout,onmouseover,onmouseup,onmozbeforepaint,onpaint,onpopstate,onreset,onresize,onscroll,onselect,onsubmit,onunload,onpageshow,onpagehide,Image,Option,Worker,Event,Range,File,FileReader,Blob,BlobBuilder,Attr,CDATASection,CharacterData,Comment,console,DocumentFragment,DocumentType,DomConfiguration,DOMError,DOMErrorHandler,DOMException,DOMImplementation,DOMImplementationList,DOMImplementationRegistry,DOMImplementationSource,DOMLocator,DOMObject,DOMString,DOMStringList,DOMTimeStamp,DOMUserData,Entity,EntityReference,MediaQueryList,MediaQueryListListener,NameList,NamedNodeMap,Node,NodeFilter,NodeIterator,NodeList,Notation,Plugin,PluginArray,ProcessingInstruction,SharedWorker,Text,TimeRanges,Treewalker,TypeInfo,UserDataHandler,Worker,WorkerGlobalScope,HTMLDocument,HTMLElement,HTMLAnchorElement,HTMLAppletElement,HTMLAudioElement,HTMLAreaElement,HTMLBaseElement,HTMLBaseFontElement,HTMLBodyElement,HTMLBRElement,HTMLButtonElement,HTMLCanvasElement,HTMLDirectoryElement,HTMLDivElement,HTMLDListElement,HTMLEmbedElement,HTMLFieldSetElement,HTMLFontElement,HTMLFormElement,HTMLFrameElement,HTMLFrameSetElement,HTMLHeadElement,HTMLHeadingElement,HTMLHtmlElement,HTMLHRElement,HTMLIFrameElement,HTMLImageElement,HTMLInputElement,HTMLKeygenElement,HTMLLabelElement,HTMLLIElement,HTMLLinkElement,HTMLMapElement,HTMLMenuElement,HTMLMetaElement,HTMLModElement,HTMLObjectElement,HTMLOListElement,HTMLOptGroupElement,HTMLOptionElement,HTMLOutputElement,HTMLParagraphElement,HTMLParamElement,HTMLPreElement,HTMLQuoteElement,HTMLScriptElement,HTMLSelectElement,HTMLSourceElement,HTMLSpanElement,HTMLStyleElement,HTMLTableElement,HTMLTableCaptionElement,HTMLTableCellElement,HTMLTableDataCellElement,HTMLTableHeaderCellElement,HTMLTableColElement,HTMLTableRowElement,HTMLTableSectionElement,HTMLTextAreaElement,HTMLTimeElement,HTMLTitleElement,HTMLTrackElement,HTMLUListElement,HTMLUnknownElement,HTMLVideoElement,HTMLCanvasElement,CanvasRenderingContext2D,CanvasGradient,CanvasPattern,TextMetrics,ImageData,CanvasPixelArray,HTMLAudioElement,HTMLVideoElement,NotifyAudioAvailableEvent,HTMLCollection,HTMLAllCollection,HTMLFormControlsCollection,HTMLOptionsCollection,HTMLPropertiesCollection,DOMTokenList,DOMSettableTokenList,DOMStringMap,RadioNodeList,SVGDocument,SVGElement,SVGAElement,SVGAltGlyphElement,SVGAltGlyphDefElement,SVGAltGlyphItemElement,SVGAnimationElement,SVGAnimateElement,SVGAnimateColorElement,SVGAnimateMotionElement,SVGAnimateTransformElement,SVGSetElement,SVGCircleElement,SVGClipPathElement,SVGColorProfileElement,SVGCursorElement,SVGDefsElement,SVGDescElement,SVGEllipseElement,SVGFilterElement,SVGFilterPrimitiveStandardAttributes,SVGFEBlendElement,SVGFEColorMatrixElement,SVGFEComponentTransferElement,SVGFECompositeElement,SVGFEConvolveMatrixElement,SVGFEDiffuseLightingElement,SVGFEDisplacementMapElement,SVGFEDistantLightElement,SVGFEFloodElement,SVGFEGaussianBlurElement,SVGFEImageElement,SVGFEMergeElement,SVGFEMergeNodeElement,SVGFEMorphologyElement,SVGFEOffsetElement,SVGFEPointLightElement,SVGFESpecularLightingElement,SVGFESpotLightElement,SVGFETileElement,SVGFETurbulenceElement,SVGComponentTransferFunctionElement,SVGFEFuncRElement,SVGFEFuncGElement,SVGFEFuncBElement,SVGFEFuncAElement,SVGFontElement,SVGFontFaceElement,SVGFontFaceFormatElement,SVGFontFaceNameElement,SVGFontFaceSrcElement,SVGFontFaceUriElement,SVGForeignObjectElement,SVGGElement,SVGGlyphElement,SVGGlyphRefElement,SVGGradientElement,SVGLinearGradientElement,SVGRadialGradientElement,SVGHKernElement,SVGImageElement,SVGLineElement,SVGMarkerElement,SVGMaskElement,SVGMetadataElement,SVGMissingGlyphElement,SVGMPathElement,SVGPathElement,SVGPatternElement,SVGPolylineElement,SVGPolygonElement,SVGRectElement,SVGScriptElement,SVGStopElement,SVGStyleElement,SVGSVGElement,SVGSwitchElement,SVGSymbolElement,SVGTextElement,SVGTextPathElement,SVGTitleElement,SVGTRefElement,SVGTSpanElement,SVGUseElement,SVGViewElement,SVGVKernElement,SVGAngle,SVGColor,SVGICCColor,SVGElementInstance,SVGElementInstanceList,SVGLength,SVGLengthList,SVGMatrix,SVGNumber,SVGNumberList,SVGPaint,SVGPoint,SVGPointList,SVGPreserveAspectRatio,SVGRect,SVGStringList,SVGTransform,SVGTransformList,SVGAnimatedAngle,SVGAnimatedBoolean,SVGAnimatedEnumeration,SVGAnimatedInteger,SVGAnimatedLength,SVGAnimatedLengthList,SVGAnimatedNumber,SVGAnimatedNumberList,SVGAnimatedPreserveAspectRatio,SVGAnimatedRect,SVGAnimatedString,SVGAnimatedTransformList,SVGPathSegList,SVGPathSeg,SVGPathSegArcAbs,SVGPathSegArcRel,SVGPathSegClosePath,SVGPathSegCurvetoCubicAbs,SVGPathSegCurvetoCubicRel,SVGPathSegCurvetoCubicSmoothAbs,SVGPathSegCurvetoCubicSmoothRel,SVGPathSegCurvetoQuadraticAbs,SVGPathSegCurvetoQuadraticRel,SVGPathSegCurvetoQuadraticSmoothAbs,SVGPathSegCurvetoQuadraticSmoothRel,SVGPathSegLinetoAbs,SVGPathSegLinetoHorizontalAbs,SVGPathSegLinetoHorizontalRel,SVGPathSegLinetoRel,SVGPathSegLinetoVerticalAbs,SVGPathSegLinetoVerticalRel,SVGPathSegMovetoAbs,SVGPathSegMovetoRel,ElementTimeControl,TimeEvent,SVGAnimatedPathData,SVGAnimatedPoints,SVGColorProfileRule,SVGCSSRule,SVGExternalResourcesRequired,SVGFitToViewBox,SVGLangSpace,SVGLocatable,SVGRenderingIntent,SVGStylable,SVGTests,SVGTextContentElement,SVGTextPositioningElement,SVGTransformable,SVGUnitTypes,SVGURIReference,SVGViewSpec,SVGZoomAndPan"); +Blockly.PHP.ORDER_ATOMIC=0;Blockly.PHP.ORDER_MEMBER=1;Blockly.PHP.ORDER_NEW=1;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;Blockly.PHP.ORDER_VOID=4;Blockly.PHP.ORDER_DELETE=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;Blockly.PHP.ORDER_INSTANCEOF=8;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_LOGICAL_AND=13;Blockly.PHP.ORDER_LOGICAL_OR=14;Blockly.PHP.ORDER_CONDITIONAL=15;Blockly.PHP.ORDER_ASSIGNMENT=16;Blockly.PHP.ORDER_COMMA=17;Blockly.PHP.ORDER_NONE=99; +Blockly.PHP.init=function(a){Blockly.PHP.definitions_=Object.create(null);Blockly.PHP.functionNames_=Object.create(null);Blockly.PHP.variableDB_?Blockly.PHP.variableDB_.reset():Blockly.PHP.variableDB_=new Blockly.Names(Blockly.PHP.RESERVED_WORDS_,!0);var b=[];a=Blockly.Variables.allVariables(a);for(var c=0;c",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.PHP.ORDER_EQUALITY:Blockly.PHP.ORDER_RELATIONAL,d=Blockly.PHP.valueToCode(a,"A",c)||"0";a=Blockly.PHP.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]}; +Blockly.PHP.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"&&":"||",c="&&"==b?Blockly.PHP.ORDER_LOGICAL_AND:Blockly.PHP.ORDER_LOGICAL_OR,d=Blockly.PHP.valueToCode(a,"A",c);a=Blockly.PHP.valueToCode(a,"B",c);if(d||a){var e="&&"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};Blockly.PHP.logic_negate=function(a){var b=Blockly.PHP.ORDER_LOGICAL_NOT;return["!"+(Blockly.PHP.valueToCode(a,"BOOL",b)||"true"),b]}; +Blockly.PHP.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"true":"false",Blockly.PHP.ORDER_ATOMIC]};Blockly.PHP.logic_null=function(a){return["null",Blockly.PHP.ORDER_ATOMIC]};Blockly.PHP.logic_ternary=function(a){var b=Blockly.PHP.valueToCode(a,"IF",Blockly.PHP.ORDER_CONDITIONAL)||"false",c=Blockly.PHP.valueToCode(a,"THEN",Blockly.PHP.ORDER_CONDITIONAL)||"null";a=Blockly.PHP.valueToCode(a,"ELSE",Blockly.PHP.ORDER_CONDITIONAL)||"null";return[b+" ? "+c+" : "+a,Blockly.PHP.ORDER_CONDITIONAL]}; +// Copyright 2015 Google Inc. Apache License 2.0 +Blockly.PHP.loops={};Blockly.PHP.controls_repeat=function(a){var b=Number(a.getFieldValue("TIMES")),c=Blockly.PHP.statementToCode(a,"DO"),c=Blockly.PHP.addLoopTrap(c,a.id);a=Blockly.PHP.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);return"for ("+a+" = 0; "+a+" < "+b+"; "+a+"++) {\n"+c+"}\n"}; +Blockly.PHP.controls_repeat_ext=function(a){var b=Blockly.PHP.valueToCode(a,"TIMES",Blockly.PHP.ORDER_ASSIGNMENT)||"0",c=Blockly.PHP.statementToCode(a,"DO"),c=Blockly.PHP.addLoopTrap(c,a.id);a="";var d=Blockly.PHP.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE),e=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(e=Blockly.PHP.variableDB_.getDistinctName("repeat_end",Blockly.Variables.NAME_TYPE),a+=e+" = "+b+";\n");return a+("for ("+d+" = 0; "+d+" < "+e+"; "+d+"++) {\n"+c+"}\n")}; +Blockly.PHP.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.PHP.valueToCode(a,"BOOL",b?Blockly.PHP.ORDER_LOGICAL_NOT:Blockly.PHP.ORDER_NONE)||"false",d=Blockly.PHP.statementToCode(a,"DO"),d=Blockly.PHP.addLoopTrap(d,a.id);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"}; +Blockly.PHP.controls_for=function(a){var b=Blockly.PHP.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.PHP.valueToCode(a,"FROM",Blockly.PHP.ORDER_ASSIGNMENT)||"0",d=Blockly.PHP.valueToCode(a,"TO",Blockly.PHP.ORDER_ASSIGNMENT)||"0",e=Blockly.PHP.valueToCode(a,"BY",Blockly.PHP.ORDER_ASSIGNMENT)||"1",f=Blockly.PHP.statementToCode(a,"DO"),f=Blockly.PHP.addLoopTrap(f,a.id);if(Blockly.isNumber(c)&&Blockly.isNumber(d)&&Blockly.isNumber(e)){var g=parseFloat(c)<=parseFloat(d); +a="for ("+b+" = "+c+"; "+b+(g?" <= ":" >= ")+d+"; "+b;b=Math.abs(parseFloat(e));a=(1==b?a+(g?"++":"--"):a+((g?" += ":" -= ")+b))+(") {\n"+f+"}\n")}else a="",g=c,c.match(/^\w+$/)||Blockly.isNumber(c)||(g=Blockly.PHP.variableDB_.getDistinctName(b+"_start",Blockly.Variables.NAME_TYPE),a+=g+" = "+c+";\n"),c=d,d.match(/^\w+$/)||Blockly.isNumber(d)||(c=Blockly.PHP.variableDB_.getDistinctName(b+"_end",Blockly.Variables.NAME_TYPE),a+=c+" = "+d+";\n"),d=Blockly.PHP.variableDB_.getDistinctName(b+"_inc",Blockly.Variables.NAME_TYPE), +a+=d+" = ",a=Blockly.isNumber(e)?a+(Math.abs(e)+";\n"):a+("abs("+e+");\n"),a+="if ("+g+" > "+c+") {\n",a+=Blockly.PHP.INDENT+d+" = -"+d+";\n",a+="}\n",a+="for ("+b+" = "+g+";\n "+d+" >= 0 ? "+b+" <= "+c+" : "+b+" >= "+c+";\n "+b+" += "+d+") {\n"+f+"}\n";return a}; +Blockly.PHP.controls_forEach=function(a){var b=Blockly.PHP.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_ASSIGNMENT)||"[]",d=Blockly.PHP.statementToCode(a,"DO"),d=Blockly.PHP.addLoopTrap(d,a.id);return""+("foreach ("+c+" as "+b+") {\n"+d+"}\n")}; +Blockly.PHP.controls_flow_statements=function(a){switch(a.getFieldValue("FLOW")){case "BREAK":return"break;\n";case "CONTINUE":return"continue;\n"}throw"Unknown flow statement.";}; +// Copyright 2015 Google Inc. Apache License 2.0 +Blockly.PHP.math={};Blockly.PHP.math_number=function(a){return[parseFloat(a.getFieldValue("NUM")),Blockly.PHP.ORDER_ATOMIC]}; +Blockly.PHP.math_arithmetic=function(a){var b={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]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.PHP.valueToCode(a,"A",b)||"0";a=Blockly.PHP.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:["pow("+d+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.math_single=function(a){var b=a.getFieldValue("OP"),c;if("NEG"==b)return a=Blockly.PHP.valueToCode(a,"NUM",Blockly.PHP.ORDER_UNARY_NEGATION)||"0","-"==a[0]&&(a=" "+a),["-"+a,Blockly.PHP.ORDER_UNARY_NEGATION];a="SIN"==b||"COS"==b||"TAN"==b?Blockly.PHP.valueToCode(a,"NUM",Blockly.PHP.ORDER_DIVISION)||"0":Blockly.PHP.valueToCode(a,"NUM",Blockly.PHP.ORDER_NONE)||"0";switch(b){case "ABS":c="abs("+a+")";break;case "ROOT":c="sqrt("+a+")";break;case "LN":c="log("+a+")";break;case "EXP":c="exp("+ +a+")";break;case "POW10":c="pow(10,"+a+")";break;case "ROUND":c="round("+a+")";break;case "ROUNDUP":c="ceil("+a+")";break;case "ROUNDDOWN":c="floor("+a+")";break;case "SIN":c="sin("+a+" / 180 * pi())";break;case "COS":c="cos("+a+" / 180 * pi())";break;case "TAN":c="tan("+a+" / 180 * pi())"}if(c)return[c,Blockly.PHP.ORDER_FUNCTION_CALL];switch(b){case "LOG10":c="log("+a+") / log(10)";break;case "ASIN":c="asin("+a+") / pi() * 180";break;case "ACOS":c="acos("+a+") / pi() * 180";break;case "ATAN":c="atan("+ +a+") / pi() * 180";break;default:throw"Unknown math operator: "+b;}return[c,Blockly.PHP.ORDER_DIVISION]};Blockly.PHP.math_constant=function(a){return{PI:["M_PI",Blockly.PHP.ORDER_ATOMIC],E:["M_E",Blockly.PHP.ORDER_ATOMIC],GOLDEN_RATIO:["(1 + sqrt(5)) / 2",Blockly.PHP.ORDER_DIVISION],SQRT2:["M_SQRT2",Blockly.PHP.ORDER_ATOMIC],SQRT1_2:["M_SQRT1_2",Blockly.PHP.ORDER_ATOMIC],INFINITY:["INF",Blockly.PHP.ORDER_ATOMIC]}[a.getFieldValue("CONSTANT")]}; +Blockly.PHP.math_number_property=function(a){var b=Blockly.PHP.valueToCode(a,"NUMBER_TO_CHECK",Blockly.PHP.ORDER_MODULUS)||"0",c=a.getFieldValue("PROPERTY"),d;if("PRIME"==c)return d=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;","}"])+"("+b+")",[d,Blockly.JavaScript.ORDER_FUNCTION_CALL];switch(c){case "EVEN":d=b+" % 2 == 0";break;case "ODD":d=b+" % 2 == 1";break;case "WHOLE":d="is_int("+b+")";break;case "POSITIVE":d= +b+" > 0";break;case "NEGATIVE":d=b+" < 0";break;case "DIVISIBLE_BY":a=Blockly.PHP.valueToCode(a,"DIVISOR",Blockly.PHP.ORDER_MODULUS)||"0",d=b+" % "+a+" == 0"}return[d,Blockly.PHP.ORDER_EQUALITY]};Blockly.PHP.math_change=function(a){var b=Blockly.PHP.valueToCode(a,"DELTA",Blockly.PHP.ORDER_ADDITION)||"0";return Blockly.PHP.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE)+" += "+b+";\n"};Blockly.PHP.math_round=Blockly.PHP.math_single;Blockly.PHP.math_trig=Blockly.PHP.math_single; +Blockly.PHP.math_on_list=function(a){var b=a.getFieldValue("OP");switch(b){case "SUM":a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_FUNCTION_CALL)||"array()";a="array_sum("+a+")";break;case "MIN":a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_FUNCTION_CALL)||"array()";a="min("+a+")";break;case "MAX":a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_FUNCTION_CALL)||"array()";a="max("+a+")";break;case "AVERAGE":b=Blockly.PHP.provideFunction_("math_mean",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+ +"($myList) {"," return array_sum($myList) / count($myList);","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"array()";a=b+"("+a+")";break;case "MEDIAN":b=Blockly.PHP.provideFunction_("math_median",["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;","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)|| +"[]";a=b+"("+a+")";break;case "MODE":b=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 array($total);","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "STD_DEV":b=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));","}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;case "RANDOM":b=Blockly.PHP.provideFunction_("math_random_list",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($list) {"," $x = rand(0, count($list)-1);"," return $list[$x];", +"}"]);a=Blockly.PHP.valueToCode(a,"LIST",Blockly.PHP.ORDER_NONE)||"[]";a=b+"("+a+")";break;default:throw"Unknown operator: "+b;}return[a,Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.math_modulo=function(a){var b=Blockly.PHP.valueToCode(a,"DIVIDEND",Blockly.PHP.ORDER_MODULUS)||"0";a=Blockly.PHP.valueToCode(a,"DIVISOR",Blockly.PHP.ORDER_MODULUS)||"0";return[b+" % "+a,Blockly.PHP.ORDER_MODULUS]}; +Blockly.PHP.math_constrain=function(a){var b=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_COMMA)||"0",c=Blockly.PHP.valueToCode(a,"LOW",Blockly.PHP.ORDER_COMMA)||"0";a=Blockly.PHP.valueToCode(a,"HIGH",Blockly.PHP.ORDER_COMMA)||"Infinity";return["min(max("+b+", "+c+"), "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.math_random_int=function(a){var b=Blockly.PHP.valueToCode(a,"FROM",Blockly.PHP.ORDER_COMMA)||"0";a=Blockly.PHP.valueToCode(a,"TO",Blockly.PHP.ORDER_COMMA)||"0";return[Blockly.PHP.provideFunction_("math_random_int",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($a, $b) {"," if ($a > $b) {"," return rand($b, $a);"," }"," return rand($a, $b);","}"])+"("+b+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; +Blockly.PHP.math_random_float=function(a){return["(float)rand()/(float)getrandmax()",Blockly.PHP.ORDER_FUNCTION_CALL]}; +// Copyright 2015 Google Inc. Apache License 2.0 +Blockly.PHP.procedures={}; +Blockly.PHP.procedures_defreturn=function(a){for(var b=Blockly.Variables.allVariables(a),c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.PHP.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}b=b.length?" global "+b.join(", ")+";\n":"";c=Blockly.PHP.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE);d=Blockly.PHP.statementToCode(a,"STACK");Blockly.PHP.STATEMENT_PREFIX&&(d=Blockly.PHP.prefixLines(Blockly.PHP.STATEMENT_PREFIX.replace(/%1/g,"'"+ +a.id+"'"),Blockly.PHP.INDENT)+d);Blockly.PHP.INFINITE_LOOP_TRAP&&(d=Blockly.PHP.INFINITE_LOOP_TRAP.replace(/%1/g,"'"+a.id+"'")+d);var e=Blockly.PHP.valueToCode(a,"RETURN",Blockly.PHP.ORDER_NONE)||"";e&&(e=" return "+e+";\n");for(var f=[],g=0;g + + + + + + + + + + + + @@ -126,6 +138,11 @@ function toPython() { setOutput(code); } +function toPhp() { + var code = Blockly.PHP.workspaceToCode(workspace); + setOutput(code); +} + function toDart() { var code = Blockly.Dart.workspaceToCode(workspace); setOutput(code); @@ -406,6 +423,7 @@ h1 { +

diff --git a/tests/generators/unittest_php.js b/tests/generators/unittest_php.js new file mode 100644 index 000000000..429831ae9 --- /dev/null +++ b/tests/generators/unittest_php.js @@ -0,0 +1,162 @@ +/** + * @license + * Visual Blocks Language + * + * Copyright 2015 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 unit test blocks. + * @author daarond@gmail.com (Daaron Dwyer) + */ +'use strict'; + +Blockly.PHP['unittest_main'] = function(block) { + // Container for unit tests. + var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', + Blockly.Variables.NAME_TYPE); + var functionName = Blockly.PHP.provideFunction_( + 'unittest_report', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', + 'global ' + resultsVar + ';', + ' // Create test report.', + ' $report = array();', + ' $summary = array();', + ' $fails = 0;', + ' for ($x = 0; $x < count(' + resultsVar + '); $x++) {', + ' if (' + resultsVar + '[$x][0]) {', + ' array_push($summary, ".");', + ' } else {', + ' array_push($summary, "F");', + ' $fails++;', + ' array_push($report,"");', + ' array_push($report, "FAIL: " . ' + resultsVar + '[$x][2]);', + ' array_push($report, ' + resultsVar + '[$x][1]);', + ' }', + ' }', + ' array_unshift($report, implode("",$summary));', + ' array_push($report, "");', + ' array_push($report, "Number of tests run: " . count(' + resultsVar + '));', + ' array_push($report, "");', + ' if ($fails) {', + ' array_push($report, "FAILED (failures=" . $fails + ")");', + ' } else {', + ' array_push($report, "OK");', + ' }', + ' return implode("\\n", $report);', + '}']); + // Setup global to hold test results. + var code = resultsVar + ' = array();\n'; + // Run tests (unindented). + code += Blockly.PHP.statementToCode(block, 'DO') + .replace(/^ /, '').replace(/\n /g, '\n'); + var reportVar = Blockly.PHP.variableDB_.getDistinctName( + 'report', Blockly.Variables.NAME_TYPE); + code += reportVar + ' = ' + functionName + '();\n'; + // Destroy results. + code += resultsVar + ' = null;\n'; + // Send the report to the console (that's where errors will go anyway). + code += 'print(' + reportVar + ');\n'; + return code; +}; + +Blockly.PHP['unittest_main'].defineAssert_ = function(block) { + var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', + Blockly.Variables.NAME_TYPE); + var functionName = Blockly.PHP.provideFunction_( + 'assertEquals', + [ ' function equals($a, $b) {', + ' if ($a === $b) {', + ' return true;', + ' } else if ((is_numeric($a)) && (is_numeric($b)) &&', + ' (round($a,15) == round($b,15))) {', + ' return true;', + ' } else if (is_array($a) && is_array($b)) {', + ' if (count($a) != count($b)) {', + ' return false;', + ' }', + ' for ($i = 0; $i < count($a); $i++) {', + ' if (!equals($a[$i], $b[$i])) {', + ' return false;', + ' }', + ' }', + ' return true;', + ' }', + ' return false;', + ' }', + 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($actual, $expected, $message) {', + 'global ' + resultsVar + ';', + ' // Asserts that a value equals another value.', + ' if (!is_array(' + resultsVar + ')) {', + ' throw new Exception("Orphaned assert: " . $message);', + ' }', + ' if (equals($actual, $expected)) {', + ' array_push(' + resultsVar + ', [true, "OK", $message]);', + ' } else {', + ' array_push(' + resultsVar + ', [false, ' + + '"Expected: " . $expected . "\\nActual: " . $actual, $message]);', + ' }', + '}']); + return functionName; +}; + +Blockly.PHP['unittest_assertequals'] = function(block) { + // Asserts that a value equals another value. + var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); + var actual = Blockly.PHP.valueToCode(block, 'ACTUAL', + Blockly.PHP.ORDER_COMMA) || 'null'; + var expected = Blockly.PHP.valueToCode(block, 'EXPECTED', + Blockly.PHP.ORDER_COMMA) || 'null'; + return Blockly.PHP['unittest_main'].defineAssert_() + + '(' + actual + ', ' + expected + ', ' + message + ');\n'; +}; + +Blockly.PHP['unittest_assertvalue'] = function(block) { + // Asserts that a value is true, false, or null. + var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); + var actual = Blockly.PHP.valueToCode(block, 'ACTUAL', + Blockly.PHP.ORDER_COMMA) || 'null'; + var expected = block.getFieldValue('EXPECTED'); + if (expected == 'TRUE') { + expected = 'true'; + } else if (expected == 'FALSE') { + expected = 'false'; + } else if (expected == 'NULL') { + expected = 'null'; + } + return Blockly.PHP['unittest_main'].defineAssert_() + + '(' + actual + ', ' + expected + ', ' + message + ');\n'; +}; + +Blockly.PHP['unittest_fail'] = function(block) { + // Always assert an error. + var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', + Blockly.Variables.NAME_TYPE); + var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); + var functionName = Blockly.PHP.provideFunction_( + 'unittest_fail', + [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + + '($message) {', + 'global ' + resultsVar + ';', + ' // Always assert an error.', + ' if (!' + resultsVar + ') {', + ' throw new Exception("Orphaned assert fail: " . $message);', + ' }', + ' array_push(' + resultsVar + ', [false, "Fail.", $message]);', + '}']); + return functionName + '(' + message + ');\n'; +}; diff --git a/tests/playground.html b/tests/playground.html index b42f4c27f..df1c5e993 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -13,6 +13,15 @@ + + + + + + + + + @@ -40,6 +49,7 @@ +