text: /**
   * @license
   * Visual Blocks Editor
   *
   * Copyright 2012 Google Inc.
   * https://developers.google.com/blockly/
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /**
   * @fileoverview Utility functions for generating executable code from
   * Blockly code.
   * @author fraser@google.com (Neil Fraser)
   */
  'use strict';
  
  goog.provide('Blockly.Generator');
  
  goog.require('Blockly.Block');
  goog.require('goog.asserts');
  
  
  /**
   * Class for a code generator that translates the blocks into a language.
   * @param {string} name Language name of this generator.
   * @constructor
   */
  Blockly.Generator = function(name) {
    this.name_ = name;
    this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ =
        new RegExp(this.FUNCTION_NAME_PLACEHOLDER_, 'g');
  };
  
  /**
   * Category to separate generated function names from variables and procedures.
   */
  Blockly.Generator.NAME_TYPE = 'generated_function';
  
  /**
   * Arbitrary code to inject into locations that risk causing infinite loops.
   * Any instances of '%1' will be replaced by the block ID that failed.
   * E.g. '  checkTimeout(%1);\n'
   * @type {?string}
   */
  Blockly.Generator.prototype.INFINITE_LOOP_TRAP = null;
  
  /**
   * Arbitrary code to inject before every statement.
   * Any instances of '%1' will be replaced by the block ID of the statement.
   * E.g. 'highlight(%1);\n'
   * @type {?string}
   */
  Blockly.Generator.prototype.STATEMENT_PREFIX = null;
  
  /**
   * The method of indenting.  Defaults to two spaces, but language generators
   * may override this to increase indent or change to tabs.
   * @type {string}
   */
  Blockly.Generator.prototype.INDENT = '  ';
  
  /**
   * Maximum length for a comment before wrapping.  Does not account for
   * indenting level.
   * @type {number}
   */
  Blockly.Generator.prototype.COMMENT_WRAP = 60;
  
  /**
   * List of outer-inner pairings that do NOT require parentheses.
   * @type {!Array.<!Array.<number>>}
   */
  Blockly.Generator.prototype.ORDER_OVERRIDES = [];
  
  /**
   * Generate code for all blocks in the workspace to the specified language.
   * @param {Blockly.Workspace} workspace Workspace to generate code from.
   * @return {string} Generated code.
   */
  Blockly.Generator.prototype.workspaceToCode = function(workspace) {
    if (!workspace) {
      // Backwards compatibility from before there could be multiple workspaces.
      console.warn('No workspace specified in workspaceToCode call.  Guessing.');
      workspace = Blockly.getMainWorkspace();
    }
    var code = [];
    this.init(workspace);
    var blocks = workspace.getTopBlocks(true);
    for (var x = 0, block; block = blocks[x]; x++) {
      block.setLineNumber(""+x);
      var line = this.blockToCode(block);
      if (goog.isArray(line)) {
        // Value blocks return tuples of code and operator order.
        // Top-level blocks don't care about operator order.
        line = line[0];
      }
      if (line) {
        if (block.outputConnection && this.scrubNakedValue) {
          // This block is a naked value.  Ask the language's code generator if
          // it wants to append a semicolon, or something.
          line = this.scrubNakedValue(line);
        }
        // If this block isn't at the top, then we can store its position.
        if (x != 0) {
            var location = block.getRelativeToSurfaceXY();
            //code.push("#location:"+location.x+","+location.y);
        }
        code.push(line);
      }
    }
    code = code.join('\n');  // Blank line between each section.
    code = this.finish(code);
    // Final scrubbing of whitespace.
    code = code.replace(/^\s+\n/, '');
    code = code.replace(/\n\s+$/, '\n');
    code = code.replace(/[ \t]+\n/g, '\n');
    return code;
  };
  
  // The following are some helpful functions which can be used by multiple
  // languages.
  
  /**
   * Prepend a common prefix onto each line of code.
   * @param {string} text The lines of code.
   * @param {string} prefix The common prefix.
   * @return {string} The prefixed lines of code.
   */
  Blockly.Generator.prototype.prefixLines = function(text, prefix) {
    return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix);
  };
  
  /**
   * Recursively spider a tree of blocks, returning all their comments.
   * @param {!Blockly.Block} block The block from which to start spidering.
   * @return {string} Concatenated list of comments.
   */
  Blockly.Generator.prototype.allNestedComments = function(block) {
    var comments = [];
    var blocks = block.getDescendants();
    for (var i = 0; i < blocks.length; i++) {
      var comment = blocks[i].getCommentText();
      if (comment) {
        comments.push(comment);
      }
    }
    // Append an empty string to create a trailing line break when joined.
    if (comments.length) {
      comments.push('');
    }
    return comments.join('\n');
  };
  
  /**
   * Generate code for the specified block (and attached blocks).
   * @param {Blockly.Block} block The block to generate code for.
   * @return {string|!Array} For statement blocks, the generated code.
   *     For value blocks, an array containing the generated code and an
   *     operator order value.  Returns '' if block is null.
   */
  Blockly.Generator.prototype.blockToCode = function(block) {
    if (!block) {
      return '';
    }
    if (block.disabled) {
      // Skip past this block if it is disabled.
      return this.blockToCode(block.getNextBlock());
    }
  
    var func = this[block.type];
    goog.asserts.assertFunction(func,
        'Language "%s" does not know how to generate code for block type "%s".',
        this.name_, block.type);
    // First argument to func.call is the value of 'this' in the generator.
    // Prior to 24 September 2013 'this' was the only way to access the block.
    // The current prefered method of accessing the block is through the second
    // argument to func.call, which becomes the first parameter to the generator.
    var code = func.call(block, block);
    if (goog.isArray(code)) {
      // Value blocks return tuples of code and operator order.
      goog.asserts.assert(block.outputConnection,
          'Expecting string from statement block "%s".', block.type);
      return [this.scrub_(block, code[0]), code[1]];
    } else if (goog.isString(code)) {
      var id = block.id.replace(/\$/g, '$$$$');  // Issue 251.
      if (this.STATEMENT_PREFIX) {
        code = this.STATEMENT_PREFIX.replace(/%1/g, '\'' + id + '\'') +
            code;
      }
      return this.scrub_(block, code);
    } else if (code === null) {
      // Block has handled code generation itself.
      return '';
    } else {
      goog.asserts.fail('Invalid code generated: %s', code);
    }
  };
  
  /**
   * Generate code representing the specified value input.
   * @param {!Blockly.Block} block The block containing the input.
   * @param {string} name The name of the input.
   * @param {number} outerOrder The maximum binding strength (minimum order value)
   *     of any operators adjacent to "block".
   * @return {string} Generated code or '' if no blocks are connected or the
   *     specified input does not exist.
   */
  Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) {
    if (isNaN(outerOrder)) {
      goog.asserts.fail('Expecting valid order from block "%s".', block.type);
    }
    var targetBlock = block.getInputTargetBlock(name);
    if (!targetBlock) {
      return '';
    }
    var tuple = this.blockToCode(targetBlock);
    if (tuple === '') {
      // Disabled block.
      return '';
    }
    // Value blocks must return code and order of operations info.
    // Statement blocks must only return code.
    goog.asserts.assertArray(tuple, 'Expecting tuple from value block "%s".',
        targetBlock.type);
    var code = tuple[0];
    var innerOrder = tuple[1];
    if (isNaN(innerOrder)) {
      goog.asserts.fail('Expecting valid order from value block "%s".',
          targetBlock.type);
    }
    if (!code) {
      return '';
    }
  
    // Add parentheses if needed.
    var parensNeeded = false;
    var outerOrderClass = Math.floor(outerOrder);
    var innerOrderClass = Math.floor(innerOrder);
    if (outerOrderClass <= innerOrderClass) {
      if (outerOrderClass == innerOrderClass &&
          (outerOrderClass == 0 || outerOrderClass == 99)) {
        // Don't generate parens around NONE-NONE and ATOMIC-ATOMIC pairs.
        // 0 is the atomic order, 99 is the none order.  No parentheses needed.
        // In all known languages multiple such code blocks are not order
        // sensitive.  In fact in Python ('a' 'b') 'c' would fail.
      } else {
        // The operators outside this code are stonger than the operators
        // inside this code.  To prevent the code from being pulled apart,
        // wrap the code in parentheses.
        parensNeeded = true;
        // Check for special exceptions.
        for (var i = 0; i < this.ORDER_OVERRIDES.length; i++) {
          if (this.ORDER_OVERRIDES[i][0] == outerOrder &&
              this.ORDER_OVERRIDES[i][1] == innerOrder) {
            parensNeeded = false;
            break;
          }
        }
      }
    }
    if (parensNeeded) {
      // Technically, this should be handled on a language-by-language basis.
      // However all known (sane) languages use parentheses for grouping.
      code = '(' + code + ')';
    }
    return code;
  };
  
  /**
   * Generate code representing the statement.  Indent the code.
   * @param {!Blockly.Block} block The block containing the input.
   * @param {string} name The name of the input.
   * @return {string} Generated code or '' if no blocks are connected.
   */
  Blockly.Generator.prototype.statementToCode = function(block, name) {
    var targetBlock = block.getInputTargetBlock(name);
    var code = this.blockToCode(targetBlock);
    // Value blocks must return code and order of operations info.
    // Statement blocks must only return code.
    goog.asserts.assertString(code, 'Expecting code from statement block "%s".',
        targetBlock && targetBlock.type);
    if (code) {
      code = this.prefixLines(/** @type {string} */ (code), this.INDENT);
    }
    return code;
  };
  
  /**
   * Add an infinite loop trap to the contents of a loop.
   * If loop is empty, add a statment prefix for the loop block.
   * @param {string} branch Code for loop contents.
   * @param {string} id ID of enclosing block.
   * @return {string} Loop contents, with infinite loop trap added.
   */
  Blockly.Generator.prototype.addLoopTrap = function(branch, id) {
    id = id.replace(/\$/g, '$$$$');  // Issue 251.
    if (this.INFINITE_LOOP_TRAP) {
      branch = this.INFINITE_LOOP_TRAP.replace(/%1/g, '\'' + id + '\'') + branch;
    }
    if (this.STATEMENT_PREFIX) {
      branch += this.prefixLines(this.STATEMENT_PREFIX.replace(/%1/g,
          '\'' + id + '\''), this.INDENT);
    }
    return branch;
  };
  
  /**
   * The method of indenting.  Defaults to two spaces, but language generators
   * may override this to increase indent or change to tabs.
   * @type {string}
   */
  Blockly.Generator.prototype.INDENT = '    ';
  
  /**
   * Comma-separated list of reserved words.
   * @type {string}
   * @private
   */
  Blockly.Generator.prototype.RESERVED_WORDS_ = '';
  
  /**
   * Add one or more words to the list of reserved words for this language.
   * @param {string} words Comma-separated list of words to add to the list.
   *     No spaces.  Duplicates are ok.
   */
  Blockly.Generator.prototype.addReservedWords = function(words) {
    this.RESERVED_WORDS_ += words + ',';
  };
  
  /**
   * This is used as a placeholder in functions defined using
   * Blockly.Generator.provideFunction_.  It must not be legal code that could
   * legitimately appear in a function definition (or comment), and it must
   * not confuse the regular expression parser.
   * @type {string}
   * @private
   */
  Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_ = '{leCUI8hutHZI4480Dc}';
  
  /**
   * Define a function to be included in the generated code.
   * The first time this is called with a given desiredName, the code is
   * saved and an actual name is generated.  Subsequent calls with the
   * same desiredName have no effect but have the same return value.
   *
   * It is up to the caller to make sure the same desiredName is not
   * used for different code values.
   *
   * The code gets output when Blockly.Generator.finish() is called.
   *
   * @param {string} desiredName The desired name of the function (e.g., isPrime).
   * @param {!Array.<string>} code A list of statements.  Use '  ' for indents.
   * @return {string} The actual name of the new function.  This may differ
   *     from desiredName if the former has already been taken by the user.
   * @private
   */
  Blockly.Generator.prototype.provideFunction_ = function(desiredName, code) {
    if (!this.definitions_[desiredName]) {
      var functionName = this.variableDB_.getDistinctName(desiredName,
          Blockly.Procedures.NAME_TYPE);
      this.functionNames_[desiredName] = functionName;
      var codeText = code.join('\n').replace(
          this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName);
      // Change all '  ' indents into the desired indent.
      // To avoid an infinite loop of replacements, change all indents to '\0'
      // character first, then replace them all with the indent.
      // We are assuming that no provided functions contain a literal null char.
      var oldCodeText;
      while (oldCodeText != codeText) {
        oldCodeText = codeText;
        codeText = codeText.replace(/^((  )*)  /gm, '$1\0');
      }
      codeText = codeText.replace(/\0/g, this.INDENT);
      this.definitions_[desiredName] = codeText;
    }
    return this.functionNames_[desiredName];
  };
  