/**
* @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)
*/
using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Bridge;
using Bridge.Html5;
namespace WebMrbc
{
[ComVisible(true)]
public abstract class Generator
{
public string name_;
///
/// Class for a code generator that translates the blocks into a language.
///
/// Language name of this generator.
public Generator(string 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.
///
public static string 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'
///
public string 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'
///
public string STATEMENT_PREFIX = null;
///
/// The method of indenting. Defaults to two spaces, but language generators
/// may override this to increase indent or change to tabs.
///
public string INDENT = " ";
///
/// Maximum length for a comment before wrapping. Does not account for
/// indenting level.
///
public int COMMENT_WRAP = 60;
///
/// List of outer-inner pairings that do NOT require parentheses.
///
public int[][] ORDER_OVERRIDES = new int[0][];
public abstract void init(Workspace workspace);
public abstract string finish(node[] code);
public abstract node[] scrubNakedValue(node[] line);
public abstract node[] scrub_(Block block, node[] code);
///
/// Generate code for all blocks in the workspace to the specified language.
///
/// workspace Workspace to generate code from.
/// Generated code.
public string workspaceToCode(Workspace workspace)
{
if (workspace == null) {
// Backwards compatibility from before there could be multiple workspaces.
App.WriteLine("No workspace specified in workspaceToCode call. Guessing.");
workspace = Blockly.getMainWorkspace();
}
this.init(workspace);
var codes = workspaceToNodes(workspace);
return this.finish(codes);
}
public node[] workspaceToNodes(Workspace workspace)
{
var nodes = new node[0];
var blocks = workspace.getTopBlocks(true);
foreach (var block in blocks) {
var line = this.blockToCode(block);
if (line != null) {
if (block.outputConnection != null/*&& 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);
}
nodes = nodes.Concat(line).ToArray();
}
}
return nodes;
}
// The following are some helpful functions which can be used by multiple
// languages.
///
/// Prepend a common prefix onto each line of code.
///
/// The lines of code.
/// The common prefix.
/// The prefixed lines of code.
public string prefixLines(string text, string prefix)
{
return prefix + text.Replace(new RegExp("(?!\n$)\n"), "\n" + prefix);
}
///
/// Recursively spider a tree of blocks, returning all their comments.
///
/// The block from which to start spidering.
/// Concatenated list of comments.
public string allNestedComments(Block block)
{
var comments = new string[0];
var blocks = block.getDescendants();
for (var i = 0; i < blocks.Length; i++) {
var comment = blocks[i].getCommentText();
if (comment != null) {
comments.Push(comment);
}
}
// Append an empty string to create a trailing line break when joined.
if (comments.Length != 0) {
comments.Push("");
}
return String.Join("\n", comments);
}
///
/// Generate code for the specified block (and attached blocks).
///
/// The block to generate code for.
/// 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.
///
public node[] blockToCode(Block block)
{
if (block == null) {
return null;
}
if (block.disabled) {
// Skip past this block if it is disabled.
return this.blockToCode(block.getNextBlock());
}
var func = (dynamic)this[block.type];
if (func == null)
return null;
var code = (node)func.call(this, block);
if (code == null) {
// Block has handled code generation itself.
return null;
}
var result = new node[0];
if (code.GetType() == typeof(node)) {
do {
var c = (node)code.car;
c.block_id = block.id;
result.Push(c);
} while ((code = code.cdr as node) != null);
}
else {
code.block_id = block.id;
result.Push(code);
}
return this.scrub_(block, result);
}
///
/// Generate code representing the specified value input.
///
/// The block containing the input.
/// The name of the input.
///
/// Generated code or '' if no blocks are connected or the
/// specified input does not exist.
public node valueToCode(Block block, string name)
{
var targetBlock = block.getInputTargetBlock(name);
if (targetBlock == null) {
return null;
}
var code = this.blockToCode(targetBlock);
if(code == null) {
return null;
}
else if (code.Length == 1) {
return code[0];
}
else {
throw new Exception();
}
}
///
/// Generate code representing the statement. Indent the code.
///
/// The block containing the input.
/// The name of the input.
/// Generated code or '' if no blocks are connected.
public begin_node statementToCode(Block block, string 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 != null ? targetBlock.type : "");
if (code == null)
code = new node[0];
return new begin_node((IMrbParser)this, code);
}
///
/// Comma-separated list of reserved words.
///
public static string RESERVED_WORDS_ = "";
///
/// Add one or more words to the list of reserved words for this language.
///
/// Comma-separated list of words to add to the list.
/// No spaces. Duplicates are ok.
public static void addReservedWords(string words)
{
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.
///
public string FUNCTION_NAME_PLACEHOLDER_ = "{leCUI8hutHZI4480Dc}";
public RegExp FUNCTION_NAME_PLACEHOLDER_REGEXP_;
}
}