python_to_blockly.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784
  1. /**
  2. * An object for converting Python source code to the
  3. * Blockly XML representation.
  4. *
  5. * @constructor
  6. * @this {PythonToBlocks}
  7. */
  8. function PythonToBlocks() {
  9. }
  10. function xmlToString(xml) {
  11. return new XMLSerializer().serializeToString(xml);
  12. }
  13. PythonToBlocks.prototype.convertSourceToCodeBlock = function(python_source) {
  14. var xml = document.createElement("xml");
  15. xml.appendChild(raw_block(python_source));
  16. return xmlToString(xml);
  17. }
  18. /**
  19. * The main function for converting a string representation of Python
  20. * code to the Blockly XML representation.
  21. *
  22. * @param {string} python_source - The string representation of Python
  23. * code (e.g., "a = 0").
  24. * @returns {Object} An object which will either have the converted
  25. * source code or an error message and the code as a code-block.
  26. */
  27. PythonToBlocks.prototype.convertSource = function(python_source) {
  28. var xml = document.createElement("xml");
  29. if (python_source.trim() === "") {
  30. return {"xml": xmlToString(xml), "error": null};
  31. }
  32. this.source = python_source.split("\n");
  33. var filename = 'user_code.py';
  34. // Attempt parsing - might fail!
  35. var parse, ast, symbol_table, error;
  36. try {
  37. parse = Sk.parse(filename, python_source);
  38. ast = Sk.astFromParse(parse.cst, filename, parse.flags);
  39. //symbol_table = Sk.symboltable(ast, filename, python_source, filename, parse.flags);
  40. } catch (e) {
  41. error = e;
  42. xml.appendChild(raw_block(python_source))
  43. return {"xml": xmlToString(xml), "error": error};
  44. }
  45. this.comments = {};
  46. for (var commentLocation in parse.comments) {
  47. var lineColumn = commentLocation.split(",");
  48. var yLocation = parseInt(lineColumn[0], 10);
  49. this.comments[yLocation] = parse.comments[commentLocation];
  50. }
  51. this.highestLineSeen = 0;
  52. this.levelIndex = 0;
  53. this.nextExpectedLine = 0;
  54. this.measureNode(ast);
  55. var converted = this.convert(ast);
  56. if (converted !== null) {
  57. for (var block = 0; block < converted.length; block+= 1) {
  58. xml.appendChild(converted[block]);
  59. }
  60. }
  61. return {"xml": xmlToString(xml), "error": null, "lineMap": this.lineMap, 'comment': this.comments};
  62. }
  63. PythonToBlocks.prototype.identifier = function(node) {
  64. return Sk.ffi.remapToJs(node);
  65. }
  66. PythonToBlocks.prototype.recursiveMeasure = function(node, nextBlockLine) {
  67. if (node === undefined) {
  68. return;
  69. }
  70. var myNext = nextBlockLine;
  71. if ("orelse" in node && node.orelse.length > 0) {
  72. if (node.orelse.length == 1 && node.orelse[0]._astname == "If") {
  73. myNext = node.orelse[0].lineno-1;
  74. } else {
  75. myNext = node.orelse[0].lineno-1-1;
  76. }
  77. }
  78. this.heights.push(nextBlockLine);
  79. if ("body" in node) {
  80. for (var i = 0; i < node.body.length; i++) {
  81. var next;
  82. if (i+1 == node.body.length) {
  83. next = myNext;
  84. } else {
  85. next = node.body[i+1].lineno-1;
  86. }
  87. this.recursiveMeasure(node.body[i], next);
  88. }
  89. }
  90. if ("orelse" in node) {
  91. for (var i = 0; i < node.orelse.length; i++) {
  92. var next;
  93. if (i == node.orelse.length) {
  94. next = nextBlockLine;
  95. } else {
  96. next = 1+(node.orelse[i].lineno-1);
  97. }
  98. this.recursiveMeasure(node.orelse[i], next);
  99. }
  100. }
  101. }
  102. PythonToBlocks.prototype.measureNode = function(node) {
  103. this.heights = [];
  104. this.recursiveMeasure(node, this.source.length-1);
  105. this.heights.shift();
  106. }
  107. PythonToBlocks.prototype.getSourceCode = function(frm, to) {
  108. var lines = this.source.slice(frm-1, to);
  109. // Strip out any starting indentation.
  110. if (lines.length > 0) {
  111. var indentation = lines[0].search(/\S/);
  112. for (var i = 0; i < lines.length; i++) {
  113. lines[i] = lines[i].substring(indentation);
  114. }
  115. }
  116. return lines.join("\n");
  117. }
  118. PythonToBlocks.prototype.convertBody = function(node, is_top_level) {
  119. this.levelIndex += 1;
  120. // Empty body, return nothing
  121. if (node.length == 0) {
  122. return null;
  123. }
  124. // Final result list
  125. var children = [], // The complete set of peers
  126. root = null, // The top of the current peer
  127. current = null, // The bottom of the current peer
  128. levelIndex = this.levelIndex;
  129. function addPeer(peer) {
  130. if (root == null) {
  131. children.push(peer);
  132. } else {
  133. children.push(root);
  134. }
  135. root = peer;
  136. current = peer;
  137. }
  138. function finalizePeers() {
  139. if (root != null) {
  140. children.push(root);
  141. }
  142. }
  143. function nestChild(child) {
  144. if (root == null) {
  145. root = child;
  146. current = child;
  147. } else if (current == null) {
  148. root = current;
  149. } else {
  150. var nextElement = document.createElement("next");
  151. nextElement.appendChild(child);
  152. current.appendChild(nextElement);
  153. current = child;
  154. }
  155. }
  156. var lineNumberInBody = 0,
  157. lineNumberInProgram,
  158. previousLineInProgram=null,
  159. distance,
  160. skipped_line,
  161. commentCount,
  162. previousHeight = null,
  163. previousWasStatement = false;
  164. visitedFirstLine = false;
  165. // Iterate through each node
  166. for (var i = 0; i < node.length; i++) {
  167. lineNumberInBody += 1;
  168. lineNumberInProgram = node[i].lineno;
  169. distance = 0, wasFirstLine = true;
  170. if (previousLineInProgram != null) {
  171. distance = lineNumberInProgram - previousLineInProgram-1;
  172. wasFirstLine = false;
  173. }
  174. lineNumberInBody += distance;
  175. // Handle earlier comments
  176. commentCount = 0;
  177. for (var commentLineInProgram in this.comments) {
  178. if (commentLineInProgram < lineNumberInProgram) {
  179. commentChild = this.Comment(this.comments[commentLineInProgram], commentLineInProgram);
  180. if (previousLineInProgram == null) {
  181. nestChild(commentChild);
  182. } else {
  183. skipped_previous_line = Math.abs(previousLineInProgram-commentLineInProgram) > 1;
  184. if (is_top_level && skipped_previous_line) {
  185. addPeer(commentChild);
  186. } else {
  187. nestChild(commentChild);
  188. }
  189. }
  190. previousLineInProgram = commentLineInProgram;
  191. this.highestLineSeen = Math.max(this.highestLineSeen, parseInt(commentLineInProgram, 10));
  192. distance = lineNumberInProgram - previousLineInProgram;
  193. delete this.comments[commentLineInProgram];
  194. commentCount += 1;
  195. }
  196. }
  197. distance = lineNumberInProgram - this.highestLineSeen;
  198. this.highestLineSeen = Math.max(lineNumberInProgram, this.highestLineSeen);
  199. // Now convert the actual node
  200. var height = this.heights.shift();
  201. var originalSourceCode = this.getSourceCode(lineNumberInProgram, height);
  202. var newChild = this.convertStatement(node[i], originalSourceCode, is_top_level);
  203. // Skip null blocks (e.g., imports)
  204. if (newChild == null) {
  205. continue;
  206. }
  207. skipped_line = distance > 1;
  208. previousLineInProgram = lineNumberInProgram;
  209. previousHeight = height;
  210. // Handle top-level expression blocks
  211. if (is_top_level && newChild.constructor == Array) {
  212. addPeer(newChild[0]);
  213. // Handle skipped line
  214. } else if (is_top_level && skipped_line && visitedFirstLine) {
  215. addPeer(newChild);
  216. // The previous line was not a Peer
  217. } else if (is_top_level && !previousWasStatement) {
  218. addPeer(newChild);
  219. // Otherwise, always embed it in there.
  220. } else {
  221. nestChild(newChild);
  222. }
  223. previousWasStatement = newChild.constructor !== Array;
  224. visitedFirstLine = true;
  225. }
  226. // Handle comments that are on the very last line
  227. var lastLineNumber = lineNumberInProgram+1
  228. if (lastLineNumber in this.comments) {
  229. commentChild = this.Comment(this.comments[lastLineNumber], lastLineNumber);
  230. nestChild(commentChild);
  231. delete this.comments[lastLineNumber];
  232. }
  233. // Handle any extra comments that stuck around
  234. if (is_top_level) {
  235. for (var commentLineInProgram in this.comments) {
  236. commentChild = this.Comment(this.comments[commentLineInProgram], commentLineInProgram);
  237. distance = commentLineInProgram - previousLineInProgram;
  238. if (previousLineInProgram == null) {
  239. addPeer(commentChild);
  240. } else if (distance > 1) {
  241. addPeer(commentChild);
  242. } else {
  243. nestChild(commentChild);
  244. }
  245. previousLineInProgram = commentLineInProgram;
  246. delete this.comments[lastLineNumber];
  247. }
  248. }
  249. finalizePeers();
  250. this.levelIndex -= 1;
  251. return children;
  252. }
  253. function block(type, lineNumber, fields, values, settings, mutations, statements) {
  254. var newBlock = document.createElement("block");
  255. // Settings
  256. newBlock.setAttribute("type", type);
  257. newBlock.setAttribute("line_number", lineNumber);
  258. for (var setting in settings) {
  259. var settingValue = settings[setting];
  260. newBlock.setAttribute(setting, settingValue);
  261. }
  262. // Mutations
  263. if (mutations !== undefined && Object.keys(mutations).length > 0) {
  264. var newMutation = document.createElement("mutation");
  265. for (var mutation in mutations) {
  266. var mutationValue = mutations[mutation];
  267. if (mutation.charAt(0) == '@') {
  268. newMutation.setAttribute(mutation.substr(1), mutationValue);
  269. } else if (mutationValue != null && mutationValue.constructor === Array) {
  270. for (var i = 0; i < mutationValue.length; i++) {
  271. var mutationNode = document.createElement(mutation);
  272. mutationNode.setAttribute("name", mutationValue[i]);
  273. newMutation.appendChild(mutationNode);
  274. }
  275. } else {
  276. var mutationNode = document.createElement("arg");
  277. if (mutation.charAt(0) == '*') {
  278. mutationNode.setAttribute("name", "");
  279. } else {
  280. mutationNode.setAttribute("name", mutation);
  281. }
  282. if (mutationValue !== null) {
  283. mutationNode.appendChild(mutationValue);
  284. }
  285. newMutation.appendChild(mutationNode);
  286. }
  287. }
  288. newBlock.appendChild(newMutation);
  289. }
  290. // Fields
  291. for (var field in fields) {
  292. var fieldValue = fields[field];
  293. var newField = document.createElement("field");
  294. newField.setAttribute("name", field);
  295. newField.appendChild(document.createTextNode(fieldValue));
  296. newBlock.appendChild(newField);
  297. }
  298. // Values
  299. for (var value in values) {
  300. var valueValue = values[value];
  301. var newValue = document.createElement("value");
  302. if (valueValue !== null) {
  303. newValue.setAttribute("name", value);
  304. newValue.appendChild(valueValue);
  305. newBlock.appendChild(newValue);
  306. }
  307. }
  308. // Statements
  309. if (statements !== undefined && Object.keys(statements).length > 0) {
  310. for (var statement in statements) {
  311. var statementValue = statements[statement];
  312. if (statementValue == null) {
  313. continue;
  314. } else {
  315. for (var i = 0; i < statementValue.length; i += 1) {
  316. // In most cases, you really shouldn't ever have more than
  317. // one statement in this list. I'm not sure Blockly likes
  318. // that.
  319. var newStatement = document.createElement("statement");
  320. newStatement.setAttribute("name", statement);
  321. newStatement.appendChild(statementValue[i]);
  322. newBlock.appendChild(newStatement);
  323. }
  324. }
  325. }
  326. }
  327. return newBlock;
  328. }
  329. raw_block = function(txt) {
  330. // TODO: lineno as second parameter!
  331. return block("raw_block", 0, { "TEXT": txt });
  332. }
  333. raw_expression = function(txt, lineno) {
  334. return block("raw_expression", lineno, {"TEXT": txt});
  335. }
  336. PythonToBlocks.prototype.convert = function(node, is_top_level) {
  337. return this[node._astname](node, is_top_level);
  338. }
  339. function arrayMax(array) {
  340. return array.reduce(function(a, b) {
  341. return Math.max(a, b);
  342. });
  343. }
  344. function arrayMin(array) {
  345. return array.reduce(function(a, b) {
  346. return Math.min(a, b);
  347. });
  348. }
  349. PythonToBlocks.prototype.convertStatement = function(node, full_source, is_top_level) {
  350. try {
  351. return this.convert(node, is_top_level);
  352. } catch (e) {
  353. heights = this.getChunkHeights(node);
  354. extractedSource = this.getSourceCode(arrayMin(heights), arrayMax(heights));
  355. console.error(e);
  356. return raw_block(extractedSource);
  357. }
  358. }
  359. PythonToBlocks.prototype.getChunkHeights = function(node) {
  360. var lineNumbers = [];
  361. if (node.hasOwnProperty("lineno")) {
  362. lineNumbers.push(node.lineno);
  363. }
  364. if (node.hasOwnProperty("body")) {
  365. for (var i = 0; i < node.body.length; i += 1) {
  366. var subnode = node.body[i];
  367. lineNumbers = lineNumbers.concat(this.getChunkHeights(subnode));
  368. }
  369. }
  370. if (node.hasOwnProperty("orelse")) {
  371. for (var i = 0; i < node.orelse.length; i += 1) {
  372. var subnode = node.orelse[i];
  373. lineNumbers = lineNumbers.concat(this.getChunkHeights(subnode));
  374. }
  375. }
  376. return lineNumbers;
  377. }
  378. /* ----- Nodes ---- */
  379. /*
  380. * NO LINE OR COLUMN NUMBERS
  381. * Module
  382. * body: asdl_seq
  383. */
  384. PythonToBlocks.prototype.Module = function(node)
  385. {
  386. return this.convertBody(node.body, true);
  387. }
  388. PythonToBlocks.prototype.Comment = function(txt, lineno) {
  389. return block("comment_single", lineno, {
  390. "BODY": txt.slice(1)
  391. }, {}, {}, {}, {})
  392. }
  393. /*
  394. * NO LINE OR COLUMN NUMBERS
  395. * Interactive
  396. * body: asdl_seq
  397. */
  398. PythonToBlocks.prototype.Interactive = function(body)
  399. {
  400. return this.convertBody(node.body);
  401. }
  402. /*
  403. * NO LINE OR COLUMN NUMBERS
  404. * TODO
  405. * body: expr_ty
  406. */
  407. PythonToBlocks.prototype.Expression = function(body)
  408. {
  409. this.body = body;
  410. }
  411. /*
  412. * NO LINE OR COLUMN NUMBERS
  413. *
  414. * body: asdl_seq
  415. */
  416. PythonToBlocks.prototype.Suite = function(body)
  417. {
  418. this.asdl_seq(node.body);
  419. }
  420. /*
  421. *
  422. * name: identifier
  423. * args: arguments__ty
  424. * body: asdl_seq
  425. * decorator_list: asdl_seq
  426. */
  427. PythonToBlocks.prototype.FunctionDef = function(node)
  428. {
  429. var name = node.name;
  430. var args = node.args;
  431. var body = node.body;
  432. var decorator_list = node.decorator_list;
  433. if (decorator_list.length > 0) {
  434. throw new Error("Decorators are not implemented.");
  435. }
  436. return block("procedures_defnoreturn", node.lineno, {
  437. "NAME": this.identifier(name)
  438. }, {
  439. }, {
  440. "inline": "false"
  441. }, {
  442. "arg": this.arguments_(args)
  443. }, {
  444. "STACK": this.convertBody(body)
  445. });
  446. }
  447. /*
  448. * name: identifier
  449. * args: arguments__ty
  450. * bases: asdl_seq
  451. * body: asdl_seq
  452. * decorator_list: asdl_seq
  453. */
  454. PythonToBlocks.prototype.ClassDef = function(node)
  455. {
  456. var name = node.name;
  457. var bases = node.bases;
  458. var body = node.body;
  459. var decorator_list = node.decorator_list;
  460. if (decorator_list.length > 0) {
  461. throw new Error("Decorators are not implemented.");
  462. }
  463. return block("class_creation", node.lineno, {
  464. "CLASS": this.identifier(name)
  465. }, {
  466. }, {
  467. "inline": "false"
  468. }, {
  469. //"arg": this.arguments_(args)
  470. }, {
  471. "BODY": this.convertBody(body)
  472. });
  473. }
  474. /*
  475. * value: expr_ty
  476. *
  477. */
  478. PythonToBlocks.prototype.Return = function(node)
  479. {
  480. var value = node.value;
  481. // No field, one title, one setting
  482. return block("procedures_return", node.lineno, {}, {
  483. "VALUE": this.convert(value)
  484. }, {
  485. "inline": "false"
  486. });
  487. }
  488. /*
  489. * targets: asdl_seq
  490. *
  491. */
  492. PythonToBlocks.prototype.Delete = function(/* {asdl_seq *} */ targets)
  493. {
  494. this.targets = targets;
  495. // TODO
  496. throw new Error("Delete is not implemented");
  497. }
  498. /*
  499. * targets: asdl_seq
  500. * value: expr_ty
  501. */
  502. PythonToBlocks.prototype.Assign = function(node)
  503. {
  504. var targets = node.targets;
  505. var value = node.value;
  506. if (targets.length == 0) {
  507. throw new Error("Nothing to assign to!");
  508. } else if (targets.length == 1) {
  509. //alert(node.targets[0].id.v);
  510. //alert(JSON.stringify(node.value.func));//&&node.value.id.v == "turtle.Turtle()" =="turtle"
  511. if(value.func!=null && targets[0].id!= null ){
  512. if(value.func.value.id.v == "turtle" && value.func.attr.v=="Turtle" && value.args.length==0)
  513. return block("turtle_create", node.lineno, {}, {});
  514. }else {
  515. return block("variables_set", node.lineno, {
  516. "VAR": this.Name_str(targets[0]) //targets
  517. }, {
  518. "VALUE": this.convert(value)
  519. });
  520. }
  521. } else {
  522. //TODO
  523. throw new Error("Multiple Assigment Targets Not implemented");
  524. }
  525. }
  526. /*
  527. * target: expr_ty
  528. * op: operator_ty
  529. * value: expr_ty
  530. */
  531. PythonToBlocks.prototype.AugAssign = function(node)
  532. {
  533. var target = node.target;
  534. var op = node.op;
  535. var value = node.value;
  536. if (op.name != "Add") {
  537. //TODO
  538. throw new Error("Only addition is currently supported for augmented assignment!");
  539. } else {
  540. return block("math_change", node.lineno, {
  541. "VAR": this.Name_str(target)
  542. }, {
  543. "DELTA": this.convert(value)
  544. });
  545. }
  546. }
  547. /*
  548. * dest: expr_ty
  549. * values: asdl_seq
  550. * nl: bool
  551. *
  552. */
  553. PythonToBlocks.prototype.Print = function(node)
  554. {
  555. var dest = node.dest;
  556. var values = node.values;
  557. var nl = node.nl;
  558. if (values.length == 1) {
  559. return block("text_print", node.lineno, {}, {
  560. "TEXT": this.convert(values[0])
  561. });
  562. } else {
  563. return block("esp32_main_controller_text_print", node.lineno, {},
  564. this.convertElements("PRINT", values),
  565. {
  566. "inline": "true"
  567. }, {
  568. "@items": values.length
  569. });
  570. }
  571. }
  572. /*
  573. * target: expr_ty
  574. * iter: expr_ty
  575. * body: asdl_seq
  576. * orelse: asdl_seq
  577. *
  578. */
  579. PythonToBlocks.prototype.For = function(node) {
  580. var target = node.target;
  581. var iter = node.iter;
  582. var body = node.body;
  583. var orelse = node.orelse;
  584. if (orelse.length > 0) {
  585. // TODO
  586. throw new Error("Or-else block of For is not implemented.");
  587. }
  588. return block("controls_forEach", node.lineno, {
  589. }, {
  590. "LIST": this.convert(iter),
  591. "VAR": this.convert(target)
  592. }, {
  593. "inline": "true"
  594. }, {}, {
  595. "DO": this.convertBody(body)
  596. });
  597. }
  598. /*
  599. * test: expr_ty
  600. * body: asdl_seq
  601. * orelse: asdl_seq
  602. */
  603. PythonToBlocks.prototype.While = function(node) {
  604. var test = node.test;
  605. var body = node.body;
  606. var orelse = node.orelse;
  607. if (orelse.length > 0) {
  608. // TODO
  609. throw new Error("Or-else block of While is not implemented.");
  610. }
  611. return block("controls_while", node.lineno, {}, {
  612. "BOOL": this.convert(test)
  613. }, {}, {}, {
  614. "DO": this.convertBody(body)
  615. });
  616. }
  617. /*
  618. * test: expr_ty
  619. * body: asdl_seq
  620. * orelse: asdl_seq
  621. *
  622. */
  623. PythonToBlocks.prototype.If = function(node)
  624. {
  625. var test = node.test;
  626. var body = node.body;
  627. var orelse = node.orelse;
  628. var IF_values = {"IF0": this.convert(test)};
  629. var DO_values = {"DO0": this.convertBody(body)};
  630. var elseifCount = 0;
  631. var elseCount = 0;
  632. var potentialElseBody = null;
  633. // Handle weird orelse stuff
  634. if (orelse !== undefined) {
  635. if (orelse.length == 1 && orelse[0]._astname == "If") {
  636. // This is an 'ELIF'
  637. while (orelse.length == 1 && orelse[0]._astname == "If") {
  638. this.heights.shift();
  639. elseifCount += 1;
  640. body = orelse[0].body;
  641. test = orelse[0].test;
  642. orelse = orelse[0].orelse;
  643. DO_values["DO"+elseifCount] = this.convertBody(body, false);
  644. if (test !== undefined) {
  645. IF_values["IF"+elseifCount] = this.convert(test);
  646. }
  647. }
  648. }
  649. if (orelse !== undefined && orelse.length > 0) {
  650. // Or just the body of an Else statement
  651. elseCount += 1;
  652. DO_values["ELSE"] = this.convertBody(orelse);
  653. }
  654. }
  655. return block("controls_if_better", node.lineno, {
  656. }, IF_values, {
  657. "inline": "false"
  658. }, {
  659. "@elseif": elseifCount,
  660. "@else": elseCount
  661. }, DO_values);
  662. }
  663. /*
  664. * context_expr: expr_ty
  665. * optional_vars: expr_ty
  666. * body: asdl_seq
  667. */
  668. PythonToBlocks.prototype.With = function(node)
  669. {
  670. var context_expr = node.context_expr;
  671. var optional_vars = node.optional_vars;
  672. var body = node.body;
  673. throw new Error("With_ not implemented");
  674. }
  675. /*
  676. * type: expr_ty
  677. * inst: expr_ty
  678. * tback: expr_ty
  679. */
  680. PythonToBlocks.prototype.Raise = function(node)
  681. {
  682. var type = node.type;
  683. var inst = node.inst;
  684. var tback = node.tback;
  685. throw new Error("Raise not implemented");
  686. }
  687. /*
  688. * body: asdl_seq
  689. * handlers: asdl_seq
  690. * orelse: asdl_seq
  691. *
  692. */
  693. PythonToBlocks.prototype.TryExcept = function(node)
  694. {
  695. var body = node.body;
  696. var handlers = node.handlers;
  697. var orelse = node.orelse;
  698. throw new Error("TryExcept not implemented");
  699. }
  700. /*
  701. * body: asdl_seq
  702. * finalbody: asdl_seq
  703. *
  704. */
  705. PythonToBlocks.prototype.TryFinally = function(node)
  706. {
  707. var body = node.body;
  708. var finalbody = node.finalbody;
  709. throw new Error("TryExcept not implemented");
  710. }
  711. /*
  712. * test: expr_ty
  713. * msg: expr_ty
  714. */
  715. PythonToBlocks.prototype.Assert = function(node)
  716. {
  717. var test = node.test;
  718. var msg = node.msg;
  719. throw new Error("Assert not implemented");
  720. }
  721. /*
  722. * names: asdl_seq
  723. *
  724. */
  725. PythonToBlocks.prototype.Import = function(node)
  726. {
  727. var names = node.names;
  728. // The import statement isn't used in blockly because it happens implicitly
  729. return null;
  730. }
  731. /*
  732. * module: identifier
  733. * names: asdl_seq
  734. * level: int
  735. *
  736. */
  737. PythonToBlocks.prototype.ImportFrom = function(node)
  738. {
  739. var module = node.module;
  740. var names = node.names;
  741. var level = node.level;
  742. // The import statement isn't used in blockly because it happens implicitly
  743. return null;
  744. }
  745. /*
  746. * body: expr_ty
  747. * globals: expr_ty
  748. * locals: expr_ty
  749. *
  750. */
  751. PythonToBlocks.prototype.Exec = function(node) {
  752. var body = node.body;
  753. var globals = node.globals;
  754. var locals = node.locals;
  755. throw new Error("Exec not implemented");
  756. }
  757. /*
  758. * names: asdl_seq
  759. *
  760. */
  761. PythonToBlocks.prototype.Global = function(node)
  762. {
  763. var names = node.names;
  764. throw new Error("Globals not implemented");
  765. }
  766. /*
  767. * value: expr_ty
  768. *
  769. */
  770. PythonToBlocks.prototype.Expr = function(node, is_top_level) {
  771. var value = node.value;
  772. var converted = this.convert(value);
  773. if (converted.constructor == Array) {
  774. return converted[0];
  775. } else if (is_top_level === true) {
  776. return [this.convert(value)];
  777. } else {
  778. return block("raw_empty", node.lineno, {}, {
  779. "VALUE": this.convert(value)
  780. });
  781. }
  782. }
  783. /*
  784. *
  785. *
  786. */
  787. PythonToBlocks.prototype.Pass = function() {
  788. return null; //block("controls_pass");
  789. }
  790. /*
  791. *
  792. *
  793. */
  794. PythonToBlocks.prototype.Break = function(node) {
  795. return block("controls_flow_statements", node.lineno, {
  796. "FLOW": "BREAK"
  797. });
  798. }
  799. /*
  800. *
  801. *
  802. */
  803. PythonToBlocks.prototype.Continue = function(node) {
  804. return block("controls_flow_statements", node.lineno, {
  805. "FLOW": "CONTINUE"
  806. });
  807. }
  808. /*
  809. * TODO: what does this do?
  810. *
  811. */
  812. PythonToBlocks.prototype.Debugger = function() {
  813. return null;
  814. }
  815. PythonToBlocks.prototype.booleanOperator = function(op) {
  816. switch (op.name) {
  817. case "And": return "AND";
  818. case "Or": return "OR";
  819. default: throw new Error("Operator not supported:"+op.name);
  820. }
  821. }
  822. /*
  823. * op: boolop_ty
  824. * values: asdl_seq
  825. */
  826. PythonToBlocks.prototype.BoolOp = function(node) {
  827. var op = node.op;
  828. var values = node.values;
  829. // TODO: is there ever a case where it's < 1 values?
  830. var result_block = this.convert(values[0]);
  831. for (var i = 1; i < values.length; i+= 1) {
  832. result_block = block("logic_operation", node.lineno, {
  833. "OP": this.booleanOperator(op)
  834. }, {
  835. "A": result_block,
  836. "B": this.convert(values[i])
  837. }, {
  838. "inline": "true"
  839. });
  840. }
  841. return result_block;
  842. }
  843. PythonToBlocks.prototype.binaryOperator = function(op) {
  844. switch (op.name) {
  845. case "Add": return "ADD";
  846. case "Sub": return "MINUS";
  847. case "Div": case "FloorDiv": return "DIVIDE";
  848. case "Mult": return "MULTIPLY";
  849. case "Pow": return "POWER";
  850. case "Mod": return "MODULO";
  851. default: throw new Error("Operator not supported:"+op.name);
  852. }
  853. }
  854. /*
  855. * left: expr_ty
  856. * op: operator_ty
  857. * right: expr_ty
  858. */
  859. PythonToBlocks.prototype.BinOp = function(node)
  860. {
  861. var left = node.left;
  862. var op = node.op;
  863. var right = node.right;
  864. return block("math_arithmetic", node.lineno, {
  865. "OP": this.binaryOperator(op) // TODO
  866. }, {
  867. "A": this.convert(left),
  868. "B": this.convert(right)
  869. }, {
  870. "inline": true
  871. });
  872. }
  873. /*
  874. * op: unaryop_ty
  875. * operand: expr_ty
  876. */
  877. PythonToBlocks.prototype.UnaryOp = function(node)
  878. {
  879. var op = node.op;
  880. var operand = node.operand;
  881. if (op.name == "Not") {
  882. return block("logic_negate", node.lineno, {}, {
  883. "BOOL": this.convert(operand)
  884. }, {
  885. "inline": "false"
  886. });
  887. } else {
  888. throw new Error("Other unary operators are not implemented yet.");
  889. }
  890. }
  891. /*
  892. * args: arguments__ty
  893. * body: expr_ty
  894. */
  895. PythonToBlocks.prototype.Lambda = function(node) {
  896. var args = node.args;
  897. var body = node.body;
  898. throw new Error("Lambda functions are not implemented yet.");
  899. }
  900. /*
  901. * test: expr_ty
  902. * body: expr_ty
  903. * orelse: expr_ty
  904. */
  905. PythonToBlocks.prototype.IfExp = function(node)
  906. {
  907. var test = node.test;
  908. var body = node.body;
  909. var orelse = node.orelse;
  910. throw new Error("Inline IF expressions are not implemented yet.");
  911. }
  912. /*
  913. * keys: asdl_seq
  914. * values: asdl_seq
  915. */
  916. PythonToBlocks.prototype.Dict = function(node) {
  917. var keys = node.keys;
  918. var values = node.values;
  919. var keyList = [];
  920. var valueList = [];
  921. for (var i = 0; i < keys.length; i+= 1) {
  922. if (keys[i]._astname != "Str") {
  923. throw new Error("Dictionary Keys should be Strings.");
  924. }
  925. keyList["KEY"+i] = this.Str_value(keys[i]);
  926. valueList["VALUE"+i] = this.convert(values[i]);
  927. }
  928. return block("dicts_create_with", node.lineno, keyList, valueList, {
  929. "inline": "false"
  930. }, {
  931. "@items": keys.length
  932. });
  933. }
  934. /*
  935. * elts: asdl_seq
  936. *
  937. */
  938. PythonToBlocks.prototype.Set = function(node)
  939. {
  940. var elts = node.elts;
  941. var ctx = node.ctx;
  942. return block("set_create", node.lineno, {},
  943. this.convertElements("ADD", elts)
  944. , {
  945. "inline": elts.length > 3 ? "false" : "true",
  946. }, {
  947. "@items": elts.length
  948. });
  949. }
  950. /*
  951. * elt: expr_ty
  952. * generators: asdl_seq
  953. */
  954. PythonToBlocks.prototype.ListComp = function(node)
  955. {
  956. var elt = node.elt;
  957. var generators = node.generators;
  958. // TODO
  959. }
  960. /*
  961. * elt: expr_ty
  962. * generators: asdl_seq
  963. */
  964. PythonToBlocks.prototype.SetComp = function(node)
  965. {
  966. var elt = node.elt;
  967. var generators = node.generators;
  968. throw new Error("Set Comprehensions are not implemented");
  969. }
  970. /*
  971. * key: expr_ty
  972. * value: expr_ty
  973. * generators: asdl_seq
  974. */
  975. PythonToBlocks.prototype.DictComp = function(node)
  976. {
  977. var key = node.key;
  978. var value = node.value;
  979. var generators = node.generators;
  980. throw new Error("Dictionary Comprehensions are not implemented");
  981. }
  982. /*
  983. * elt: expr_ty
  984. * generators: asdl_seq
  985. */
  986. PythonToBlocks.prototype.GeneratorExp = function(node) {
  987. var elt = node.elt;
  988. var generators = node.generators;
  989. throw new Error("Generator Expresions are not implemented");
  990. }
  991. /*
  992. * value: expr_ty
  993. *
  994. */
  995. PythonToBlocks.prototype.Yield = function(node)
  996. {
  997. var value = value;
  998. throw new Error("Yield expression is not implemented");
  999. }
  1000. PythonToBlocks.prototype.compareOperator = function(op) {
  1001. switch (op.name) {
  1002. case "Eq": return "EQ";
  1003. case "NotEq": return "NEQ";
  1004. case "Lt": return "LT";
  1005. case "Gt": return "GT";
  1006. case "LtE": return "LTE";
  1007. case "GtE": return "GTE";
  1008. case "In_": return "IN";
  1009. case "NotIn": return "NOTIN";
  1010. // Is, IsNot, In, NotIn
  1011. default: throw new Error("Operator not supported:"+op.name);
  1012. }
  1013. }
  1014. /*
  1015. * left: expr_ty
  1016. * ops: asdl_int_seq
  1017. * asdl_seq: comparators
  1018. */
  1019. PythonToBlocks.prototype.Compare = function(node)
  1020. {
  1021. var left = node.left;
  1022. var ops = node.ops;
  1023. var comparators = node.comparators;
  1024. if (ops.length != 1) {
  1025. throw new Error("Only one comparison operator is supported");
  1026. } else if (ops[0].name == "In_" || ops[0].name == "NotIn") {
  1027. return block("logic_isIn", node.lineno, {
  1028. "OP": this.compareOperator(ops[0])
  1029. }, {
  1030. "ITEM": this.convert(left),
  1031. "LIST": this.convert(comparators[0])
  1032. }, {
  1033. "inline": "true"
  1034. });
  1035. } else {
  1036. return block("logic_compare", node.lineno, {
  1037. "OP": this.compareOperator(ops[0])
  1038. }, {
  1039. "A": this.convert(left),
  1040. "B": this.convert(comparators[0])
  1041. }, {
  1042. "inline": "true"
  1043. });
  1044. }
  1045. }
  1046. convertStockSymbols = function(symbol) {
  1047. switch (symbol) {
  1048. case 'FB': case "Facebook":
  1049. return "Facebook";
  1050. case "AAPL": case "Apple":
  1051. return "Apple";
  1052. case "MSFT": case "Microsoft":
  1053. return "Microsoft";
  1054. case "GOOG": case "Google":
  1055. return "Google";
  1056. default:
  1057. throw new Error("Unknown Stock Symbol.");
  1058. }
  1059. }
  1060. toTitleCase = function(str) {
  1061. return str.replace(/\w\S*/g, function(txt){
  1062. return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  1063. });
  1064. }
  1065. PythonToBlocks.KNOWN_MODULES = {
  1066. "weather": {
  1067. "get_temperature": ["weather_temperature", "CITY"],
  1068. "get_report": ["weather_report", "CITY"],
  1069. "get_forecasts": ["weather_forecasts", "CITY"],
  1070. "get_highs_lows": ["weather_highs_lows", "CITY"],
  1071. "get_all_forecasted_temperatures": ["weather_all_forecasts"],
  1072. "get_forecasted_reports": ["weather_report_forecasts", "CITY"]
  1073. },
  1074. "earthquakes": {
  1075. "get": ["earthquake_get", "PROPERTY"],
  1076. "get_both": ["earthquake_both"],
  1077. "get_all": ["earthquake_all"]
  1078. },
  1079. "stocks": {
  1080. "get_current": ["stocks_current", ["TICKER", convertStockSymbols]],
  1081. "get_past": ["stocks_past", ["TICKER", convertStockSymbols]]
  1082. },
  1083. "crime": {
  1084. // STATE = toTitleCase
  1085. "get_property_crimes": ["crime_state", ["STATE", toTitleCase],
  1086. ["TYPE", "property"]],
  1087. "get_violent_crimes": ["crime_state", ["STATE", toTitleCase],
  1088. ["TYPE", "violent"]],
  1089. "get_by_year": ["crime_year", "YEAR"],
  1090. "get_all": ["crime_all"]
  1091. },
  1092. "books": {
  1093. "get_all": ["books_get"]
  1094. },
  1095. "plt": {
  1096. "title": ["*plot_title", "TEXT"],
  1097. "xlabel": ["*plot_xlabel", "TEXT"],
  1098. "ylabel": ["*plot_ylabel", "TEXT"],
  1099. "hist": ["*plot_hist", {"type": "variable", "mode": "value", "name": "values"}],
  1100. "scatter": ["*plot_scatter", {"type": "variable", "mode": "value", "name": "x_values"},
  1101. {"type": "variable", "mode": "value", "name": "y_values"}],
  1102. "show": ["*plot_show"],
  1103. "legend": ["*plot_legend"]
  1104. }
  1105. };
  1106. PythonToBlocks.prototype.KNOWN_FUNCTIONS = ["append", "strip", "rstrip", "lstrip"];
  1107. PythonToBlocks.KNOWN_ATTR_FUNCTIONS = {};
  1108. PythonToBlocks.prototype.CallAttribute = function(func, args, keywords, starargs, kwargs, node) {
  1109. var name = this.identifier(func.attr);
  1110. if (func.value._astname == "Name") {
  1111. var module = this.identifier(func.value.id);
  1112. if (module == "plt" && name == "plot") {
  1113. if (args.length == 1) {
  1114. return [block("plot_line", func.lineno, {}, {
  1115. "y_values": this.convert(args[0])
  1116. }, {"inline": "false"})];
  1117. } else if (args.length == 2) {
  1118. return [block("plot_lineXY", func.lineno, {}, {
  1119. "x_values": this.convert(args[0]),
  1120. "y_values": this.convert(args[1])
  1121. }, {"inline": "false"})];
  1122. } else {
  1123. throw new Error("Incorrect number of arguments to plt.plot");
  1124. }
  1125. } else if (module in PythonToBlocks.KNOWN_MODULES && name in PythonToBlocks.KNOWN_MODULES[module]) {
  1126. var definition = PythonToBlocks.KNOWN_MODULES[module][name];
  1127. var blockName = definition[0];
  1128. var isExpression = true;
  1129. if (blockName.charAt(0) == "*") {
  1130. blockName = blockName.slice(1);
  1131. isExpression = false;
  1132. }
  1133. var fields = {};
  1134. var mutations = {};
  1135. var values = {};
  1136. for (var i = 0; i < args.length; i++) {
  1137. var argument = definition[1+i];
  1138. var destination = fields;
  1139. if (typeof argument == "string") {
  1140. fields[argument] = this.Str_value(args[i]);
  1141. } else if (typeof argument == "object") {
  1142. if (argument.mode == "value") {
  1143. destination = values;
  1144. }
  1145. if (argument.add_mutation !== undefined) {
  1146. mutations[argument.add_mutation.name] = argument.add_mutation.value;
  1147. }
  1148. if (argument.type == 'mutation') {
  1149. if (argument.index == undefined) {
  1150. mutations[argument.name] = this.Str_value(args[i]);
  1151. } else {
  1152. mutations[argument.name] = this.Str_value(args[argument.index+1]);
  1153. }
  1154. } else if (argument.type == "integer") {
  1155. destination[argument.name] = this.Num_value(args[i]);
  1156. } else if (argument.type == 'variable') {
  1157. destination[argument.name] = this.convert(args[i]);
  1158. } else if (argument.type == "integer_mapper") {
  1159. // Okay we jumped the shark here
  1160. var argumentName = argument.name;
  1161. var argumentMapper = argument.method;
  1162. destination[argumentName] = argumentMapper(this.Num_value(args[i]));
  1163. } else if (argument.type == 'mapper') {
  1164. var argumentName = argument.name;
  1165. var argumentMapper = argument.method;
  1166. destination[argumentName] = argumentMapper(this.Str_value(args[i]));
  1167. }
  1168. } else {
  1169. var argumentName = argument[0];
  1170. var argumentMapper = argument[1];
  1171. fields[argumentName] = argumentMapper(this.Str_value(args[i]));
  1172. }
  1173. }
  1174. for (var i = 1+args.length; i < definition.length; i++) {
  1175. var first = definition[i][0];
  1176. var second = definition[i][1];
  1177. fields[first] = second;
  1178. }
  1179. if (isExpression) {
  1180. var k = block(blockName, func.lineno, fields, values, [], mutations);
  1181. return k;
  1182. } else {
  1183. return [block(blockName, func.lineno, fields, values, [], mutations)];
  1184. }
  1185. }
  1186. }
  1187. if (this.KNOWN_FUNCTIONS.indexOf(name) > -1) {
  1188. switch (name) {
  1189. case "append":
  1190. if (args.length !== 1) {
  1191. throw new Error("Incorrect number of arguments to .append");
  1192. }
  1193. // Return as statement
  1194. return [block("lists_append", func.lineno, {}, {
  1195. "ITEM": this.convert(args[0]),
  1196. "LIST": this.convert(func.value)
  1197. }, {
  1198. "inline": "true"
  1199. })];
  1200. case "strip":
  1201. return block("text_trim", func.lineno, { "MODE": "BOTH" },
  1202. { "TEXT": this.convert(func.value) });
  1203. case "lstrip":
  1204. return block("text_trim", func.lineno, { "MODE": "LEFT" },
  1205. { "TEXT": this.convert(func.value) });
  1206. case "rstrip":
  1207. return block("text_trim", func.lineno, { "MODE": "RIGHT" },
  1208. { "TEXT": this.convert(func.value) });
  1209. default: throw new Error("Unknown function call!");
  1210. }
  1211. } else if (name in PythonToBlocks.KNOWN_ATTR_FUNCTIONS) {
  1212. return PythonToBlocks.KNOWN_ATTR_FUNCTIONS[name].bind(this)(func, args, keywords, starargs, kwargs, node)
  1213. } else {
  1214. heights = this.getChunkHeights(node);
  1215. extractedSource = this.getSourceCode(arrayMin(heights), arrayMax(heights));
  1216. var col_endoffset = node.col_endoffset;
  1217. if (args.length > 0) {
  1218. for (var i = 0; i < args.length; i+= 1) {
  1219. col_endoffset = args[i].col_endoffset;
  1220. }
  1221. } else {
  1222. col_endoffset += 2;
  1223. expressionCall += "()";
  1224. }
  1225. var expressionCall = extractedSource.slice(node.col_offset, 1+col_endoffset);
  1226. //console.log(node, extractedSource, node.col_offset, node.col_endoffset);
  1227. var lineno = node.lineno;
  1228. //console.error(e);
  1229. //return raw_expression(expressionCall, lineno);
  1230. var argumentsNormal = {};
  1231. var argumentsMutation = {"@name": name};
  1232. for (var i = 0; i < args.length; i+= 1) {
  1233. argumentsNormal["ARG"+i] = this.convert(args[i]);
  1234. argumentsMutation["*"+i] = this.convert(args[i]);
  1235. }
  1236. var methodCall = block("procedures_callreturn", node.lineno, {
  1237. }, argumentsNormal, {
  1238. "inline": "true"
  1239. }, argumentsMutation);
  1240. return block("attribute_access", node.lineno, {}, {
  1241. "MODULE": this.convert(func.value),
  1242. "NAME": methodCall
  1243. }, { "inline": "true"}, {});
  1244. }
  1245. }
  1246. /*
  1247. * func: expr_ty
  1248. * args: asdl_seq
  1249. * keywords: asdl_seq
  1250. * starargs: expr_ty
  1251. * kwargs: expr_ty
  1252. *
  1253. */
  1254. PythonToBlocks.prototype.Call = function(node) {
  1255. var func = node.func;
  1256. var args = node.args;
  1257. var keywords = node.keywords;
  1258. var starargs = node.starargs;
  1259. var kwargs = node.kwargs;
  1260. switch (func._astname) {
  1261. case "Name":
  1262. switch (this.identifier(func.id)) {
  1263. case "print":
  1264. if (args.length == 1) {
  1265. return [block("text_print", node.lineno, {}, {
  1266. "TEXT": this.convert(args[0])})];
  1267. } else {
  1268. return [block("esp32_main_controller_text_print", node.lineno, {},
  1269. this.convertElements("PRINT", args),
  1270. {"inline": "true"
  1271. }, { "@items": args.length})];
  1272. }
  1273. case "input":
  1274. return block("text_input", node.lineno, {"MESSAGE": args.length ? this.Str_value(args[0]) :""});
  1275. case "abs":
  1276. return block("math_single", node.lineno, {"OP": "ABS"}, {"NUM": this.convert(args[0])})
  1277. case "round":
  1278. return block("math_round", node.lineno, {"OP": "ROUND"}, {"NUM": this.convert(args[0])})
  1279. case "ceil":
  1280. return block("math_round", node.lineno, {"OP": "ROUNDUP"}, {"NUM": this.convert(args[0])})
  1281. case "floor":
  1282. return block("math_round", node.lineno, {"OP": "ROUNDDOWN"}, {"NUM": this.convert(args[0])})
  1283. case "sum":
  1284. return block("math_on_list", node.lineno, {"OP": "SUM"}, {"LIST": this.convert(args[0])})
  1285. case "min":
  1286. return block("math_on_list", node.lineno, {"OP": "MIN"}, {"LIST": this.convert(args[0])})
  1287. case "max":
  1288. return block("math_on_list", node.lineno, {"OP": "MAX"}, {"LIST": this.convert(args[0])})
  1289. case "len":
  1290. return block("lists_length", node.lineno, {}, {"VALUE": this.convert(args[0])})
  1291. case "xrange":
  1292. return block("procedures_callreturn", node.lineno, {},
  1293. {"ARG0": this.convert(args[0])},
  1294. {"inline": "true"},
  1295. {"@name": "xrange",
  1296. "": this.convert(args[0])})
  1297. default:
  1298. if (starargs !== null && starargs.length > 0) {
  1299. throw new Error("*args (variable arguments) are not implemented yet.");
  1300. } else if (kwargs !== null && kwargs.length > 0) {
  1301. throw new Error("**args (keyword arguments) are not implemented yet.");
  1302. }
  1303. var argumentsNormal = {};
  1304. var argumentsMutation = {"@name": this.identifier(func.id)};
  1305. for (var i = 0; i < args.length; i+= 1) {
  1306. argumentsNormal["ARG"+i] = this.convert(args[i]);
  1307. argumentsMutation["*"+i] = this.convert(args[i]);
  1308. }
  1309. return block("procedures_callreturn", node.lineno, {}, argumentsNormal, {
  1310. "inline": "true"
  1311. }, argumentsMutation);
  1312. }
  1313. // Direct function call
  1314. case "Attribute":
  1315. // Module function call
  1316. return this.CallAttribute(func, args, keywords, starargs, kwargs, node);
  1317. }
  1318. }
  1319. /*
  1320. * value: expr_ty
  1321. *
  1322. */
  1323. PythonToBlocks.prototype.Repr = function(node)
  1324. {
  1325. var value = node.value;
  1326. throw new Error("Repr is not yet implemented");
  1327. }
  1328. /*
  1329. * n: object
  1330. *
  1331. */
  1332. PythonToBlocks.prototype.Num = function(node)
  1333. {
  1334. var n = node.n;
  1335. return block("math_number", node.lineno, {"NUM": Sk.ffi.remapToJs(n)});
  1336. }
  1337. PythonToBlocks.prototype.Num_value = function(node)
  1338. {
  1339. var n = node.n;
  1340. return Sk.ffi.remapToJs(n);
  1341. }
  1342. /*
  1343. * s: string
  1344. *
  1345. */
  1346. PythonToBlocks.prototype.Str = function(node)
  1347. {
  1348. var s = node.s;
  1349. var strValue = Sk.ffi.remapToJs(s);
  1350. if (strValue.split("\n").length > 1) {
  1351. return block("string_multiline", node.lineno, {"TEXT": strValue});
  1352. } else {
  1353. return block("text", node.lineno, {"TEXT": strValue});
  1354. }
  1355. }
  1356. PythonToBlocks.prototype.Str_value = function(node) {
  1357. var s = node.s;
  1358. return Sk.ffi.remapToJs(s);
  1359. }
  1360. /*
  1361. * value: expr_ty
  1362. * attr: identifier
  1363. * ctx: expr_context_ty
  1364. *
  1365. */
  1366. PythonToBlocks.prototype.Attribute = function(node)
  1367. {
  1368. var value = node.value;
  1369. var attr = node.attr;
  1370. var ctx = node.ctx;
  1371. console.log(node);
  1372. return block("attribute_access", node.lineno, {
  1373. "MODULE": this.convert(value),
  1374. "NAME": this.convert(attr)
  1375. });
  1376. //throw new Error("Attribute access not implemented");
  1377. }
  1378. /*
  1379. * value: expr_ty
  1380. * slice: slice_ty
  1381. * ctx: expr_context_ty
  1382. *
  1383. */
  1384. PythonToBlocks.prototype.Subscript = function(node)
  1385. {
  1386. var value = node.value;
  1387. var slice = node.slice;
  1388. var ctx = node.ctx;
  1389. if (slice._astname == "Index") {
  1390. if (slice.value._astname == "Str") {
  1391. return block("dict_get_literal", node.lineno, {
  1392. "ITEM": this.Str_value(slice.value)
  1393. }, {
  1394. "DICT": this.convert(value)
  1395. });
  1396. } else if (slice.value._astname == "Num") {
  1397. return block("lists_index", node.lineno, {}, {
  1398. "ITEM": this.convert(slice.value),
  1399. "LIST": this.convert(value),
  1400. });
  1401. }
  1402. } else if (slice._astname == "Slice") {
  1403. return block("lists_getSublist", node.lineno, {
  1404. "WHERE1": "FROM_START",
  1405. "WHERE2": "FROM_START"
  1406. }, {
  1407. "LIST": this.convert(value),
  1408. "AT1": (slice.lower == null) ?
  1409. null : this.convert(slice.lower),
  1410. "AT2": (slice.upper == null) ?
  1411. null : this.convert(slice.upper),
  1412. }, {}, {
  1413. "@at1": "true",
  1414. "@at2": "true",
  1415. });
  1416. }
  1417. throw new Error("This kind of subscript is not supported.");
  1418. }
  1419. /*
  1420. * id: identifier
  1421. * ctx: expr_context_ty
  1422. */
  1423. PythonToBlocks.prototype.Name = function(node)
  1424. {
  1425. var id = node.id;
  1426. var ctx = node.ctx;
  1427. switch (this.Name_str(node)) {
  1428. case "True":
  1429. return block("logic_boolean", node.lineno, {"BOOL": "TRUE"});
  1430. case "False":
  1431. return block("logic_boolean", node.lineno, {"BOOL": "FALSE"});
  1432. case "None":
  1433. return block("logic_null", node.lineno);
  1434. case "___":
  1435. return null;
  1436. default:
  1437. return block('variables_get', node.lineno, {
  1438. "VAR": this.identifier(id)
  1439. });
  1440. }
  1441. }
  1442. /*
  1443. * id: identifier
  1444. * ctx: expr_context_ty
  1445. */
  1446. PythonToBlocks.prototype.Name_str = function(node)
  1447. {
  1448. var id = node.id;
  1449. var ctx = node.ctx;
  1450. return this.identifier(id);
  1451. }
  1452. PythonToBlocks.prototype.convertElements = function(key, values, plusser) {
  1453. if (plusser === undefined) {
  1454. plusser = 0;
  1455. }
  1456. var output = {};
  1457. for (var i = 0; i < values.length; i++) {
  1458. output[key+(plusser+i)] = this.convert(values[i]);
  1459. }
  1460. return output;
  1461. }
  1462. /*
  1463. * elts: asdl_seq
  1464. * ctx: expr_context_ty
  1465. *
  1466. */
  1467. PythonToBlocks.prototype.List = function(node) {
  1468. var elts = node.elts;
  1469. var ctx = node.ctx;
  1470. return block("lists_create_with", node.lineno, {},
  1471. this.convertElements("ADD", elts)
  1472. , {
  1473. "inline": elts.length > 3 ? "false" : "true",
  1474. }, {
  1475. "@items": elts.length
  1476. });
  1477. }
  1478. /*
  1479. * elts: asdl_seq
  1480. * ctx: expr_context_ty
  1481. */
  1482. PythonToBlocks.prototype.Tuple = function(node)
  1483. {
  1484. var elts = node.elts;
  1485. var ctx = node.ctx;
  1486. return block("tuple_create", node.lineno, {},
  1487. this.convertElements("ADD", elts)
  1488. , {
  1489. "inline": elts.length > 3 ? "false" : "true",
  1490. }, {
  1491. "@items": elts.length
  1492. });
  1493. }
  1494. /*
  1495. *
  1496. *
  1497. */
  1498. PythonToBlocks.prototype.Ellipsis = function() {
  1499. throw new Error("Ellipsis not implemented");
  1500. }
  1501. /*
  1502. * lower: expr_ty
  1503. * upper: expr_ty
  1504. * step: expr_ty
  1505. *
  1506. */
  1507. PythonToBlocks.prototype.Slice = function(node)
  1508. {
  1509. var lower = node.lower;
  1510. var upper = node.upper;
  1511. var step = node.step;
  1512. throw new Error("Slices not implemented");
  1513. }
  1514. /*
  1515. * dims: asdl_seq
  1516. *
  1517. */
  1518. PythonToBlocks.prototype.ExtSlice = function(node)
  1519. {
  1520. var dims = node.dims;
  1521. throw new Error("ExtSlice is not implemented.");
  1522. }
  1523. /*
  1524. * value: expr_ty
  1525. *
  1526. */
  1527. PythonToBlocks.prototype.Index = function(value)
  1528. {
  1529. var value = node.value;
  1530. throw new Error("Index is not implemented");
  1531. }
  1532. /*
  1533. * target: expr_ty
  1534. * iter: expr_ty
  1535. * ifs: asdl_seq
  1536. *
  1537. */
  1538. PythonToBlocks.prototype.comprehension = function(node)
  1539. {
  1540. var target = node.target;
  1541. var iter = node.iter;
  1542. var ifs = node.ifs;
  1543. throw new Error("Comprehensions not implemented.");
  1544. }
  1545. /*
  1546. * type: expr_ty
  1547. * name: expr_ty
  1548. * body: asdl_seq
  1549. *
  1550. */
  1551. PythonToBlocks.prototype.ExceptHandler = function(node)
  1552. {
  1553. var type = node.type;
  1554. var name = node.name;
  1555. var body = node.boy;
  1556. throw new Error("Except handlers are not implemented");
  1557. }
  1558. PythonToBlocks.prototype.argument_ = function(node) {
  1559. var id = node.id;
  1560. return this.identifier(id);
  1561. }
  1562. /*
  1563. * args: asdl_seq
  1564. * vararg: identifier
  1565. * kwarg: identifier
  1566. * defaults: asdl_seq
  1567. *
  1568. */
  1569. PythonToBlocks.prototype.arguments_ = function(node)
  1570. {
  1571. var args = node.args;
  1572. var vararg = node.vararg;
  1573. var kwarg = node.kwarg;
  1574. var defaults = node.defaults;
  1575. var allArgs = [];
  1576. for (var i = 0; i < args.length; i++) {
  1577. var arg = args[i];
  1578. allArgs.push(this.argument_(arg));
  1579. }
  1580. return allArgs;
  1581. }
  1582. /*
  1583. * arg: identifier
  1584. * value: expr_ty
  1585. *
  1586. */
  1587. PythonToBlocks.prototype.keyword = function(node)
  1588. {
  1589. var arg = node.arg;
  1590. var value = node.value;
  1591. throw new Error("Keywords are not implemented");
  1592. }
  1593. /*
  1594. * name: identifier
  1595. * asname: identifier
  1596. *
  1597. */
  1598. PythonToBlocks.prototype.alias = function(node)
  1599. {
  1600. var name = node.name;
  1601. var asname = node.asname;
  1602. throw new Error("Aliases are not implemented");
  1603. }
  1604. /* ----- expr_context ----- */
  1605. /*
  1606. Load
  1607. Store
  1608. Del
  1609. AugLoad
  1610. AugStore
  1611. Param
  1612. */
  1613. /* ----- operator ----- */
  1614. /*
  1615. Add
  1616. Sub
  1617. Mult
  1618. Div
  1619. Mod
  1620. Pow
  1621. LShift
  1622. RShift
  1623. BitOr
  1624. BitXor
  1625. BitAnd
  1626. FloorDiv
  1627. */
  1628. /* ----- unaryop ----- */
  1629. /*
  1630. Invert
  1631. Not
  1632. UAdd
  1633. USub
  1634. */