JS Interpreter with Wait block in Step mode

236 views
Skip to first unread message

Guilherme Augusto

unread,
Feb 6, 2018, 8:11:28 PM2/6/18
to Blockly
Hello, i've looked and didn't found anyone with this issue.
I've implemented the wait block in my blockly based project and it works well with the runCode function. But when i'm running in step mode the code seem to get stucked in the wait function and don't goes ahead, the page freezes and i can`t step anymore.
Does anyone had this problem?
Thanks

Neil Fraser

unread,
Feb 6, 2018, 8:29:39 PM2/6/18
to blo...@googlegroups.com
Can you give us code snippets of code that this block generates, as well as the API wrapper functions you've added to the JS-Interpreter?  Thanks.

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

Guilherme Augusto

unread,
Feb 7, 2018, 2:53:56 PM2/7/18
to Blockly
Sure, im posting a exemple witch has the same behaviour. It is just a simple joint of step-execution and async-execution demos.
Thanks

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="acorn_interpreter.js"></script>
  <script src="../../blockly_compressed.js"></script>
  <script src="../../blocks_compressed.js"></script>
  <script src="../../javascript_compressed.js"></script>
  <script src="../../msg/js/en.js"></script>
  <script src="wait_block.js"></script>
  <style>
    body {
      background-color: #fff;
      font-family: sans-serif;
    }
    h1 {
      font-weight: normal;
      font-size: 140%;
    }
  </style>
