From 287c76d1574b6a7d50bf15b14128d2bc20b38d57 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Sep 2022 11:48:11 +0800 Subject: [PATCH 1/2] add statement solution to function --- .gitignore | 1 + .../src/blocks/controls.js | 338 ------------------ .../src/blocks/lexical-variables.js | 2 +- .../src/blocks/procedures.js | 9 + .../src/generators/lexical-variables.js | 22 +- .../src/generators/procedures.js | 5 + 6 files changed, 36 insertions(+), 341 deletions(-) delete mode 100644 block-lexical-variables/src/blocks/controls.js diff --git a/.gitignore b/.gitignore index 1e85899..e331a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ */build/** */dist/** */.idea/** +.idea diff --git a/block-lexical-variables/src/blocks/controls.js b/block-lexical-variables/src/blocks/controls.js deleted file mode 100644 index 157a870..0000000 --- a/block-lexical-variables/src/blocks/controls.js +++ /dev/null @@ -1,338 +0,0 @@ -/** - * @fileoverview Control blocks for Blockly, modified for App Inventor. - * @author fraser@google.com (Neil Fraser) - * @author andrew.f.mckinney@gmail.com (Andrew F. McKinney) - * Due to the frequency of long strings, the 80-column wrap rule need not apply - * to language files. - */ - -/** - * Lyn's History: - * [lyn, written 11/16-17/13, added 07/01/14] Added freeVariables, renameFree, - * and renameBound to forRange and forEach loops - * [lyn, 10/27/13] Specify direction of flydowns - * [lyn, 10/25/13] Made collapsed block labels more sensible. - * [lyn, 10/10-14/13] - * + Installed flydown index variable declarations in forRange and forEach - * loops - * + Abstracted over string labels on all blocks using constants defined in - * en/_messages.js - * + Renamed "for start [] end [] step []" block to "for each - * from [] to [] by []" - * + Renamed "for each in list []" block to "for each in list []" - * + Renamed "choose test [] then-return [] else-return []" to "if [] then [] - * else []" - * (TODO: still needs to have a mutator like the "if" statement blocks). - * + Renamed "evaluate" block to "evaluate but ignore result" - * + Renamed "do {} then-return []" block to "do {} result []" and re-added - * this block to the Control drawer (who removed it?) - * + Removed get block (still in Variable drawer; no longer needed with - * parameter flydowns) - * [lyn, 11/29-30/12] - * + Change forEach and forRange loops to take name as input text rather than - * via plug. - * + For these blocks, add extra methods to support renaming. - */ - -'use strict'; - -import * as Blockly from 'blockly'; -import '../msg'; -import {FieldParameterFlydown} from '../fields/field_parameter_flydown'; -import {FieldFlydown} from '../fields/field_flydown'; -import { - FieldLexicalVariable, - LexicalVariable, -} from '../fields/field_lexical_variable'; -import * as Utilities from '../utilities'; -import * as Shared from '../shared'; - -Blockly.Blocks['controls_forRange'] = { - // For range. - category: 'Control', - helpUrl: Blockly.Msg.LANG_CONTROLS_FORRANGE_HELPURL, - init: function() { - // Let the theme determine the color. - // this.setColour(Blockly.CONTROL_CATEGORY_HUE); - this.setStyle('loop_blocks'); - // this.setOutput(true, null); - // Need to deal with variables here - // [lyn, 11/30/12] Changed variable to be text input box that does renaming - // right (i.e., avoids variable capture) - this.appendValueInput('START') - .setCheck(Utilities.yailTypeToBlocklyType('number', - Utilities.INPUT)) - .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_ITEM) - .appendField(new FieldParameterFlydown( - Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_VAR, true, - FieldFlydown.DISPLAY_BELOW), 'VAR') - .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_START) - .setAlign(Blockly.ALIGN_RIGHT); - this.appendValueInput('END') - .setCheck(Utilities.yailTypeToBlocklyType('number', - Utilities.INPUT)) - .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_END) - .setAlign(Blockly.ALIGN_RIGHT); - this.appendValueInput('STEP') - .setCheck(Utilities.yailTypeToBlocklyType('number', - Utilities.INPUT)) - .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_STEP) - .setAlign(Blockly.ALIGN_RIGHT); - this.appendStatementInput('DO') - .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_DO) - .setAlign(Blockly.ALIGN_RIGHT); - this.setPreviousStatement(true); - this.setNextStatement(true); - this.setTooltip(Blockly.Msg.LANG_CONTROLS_FORRANGE_TOOLTIP); - this.lexicalVarPrefix = Shared.loopRangeParameterPrefix; - }, - referenceResults: function(name, prefix, env) { - let loopVar = this.getFieldValue('VAR'); - // Invariant: Shared.showPrefixToUser must also be true! - if (Shared.usePrefixInCode) { - loopVar = - (Shared.possiblyPrefixMenuNameWith(Shared.loopRangeParameterPrefix))( - loopVar); - } - const newEnv = env.concat([loopVar]); - const startResults = LexicalVariable.referenceResult( - this.getInputTargetBlock('START'), name, prefix, env); - const endResults = LexicalVariable.referenceResult( - this.getInputTargetBlock('END'), name, prefix, env); - const stepResults = LexicalVariable.referenceResult( - this.getInputTargetBlock('STEP'), name, prefix, env); - const doResults = LexicalVariable.referenceResult( - this.getInputTargetBlock('DO'), name, prefix, newEnv); - const nextResults = LexicalVariable.referenceResult( - LexicalVariable.getNextTargetBlock(this), name, prefix, env); - return [startResults, endResults, stepResults, doResults, nextResults]; - }, - withLexicalVarsAndPrefix: function(child, proc) { - if (this.getInputTargetBlock('DO') == child) { - const lexVar = this.getFieldValue('VAR'); - proc(lexVar, this.lexicalVarPrefix); - } - }, - getVars: function() { - return [this.getFieldValue('VAR')]; - }, - blocksInScope: function() { - const doBlock = this.getInputTargetBlock('DO'); - if (doBlock) { - return [doBlock]; - } else { - return []; - } - }, - declaredNames: function() { - return [this.getFieldValue('VAR')]; - }, - renameVar: function(oldName, newName) { - if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { - this.setFieldValue(newName, 'VAR'); - } - }, - renameBound: function(boundSubstitution, freeSubstitution) { - LexicalVariable.renameFree(this.getInputTargetBlock('START'), - freeSubstitution); - LexicalVariable.renameFree(this.getInputTargetBlock('END'), - freeSubstitution); - LexicalVariable.renameFree(this.getInputTargetBlock('STEP'), - freeSubstitution); - const oldIndexVar = this.getFieldValue('VAR'); - const newIndexVar = boundSubstitution.apply(oldIndexVar); - if (newIndexVar !== oldIndexVar) { - this.renameVar(oldIndexVar, newIndexVar); - const indexSubstitution = Blockly.Substitution.simpleSubstitution( - oldIndexVar, newIndexVar); - const extendedFreeSubstitution = freeSubstitution.extend( - indexSubstitution); - LexicalVariable.renameFree(this.getInputTargetBlock('DO'), - extendedFreeSubstitution); - } else { - const removedFreeSubstitution = freeSubstitution.remove([oldIndexVar]); - LexicalVariable.renameFree(this.getInputTargetBlock('DO'), - removedFreeSubstitution); - } - if (this.nextConnection) { - const nextBlock = this.nextConnection.targetBlock(); - LexicalVariable.renameFree(nextBlock, freeSubstitution); - } - }, - renameFree: function(freeSubstitution) { - const indexVar = this.getFieldValue('VAR'); - const bodyFreeVars = LexicalVariable.freeVariables( - this.getInputTargetBlock('DO')); - bodyFreeVars.deleteName(indexVar); - const renamedBodyFreeVars = bodyFreeVars.renamed(freeSubstitution); - if (renamedBodyFreeVars.isMember(indexVar)) { // Variable capture! - const newIndexVar = FieldLexicalVariable.nameNotIn(indexVar, - renamedBodyFreeVars.toList()); - const boundSubstitution = Blockly.Substitution.simpleSubstitution( - indexVar, newIndexVar); - this.renameBound(boundSubstitution, freeSubstitution); - } else { - this.renameBound(new Blockly.Substitution(), freeSubstitution); - } - }, - freeVariables: function() { // return the free variables of this block - const result = LexicalVariable.freeVariables( - this.getInputTargetBlock('DO')); - // Remove bound index variable from body free vars - result.deleteName(this.getFieldValue('VAR')); - result.unite(LexicalVariable.freeVariables( - this.getInputTargetBlock('START'))); - result.unite( - LexicalVariable.freeVariables(this.getInputTargetBlock('END'))); - result.unite(LexicalVariable.freeVariables( - this.getInputTargetBlock('STEP'))); - if (this.nextConnection) { - const nextBlock = this.nextConnection.targetBlock(); - result.unite(LexicalVariable.freeVariables(nextBlock)); - } - return result; - }, -}; - -// Alias controls_for to controls_forRange We need this because -// we can't use controls_flow_statements within controls_forRange -// due to Blockly checking -delete Blockly.Blocks['controls_for']; -Blockly.Blocks['controls_for'] = Blockly.Blocks['controls_forRange']; - -Blockly.Blocks['controls_forEach'] = { - // For each loop. - category: 'Control', - helpUrl: Blockly.Msg.LANG_CONTROLS_FOREACH_HELPURL, - init: function() { - // Let the theme determine the color. - // this.setColour(Blockly.CONTROL_CATEGORY_HUE); - this.setStyle('loop_blocks'); - // this.setOutput(true, null); - // [lyn, 10/07/13] Changed default name from "i" to "item" - // [lyn, 11/29/12] Changed variable to be text input box that does renaming - // right (i.e., avoids variable capture) - this.appendValueInput('LIST') - .setCheck(Utilities.yailTypeToBlocklyType('list', - Utilities.INPUT)) - .appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_ITEM) - .appendField(new FieldParameterFlydown( - Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_VAR, - true, FieldFlydown.DISPLAY_BELOW), 'VAR') - .appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_INLIST) - .setAlign(Blockly.ALIGN_RIGHT); - this.appendStatementInput('DO') - .appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_DO); - this.setPreviousStatement(true); - this.setNextStatement(true); - this.setTooltip(Blockly.Msg.LANG_CONTROLS_FOREACH_TOOLTIP); - this.lexicalVarPrefix = Shared.loopParameterPrefix; - }, - referenceResults: function(name, prefix, env) { - let loopVar = this.getFieldValue('VAR'); - // Invariant: Shared.showPrefixToUser must also be true! - if (Shared.usePrefixInCode) { - loopVar = (Shared.possiblyPrefixMenuNameWith(Shared.loopParameterPrefix))( - loopVar); - } - const newEnv = env.concat([loopVar]); - const listResults = LexicalVariable.referenceResult( - this.getInputTargetBlock('LIST'), name, prefix, env); - const doResults = LexicalVariable.referenceResult( - this.getInputTargetBlock('DO'), name, prefix, newEnv); - const nextResults = LexicalVariable.referenceResult( - LexicalVariable.getNextTargetBlock(this), name, prefix, env); - return [listResults, doResults, nextResults]; - }, - withLexicalVarsAndPrefix: - Blockly.Blocks.controls_forRange.withLexicalVarsAndPrefix, - getVars: function() { - return [this.getFieldValue('VAR')]; - }, - blocksInScope: function() { - const doBlock = this.getInputTargetBlock('DO'); - if (doBlock) { - return [doBlock]; - } else { - return []; - } - }, - declaredNames: function() { - return [this.getFieldValue('VAR')]; - }, - renameVar: function(oldName, newName) { - if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { - this.setFieldValue(newName, 'VAR'); - } - }, - renameBound: function(boundSubstitution, freeSubstitution) { - LexicalVariable.renameFree(this.getInputTargetBlock('LIST'), - freeSubstitution); - const oldIndexVar = this.getFieldValue('VAR'); - const newIndexVar = boundSubstitution.apply(oldIndexVar); - if (newIndexVar !== oldIndexVar) { - this.renameVar(oldIndexVar, newIndexVar); - const indexSubstitution = Blockly.Substitution.simpleSubstitution( - oldIndexVar, newIndexVar); - const extendedFreeSubstitution = freeSubstitution.extend( - indexSubstitution); - LexicalVariable.renameFree(this.getInputTargetBlock('DO'), - extendedFreeSubstitution); - } else { - const removedFreeSubstitution = freeSubstitution.remove([oldIndexVar]); - LexicalVariable.renameFree(this.getInputTargetBlock('DO'), - removedFreeSubstitution); - } - if (this.nextConnection) { - const nextBlock = this.nextConnection.targetBlock(); - LexicalVariable.renameFree(nextBlock, freeSubstitution); - } - }, - renameFree: function(freeSubstitution) { - const indexVar = this.getFieldValue('VAR'); - const bodyFreeVars = LexicalVariable.freeVariables( - this.getInputTargetBlock('DO')); - bodyFreeVars.deleteName(indexVar); - const renamedBodyFreeVars = bodyFreeVars.renamed(freeSubstitution); - if (renamedBodyFreeVars.isMember(indexVar)) { // Variable capture! - const newIndexVar = FieldLexicalVariable.nameNotIn(indexVar, - renamedBodyFreeVars.toList()); - const boundSubstitution = Blockly.Substitution.simpleSubstitution( - indexVar, newIndexVar); - this.renameBound(boundSubstitution, freeSubstitution); - } else { - this.renameBound(new Blockly.Substitution(), freeSubstitution); - } - }, - freeVariables: function() { // return the free variables of this block - const result = LexicalVariable.freeVariables( - this.getInputTargetBlock('DO')); - // Remove bound index variable from body free vars - result.deleteName(this.getFieldValue('VAR')); - result.unite(LexicalVariable.freeVariables( - this.getInputTargetBlock('LIST'))); - if (this.nextConnection) { - const nextBlock = this.nextConnection.targetBlock(); - result.unite(LexicalVariable.freeVariables(nextBlock)); - } - return result; - }, -}; - -Blockly.Blocks['controls_do_then_return'] = { - // String length. - category: 'Control', - helpUrl: Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_HELPURL, - init: function() { - // this.setColour(Blockly.CONTROL_CATEGORY_HUE); - this.setStyle('loop_blocks'); - this.appendStatementInput('STM') - .appendField(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_INPUT_DO); - this.appendValueInput('VALUE') - .appendField(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_INPUT_RETURN) - .setAlign(Blockly.ALIGN_RIGHT); - this.setOutput(true, null); - this.setTooltip(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_TOOLTIP); - }, -}; - diff --git a/block-lexical-variables/src/blocks/lexical-variables.js b/block-lexical-variables/src/blocks/lexical-variables.js index 4a75fa7..da517a6 100644 --- a/block-lexical-variables/src/blocks/lexical-variables.js +++ b/block-lexical-variables/src/blocks/lexical-variables.js @@ -768,7 +768,7 @@ Blockly.Blocks['local_declaration_expression'] = { this.setStyle('variables_blocks'); this.initLocals(); // this.appendIndentedValueInput('RETURN') - this.appendValueInput('RETURN') + this.appendStatementInput('STACK') .appendField( Blockly.Msg.LANG_VARIABLES_LOCAL_DECLARATION_EXPRESSION_IN_RETURN); // Create plug for expression output diff --git a/block-lexical-variables/src/blocks/procedures.js b/block-lexical-variables/src/blocks/procedures.js index f634b75..58383fc 100644 --- a/block-lexical-variables/src/blocks/procedures.js +++ b/block-lexical-variables/src/blocks/procedures.js @@ -1056,3 +1056,12 @@ Blockly.Blocks['procedures_callreturn'] = { removeProcedureValue: Blockly.Blocks.procedures_callnoreturn.removeProcedureValue, }; + +Blockly.Blocks['procedures_ifreturn'] = { + init: function() { + this.appendValueInput('VALUE').appendField( + "RETURN"); + this.setPreviousStatement(true); + this.setStyle('procedure_blocks'); + }, +}; \ No newline at end of file diff --git a/block-lexical-variables/src/generators/lexical-variables.js b/block-lexical-variables/src/generators/lexical-variables.js index 8a81797..773b6a3 100644 --- a/block-lexical-variables/src/generators/lexical-variables.js +++ b/block-lexical-variables/src/generators/lexical-variables.js @@ -67,6 +67,24 @@ Blockly.JavaScript['local_declaration_statement'] = function() { }; Blockly.JavaScript['local_declaration_expression'] = function() { - // TODO - return 'NOT IMPLEMENTED YET'; + let code = '(function() {\n '; + let hasVar = this.getFieldValue('VAR0') + if(hasVar) { + code += 'let ' + for (let i=0; this.getFieldValue('VAR' + i); i++) { + code += (Shared.usePrefixInCode ? 'local_' : '') + + this.getFieldValue('VAR' + i); + code += ' = ' + ( Blockly.JavaScript.valueToCode(this, + 'DECL' + i, Blockly.JavaScript.ORDER_NONE) || '0' ); + code += ', '; + } + // Get rid of the last comma + code = code.slice(0, -2); + code += ';\n'; + } + + code += Blockly.JavaScript.statementToCode(this, 'STACK', + Blockly.JavaScript.ORDER_NONE); + code += '\n})()\n'; + return [code, Blockly.JavaScript.ORDER_NONE]; }; diff --git a/block-lexical-variables/src/generators/procedures.js b/block-lexical-variables/src/generators/procedures.js index b6ace1a..15aa545 100644 --- a/block-lexical-variables/src/generators/procedures.js +++ b/block-lexical-variables/src/generators/procedures.js @@ -21,3 +21,8 @@ Blockly.JavaScript['procedures_callreturn'] = function(block) { const code = funcName + '(' + args.join(', ') + ')'; return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; + +Blockly.JavaScript['procedures_ifreturn'] = function (block) { + let retVal = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC); + return '\nreturn ' + retVal + ';\n' +}; \ No newline at end of file From 72e76e704cc62fc53adbd74fe676a37b6872a7cf Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Sep 2022 12:36:06 +0800 Subject: [PATCH 2/2] accidentally deleted controls.js --- .../src/blocks/controls.js | 338 ++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 block-lexical-variables/src/blocks/controls.js diff --git a/block-lexical-variables/src/blocks/controls.js b/block-lexical-variables/src/blocks/controls.js new file mode 100644 index 0000000..157a870 --- /dev/null +++ b/block-lexical-variables/src/blocks/controls.js @@ -0,0 +1,338 @@ +/** + * @fileoverview Control blocks for Blockly, modified for App Inventor. + * @author fraser@google.com (Neil Fraser) + * @author andrew.f.mckinney@gmail.com (Andrew F. McKinney) + * Due to the frequency of long strings, the 80-column wrap rule need not apply + * to language files. + */ + +/** + * Lyn's History: + * [lyn, written 11/16-17/13, added 07/01/14] Added freeVariables, renameFree, + * and renameBound to forRange and forEach loops + * [lyn, 10/27/13] Specify direction of flydowns + * [lyn, 10/25/13] Made collapsed block labels more sensible. + * [lyn, 10/10-14/13] + * + Installed flydown index variable declarations in forRange and forEach + * loops + * + Abstracted over string labels on all blocks using constants defined in + * en/_messages.js + * + Renamed "for start [] end [] step []" block to "for each + * from [] to [] by []" + * + Renamed "for each in list []" block to "for each in list []" + * + Renamed "choose test [] then-return [] else-return []" to "if [] then [] + * else []" + * (TODO: still needs to have a mutator like the "if" statement blocks). + * + Renamed "evaluate" block to "evaluate but ignore result" + * + Renamed "do {} then-return []" block to "do {} result []" and re-added + * this block to the Control drawer (who removed it?) + * + Removed get block (still in Variable drawer; no longer needed with + * parameter flydowns) + * [lyn, 11/29-30/12] + * + Change forEach and forRange loops to take name as input text rather than + * via plug. + * + For these blocks, add extra methods to support renaming. + */ + +'use strict'; + +import * as Blockly from 'blockly'; +import '../msg'; +import {FieldParameterFlydown} from '../fields/field_parameter_flydown'; +import {FieldFlydown} from '../fields/field_flydown'; +import { + FieldLexicalVariable, + LexicalVariable, +} from '../fields/field_lexical_variable'; +import * as Utilities from '../utilities'; +import * as Shared from '../shared'; + +Blockly.Blocks['controls_forRange'] = { + // For range. + category: 'Control', + helpUrl: Blockly.Msg.LANG_CONTROLS_FORRANGE_HELPURL, + init: function() { + // Let the theme determine the color. + // this.setColour(Blockly.CONTROL_CATEGORY_HUE); + this.setStyle('loop_blocks'); + // this.setOutput(true, null); + // Need to deal with variables here + // [lyn, 11/30/12] Changed variable to be text input box that does renaming + // right (i.e., avoids variable capture) + this.appendValueInput('START') + .setCheck(Utilities.yailTypeToBlocklyType('number', + Utilities.INPUT)) + .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_ITEM) + .appendField(new FieldParameterFlydown( + Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_VAR, true, + FieldFlydown.DISPLAY_BELOW), 'VAR') + .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_START) + .setAlign(Blockly.ALIGN_RIGHT); + this.appendValueInput('END') + .setCheck(Utilities.yailTypeToBlocklyType('number', + Utilities.INPUT)) + .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_END) + .setAlign(Blockly.ALIGN_RIGHT); + this.appendValueInput('STEP') + .setCheck(Utilities.yailTypeToBlocklyType('number', + Utilities.INPUT)) + .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_STEP) + .setAlign(Blockly.ALIGN_RIGHT); + this.appendStatementInput('DO') + .appendField(Blockly.Msg.LANG_CONTROLS_FORRANGE_INPUT_DO) + .setAlign(Blockly.ALIGN_RIGHT); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setTooltip(Blockly.Msg.LANG_CONTROLS_FORRANGE_TOOLTIP); + this.lexicalVarPrefix = Shared.loopRangeParameterPrefix; + }, + referenceResults: function(name, prefix, env) { + let loopVar = this.getFieldValue('VAR'); + // Invariant: Shared.showPrefixToUser must also be true! + if (Shared.usePrefixInCode) { + loopVar = + (Shared.possiblyPrefixMenuNameWith(Shared.loopRangeParameterPrefix))( + loopVar); + } + const newEnv = env.concat([loopVar]); + const startResults = LexicalVariable.referenceResult( + this.getInputTargetBlock('START'), name, prefix, env); + const endResults = LexicalVariable.referenceResult( + this.getInputTargetBlock('END'), name, prefix, env); + const stepResults = LexicalVariable.referenceResult( + this.getInputTargetBlock('STEP'), name, prefix, env); + const doResults = LexicalVariable.referenceResult( + this.getInputTargetBlock('DO'), name, prefix, newEnv); + const nextResults = LexicalVariable.referenceResult( + LexicalVariable.getNextTargetBlock(this), name, prefix, env); + return [startResults, endResults, stepResults, doResults, nextResults]; + }, + withLexicalVarsAndPrefix: function(child, proc) { + if (this.getInputTargetBlock('DO') == child) { + const lexVar = this.getFieldValue('VAR'); + proc(lexVar, this.lexicalVarPrefix); + } + }, + getVars: function() { + return [this.getFieldValue('VAR')]; + }, + blocksInScope: function() { + const doBlock = this.getInputTargetBlock('DO'); + if (doBlock) { + return [doBlock]; + } else { + return []; + } + }, + declaredNames: function() { + return [this.getFieldValue('VAR')]; + }, + renameVar: function(oldName, newName) { + if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { + this.setFieldValue(newName, 'VAR'); + } + }, + renameBound: function(boundSubstitution, freeSubstitution) { + LexicalVariable.renameFree(this.getInputTargetBlock('START'), + freeSubstitution); + LexicalVariable.renameFree(this.getInputTargetBlock('END'), + freeSubstitution); + LexicalVariable.renameFree(this.getInputTargetBlock('STEP'), + freeSubstitution); + const oldIndexVar = this.getFieldValue('VAR'); + const newIndexVar = boundSubstitution.apply(oldIndexVar); + if (newIndexVar !== oldIndexVar) { + this.renameVar(oldIndexVar, newIndexVar); + const indexSubstitution = Blockly.Substitution.simpleSubstitution( + oldIndexVar, newIndexVar); + const extendedFreeSubstitution = freeSubstitution.extend( + indexSubstitution); + LexicalVariable.renameFree(this.getInputTargetBlock('DO'), + extendedFreeSubstitution); + } else { + const removedFreeSubstitution = freeSubstitution.remove([oldIndexVar]); + LexicalVariable.renameFree(this.getInputTargetBlock('DO'), + removedFreeSubstitution); + } + if (this.nextConnection) { + const nextBlock = this.nextConnection.targetBlock(); + LexicalVariable.renameFree(nextBlock, freeSubstitution); + } + }, + renameFree: function(freeSubstitution) { + const indexVar = this.getFieldValue('VAR'); + const bodyFreeVars = LexicalVariable.freeVariables( + this.getInputTargetBlock('DO')); + bodyFreeVars.deleteName(indexVar); + const renamedBodyFreeVars = bodyFreeVars.renamed(freeSubstitution); + if (renamedBodyFreeVars.isMember(indexVar)) { // Variable capture! + const newIndexVar = FieldLexicalVariable.nameNotIn(indexVar, + renamedBodyFreeVars.toList()); + const boundSubstitution = Blockly.Substitution.simpleSubstitution( + indexVar, newIndexVar); + this.renameBound(boundSubstitution, freeSubstitution); + } else { + this.renameBound(new Blockly.Substitution(), freeSubstitution); + } + }, + freeVariables: function() { // return the free variables of this block + const result = LexicalVariable.freeVariables( + this.getInputTargetBlock('DO')); + // Remove bound index variable from body free vars + result.deleteName(this.getFieldValue('VAR')); + result.unite(LexicalVariable.freeVariables( + this.getInputTargetBlock('START'))); + result.unite( + LexicalVariable.freeVariables(this.getInputTargetBlock('END'))); + result.unite(LexicalVariable.freeVariables( + this.getInputTargetBlock('STEP'))); + if (this.nextConnection) { + const nextBlock = this.nextConnection.targetBlock(); + result.unite(LexicalVariable.freeVariables(nextBlock)); + } + return result; + }, +}; + +// Alias controls_for to controls_forRange We need this because +// we can't use controls_flow_statements within controls_forRange +// due to Blockly checking +delete Blockly.Blocks['controls_for']; +Blockly.Blocks['controls_for'] = Blockly.Blocks['controls_forRange']; + +Blockly.Blocks['controls_forEach'] = { + // For each loop. + category: 'Control', + helpUrl: Blockly.Msg.LANG_CONTROLS_FOREACH_HELPURL, + init: function() { + // Let the theme determine the color. + // this.setColour(Blockly.CONTROL_CATEGORY_HUE); + this.setStyle('loop_blocks'); + // this.setOutput(true, null); + // [lyn, 10/07/13] Changed default name from "i" to "item" + // [lyn, 11/29/12] Changed variable to be text input box that does renaming + // right (i.e., avoids variable capture) + this.appendValueInput('LIST') + .setCheck(Utilities.yailTypeToBlocklyType('list', + Utilities.INPUT)) + .appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_ITEM) + .appendField(new FieldParameterFlydown( + Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_VAR, + true, FieldFlydown.DISPLAY_BELOW), 'VAR') + .appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_INLIST) + .setAlign(Blockly.ALIGN_RIGHT); + this.appendStatementInput('DO') + .appendField(Blockly.Msg.LANG_CONTROLS_FOREACH_INPUT_DO); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setTooltip(Blockly.Msg.LANG_CONTROLS_FOREACH_TOOLTIP); + this.lexicalVarPrefix = Shared.loopParameterPrefix; + }, + referenceResults: function(name, prefix, env) { + let loopVar = this.getFieldValue('VAR'); + // Invariant: Shared.showPrefixToUser must also be true! + if (Shared.usePrefixInCode) { + loopVar = (Shared.possiblyPrefixMenuNameWith(Shared.loopParameterPrefix))( + loopVar); + } + const newEnv = env.concat([loopVar]); + const listResults = LexicalVariable.referenceResult( + this.getInputTargetBlock('LIST'), name, prefix, env); + const doResults = LexicalVariable.referenceResult( + this.getInputTargetBlock('DO'), name, prefix, newEnv); + const nextResults = LexicalVariable.referenceResult( + LexicalVariable.getNextTargetBlock(this), name, prefix, env); + return [listResults, doResults, nextResults]; + }, + withLexicalVarsAndPrefix: + Blockly.Blocks.controls_forRange.withLexicalVarsAndPrefix, + getVars: function() { + return [this.getFieldValue('VAR')]; + }, + blocksInScope: function() { + const doBlock = this.getInputTargetBlock('DO'); + if (doBlock) { + return [doBlock]; + } else { + return []; + } + }, + declaredNames: function() { + return [this.getFieldValue('VAR')]; + }, + renameVar: function(oldName, newName) { + if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { + this.setFieldValue(newName, 'VAR'); + } + }, + renameBound: function(boundSubstitution, freeSubstitution) { + LexicalVariable.renameFree(this.getInputTargetBlock('LIST'), + freeSubstitution); + const oldIndexVar = this.getFieldValue('VAR'); + const newIndexVar = boundSubstitution.apply(oldIndexVar); + if (newIndexVar !== oldIndexVar) { + this.renameVar(oldIndexVar, newIndexVar); + const indexSubstitution = Blockly.Substitution.simpleSubstitution( + oldIndexVar, newIndexVar); + const extendedFreeSubstitution = freeSubstitution.extend( + indexSubstitution); + LexicalVariable.renameFree(this.getInputTargetBlock('DO'), + extendedFreeSubstitution); + } else { + const removedFreeSubstitution = freeSubstitution.remove([oldIndexVar]); + LexicalVariable.renameFree(this.getInputTargetBlock('DO'), + removedFreeSubstitution); + } + if (this.nextConnection) { + const nextBlock = this.nextConnection.targetBlock(); + LexicalVariable.renameFree(nextBlock, freeSubstitution); + } + }, + renameFree: function(freeSubstitution) { + const indexVar = this.getFieldValue('VAR'); + const bodyFreeVars = LexicalVariable.freeVariables( + this.getInputTargetBlock('DO')); + bodyFreeVars.deleteName(indexVar); + const renamedBodyFreeVars = bodyFreeVars.renamed(freeSubstitution); + if (renamedBodyFreeVars.isMember(indexVar)) { // Variable capture! + const newIndexVar = FieldLexicalVariable.nameNotIn(indexVar, + renamedBodyFreeVars.toList()); + const boundSubstitution = Blockly.Substitution.simpleSubstitution( + indexVar, newIndexVar); + this.renameBound(boundSubstitution, freeSubstitution); + } else { + this.renameBound(new Blockly.Substitution(), freeSubstitution); + } + }, + freeVariables: function() { // return the free variables of this block + const result = LexicalVariable.freeVariables( + this.getInputTargetBlock('DO')); + // Remove bound index variable from body free vars + result.deleteName(this.getFieldValue('VAR')); + result.unite(LexicalVariable.freeVariables( + this.getInputTargetBlock('LIST'))); + if (this.nextConnection) { + const nextBlock = this.nextConnection.targetBlock(); + result.unite(LexicalVariable.freeVariables(nextBlock)); + } + return result; + }, +}; + +Blockly.Blocks['controls_do_then_return'] = { + // String length. + category: 'Control', + helpUrl: Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_HELPURL, + init: function() { + // this.setColour(Blockly.CONTROL_CATEGORY_HUE); + this.setStyle('loop_blocks'); + this.appendStatementInput('STM') + .appendField(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_INPUT_DO); + this.appendValueInput('VALUE') + .appendField(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_INPUT_RETURN) + .setAlign(Blockly.ALIGN_RIGHT); + this.setOutput(true, null); + this.setTooltip(Blockly.Msg.LANG_CONTROLS_DO_THEN_RETURN_TOOLTIP); + }, +}; +