lists.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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 code = new Array(block.itemCount_);
  35. for (var n = 0; n < block.itemCount_; n++) {
  36. code[n] = Blockly.Dart.valueToCode(block, 'ADD' + n,
  37. Blockly.Dart.ORDER_NONE) || 'null';
  38. }
  39. code = '[' + code.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 argument0 = Blockly.Dart.valueToCode(block, 'ITEM',
  45. Blockly.Dart.ORDER_NONE) || 'null';
  46. var argument1 = Blockly.Dart.valueToCode(block, 'NUM',
  47. Blockly.Dart.ORDER_NONE) || '0';
  48. var code = 'new List.filled(' + argument1 + ', ' + argument0 + ')';
  49. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  50. };
  51. Blockly.Dart['lists_length'] = function(block) {
  52. // String or array length.
  53. var argument0 = Blockly.Dart.valueToCode(block, 'VALUE',
  54. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  55. return [argument0 + '.length', Blockly.Dart.ORDER_UNARY_POSTFIX];
  56. };
  57. Blockly.Dart['lists_isEmpty'] = function(block) {
  58. // Is the string null or array empty?
  59. var argument0 = Blockly.Dart.valueToCode(block, 'VALUE',
  60. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  61. return [argument0 + '.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 argument0 = Blockly.Dart.valueToCode(block, 'FIND',
  68. Blockly.Dart.ORDER_NONE) || '\'\'';
  69. var argument1 = Blockly.Dart.valueToCode(block, 'VALUE',
  70. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  71. var code = argument1 + '.' + operator + '(' + argument0 + ') + 1';
  72. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  73. };
  74. Blockly.Dart['lists_getIndex'] = function(block) {
  75. // Get element at index.
  76. // Note: Until January 2013 this block did not have MODE or WHERE inputs.
  77. var mode = block.getFieldValue('MODE') || 'GET';
  78. var where = block.getFieldValue('WHERE') || 'FROM_START';
  79. var at = Blockly.Dart.valueToCode(block, 'AT',
  80. Blockly.Dart.ORDER_UNARY_PREFIX) || '1';
  81. var list = Blockly.Dart.valueToCode(block, 'VALUE',
  82. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  83. if (where == 'FIRST') {
  84. if (mode == 'GET') {
  85. var code = list + '.first';
  86. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  87. } else if (mode == 'GET_REMOVE') {
  88. var code = list + '.removeAt(0)';
  89. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  90. } else if (mode == 'REMOVE') {
  91. return list + '.removeAt(0);\n';
  92. }
  93. } else if (where == 'LAST') {
  94. if (mode == 'GET') {
  95. var code = list + '.last';
  96. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  97. } else if (mode == 'GET_REMOVE') {
  98. var code = list + '.removeLast()';
  99. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  100. } else if (mode == 'REMOVE') {
  101. return list + '.removeLast();\n';
  102. }
  103. } else if (where == 'FROM_START') {
  104. // Blockly uses one-based indicies.
  105. if (Blockly.isNumber(at)) {
  106. // If the index is a naked number, decrement it right now.
  107. at = parseInt(at, 10) - 1;
  108. } else {
  109. // If the index is dynamic, decrement it in code.
  110. at += ' - 1';
  111. }
  112. if (mode == 'GET') {
  113. var code = list + '[' + at + ']';
  114. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  115. } else if (mode == 'GET_REMOVE') {
  116. var code = list + '.removeAt(' + at + ')';
  117. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  118. } else if (mode == 'REMOVE') {
  119. return list + '.removeAt(' + at + ');\n';
  120. }
  121. } else if (where == 'FROM_END') {
  122. if (mode == 'GET') {
  123. var functionName = Blockly.Dart.provideFunction_(
  124. 'lists_get_from_end',
  125. [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  126. '(List myList, num x) {',
  127. ' x = myList.length - x;',
  128. ' return myList.removeAt(x);',
  129. '}']);
  130. code = functionName + '(' + list + ', ' + at + ')';
  131. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  132. } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') {
  133. var functionName = Blockly.Dart.provideFunction_(
  134. 'lists_remove_from_end',
  135. [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  136. '(List myList, num x) {',
  137. ' x = myList.length - x;',
  138. ' return myList.removeAt(x);',
  139. '}']);
  140. code = functionName + '(' + list + ', ' + at + ')';
  141. if (mode == 'GET_REMOVE') {
  142. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  143. } else if (mode == 'REMOVE') {
  144. return code + ';\n';
  145. }
  146. }
  147. } else if (where == 'RANDOM') {
  148. Blockly.Dart.definitions_['import_dart_math'] =
  149. 'import \'dart:math\' as Math;';
  150. var functionName = Blockly.Dart.provideFunction_(
  151. 'lists_get_random_item',
  152. [ 'dynamic ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  153. '(List myList, bool remove) {',
  154. ' int x = new Math.Random().nextInt(myList.length);',
  155. ' if (remove) {',
  156. ' return myList.removeAt(x);',
  157. ' } else {',
  158. ' return myList[x];',
  159. ' }',
  160. '}']);
  161. code = functionName + '(' + list + ', ' + (mode != 'GET') + ')';
  162. if (mode == 'GET' || mode == 'GET_REMOVE') {
  163. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  164. } else if (mode == 'REMOVE') {
  165. return code + ';\n';
  166. }
  167. }
  168. throw 'Unhandled combination (lists_getIndex).';
  169. };
  170. Blockly.Dart['lists_setIndex'] = function(block) {
  171. // Set element at index.
  172. // Note: Until February 2013 this block did not have MODE or WHERE inputs.
  173. var list = Blockly.Dart.valueToCode(block, 'LIST',
  174. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  175. var mode = block.getFieldValue('MODE') || 'GET';
  176. var where = block.getFieldValue('WHERE') || 'FROM_START';
  177. var at = Blockly.Dart.valueToCode(block, 'AT',
  178. Blockly.Dart.ORDER_ADDITIVE) || '1';
  179. var value = Blockly.Dart.valueToCode(block, 'TO',
  180. Blockly.Dart.ORDER_ASSIGNMENT) || 'null';
  181. // Cache non-trivial values to variables to prevent repeated look-ups.
  182. // Closure, which accesses and modifies 'list'.
  183. function cacheList() {
  184. if (list.match(/^\w+$/)) {
  185. return '';
  186. }
  187. var listVar = Blockly.Dart.variableDB_.getDistinctName(
  188. 'tmp_list', Blockly.Variables.NAME_TYPE);
  189. var code = 'List ' + listVar + ' = ' + list + ';\n';
  190. list = listVar;
  191. return code;
  192. }
  193. if (where == 'FIRST') {
  194. if (mode == 'SET') {
  195. return list + '[0] = ' + value + ';\n';
  196. } else if (mode == 'INSERT') {
  197. return list + '.insert(0, ' + value + ');\n';
  198. }
  199. } else if (where == 'LAST') {
  200. if (mode == 'SET') {
  201. var code = cacheList();
  202. code += list + '[' + list + '.length - 1] = ' + value + ';\n';
  203. return code;
  204. } else if (mode == 'INSERT') {
  205. return list + '.add(' + value + ');\n';
  206. }
  207. } else if (where == 'FROM_START') {
  208. // Blockly uses one-based indicies.
  209. if (Blockly.isNumber(at)) {
  210. // If the index is a naked number, decrement it right now.
  211. at = parseInt(at, 10) - 1;
  212. } else {
  213. // If the index is dynamic, decrement it in code.
  214. at += ' - 1';
  215. }
  216. if (mode == 'SET') {
  217. return list + '[' + at + '] = ' + value + ';\n';
  218. } else if (mode == 'INSERT') {
  219. return list + '.insert(' + at + ', ' + value + ');\n';
  220. }
  221. } else if (where == 'FROM_END') {
  222. var code = cacheList();
  223. if (mode == 'SET') {
  224. code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n';
  225. return code;
  226. } else if (mode == 'INSERT') {
  227. code += list + '.insert(' + list + '.length - ' + at + ', ' +
  228. value + ');\n';
  229. return code;
  230. }
  231. } else if (where == 'RANDOM') {
  232. Blockly.Dart.definitions_['import_dart_math'] =
  233. 'import \'dart:math\' as Math;';
  234. var code = cacheList();
  235. var xVar = Blockly.Dart.variableDB_.getDistinctName(
  236. 'tmp_x', Blockly.Variables.NAME_TYPE);
  237. code += 'int ' + xVar +
  238. ' = new Math.Random().nextInt(' + list + '.length);';
  239. if (mode == 'SET') {
  240. code += list + '[' + xVar + '] = ' + value + ';\n';
  241. return code;
  242. } else if (mode == 'INSERT') {
  243. code += list + '.insert(' + xVar + ', ' + value + ');\n';
  244. return code;
  245. }
  246. }
  247. throw 'Unhandled combination (lists_setIndex).';
  248. };
  249. Blockly.Dart['lists_getSublist'] = function(block) {
  250. // Get sublist.
  251. var list = Blockly.Dart.valueToCode(block, 'LIST',
  252. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  253. var where1 = block.getFieldValue('WHERE1');
  254. var where2 = block.getFieldValue('WHERE2');
  255. var at1 = Blockly.Dart.valueToCode(block, 'AT1',
  256. Blockly.Dart.ORDER_NONE) || '1';
  257. var at2 = Blockly.Dart.valueToCode(block, 'AT2',
  258. Blockly.Dart.ORDER_NONE) || '1';
  259. if ((where1 == 'FIRST' || where1 == 'FROM_START' && Blockly.isNumber(at1)) &&
  260. (where2 == 'LAST' || where2 == 'FROM_START' && Blockly.isNumber(at2))) {
  261. // Simple case that can be done inline.
  262. at1 = where1 == 'FIRST' ? 0 : parseInt(at1, 10) - 1;
  263. if (where2 == 'LAST') {
  264. code = list + '.sublist(' + at1 + ')';
  265. } else {
  266. at2 = parseInt(at2, 10);
  267. code = list + '.sublist(' + at1 + ', ' + at2 + ')';
  268. }
  269. } else {
  270. var functionName = Blockly.Dart.provideFunction_(
  271. 'lists_get_sublist',
  272. [ 'List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  273. '(list, where1, at1, where2, at2) {',
  274. ' int getAt(where, at) {',
  275. ' if (where == \'FROM_START\') {',
  276. ' at--;',
  277. ' } else if (where == \'FROM_END\') {',
  278. ' at = list.length - at;',
  279. ' } else if (where == \'FIRST\') {',
  280. ' at = 0;',
  281. ' } else if (where == \'LAST\') {',
  282. ' at = list.length - 1;',
  283. ' } else {',
  284. ' throw \'Unhandled option (lists_getSublist).\';',
  285. ' }',
  286. ' return at;',
  287. ' }',
  288. ' at1 = getAt(where1, at1);',
  289. ' at2 = getAt(where2, at2) + 1;',
  290. ' return list.sublist(at1, at2);',
  291. '}']);
  292. var code = functionName + '(' + list + ', \'' +
  293. where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')';
  294. }
  295. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  296. };
  297. Blockly.Dart['lists_sort'] = function(block) {
  298. // Block for sorting a list.
  299. var listCode = Blockly.Dart.valueToCode(block, 'LIST',
  300. Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
  301. var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1;
  302. var type = block.getFieldValue('TYPE');
  303. var sortFunctionName = Blockly.Dart.provideFunction_(
  304. 'lists_sort',
  305. ['List ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ +
  306. '(list, type, direction) {',
  307. ' var compareFuncs = {',
  308. ' "NUMERIC": (a, b) => direction * a.compareTo(b),',
  309. ' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),',
  310. ' "IGNORE_CASE": ',
  311. ' (a, b) => direction * ',
  312. ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())',
  313. ' };',
  314. ' list = new List.from(list);', // Clone the list.
  315. ' var compare = compareFuncs[type];',
  316. ' list.sort(compare);',
  317. ' return list;',
  318. '}']);
  319. return [sortFunctionName + '(' + listCode + ', ' +
  320. '"' + type + '", ' + direction + ')',
  321. Blockly.Dart.ORDER_UNARY_POSTFIX];
  322. };
  323. Blockly.Dart['lists_split'] = function(block) {
  324. // Block for splitting text into a list, or joining a list into text.
  325. var value_input = Blockly.Dart.valueToCode(block, 'INPUT',
  326. Blockly.Dart.ORDER_UNARY_POSTFIX);
  327. var value_delim = Blockly.Dart.valueToCode(block, 'DELIM',
  328. Blockly.Dart.ORDER_NONE) || '\'\'';
  329. var mode = block.getFieldValue('MODE');
  330. if (mode == 'SPLIT') {
  331. if (!value_input) {
  332. value_input = '\'\'';
  333. }
  334. var functionName = 'split';
  335. } else if (mode == 'JOIN') {
  336. if (!value_input) {
  337. value_input = '[]';
  338. }
  339. var functionName = 'join';
  340. } else {
  341. throw 'Unknown mode: ' + mode;
  342. }
  343. var code = value_input + '.' + functionName + '(' + value_delim + ')';
  344. return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
  345. };