blockly 7.20211209.4 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/blockly.d.ts +18963 -18432
  2. package/blockly.min.js +852 -844
  3. package/blockly_compressed.js +669 -664
  4. package/blockly_compressed.js.map +1 -1
  5. package/blocks/blocks.js +47 -0
  6. package/blocks/colour.js +13 -3
  7. package/blocks/lists.js +22 -13
  8. package/blocks/logic.js +13 -3
  9. package/blocks/loops.js +24 -11
  10. package/blocks/math.js +12 -3
  11. package/blocks/procedures.js +41 -27
  12. package/blocks/text.js +22 -13
  13. package/blocks/variables.js +14 -3
  14. package/blocks/variables_dynamic.js +13 -3
  15. package/blocks_compressed.js +146 -141
  16. package/blocks_compressed.js.map +1 -1
  17. package/core/block.js +1869 -1814
  18. package/core/block_drag_surface.js +201 -200
  19. package/core/block_dragger.js +377 -373
  20. package/core/block_svg.js +1593 -1479
  21. package/core/blockly.js +8 -22
  22. package/core/blocks.js +9 -2
  23. package/core/browser_events.js +22 -5
  24. package/core/bubble.js +841 -797
  25. package/core/bubble_dragger.js +213 -206
  26. package/core/bump_objects.js +2 -2
  27. package/core/clipboard.js +9 -9
  28. package/core/comment.js +353 -332
  29. package/core/common.js +46 -17
  30. package/core/component_manager.js +181 -174
  31. package/core/config.js +87 -0
  32. package/core/connection.js +595 -584
  33. package/core/connection_checker.js +242 -244
  34. package/core/connection_db.js +235 -230
  35. package/core/contextmenu.js +9 -6
  36. package/core/contextmenu_items.js +1 -2
  37. package/core/contextmenu_registry.js +93 -89
  38. package/core/css.js +474 -474
  39. package/core/delete_area.js +45 -42
  40. package/core/drag_target.js +57 -56
  41. package/core/dropdowndiv.js +153 -163
  42. package/core/events/events.js +2 -2
  43. package/core/events/events_abstract.js +89 -77
  44. package/core/events/events_block_base.js +37 -36
  45. package/core/events/events_block_change.js +130 -124
  46. package/core/events/events_block_create.js +73 -71
  47. package/core/events/events_block_delete.js +84 -82
  48. package/core/events/events_block_drag.js +50 -49
  49. package/core/events/events_block_move.js +147 -140
  50. package/core/events/events_bubble_open.js +51 -50
  51. package/core/events/events_click.js +48 -44
  52. package/core/events/events_comment_base.js +72 -69
  53. package/core/events/events_comment_change.js +63 -61
  54. package/core/events/events_comment_create.js +44 -42
  55. package/core/events/events_comment_delete.js +42 -40
  56. package/core/events/events_comment_move.js +106 -104
  57. package/core/events/events_marker_move.js +65 -64
  58. package/core/events/events_selected.js +46 -45
  59. package/core/events/events_theme_change.js +36 -35
  60. package/core/events/events_toolbox_item_select.js +46 -45
  61. package/core/events/events_trashcan_open.js +37 -36
  62. package/core/events/events_ui.js +47 -46
  63. package/core/events/events_ui_base.js +30 -29
  64. package/core/events/events_var_base.js +37 -36
  65. package/core/events/events_var_create.js +50 -48
  66. package/core/events/events_var_delete.js +50 -48
  67. package/core/events/events_var_rename.js +51 -49
  68. package/core/events/events_viewport.js +66 -65
  69. package/core/events/utils.js +29 -14
  70. package/core/events/workspace_events.js +49 -55
  71. package/core/extensions.js +4 -3
  72. package/core/field.js +1061 -997
  73. package/core/field_angle.js +462 -442
  74. package/core/field_checkbox.js +194 -182
  75. package/core/field_colour.js +519 -505
  76. package/core/field_dropdown.js +617 -598
  77. package/core/field_image.js +229 -220
  78. package/core/field_label.js +102 -91
  79. package/core/field_label_serializable.js +42 -41
  80. package/core/field_multilineinput.js +372 -358
  81. package/core/field_number.js +272 -253
  82. package/core/field_textinput.js +499 -467
  83. package/core/field_variable.js +458 -420
  84. package/core/flyout_base.js +1005 -952
  85. package/core/flyout_button.js +277 -260
  86. package/core/flyout_horizontal.js +304 -302
  87. package/core/flyout_metrics_manager.js +64 -64
  88. package/core/flyout_vertical.js +306 -300
  89. package/core/generator.js +459 -446
  90. package/core/gesture.js +829 -813
  91. package/core/grid.js +166 -163
  92. package/core/icon.js +168 -159
  93. package/core/inject.js +7 -5
  94. package/core/input.js +257 -248
  95. package/core/insertion_marker_manager.js +655 -624
  96. package/core/internal_constants.js +0 -129
  97. package/core/keyboard_nav/ast_node.js +605 -596
  98. package/core/keyboard_nav/basic_cursor.js +166 -165
  99. package/core/keyboard_nav/cursor.js +99 -97
  100. package/core/keyboard_nav/marker.js +83 -79
  101. package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
  102. package/core/marker_manager.js +153 -141
  103. package/core/menu.js +377 -372
  104. package/core/menuitem.js +223 -217
  105. package/core/metrics_manager.js +403 -390
  106. package/core/mutator.js +468 -437
  107. package/core/names.js +229 -188
  108. package/core/options.js +290 -284
  109. package/core/procedures.js +29 -17
  110. package/core/registry.js +19 -16
  111. package/core/rendered_connection.js +482 -463
  112. package/core/renderers/common/block_rendering.js +9 -3
  113. package/core/renderers/common/constants.js +1119 -1112
  114. package/core/renderers/common/debug.js +14 -0
  115. package/core/renderers/common/debugger.js +338 -316
  116. package/core/renderers/common/drawer.js +380 -370
  117. package/core/renderers/common/i_path_object.js +2 -2
  118. package/core/renderers/common/info.js +626 -618
  119. package/core/renderers/common/marker_svg.js +579 -541
  120. package/core/renderers/common/path_object.js +203 -200
  121. package/core/renderers/common/renderer.js +220 -218
  122. package/core/renderers/geras/constants.js +36 -36
  123. package/core/renderers/geras/drawer.js +155 -147
  124. package/core/renderers/geras/highlight_constants.js +244 -238
  125. package/core/renderers/geras/highlighter.js +231 -179
  126. package/core/renderers/geras/info.js +392 -369
  127. package/core/renderers/geras/measurables/inline_input.js +25 -19
  128. package/core/renderers/geras/measurables/statement_input.js +23 -17
  129. package/core/renderers/geras/path_object.js +106 -121
  130. package/core/renderers/geras/renderer.js +96 -98
  131. package/core/renderers/measurables/base.js +30 -18
  132. package/core/renderers/measurables/bottom_row.js +83 -80
  133. package/core/renderers/measurables/connection.js +22 -15
  134. package/core/renderers/measurables/external_value_input.js +35 -22
  135. package/core/renderers/measurables/field.js +35 -20
  136. package/core/renderers/measurables/hat.js +18 -13
  137. package/core/renderers/measurables/icon.js +24 -17
  138. package/core/renderers/measurables/in_row_spacer.js +15 -13
  139. package/core/renderers/measurables/inline_input.js +43 -33
  140. package/core/renderers/measurables/input_connection.js +41 -28
  141. package/core/renderers/measurables/input_row.js +50 -44
  142. package/core/renderers/measurables/jagged_edge.js +14 -12
  143. package/core/renderers/measurables/next_connection.js +16 -14
  144. package/core/renderers/measurables/output_connection.js +26 -20
  145. package/core/renderers/measurables/previous_connection.js +16 -15
  146. package/core/renderers/measurables/round_corner.js +20 -18
  147. package/core/renderers/measurables/row.js +184 -168
  148. package/core/renderers/measurables/spacer_row.js +38 -23
  149. package/core/renderers/measurables/square_corner.js +18 -16
  150. package/core/renderers/measurables/statement_input.js +23 -20
  151. package/core/renderers/measurables/top_row.js +88 -85
  152. package/core/renderers/minimalist/constants.js +8 -7
  153. package/core/renderers/minimalist/drawer.js +11 -10
  154. package/core/renderers/minimalist/info.js +18 -18
  155. package/core/renderers/minimalist/renderer.js +40 -39
  156. package/core/renderers/thrasos/info.js +258 -248
  157. package/core/renderers/thrasos/renderer.js +20 -20
  158. package/core/renderers/zelos/constants.js +898 -873
  159. package/core/renderers/zelos/drawer.js +186 -169
  160. package/core/renderers/zelos/info.js +502 -479
  161. package/core/renderers/zelos/marker_svg.js +129 -115
  162. package/core/renderers/zelos/measurables/bottom_row.js +31 -30
  163. package/core/renderers/zelos/measurables/inputs.js +22 -21
  164. package/core/renderers/zelos/measurables/row_elements.js +14 -13
  165. package/core/renderers/zelos/measurables/top_row.js +34 -33
  166. package/core/renderers/zelos/path_object.js +181 -180
  167. package/core/renderers/zelos/renderer.js +91 -92
  168. package/core/scrollbar.js +759 -713
  169. package/core/scrollbar_pair.js +250 -245
  170. package/core/serialization/blocks.js +19 -9
  171. package/core/serialization/workspaces.js +3 -2
  172. package/core/shortcut_registry.js +286 -277
  173. package/core/sprites.js +31 -0
  174. package/core/theme.js +135 -141
  175. package/core/theme_manager.js +147 -143
  176. package/core/toolbox/category.js +602 -576
  177. package/core/toolbox/collapsible_category.js +226 -227
  178. package/core/toolbox/separator.js +70 -61
  179. package/core/toolbox/toolbox.js +934 -927
  180. package/core/toolbox/toolbox_item.js +115 -99
  181. package/core/tooltip.js +108 -35
  182. package/core/touch.js +8 -3
  183. package/core/touch_gesture.js +254 -251
  184. package/core/trashcan.js +606 -595
  185. package/core/utils/coordinate.js +97 -95
  186. package/core/utils/dom.js +2 -2
  187. package/core/utils/global.js +2 -0
  188. package/core/utils/rect.js +41 -37
  189. package/core/utils/sentinel.js +25 -0
  190. package/core/utils/size.js +30 -27
  191. package/core/utils/svg.js +18 -16
  192. package/core/variable_map.js +325 -341
  193. package/core/variable_model.js +55 -54
  194. package/core/variables.js +9 -2
  195. package/core/variables_dynamic.js +3 -1
  196. package/core/warning.js +126 -120
  197. package/core/widgetdiv.js +4 -4
  198. package/core/workspace.js +685 -664
  199. package/core/workspace_audio.js +124 -118
  200. package/core/workspace_comment.js +308 -298
  201. package/core/workspace_comment_svg.js +1029 -951
  202. package/core/workspace_drag_surface_svg.js +147 -140
  203. package/core/workspace_dragger.js +70 -71
  204. package/core/workspace_svg.js +2322 -2297
  205. package/core/xml.js +30 -20
  206. package/core/zoom_controls.js +431 -439
  207. package/dart_compressed.js +40 -43
  208. package/dart_compressed.js.map +1 -1
  209. package/generators/dart/colour.js +56 -64
  210. package/generators/dart/lists.js +61 -50
  211. package/generators/dart/math.js +160 -148
  212. package/generators/dart/text.js +83 -61
  213. package/generators/javascript/colour.js +37 -34
  214. package/generators/javascript/lists.js +50 -43
  215. package/generators/javascript/math.js +123 -139
  216. package/generators/javascript/text.js +67 -81
  217. package/generators/lua/colour.js +25 -23
  218. package/generators/lua/lists.js +97 -69
  219. package/generators/lua/logic.js +1 -2
  220. package/generators/lua/math.js +182 -144
  221. package/generators/lua/text.js +116 -99
  222. package/generators/php/colour.js +38 -32
  223. package/generators/php/lists.js +109 -89
  224. package/generators/php/math.js +90 -81
  225. package/generators/php/text.js +63 -61
  226. package/generators/python/colour.js +18 -18
  227. package/generators/python/lists.js +38 -30
  228. package/generators/python/loops.js +12 -8
  229. package/generators/python/math.js +104 -106
  230. package/generators/python/text.js +34 -30
  231. package/javascript_compressed.js +37 -39
  232. package/javascript_compressed.js.map +1 -1
  233. package/lua_compressed.js +39 -42
  234. package/lua_compressed.js.map +1 -1
  235. package/msg/az.js +2 -2
  236. package/msg/be.js +4 -4
  237. package/msg/cs.js +15 -15
  238. package/msg/de.js +1 -1
  239. package/msg/diq.js +1 -1
  240. package/msg/eo.js +1 -1
  241. package/msg/es.js +1 -1
  242. package/msg/fa.js +1 -1
  243. package/msg/fr.js +4 -4
  244. package/msg/he.js +1 -1
  245. package/msg/hr.js +2 -2
  246. package/msg/hy.js +2 -2
  247. package/msg/id.js +12 -12
  248. package/msg/inh.js +14 -14
  249. package/msg/ja.js +7 -7
  250. package/msg/lv.js +29 -29
  251. package/msg/pa.js +3 -3
  252. package/msg/smn.js +436 -0
  253. package/msg/te.js +1 -1
  254. package/msg/yue.js +1 -1
  255. package/msg/zh-hans.js +3 -3
  256. package/msg/zh-hant.js +3 -3
  257. package/package.json +7 -6
  258. package/php_compressed.js +38 -42
  259. package/php_compressed.js.map +1 -1
  260. package/python_compressed.js +26 -25
  261. package/python_compressed.js.map +1 -1
  262. package/blocks/all.js +0 -23
