lists.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /**
  2. * @license
  3. * Visual Blocks Language
  4. *
  5. * Copyright 2015 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Generating PHP for list blocks.
  22. * @author daarond@gmail.com (Daaron Dwyer)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.PHP.lists');
  26. goog.require('Blockly.PHP');
  27. Blockly.PHP['lists_create_empty'] = function(block) {
  28. // Create an empty list.
  29. return ['array()', Blockly.PHP.ORDER_ATOMIC];
  30. };
  31. Blockly.PHP['lists_create_with'] = function(block) {
  32. // Create a list with any number of elements of any type.
  33. var code = new Array(block.itemCount_);
  34. for (var n = 0; n < block.itemCount_; n++) {
  35. code[n] = Blockly.PHP.valueToCode(block, 'ADD' + n,
  36. Blockly.PHP.ORDER_COMMA) || 'null';
  37. }
  38. code = 'array(' + code.join(', ') + ')';
  39. return [code, Blockly.PHP.ORDER_ATOMIC];
  40. };
  41. Blockly.PHP['lists_repeat'] = function(block) {
  42. // Create a list with one element repeated.
  43. var functionName = Blockly.PHP.provideFunction_(
  44. 'lists_repeat',
  45. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  46. '($value, $count) {',
  47. ' $array = array();',
  48. ' for ($index = 0; $index < $count; $index++) {',
  49. ' $array[] = $value;',
  50. ' }',
  51. ' return $array;',
  52. '}']);
  53. var argument0 = Blockly.PHP.valueToCode(block, 'ITEM',
  54. Blockly.PHP.ORDER_COMMA) || 'null';
  55. var argument1 = Blockly.PHP.valueToCode(block, 'NUM',
  56. Blockly.PHP.ORDER_COMMA) || '0';
  57. var code = functionName + '(' + argument0 + ', ' + argument1 + ')';
  58. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  59. };
  60. Blockly.PHP['lists_length'] = function(block) {
  61. // String or array length.
  62. var functionName = Blockly.PHP.provideFunction_(
  63. 'length',
  64. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {',
  65. ' if (is_string($value)) {',
  66. ' return strlen($value);',
  67. ' } else {',
  68. ' return count($value);',
  69. ' }',
  70. '}']);
  71. var argument0 = Blockly.PHP.valueToCode(block, 'VALUE',
  72. Blockly.PHP.ORDER_FUNCTION_CALL) || '\'\'';
  73. return [functionName + '(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL];
  74. };
  75. Blockly.PHP['lists_isEmpty'] = function(block) {
  76. // Is the string null or array empty?
  77. var argument0 = Blockly.PHP.valueToCode(block, 'VALUE',
  78. Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()';
  79. return ['empty(' + argument0 + ')', Blockly.PHP.ORDER_FUNCTION_CALL];
  80. };
  81. Blockly.PHP['lists_indexOf'] = function(block) {
  82. // Find an item in the list.
  83. var operator = block.getFieldValue('END') == 'FIRST' ?
  84. 'indexOf' : 'lastIndexOf';
  85. var argument0 = Blockly.PHP.valueToCode(block, 'FIND',
  86. Blockly.PHP.ORDER_NONE) || '\'\'';
  87. var argument1 = Blockly.PHP.valueToCode(block, 'VALUE',
  88. Blockly.PHP.ORDER_MEMBER) || '[]';
  89. var functionName;
  90. if (block.getFieldValue('END') == 'FIRST'){
  91. // indexOf
  92. functionName = Blockly.PHP.provideFunction_(
  93. 'indexOf',
  94. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  95. '($haystack, $needle) {',
  96. ' for ($index = 0; $index < count($haystack); $index++) {',
  97. ' if ($haystack[$index] == $needle) return $index+1;',
  98. ' }',
  99. ' return 0;',
  100. '}']);
  101. } else {
  102. // lastIndexOf
  103. functionName = Blockly.PHP.provideFunction_(
  104. 'lastIndexOf',
  105. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  106. '($haystack, $needle) {',
  107. ' $last = 0;',
  108. ' for ($index = 0; $index < count($haystack); $index++) {',
  109. ' if ($haystack[$index] == $needle) $last = $index+1;',
  110. ' }',
  111. ' return $last;',
  112. '}']);
  113. }
  114. var code = functionName + '(' + argument1 + ', ' + argument0 + ')';
  115. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  116. };
  117. Blockly.PHP['lists_getIndex'] = function(block) {
  118. // Get element at index.
  119. var mode = block.getFieldValue('MODE') || 'GET';
  120. var where = block.getFieldValue('WHERE') || 'FROM_START';
  121. var at = Blockly.PHP.valueToCode(block, 'AT',
  122. Blockly.PHP.ORDER_UNARY_NEGATION) || '1';
  123. var list = Blockly.PHP.valueToCode(block, 'VALUE',
  124. Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()';
  125. if (where == 'FIRST') {
  126. if (mode == 'GET') {
  127. var code = list + '[0]';
  128. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  129. } else if (mode == 'GET_REMOVE') {
  130. var code = 'array_shift(' + list + ')';
  131. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  132. } else if (mode == 'REMOVE') {
  133. return 'array_shift(' + list + ');\n';
  134. }
  135. } else if (where == 'LAST') {
  136. if (mode == 'GET') {
  137. var code = 'end(' + list + ')';
  138. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  139. } else if (mode == 'GET_REMOVE') {
  140. var code = 'array_pop(' + list + ')';
  141. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  142. } else if (mode == 'REMOVE') {
  143. return 'array_pop(' + list + ');\n';
  144. }
  145. } else if (where == 'FROM_START') {
  146. // Blockly uses one-based indicies.
  147. if (Blockly.isNumber(at)) {
  148. // If the index is a naked number, decrement it right now.
  149. at = parseFloat(at) - 1;
  150. } else {
  151. // If the index is dynamic, decrement it in code.
  152. at += ' - 1';
  153. }
  154. if (mode == 'GET') {
  155. var code = list + '[' + at + ']';
  156. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  157. } else if (mode == 'GET_REMOVE') {
  158. var code = 'array_splice(' + list + ', ' + at + ', 1)[0]';
  159. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  160. } else if (mode == 'REMOVE') {
  161. return 'array_splice(' + list + ', ' + at + ', 1);\n';
  162. }
  163. } else if (where == 'FROM_END') {
  164. if (mode == 'GET') {
  165. var code = 'array_slice(' + list + ', -' + at + ', 1)[0]';
  166. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  167. } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') {
  168. code = 'array_splice(' + list +
  169. ', count(' + list + ') - ' + at + ', 1)[0]';
  170. if (mode == 'GET_REMOVE') {
  171. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  172. } else if (mode == 'REMOVE') {
  173. return code + ';\n';
  174. }
  175. }
  176. } else if (where == 'RANDOM') {
  177. if (mode == 'GET'){
  178. var functionName = Blockly.PHP.provideFunction_(
  179. 'lists_get_random_item',
  180. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  181. '($list) {',
  182. ' return $list[rand(0,count($list)-1)];',
  183. '}']);
  184. code = functionName + '(' + list + ')';
  185. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  186. } else if (mode == 'GET_REMOVE'){
  187. var functionName = Blockly.PHP.provideFunction_(
  188. 'lists_get_remove_random_item',
  189. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  190. '(&$list) {',
  191. ' $x = rand(0,count($list)-1);',
  192. ' unset($list[$x]);',
  193. ' return array_values($list);',
  194. '}']);
  195. code = functionName + '(' + list + ')';
  196. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  197. } else if (mode == 'REMOVE') {
  198. var functionName = Blockly.PHP.provideFunction_(
  199. 'lists_remove_random_item',
  200. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  201. '(&$list) {',
  202. ' unset($list[rand(0,count($list)-1)]);',
  203. '}']);
  204. return functionName + '(' + list + ');\n';
  205. }
  206. }
  207. throw 'Unhandled combination (lists_getIndex).';
  208. };
  209. Blockly.PHP['lists_setIndex'] = function(block) {
  210. // Set element at index.
  211. // Note: Until February 2013 this block did not have MODE or WHERE inputs.
  212. var list = Blockly.PHP.valueToCode(block, 'LIST',
  213. Blockly.PHP.ORDER_MEMBER) || 'array()';
  214. var mode = block.getFieldValue('MODE') || 'GET';
  215. var where = block.getFieldValue('WHERE') || 'FROM_START';
  216. var at = Blockly.PHP.valueToCode(block, 'AT',
  217. Blockly.PHP.ORDER_NONE) || '1';
  218. var value = Blockly.PHP.valueToCode(block, 'TO',
  219. Blockly.PHP.ORDER_ASSIGNMENT) || 'null';
  220. // Cache non-trivial values to variables to prevent repeated look-ups.
  221. // Closure, which accesses and modifies 'list'.
  222. function cacheList() {
  223. if (list.match(/^\w+$/)) {
  224. return '';
  225. }
  226. var listVar = Blockly.PHP.variableDB_.getDistinctName(
  227. 'tmp_list', Blockly.Variables.NAME_TYPE);
  228. var code = listVar + ' = &' + list + ';\n';
  229. list = listVar;
  230. return code;
  231. }
  232. if (where == 'FIRST') {
  233. if (mode == 'SET') {
  234. return list + '[0] = ' + value + ';\n';
  235. } else if (mode == 'INSERT') {
  236. return 'array_unshift(' + list + ', ' + value + ');\n';
  237. }
  238. } else if (where == 'LAST') {
  239. if (mode == 'SET') {
  240. var functionName = Blockly.PHP.provideFunction_(
  241. 'lists_set_last_item',
  242. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  243. '(&$list, $value) {',
  244. ' $list[count($list) - 1] = $value;',
  245. '}']);
  246. return functionName + '(' + list + ', ' + value + ');\n';
  247. } else if (mode == 'INSERT') {
  248. return 'array_push(' + list + ', ' + value + ');\n';
  249. }
  250. } else if (where == 'FROM_START') {
  251. // Blockly uses one-based indicies.
  252. if (Blockly.isNumber(at)) {
  253. // If the index is a naked number, decrement it right now.
  254. at = parseFloat(at) - 1;
  255. } else {
  256. // If the index is dynamic, decrement it in code.
  257. at += ' - 1';
  258. }
  259. if (mode == 'SET') {
  260. return list + '[' + at + '] = ' + value + ';\n';
  261. } else if (mode == 'INSERT') {
  262. return 'array_splice(' + list + ', ' + at + ', 0, ' + value + ');\n';
  263. }
  264. } else if (where == 'FROM_END') {
  265. if (mode == 'SET') {
  266. var functionName = Blockly.PHP.provideFunction_(
  267. 'lists_set_from_end',
  268. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  269. '(&$list, $at, $value) {',
  270. ' $list[count($list) - $at] = $value;',
  271. '}']);
  272. return functionName + '(' + list + ', ' + at + ', ' + value + ');\n';
  273. } else if (mode == 'INSERT') {
  274. var functionName = Blockly.PHP.provideFunction_(
  275. 'lists_insert_from_end',
  276. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  277. '(&$list, $at, $value) {',
  278. ' return array_splice($list, count($list) - $at, 0, $value);',
  279. '}']);
  280. return functionName + '(' + list + ', ' + at + ', ' + value + ');\n';
  281. }
  282. } else if (where == 'RANDOM') {
  283. var code = cacheList();
  284. var xVar = Blockly.PHP.variableDB_.getDistinctName(
  285. 'tmp_x', Blockly.Variables.NAME_TYPE);
  286. code += xVar + ' = rand(0, count(' + list + ')-1);\n';
  287. if (mode == 'SET') {
  288. code += list + '[' + xVar + '] = ' + value + ';\n';
  289. return code;
  290. } else if (mode == 'INSERT') {
  291. code += 'array_splice(' + list + ', ' + xVar + ', 0, ' + value + ');\n';
  292. return code;
  293. }
  294. }
  295. throw 'Unhandled combination (lists_setIndex).';
  296. };
  297. Blockly.PHP['lists_getSublist'] = function(block) {
  298. // Get sublist.
  299. var list = Blockly.PHP.valueToCode(block, 'LIST',
  300. Blockly.PHP.ORDER_MEMBER) || 'array()';
  301. var where1 = block.getFieldValue('WHERE1');
  302. var where2 = block.getFieldValue('WHERE2');
  303. var at1 = Blockly.PHP.valueToCode(block, 'AT1',
  304. Blockly.PHP.ORDER_NONE) || '1';
  305. var at2 = Blockly.PHP.valueToCode(block, 'AT2',
  306. Blockly.PHP.ORDER_NONE) || '1';
  307. if (where1 == 'FIRST' && where2 == 'LAST') {
  308. var code = list;
  309. } else {
  310. var functionName = Blockly.PHP.provideFunction_(
  311. 'lists_get_sublist',
  312. [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  313. '($list, $where1, $at1, $where2, $at2) {',
  314. ' if ($where2 == \'FROM_START\') {',
  315. ' $at2--;',
  316. ' } else if ($where2 == \'FROM_END\') {',
  317. ' $at2 = $at2 - $at1;',
  318. ' } else if ($where2 == \'FIRST\') {',
  319. ' $at2 = 0;',
  320. ' } else if ($where2 == \'LAST\') {',
  321. ' $at2 = count($list);',
  322. ' } else {',
  323. ' throw \'Unhandled option (lists_getSublist).\';',
  324. ' }',
  325. ' if ($where1 == \'FROM_START\') {',
  326. ' $at1--;',
  327. ' } else if ($where1 == \'FROM_END\') {',
  328. ' $at1 = count($list) - $at1;',
  329. ' } else if ($where1 == \'FIRST\') {',
  330. ' $at1 = 0;',
  331. ' } else if ($where1 == \'LAST\') {',
  332. ' $at1 = count($list) - 1;',
  333. ' } else {',
  334. ' throw \'Unhandled option (lists_getSublist).\';',
  335. ' }',
  336. ' return array_slice($list, $at1, $at2);',
  337. '}']);
  338. var code = functionName + '(' + list + ', \'' +
  339. where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')';
  340. }
  341. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  342. };
  343. Blockly.PHP['lists_sort'] = function(block) {
  344. // Block for sorting a list.
  345. var listCode = Blockly.PHP.valueToCode(block, 'LIST',
  346. Blockly.PHP.ORDER_FUNCTION_CALL) || 'array()';
  347. var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1;
  348. var type = block.getFieldValue('TYPE');
  349. var functionName = Blockly.PHP.provideFunction_(
  350. 'lists_sort', [
  351. 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ +
  352. '($list, $type, $direction) {',
  353. ' $sortCmpFuncs = array(',
  354. ' "NUMERIC" => "strnatcasecmp",',
  355. ' "TEXT" => "strcmp",',
  356. ' "IGNORE_CASE" => "strcasecmp"',
  357. ' );',
  358. ' $sortCmp = $sortCmpFuncs[$type];',
  359. ' $list2 = $list;', // Clone list.
  360. ' usort($list2, $sortCmp);',
  361. ' if ($direction == -1) {',
  362. ' $list2 = array_reverse($list2);',
  363. ' }',
  364. ' return $list2;',
  365. '}']);
  366. var sortCode = functionName +
  367. '(' + listCode + ', "' + type + '", ' + direction + ')';
  368. return [sortCode, Blockly.PHP.ORDER_FUNCTION_CALL];
  369. };
  370. Blockly.PHP['lists_split'] = function(block) {
  371. // Block for splitting text into a list, or joining a list into text.
  372. var value_input = Blockly.PHP.valueToCode(block, 'INPUT',
  373. Blockly.PHP.ORDER_MEMBER);
  374. var value_delim = Blockly.PHP.valueToCode(block, 'DELIM',
  375. Blockly.PHP.ORDER_NONE) || '\'\'';
  376. var mode = block.getFieldValue('MODE');
  377. if (mode == 'SPLIT') {
  378. if (!value_input) {
  379. value_input = '\'\'';
  380. }
  381. var functionName = 'explode';
  382. } else if (mode == 'JOIN') {
  383. if (!value_input) {
  384. value_input = 'array()';
  385. }
  386. var functionName = 'implode';
  387. } else {
  388. throw 'Unknown mode: ' + mode;
  389. }
  390. var code = functionName + '(' + value_delim + ', ' + value_input + ')';
  391. return [code, Blockly.PHP.ORDER_FUNCTION_CALL];
  392. };