diff --git a/HISTORY.md b/HISTORY.md
index b2ec2383..f8b9f699 100755
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -69,6 +69,9 @@
* Chinese, thanks, Simon!
* Brazilian Portuguese, thank you, Cassiano D'Andrea!
+### 2021-12-08
+* blocks: refactored syntax trees
+
### 2021-12-07
* objects: backwards-compatibility fix for key-event hat blocks
* store, objects: load category-less custom blocks into "other"
diff --git a/snap.html b/snap.html
index 41c7d3f1..eda60868 100755
--- a/snap.html
+++ b/snap.html
@@ -16,7 +16,7 @@
-
+
diff --git a/src/blocks.js b/src/blocks.js
index ef8975c8..00e2159c 100644
--- a/src/blocks.js
+++ b/src/blocks.js
@@ -160,7 +160,7 @@ CustomCommandBlockMorph, ToggleButtonMorph, DialMorph, SnapExtensions*/
// Global stuff ////////////////////////////////////////////////////////
-modules.blocks = '2021-December-06';
+modules.blocks = '2021-December-08';
var SyntaxElementMorph;
var BlockMorph;
@@ -3721,8 +3721,72 @@ BlockMorph.prototype.exportResultPic = function () {
// BlockMorph components - EXPERIMENTAL
-BlockMorph.prototype.components = function () {
- throw new Error('subclass responsility');
+BlockMorph.prototype.components = function (parameterNames = []) {
+ if (this instanceof ReporterBlockMorph) {
+ return this.syntaxTree(parameterNames);
+ }
+ var seq = new List(this.blockSequence()).map((block, i) =>
+ block.syntaxTree(i < 1 ? parameterNames : [])
+ );
+ return seq.length() === 1 ? seq.at(1) : seq;
+};
+
+BlockMorph.prototype.syntaxTree = function (parameterNames) {
+ var expr = this.fullCopy(),
+ nb = expr.nextBlock ? expr.nextBlock() : null,
+ inputs, parts;
+ if (nb) {
+ nb.destroy();
+ }
+ expr.fixBlockColor(null, true);
+ inputs = expr.inputs();
+ parts = new List([expr.reify()]);
+ inputs.forEach(inp => {
+ var val;
+ if (inp instanceof BlockMorph) {
+ if (inp instanceof RingMorph && inp.isEmptySlot()) {
+ parts.add();
+ return;
+ }
+ parts.add(inp.components());
+ expr.revertToDefaultInput(inp, true);
+ } else if (inp.isEmptySlot()) {
+ if (!inp.isStatic) {
+ parts.add();
+ expr.revertToDefaultInput(inp, true);
+ }
+ } else if (inp instanceof MultiArgMorph) {
+ inp.inputs().forEach((slot, i) => {
+ var entry;
+ if (slot instanceof BlockMorph) {
+ if (slot instanceof RingMorph && slot.isEmptySlot()) {
+ parts.add();
+ return;
+ }
+ parts.add(slot.components());
+ } else if (slot.isEmptySlot()) {
+ parts.add();
+ } else {
+ entry = slot.evaluate();
+ parts.add(entry instanceof BlockMorph ?
+ entry.components() : entry);
+ }
+ if (!(slot instanceof TemplateSlotMorph)) {
+ inp.revertToDefaultInput(slot, true);
+ }
+ });
+ } else if (inp instanceof ArgLabelMorph) {
+ parts.add(inp.argMorph().components());
+ expr.revertToDefaultInput(inp, true).collapseAll();
+ } else {
+ val = inp.evaluate();
+ parts.add(val instanceof BlockMorph ? val.components() : val);
+ expr.revertToDefaultInput(inp, true);
+ }
+ });
+ parts.at(1).updateEmptySlots();
+ parameterNames.forEach(name => parts.add(name));
+ return parts;
};
BlockMorph.prototype.equalTo = function (other) {
@@ -3815,6 +3879,19 @@ BlockMorph.prototype.copyWithInputs = function (inputs) {
return cpy.reify(dta.slice(count));
};
+BlockMorph.prototype.copyWithNext = function (next, parameterNames) {
+ var expr = this.fullCopy(),
+ top;
+ if (this instanceof ReporterBlockMorph) {
+ return expr.reify();
+ }
+ top = next.fullCopy().topBlock();
+ if (top instanceof CommandBlockMorph) {
+ expr.bottomBlock().nextBlock(top);
+ }
+ return expr.reify(parameterNames);
+};
+
BlockMorph.prototype.reify = function (inputNames) {
// private - assumes that I've already been deep copied
var context = new Context();
@@ -5558,80 +5635,6 @@ CommandBlockMorph.prototype.extract = function () {
}
};
-// CommandBlockMorph components - EXPERIMENTAL
-
-CommandBlockMorph.prototype.components = function (parameterNames = []) {
- var seq = new List(this.blockSequence()).map(block => {
- var expr = block.fullCopy(),
- nb = expr.nextBlock(),
- inputs, parts;
- if (nb) {
- nb.destroy();
- }
- expr.fixBlockColor(null, true);
- inputs = expr.inputs();
- parts = new List([expr.reify()]);
- inputs.forEach(inp => {
- var val;
- if (inp instanceof BlockMorph) {
- if (inp instanceof RingMorph && inp.isEmptySlot()) {
- parts.add();
- return;
- }
- parts.add(inp.components());
- expr.revertToDefaultInput(inp, true);
- } else if (inp.isEmptySlot()) {
- if (!inp.isStatic) {
- parts.add();
- expr.revertToDefaultInput(inp, true);
- }
- } else if (inp instanceof MultiArgMorph) {
- inp.inputs().forEach((slot, i) => {
- var entry;
- if (slot instanceof BlockMorph) {
- if (slot instanceof RingMorph && slot.isEmptySlot()) {
- parts.add();
- return;
- }
- parts.add(slot.components());
- } else if (slot.isEmptySlot()) {
- parts.add();
- } else {
- entry = slot.evaluate();
- parts.add(entry instanceof BlockMorph ?
- entry.components() : entry);
- }
- if (!(slot instanceof TemplateSlotMorph)) {
- inp.revertToDefaultInput(slot, true);
- }
- });
- } else if (inp instanceof ArgLabelMorph) {
- parts.add(inp.argMorph().components());
- expr.revertToDefaultInput(inp, true).collapseAll();
- } else {
- val = inp.evaluate();
- parts.add(val instanceof BlockMorph ? val.components() : val);
- expr.revertToDefaultInput(inp, true);
- }
- });
- parts.at(1).updateEmptySlots();
- parameterNames.forEach(name => parts.add(name));
- return parts;
- });
- return seq.length() === 1 ? seq.at(1) : seq;
-};
-
-CommandBlockMorph.prototype.copyWithNext = function (next, parameterNames) {
- var exp = this.fullCopy(),
- bottom = exp.bottomBlock(),
- top = next.fullCopy().topBlock();
-
- if (top instanceof CommandBlockMorph) {
- bottom.nextBlock(top);
- }
- return exp.reify(parameterNames);
-};
-
// CommandBlockMorph drawing:
CommandBlockMorph.prototype.outlinePath = function(ctx, inset) {
@@ -6406,66 +6409,6 @@ ReporterBlockMorph.prototype.userDestroy = function () {
this.destroy();
};
-// ReporterBlockMorph components - EXPERIMENTAL
-
-ReporterBlockMorph.prototype.components = function (parameterNames = []) {
- var expr = this.fullCopy(),
- inputs = expr.inputs(),
- parts;
- expr.fixBlockColor(null, true);
- parts = new List([expr.reify()]);
- inputs.forEach(inp => {
- var val;
- if (inp instanceof BlockMorph) {
- if (inp instanceof RingMorph && inp.isEmptySlot()) {
- parts.add();
- return;
- }
- parts.add(inp.components());
- expr.revertToDefaultInput(inp, true);
- } else if (inp.isEmptySlot()) {
- if (!inp.isStatic) {
- parts.add();
- expr.revertToDefaultInput(inp, true);
- }
- } else if (inp instanceof MultiArgMorph) {
- inp.inputs().forEach((slot, i) => {
- var entry;
- if (slot instanceof BlockMorph) {
- if (slot instanceof RingMorph && slot.isEmptySlot()) {
- parts.add();
- return;
- }
- parts.add(slot.components());
- } else if (slot.isEmptySlot()) {
- parts.add();
- } else {
- entry = slot.evaluate();
- parts.add(entry instanceof BlockMorph ?
- entry.components() : entry);
- }
- if (!(slot instanceof TemplateSlotMorph)) {
- inp.revertToDefaultInput(slot, true);
- }
- });
- } else if (inp instanceof ArgLabelMorph) {
- parts.add(inp.argMorph().components());
- expr.revertToDefaultInput(inp, true).collapseAll();
- } else {
- val = inp.evaluate();
- parts.add(val instanceof BlockMorph ? val.components() : val);
- expr.revertToDefaultInput(inp, true);
- }
- });
- parameterNames.forEach(name => parts.add(name));
- parts.at(1).updateEmptySlots();
- return parts;
-};
-
-ReporterBlockMorph.prototype.copyWithNext = function (next) {
- return this.fullCopy().reify();
-};
-
// ReporterBlockMorph drawing:
ReporterBlockMorph.prototype.outlinePath = function (ctx, inset) {