diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js index 39707ef5c6c..cb6f2a17226 100644 --- a/src/compiler/irgen.js +++ b/src/compiler/irgen.js @@ -664,7 +664,7 @@ class ScriptTreeGenerator { const blockInfo = this.getBlockInfo(block.opcode); if (blockInfo) { const type = blockInfo.info.blockType; - if (type === BlockType.REPORTER || type === BlockType.BOOLEAN) { + if (type === BlockType.REPORTER || type === BlockType.BOOLEAN || type === BlockType.INLINE) { return this.descendCompatLayer(block); } } @@ -1416,7 +1416,7 @@ class ScriptTreeGenerator { const blockInfo = this.getBlockInfo(block.opcode); const blockType = (blockInfo && blockInfo.info && blockInfo.info.blockType) || BlockType.COMMAND; const substacks = {}; - if (blockType === BlockType.CONDITIONAL || blockType === BlockType.LOOP) { + if (blockType === BlockType.CONDITIONAL || blockType === BlockType.LOOP || blockType === BlockType.INLINE) { for (const inputName in block.inputs) { if (!inputName.startsWith('SUBSTACK')) continue; const branchNum = inputName === 'SUBSTACK' ? 1 : +inputName.substring('SUBSTACK'.length); diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js index d3e7784d81f..4fec750b5ed 100644 --- a/src/compiler/jsgen.js +++ b/src/compiler/jsgen.js @@ -439,9 +439,29 @@ class JSGenerator { return new TypedInput(`p${node.index}`, TYPE_UNKNOWN); case 'compat': + if (node.blockType === BlockType.INLINE) { + const branchVariable = this.localVariables.next(); + const returnVariable = this.localVariables.next(); + let source = '(yield* (function*() {\n'; + source += `let ${returnVariable} = undefined;\n`; + source += `const ${branchVariable} = createBranchInfo(false);\n`; + source += `${returnVariable} = (${this.generateCompatibilityLayerCall(node, false, branchVariable)});\n`; + source += `${branchVariable}.branch = globalState.blockUtility._startedBranch[0];\n`; + source += `switch (${branchVariable}.branch) {\n`; + for (const index in node.substacks) { + source += `case ${+index}: {\n`; + source += this.descendStackForSource(node.substacks[index], new Frame(false)); + source += `break;\n`; + source += `}\n`; // close case + } + source += '}\n'; // close switch + source += `return ${returnVariable};\n`; + source += '})())'; // close function and yield + return new TypedInput(source, TYPE_UNKNOWN); + } // Compatibility layer inputs never use flags. return new TypedInput(`(${this.generateCompatibilityLayerCall(node, false)})`, TYPE_UNKNOWN); - + case 'constant': return this.safeConstantInput(node.value); @@ -768,7 +788,7 @@ 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 || blockType === BlockType.LOOP) { + } else if (blockType === BlockType.CONDITIONAL || blockType === BlockType.LOOP || blockType === BlockType.INLINE) { 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`; @@ -1192,6 +1212,16 @@ class JSGenerator { this.variableInputs = {}; } + descendStackForSource (nodes, frame) { + // Wrapper for descendStack to get the source + const oldSource = this.source; + this.source = ''; + this.descendStack(nodes, frame); + const stackSource = this.source; + this.source = oldSource; + return stackSource; + } + descendStack (nodes, frame) { // Entering a stack -- all bets are off. // TODO: allow if/else to inherit values diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 169c880f7d7..520f966f49a 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1427,6 +1427,12 @@ class Runtime extends EventEmitter { blockJSON.nextStatement = null; // null = available connection; undefined = terminal } break; + case BlockType.INLINE: + blockJSON.output = null; + blockInfo.disableMonitor = true; + blockInfo.branchCount = blockInfo.branchCount || 1; + blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE; + break; } const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text]; diff --git a/src/extension-support/block-type.js b/src/extension-support/block-type.js index ecaf70754dc..4680ef4af10 100644 --- a/src/extension-support/block-type.js +++ b/src/extension-support/block-type.js @@ -54,7 +54,13 @@ const BlockType = { /** * Arbitrary scratch-blocks XML. */ - XML: 'xml' + XML: 'xml', + + /** + * Specialized reporter block that allows for the insertion and evaluation + * of a substack. + */ + INLINE: 'inline' }; module.exports = BlockType;