From 4acf1896bd9fa57d3dbe3f4721b6790c6f3b0d56 Mon Sep 17 00:00:00 2001 From: Jens Mönig Date: Sat, 21 Mar 2015 11:12:43 +0100 Subject: Prototypal inheritance of sprite-local variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (experimental) slotwise inheritance à la Henry Lieberman for sprite-local variables. see http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.htm l Let a sprite inherit another sprite’s local variables by making it the “parent” in the sprite-icon’s context menu (the button icon in the sprite corral underneath the stage). The child not only inherits the variable slot but also - dynamically - the parent variable’s value. Changing the parent’s variable value also changes it for every child. If a child uses SET or CHANGE on an inherited variable it automatically “shadows” it with its own value, thereby stopping dynamic participation in the parent slot’s value (in effect dis-inheriting that slot). Deleting a shadowed variable slot once again reinstates its inheritance status. inherited variables are shown as “ghosted” both in the child’s variables palette and in such stage watchers. “Shadowing” them un-ghosts both the variable blob template in the palette and the watcher onstage (if any). Deleting a shadowed variable once again ghosts the watcher and the palette block template. Delete a (shadowed) variable either via the “Delete a variable” button in the IDE or using the new “Delete” block in the variables category --- threads.js | 73 +++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 22 deletions(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 50af8dd..d3dcfc9 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2015-February-28'; +modules.threads = '2015-March-21'; var ThreadManager; var Process; @@ -1159,7 +1159,7 @@ Process.prototype.doSetVar = function (varName, value) { name = name.expression.blockSpec; } } - varFrame.setVar(name, value); + varFrame.setVar(name, value, this.blockReceiver()); }; Process.prototype.doChangeVar = function (varName, value) { @@ -1171,7 +1171,7 @@ Process.prototype.doChangeVar = function (varName, value) { name = name.expression.blockSpec; } } - varFrame.changeVar(name, value); + varFrame.changeVar(name, value, this.blockReceiver()); }; Process.prototype.reportGetVar = function () { @@ -1295,6 +1295,20 @@ Process.prototype.doRemoveTemporaries = function () { } }; +// Process sprite inheritance primitives + +Process.prototype.doDeleteAttr = function (attrName) { + // currently only variables are deletable + var name = attrName; + + if (name instanceof Context) { + if (name.expression.selector === 'reportGetVar') { + name = name.expression.blockSpec; + } + } + this.blockReceiver().deleteVariable(name); +}; + // Process lists primitives Process.prototype.reportNewList = function (elements) { @@ -3103,35 +3117,50 @@ VariableFrame.prototype.silentFind = function (name) { return null; }; -VariableFrame.prototype.setVar = function (name, value) { -/* - change the specified variable if it exists - else throw an error, because variables need to be - declared explicitly (e.g. through a "script variables" block), - before they can be accessed. -*/ +VariableFrame.prototype.setVar = function (name, value, sender) { + // change the specified variable if it exists + // else throw an error, because variables need to be + // declared explicitly (e.g. through a "script variables" block), + // before they can be accessed. + // if the found frame is inherited by the sender sprite + // shadow it (create an explicit one for the sender) + // before setting the value ("create-on-write") + var frame = this.find(name); if (frame) { - frame.vars[name].value = value; + if (sender instanceof SpriteMorph && + (frame.owner instanceof SpriteMorph) && + (sender !== frame.owner)) { + sender.shadowVar(name, value); + } else { + frame.vars[name].value = value; + } } }; -VariableFrame.prototype.changeVar = function (name, delta) { -/* - change the specified variable if it exists - else throw an error, because variables need to be - declared explicitly (e.g. through a "script variables" block, - before they can be accessed. -*/ +VariableFrame.prototype.changeVar = function (name, delta, sender) { + // change the specified variable if it exists + // else throw an error, because variables need to be + // declared explicitly (e.g. through a "script variables" block, + // before they can be accessed. + // if the found frame is inherited by the sender sprite + // shadow it (create an explicit one for the sender) + // before changing the value ("create-on-write") + var frame = this.find(name), - value; + value, + newValue; if (frame) { value = parseFloat(frame.vars[name].value); - if (isNaN(value)) { - frame.vars[name].value = delta; + newValue = isNaN(value) ? delta : value + parseFloat(delta); + if (sender instanceof SpriteMorph && + (frame.owner instanceof SpriteMorph) && + (sender !== frame.owner)) { + sender.shadowVar(name, newValue); } else { - frame.vars[name].value = value + parseFloat(delta); + frame.vars[name].value = newValue; } + } }; -- cgit v1.3.1 From af9b3dec293c45ac4b109f4ef3c21ba2f5bc55c3 Mon Sep 17 00:00:00 2001 From: Jens Mönig Date: Mon, 23 Mar 2015 12:44:46 +0100 Subject: OOP: Tweaks for scope conflicts when adding / removing vars --- blocks.js | 29 +++++++++++++++++++++++++++-- history.txt | 4 ++++ objects.js | 38 ++++++++++++++++++++++---------------- threads.js | 9 ++++++--- 4 files changed, 59 insertions(+), 21 deletions(-) (limited to 'threads.js') diff --git a/blocks.js b/blocks.js index 711976f..45f1049 100644 --- a/blocks.js +++ b/blocks.js @@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2015-March-21'; +modules.blocks = '2015-March-23'; var SyntaxElementMorph; @@ -1191,6 +1191,15 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { ); part.isStatic = true; break; + case '%shd': + part = new InputSlotMorph( + null, + false, + 'shadowedVariablesMenu', + true + ); + part.isStatic = true; + break; case '%lst': part = new InputSlotMorph( null, @@ -1889,7 +1898,8 @@ SyntaxElementMorph.prototype.endLayout = function () { %att - chameleon colored rectangular drop-down for attributes %fun - chameleon colored rectangular drop-down for math functions %typ - chameleon colored rectangular drop-down for data types - %var - chameleon colored rectangular drop-down for variable names + %var - chameleon colored rectangular drop-down for variable names + %shd - Chameleon colored rectuangular drop-down for shadowed var names %lst - chameleon colored rectangular drop-down for list names %b - chameleon colored hexagonal slot (for predicates) %l - list icon @@ -6902,6 +6912,21 @@ InputSlotMorph.prototype.getVarNamesDict = function () { return {}; }; +InputSlotMorph.prototype.shadowedVariablesMenu = function () { + var block = this.parentThatIsA(BlockMorph), + rcvr, + dict = {}; + + if (!block) {return dict; } + rcvr = block.receiver(); + if (rcvr) { + rcvr.inheritedVariableNames(true).forEach(function (name) { + dict[name] = name; + }); + } + return dict; +}; + InputSlotMorph.prototype.setChoices = function (dict, readonly) { // externally specify choices and read-only status, // used for custom blocks diff --git a/history.txt b/history.txt index e02d368..fc54386 100755 --- a/history.txt +++ b/history.txt @@ -2480,3 +2480,7 @@ ______ 150321 ------ * OOP: Prototypal inheritance of sprite-local variables + +150321 +------ +* OOP: Objects, Blocks, Threads - tweaks diff --git a/objects.js b/objects.js index 56e5806..2ea8427 100644 --- a/objects.js +++ b/objects.js @@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2015-March-21'; +modules.objects = '2015-March-23'; var SpriteMorph; var StageMorph; @@ -1107,7 +1107,7 @@ SpriteMorph.prototype.initBlocks = function () { doDeleteAttr: { type: 'command', category: 'variables', - spec: 'delete %var' + spec: 'delete %shd' }, // Lists @@ -1720,12 +1720,14 @@ SpriteMorph.prototype.blockTemplates = function (category) { function addVar(pair) { var ide; if (pair) { - if (myself.isVariableNameInUse(pair[0])) { + if (myself.isVariableNameInUse(pair[0], pair[1])) { myself.inform('that name is already in use'); } else { ide = myself.parentThatIsA(IDE_Morph); myself.addVariable(pair[0], pair[1]); - myself.toggleVariableWatcher(pair[0], pair[1]); + if (!myself.showingVariableWatcher(pair[0])) { + myself.toggleVariableWatcher(pair[0], pair[1]); + } ide.flushBlocksCache('variables'); // b/c of inheritance ide.refreshPalette(); } @@ -4203,7 +4205,10 @@ SpriteMorph.prototype.allSpecimens = function () { // SpriteMorph inheritance - variables -SpriteMorph.prototype.isVariableNameInUse = function (vName) { +SpriteMorph.prototype.isVariableNameInUse = function (vName, isGlobal) { + if (isGlobal) { + return contains(this.variables.allNames(), vName); + } if (contains(this.variables.names(), vName)) {return true; } return contains(this.globalVariables().names(), vName); }; @@ -4243,8 +4248,14 @@ SpriteMorph.prototype.inheritedVariableNames = function (shadowedOnly) { }; SpriteMorph.prototype.deletableVariableNames = function () { - return this.variables.names().concat( - this.globalVariables().names() + var locals = this.variables.names(), + inherited = this.inheritedVariableNames(); + return locals.concat( + this.globalVariables().names().filter( + function (each) { + return !contains(locals, each) && !contains(inherited, each); + } + ) ); }; @@ -5406,16 +5417,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doShowVar')); blocks.push(block('doHideVar')); blocks.push(block('doDeclareVariables')); - - // inheritance: - - blocks.push('-'); - blocks.push(block('doDeleteAttr')); - - /////////////////////////////// - blocks.push('='); - blocks.push(block('reportNewList')); blocks.push('-'); blocks.push(block('reportCONS')); @@ -5813,6 +5815,10 @@ StageMorph.prototype.isVariableNameInUse StageMorph.prototype.globalVariables = SpriteMorph.prototype.globalVariables; +StageMorph.prototype.inheritedVariableNames = function () { + return []; +}; + // SpriteBubbleMorph //////////////////////////////////////////////////////// /* diff --git a/threads.js b/threads.js index d3dcfc9..82f7ab0 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2015-March-21'; +modules.threads = '2015-March-23'; var ThreadManager; var Process; @@ -1299,14 +1299,17 @@ Process.prototype.doRemoveTemporaries = function () { Process.prototype.doDeleteAttr = function (attrName) { // currently only variables are deletable - var name = attrName; + var name = attrName, + rcvr = this.blockReceiver(); if (name instanceof Context) { if (name.expression.selector === 'reportGetVar') { name = name.expression.blockSpec; } } - this.blockReceiver().deleteVariable(name); + if (contains(rcvr.inheritedVariableNames(true), name)) { + rcvr.deleteVariable(name); + } }; // Process lists primitives -- cgit v1.3.1 From 34187eb69e99b87158fe21a94a810085227b88cb Mon Sep 17 00:00:00 2001 From: Jens Mönig Date: Mon, 23 Mar 2015 13:56:05 +0100 Subject: fix (part of) "show variable" primitive for inheritance --- threads.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 82f7ab0..0511c53 100644 --- a/threads.js +++ b/threads.js @@ -1216,7 +1216,7 @@ Process.prototype.doShowVar = function (varName) { } // if no watcher exists, create a new one isGlobal = contains( - this.homeContext.receiver.variables.parentFrame.names(), + this.homeContext.receiver.globalVariables().names(), varName ); if (isGlobal || target.owner) { -- cgit v1.3.1