</head>
<body>
  
  <p>
    <button onclick="stepCode()" id="stepButton">Step JavaScript</button>
    <button onclick="runCode()" id="runButton">Run JavaScript</button>
  </p>

  <div style="width: 100%">
    <div id="blocklyDiv"
        style="display: inline-block; height: 480px; width: 58%"></div>
    <textarea id="output" disabled="disabled"
        style="display: inline-block; height: 480px; width: 38%;">
    </textarea>
  </div>

  <xml id="toolbox" style="display: none">
    <category name="Logic">
      <block type="controls_if"></block>
      <block type="logic_compare"></block>
      <block type="logic_operation"></block>
      <block type="logic_negate"></block>
      <block type="logic_boolean"></block>
    </category>
    <category name="Loops">
      <block type="controls_repeat_ext">
        <value name="TIMES">
          <block type="math_number">
            <field name="NUM">10</field>
          </block>
        </value>
      </block>
      <block type="controls_whileUntil"></block>
    </category>
    <category name="Math">
      <block type="math_number"></block>
      <block type="math_arithmetic"></block>
      <block type="math_single"></block>
    </category>
    <category name="Text">
      <block type="text"></block>
      <block type="text_length"></block>
      <block type="text_print"></block>
      <block type="text_prompt_ext">
        <value name="TEXT">
          <block type="text"></block>
        </value>
      </block>
    </category>
    <category name="Variables" custom="VARIABLE"></category>
    <category name="Functions" custom="PROCEDURE"></category>
  </xml>

  <xml id="startBlocks" style="display: none">
    <block type="variables_set" id="set_n_initial" inline="true" x="20" y="20">
      <field name="VAR">n</field>
      <value name="VALUE">
        <block type="math_number">
          <field name="NUM">1</field>
        </block>
      </value>
      <next>
        <block type="controls_repeat_ext" id="repeat" inline="true">
          <value name="TIMES">
            <block type="math_number">
              <field name="NUM">4</field>
            </block>
          </value>
          <statement name="DO">
            <block type="wait_seconds" id="wait">
              <field name="SECONDS">1.0</field>
              <next>
                <block type="variables_set" id="set_n_update" inline="true">
                  <field name="VAR">n</field>
                  <value name="VALUE">
                    <block type="math_arithmetic" inline="true">
                      <field name="OP">MULTIPLY</field>
                      <value name="A">
                        <block type="variables_get">
                          <field name="VAR">n</field>
                        </block>
                      </value>
                      <value name="B">
                        <block type="math_number">
                          <field name="NUM">2</field>
                        </block>
                      </value>
                    </block>
                  </value>
                  <next>
                    <block type="text_print" id="print" inline="false">
                      <value name="TEXT">
                        <block type="variables_get">
                          <field name="VAR">n</field>
                        </block>
                      </value>
                    </block>
                  </next>
                </block>
              </next>
            </block>
          </statement>
        </block>
      </next>
    </block>
  </xml>

  <script>
    var demoWorkspace = Blockly.inject('blocklyDiv',
        {media: '../../media/',
         toolbox: document.getElementById('toolbox')});
    Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
                               demoWorkspace);

    var outputArea = document.getElementById('output');
    var stepButton = document.getElementById('stepButton');
    var myInterpreter = null;

    function initApi(interpreter, scope) {
      // Add an API function for the alert() block, generated for "text_print" blocks.
      interpreter.setProperty(scope, 'alert',
          interpreter.createNativeFunction(function(text) {
        text = text ? text.toString() : '';
        outputArea.value += '\n' + text;
      }));

      // Add an API function for the prompt() block.
      var wrapper = function(text) {
        text = text ? text.toString() : '';
        return interpreter.createPrimitive(prompt(text));
      };
      interpreter.setProperty(scope, 'prompt',
          interpreter.createNativeFunction(wrapper));
 
        initInterpreterWaitForSeconds(interpreter, scope);

      // Add an API function for highlighting blocks.
      var wrapper = function(id) {
        id = id ? id.toString() : '';
        return interpreter.createPrimitive(highlightBlock(id));
      };
      interpreter.setProperty(scope, 'highlightBlock',
          interpreter.createNativeFunction(wrapper));
    }

    var highlightPause = false;
    var latestCode = '';

    function highlightBlock(id) {
      demoWorkspace.highlightBlock(id);
      highlightPause = true;
    }

    function resetStepUi(clearOutput) {
      demoWorkspace.highlightBlock(null);
      highlightPause = false;

      if (clearOutput) {
        outputArea.value = 'Program output:\n=================';
      }
    }

    function generateCodeAndLoadIntoInterpreter() {
      // Generate JavaScript code and parse it.
      Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
      Blockly.JavaScript.addReservedWords('highlightBlock');
      latestCode = Blockly.JavaScript.workspaceToCode(demoWorkspace);
      resetStepUi(true);
    }
        
    function runCode() {
      if (!myInterpreter) {
        // First statement of this code.
        // Clear the program output.
        resetStepUi(true);
        runButton.disabled = 'disabled';

        // And then show generated code in an alert.
        // In a timeout to allow the outputArea.value to reset first.
        setTimeout(function() {
          alert('Ready to execute the following code\n' +
            '===================================\n' +
            latestCode);

          // Begin execution
          highlightPause = false;
          myInterpreter = new Interpreter(latestCode, initApi);
          runner = function() {
            if (myInterpreter) {
              var hasMore = myInterpreter.run();
              if (hasMore) {
                // Execution is currently blocked by some async call.
                // Try again later.
                setTimeout(runner, 10);
              } else {
                // Program is complete.
                outputArea.value += '\n\n<< Program complete >>';
                resetInterpreter();
                resetStepUi(false);
              }
            }
          };
          runner();
        }, 1);
        return;
      }
    }

    function stepCode() {
      if (!myInterpreter) {
        // First statement of this code.
        // Clear the program output.
        resetStepUi(true);
        myInterpreter = new Interpreter(latestCode, initApi);

        // And then show generated code in an alert.
        // In a timeout to allow the outputArea.value to reset first.
        setTimeout(function() {
          alert('Ready to execute the following code\n' +
            '===================================\n' + latestCode);
          highlightPause = true;
          stepCode();
        }, 1);
        return;
      }
      highlightPause = false;
      do {
        try {
          var hasMoreCode = myInterpreter.step();
        } finally {
          if (!hasMoreCode) {
            // Program complete, no more code to execute.
            outputArea.value += '\n\n<< Program complete >>';

            myInterpreter = null;
            resetStepUi(false);

            // Cool down, to discourage accidentally restarting the program.
            stepButton.disabled = 'disabled';
            setTimeout(function() {
              stepButton.disabled = '';
            }, 2000);

            return;
          }
        }
        // Keep executing until a highlight statement is reached,
        // or the code completes or errors.
      } while (hasMoreCode && !highlightPause);
    }

    // Load the interpreter now, and upon future changes.
    generateCodeAndLoadIntoInterpreter();
    demoWorkspace.addChangeListener(function(event) {
      if (!(event instanceof Blockly.Events.Ui)) {
        // Something changed. Parser needs to be reloaded.
        generateCodeAndLoadIntoInterpreter();
      }
    });
  </script>
