From 3d0567fe46630dbc1783f2c324822d3bf27a4e59 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 20 Dec 2017 15:14:37 -0800 Subject: [PATCH 1/5] JS and Dart generator tests pass --- core/names.js | 40 ++++++++++++++++++++++--- core/variables.js | 32 ++++++++++++++++++++ generators/dart.js | 22 ++++++++++---- generators/javascript.js | 22 ++++++++++---- tests/generators/unittest.js | 8 ++--- tests/generators/unittest_dart.js | 6 ++-- tests/generators/unittest_javascript.js | 6 ++-- 7 files changed, 112 insertions(+), 24 deletions(-) diff --git a/core/names.js b/core/names.js index bfe942ab9..016c01538 100644 --- a/core/names.js +++ b/core/names.js @@ -26,6 +26,11 @@ goog.provide('Blockly.Names'); +/** + * Constant to separate developer variable names from user-defined variable + * names when running generators. + */ +Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE'; /** * Class for a database of entity names (variables, functions, etc). @@ -62,6 +67,23 @@ Blockly.Names = function(reservedWords, opt_variablePrefix) { Blockly.Names.prototype.reset = function() { this.db_ = Object.create(null); this.dbReverse_ = Object.create(null); + this.variableMap_ = null; +}; + +Blockly.Names.prototype.setVariableMap = function(map) { + this.variableMap_ = map; +}; + +Blockly.Names.prototype.getNameForVariable = function(id) { + if (!this.variableMap_) { + return null; + } + var variable = this.variableMap_.getVariableById(id); + if (variable) { + return variable.name; + } else { + return null; + } }; /** @@ -72,9 +94,18 @@ Blockly.Names.prototype.reset = function() { * @return {string} An entity name legal for the exported language. */ Blockly.Names.prototype.getName = function(name, type) { + if (type == Blockly.Variables.NAME_TYPE) { + var varName = this.getNameForVariable(name); + if (varName) { + name = varName; + } + } var normalized = name.toLowerCase() + '_' + type; - var prefix = (type == Blockly.Variables.NAME_TYPE) ? - this.variablePrefix_ : ''; + + var isVarType = type == Blockly.Variables.NAME_TYPE || + type == Blockly.Names.DEVELOPER_VARIABLE_TYPE; + + var prefix = isVarType ? this.variablePrefix_ : ''; if (normalized in this.db_) { return prefix + this.db_[normalized]; } @@ -103,8 +134,9 @@ Blockly.Names.prototype.getDistinctName = function(name, type) { } safeName += i; this.dbReverse_[safeName] = true; - var prefix = (type == Blockly.Variables.NAME_TYPE) ? - this.variablePrefix_ : ''; + var isVarType = type == Blockly.Variables.NAME_TYPE || + type == Blockly.Names.DEVELOPER_VARIABLE_TYPE; + var prefix = isVarType ? this.variablePrefix_ : ''; return prefix + safeName; }; diff --git a/core/variables.js b/core/variables.js index b8d40028b..3eb3bcedf 100644 --- a/core/variables.js +++ b/core/variables.js @@ -100,6 +100,38 @@ Blockly.Variables.allVariables = function(root) { return root.getAllVariables(); }; +/** + * Find all developer variables used by blocks in the workspace. + * Developer variables are never shown to the user, but are declared as global + * variables in the generated code. + * To declare developer variables, define the getDeveloperVariables function on + * your block and return a list of variable names. + * For use by generators. + * @param {!Blockly.Workspace} workspace The workspace to search. + * @return {!Array.} A list of non-duplicated variable names. + * @package + */ +Blockly.Variables.allDeveloperVariables = function(workspace) { + var blocks = workspace.getAllBlocks(); + var hash = {}; + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; + if (block.getDeveloperVars) { + var devVars = block.getDeveloperVars(); + for (var j = 0; j < devVars.length; j++) { + hash[devVars[j]] = devVars[j]; + } + } + } + + // Flatten the hash into a list. + var list = []; + for (var name in hash) { + list.push(hash[name]); + } + return list; +}; + /** * Construct the elements (blocks and button) required by the flyout for the * variable category. diff --git a/generators/dart.js b/generators/dart.js index 83ef76cd6..1d3c93b75 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -102,13 +102,25 @@ Blockly.Dart.init = function(workspace) { Blockly.Dart.variableDB_.reset(); } + Blockly.Dart.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; + // Add user variables. var variables = workspace.getAllVariables(); - if (variables.length) { - for (var i = 0; i < variables.length; i++) { - defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].name, - Blockly.Variables.NAME_TYPE); - } + for (var i = 0; i < variables.length; i++) { + defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].name, + Blockly.Variables.NAME_TYPE); + } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.Dart.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE)); + } + + // Declare all of the variables. + if (defvars.length) { Blockly.Dart.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; } diff --git a/generators/javascript.js b/generators/javascript.js index 00b96bcc1..864ae50c3 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -152,13 +152,25 @@ Blockly.JavaScript.init = function(workspace) { Blockly.JavaScript.variableDB_.reset(); } + Blockly.JavaScript.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; + // Add user variables. var variables = workspace.getAllVariables(); - if (variables.length) { - for (var i = 0; i < variables.length; i++) { - defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].name, - Blockly.Variables.NAME_TYPE); - } + for (var i = 0; i < variables.length; i++) { + defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].name, + Blockly.Variables.NAME_TYPE); + } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.JavaScript.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE)); + } + + // Declare all of the variables. + if (defvars.length) { Blockly.JavaScript.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; } diff --git a/tests/generators/unittest.js b/tests/generators/unittest.js index 5c471d1b7..6b7378210 100644 --- a/tests/generators/unittest.js +++ b/tests/generators/unittest.js @@ -34,7 +34,7 @@ Blockly.Blocks['unittest_main'] = { this.setTooltip('Executes the enclosed unit tests,\n' + 'then prints a summary.'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; @@ -54,7 +54,7 @@ Blockly.Blocks['unittest_assertequals'] = { .appendField('expected'); this.setTooltip('Tests that "actual == expected".'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; @@ -74,7 +74,7 @@ Blockly.Blocks['unittest_assertvalue'] = { [['true', 'TRUE'], ['false', 'FALSE'], ['null', 'NULL']]), 'EXPECTED'); this.setTooltip('Tests that the value is true, false, or null.'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; @@ -90,7 +90,7 @@ Blockly.Blocks['unittest_fail'] = { .appendField('fail'); this.setTooltip('Records an error.'); }, - getVars: function() { + getDeveloperVars: function() { return ['unittestResults']; } }; diff --git a/tests/generators/unittest_dart.js b/tests/generators/unittest_dart.js index 97e6ae466..436bcff63 100644 --- a/tests/generators/unittest_dart.js +++ b/tests/generators/unittest_dart.js @@ -27,7 +27,7 @@ Blockly.Dart['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.Dart.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Dart.provideFunction_( 'unittest_report', [ 'String ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', @@ -71,7 +71,7 @@ Blockly.Dart['unittest_main'] = function(block) { Blockly.Dart['unittest_main'].defineAssert_ = function() { var resultsVar = Blockly.Dart.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Dart.provideFunction_( 'unittest_assertequals', [ 'void ' + Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_ + @@ -139,7 +139,7 @@ Blockly.Dart['unittest_assertvalue'] = function(block) { Blockly.Dart['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.Dart.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.Dart.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.Dart.provideFunction_( 'unittest_fail', diff --git a/tests/generators/unittest_javascript.js b/tests/generators/unittest_javascript.js index 9fda5be30..92d8a2913 100644 --- a/tests/generators/unittest_javascript.js +++ b/tests/generators/unittest_javascript.js @@ -27,7 +27,7 @@ Blockly.JavaScript['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.JavaScript.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.JavaScript.provideFunction_( 'unittest_report', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', @@ -72,7 +72,7 @@ Blockly.JavaScript['unittest_main'] = function(block) { Blockly.JavaScript['unittest_main'].defineAssert_ = function(block) { var resultsVar = Blockly.JavaScript.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.JavaScript.provideFunction_( 'assertEquals', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + @@ -143,7 +143,7 @@ Blockly.JavaScript['unittest_assertvalue'] = function(block) { Blockly.JavaScript['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.JavaScript.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.JavaScript.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.JavaScript.provideFunction_( 'unittest_fail', From fced496d28bd21b18e252cfb6c8156ff7ddffbeb Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 20 Dec 2017 15:23:05 -0800 Subject: [PATCH 2/5] Lua generator tests pass with new variables --- generators/lua.js | 1 + tests/generators/unittest_lua.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/generators/lua.js b/generators/lua.js index 1118a31e4..b7f8d5c90 100644 --- a/generators/lua.js +++ b/generators/lua.js @@ -109,6 +109,7 @@ Blockly.Lua.init = function(workspace) { } else { Blockly.Lua.variableDB_.reset(); } + Blockly.Lua.variableDB_.setVariableMap(workspace.getVariableMap()); }; /** diff --git a/tests/generators/unittest_lua.js b/tests/generators/unittest_lua.js index 73c5dbe0d..2a9015c10 100644 --- a/tests/generators/unittest_lua.js +++ b/tests/generators/unittest_lua.js @@ -27,7 +27,7 @@ Blockly.Lua['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Lua.provideFunction_( 'unittest_report', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '()', @@ -70,7 +70,7 @@ Blockly.Lua['unittest_main'] = function(block) { Blockly.Lua['unittest_main'].defineAssert_ = function(block) { var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Lua.provideFunction_( 'assertEquals', ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + @@ -149,7 +149,7 @@ Blockly.Lua['unittest_assertvalue'] = function(block) { Blockly.Lua['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.Lua.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.Lua.provideFunction_( 'unittest_fail', From 0a0b6f384827b8e1d7ca102d44738ec38f50265e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 20 Dec 2017 16:28:14 -0800 Subject: [PATCH 3/5] Add error; python and php tests pass. --- core/names.js | 5 +++++ generators/php.js | 13 +++++++++++++ generators/python.js | 9 +++++++++ tests/generators/unittest_php.js | 6 +++--- tests/generators/unittest_python.js | 6 +++--- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/core/names.js b/core/names.js index 016c01538..77577ccba 100644 --- a/core/names.js +++ b/core/names.js @@ -76,6 +76,11 @@ Blockly.Names.prototype.setVariableMap = function(map) { Blockly.Names.prototype.getNameForVariable = function(id) { if (!this.variableMap_) { + console.log('Deprecated call to Blockly.Names.prototype.getName without ' + + 'defining a variable map. To fix, add the folowing code in your ' + + 'generator\'s init() function:\n' + + 'Blockly.YourGeneratorName.variableDB_.setVariableMap(' + + 'workspace.getVariableMap());'); return null; } var variable = this.variableMap_.getVariableById(id); diff --git a/generators/php.js b/generators/php.js index 2c65a6809..dfe966843 100644 --- a/generators/php.js +++ b/generators/php.js @@ -149,14 +149,27 @@ Blockly.PHP.init = function(workspace) { Blockly.PHP.variableDB_.reset(); } + Blockly.PHP.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; var varName; + // TODO (fenichel): Why does this use Variables.allVariables when the rest use + // workspace.getAllVariables()? var variables = Blockly.Variables.allVariables(workspace); for (var i = 0, variable; variable = variables[i]; i++) { varName = variable.name; defvars[i] = Blockly.PHP.variableDB_.getName(varName, Blockly.Variables.NAME_TYPE) + ';'; } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.PHP.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ';'); + } + + // Declare all of the variables. Blockly.PHP.definitions_['variables'] = defvars.join('\n'); }; diff --git a/generators/python.js b/generators/python.js index e81a04234..ab927d13b 100644 --- a/generators/python.js +++ b/generators/python.js @@ -160,12 +160,21 @@ Blockly.Python.init = function(workspace) { Blockly.Python.variableDB_.reset(); } + Blockly.Python.variableDB_.setVariableMap(workspace.getVariableMap()); + var defvars = []; var variables = workspace.getAllVariables(); for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.Python.variableDB_.getName(variables[i].name, Blockly.Variables.NAME_TYPE) + ' = None'; } + + // Add developer variables (not created or named by the user). + var devVarList = Blockly.Variables.allDeveloperVariables(workspace); + for (var i = 0; i < devVarList.length; i++) { + defvars.push(Blockly.Python.variableDB_.getName(devVarList[i], + Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ' = None'); + } Blockly.Python.definitions_['variables'] = defvars.join('\n'); }; diff --git a/tests/generators/unittest_php.js b/tests/generators/unittest_php.js index 528e5971d..54d7e4e97 100644 --- a/tests/generators/unittest_php.js +++ b/tests/generators/unittest_php.js @@ -27,7 +27,7 @@ Blockly.PHP['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.PHP.provideFunction_( 'unittest_report', [ 'function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', @@ -72,7 +72,7 @@ Blockly.PHP['unittest_main'] = function(block) { Blockly.PHP['unittest_main'].defineAssert_ = function(block) { var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.PHP.provideFunction_( 'assertEquals', ['function ' + Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_ + @@ -129,7 +129,7 @@ Blockly.PHP['unittest_assertvalue'] = function(block) { Blockly.PHP['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.PHP.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.PHP.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.PHP.provideFunction_( 'unittest_fail', diff --git a/tests/generators/unittest_python.js b/tests/generators/unittest_python.js index 5e00e26be..91fabd179 100644 --- a/tests/generators/unittest_python.js +++ b/tests/generators/unittest_python.js @@ -27,7 +27,7 @@ Blockly.Python['unittest_main'] = function(block) { // Container for unit tests. var resultsVar = Blockly.Python.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Python.provideFunction_( 'unittest_report', ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + '():', @@ -68,7 +68,7 @@ Blockly.Python['unittest_main'] = function(block) { Blockly.Python['unittest_main'].defineAssert_ = function() { var resultsVar = Blockly.Python.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var functionName = Blockly.Python.provideFunction_( 'assertEquals', ['def ' + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + @@ -117,7 +117,7 @@ Blockly.Python['unittest_assertvalue'] = function(block) { Blockly.Python['unittest_fail'] = function(block) { // Always assert an error. var resultsVar = Blockly.Python.variableDB_.getName('unittestResults', - Blockly.Variables.NAME_TYPE); + Blockly.Names.DEVELOPER_VARIABLE_TYPE); var message = Blockly.Python.quote_(block.getFieldValue('MESSAGE')); var functionName = Blockly.Python.provideFunction_( 'fail', From e4e389bd6069dc68e91292088c58996692aba892 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 20 Dec 2017 16:33:44 -0800 Subject: [PATCH 4/5] Add documentation --- core/names.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/core/names.js b/core/names.js index 77577ccba..6278c7dd2 100644 --- a/core/names.js +++ b/core/names.js @@ -29,6 +29,9 @@ goog.provide('Blockly.Names'); /** * Constant to separate developer variable names from user-defined variable * names when running generators. + * A developer variable will be declared as a global in the generated code, but + * will never be shown to the user in the workspace or stored in the variable + * map. */ Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE'; @@ -70,11 +73,24 @@ Blockly.Names.prototype.reset = function() { this.variableMap_ = null; }; +/** + * Set the variable map that maps from variable name to variable object. + * @param {!Blockly.VariableMap} map The map to track. + * @package + */ Blockly.Names.prototype.setVariableMap = function(map) { this.variableMap_ = map; }; -Blockly.Names.prototype.getNameForVariable = function(id) { +/** + * Get the name for a user-defined variable, based on its ID. + * This should only be used for variables of type Blockly.Variables.NAME_TYPE. + * @param {string} id The ID to look up in the variable map. + * @return {?string} The name of the referenced variable, or null if there was + * no variable map or the variable was not found in the map. + * @private + */ +Blockly.Names.prototype.getNameForUserVariable_ = function(id) { if (!this.variableMap_) { console.log('Deprecated call to Blockly.Names.prototype.getName without ' + 'defining a variable map. To fix, add the folowing code in your ' + @@ -100,7 +116,7 @@ Blockly.Names.prototype.getNameForVariable = function(id) { */ Blockly.Names.prototype.getName = function(name, type) { if (type == Blockly.Variables.NAME_TYPE) { - var varName = this.getNameForVariable(name); + var varName = this.getNameForUserVariable_(name); if (varName) { name = varName; } From 24cf1cf7dd169f889d6296fbbda6f11aeb4ebec2 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Thu, 21 Dec 2017 10:54:54 -0800 Subject: [PATCH 5/5] Remove TODO and get rid of unnecessary wrapper call. --- generators/php.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/generators/php.js b/generators/php.js index dfe966843..6dc81ba62 100644 --- a/generators/php.js +++ b/generators/php.js @@ -153,9 +153,7 @@ Blockly.PHP.init = function(workspace) { var defvars = []; var varName; - // TODO (fenichel): Why does this use Variables.allVariables when the rest use - // workspace.getAllVariables()? - var variables = Blockly.Variables.allVariables(workspace); + var variables = workspace.getAllVariables(); for (var i = 0, variable; variable = variables[i]; i++) { varName = variable.name; defvars[i] = Blockly.PHP.variableDB_.getName(varName,