arduino.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /**
  2. * @license Licensed under the Apache License, Version 2.0 (the "License"):
  3. * http://www.apache.org/licenses/LICENSE-2.0
  4. */
  5. /**
  6. * Based on work of Fred Lin (gasolin@gmail.com) for Blocklyduino.
  7. *
  8. * @fileoverview Helper functions for generating Arduino language (C++).
  9. */
  10. 'use strict';
  11. goog.provide('Blockly.Python');
  12. goog.require('Blockly.Generator');
  13. goog.require('Blockly.StaticTyping');
  14. /**
  15. * Arduino code generator.
  16. * @type {!Blockly.Generator}
  17. */
  18. Blockly.Python = new Blockly.Generator('Arduino');
  19. Blockly.Python.StaticTyping = new Blockly.StaticTyping();
  20. /**
  21. * List of illegal variable names.
  22. * This is not intended to be a security feature. Blockly is 100% client-side,
  23. * so bypassing this list is trivial. This is intended to prevent users from
  24. * accidentally clobbering a built-in object or function.
  25. * Arduino specific keywords defined in: http://arduino.cc/en/Reference/HomePage
  26. * @private
  27. */
  28. Blockly.Python.addReservedWords(
  29. 'Blockly,' + // In case JS is evaled in the current window.
  30. 'setup,loop,if,else,for,switch,case,while,do,break,continue,return,goto,' +
  31. 'define,include,HIGH,LOW,INPUT,OUTPUT,INPUT_PULLUP,true,false,integer,' +
  32. 'constants,floating,point,void,boolean,char,unsigned,byte,int,word,long,' +
  33. 'float,double,string,String,array,static,volatile,const,sizeof,pinMode,' +
  34. 'digitalWrite,digitalRead,analogReference,analogRead,analogWrite,tone,' +
  35. 'noTone,shiftOut,shitIn,pulseIn,millis,micros,delay,delayMicroseconds,' +
  36. 'min,max,abs,constrain,map,pow,sqrt,sin,cos,tan,randomSeed,random,' +
  37. 'lowByte,highByte,bitRead,bitWrite,bitSet,bitClear,bit,attachInterrupt,' +
  38. 'detachInterrupt,interrupts,noInterrupts');
  39. /** Order of operation ENUMs. */
  40. Blockly.Python.ORDER_ATOMIC = 0; // 0 "" ...
  41. Blockly.Python.ORDER_UNARY_POSTFIX = 1; // expr++ expr-- () [] .
  42. Blockly.Python.ORDER_UNARY_PREFIX = 2; // -expr !expr ~expr ++expr --expr
  43. Blockly.Python.ORDER_MULTIPLICATIVE = 3; // * / % ~/
  44. Blockly.Python.ORDER_ADDITIVE = 4; // + -
  45. Blockly.Python.ORDER_SHIFT = 5; // << >>
  46. Blockly.Python.ORDER_RELATIONAL = 6; // >= > <= <
  47. Blockly.Python.ORDER_EQUALITY = 7; // == != === !==
  48. Blockly.Python.ORDER_BITWISE_AND = 8; // &
  49. Blockly.Python.ORDER_BITWISE_XOR = 9; // ^
  50. Blockly.Python.ORDER_BITWISE_OR = 10; // |
  51. Blockly.Python.ORDER_LOGICAL_AND = 11; // &&
  52. Blockly.Python.ORDER_LOGICAL_OR = 12; // ||
  53. Blockly.Python.ORDER_CONDITIONAL = 13; // expr ? expr : expr
  54. Blockly.Python.ORDER_ASSIGNMENT = 14; // = *= /= ~/= %= += -= <<= >>= &= ^= |=
  55. Blockly.Python.ORDER_NONE = 99; // (...)
  56. /**
  57. * A list of types tasks that the pins can be assigned. Used to track usage and
  58. * warn if the same pin has been assigned to more than one task.
  59. */
  60. Blockly.Python.PinTypes = {
  61. INPUT: 'INPUT',
  62. OUTPUT: 'OUTPUT',
  63. PWM: 'PWM',
  64. SERVO: 'SERVO',
  65. STEPPER: 'STEPPER',
  66. SERIAL: 'SERIAL',
  67. I2C: 'I2C/TWI',
  68. SPI: 'SPI',
  69. // extra setup
  70. FASTLED: 'FASTLED',
  71. DHT: 'DHT'
  72. };
  73. /**
  74. * Arduino generator short name for
  75. * Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_
  76. * @type {!string}
  77. */
  78. Blockly.Python.DEF_FUNC_NAME = Blockly.Python.FUNCTION_NAME_PLACEHOLDER_;
  79. /**
  80. * Initialises the database of global definitions, the setup function, function
  81. * names, and variable names.
  82. * @param {Blockly.Workspace} workspace Workspace to generate code from.
  83. */
  84. Blockly.Python.init = function(workspace) {
  85. // Create a dictionary of definitions to be printed at the top of the sketch
  86. Blockly.Python.includes_ = Object.create(null);
  87. // Create a dictionary of global definitions to be printed after variables
  88. Blockly.Python.definitions_ = Object.create(null);
  89. // Create a dictionary of variables
  90. Blockly.Python.variables_ = Object.create(null);
  91. // Create a dictionary of functions from the code generator
  92. Blockly.Python.codeFunctions_ = Object.create(null);
  93. // Create a dictionary of functions created by the user
  94. Blockly.Python.userFunctions_ = Object.create(null);
  95. // Create a dictionary mapping desired function names in definitions_
  96. // to actual function names (to avoid collisions with user functions)
  97. Blockly.Python.functionNames_ = Object.create(null);
  98. // Create a dictionary of setups to be printed in the setup() function
  99. Blockly.Python.setups_ = Object.create(null);
  100. // Create a dictionary of pins to check if their use conflicts
  101. Blockly.Python.pins_ = Object.create(null);
  102. if (!Blockly.Python.variableDB_) {
  103. Blockly.Python.variableDB_ =
  104. new Blockly.Names(Blockly.Python.RESERVED_WORDS_);
  105. } else {
  106. Blockly.Python.variableDB_.reset();
  107. }
  108. // Iterate through to capture all blocks types and set the function arguments
  109. var varsWithTypes = Blockly.Python.StaticTyping.collectVarsWithTypes(workspace);
  110. Blockly.Python.StaticTyping.setProcedureArgs(workspace, varsWithTypes);
  111. // Set variable declarations with their Arduino type in the defines dictionary
  112. for (var varName in varsWithTypes) {
  113. if (Blockly.Python.getArduinoType_(varsWithTypes[varName]) != "array") {
  114. // console.log(Blockly.Python.getArduinoType_(varsWithTypes[varName]));
  115. Blockly.Python.addVariable(varName,
  116. Blockly.Python.getArduinoType_(varsWithTypes[varName]) +' ' +
  117. Blockly.Python.variableDB_.getName(varName, Blockly.Variables.NAME_TYPE) + ';');
  118. }
  119. }
  120. };
  121. /**
  122. * Prepare all generated code to be placed in the sketch specific locations.
  123. * @param {string} code Generated main program (loop function) code.
  124. * @return {string} Completed sketch code.
  125. */
  126. Blockly.Python.finish = function(code) {
  127. // Convert the includes, definitions, and functions dictionaries into lists
  128. var includes = [], definitions = [], variables = [], functions = [];
  129. for (var name in Blockly.Python.includes_) {
  130. includes.push(Blockly.Python.includes_[name]);
  131. }
  132. if (includes.length) {
  133. includes.push('\n');
  134. }
  135. for (var name in Blockly.Python.variables_) {
  136. variables.push(Blockly.Python.variables_[name]);
  137. }
  138. if (variables.length) {
  139. variables.push('\n');
  140. }
  141. for (var name in Blockly.Python.definitions_) {
  142. definitions.push(Blockly.Python.definitions_[name]);
  143. }
  144. if (definitions.length) {
  145. definitions.push('\n');
  146. }
  147. for (var name in Blockly.Python.codeFunctions_) {
  148. functions.push(Blockly.Python.codeFunctions_[name]);
  149. }
  150. for (var name in Blockly.Python.userFunctions_) {
  151. functions.push(Blockly.Python.userFunctions_[name]);
  152. }
  153. if (functions.length) {
  154. functions.push('\n');
  155. }
  156. // userSetupCode added at the end of the setup function without leading spaces
  157. var setups = [''], userSetupCode= '';
  158. if (Blockly.Python.setups_['userSetupCode'] !== undefined) {
  159. userSetupCode = '\n' + Blockly.Python.setups_['userSetupCode'];
  160. delete Blockly.Python.setups_['userSetupCode'];
  161. }
  162. for (var name in Blockly.Python.setups_) {
  163. setups.push(Blockly.Python.setups_[name]);
  164. }
  165. if (userSetupCode) {
  166. setups.push(userSetupCode);
  167. }
  168. // Clean up temporary data
  169. delete Blockly.Python.includes_;
  170. delete Blockly.Python.definitions_;
  171. delete Blockly.Python.codeFunctions_;
  172. delete Blockly.Python.userFunctions_;
  173. delete Blockly.Python.functionNames_;
  174. delete Blockly.Python.setups_;
  175. delete Blockly.Python.pins_;
  176. Blockly.Python.variableDB_.reset();
  177. // var allDefs = includes.join('\n') + variables.join('\n') +
  178. // definitions.join('\n') + functions.join('\n\n');
  179. // var setup = 'void setup() {' + setups.join('\n ') + '\n}\n\n';
  180. // var loop = 'void loop() {\n ' + code.replace(/\n/g, '\n ') + '\n}';
  181. // return allDefs + setup + loop;
  182. var allDefs = includes.join('\n') + variables.join('\n') +
  183. definitions.join('\n');
  184. var setup = 'void setup() {' + setups.join('\n ') + '\n}\n\n';
  185. var loop = 'void loop() {\n ' + code.replace(/\n/g, '\n ') + '\n}\n\n';
  186. return allDefs + setup + loop + functions.join('\n');
  187. };
  188. /**
  189. * Adds a string of "include" code to be added to the sketch.
  190. * Once a include is added it will not get overwritten with new code.
  191. * @param {!string} includeTag Identifier for this include code.
  192. * @param {!string} code Code to be included at the very top of the sketch.
  193. */
  194. Blockly.Python.addInclude = function(includeTag, code) {
  195. if (Blockly.Python.includes_[includeTag] === undefined) {
  196. Blockly.Python.includes_[includeTag] = code;
  197. }
  198. };
  199. /**
  200. * Adds a string of code to be declared globally to the sketch.
  201. * Once it is added it will not get overwritten with new code.
  202. * @param {!string} declarationTag Identifier for this declaration code.
  203. * @param {!string} code Code to be added below the includes.
  204. */
  205. Blockly.Python.addDeclaration = function(declarationTag, code) {
  206. if (Blockly.Python.definitions_[declarationTag] === undefined) {
  207. Blockly.Python.definitions_[declarationTag] = code;
  208. }
  209. };
  210. /**
  211. * Adds a string of code to declare a variable globally to the sketch.
  212. * Only if overwrite option is set to true it will overwrite whatever
  213. * value the identifier held before.
  214. * @param {!string} varName The name of the variable to declare.
  215. * @param {!string} code Code to be added for the declaration.
  216. * @param {boolean=} overwrite Flag to ignore previously set value.
  217. * @return {!boolean} Indicates if the declaration overwrote a previous one.
  218. */
  219. Blockly.Python.addVariable = function(varName, code, overwrite) {
  220. var overwritten = false;
  221. if (overwrite || (Blockly.Python.variables_[varName] === undefined)) {
  222. Blockly.Python.variables_[varName] = code;
  223. overwritten = true;
  224. }
  225. return overwritten;
  226. };
  227. /**
  228. * Adds a string of code into the Arduino setup() function. It takes an
  229. * identifier to not repeat the same kind of initialisation code from several
  230. * blocks. If overwrite option is set to true it will overwrite whatever
  231. * value the identifier held before.
  232. * @param {!string} setupTag Identifier for the type of set up code.
  233. * @param {!string} code Code to be included in the setup() function.
  234. * @param {boolean=} overwrite Flag to ignore previously set value.
  235. * @return {!boolean} Indicates if the new setup code overwrote a previous one.
  236. */
  237. Blockly.Python.addSetup = function(setupTag, code, overwrite) {
  238. var overwritten = false;
  239. if (overwrite || (Blockly.Python.setups_[setupTag] === undefined)) {
  240. Blockly.Python.setups_[setupTag] = code;
  241. overwritten = true;
  242. }
  243. return overwritten;
  244. };
  245. /**
  246. * Adds a string of code as a function. It takes an identifier (meant to be the
  247. * function name) to only keep a single copy even if multiple blocks might
  248. * request this function to be created.
  249. * A function (and its code) will only be added on first request.
  250. * @param {!string} preferedName Identifier for the function.
  251. * @param {!string} code Code to be included in the setup() function.
  252. * @return {!string} A unique function name based on input name.
  253. */
  254. Blockly.Python.addFunction = function(preferedName, code) {
  255. if (Blockly.Python.codeFunctions_[preferedName] === undefined) {
  256. var uniqueName = Blockly.Python.variableDB_.getDistinctName(
  257. preferedName, Blockly.Generator.NAME_TYPE);
  258. Blockly.Python.codeFunctions_[preferedName] =
  259. code.replace(Blockly.Python.DEF_FUNC_NAME, uniqueName);
  260. Blockly.Python.functionNames_[preferedName] = uniqueName;
  261. }
  262. return Blockly.Python.functionNames_[preferedName];
  263. };
  264. /**
  265. * Description.
  266. * @param {!Blockly.Block} block Description.
  267. * @param {!string} pin Description.
  268. * @param {!string} pinType Description.
  269. * @param {!string} warningTag Description.
  270. */
  271. Blockly.Python.reservePin = function(block, pin, pinType, warningTag) {
  272. if (Blockly.Python.pins_[pin] !== undefined) {
  273. if (Blockly.Python.pins_[pin] != pinType) {
  274. block.setWarningText(Blockly.Msg.ARD_PIN_WARN1.replace('%1', pin)
  275. .replace('%2', warningTag).replace('%3', pinType)
  276. .replace('%4', Blockly.Python.pins_[pin]), warningTag);
  277. } else {
  278. block.setWarningText(null, warningTag);
  279. }
  280. } else {
  281. Blockly.Python.pins_[pin] = pinType;
  282. block.setWarningText(null, warningTag);
  283. }
  284. };
  285. /**
  286. * Naked values are top-level blocks with outputs that aren't plugged into
  287. * anything. A trailing semicolon is needed to make this legal.
  288. * @param {string} line Line of generated code.
  289. * @return {string} Legal line of code.
  290. */
  291. Blockly.Python.scrubNakedValue = function(line) {
  292. return line + ';\n';
  293. };
  294. /**
  295. * Encode a string as a properly escaped Arduino string, complete with quotes.
  296. * @param {string} string Text to encode.
  297. * @return {string} Arduino string.
  298. * @private
  299. */
  300. Blockly.Python.quote_ = function(string) {
  301. // TODO: This is a quick hack. Replace with goog.string.quote
  302. string = string.replace(/\\/g, '\\\\')
  303. .replace(/\n/g, '\\\n')
  304. .replace(/\$/g, '\\$')
  305. .replace(/'/g, '\\\'');
  306. return '\"' + string + '\"';
  307. };
  308. /**
  309. * Common tasks for generating Arduino from blocks.
  310. * Handles comments for the specified block and any connected value blocks.
  311. * Calls any statements following this block.
  312. * @param {!Blockly.Block} block The current block.
  313. * @param {string} code The Arduino code created for this block.
  314. * @return {string} Arduino code with comments and subsequent blocks added.
  315. * @this {Blockly.CodeGenerator}
  316. * @private
  317. */
  318. Blockly.Python.scrub_ = function(block, code) {
  319. if (code === null) { return ''; } // Block has handled code generation itself
  320. var commentCode = '';
  321. // Only collect comments for blocks that aren't inline
  322. if (!block.outputConnection || !block.outputConnection.targetConnection) {
  323. // Collect comment for this block.
  324. var comment = block.getCommentText();
  325. if (comment) {
  326. commentCode += this.prefixLines(comment, '// ') + '\n';
  327. }
  328. // Collect comments for all value arguments
  329. // Don't collect comments for nested statements
  330. for (var x = 0; x < block.inputList.length; x++) {
  331. if (block.inputList[x].type == Blockly.INPUT_VALUE) {
  332. var childBlock = block.inputList[x].connection.targetBlock();
  333. if (childBlock) {
  334. var comment = this.allNestedComments(childBlock);
  335. if (comment) {
  336. commentCode += this.prefixLines(comment, '// ');
  337. }
  338. }
  339. }
  340. }
  341. }
  342. var nextBlock = block.nextConnection && block.nextConnection.targetBlock();
  343. var nextCode = this.blockToCode(nextBlock);
  344. return commentCode + code + nextCode;
  345. };
  346. /**
  347. * Generates Arduino Types from a Blockly Type.
  348. * @param {!Blockly.Type} typeBlockly The Blockly type to be converted.
  349. * @return {string} Arduino type for the respective Blockly input type, in a
  350. * string format.
  351. * @private
  352. */
  353. Blockly.Python.getArduinoType_ = function(typeBlockly) {
  354. switch (typeBlockly.typeId) {
  355. case Blockly.Types.SHORT_NUMBER.typeId:
  356. return 'short';
  357. case Blockly.Types.NUMBER.typeId:
  358. return 'int';
  359. case Blockly.Types.LARGE_NUMBER.typeId:
  360. return 'long';
  361. case Blockly.Types.DECIMAL.typeId:
  362. return 'float';
  363. case Blockly.Types.TEXT.typeId:
  364. return 'String';
  365. case Blockly.Types.CHARACTER.typeId:
  366. return 'char';
  367. case Blockly.Types.BOOLEAN.typeId:
  368. return 'boolean';
  369. case Blockly.Types.NULL.typeId:
  370. return 'void';
  371. case Blockly.Types.UNDEF.typeId:
  372. return 'undefined';
  373. case Blockly.Types.CHILD_BLOCK_MISSING.typeId:
  374. // If no block connected default to int, change for easier debugging
  375. //return 'ChildBlockMissing';
  376. return 'int';
  377. case Blockly.Types.ARRAY.typeId:
  378. return 'array';
  379. default:
  380. return 'Invalid Blockly Type';
  381. }
  382. };
  383. /** Used for not-yet-implemented block code generators */
  384. Blockly.Python.noGeneratorCodeInline = function() {
  385. return ['', Blockly.Python.ORDER_ATOMIC];
  386. };
  387. /** Used for not-yet-implemented block code generators */
  388. Blockly.Python.noGeneratorCodeLine = function() { return ''; };