</body>
</html>






Em terça-feira, 6 de fevereiro de 2018 23:29:39 UTC-2, Neil Fraser escreveu:
Can you give us code snippets of code that this block generates, as well as the API wrapper functions you've added to the JS-Interpreter?  Thanks.
On 6 February 2018 at 17:11, Guilherme Augusto <guia...@gmail.com> wrote:
Hello, i've looked and didn't found anyone with this issue.
I've implemented the wait block in my blockly based project and it works well with the runCode function. But when i'm running in step mode the code seem to get stucked in the wait function and don't goes ahead, the page freezes and i can`t step anymore.
Does anyone had this problem?
Thanks

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Neil Fraser

unread,
Feb 7, 2018, 6:04:22 PM2/7/18
to blo...@googlegroups.com
On 7 February 2018 at 11:53, Guilherme Augusto <guia...@gmail.com> wrote:
  <script src="wait_block.js"></script>

Can we see the contents of wait_block.js?  Looking at your initApi function I see that you are creating alert, prompt and highlightBlock functions, but I don't see any functions that define a pause.  So I'd like to see what code your wait block generates.

Guilherme Augusto

unread,
Feb 7, 2018, 6:10:51 PM2/7/18
to Blockly
Yes. See bellow. Thanks for your attention


Blockly.defineBlocksWithJsonArray([{
  "type": "wait_seconds",
  "message0": " wait %1 seconds",
  "args0": [{
    "type": "field_number",
    "name": "SECONDS",
    "min": 0,
    "max": 600,
    "value": 1
  }],
  "previousStatement": null,
  "nextStatement": null,
  "colour": "%{BKY_LOOPS_HUE}"
}]);

/**
 * Generator for wait block creates call to new method
 * <code>waitForSeconds()</code>.
 */
Blockly.JavaScript['wait_seconds'] = function(block) {
  var seconds = Number(block.getFieldValue('SECONDS'));
  var code = 'waitForSeconds(' + seconds + ');\n';
  return code;
};

/**
 * Register the interpreter asynchronous function
 * <code>waitForSeconds()</code>.
 */
function initInterpreterWaitForSeconds(interpreter, scope) {
  // Ensure function name does not conflict with variable names.
  Blockly.JavaScript.addReservedWords('waitForSeconds');

  var wrapper = interpreter.createAsyncFunction(
    function(timeInSeconds, callback) {
      // Delay the call to the callback.
      setTimeout(callback, timeInSeconds * 1000);
    });
  interpreter.setProperty(scope, 'waitForSeconds', wrapper);
}

Neil Fraser

unread,
Feb 7, 2018, 7:08:10 PM2/7/18
to blo...@googlegroups.com
I think this code is getting into an infinite loop:
do {
  ...
} while (hasMoreCode && !highlightPause);

There is more code to execute, but highlightPause remains false.  That would freeze your browser.

That said, all your code with Blockly and the JS-Interpreter appears to be completely correct.  Well done!  For reference, attached is a demo of a wait function executing properly in both run and step modes.  You can drop it into the JS-Interpreter's demo directory and all the paths should match up.

One thing I noticed (which has no impact on your issue) is that you have several calls to interpreter.createPrimitive(foo).  This function is deprecated, and may be removed, leaving just foo.  As a result of seeing this in your code, I found that our Blockly documentation still calls for this function to be used.  A change request has been filed for this.  Thanks!
wait.html
Reply all
Reply to author
Forward
0 new messages