package/core/generator.js CHANGED
@@ -29,390 +29,508 @@ const {Workspace} = goog.requireType('Blockly.Workspace');
29
29
 
30
30
  /**
31
31
  * Class for a code generator that translates the blocks into a language.
32
- * @param {string} name Language name of this generator.
33
- * @constructor
32
+ * @unrestricted
34
33
  * @alias Blockly.Generator
35
34
  */
36
- const Generator = function(name) {
37
- this.name_ = name;
38
- this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ =
39
- new RegExp(this.FUNCTION_NAME_PLACEHOLDER_, 'g');
40
- };
35
+ class Generator {
36
+ /**
37
+ * @param {string} name Language name of this generator.
38
+ */
39
+ constructor(name) {
40
+ this.name_ = name;
41
41
 
42
- /**
43
- * Arbitrary code to inject into locations that risk causing infinite loops.
44
- * Any instances of '%1' will be replaced by the block ID that failed.
45
- * E.g. ' checkTimeout(%1);\n'
46
- * @type {?string}
47
- */
48
- Generator.prototype.INFINITE_LOOP_TRAP = null;
42
+ /**
43
+ * This is used as a placeholder in functions defined using
44
+ * Generator.provideFunction_. It must not be legal code that could
45
+ * legitimately appear in a function definition (or comment), and it must
46
+ * not confuse the regular expression parser.
47
+ * @type {string}
48
+ * @protected
49
+ */
50
+ this.FUNCTION_NAME_PLACEHOLDER_ = '{leCUI8hutHZI4480Dc}';
49
51
 
50
- /**
51
- * Arbitrary code to inject before every statement.
52
- * Any instances of '%1' will be replaced by the block ID of the statement.
53
- * E.g. 'highlight(%1);\n'
54
- * @type {?string}
55
- */
56
- Generator.prototype.STATEMENT_PREFIX = null;
52
+ this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ =
53
+ new RegExp(this.FUNCTION_NAME_PLACEHOLDER_, 'g');
57
54
 
58
- /**
59
- * Arbitrary code to inject after every statement.
60
- * Any instances of '%1' will be replaced by the block ID of the statement.
61
- * E.g. 'highlight(%1);\n'
62
- * @type {?string}
63
- */
64
- Generator.prototype.STATEMENT_SUFFIX = null;
55
+ /**
56
+ * Arbitrary code to inject into locations that risk causing infinite loops.
57
+ * Any instances of '%1' will be replaced by the block ID that failed.
58
+ * E.g. ' checkTimeout(%1);\n'
59
+ * @type {?string}
60
+ */
61
+ this.INFINITE_LOOP_TRAP = null;
65
62
 
66
- /**
67
- * The method of indenting. Defaults to two spaces, but language generators
68
- * may override this to increase indent or change to tabs.
69
- * @type {string}
70
- */
71
- Generator.prototype.INDENT = ' ';
63
+ /**
64
+ * Arbitrary code to inject before every statement.
65
+ * Any instances of '%1' will be replaced by the block ID of the statement.
66
+ * E.g. 'highlight(%1);\n'
67
+ * @type {?string}
68
+ */
69
+ this.STATEMENT_PREFIX = null;
72
70
 
73
- /**
74
- * Maximum length for a comment before wrapping. Does not account for
75
- * indenting level.
76
- * @type {number}
77
- */
78
- Generator.prototype.COMMENT_WRAP = 60;
71
+ /**
72
+ * Arbitrary code to inject after every statement.
73
+ * Any instances of '%1' will be replaced by the block ID of the statement.
74
+ * E.g. 'highlight(%1);\n'
75
+ * @type {?string}
76
+ */
77
+ this.STATEMENT_SUFFIX = null;
79
78
 
80
- /**
81
- * List of outer-inner pairings that do NOT require parentheses.
82
- * @type {!Array<!Array<number>>}
83
- */
84
- Generator.prototype.ORDER_OVERRIDES = [];
79
+ /**
80
+ * The method of indenting. Defaults to two spaces, but language generators
81
+ * may override this to increase indent or change to tabs.
82
+ * @type {string}
83
+ */
84
+ this.INDENT = ' ';
85
85
 
86
- /**
87
- * Whether the init method has been called.
88
- * Generators that set this flag to false after creation and true in init
89
- * will cause blockToCode to emit a warning if the generator has not been
90
- * initialized. If this flag is untouched, it will have no effect.
91
- * @type {?boolean}
92
- */
93
- Generator.prototype.isInitialized = null;
86
+ /**
87
+ * Maximum length for a comment before wrapping. Does not account for
88
+ * indenting level.
89
+ * @type {number}
90
+ */
91
+ this.COMMENT_WRAP = 60;
94
92
 
95
- /**
96
- * Generate code for all blocks in the workspace to the specified language.
97
- * @param {!Workspace=} workspace Workspace to generate code from.
98
- * @return {string} Generated code.
99
- */
100
- Generator.prototype.workspaceToCode = function(workspace) {
101
- if (!workspace) {
102
- // Backwards compatibility from before there could be multiple workspaces.
103
- console.warn('No workspace specified in workspaceToCode call. Guessing.');
104
- workspace = common.getMainWorkspace();
93
+ /**
94
+ * List of outer-inner pairings that do NOT require parentheses.
95
+ * @type {!Array<!Array<number>>}
96
+ */
97
+ this.ORDER_OVERRIDES = [];
98
+
99
+ /**
100
+ * Whether the init method has been called.
101
+ * Generators that set this flag to false after creation and true in init
102
+ * will cause blockToCode to emit a warning if the generator has not been
103
+ * initialized. If this flag is untouched, it will have no effect.
104
+ * @type {?boolean}
105
+ */
106
+ this.isInitialized = null;
107
+
108
+ /**
109
+ * Comma-separated list of reserved words.
110
+ * @type {string}
111
+ * @protected
112
+ */
113
+ this.RESERVED_WORDS_ = '';
114
+
115
+ /**
116
+ * A dictionary of definitions to be printed before the code.
117
+ * @type {!Object|undefined}
118
+ * @protected
119
+ */
120
+ this.definitions_ = undefined;
121
+
122
+ /**
123
+ * A dictionary mapping desired function names in definitions_ to actual
124
+ * function names (to avoid collisions with user functions).
125
+ * @type {!Object|undefined}
126
+ * @protected
127
+ */
128
+ this.functionNames_ = undefined;
129
+
130
+ /**
131
+ * A database of variable and procedure names.
132
+ * @type {!Names|undefined}
133
+ * @protected
134
+ */
135
+ this.nameDB_ = undefined;
105
136
  }
106
- let code = [];
107
- this.init(workspace);
108
- const blocks = workspace.getTopBlocks(true);
109
- for (let i = 0, block; (block = blocks[i]); i++) {
110
- let line = this.blockToCode(block);
111
- if (Array.isArray(line)) {
112
- // Value blocks return tuples of code and operator order.
113
- // Top-level blocks don't care about operator order.
114
- line = line[0];
137
+
138
+ /**
139
+ * Generate code for all blocks in the workspace to the specified language.
140
+ * @param {!Workspace=} workspace Workspace to generate code from.
141
+ * @return {string} Generated code.
142
+ */
143
+ workspaceToCode(workspace) {
144
+ if (!workspace) {
145
+ // Backwards compatibility from before there could be multiple workspaces.
146
+ console.warn(
147
+ 'No workspace specified in workspaceToCode call. Guessing.');
148
+ workspace = common.getMainWorkspace();
115
149
  }
116
- if (line) {
117
- if (block.outputConnection) {
118
- // This block is a naked value. Ask the language's code generator if
119
- // it wants to append a semicolon, or something.
120
- line = this.scrubNakedValue(line);
121
- if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
122
- line = this.injectId(this.STATEMENT_PREFIX, block) + line;
123
- }
124
- if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
125
- line = line + this.injectId(this.STATEMENT_SUFFIX, block);
150
+ let code = [];
151
+ this.init(workspace);
152
+ const blocks = workspace.getTopBlocks(true);
153
+ for (let i = 0, block; (block = blocks[i]); i++) {
154
+ let line = this.blockToCode(block);
155
+ if (Array.isArray(line)) {
156
+ // Value blocks return tuples of code and operator order.
157
+ // Top-level blocks don't care about operator order.
158
+ line = line[0];
159
+ }
160
+ if (line) {
161
+ if (block.outputConnection) {
162
+ // This block is a naked value. Ask the language's code generator if
163
+ // it wants to append a semicolon, or something.
164
+ line = this.scrubNakedValue(line);
165
+ if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
166
+ line = this.injectId(this.STATEMENT_PREFIX, block) + line;
167
+ }
168
+ if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
169
+ line = line + this.injectId(this.STATEMENT_SUFFIX, block);
170
+ }
126
171
  }
172
+ code.push(line);
127
173
  }
128
- code.push(line);
129
174
  }
175
+ code = code.join('\n'); // Blank line between each section.
176
+ code = this.finish(code);
177
+ // Final scrubbing of whitespace.
178
+ code = code.replace(/^\s+\n/, '');
179
+ code = code.replace(/\n\s+$/, '\n');
180
+ code = code.replace(/[ \t]+\n/g, '\n');
181
+ return code;
130
182
  }
131
- code = code.join('\n'); // Blank line between each section.
132
- code = this.finish(code);
133
- // Final scrubbing of whitespace.
134
- code = code.replace(/^\s+\n/, '');
135
- code = code.replace(/\n\s+$/, '\n');
136
- code = code.replace(/[ \t]+\n/g, '\n');
137
- return code;
138
- };
139
-
140
- // The following are some helpful functions which can be used by multiple
141
- // languages.
142
183
 
143
- /**
144
- * Prepend a common prefix onto each line of code.
145
- * Intended for indenting code or adding comment markers.
146
- * @param {string} text The lines of code.
147
- * @param {string} prefix The common prefix.
148
- * @return {string} The prefixed lines of code.
149
- */
150
- Generator.prototype.prefixLines = function(text, prefix) {
151
- return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix);
152
- };
184
+ // The following are some helpful functions which can be used by multiple
153
185
 
154
- /**
155
- * Recursively spider a tree of blocks, returning all their comments.
156
- * @param {!Block} block The block from which to start spidering.
157
- * @return {string} Concatenated list of comments.
158
- */
159
- Generator.prototype.allNestedComments = function(block) {
160
- const comments = [];
161
- const blocks = block.getDescendants(true);
162
- for (let i = 0; i < blocks.length; i++) {
163
- const comment = blocks[i].getCommentText();
164
- if (comment) {
165
- comments.push(comment);
166
- }
167
- }
168
- // Append an empty string to create a trailing line break when joined.
169
- if (comments.length) {
170
- comments.push('');
171
- }
172
- return comments.join('\n');
173
- };
186
+ // languages.
174
187
 
175
- /**
176
- * Generate code for the specified block (and attached blocks).
177
- * The generator must be initialized before calling this function.
178
- * @param {Block} block The block to generate code for.
179
- * @param {boolean=} opt_thisOnly True to generate code for only this statement.
180
- * @return {string|!Array} For statement blocks, the generated code.
181
- * For value blocks, an array containing the generated code and an
182
- * operator order value. Returns '' if block is null.
183
- */
184
- Generator.prototype.blockToCode = function(block, opt_thisOnly) {
185
- if (this.isInitialized === false) {
186
- console.warn(
187
- 'Generator init was not called before blockToCode was called.');
188
- }
189
- if (!block) {
190
- return '';
191
- }
192
- if (!block.isEnabled()) {
193
- // Skip past this block if it is disabled.
194
- return opt_thisOnly ? '' : this.blockToCode(block.getNextBlock());
195
- }
196
- if (block.isInsertionMarker()) {
197
- // Skip past insertion markers.
198
- return opt_thisOnly ? '' : this.blockToCode(block.getChildren(false)[0]);
188
+ /**
189
+ * Prepend a common prefix onto each line of code.
190
+ * Intended for indenting code or adding comment markers.
191
+ * @param {string} text The lines of code.
192
+ * @param {string} prefix The common prefix.
193
+ * @return {string} The prefixed lines of code.
194
+ */
195
+ prefixLines(text, prefix) {
196
+ return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix);
199
197
  }
200
198
 
201
- const func = this[block.type];
202
- if (typeof func !== 'function') {
203
- throw Error(
204
- 'Language "' + this.name_ + '" does not know how to generate ' +
205
- 'code for block type "' + block.type + '".');
199
+ /**
200
+ * Recursively spider a tree of blocks, returning all their comments.
201
+ * @param {!Block} block The block from which to start spidering.
202
+ * @return {string} Concatenated list of comments.
203
+ */
204
+ allNestedComments(block) {
205
+ const comments = [];
206
+ const blocks = block.getDescendants(true);
207
+ for (let i = 0; i < blocks.length; i++) {
208
+ const comment = blocks[i].getCommentText();
209
+ if (comment) {
210
+ comments.push(comment);
211
+ }
212
+ }
213
+ // Append an empty string to create a trailing line break when joined.
214
+ if (comments.length) {
215
+ comments.push('');
216
+ }
217
+ return comments.join('\n');
206
218
  }
207
- // First argument to func.call is the value of 'this' in the generator.
208
- // Prior to 24 September 2013 'this' was the only way to access the block.
209
- // The current preferred method of accessing the block is through the second
210
- // argument to func.call, which becomes the first parameter to the generator.
211
- let code = func.call(block, block);
212
- if (Array.isArray(code)) {
213
- // Value blocks return tuples of code and operator order.
214
- if (!block.outputConnection) {
215
- throw TypeError('Expecting string from statement block: ' + block.type);
219
+
220
+ /**
221
+ * Generate code for the specified block (and attached blocks).
222
+ * The generator must be initialized before calling this function.
223
+ * @param {?Block} block The block to generate code for.
224
+ * @param {boolean=} opt_thisOnly True to generate code for only this
225
+ * statement.
226
+ * @return {string|!Array} For statement blocks, the generated code.
227
+ * For value blocks, an array containing the generated code and an
228
+ * operator order value. Returns '' if block is null.
229
+ */
230
+ blockToCode(block, opt_thisOnly) {
231
+ if (this.isInitialized === false) {
232
+ console.warn(
233
+ 'Generator init was not called before blockToCode was called.');
216
234
  }
217
- return [this.scrub_(block, code[0], opt_thisOnly), code[1]];
218
- } else if (typeof code === 'string') {
219
- if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
220
- code = this.injectId(this.STATEMENT_PREFIX, block) + code;
235
+ if (!block) {
236
+ return '';
221
237
  }
222
- if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
223
- code = code + this.injectId(this.STATEMENT_SUFFIX, block);
238
+ if (!block.isEnabled()) {
239
+ // Skip past this block if it is disabled.
240
+ return opt_thisOnly ? '' : this.blockToCode(block.getNextBlock());
241
+ }
242
+ if (block.isInsertionMarker()) {
243
+ // Skip past insertion markers.
244
+ return opt_thisOnly ? '' : this.blockToCode(block.getChildren(false)[0]);
224
245
  }
225
- return this.scrub_(block, code, opt_thisOnly);
226
- } else if (code === null) {
227
- // Block has handled code generation itself.
228
- return '';
229
- }
230
- throw SyntaxError('Invalid code generated: ' + code);
231
- };
232
246
 
233
- /**
234
- * Generate code representing the specified value input.
235
- * @param {!Block} block The block containing the input.
236
- * @param {string} name The name of the input.
237
- * @param {number} outerOrder The maximum binding strength (minimum order value)
238
- * of any operators adjacent to "block".
239
- * @return {string} Generated code or '' if no blocks are connected or the
240
- * specified input does not exist.
241
- */
242
- Generator.prototype.valueToCode = function(block, name, outerOrder) {
243
- if (isNaN(outerOrder)) {
244
- throw TypeError('Expecting valid order from block: ' + block.type);
245
- }
246
- const targetBlock = block.getInputTargetBlock(name);
247
- if (!targetBlock) {
248
- return '';
249
- }
250
- const tuple = this.blockToCode(targetBlock);
251
- if (tuple === '') {
252
- // Disabled block.
253
- return '';
254
- }
255
- // Value blocks must return code and order of operations info.
256
- // Statement blocks must only return code.
257
- if (!Array.isArray(tuple)) {
258
- throw TypeError('Expecting tuple from value block: ' + targetBlock.type);
259
- }
260
- let code = tuple[0];
261
- const innerOrder = tuple[1];
262
- if (isNaN(innerOrder)) {
263
- throw TypeError(
264
- 'Expecting valid order from value block: ' + targetBlock.type);
265
- }
266
- if (!code) {
267
- return '';
247
+ const func = this[block.type];
248
+ if (typeof func !== 'function') {
249
+ throw Error(
250
+ 'Language "' + this.name_ + '" does not know how to generate ' +
251
+ 'code for block type "' + block.type + '".');
252
+ }
253
+ // First argument to func.call is the value of 'this' in the generator.
254
+ // Prior to 24 September 2013 'this' was the only way to access the block.
255
+ // The current preferred method of accessing the block is through the second
256
+ // argument to func.call, which becomes the first parameter to the
257
+ // generator.
258
+ let code = func.call(block, block);
259
+ if (Array.isArray(code)) {
260
+ // Value blocks return tuples of code and operator order.
261
+ if (!block.outputConnection) {
262
+ throw TypeError('Expecting string from statement block: ' + block.type);
263
+ }
264
+ return [this.scrub_(block, code[0], opt_thisOnly), code[1]];
265
+ } else if (typeof code === 'string') {
266
+ if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
267
+ code = this.injectId(this.STATEMENT_PREFIX, block) + code;
268
+ }
269
+ if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
270
+ code = code + this.injectId(this.STATEMENT_SUFFIX, block);
271
+ }
272
+ return this.scrub_(block, code, opt_thisOnly);
273
+ } else if (code === null) {
274
+ // Block has handled code generation itself.
275
+ return '';
276
+ }
277
+ throw SyntaxError('Invalid code generated: ' + code);
268
278
  }
269
279
 
270
- // Add parentheses if needed.
271
- let parensNeeded = false;
272
- const outerOrderClass = Math.floor(outerOrder);
273
- const innerOrderClass = Math.floor(innerOrder);
274
- if (outerOrderClass <= innerOrderClass) {
275
- if (outerOrderClass === innerOrderClass &&
276
- (outerOrderClass === 0 || outerOrderClass === 99)) {
277
- // Don't generate parens around NONE-NONE and ATOMIC-ATOMIC pairs.
278
- // 0 is the atomic order, 99 is the none order. No parentheses needed.
279
- // In all known languages multiple such code blocks are not order
280
- // sensitive. In fact in Python ('a' 'b') 'c' would fail.
281
- } else {
282
- // The operators outside this code are stronger than the operators
283
- // inside this code. To prevent the code from being pulled apart,
284
- // wrap the code in parentheses.
285
- parensNeeded = true;
286
- // Check for special exceptions.
287
- for (let i = 0; i < this.ORDER_OVERRIDES.length; i++) {
288
- if (this.ORDER_OVERRIDES[i][0] === outerOrder &&
289
- this.ORDER_OVERRIDES[i][1] === innerOrder) {
290
- parensNeeded = false;
291
- break;
280
+ /**
281
+ * Generate code representing the specified value input.
282
+ * @param {!Block} block The block containing the input.
283
+ * @param {string} name The name of the input.
284
+ * @param {number} outerOrder The maximum binding strength (minimum order
285
+ * value) of any operators adjacent to "block".
286
+ * @return {string} Generated code or '' if no blocks are connected or the
287
+ * specified input does not exist.
288
+ */
289
+ valueToCode(block, name, outerOrder) {
290
+ if (isNaN(outerOrder)) {
291
+ throw TypeError('Expecting valid order from block: ' + block.type);
292
+ }
293
+ const targetBlock = block.getInputTargetBlock(name);
294
+ if (!targetBlock) {
295
+ return '';
296
+ }
297
+ const tuple = this.blockToCode(targetBlock);
298
+ if (tuple === '') {
299
+ // Disabled block.
300
+ return '';
301
+ }
302
+ // Value blocks must return code and order of operations info.
303
+ // Statement blocks must only return code.
304
+ if (!Array.isArray(tuple)) {
305
+ throw TypeError('Expecting tuple from value block: ' + targetBlock.type);
306
+ }
307
+ let code = tuple[0];
308
+ const innerOrder = tuple[1];
309
+ if (isNaN(innerOrder)) {
310
+ throw TypeError(
311
+ 'Expecting valid order from value block: ' + targetBlock.type);
312
+ }
313
+ if (!code) {
314
+ return '';
315
+ }
316
+
317
+ // Add parentheses if needed.
318
+ let parensNeeded = false;
319
+ const outerOrderClass = Math.floor(outerOrder);
320
+ const innerOrderClass = Math.floor(innerOrder);
321
+ if (outerOrderClass <= innerOrderClass) {
322
+ if (outerOrderClass === innerOrderClass &&
323
+ (outerOrderClass === 0 || outerOrderClass === 99)) {
324
+ // Don't generate parens around NONE-NONE and ATOMIC-ATOMIC pairs.
325
+ // 0 is the atomic order, 99 is the none order. No parentheses needed.
326
+ // In all known languages multiple such code blocks are not order
327
+ // sensitive. In fact in Python ('a' 'b') 'c' would fail.
328
+ } else {
329
+ // The operators outside this code are stronger than the operators
330
+ // inside this code. To prevent the code from being pulled apart,
331
+ // wrap the code in parentheses.
332
+ parensNeeded = true;
333
+ // Check for special exceptions.
334
+ for (let i = 0; i < this.ORDER_OVERRIDES.length; i++) {
335
+ if (this.ORDER_OVERRIDES[i][0] === outerOrder &&
336
+ this.ORDER_OVERRIDES[i][1] === innerOrder) {
337
+ parensNeeded = false;
338
+ break;
339
+ }
292
340
  }
293
341
  }
294
342
  }
343
+ if (parensNeeded) {
344
+ // Technically, this should be handled on a language-by-language basis.
345
+ // However all known (sane) languages use parentheses for grouping.
346
+ code = '(' + code + ')';
347
+ }
348
+ return code;
295
349
  }
296
- if (parensNeeded) {
297
- // Technically, this should be handled on a language-by-language basis.
298
- // However all known (sane) languages use parentheses for grouping.
299
- code = '(' + code + ')';
300
- }
301
- return code;
302
- };
303
350
 
304
- /**
305
- * Generate a code string representing the blocks attached to the named
306
- * statement input. Indent the code.
307
- * This is mainly used in generators. When trying to generate code to evaluate
308
- * look at using workspaceToCode or blockToCode.
309
- * @param {!Block} block The block containing the input.
310
- * @param {string} name The name of the input.
311
- * @return {string} Generated code or '' if no blocks are connected.
312
- */
313
- Generator.prototype.statementToCode = function(block, name) {
314
- const targetBlock = block.getInputTargetBlock(name);
315
- let code = this.blockToCode(targetBlock);
316
- // Value blocks must return code and order of operations info.
317
- // Statement blocks must only return code.
318
- if (typeof code !== 'string') {
319
- throw TypeError(
320
- 'Expecting code from statement block: ' +
321
- (targetBlock && targetBlock.type));
322
- }
323
- if (code) {
324
- code = this.prefixLines(/** @type {string} */ (code), this.INDENT);
351
+ /**
352
+ * Generate a code string representing the blocks attached to the named
353
+ * statement input. Indent the code.
354
+ * This is mainly used in generators. When trying to generate code to evaluate
355
+ * look at using workspaceToCode or blockToCode.
356
+ * @param {!Block} block The block containing the input.
357
+ * @param {string} name The name of the input.
358
+ * @return {string} Generated code or '' if no blocks are connected.
359
+ */
360
+ statementToCode(block, name) {
361
+ const targetBlock = block.getInputTargetBlock(name);
362
+ let code = this.blockToCode(targetBlock);
363
+ // Value blocks must return code and order of operations info.
364
+ // Statement blocks must only return code.
365
+ if (typeof code !== 'string') {
366
+ throw TypeError(
367
+ 'Expecting code from statement block: ' +
368
+ (targetBlock && targetBlock.type));
369
+ }
370
+ if (code) {
371
+ code = this.prefixLines(/** @type {string} */ (code), this.INDENT);
372
+ }
373
+ return code;
325
374
  }
326
- return code;
327
- };
328
375
 
329
- /**
330
- * Add an infinite loop trap to the contents of a loop.
331
- * Add statement suffix at the start of the loop block (right after the loop
332
- * statement executes), and a statement prefix to the end of the loop block
333
- * (right before the loop statement executes).
334
- * @param {string} branch Code for loop contents.
335
- * @param {!Block} block Enclosing block.
336
- * @return {string} Loop contents, with infinite loop trap added.
337
- */
338
- Generator.prototype.addLoopTrap = function(branch, block) {
339
- if (this.INFINITE_LOOP_TRAP) {
340
- branch = this.prefixLines(
341
- this.injectId(this.INFINITE_LOOP_TRAP, block), this.INDENT) +
342
- branch;
343
- }
344
- if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
345
- branch = this.prefixLines(
346
- this.injectId(this.STATEMENT_SUFFIX, block), this.INDENT) +
347
- branch;
348
- }
349
- if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
350
- branch = branch +
351
- this.prefixLines(
352
- this.injectId(this.STATEMENT_PREFIX, block), this.INDENT);
376
+ /**
377
+ * Add an infinite loop trap to the contents of a loop.
378
+ * Add statement suffix at the start of the loop block (right after the loop
379
+ * statement executes), and a statement prefix to the end of the loop block
380
+ * (right before the loop statement executes).
381
+ * @param {string} branch Code for loop contents.
382
+ * @param {!Block} block Enclosing block.
383
+ * @return {string} Loop contents, with infinite loop trap added.
384
+ */
385
+ addLoopTrap(branch, block) {
386
+ if (this.INFINITE_LOOP_TRAP) {
387
+ branch = this.prefixLines(
388
+ this.injectId(this.INFINITE_LOOP_TRAP, block), this.INDENT) +
389
+ branch;
390
+ }
391
+ if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) {
392
+ branch = this.prefixLines(
393
+ this.injectId(this.STATEMENT_SUFFIX, block), this.INDENT) +
394
+ branch;
395
+ }
396
+ if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) {
397
+ branch = branch +
398
+ this.prefixLines(
399
+ this.injectId(this.STATEMENT_PREFIX, block), this.INDENT);
400
+ }
401
+ return branch;
353
402
  }
354
- return branch;
355
- };
356
403
 
357
- /**
358
- * Inject a block ID into a message to replace '%1'.
359
- * Used for STATEMENT_PREFIX, STATEMENT_SUFFIX, and INFINITE_LOOP_TRAP.
360
- * @param {string} msg Code snippet with '%1'.
361
- * @param {!Block} block Block which has an ID.
362
- * @return {string} Code snippet with ID.
363
- */
364
- Generator.prototype.injectId = function(msg, block) {
365
- const id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
366
- return msg.replace(/%1/g, '\'' + id + '\'');
367
- };
404
+ /**
405
+ * Inject a block ID into a message to replace '%1'.
406
+ * Used for STATEMENT_PREFIX, STATEMENT_SUFFIX, and INFINITE_LOOP_TRAP.
407
+ * @param {string} msg Code snippet with '%1'.
408
+ * @param {!Block} block Block which has an ID.
409
+ * @return {string} Code snippet with ID.
410
+ */
411
+ injectId(msg, block) {
412
+ const id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
413
+ return msg.replace(/%1/g, '\'' + id + '\'');
414
+ }
368
415
 
369
- /**
370
- * Comma-separated list of reserved words.
371
- * @type {string}
372
- * @protected
373
- */
374
- Generator.prototype.RESERVED_WORDS_ = '';
416
+ /**
417
+ * Add one or more words to the list of reserved words for this language.
418
+ * @param {string} words Comma-separated list of words to add to the list.
419
+ * No spaces. Duplicates are ok.
420
+ */
421
+ addReservedWords(words) {
422
+ this.RESERVED_WORDS_ += words + ',';
423
+ }
375
424
 
376
- /**
377
- * Add one or more words to the list of reserved words for this language.
378
- * @param {string} words Comma-separated list of words to add to the list.
379
- * No spaces. Duplicates are ok.
380
- */
381
- Generator.prototype.addReservedWords = function(words) {
382
- this.RESERVED_WORDS_ += words + ',';
383
- };
425
+ /**
426
+ * Define a developer-defined function (not a user-defined procedure) to be
427
+ * included in the generated code. Used for creating private helper
428
+ * functions. The first time this is called with a given desiredName, the code
429
+ * is saved and an actual name is generated. Subsequent calls with the same
430
+ * desiredName have no effect but have the same return value.
431
+ *
432
+ * It is up to the caller to make sure the same desiredName is not
433
+ * used for different helper functions (e.g. use "colourRandom" and
434
+ * "listRandom", not "random"). There is no danger of colliding with reserved
435
+ * words, or user-defined variable or procedure names.
436
+ *
437
+ * The code gets output when Generator.finish() is called.
438
+ *
439
+ * @param {string} desiredName The desired name of the function
440
+ * (e.g. mathIsPrime).
441
+ * @param {!Array<string>|string} code A list of statements or one multi-line
442
+ * code string. Use ' ' for indents (they will be replaced).
443
+ * @return {string} The actual name of the new function. This may differ
444
+ * from desiredName if the former has already been taken by the user.
445
+ * @protected
446
+ */
447
+ provideFunction_(desiredName, code) {
448
+ if (!this.definitions_[desiredName]) {
449
+ const functionName =
450
+ this.nameDB_.getDistinctName(desiredName, NameType.PROCEDURE);
451
+ this.functionNames_[desiredName] = functionName;
452
+ if (Array.isArray(code)) {
453
+ code = code.join('\n');
454
+ }
455
+ let codeText = code.trim().replace(
456
+ this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName);
457
+ // Change all ' ' indents into the desired indent.
458
+ // To avoid an infinite loop of replacements, change all indents to '\0'
459
+ // character first, then replace them all with the indent.
460
+ // We are assuming that no provided functions contain a literal null char.
461
+ let oldCodeText;
462
+ while (oldCodeText !== codeText) {
463
+ oldCodeText = codeText;
464
+ codeText = codeText.replace(/^(( {2})*) {2}/gm, '$1\0');
465
+ }
466
+ codeText = codeText.replace(/\0/g, this.INDENT);
467
+ this.definitions_[desiredName] = codeText;
468
+ }
469
+ return this.functionNames_[desiredName];
470
+ }
384
471
 
385
- /**
386
- * This is used as a placeholder in functions defined using
387
- * Generator.provideFunction_. It must not be legal code that could
388
- * legitimately appear in a function definition (or comment), and it must
389
- * not confuse the regular expression parser.
390
- * @type {string}
391
- * @protected
392
- */
393
- Generator.prototype.FUNCTION_NAME_PLACEHOLDER_ = '{leCUI8hutHZI4480Dc}';
472
+ /**
473
+ * Hook for code to run before code generation starts.
474
+ * Subclasses may override this, e.g. to initialise the database of variable
475
+ * names.
476
+ * @param {!Workspace} _workspace Workspace to generate code from.
477
+ */
478
+ init(_workspace) {
479
+ // Optionally override
480
+ // Create a dictionary of definitions to be printed before the code.
481
+ this.definitions_ = Object.create(null);
482
+ // Create a dictionary mapping desired developer-defined function names in
483
+ // definitions_ to actual function names (to avoid collisions with
484
+ // user-defined procedures).
485
+ this.functionNames_ = Object.create(null);
486
+ }
394
487
 
395
- /**
396
- * A dictionary of definitions to be printed before the code.
397
- * @type {!Object|undefined}
398
- * @protected
399
- */
400
- Generator.prototype.definitions_;
488
+ /**
489
+ * Common tasks for generating code from blocks. This is called from
490
+ * blockToCode and is called on every block, not just top level blocks.
491
+ * Subclasses may override this, e.g. to generate code for statements
492
+ * following the block, or to handle comments for the specified block and any
493
+ * connected value blocks.
494
+ * @param {!Block} _block The current block.
495
+ * @param {string} code The code created for this block.
496
+ * @param {boolean=} _opt_thisOnly True to generate code for only this
497
+ * statement.
498
+ * @return {string} Code with comments and subsequent blocks added.
499
+ * @protected
500
+ */
501
+ scrub_(_block, code, _opt_thisOnly) {
502
+ // Optionally override
503
+ return code;
504
+ }
401
505
 
402
- /**
403
- * A dictionary mapping desired function names in definitions_ to actual
404
- * function names (to avoid collisions with user functions).
405
- * @type {!Object|undefined}
406
- * @protected
407
- */
408
- Generator.prototype.functionNames_;
506
+ /**
507
+ * Hook for code to run at end of code generation.
508
+ * Subclasses may override this, e.g. to prepend the generated code with
509
+ * import statements or variable definitions.
510
+ * @param {string} code Generated code.
511
+ * @return {string} Completed code.
512
+ */
513
+ finish(code) {
514
+ // Optionally override
515
+ // Clean up temporary data.
516
+ delete this.definitions_;
517
+ delete this.functionNames_;
518
+ return code;
519
+ }
409
520
 
410
- /**
411
- * A database of variable and procedure names.
412
- * @type {!Names|undefined}
413
- * @protected
414
- */
415
- Generator.prototype.nameDB_;
521
+ /**
522
+ * Naked values are top-level blocks with outputs that aren't plugged into
523
+ * anything.
524
+ * Subclasses may override this, e.g. if their language does not allow
525
+ * naked values.
526
+ * @param {string} line Line of generated code.
527
+ * @return {string} Legal line of code.
528
+ */
529
+ scrubNakedValue(line) {
530
+ // Optionally override
531
+ return line;
532
+ }
533
+ }
416
534
 
417
535
  Object.defineProperties(Generator.prototype, {
418
536
  /**
@@ -443,109 +561,4 @@ Object.defineProperties(Generator.prototype, {
443
561
  },
444
562
  });
445
563
 
446
- /**
447
- * Define a developer-defined function (not a user-defined procedure) to be
448
- * included in the generated code. Used for creating private helper functions.
449
- * The first time this is called with a given desiredName, the code is
450
- * saved and an actual name is generated. Subsequent calls with the
451
- * same desiredName have no effect but have the same return value.
452
- *
453
- * It is up to the caller to make sure the same desiredName is not
454
- * used for different helper functions (e.g. use "colourRandom" and
455
- * "listRandom", not "random"). There is no danger of colliding with reserved
456
- * words, or user-defined variable or procedure names.
457
- *
458
- * The code gets output when Generator.finish() is called.
459
- *
460
- * @param {string} desiredName The desired name of the function
461
- * (e.g. mathIsPrime).
462
- * @param {!Array<string>} code A list of statements. Use ' ' for indents.
463
- * @return {string} The actual name of the new function. This may differ
464
- * from desiredName if the former has already been taken by the user.
465
- * @protected
466
- */
467
- Generator.prototype.provideFunction_ = function(desiredName, code) {
468
- if (!this.definitions_[desiredName]) {
469
- const functionName =
470
- this.nameDB_.getDistinctName(desiredName, NameType.PROCEDURE);
471
- this.functionNames_[desiredName] = functionName;
472
- let codeText = code.join('\n').replace(
473
- this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName);
474
- // Change all ' ' indents into the desired indent.
475
- // To avoid an infinite loop of replacements, change all indents to '\0'
476
- // character first, then replace them all with the indent.
477
- // We are assuming that no provided functions contain a literal null char.
478
- let oldCodeText;
479
- while (oldCodeText !== codeText) {
480
- oldCodeText = codeText;
481
- codeText = codeText.replace(/^(( {2})*) {2}/gm, '$1\0');
482
- }
483
- codeText = codeText.replace(/\0/g, this.INDENT);
484
- this.definitions_[desiredName] = codeText;
485
- }
486
- return this.functionNames_[desiredName];
487
- };
488
-
489
- /**
490
- * Hook for code to run before code generation starts.
491
- * Subclasses may override this, e.g. to initialise the database of variable
492
- * names.
493
- * @param {!Workspace} _workspace Workspace to generate code from.
494
- */
495
- Generator.prototype.init = function(_workspace) {
496
- // Optionally override
497
- // Create a dictionary of definitions to be printed before the code.
498
- this.definitions_ = Object.create(null);
499
- // Create a dictionary mapping desired developer-defined function names in
500
- // definitions_ to actual function names (to avoid collisions with
501
- // user-defined procedures).
502
- this.functionNames_ = Object.create(null);
503
- };
504
-
505
- /**
506
- * Common tasks for generating code from blocks. This is called from
507
- * blockToCode and is called on every block, not just top level blocks.
508
- * Subclasses may override this, e.g. to generate code for statements following
509
- * the block, or to handle comments for the specified block and any connected
510
- * value blocks.
511
- * @param {!Block} _block The current block.
512
- * @param {string} code The code created for this block.
513
- * @param {boolean=} _opt_thisOnly True to generate code for only this
514
- * statement.
515
- * @return {string} Code with comments and subsequent blocks added.
516
- * @protected
517
- */
518
- Generator.prototype.scrub_ = function(_block, code, _opt_thisOnly) {
519
- // Optionally override
520
- return code;
521
- };
522
-
523
- /**
524
- * Hook for code to run at end of code generation.
525
- * Subclasses may override this, e.g. to prepend the generated code with import
526
- * statements or variable definitions.
527
- * @param {string} code Generated code.
528
- * @return {string} Completed code.
529
- */
530
- Generator.prototype.finish = function(code) {
531
- // Optionally override
532
- // Clean up temporary data.
533
- delete this.definitions_;
534
- delete this.functionNames_;
535
- return code;
536
- };
537
-
538
- /**
539
- * Naked values are top-level blocks with outputs that aren't plugged into
540
- * anything.
541
- * Subclasses may override this, e.g. if their language does not allow
542
- * naked values.
543
- * @param {string} line Line of generated code.
544
- * @return {string} Legal line of code.
545
- */
546
- Generator.prototype.scrubNakedValue = function(line) {
547
- // Optionally override
548
- return line;
549
- };
550
-
551
564
  exports.Generator = Generator;