Implement startBranch() in compiler
This commit is contained in:
1 parent
1e88fad562
commit
2adf5ce38e
3 files changed
+44
-32
No files matched your search
@@ -4,16 +4,17 @@ class CompatibilityLayerBlockUtility extends BlockUtility {
|
||||
constructor () {
|
||||
super();
|
||||
this._stackFrame = {};
|
||||
this._startedBranch = null;
|
||||
}
|
||||
|
||||
get stackFrame () {
|
||||
return this._stackFrame;
|
||||
}
|
||||
|
||||
// Branching operations are not supported.
|
||||
startBranch () {
|
||||
throw new Error('startBranch is not supported by this BlockUtility');
|
||||
startBranch (branchNumber, isLoop) {
|
||||
this._startedBranch = [branchNumber, isLoop];
|
||||
}
|
||||
|
||||
startProcedure () {
|
||||
throw new Error('startProcedure is not supported by this BlockUtility');
|
||||
}
|
||||
@@ -33,6 +34,7 @@ class CompatibilityLayerBlockUtility extends BlockUtility {
|
||||
this.thread = thread;
|
||||
this.sequencer = thread.target.runtime.sequencer;
|
||||
this._stackFrame = stackFrame;
|
||||
this._startedBranch = null;
|
||||
thread.stack[0] = fakeBlockId;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-19
@@ -101,7 +101,7 @@ runtimeFunctions.waitThreads = `const waitThreads = function*(threads) {
|
||||
* @param {function} blockFunction The primitive's function.
|
||||
* @param {boolean} useFlags Whether to set flags (hasResumedFromPromise)
|
||||
* @param {string} blockId Block ID to set on the emulated block utility.
|
||||
* @param {*|null} stackFrame Object to use as stack frame.
|
||||
* @param {*|null} branchInfo Extra information object for CONDITIONAL and LOOP blocks. See createBranchInfo().
|
||||
* @returns {*} the value returned by the block, if any.
|
||||
*/
|
||||
runtimeFunctions.executeInCompatibilityLayer = `let hasResumedFromPromise = false;
|
||||
@@ -132,10 +132,22 @@ const isPromise = value => (
|
||||
typeof value === 'object' &&
|
||||
typeof value.then === 'function'
|
||||
);
|
||||
const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, useFlags, blockId, stackFrame) {
|
||||
const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, useFlags, blockId, branchInfo) {
|
||||
const thread = globalState.thread;
|
||||
const blockUtility = globalState.blockUtility;
|
||||
if (!stackFrame) stackFrame = {};
|
||||
const stackFrame = branchInfo ? branchInfo.stackFrame : {};
|
||||
|
||||
const finish = (returnValue) => {
|
||||
if (branchInfo) {
|
||||
if (typeof returnValue === 'undefined' && blockUtility._startedBranch) {
|
||||
branchInfo.isLoop = blockUtility._startedBranch[1];
|
||||
return blockUtility._startedBranch[0];
|
||||
}
|
||||
branchInfo.isLoop = branchInfo.defaultIsLoop;
|
||||
return returnValue;
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
const executeBlock = () => {
|
||||
blockUtility.init(thread, blockId, stackFrame);
|
||||
@@ -143,13 +155,8 @@ const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, use
|
||||
};
|
||||
|
||||
let returnValue = executeBlock();
|
||||
|
||||
if (isPromise(returnValue)) {
|
||||
returnValue = yield* waitPromise(returnValue);
|
||||
if (useFlags) {
|
||||
hasResumedFromPromise = true;
|
||||
}
|
||||
return returnValue;
|
||||
return finish(yield* waitPromise(returnValue));
|
||||
}
|
||||
|
||||
if (thread.status === 1 /* STATUS_PROMISE_WAIT */) {
|
||||
@@ -173,30 +180,31 @@ const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, use
|
||||
}
|
||||
|
||||
returnValue = executeBlock();
|
||||
|
||||
if (isPromise(returnValue)) {
|
||||
returnValue = yield* waitPromise(returnValue);
|
||||
if (useFlags) {
|
||||
hasResumedFromPromise = true;
|
||||
}
|
||||
return returnValue;
|
||||
return finish(yield* waitPromise(returnValue));
|
||||
}
|
||||
|
||||
if (thread.status === 1 /* STATUS_PROMISE_WAIT */) {
|
||||
yield;
|
||||
return '';
|
||||
return finish('');
|
||||
}
|
||||
}
|
||||
|
||||
// todo: do we have to do anything extra if status is STATUS_DONE?
|
||||
|
||||
return returnValue;
|
||||
return finish(returnValue);
|
||||
}`;
|
||||
|
||||
/**
|
||||
* @returns {unknown} An object to use as a stack frame.
|
||||
* @param {boolean} isLoop True if the block is a LOOP by default (can be overridden by startBranch() call)
|
||||
* @returns {unknown} Branch info object for compatibility layer.
|
||||
*/
|
||||
runtimeFunctions.persistentStackFrame = `const persistentStackFrame = () => ({});`;
|
||||
runtimeFunctions.createBranchInfo = `const createBranchInfo = (isLoop) => ({
|
||||
defaultIsLoop: isLoop,
|
||||
isLoop: false,
|
||||
branch: 0,
|
||||
stackFrame: {}
|
||||
});`;
|
||||
|
||||
/**
|
||||
* End the current script.
|
||||
|
||||
+12
-10
@@ -765,21 +765,23 @@ class JSGenerator {
|
||||
const blockType = node.blockType;
|
||||
if (blockType === BlockType.COMMAND || blockType === BlockType.HAT) {
|
||||
this.source += `${this.generateCompatibilityLayerCall(node, isLastInLoop)};\n`;
|
||||
} else if (blockType === BlockType.CONDITIONAL) {
|
||||
this.source += `switch (+(${this.generateCompatibilityLayerCall(node, isLastInLoop)})) {\n`;
|
||||
} else if (blockType === BlockType.CONDITIONAL || blockType === BlockType.LOOP) {
|
||||
const branchVariable = this.localVariables.next();
|
||||
this.source += `const ${branchVariable} = createBranchInfo(${blockType === BlockType.LOOP});\n`;
|
||||
this.source += `while (${branchVariable}.branch = +(${this.generateCompatibilityLayerCall(node, false, branchVariable)})) {\n`;
|
||||
this.source += `switch (${branchVariable}.branch) {\n`;
|
||||
for (let i = 0; i < node.substacks.length; i++) {
|
||||
this.source += `case ${i + 1}: {\n`;
|
||||
this.descendStack(node.substacks[i], new Frame(false));
|
||||
this.source += `break;\n`;
|
||||
this.source += `}\n`;
|
||||
this.source += `}\n`; // close case
|
||||
}
|
||||
this.source += `}\n`;
|
||||
} else if (node.blockType === BlockType.LOOP) {
|
||||
const stackFrameName = this.localVariables.next();
|
||||
this.source += `const ${stackFrameName} = persistentStackFrame();\n`;
|
||||
this.source += `while (toBoolean(${this.generateCompatibilityLayerCall(node, isLastInLoop, stackFrameName)})) {\n`;
|
||||
this.descendStack(node.substacks[0], new Frame(true));
|
||||
this.source += '}\n';
|
||||
this.source += '}\n'; // close switch
|
||||
this.source += `if (!${branchVariable}.isLoop) break;\n`;
|
||||
this.yieldLoop();
|
||||
this.source += '}\n'; // close while
|
||||
} else {
|
||||
throw new Error(`Unknown block type: ${blockType}`);
|
||||
}
|
||||
|
||||
if (isLastInLoop) {
|
||||
|
||||
Reference in new issue
Block a user