lists.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /**
  2. * @license
  3. * Visual Blocks Language
  4. *
  5. * Copyright 2014 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 Dart for list blocks.
  22. * @author fraser@google.com (Neil Fraser)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Dart.lists');
  26. goog.require('Blockly.Dart');
  27. Blockly.Dart.addReservedWords('Math');
  28. Blockly.Dart['lists_create_empty'] = function(block) {
  29. // Create an empty list.
  30. return ['[]', Blockly.Dart.ORDER_ATOMIC];
  31. };
  32. Blockly.Dart['lists_create_with'] = function(block) {
  33. // Create a list with any number of elements of any type.
  34. var elements = new Array(block.itemCount_);
  35. for (var i = 0; i < block.itemCount_; i++) {
  36. elements[i] = Blockly.Dart.valueToCode(block, 'ADD' + i,
  37. Blockly.Dart.ORDER_NONE) || 'null';
  38. }
  39. var code = '[' + elements.join(', ') + ']';
  40. return [code, Blockly.Dart.ORDER_ATOMIC];
  41. };
  42. Blockly.Dart['lists_repeat'] = function(block) {
  43. // Create a list with one element repeated.
  44. var element = Blockly.Dart.valueToCode(block, 'ITEM',
  45. Blockly.Dart.ORDER_NONE) || 'null';
  46. var repeatCount = Blockly.Dart.valueToCode(block, 'NUM',
  47. Blockly.Dart.ORDER_NONE) || '0';
  48. var code = 'new List.filled(' + repeatCount + ', ' + element + ')';
  49. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  50. };
  51. Blockly.Dart['lists_length'] = function(block) {
  52. // String or array length.
  53. var list = Blockly.Dart.valueToCode(block, 'VALUE',
  54. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  55. return [list + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX];
  56. };
  57. Blockly.Dart['lists_isEmpty'] = function(block) {
  58. // Is the string null or array empty?
  59. var list = Blockly.Dart.valueToCode(block, 'VALUE',
  60. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  61. return [list + '.isEmpty', Blockly.Dart.ORDER_UNARY_POSTFIX];
  62. };
  63. Blockly.Dart['lists_indexOf'] = function(block) {
  64. // Find an item in the list.
  65. var operator = block.getFieldValue('END') == 'FIRST' ?
  66. 'indexOf' : 'lastIndexOf';
  67. var item = Blockly.Dart.valueToCode(block, 'FIND',
  68. Blockly.Dart.ORDER_NONE) || '\'\'';
  69. var list = Blockly.Dart.valueToCode(block, 'VALUE',
  70. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  71. var code = list + '.' + operator + '(' + item + ')';
  72. if (Blockly.Dart.ONE_BASED_INDEXING) {
  73. return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE];
  74. }
  75. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  76. };
  77. Blockly.Dart['lists_getIndex'] = function(block) {
  78. // Get element at index.
  79. // Note: Until January 2013 this block did not have MODE or WHERE inputs.
  80. var mode = block.getFieldValue('MODE') || 'GET';
  81. var where = block.getFieldValue('WHERE') || 'FROM_START';
  82. var listOrder = (where == 'RANDOM' || where == 'FROM_END') ?
  83. Blockly.Dart.ORDER_NONE : Blockly.Dart.ORDER_UNARY_POSTFIX;
  84. var list = Blockly.Dart.valueToCode(block, 'VALUE', listOrder) || '[]';
  85. // Cache non-trivial values to variables to prevent repeated look-ups.
  86. // Closure, which accesses and modifies 'list'.
  87. function cacheList() {
  88. var listVar = Blockly.Dart.variableDB_.getDistinctName(
  89. 'tmp_list', Blockly.Variables.NAME_TYPE);
  90. var code = 'List ' + listVar + ' = ' + list + ';\n';
  91. list = listVar;
  92. return code;
  93. }
  94. // If `list` would be evaluated more than once (which is the case for
  95. // RANDOM REMOVE and FROM_END) and is non-trivial, make sure to access it
  96. // only once.
  97. if (((where == 'RANDOM' && mode == 'REMOVE') || where == 'FROM_END') &&
  98. !list.match(/^\w+$/)) {
  99. // `list` is an expression, so we may not evaluate it more than once.
  100. if (where == 'RANDOM') {
  101. Blockly.Dart.definitions_['import_dart_math'] =
  102. 'import \'dart:math\' as Math;';
  103. // We can use multiple statements.
  104. var code = cacheList();
  105. var xVar = Blockly.Dart.variableDB_.getDistinctName(
  106. 'tmp_x', Blockly.Variables.NAME_TYPE);
  107. code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list +
  108. '.length);\n';
  109. code += list + '.removeAt(' + xVar + ');\n';
  110. return code;
  111. } else { // where == 'FROM_END'
  112. if (mode == 'REMOVE') {
  113. // We can use multiple statements.
  114. var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false,
  115. Blockly.Dart.ORDER_ADDITIVE);
  116. var code = cacheList();
  117. code += list + '.removeAt(' + list + '.length' + ' - ' + at + ');\n';
  118. return code;
  119. } else if (mode == 'GET') {
  120. var at = Blockly.Dart.getAdjusted(block, 'AT', 1);
  121. // We need to create a procedure to avoid reevaluating values.
  122. var functionName = Blockly.Dart.provideFunction_(
  123. 'lists_get_from_end',
  124. ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  125. '(List my_list, num x) {',
  126. ' x = my_list.length - x;',
  127. ' return my_list[x];',
  128. '}']);
  129. var code = functionName + '(' + list + ', ' + at + ')';
  130. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  131. } else if (mode == 'GET_REMOVE') {
  132. var at = Blockly.Dart.getAdjusted(block, 'AT', 1);
  133. // We need to create a procedure to avoid reevaluating values.
  134. var functionName = Blockly.Dart.provideFunction_(
  135. 'lists_remove_from_end',
  136. ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  137. '(List my_list, num x) {',
  138. ' x = my_list.length - x;',
  139. ' return my_list.removeAt(x);',
  140. '}']);
  141. var code = functionName + '(' + list + ', ' + at + ')';
  142. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  143. }
  144. }
  145. } else {
  146. // Either `list` is a simple variable, or we only need to refer to `list`
  147. // once.
  148. switch (where) {
  149. case 'FIRST':
  150. if (mode == 'GET') {
  151. var code = list + '.first';
  152. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  153. } else if (mode == 'GET_REMOVE') {
  154. var code = list + '.removeAt(0)';
  155. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  156. } else if (mode == 'REMOVE') {
  157. return list + '.removeAt(0);\n';
  158. }
  159. break;
  160. case 'LAST':
  161. if (mode == 'GET') {
  162. var code = list + '.last';
  163. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  164. } else if (mode == 'GET_REMOVE') {
  165. var code = list + '.removeLast()';
  166. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  167. } else if (mode == 'REMOVE') {
  168. return list + '.removeLast();\n';
  169. }
  170. break;
  171. case 'FROM_START':
  172. var at = Blockly.Dart.getAdjusted(block, 'AT');
  173. if (mode == 'GET') {
  174. var code = list + '[' + at + ']';
  175. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  176. } else if (mode == 'GET_REMOVE') {
  177. var code = list + '.removeAt(' + at + ')';
  178. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  179. } else if (mode == 'REMOVE') {
  180. return list + '.removeAt(' + at + ');\n';
  181. }
  182. break;
  183. case 'FROM_END':
  184. var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false,
  185. Blockly.Dart.ORDER_ADDITIVE);
  186. if (mode == 'GET') {
  187. var code = list + '[' + list + '.length - ' + at + ']';
  188. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  189. } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') {
  190. var code = list + '.removeAt(' + list + '.length - ' + at + ')';
  191. if (mode == 'GET_REMOVE') {
  192. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  193. } else if (mode == 'REMOVE') {
  194. return code + ';\n';
  195. }
  196. }
  197. break;
  198. case 'RANDOM':
  199. Blockly.Dart.definitions_['import_dart_math'] =
  200. 'import \'dart:math\' as Math;';
  201. if (mode == 'REMOVE') {
  202. // We can use multiple statements.
  203. var xVar = Blockly.Dart.variableDB_.getDistinctName(
  204. 'tmp_x', Blockly.Variables.NAME_TYPE);
  205. var code = 'int ' + xVar + ' = new Math.Random().nextInt(' + list +
  206. '.length);\n';
  207. code += list + '.removeAt(' + xVar + ');\n';
  208. return code;
  209. } else if (mode == 'GET') {
  210. var functionName = Blockly.Dart.provideFunction_(
  211. 'lists_get_random_item',
  212. ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  213. '(List my_list) {',
  214. ' int x = new Math.Random().nextInt(my_list.length);',
  215. ' return my_list[x];',
  216. '}']);
  217. var code = functionName + '(' + list + ')';
  218. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  219. } else if (mode == 'GET_REMOVE') {
  220. var functionName = Blockly.Dart.provideFunction_(
  221. 'lists_remove_random_item',
  222. ['dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  223. '(List my_list) {',
  224. ' int x = new Math.Random().nextInt(my_list.length);',
  225. ' return my_list.removeAt(x);',
  226. '}']);
  227. var code = functionName + '(' + list + ')';
  228. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  229. }
  230. break;
  231. }
  232. }
  233. throw 'Unhandled combination (lists_getIndex).';
  234. };
  235. Blockly.Dart['lists_setIndex'] = function(block) {
  236. // Set element at index.
  237. // Note: Until February 2013 this block did not have MODE or WHERE inputs.
  238. var mode = block.getFieldValue('MODE') || 'GET';
  239. var where = block.getFieldValue('WHERE') || 'FROM_START';
  240. var list = Blockly.Dart.valueToCode(block, 'LIST',
  241. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  242. var value = Blockly.Dart.valueToCode(block, 'TO',
  243. Blockly.Dart.ORDER_ASSIGNMENT) || 'null';
  244. // Cache non-trivial values to variables to prevent repeated look-ups.
  245. // Closure, which accesses and modifies 'list'.
  246. function cacheList() {
  247. if (list.match(/^\w+$/)) {
  248. return '';
  249. }
  250. var listVar = Blockly.Dart.variableDB_.getDistinctName(
  251. 'tmp_list', Blockly.Variables.NAME_TYPE);
  252. var code = 'List ' + listVar + ' = ' + list + ';\n';
  253. list = listVar;
  254. return code;
  255. }
  256. switch (where) {
  257. case 'FIRST':
  258. if (mode == 'SET') {
  259. return list + '[0] = ' + value + ';\n';
  260. } else if (mode == 'INSERT') {
  261. return list + '.insert(0, ' + value + ');\n';
  262. }
  263. break;
  264. case 'LAST':
  265. if (mode == 'SET') {
  266. var code = cacheList();
  267. code += list + '[' + list + '.length - 1] = ' + value + ';\n';
  268. return code;
  269. } else if (mode == 'INSERT') {
  270. return list + '.add(' + value + ');\n';
  271. }
  272. break;
  273. case 'FROM_START':
  274. var at = Blockly.Dart.getAdjusted(block, 'AT');
  275. if (mode == 'SET') {
  276. return list + '[' + at + '] = ' + value + ';\n';
  277. } else if (mode == 'INSERT') {
  278. return list + '.insert(' + at + ', ' + value + ');\n';
  279. }
  280. break;
  281. case 'FROM_END':
  282. var at = Blockly.Dart.getAdjusted(block, 'AT', 1, false,
  283. Blockly.Dart.ORDER_ADDITIVE);
  284. var code = cacheList();
  285. if (mode == 'SET') {
  286. code += list + '[' + list + '.length - ' + at + '] = ' + value +
  287. ';\n';
  288. return code;
  289. } else if (mode == 'INSERT') {
  290. code += list + '.insert(' + list + '.length - ' + at + ', ' +
  291. value + ');\n';
  292. return code;
  293. }
  294. break;
  295. case 'RANDOM':
  296. Blockly.Dart.definitions_['import_dart_math'] =
  297. 'import \'dart:math\' as Math;';
  298. var code = cacheList();
  299. var xVar = Blockly.Dart.variableDB_.getDistinctName(
  300. 'tmp_x', Blockly.Variables.NAME_TYPE);
  301. code += 'int ' + xVar +
  302. ' = new Math.Random().nextInt(' + list + '.length);\n';
  303. if (mode == 'SET') {
  304. code += list + '[' + xVar + '] = ' + value + ';\n';
  305. return code;
  306. } else if (mode == 'INSERT') {
  307. code += list + '.insert(' + xVar + ', ' + value + ');\n';
  308. return code;
  309. }
  310. break;
  311. }
  312. throw 'Unhandled combination (lists_setIndex).';
  313. };
  314. Blockly.Dart['lists_getSublist'] = function(block) {
  315. // Get sublist.
  316. var list = Blockly.Dart.valueToCode(block, 'LIST',
  317. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  318. var where1 = block.getFieldValue('WHERE1');
  319. var where2 = block.getFieldValue('WHERE2');
  320. if (list.match(/^\w+$/) || (where1 != 'FROM_END' && where2 == 'FROM_START')) {
  321. // If the list is a is a variable or doesn't require a call for length,
  322. // don't generate a helper function.
  323. switch (where1) {
  324. case 'FROM_START':
  325. var at1 = Blockly.Dart.getAdjusted(block, 'AT1');
  326. break;
  327. case 'FROM_END':
  328. var at1 = Blockly.Dart.getAdjusted(block, 'AT1', 1, false,
  329. Blockly.Dart.ORDER_ADDITIVE);
  330. at1 = list + '.length - ' + at1;
  331. break;
  332. case 'FIRST':
  333. var at1 = '0';
  334. break;
  335. default:
  336. throw 'Unhandled option (lists_getSublist).';
  337. }
  338. switch (where2) {
  339. case 'FROM_START':
  340. var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 1);
  341. break;
  342. case 'FROM_END':
  343. var at2 = Blockly.Dart.getAdjusted(block, 'AT2', 0, false,
  344. Blockly.Dart.ORDER_ADDITIVE);
  345. at2 = list + '.length - ' + at2;
  346. break;
  347. case 'LAST':
  348. // There is no second index if LAST option is chosen.
  349. break;
  350. default:
  351. throw 'Unhandled option (lists_getSublist).';
  352. }
  353. if (where2 == 'LAST') {
  354. var code = list + '.sublist(' + at1 + ')';
  355. } else {
  356. var code = list + '.sublist(' + at1 + ', ' + at2 + ')';
  357. }
  358. } else {
  359. var at1 = Blockly.Dart.getAdjusted(block, 'AT1');
  360. var at2 = Blockly.Dart.getAdjusted(block, 'AT2');
  361. var functionName = Blockly.Dart.provideFunction_(
  362. 'lists_get_sublist',
  363. ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  364. '(list, where1, at1, where2, at2) {',
  365. ' int getAt(where, at) {',
  366. ' if (where == \'FROM_END\') {',
  367. ' at = list.length - 1 - at;',
  368. ' } else if (where == \'FIRST\') {',
  369. ' at = 0;',
  370. ' } else if (where == \'LAST\') {',
  371. ' at = list.length - 1;',
  372. ' } else if (where != \'FROM_START\') {',
  373. ' throw \'Unhandled option (lists_getSublist).\';',
  374. ' }',
  375. ' return at;',
  376. ' }',
  377. ' at1 = getAt(where1, at1);',
  378. ' at2 = getAt(where2, at2) + 1;',
  379. ' return list.sublist(at1, at2);',
  380. '}']);
  381. var code = functionName + '(' + list + ', \'' +
  382. where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')';
  383. }
  384. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  385. };
  386. Blockly.Dart['lists_sort'] = function(block) {
  387. // Block for sorting a list.
  388. var list = Blockly.Dart.valueToCode(block, 'LIST',
  389. Blockly.Dart.ORDER_NONE) || '[]';
  390. var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1;
  391. var type = block.getFieldValue('TYPE');
  392. var sortFunctionName = Blockly.Dart.provideFunction_(
  393. 'lists_sort',
  394. ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  395. '(list, type, direction) {',
  396. ' var compareFuncs = {',
  397. ' "NUMERIC": (a, b) => direction * a.compareTo(b),',
  398. ' "TEXT": (a, b) => direction * ' +
  399. 'a.toString().compareTo(b.toString()),',
  400. ' "IGNORE_CASE": ',
  401. ' (a, b) => direction * ',
  402. ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())',
  403. ' };',
  404. ' list = new List.from(list);', // Clone the list.
  405. ' var compare = compareFuncs[type];',
  406. ' list.sort(compare);',
  407. ' return list;',
  408. '}']);
  409. return [sortFunctionName + '(' + list + ', ' +
  410. '"' + type + '", ' + direction + ')',
  411. Blockly.Dart.ORDER_UNARY_POSTFIX];
  412. };
  413. Blockly.Dart['lists_split'] = function(block) {
  414. // Block for splitting text into a list, or joining a list into text.
  415. var input = Blockly.Dart.valueToCode(block, 'INPUT',
  416. Blockly.Dart.ORDER_UNARY_POSTFIX);
  417. var delimiter = Blockly.Dart.valueToCode(block, 'DELIM',
  418. Blockly.Dart.ORDER_NONE) || '\'\'';
  419. var mode = block.getFieldValue('MODE');
  420. if (mode == 'SPLIT') {
  421. if (!input) {
  422. input = '\'\'';
  423. }
  424. var functionName = 'split';
  425. } else if (mode == 'JOIN') {
  426. if (!input) {
  427. input = '[]';
  428. }
  429. var functionName = 'join';
  430. } else {
  431. throw 'Unknown mode: ' + mode;
  432. }
  433. var code = input + '.' + functionName + '(' + delimiter + ')';
  434. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  435. };