123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- /**
- * @license Licensed under the Apache License, Version 2.0 (the "License"):
- * http://www.apache.org/licenses/LICENSE-2.0
- */
- /**
- * @fileoverview Object that defines static objects and methods to assign
- * Blockly types to Blockly blocks. These can then be converted to language
- * specific types in each language generator.
- */
- 'use strict';
- goog.provide('Blockly.StaticTyping');
- goog.require('Blockly.Block');
- goog.require('Blockly.Type');
- goog.require('Blockly.Types');
- goog.require('Blockly.Workspace');
- goog.require('goog.asserts');
- /**
- * Class for Static Typing.
- * @constructor
- */
- Blockly.StaticTyping = function() {
- this.varTypeDict = Object.create(null);
- this.pendingVarTypeDict = Object.create(null);
- };
- /**
- * Navigates through all the statement blocks, collecting all variables and
- * their type into an associative array with the variable names as the keys and
- * the type as the values.
- * @param {Blockly.Workspace} workspace Blockly Workspace to collect variables.
- * @return {Object{ String: Blockly.Type, } Associative array with the variable
- * names as the keys and the type as the values.
- */
- Blockly.StaticTyping.prototype.collectVarsWithTypes = function(workspace) {
- this.varTypeDict = Object.create(null);
- this.pendingVarTypeDict = Object.create(null);
- var blocks = Blockly.StaticTyping.getAllStatementsOrdered(workspace);
- for (var i = 0; i < blocks.length; i++) {
- //blocks[i].select(); // for step debugging, highlights block in workspace
- // Each statement block iterates through its input children collecting vars
- var blockVarAndTypes = Blockly.StaticTyping.getBlockVars(blocks[i]);
- for (var j = 0; j < blockVarAndTypes.length; j++) {
- var variableName = blockVarAndTypes[j][0];
- var variableType = blockVarAndTypes[j][1];
- // If the type comes from a variable, so it's not directly defined, it
- // returns an Array<String(block type), String(source variable name)>
- if (goog.isArray(variableType)) {
- if (this.varTypeDict[variableType[1]]) {
- variableType = this.varTypeDict[variableType[1]];
- } else {
- // Dependant variable undefined, add this var to the pending list
- if (!goog.isArray(this.pendingVarTypeDict[variableType[1]])) {
- this.pendingVarTypeDict[variableType[1]] = [variableName];
- } else {
- this.pendingVarTypeDict[variableType[1]].push(variableName);
- }
- variableType = Blockly.Types.UNDEF;
- }
- }
- this.assignTypeToVars(blocks[i], variableName, variableType);
- }
- }
- return this.varTypeDict;
- };
- /**
- * Navigates through each top level block in the workspace to collect all
- * statement blocks, ordered from top left.
- * @param {Blockly.Workspace} workspace Blockly Workspace to collect blocks.
- * @return {Array<Blockly.Block>} Array containing all workspace statement
- * blocks.
- */
- Blockly.StaticTyping.getAllStatementsOrdered = function(workspace) {
- if (!workspace.getTopBlocks) {
- throw 'Not a valid workspace: ' + workspace;
- }
- /**
- * Navigates through each continuous block to collect all statement blocks.
- * Function required to use recursion for block input statements.
- * @param {Blockly.Block} startBlock Block to start iterating from..
- * @return {Array<Blockly.Block>} Array containing all continuous statement
- * blocks.
- */
- var getAllContinuousStatements = function(startBlock) {
- var block = startBlock;
- var nextBlock = null;
- var connections = null;
- var blockNextConnection = null;
- var blocks = [];
- do {
- //block.select(); // for step debugging, highlights block in workspace
- blocks.push(block);
- blockNextConnection = block.nextConnection;
- connections = block.getConnections_();
- block = null;
- for (var j = 0; j < connections.length; j++) {
- if (connections[j].type == Blockly.NEXT_STATEMENT) {
- nextBlock = connections[j].targetBlock();
- if (nextBlock) {
- // If it is the next connection select it and move to the next block
- if (connections[j] === blockNextConnection) {
- block = nextBlock;
- } else {
- // Recursion as block children can have their own input statements
- blocks = blocks.concat(getAllContinuousStatements(nextBlock));
- }
- }
- }
- }
- } while (block);
- return blocks;
- };
- var allStatementBlocks = [];
- var topBlocks = workspace.getTopBlocks(true);
- for (var i = 0; i < topBlocks.length; i++) {
- allStatementBlocks = allStatementBlocks.concat(
- getAllContinuousStatements(topBlocks[i]));
- }
- return allStatementBlocks;
- };
- /**
- * Retrieves the input argument block variables with their set type.
- * @param {Blockly.Block} block Block to retrieve variables from.
- * @return {Array<Array<String, Blockly.Type>>} Two dimensional array with the
- * block variable as the first item pair and variable type as the second.
- */
- Blockly.StaticTyping.getBlockVars = function(block) {
- var blockVarAndTypes = [];
- var getVars = block.getVars;
- if (getVars) {
- var blockVariables = getVars.call(block);
- // Iterate through the variables used in this block
- for (var i = 0; i < blockVariables.length; i++) {
- var varName = blockVariables[i];
- var getVarType = block.getVarType;
- if (getVarType) {
- var varType = getVarType.call(block, varName);
- blockVarAndTypes.push([varName, varType]);
- } else {
- blockVarAndTypes.push([varName, Blockly.Types.NULL]);
- }
- }
- } // else: !(block.getVars), block does not define variables, so do nothing
- return blockVarAndTypes;
- };
- /**
- * Manages the associative array of variables with their type.
- * @param {Blockly.Block} block Blockly providing the variable to manage.
- * @param {string} varName Name of the variable to manage.
- * @param {Blockly.Type} varType Type assigned by current block.
- */
- Blockly.StaticTyping.prototype.assignTypeToVars =
- function(block, varName, varType) {
- switch (this.varTypeDict[varName]) {
- // First time variable is encountered, or previously undefined
- case undefined:
- case Blockly.Types.UNDEF:
- this.varTypeDict[varName] = varType;
- if ((varType != Blockly.Types.UNDEF) &&
- (this.pendingVarTypeDict[varName] !== undefined)) {
- for (var i = 0; i < this.pendingVarTypeDict[varName].length; i++) {
- this.assignTypeToVars(
- block, this.pendingVarTypeDict[varName][i], varType);
- }
- }
- break;
- // Variable with valid type already registered
- default:
- this.setBlockTypeWarning(
- block, varType, varName, this.varTypeDict[varName]);
- break;
- }
- };
- /**
- * When a block uses a variable this function can compare its type with the
- * variable type and set a warning if they are not the same or compatible.
- * @param {!Blockly.Block} block The block to manage its warning.
- * @param {!Blockly.Type} blockType The type of this block.
- * @param {!string} varName The variable name.
- */
- Blockly.StaticTyping.prototype.setBlockTypeWarning =
- function(block, blockType, varName) {
- var warningLabel = 'varType';
- if ((blockType == Blockly.Types.CHILD_BLOCK_MISSING) ||
- (this.varTypeDict[varName] == Blockly.Types.CHILD_BLOCK_MISSING)) {
- // User still has to attach a block to this variable or its first
- // declaration, so for now do not display any warning
- block.setWarningText(null, warningLabel);
- } else if ((this.varTypeDict[varName] !== blockType) &&
- (blockType !== Blockly.Types.UNDEF)) {
- block.setWarningText('The variable ' + varName + ' has been first ' +
- 'assigned to the "' + this.varTypeDict[varName].typeName + '" type\n' +
- 'and this block tries to assign the type "' + blockType.typeName + '"!',
- warningLabel);
- } else {
- block.setWarningText(null, warningLabel);
- }
- };
- /**
- * Iterates through the list of top level blocks and sets the function arguments
- * types.
- * @param {Blockly.Workspace} workspace Blockly Workspace to collect variables.
- */
- Blockly.StaticTyping.prototype.setProcedureArgs = function(workspace) {
- var blocks = workspace.getTopBlocks();
- for (var i = 0, length_ = blocks.length; i < length_; i++) {
- var setArgsType = blocks[i].setArgsType;
- if (setArgsType) {
- setArgsType.call(blocks[i], this.varTypeDict);
- }
- }
- };
|