From dda2d48f16ae56a139cad8feeb8172c3a54b182f Mon Sep 17 00:00:00 2001 From: Manuel Menezes de Sequeira Date: Tue, 14 Oct 2014 17:58:57 +0100 Subject: add localization to unknown variable error --- threads.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 6a59f3c..086ffcb 100644 --- a/threads.js +++ b/threads.js @@ -3003,9 +3003,9 @@ VariableFrame.prototype.find = function (name) { var frame = this.silentFind(name); if (frame) {return frame; } throw new Error( - 'a variable of name \'' + localize('a variable of name \'') + name - + '\'\ndoes not exist in this context' + + localize('\'\ndoes not exist in this context') ); }; @@ -3070,9 +3070,9 @@ VariableFrame.prototype.getVar = function (name) { return ''; } throw new Error( - 'a variable of name \'' + localize('a variable of name \'') + name - + '\'\ndoes not exist in this context' + + localize('\'\ndoes not exist in this context') ); }; -- cgit v1.3.1 From dbf2e6665b4ff7ef8cb863bcfef5939117a2f795 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Sat, 18 Oct 2014 23:00:11 -0700 Subject: Improvements to Split block for whitespace and lines: * Split by whitespace now uses the built-in definition of whitespace \s This catches all characters definted as whitespace, see below: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp * Split a line by all unicode compliant line breaks. The biggest impact here is that OSX and Windows files will now split the same way. The cr option is still around, but ther's no longer a need for it, IMO. --- threads.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 6a59f3c..2359166 100644 --- a/threads.js +++ b/threads.js @@ -2134,15 +2134,17 @@ Process.prototype.reportTextSplit = function (string, delimiter) { str, del; if (!contains(types, strType)) { - throw new Error('expecting a text instad of a ' + strType); + throw new Error('expecting text instead of a ' + strType); } if (!contains(types, delType)) { - throw new Error('expecting a text delimiter instad of a ' + delType); + throw new Error('expecting a text delimiter instead of a ' + delType); } str = (string || '').toString(); switch (this.inputOption(delimiter)) { case 'line': - del = '\n'; + // Entirely Unicode Compliant Line Splitting (Platform independent) + // http://www.unicode.org/reports/tr18/#Line_Boundaries + del = /\r\n|[\n\v\f\r\x85\u2028\u2029]/; break; case 'tab': del = '\t'; @@ -2151,7 +2153,9 @@ Process.prototype.reportTextSplit = function (string, delimiter) { del = '\r'; break; case 'whitespace': - return new List(str.trim().split(/[\t\r\n ]+/)); + str = str.trim(); + del = /\s+/; + break; case 'letter': del = ''; break; -- cgit v1.3.1 From b36a358173fcf4f2a4a77a9c202d6dc0d6c1a7b8 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 14 Nov 2014 12:49:01 +0100 Subject: Fix reporting out of nested custom C-shaped blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit REPORT now reports to the nearest lexical element expecting an input (which may not be the block holding the REPORT statement, this lets you REPORT out of nested FOR loops). STOP THIS BLOCK behaves as it used to. If you’ve been using REPORT instead of STOP THIS BLOCK, you should migrate. --- history.txt | 8 +++ store.js | 5 +- threads.js | 170 ++++++++++++++++++++++++------------------------------------ 3 files changed, 77 insertions(+), 106 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 28116b9..b3bc7da 100755 --- a/history.txt +++ b/history.txt @@ -2309,3 +2309,11 @@ ______ 141008 ------ * Objects: fixed #608, #610 + +141106 +------ +* Morphic: Enable mouseMove events with right button pressed + +141114 +------ +* Threads, Store: Fix reporting out of nested custom C-shaped blocks diff --git a/store.js b/store.js index cb91139..bc24849 100644 --- a/store.js +++ b/store.js @@ -61,7 +61,7 @@ SyntaxElementMorph, Variable*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2014-October-01'; +modules.store = '2014-November-14'; // XML_Serializer /////////////////////////////////////////////////////// @@ -1824,9 +1824,8 @@ Context.prototype.toXML = function (serializer) { return ''; } return serializer.format( - '%%' + + '%%' + '%%%', - this.isLambda ? ' lambda="lambda"' : '', this.inputs.reduce( function (xml, input) { return xml + serializer.format('$', input); diff --git a/threads.js b/threads.js index 6a59f3c..dfa335e 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-October-01'; +modules.threads = '2014-November-15'; var ThreadManager; var Process; @@ -586,7 +586,6 @@ Process.prototype.evaluateInput = function (input) { ) || (input instanceof CSlotMorph && !input.isStatic)) { // I know, this still needs yet to be done right.... ans = this.reify(ans, new List()); - ans.isImplicitLambda = true; } } } @@ -597,8 +596,6 @@ Process.prototype.evaluateInput = function (input) { Process.prototype.evaluateSequence = function (arr) { var pc = this.context.pc, outer = this.context.outerContext, - isLambda = this.context.isLambda, - isImplicitLambda = this.context.isImplicitLambda, isCustomBlock = this.context.isCustomBlock; if (pc === (arr.length - 1)) { // tail call elimination this.context = new Context( @@ -607,8 +604,6 @@ Process.prototype.evaluateSequence = function (arr) { this.context.outerContext, this.context.receiver ); - this.context.isLambda = isLambda; - this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; } else { if (pc >= arr.length) { @@ -626,8 +621,8 @@ Process.prototype.evaluateSequence = function (arr) { Caution: we cannot just revert to this version of the method, because to make tail call elimination work many tweaks had to be done to various primitives. For the most part these tweaks are about schlepping the outer context (for -the variable bindings) and the isLambda flag along, and are indicated by a -short comment in the code. But to really revert would take a good measure +the variable bindings) and the isCustomBlock flag along, and are indicated +by a short comment in the code. But to really revert would take a good measure of trial and error as well as debugging. In the developers file archive there is a version of threads.js dated 120119(2) which basically resembles the last version before introducing tail call optimization on 120123. @@ -679,6 +674,11 @@ Process.prototype.doYield = function () { } }; +Process.prototype.exitReporter = function () { + // catch-tag for REPORT and STOP BLOCK primitives + this.popContext(); +}; + // Process Exception Handling Process.prototype.handleError = function (error, element) { @@ -757,9 +757,15 @@ Process.prototype.reportJSFunction = function (parmNames, body) { ); }; +/* Process.prototype.doRun = function (context, args, isCustomBlock) { return this.evaluate(context, args, true, isCustomBlock); }; +*/ + +Process.prototype.doRun = function (context, args) { + return this.evaluate(context, args, true); +}; Process.prototype.evaluate = function ( context, @@ -783,6 +789,7 @@ Process.prototype.evaluate = function ( var outer = new Context(null, null, context.outerContext), runnable, extra, + exit, parms = args.asArray(), i, value; @@ -798,23 +805,17 @@ Process.prototype.evaluate = function ( ); extra = new Context(runnable, 'doYield'); - /* - Note: if the context's expression is a ReporterBlockMorph, - the extra context gets popped off immediately without taking - effect (i.e. it doesn't yield within evaluating a stack of - nested reporters) - */ + // Note: if the context's expression is a ReporterBlockMorph, + // the extra context gets popped off immediately without taking + // effect (i.e. it doesn't yield within evaluating a stack of + // nested reporters) - if (isCommand || (context.expression instanceof ReporterBlockMorph)) { + if (context.expression instanceof ReporterBlockMorph) { this.context.parentContext = extra; } else { this.context.parentContext = runnable; } - runnable.isLambda = true; - runnable.isImplicitLambda = context.isImplicitLambda; - runnable.isCustomBlock = false; - // assign parameters if any were passed if (parms.length > 0) { @@ -858,6 +859,18 @@ Process.prototype.evaluate = function ( if (runnable.expression instanceof CommandBlockMorph) { runnable.expression = runnable.expression.blockSequence(); + + // insert a reporter exit tag for the + // CALL SCRIPT primitive variant + if (!isCommand) { + exit = new Context( + runnable.parentContext, + 'exitReporter', + outer, + outer.receiver + ); + runnable.parentContext = exit; + } } }; @@ -879,8 +892,6 @@ Process.prototype.fork = function (context, args) { stage = this.homeContext.receiver.parentThatIsA(StageMorph), proc = new Process(); - runnable.isLambda = true; - // assign parameters if any were passed if (parms.length > 0) { @@ -933,68 +944,39 @@ Process.prototype.fork = function (context, args) { stage.threads.processes.push(proc); }; -Process.prototype.doReport = function (value, isCSlot) { - while (this.context && !this.context.isLambda) { +Process.prototype.doReport = function (value) { + while (this.context && this.context.expression !== 'exitReporter') { if (this.context.expression === 'doStopWarping') { this.doStopWarping(); } else { this.popContext(); } } - if (this.context && this.context.isImplicitLambda) { + return value; +}; + +Process.prototype.doStopBlock = function () { + while (this.context && !this.context.isCustomBlock) { if (this.context.expression === 'doStopWarping') { this.doStopWarping(); } else { this.popContext(); } - return this.doReport(value, true); - } - if (this.context && this.context.isCustomBlock) { - // now I'm back at the custom block sequence. - // advance my pc to my expression's length - this.context.pc = this.context.expression.length - 1; - } - if (isCSlot) { - if (this.context && - this.context.parentContext && - this.context.parentContext.expression instanceof Array) { - this.popContext(); - } } - return value; -}; - -Process.prototype.doStopBlock = function () { - this.doReport(); }; -// Process evaluation variants, commented out for now (redundant) - -/* -Process.prototype.doRunWithInputList = function (context, args) { - // provide an extra selector for the palette - return this.doRun(context, args); -}; - -Process.prototype.evaluateWithInputList = function (context, args) { - // provide an extra selector for the palette - return this.evaluate(context, args); -}; - -Process.prototype.forkWithInputList = function (context, args) { - // provide an extra selector for the palette - return this.fork(context, args); -}; -*/ - // Process continuations primitives -Process.prototype.doCallCC = function (aContext) { - this.evaluate(aContext, new List([this.context.continuation()])); +Process.prototype.doCallCC = function (aContext, isReporter) { + this.evaluate( + aContext, + new List([this.context.continuation()]), + !isReporter + ); }; Process.prototype.reportCallCC = function (aContext) { - this.doCallCC(aContext); + this.doCallCC(aContext, true); }; Process.prototype.runContinuation = function (aContext, args) { @@ -1017,6 +999,7 @@ Process.prototype.evaluateCustomBlock = function () { args = new List(this.context.inputs), parms = args.asArray(), runnable, + exit, extra, i, value, @@ -1024,7 +1007,7 @@ Process.prototype.evaluateCustomBlock = function () { if (!context) {return null; } outer = new Context(); - outer.receiver = this.context.receiver; // || this.homeContext.receiver; + outer.receiver = this.context.receiver; outer.variables.parentFrame = outer.receiver ? outer.receiver.variables : null; @@ -1032,16 +1015,12 @@ Process.prototype.evaluateCustomBlock = function () { this.context.parentContext, context.expression, outer, - outer.receiver, - true // is custom block + outer.receiver ); + runnable.isCustomBlock = true; extra = new Context(runnable, 'doYield'); - this.context.parentContext = extra; - runnable.isLambda = true; - runnable.isCustomBlock = true; - // passing parameters if any were passed if (parms.length > 0) { @@ -1064,6 +1043,18 @@ Process.prototype.evaluateCustomBlock = function () { if (runnable.expression instanceof CommandBlockMorph) { runnable.expression = runnable.expression.blockSequence(); + + // insert a reporter exit tag for the + // CALL SCRIPT primitive variant + if (this.context.expression.definition.type !== 'command') { + exit = new Context( + runnable.parentContext, + 'exitReporter', + outer, + outer.receiver + ); + runnable.parentContext = exit; + } } }; @@ -1308,16 +1299,12 @@ Process.prototype.reportListContainsItem = function (list, element) { Process.prototype.doIf = function () { var args = this.context.inputs, outer = this.context.outerContext, // for tail call elimination - isLambda = this.context.isLambda, - isImplicitLambda = this.context.isImplicitLambda, isCustomBlock = this.context.isCustomBlock; this.popContext(); if (args[0]) { if (args[1]) { this.pushContext(args[1].blockSequence(), outer); - this.context.isLambda = isLambda; - this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; } } @@ -1327,8 +1314,6 @@ Process.prototype.doIf = function () { Process.prototype.doIfElse = function () { var args = this.context.inputs, outer = this.context.outerContext, // for tail call elimination - isLambda = this.context.isLambda, - isImplicitLambda = this.context.isImplicitLambda, isCustomBlock = this.context.isCustomBlock; this.popContext(); @@ -1344,8 +1329,6 @@ Process.prototype.doIfElse = function () { } } if (this.context) { - this.context.isLambda = isLambda; - this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; } @@ -1420,8 +1403,6 @@ Process.prototype.doStopOthers = function (choice) { Process.prototype.doWarp = function (body) { // execute my contents block atomically (more or less) var outer = this.context.outerContext, // for tail call elimination - isLambda = this.context.isLambda, - isImplicitLambda = this.context.isImplicitLambda, isCustomBlock = this.context.isCustomBlock, stage; @@ -1438,13 +1419,8 @@ Process.prototype.doWarp = function (body) { stage.fps = 0; // variable frame rate } } - this.pushContext('doYield'); - - this.context.isLambda = isLambda; - this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; - if (!this.isAtomic) { this.pushContext('doStopWarping'); } @@ -1523,28 +1499,19 @@ Process.prototype.doForever = function (body) { Process.prototype.doRepeat = function (counter, body) { var block = this.context.expression, outer = this.context.outerContext, // for tail call elimination - isLambda = this.context.isLambda, - isImplicitLambda = this.context.isImplicitLambda, isCustomBlock = this.context.isCustomBlock; if (counter < 1) { // was '=== 0', which caused infinite loops on non-ints return null; } this.popContext(); - this.pushContext(block, outer); - - this.context.isLambda = isLambda; - this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; - this.context.addInput(counter - 1); - this.pushContext('doYield'); if (body) { this.pushContext(body.blockSequence()); } - this.pushContext(); }; @@ -2775,8 +2742,6 @@ Process.prototype.reportFrameCount = function () { startValue initial value for interpolated operations activeAudio audio buffer for interpolated operations, don't persist activeNote audio oscillator for interpolated ops, don't persist - isLambda marker for return ops - isImplicitLambda marker for return ops isCustomBlock marker for return ops emptySlots caches the number of empty slots for reification */ @@ -2801,22 +2766,18 @@ function Context( this.startTime = null; this.activeAudio = null; this.activeNote = null; - this.isLambda = false; // marks the end of a lambda - this.isImplicitLambda = false; // marks the end of a C-shaped slot this.isCustomBlock = false; // marks the end of a custom block's stack this.emptySlots = 0; // used for block reification } Context.prototype.toString = function () { - var pref = this.isLambda ? '\u03BB-' : '', - expr = this.expression; - + var expr = this.expression; if (expr instanceof Array) { if (expr.length > 0) { expr = '[' + expr[0] + ']'; } } - return pref + 'Context >> ' + expr + ' ' + this.variables; + return 'Context >> ' + expr + ' ' + this.variables; }; Context.prototype.image = function () { @@ -2872,6 +2833,9 @@ Context.prototype.continuation = function () { } else { return new Context(null, 'doStop'); } + if (cont.expression === 'exitReporter') { + return cont.continuation(); + } cont = cont.copyForContinuation(); cont.isContinuation = true; return cont; -- cgit v1.3.1 From ea05f7859fe5a042f0c0dd49b189c594b9a798d3 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 17 Nov 2014 10:22:39 +0100 Subject: Treat REPORT blocks inside custom command definitions as STOP THIS BLOCK / IGNORE INPUTS this also enables all existing FINCH blocks and other hardware extensions again, which used the REPORT (HTTP://) pattern --- blocks.js | 33 +++++++++++++++++++++++++-------- history.txt | 4 ++++ threads.js | 12 +++++++++--- 3 files changed, 38 insertions(+), 11 deletions(-) (limited to 'threads.js') diff --git a/blocks.js b/blocks.js index 73ef884..82ef601 100644 --- a/blocks.js +++ b/blocks.js @@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2014-October-01'; +modules.blocks = '2014-November-17'; var SyntaxElementMorph; @@ -394,10 +394,8 @@ SyntaxElementMorph.prototype.allInputs = function () { }; SyntaxElementMorph.prototype.allEmptySlots = function () { -/* - answer empty input slots of all children excluding myself, - but omit those in nested rings (lambdas) and JS-Function primitives -*/ + // answer empty input slots of all children excluding myself, + // but omit those in nested rings (lambdas) and JS-Function primitives var empty = []; if (!(this instanceof RingMorph) && (this.selector !== 'reportJSFunction')) { @@ -412,6 +410,21 @@ SyntaxElementMorph.prototype.allEmptySlots = function () { return empty; }; +SyntaxElementMorph.prototype.allReportBlocks = function () { + // answer report blocks of all children including myself, + // but omit those in nested rings (lambdas) + if (this.selector === 'doReport') {return [this]; } + var reports = []; + if (!(this instanceof RingMorph)) { + this.children.forEach(function (morph) { + if (morph.allReportBlocks) { + reports = reports.concat(morph.allReportBlocks()); + } + }); + } + return reports; +}; + SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) { var scripts = this.parentThatIsA(ScriptsMorph), replacement = newArg, @@ -3169,9 +3182,13 @@ BlockMorph.prototype.snap = function () { I inherit from BlockMorph adding the following most important public accessors: - nextBlock() - set / get the block attached to my bottom - bottomBlock() - answer the bottom block of my stack - blockSequence() - answer an array of blocks starting with myself + nextBlock() - set / get the block attached to my bottom + bottomBlock() - answer the bottom block of my stack + blockSequence() - answer an array of blocks starting with myself + + and the following "lexical awareness" indicator: + + partOfCustomCommand - temporary bool set by the evaluator */ // CommandBlockMorph inherits from BlockMorph: diff --git a/history.txt b/history.txt index b3bc7da..88d7ca4 100755 --- a/history.txt +++ b/history.txt @@ -2317,3 +2317,7 @@ ______ 141114 ------ * Threads, Store: Fix reporting out of nested custom C-shaped blocks + +141117 +------ +* Threads, Blocks: Treat REPORT blocks inside custom command definitions as STOP THIS BLOCK / IGNORE INPUTS diff --git a/threads.js b/threads.js index dfa335e..cf97362 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-15'; +modules.threads = '2014-November-17'; var ThreadManager; var Process; @@ -945,6 +945,9 @@ Process.prototype.fork = function (context, args) { }; Process.prototype.doReport = function (value) { + if (this.context.expression.partOfCustomCommand) { + return this.doStopBlock(); + } while (this.context && this.context.expression !== 'exitReporter') { if (this.context.expression === 'doStopWarping') { this.doStopWarping(); @@ -1042,8 +1045,6 @@ Process.prototype.evaluateCustomBlock = function () { } if (runnable.expression instanceof CommandBlockMorph) { - runnable.expression = runnable.expression.blockSequence(); - // insert a reporter exit tag for the // CALL SCRIPT primitive variant if (this.context.expression.definition.type !== 'command') { @@ -1054,7 +1055,12 @@ Process.prototype.evaluateCustomBlock = function () { outer.receiver ); runnable.parentContext = exit; + } else { // mark all REPORT blocks as being part of a custom command + runnable.expression.allReportBlocks().forEach(function (rb) { + rb.partOfCustomCommand = true; + }); } + runnable.expression = runnable.expression.blockSequence(); } }; -- cgit v1.3.1 From f537f62ace4092e1f46d0ca54577ea8810e7510a Mon Sep 17 00:00:00 2001 From: Bernat Romagosa Date: Mon, 17 Nov 2014 14:05:13 +0100 Subject: Added callback to Process --- threads.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index cf97362..4592e92 100644 --- a/threads.js +++ b/threads.js @@ -136,7 +136,8 @@ ThreadManager.prototype.toggleProcess = function (block) { ThreadManager.prototype.startProcess = function ( block, isThreadSafe, - exportResult + exportResult, + callback ) { var active = this.findProcess(block), top = block.topBlock(), @@ -149,7 +150,7 @@ ThreadManager.prototype.startProcess = function ( this.removeTerminatedProcesses(); } top.addHighlight(); - newProc = new Process(block.topBlock()); + newProc = new Process(block.topBlock(), callback); newProc.exportResult = exportResult; this.processes.push(newProc); return newProc; @@ -314,6 +315,7 @@ ThreadManager.prototype.findProcess = function (block) { and when the process was paused exportResult boolean flag indicating whether a picture of the top block along with the result bubble shoud be exported + callback a function to be executed when the process is done */ Process.prototype = {}; @@ -321,7 +323,7 @@ Process.prototype.contructor = Process; Process.prototype.timeout = 500; // msecs after which to force yield Process.prototype.isCatchingErrors = true; -function Process(topBlock) { +function Process(topBlock, callback) { this.topBlock = topBlock || null; this.readyToYield = false; @@ -338,6 +340,7 @@ function Process(topBlock) { this.pauseOffset = null; this.frameCount = 0; this.exportResult = false; + this.callback = callback; if (topBlock) { this.homeContext.receiver = topBlock.receiver(); @@ -435,8 +438,8 @@ Process.prototype.pauseStep = function () { Process.prototype.evaluateContext = function () { var exp = this.context.expression; - this.frameCount += 1; + if (exp instanceof Array) { return this.evaluateSequence(exp); } @@ -476,9 +479,9 @@ Process.prototype.evaluateBlock = function (block, argCount) { } if (this.isCatchingErrors) { try { - this.returnValueToParentContext( - rcvr[block.selector].apply(rcvr, inputs) - ); + var result = rcvr[block.selector].apply(rcvr, inputs); + if (this.callback) { this.callback(result) }; + this.returnValueToParentContext(result); this.popContext(); } catch (error) { this.handleError(error, block); -- cgit v1.3.1 From 8e992dcaa877044a2b2f15edf86c003f4df7253c Mon Sep 17 00:00:00 2001 From: Manuel Menezes de Sequeira Date: Tue, 18 Nov 2014 18:11:14 +0000 Subject: Add localization to number of arguments error and temporary watchers --- threads.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 086ffcb..d194220 100644 --- a/threads.js +++ b/threads.js @@ -849,8 +849,8 @@ Process.prototype.evaluate = function ( } else if (context.emptySlots !== 1) { throw new Error( - 'expecting ' + context.emptySlots + ' input(s), ' - + 'but getting ' + parms.length + localize('expecting') + ' ' + context.emptySlots + ' ' + + localize('input(s), but getting') + ' ' + parms.length ); } } @@ -915,8 +915,8 @@ Process.prototype.fork = function (context, args) { } else if (context.emptySlots !== 1) { throw new Error( - 'expecting ' + context.emptySlots + ' input(s), ' - + 'but getting ' + parms.length + localize('expecting') + ' ' + context.emptySlots + ' ' + + localize('input(s), but getting') + ' ' + parms.length ); } } @@ -1148,7 +1148,7 @@ Process.prototype.doShowVar = function (varName) { if (isGlobal || target.owner) { label = name; } else { - label = name + ' (temporary)'; + label = name + ' ' + localize('(temporary)'); } watcher = new WatcherMorph( label, -- cgit v1.3.1 From 91690adb29633368ec458ca510086c3b17e0490f Mon Sep 17 00:00:00 2001 From: Bernat Romagosa Date: Wed, 19 Nov 2014 09:34:59 +0100 Subject: callback is only executed when the outmost block returns a value --- threads.js | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 4592e92..4522d35 100644 --- a/threads.js +++ b/threads.js @@ -137,7 +137,7 @@ ThreadManager.prototype.startProcess = function ( block, isThreadSafe, exportResult, - callback + callback ) { var active = this.findProcess(block), top = block.topBlock(), @@ -236,18 +236,22 @@ ThreadManager.prototype.removeTerminatedProcesses = function () { } if (proc.topBlock instanceof ReporterBlockMorph) { - if (proc.homeContext.inputs[0] instanceof List) { - proc.topBlock.showBubble( - new ListWatcherMorph( - proc.homeContext.inputs[0] - ), - proc.exportResult - ); + if (proc.callback) { + proc.callback(proc.homeContext.inputs[0]) } else { - proc.topBlock.showBubble( - proc.homeContext.inputs[0], - proc.exportResult - ); + if (proc.homeContext.inputs[0] instanceof List) { + proc.topBlock.showBubble( + new ListWatcherMorph( + proc.homeContext.inputs[0] + ), + proc.exportResult + ); + } else { + proc.topBlock.showBubble( + proc.homeContext.inputs[0], + proc.exportResult + ); + } } } } else { @@ -315,7 +319,7 @@ ThreadManager.prototype.findProcess = function (block) { and when the process was paused exportResult boolean flag indicating whether a picture of the top block along with the result bubble shoud be exported - callback a function to be executed when the process is done + callback a function to be executed when the process is done */ Process.prototype = {}; @@ -340,7 +344,7 @@ function Process(topBlock, callback) { this.pauseOffset = null; this.frameCount = 0; this.exportResult = false; - this.callback = callback; + this.callback = callback; if (topBlock) { this.homeContext.receiver = topBlock.receiver(); @@ -439,7 +443,7 @@ Process.prototype.pauseStep = function () { Process.prototype.evaluateContext = function () { var exp = this.context.expression; this.frameCount += 1; - + if (exp instanceof Array) { return this.evaluateSequence(exp); } @@ -479,9 +483,7 @@ Process.prototype.evaluateBlock = function (block, argCount) { } if (this.isCatchingErrors) { try { - var result = rcvr[block.selector].apply(rcvr, inputs); - if (this.callback) { this.callback(result) }; - this.returnValueToParentContext(result); + this.returnValueToParentContext(rcvr[block.selector].apply(rcvr, inputs)); this.popContext(); } catch (error) { this.handleError(error, block); -- cgit v1.3.1 From 5771e93fa150c9a39e84bd9fad0e8a0bd2d61085 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 20 Nov 2014 14:21:56 +0100 Subject: Fixed #364 avoid “freezing” when calling LAUNCH on empty ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- history.txt | 3 ++- threads.js | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 53bd4a6..c26599c 100755 --- a/history.txt +++ b/history.txt @@ -2324,4 +2324,5 @@ ______ 141120 ------ -* Lists: Fixed #642, avoid “freezing” when calling CONS on non-list/null +* Lists: Fixed #642 avoid “freezing” when calling CONS on non-list/null +* Threads: Fixed #364 avoid “freezing” when calling LAUNCH on empty ring diff --git a/threads.js b/threads.js index cf97362..c6b0fde 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-17'; +modules.threads = '2014-November-20'; var ThreadManager; var Process; @@ -225,8 +225,9 @@ ThreadManager.prototype.removeTerminatedProcesses = function () { var remaining = []; this.processes.forEach(function (proc) { if (!proc.isRunning() && !proc.errorFlag && !proc.isDead) { - proc.topBlock.removeHighlight(); - + if (proc.topBlock instanceof BlockMorph) { + proc.topBlock.removeHighlight(); + } if (proc.prompter) { proc.prompter.destroy(); if (proc.homeContext.receiver.stopTalking) { @@ -880,6 +881,9 @@ Process.prototype.fork = function (context, args) { 'continuations cannot be forked' ); } + if (!(context instanceof Context)) { + throw new Error('expecting a ring but getting ' + context); + } var outer = new Context(null, null, context.outerContext), runnable = new Context(null, -- cgit v1.3.1 From f37e90436a21a906d3cef536871c3e1913a4a2b7 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 20 Nov 2014 14:40:13 +0100 Subject: renamed Process::callback to "onComplete" --- history.txt | 1 + threads.js | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index c26599c..bb6eac8 100755 --- a/history.txt +++ b/history.txt @@ -2326,3 +2326,4 @@ ______ ------ * Lists: Fixed #642 avoid “freezing” when calling CONS on non-list/null * Threads: Fixed #364 avoid “freezing” when calling LAUNCH on empty ring +* Threads: Added optional “onComplete” callback to Process, thanks, @bromagosa! diff --git a/threads.js b/threads.js index d1209bf..f210380 100644 --- a/threads.js +++ b/threads.js @@ -237,8 +237,8 @@ ThreadManager.prototype.removeTerminatedProcesses = function () { } if (proc.topBlock instanceof ReporterBlockMorph) { - if (proc.callback) { - proc.callback(proc.homeContext.inputs[0]) + if (proc.onComplete instanceof Function) { + proc.onComplete(proc.homeContext.inputs[0]); } else { if (proc.homeContext.inputs[0] instanceof List) { proc.topBlock.showBubble( @@ -301,9 +301,9 @@ ThreadManager.prototype.findProcess = function (block) { are children receiver object (sprite) to which the process applies, cached from the top block - context the Context describing the current state + context the Context describing the current state of this process - homeContext stores information relevant to the whole process, + homeContext stores information relevant to the whole process, i.e. its receiver, result etc. isPaused boolean indicating whether to pause readyToYield boolean indicating whether to yield control to @@ -311,16 +311,17 @@ ThreadManager.prototype.findProcess = function (block) { readyToTerminate boolean indicating whether the stop method has been called isDead boolean indicating a terminated clone process - timeout msecs after which to force yield - lastYield msecs when the process last yielded - errorFlag boolean indicating whether an error was encountered + timeout msecs after which to force yield + lastYield msecs when the process last yielded + errorFlag boolean indicating whether an error was encountered prompter active instance of StagePrompterMorph httpRequest active instance of an HttpRequest or null pauseOffset msecs between the start of an interpolated operation and when the process was paused exportResult boolean flag indicating whether a picture of the top block along with the result bubble shoud be exported - callback a function to be executed when the process is done + onComplete an optional callback function to be executed when + the process is done */ Process.prototype = {}; @@ -328,7 +329,7 @@ Process.prototype.contructor = Process; Process.prototype.timeout = 500; // msecs after which to force yield Process.prototype.isCatchingErrors = true; -function Process(topBlock, callback) { +function Process(topBlock, onComplete) { this.topBlock = topBlock || null; this.readyToYield = false; @@ -345,7 +346,7 @@ function Process(topBlock, callback) { this.pauseOffset = null; this.frameCount = 0; this.exportResult = false; - this.callback = callback; + this.onComplete = onComplete || null; if (topBlock) { this.homeContext.receiver = topBlock.receiver(); @@ -444,7 +445,6 @@ Process.prototype.pauseStep = function () { Process.prototype.evaluateContext = function () { var exp = this.context.expression; this.frameCount += 1; - if (exp instanceof Array) { return this.evaluateSequence(exp); } @@ -484,7 +484,9 @@ Process.prototype.evaluateBlock = function (block, argCount) { } if (this.isCatchingErrors) { try { - this.returnValueToParentContext(rcvr[block.selector].apply(rcvr, inputs)); + this.returnValueToParentContext( + rcvr[block.selector].apply(rcvr, inputs) + ); this.popContext(); } catch (error) { this.handleError(error, block); -- cgit v1.3.1 From ce93fe8da73738e67683fa9cbfe7741de62f5336 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 20 Nov 2014 15:16:12 +0100 Subject: fix ‘line’ option in ‘split’ block for Windows files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thanks, @brianharvey! --- history.txt | 1 + threads.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index b022790..c44a57b 100755 --- a/history.txt +++ b/history.txt @@ -2330,3 +2330,4 @@ ______ * GUI: Set Default Save location to Cloud on load, thanks, @cycomachead! * GUI: Updated the “About” Dialog with a mention of support from CDG (SAP Labs) * BYOB: Percent sign fix for block labels, thanks, @natashasandy! +* Threads: fix ‘line’ option in ‘split’ block for Windows files, thanks, @brianharvey! diff --git a/threads.js b/threads.js index f210380..11b72dc 100644 --- a/threads.js +++ b/threads.js @@ -2126,7 +2126,7 @@ Process.prototype.reportTextSplit = function (string, delimiter) { str = (string || '').toString(); switch (this.inputOption(delimiter)) { case 'line': - del = '\n'; + del = '\r?\n'; break; case 'tab': del = '\t'; -- cgit v1.3.1 From f2d0c2eba5d6b012fc3fb1167c82b64b2f8ed447 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 20 Nov 2014 15:53:14 +0100 Subject: integrate translation update --- history.txt | 1 + threads.js | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 110ea89..0faac6b 100755 --- a/history.txt +++ b/history.txt @@ -2332,3 +2332,4 @@ ______ * BYOB: Percent sign fix for block labels, thanks, @natashasandy! * Threads: fix ‘line’ option in ‘split’ block for Windows files, thanks, @brianharvey! * Morphic: fix slider range 1, thanks, @tonychenr ! +* translation update, thanks, Manuel! diff --git a/threads.js b/threads.js index ea563f1..7d656c4 100644 --- a/threads.js +++ b/threads.js @@ -858,8 +858,9 @@ Process.prototype.evaluate = function ( } else if (context.emptySlots !== 1) { throw new Error( - localize('expecting') + ' ' + context.emptySlots + ' ' - + localize('input(s), but getting') + ' ' + parms.length + localize('expecting') + ' ' + context.emptySlots + ' ' + + localize('input(s), but getting') + ' ' + + parms.length ); } } @@ -937,8 +938,9 @@ Process.prototype.fork = function (context, args) { } else if (context.emptySlots !== 1) { throw new Error( - localize('expecting') + ' ' + context.emptySlots + ' ' - + localize('input(s), but getting') + ' ' + parms.length + localize('expecting') + ' ' + context.emptySlots + ' ' + + localize('input(s), but getting') + ' ' + + parms.length ); } } @@ -2986,7 +2988,7 @@ VariableFrame.prototype.find = function (name) { throw new Error( localize('a variable of name \'') + name - + localize('\'\ndoes not exist in this context') + + localize('\'\ndoes not exist in this context') ); }; -- cgit v1.3.1 From 9e91a93ac029056adae89e7b8e07558b47f9634f Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 Nov 2014 16:55:25 +0100 Subject: Fix "stop this block"’s lexical awareness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “stop this block” when used inside a custom block definition now always returns out of the lexically enclosing script (the definition), even if it is used inside other nested, C-shaped custom blocks in the definition code. Previously it only stopped the nearest encompassing “for” block, now it always stops the block whose definition it is in. I don’t expect this fix to break any existing projects. --- blocks.js | 40 +++++++++++++++++++++++-------------- history.txt | 4 ++++ threads.js | 65 ++++++++++++++++++++++++++++++++++++++++++------------------- 3 files changed, 74 insertions(+), 35 deletions(-) (limited to 'threads.js') diff --git a/blocks.js b/blocks.js index 82ef601..e56d0ed 100644 --- a/blocks.js +++ b/blocks.js @@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2014-November-17'; +modules.blocks = '2014-November-21'; var SyntaxElementMorph; @@ -395,7 +395,9 @@ SyntaxElementMorph.prototype.allInputs = function () { SyntaxElementMorph.prototype.allEmptySlots = function () { // answer empty input slots of all children excluding myself, - // but omit those in nested rings (lambdas) and JS-Function primitives + // but omit those in nested rings (lambdas) and JS-Function primitives. + // Used by the evaluator when binding implicit formal parameters + // to empty input slots var empty = []; if (!(this instanceof RingMorph) && (this.selector !== 'reportJSFunction')) { @@ -410,19 +412,24 @@ SyntaxElementMorph.prototype.allEmptySlots = function () { return empty; }; -SyntaxElementMorph.prototype.allReportBlocks = function () { - // answer report blocks of all children including myself, - // but omit those in nested rings (lambdas) - if (this.selector === 'doReport') {return [this]; } - var reports = []; - if (!(this instanceof RingMorph)) { - this.children.forEach(function (morph) { - if (morph.allReportBlocks) { - reports = reports.concat(morph.allReportBlocks()); - } - }); +SyntaxElementMorph.prototype.tagExitBlocks = function (stopTag, isCommand) { + // tag 'report' and 'stop this block' blocks of all children including + // myself, with either a stopTag (for "stop" blocks) or an indicator of + // being inside a command block definition, but omit those in nested + // rings (lambdas. Used by the evaluator when entering a procedure + if (this.selector === 'doReport') { + this.partOfCustomCommand = isCommand; + } else if (this.selector === 'doStopThis') { + this.exitTag = stopTag; + } else { + if (!(this instanceof RingMorph)) { + this.children.forEach(function (morph) { + if (morph.tagExitBlocks) { + morph.tagExitBlocks(stopTag, isCommand); + } + }); + } } - return reports; }; SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) { @@ -3186,9 +3193,10 @@ BlockMorph.prototype.snap = function () { bottomBlock() - answer the bottom block of my stack blockSequence() - answer an array of blocks starting with myself - and the following "lexical awareness" indicator: + and the following "lexical awareness" indicators: partOfCustomCommand - temporary bool set by the evaluator + exitTag - temporary string or number set by the evaluator */ // CommandBlockMorph inherits from BlockMorph: @@ -3206,6 +3214,8 @@ function CommandBlockMorph() { CommandBlockMorph.prototype.init = function () { CommandBlockMorph.uber.init.call(this); this.setExtent(new Point(200, 100)); + this.partOfCustomCommand = false; + this.exitTag = null; }; // CommandBlockMorph enumerating: diff --git a/history.txt b/history.txt index 0faac6b..c468c99 100755 --- a/history.txt +++ b/history.txt @@ -2333,3 +2333,7 @@ ______ * Threads: fix ‘line’ option in ‘split’ block for Windows files, thanks, @brianharvey! * Morphic: fix slider range 1, thanks, @tonychenr ! * translation update, thanks, Manuel! + +141121 +------ +* Threads, Blocks: Fix STOP THIS BLOCK’s lexical awareness diff --git a/threads.js b/threads.js index 7d656c4..ab40460 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-20'; +modules.threads = '2014-November-21'; var ThreadManager; var Process; @@ -322,6 +322,9 @@ ThreadManager.prototype.findProcess = function (block) { block along with the result bubble shoud be exported onComplete an optional callback function to be executed when the process is done + procedureCount number counting procedure call entries, + used to tag custom block calls, so "stop block" + invocations can catch them */ Process.prototype = {}; @@ -347,6 +350,7 @@ function Process(topBlock, onComplete) { this.frameCount = 0; this.exportResult = false; this.onComplete = onComplete || null; + this.procedureCount = 0; if (topBlock) { this.homeContext.receiver = topBlock.receiver(); @@ -765,12 +769,6 @@ Process.prototype.reportJSFunction = function (parmNames, body) { ); }; -/* -Process.prototype.doRun = function (context, args, isCustomBlock) { - return this.evaluate(context, args, true, isCustomBlock); -}; -*/ - Process.prototype.doRun = function (context, args) { return this.evaluate(context, args, true); }; @@ -957,9 +955,11 @@ Process.prototype.fork = function (context, args) { stage.threads.processes.push(proc); }; +// Process "return" primitives + Process.prototype.doReport = function (value) { if (this.context.expression.partOfCustomCommand) { - return this.doStopBlock(); + return this.doStopCustomBlock(); } while (this.context && this.context.expression !== 'exitReporter') { if (this.context.expression === 'doStopWarping') { @@ -972,6 +972,22 @@ Process.prototype.doReport = function (value) { }; Process.prototype.doStopBlock = function () { + var target = this.context.expression.exitTag; + if (isNil(target)) { + return this.doStopCustomBlock(); + } + while (this.context && this.context.tag !== target) { + if (this.context.expression === 'doStopWarping') { + this.doStopWarping(); + } else { + this.popContext(); + } + } +}; + +Process.prototype.doStopCustomBlock = function () { + // fallback solution for "report" blocks inside + // custom command definitions and untagged "stop" blocks while (this.context && !this.context.isCustomBlock) { if (this.context.expression === 'doStopWarping') { this.doStopWarping(); @@ -1022,6 +1038,7 @@ Process.prototype.evaluateCustomBlock = function () { outer; if (!context) {return null; } + this.procedureCount += 1; outer = new Context(); outer.receiver = this.context.receiver; outer.variables.parentFrame = outer.receiver ? @@ -1068,10 +1085,15 @@ Process.prototype.evaluateCustomBlock = function () { outer.receiver ); runnable.parentContext = exit; - } else { // mark all REPORT blocks as being part of a custom command - runnable.expression.allReportBlocks().forEach(function (rb) { - rb.partOfCustomCommand = true; - }); + } else { + // tag all "stop this block" blocks with the current + // procedureCount as exitTag, and mark all "report" blocks + // as being inside a custom command definition + runnable.expression.tagExitBlocks(this.procedureCount, true); + + // tag runnable with the current procedure count, so + // "stop this block" blocks can catch it + runnable.tag = this.procedureCount; } runnable.expression = runnable.expression.blockSequence(); } @@ -2746,23 +2768,25 @@ Process.prototype.reportFrameCount = function () { structure: - parentContext the Context to return to when this one has + parentContext the Context to return to when this one has been evaluated. outerContext the Context holding my lexical scope - expression SyntaxElementMorph, an array of blocks to evaluate, + expression SyntaxElementMorph, an array of blocks to evaluate, null or a String denoting a selector, e.g. 'doYield' receiver the object to which the expression applies, if any - variables the current VariableFrame, if any - inputs an array of input values computed so far + variables the current VariableFrame, if any + inputs an array of input values computed so far (if expression is a BlockMorph) - pc the index of the next block to evaluate + pc the index of the next block to evaluate (if expression is an array) - startTime time when the context was first evaluated - startValue initial value for interpolated operations + startTime time when the context was first evaluated + startValue initial value for interpolated operations activeAudio audio buffer for interpolated operations, don't persist activeNote audio oscillator for interpolated ops, don't persist isCustomBlock marker for return ops - emptySlots caches the number of empty slots for reification + emptySlots caches the number of empty slots for reification + tag string or number to optionally identify the Context, + as a "return" target (for the "stop block" primitive) */ function Context( @@ -2787,6 +2811,7 @@ function Context( this.activeNote = null; this.isCustomBlock = false; // marks the end of a custom block's stack this.emptySlots = 0; // used for block reification + this.tag = null; // lexical catch-tag for custom blocks } Context.prototype.toString = function () { -- cgit v1.3.1 From 781144aa3cf05ea00652d224baef03eb157dce52 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Sun, 23 Nov 2014 13:53:34 +0100 Subject: Fix “stop this block” primitive for tail-call-elimination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- history.txt | 4 ++++ threads.js | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index c468c99..5a41f5a 100755 --- a/history.txt +++ b/history.txt @@ -2337,3 +2337,7 @@ ______ 141121 ------ * Threads, Blocks: Fix STOP THIS BLOCK’s lexical awareness + +1411213 +------- +* Threads: Fix “stop this block” primitive for tail-call-elimination diff --git a/threads.js b/threads.js index ab40460..68387dc 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-21'; +modules.threads = '2014-November-23'; var ThreadManager; var Process; @@ -976,13 +976,15 @@ Process.prototype.doStopBlock = function () { if (isNil(target)) { return this.doStopCustomBlock(); } - while (this.context && this.context.tag !== target) { + while (this.context && + (isNil(this.context.tag) || (this.context.tag > target))) { if (this.context.expression === 'doStopWarping') { this.doStopWarping(); } else { this.popContext(); } } + this.pushContext(); }; Process.prototype.doStopCustomBlock = function () { @@ -1026,7 +1028,8 @@ Process.prototype.runContinuation = function (aContext, args) { // Process custom block primitives Process.prototype.evaluateCustomBlock = function () { - var context = this.context.expression.definition.body, + var caller = this.context.parentContext, + context = this.context.expression.definition.body, declarations = this.context.expression.definition.declarations, args = new List(this.context.inputs), parms = args.asArray(), @@ -1091,9 +1094,12 @@ Process.prototype.evaluateCustomBlock = function () { // as being inside a custom command definition runnable.expression.tagExitBlocks(this.procedureCount, true); - // tag runnable with the current procedure count, so - // "stop this block" blocks can catch it - runnable.tag = this.procedureCount; + // tag the caller with the current procedure count, so + // "stop this block" blocks can catch it, but only + // if the caller hasn't been tagged already + if (caller && !caller.tag) { + caller.tag = this.procedureCount; + } } runnable.expression = runnable.expression.blockSequence(); } -- cgit v1.3.1 From 8814b61a323a2fb554cf1869f6a7b8ab06009764 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 24 Nov 2014 09:28:45 +0100 Subject: Fixed #318 --- history.txt | 5 +++++ threads.js | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 5a41f5a..4d17e3e 100755 --- a/history.txt +++ b/history.txt @@ -2341,3 +2341,8 @@ ______ 1411213 ------- * Threads: Fix “stop this block” primitive for tail-call-elimination + +1411214 +------- +* Threads: Fixed #318 + diff --git a/threads.js b/threads.js index 68387dc..d5040c5 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-23'; +modules.threads = '2014-November-24'; var ThreadManager; var Process; @@ -100,8 +100,18 @@ function snapEquals(a, b) { var x = +a, y = +b, + i, specials = [true, false, '']; + // "zum Schneckengang verdorben, was Adlerflug geworden wäre" + // collecting edge-cases that somebody complained about + // on Github. Folks, take it easy and keep it fun, okay? + // Shit like this is patently ugly and slows Snap down. Tnx! + for (i = 9; i <= 13; i += 1) { + specials.push(String.fromCharCode(i)); + } + specials.push(String.fromCharCode(160)); + // check for special values before coercing to numbers if (isNaN(x) || isNaN(y) || [a, b].some(function (any) {return contains(specials, any) || @@ -110,7 +120,7 @@ function snapEquals(a, b) { y = b; } - // handle text comparision case-insensitive. + // handle text comparison case-insensitive. if (isString(x) && isString(y)) { return x.toLowerCase() === y.toLowerCase(); } -- cgit v1.3.1 From e48eda6cc0bdd234f2b385974d1cee66656e070a Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 24 Nov 2014 10:48:49 +0100 Subject: Fixed #644 --- history.txt | 1 + threads.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 2d7e53e..819dcf0 100755 --- a/history.txt +++ b/history.txt @@ -2347,3 +2347,4 @@ ______ * Threads: Fixed #318 * Objects: Fixed #416 * Objects: Fixed #372 +* Threads: Fixed #644 diff --git a/threads.js b/threads.js index d5040c5..574fd35 100644 --- a/threads.js +++ b/threads.js @@ -1307,7 +1307,7 @@ Process.prototype.doInsertInList = function (element, index, list) { return null; } if (this.inputOption(index) === 'any') { - idx = this.reportRandom(1, list.length()); + idx = this.reportRandom(1, list.length() + 1); } if (this.inputOption(index) === 'last') { idx = list.length() + 1; -- cgit v1.3.1 From 68c4d2d291c8da03798164f0740ed18889f09db3 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 24 Nov 2014 12:59:02 +0100 Subject: fixed #131 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and display an error if a reporter or a “called” ring is missing a “report” statement --- history.txt | 1 + threads.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 2efc442..30de1ab 100755 --- a/history.txt +++ b/history.txt @@ -2349,3 +2349,4 @@ ______ * Objects: Fixed #372 * Threads: Fixed #644 * Store: Fixed #34 +* Threads: Fixed #131 diff --git a/threads.js b/threads.js index 574fd35..9161417 100644 --- a/threads.js +++ b/threads.js @@ -698,7 +698,7 @@ Process.prototype.doYield = function () { Process.prototype.exitReporter = function () { // catch-tag for REPORT and STOP BLOCK primitives - this.popContext(); + this.handleError(new Error("missing 'report' statement in reporter")); }; // Process Exception Handling @@ -1098,6 +1098,7 @@ Process.prototype.evaluateCustomBlock = function () { outer.receiver ); runnable.parentContext = exit; + this.popContext(); // don't yield when done } else { // tag all "stop this block" blocks with the current // procedureCount as exitTag, and mark all "report" blocks -- cgit v1.3.1 From bd37771334e7c82f86aa99f7509f88dc6950b8c5 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 24 Nov 2014 14:38:27 +0100 Subject: allow recursive reporters to be stopped by user --- threads.js | 1 - 1 file changed, 1 deletion(-) (limited to 'threads.js') diff --git a/threads.js b/threads.js index 1782984..1f1915b 100644 --- a/threads.js +++ b/threads.js @@ -1098,7 +1098,6 @@ Process.prototype.evaluateCustomBlock = function () { outer.receiver ); runnable.parentContext = exit; - this.popContext(); // don't yield when done } else { // tag all "stop this block" blocks with the current // procedureCount as exitTag, and mark all "report" blocks -- cgit v1.3.1 From 4be96bb240ea8518fa95218cba7111846af93baa Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 24 Nov 2014 16:02:21 +0100 Subject: tail-call-elimination for reporters - experiment (commented out, under construction) --- history.txt | 1 + threads.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'threads.js') diff --git a/history.txt b/history.txt index f710d77..964686e 100755 --- a/history.txt +++ b/history.txt @@ -2352,3 +2352,4 @@ ______ * Threads: Fixed #131 * snap.html, favicon.ico: new Favicon, thanks, Michael! * Threads: improved whitespace detection for “split” primitive, thanks, Michael! +* Threads: tail-call-elimination for reporters experiment (commented out, under construction) diff --git a/threads.js b/threads.js index 1f1915b..a6b824b 100644 --- a/threads.js +++ b/threads.js @@ -548,6 +548,34 @@ Process.prototype.reportAnd = function (block) { } }; +/* + tail-call-elimination for reporters + ----------------------------------- + currently under construction, commented out for now + to activate add 'doReport' to the list of special forms + selectors in evaluateBlock() and comment out / remove + the current 'doReport' primitive in the "return" + section of the code + +Process.prototype.doReport = function (block) { + +// if (this.context.expression.partOfCustomCommand) { +// return this.doStopCustomBlock(); +// } + + var outer = this.context.outerContext; + while (this.context && this.context.expression !== 'exitReporter') { + if (this.context.expression === 'doStopWarping') { + this.doStopWarping(); + } else { + this.popContext(); + } + } + this.popContext(); + this.pushContext(block.inputs()[0], outer); +}; +*/ + // Process: Non-Block evaluation Process.prototype.evaluateMultiSlot = function (multiSlot, argCount) { -- cgit v1.3.1 From 75849a59a2ac594c2c78a7cf6ef1e8be6c1eec57 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 25 Nov 2014 12:24:20 +0100 Subject: Evaluator optimizations reducing the stack size for reporters --- history.txt | 6 ++- threads.js | 127 +++++++++++++++++++++++++++++------------------------------- 2 files changed, 67 insertions(+), 66 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 964686e..22215e9 100755 --- a/history.txt +++ b/history.txt @@ -2342,7 +2342,7 @@ ______ ------- * Threads: Fix “stop this block” primitive for tail-call-elimination -1411214 +1411224 ------- * Threads: Fixed #318 * Objects: Fixed #416 @@ -2353,3 +2353,7 @@ ______ * snap.html, favicon.ico: new Favicon, thanks, Michael! * Threads: improved whitespace detection for “split” primitive, thanks, Michael! * Threads: tail-call-elimination for reporters experiment (commented out, under construction) + +1411225 +------- +* Threads: Evaluator optimizations (reducing the stack size for reporters) diff --git a/threads.js b/threads.js index a6b824b..aef13a1 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-24'; +modules.threads = '2014-November-25'; var ThreadManager; var Process; @@ -218,11 +218,10 @@ ThreadManager.prototype.resumeAll = function (stage) { }; ThreadManager.prototype.step = function () { -/* - run each process until it gives up control, skipping processes - for sprites that are currently picked up, then filter out any - processes that have been terminated -*/ + // run each process until it gives up control, skipping processes + // for sprites that are currently picked up, then filter out any + // processes that have been terminated + this.processes.forEach(function (proc) { if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) { proc.runStep(); @@ -384,13 +383,13 @@ Process.prototype.isRunning = function () { // Process entry points Process.prototype.runStep = function () { -/* - a step is an an uninterruptable 'atom', it can consist - of several contexts, even of several blocks -*/ + // a step is an an uninterruptable 'atom', it can consist + // of several contexts, even of several blocks + if (this.isPaused) { // allow pausing in between atomic steps: return this.pauseStep(); } + this.readyToYield = false; while (!this.readyToYield && this.context @@ -459,6 +458,9 @@ Process.prototype.pauseStep = function () { Process.prototype.evaluateContext = function () { var exp = this.context.expression; this.frameCount += 1; + if (this.context.tag === 'exit') { + this.expectReport(); + } if (exp instanceof Array) { return this.evaluateSequence(exp); } @@ -482,7 +484,7 @@ Process.prototype.evaluateContext = function () { Process.prototype.evaluateBlock = function (block, argCount) { // check for special forms - if (contains(['reportOr', 'reportAnd'], block.selector)) { + if (contains(['reportOr', 'reportAnd', 'doReport'], block.selector)) { return this[block.selector](block); } @@ -548,33 +550,29 @@ Process.prototype.reportAnd = function (block) { } }; -/* - tail-call-elimination for reporters - ----------------------------------- - currently under construction, commented out for now - to activate add 'doReport' to the list of special forms - selectors in evaluateBlock() and comment out / remove - the current 'doReport' primitive in the "return" - section of the code - Process.prototype.doReport = function (block) { - -// if (this.context.expression.partOfCustomCommand) { -// return this.doStopCustomBlock(); -// } - + if (this.context.expression.partOfCustomCommand) { + this.doStopCustomBlock(); + this.popContext(); + return; + } var outer = this.context.outerContext; - while (this.context && this.context.expression !== 'exitReporter') { + while (this.context && this.context.tag !== 'exit') { if (this.context.expression === 'doStopWarping') { this.doStopWarping(); } else { this.popContext(); } } - this.popContext(); + if (this.context.expression === 'expectReport') { + // pop off inserted top-level exit context + this.popContext(); + } else { + // un-tag and preserve original caller + this.context.tag = null; + } this.pushContext(block.inputs()[0], outer); }; -*/ // Process: Non-Block evaluation @@ -724,9 +722,8 @@ Process.prototype.doYield = function () { } }; -Process.prototype.exitReporter = function () { - // catch-tag for REPORT and STOP BLOCK primitives - this.handleError(new Error("missing 'report' statement in reporter")); +Process.prototype.expectReport = function () { + this.handleError(new Error("reporter didn't report")); }; // Process Exception Handling @@ -831,9 +828,10 @@ Process.prototype.evaluate = function ( } var outer = new Context(null, null, context.outerContext), + caller = this.context.parentContext, + exit, runnable, extra, - exit, parms = args.asArray(), i, value; @@ -904,17 +902,22 @@ Process.prototype.evaluate = function ( if (runnable.expression instanceof CommandBlockMorph) { runnable.expression = runnable.expression.blockSequence(); - - // insert a reporter exit tag for the - // CALL SCRIPT primitive variant if (!isCommand) { - exit = new Context( - runnable.parentContext, - 'exitReporter', - outer, - outer.receiver - ); - runnable.parentContext = exit; + if (caller) { + // tag caller, so "report" can catch it later + caller.tag = 'exit'; + } else { + // top-level context, insert a tagged exit context + // which "report" can catch later + exit = new Context( + runnable.parentContext, + 'expectReport', + outer, + outer.receiver + ); + exit.tag = 'exit'; + runnable.parentContext = exit; + } } } }; @@ -993,21 +996,7 @@ Process.prototype.fork = function (context, args) { stage.threads.processes.push(proc); }; -// Process "return" primitives - -Process.prototype.doReport = function (value) { - if (this.context.expression.partOfCustomCommand) { - return this.doStopCustomBlock(); - } - while (this.context && this.context.expression !== 'exitReporter') { - if (this.context.expression === 'doStopWarping') { - this.doStopWarping(); - } else { - this.popContext(); - } - } - return value; -}; +// Process stopping blocks primitives Process.prototype.doStopBlock = function () { var target = this.context.expression.exitTag; @@ -1119,13 +1108,21 @@ Process.prototype.evaluateCustomBlock = function () { // insert a reporter exit tag for the // CALL SCRIPT primitive variant if (this.context.expression.definition.type !== 'command') { - exit = new Context( - runnable.parentContext, - 'exitReporter', - outer, - outer.receiver - ); - runnable.parentContext = exit; + if (caller) { + // tag caller, so "report" can catch it later + caller.tag = 'exit'; + } else { + // top-level context, insert a tagged exit context + // which "report" can catch later + exit = new Context( + runnable.parentContext, + 'expectReport', + outer, + outer.receiver + ); + exit.tag = 'exit'; + runnable.parentContext = exit; + } } else { // tag all "stop this block" blocks with the current // procedureCount as exitTag, and mark all "report" blocks @@ -2925,7 +2922,7 @@ Context.prototype.continuation = function () { } else { return new Context(null, 'doStop'); } - if (cont.expression === 'exitReporter') { + if (cont.expression === 'expectReport') { return cont.continuation(); } cont = cont.copyForContinuation(); -- cgit v1.3.1 From 723c232f3d6655bb592b98333a2397d3cc2689c1 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 25 Nov 2014 17:51:04 +0100 Subject: Full TCO (tail-call-elimination) now Snap! really *is* Scheme :-) --- history.txt | 1 + threads.js | 102 ++++++++++++++++++++++++++++-------------------------------- 2 files changed, 49 insertions(+), 54 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 22215e9..7d876c8 100755 --- a/history.txt +++ b/history.txt @@ -2357,3 +2357,4 @@ ______ 1411225 ------- * Threads: Evaluator optimizations (reducing the stack size for reporters) +* Threads: Full TCO (tail-call-elimination), now Snap! *is* Scheme :-) diff --git a/threads.js b/threads.js index aef13a1..bdb780a 100644 --- a/threads.js +++ b/threads.js @@ -564,12 +564,14 @@ Process.prototype.doReport = function (block) { this.popContext(); } } - if (this.context.expression === 'expectReport') { - // pop off inserted top-level exit context - this.popContext(); - } else { - // un-tag and preserve original caller - this.context.tag = null; + if (this.context) { + if (this.context.expression === 'expectReport') { + // pop off inserted top-level exit context + this.popContext(); + } else { + // un-tag and preserve original caller + this.context.tag = null; + } } this.pushContext(block.inputs()[0], outer); }; @@ -831,7 +833,6 @@ Process.prototype.evaluate = function ( caller = this.context.parentContext, exit, runnable, - extra, parms = args.asArray(), i, value; @@ -845,17 +846,11 @@ Process.prototype.evaluate = function ( outer, context.receiver ); - extra = new Context(runnable, 'doYield'); - - // Note: if the context's expression is a ReporterBlockMorph, - // the extra context gets popped off immediately without taking - // effect (i.e. it doesn't yield within evaluating a stack of - // nested reporters) + this.context.parentContext = runnable; if (context.expression instanceof ReporterBlockMorph) { - this.context.parentContext = extra; - } else { - this.context.parentContext = runnable; + // auto-"warp" nested reporters + this.readyToYield = (Date.now() - this.lastYield > this.timeout); } // assign parameters if any were passed @@ -1062,7 +1057,6 @@ Process.prototype.evaluateCustomBlock = function () { parms = args.asArray(), runnable, exit, - extra, i, value, outer; @@ -1081,8 +1075,7 @@ Process.prototype.evaluateCustomBlock = function () { outer.receiver ); runnable.isCustomBlock = true; - extra = new Context(runnable, 'doYield'); - this.context.parentContext = extra; + this.context.parentContext = runnable; // passing parameters if any were passed if (parms.length > 0) { @@ -1104,40 +1097,43 @@ Process.prototype.evaluateCustomBlock = function () { } } - if (runnable.expression instanceof CommandBlockMorph) { - // insert a reporter exit tag for the - // CALL SCRIPT primitive variant - if (this.context.expression.definition.type !== 'command') { - if (caller) { - // tag caller, so "report" can catch it later - caller.tag = 'exit'; - } else { - // top-level context, insert a tagged exit context - // which "report" can catch later - exit = new Context( - runnable.parentContext, - 'expectReport', - outer, - outer.receiver - ); - exit.tag = 'exit'; - runnable.parentContext = exit; - } + // tag return target + if (this.context.expression.definition.type !== 'command') { + if (caller) { + // tag caller, so "report" can catch it later + caller.tag = 'exit'; } else { - // tag all "stop this block" blocks with the current - // procedureCount as exitTag, and mark all "report" blocks - // as being inside a custom command definition - runnable.expression.tagExitBlocks(this.procedureCount, true); - - // tag the caller with the current procedure count, so - // "stop this block" blocks can catch it, but only - // if the caller hasn't been tagged already - if (caller && !caller.tag) { - caller.tag = this.procedureCount; - } + // top-level context, insert a tagged exit context + // which "report" can catch later + exit = new Context( + runnable.parentContext, + 'expectReport', + outer, + outer.receiver + ); + exit.tag = 'exit'; + runnable.parentContext = exit; + } + // auto-"warp" nested reporters + this.readyToYield = (Date.now() - this.lastYield > this.timeout); + } else { + // tag all "stop this block" blocks with the current + // procedureCount as exitTag, and mark all "report" blocks + // as being inside a custom command definition + runnable.expression.tagExitBlocks(this.procedureCount, true); + + // tag the caller with the current procedure count, so + // "stop this block" blocks can catch it, but only + // if the caller hasn't been tagged already + if (caller && !caller.tag) { + caller.tag = this.procedureCount; + } + // yield commands unless explicitly "warped" + if (!this.isAtomic) { + this.readyToYield = true; } - runnable.expression = runnable.expression.blockSequence(); } + runnable.expression = runnable.expression.blockSequence(); }; // Process variables primitives @@ -2920,12 +2916,10 @@ Context.prototype.continuation = function () { } else if (this.parentContext) { cont = this.parentContext; } else { - return new Context(null, 'doStop'); - } - if (cont.expression === 'expectReport') { - return cont.continuation(); + return new Context(null, 'doYield'); } cont = cont.copyForContinuation(); + cont.tag = null; cont.isContinuation = true; return cont; }; -- cgit v1.3.1 From 320bfd0c990ea60e1dcf6bfdbe7538b203283709 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 26 Nov 2014 16:26:53 +0100 Subject: Fixed #656 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make sure to always evaluate the “report” block’s input, even if used inside a custom command definition, because hardware extensions (and other reporters with side-effects) rely on it. --- history.txt | 4 ++++ threads.js | 37 ++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 17 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 7d876c8..39a801d 100755 --- a/history.txt +++ b/history.txt @@ -2358,3 +2358,7 @@ ______ ------- * Threads: Evaluator optimizations (reducing the stack size for reporters) * Threads: Full TCO (tail-call-elimination), now Snap! *is* Scheme :-) + +1411225 +------- +* Threads: Fixed #656 diff --git a/threads.js b/threads.js index bdb780a..59dd23a 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-25'; +modules.threads = '2014-November-26'; var ThreadManager; var Process; @@ -551,28 +551,31 @@ Process.prototype.reportAnd = function (block) { }; Process.prototype.doReport = function (block) { + var outer = this.context.outerContext; if (this.context.expression.partOfCustomCommand) { this.doStopCustomBlock(); this.popContext(); - return; - } - var outer = this.context.outerContext; - while (this.context && this.context.tag !== 'exit') { - if (this.context.expression === 'doStopWarping') { - this.doStopWarping(); - } else { - this.popContext(); + } else { + while (this.context && this.context.tag !== 'exit') { + if (this.context.expression === 'doStopWarping') { + this.doStopWarping(); + } else { + this.popContext(); + } } - } - if (this.context) { - if (this.context.expression === 'expectReport') { - // pop off inserted top-level exit context - this.popContext(); - } else { - // un-tag and preserve original caller - this.context.tag = null; + if (this.context) { + if (this.context.expression === 'expectReport') { + // pop off inserted top-level exit context + this.popContext(); + } else { + // un-tag and preserve original caller + this.context.tag = null; + } } } + // in any case evaluate (and ignore) + // the input, because it could be + // and HTTP Request for a hardware extension this.pushContext(block.inputs()[0], outer); }; -- cgit v1.3.1 From 17b6ae839b5c15c942440f38fc07bfcc45bc0811 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 3 Dec 2014 12:48:31 +0100 Subject: Improve edge-collision detection of default sprite “arrow” shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- history.txt | 5 +++++ objects.js | 9 ++++++--- threads.js | 14 ++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index 986bcc4..b8af0f0 100755 --- a/history.txt +++ b/history.txt @@ -2372,3 +2372,8 @@ ______ 141202 ------ * New Kannada translation. Yay!! Thanks, Vinayakumar R!! + +141203 +------ +* Morphic: Cache actual bounding box of the Pen arrow shape +* Threads, Objects: Improve edge-collision detection of default sprite “arrow” shape diff --git a/objects.js b/objects.js index f0fe942..4ad376c 100644 --- a/objects.js +++ b/objects.js @@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2014-December-01'; +modules.objects = '2014-December-03'; var SpriteMorph; var StageMorph; @@ -3238,8 +3238,11 @@ SpriteMorph.prototype.setCenter = function (aPoint, justMe) { SpriteMorph.prototype.nestingBounds = function () { // same as fullBounds(), except that it uses "parts" instead of children - var result; - result = this.bounds; + // and special cases the costume-less "arrow" shape's bounding box + var result = this.bounds; + if (!this.costume && this.penBounds) { + result = this.penBounds.translateBy(this.position()); + } this.parts.forEach(function (part) { if (part.isVisible) { result = result.merge(part.nestingBounds()); diff --git a/threads.js b/threads.js index 59dd23a..9f9a221 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-26'; +modules.threads = '2014-December-03'; var ThreadManager; var Process; @@ -2368,6 +2368,7 @@ Process.prototype.objectTouchingObject = function (thisObj, name) { var myself = this, those, stage, + box, mouse; if (this.inputOption(name) === 'mouse-pointer') { @@ -2379,9 +2380,14 @@ Process.prototype.objectTouchingObject = function (thisObj, name) { } else { stage = thisObj.parentThatIsA(StageMorph); if (stage) { - if (this.inputOption(name) === 'edge' && - !stage.bounds.containsRectangle(thisObj.bounds)) { - return true; + if (this.inputOption(name) === 'edge') { + box = thisObj.bounds; + if (!thisObj.costume && thisObj.penBounds) { + box = thisObj.penBounds.translateBy(thisObj.position()); + } + if (!stage.bounds.containsRectangle(box)) { + return true; + } } if (this.inputOption(name) === 'pen trails' && thisObj.isTouching(stage.penTrailsMorph())) { -- cgit v1.3.1 From ad1fe34d1ed900e1e4fd75e5c5666f6ab28b9c80 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 4 Dec 2014 15:45:18 +0100 Subject: Experimental “ForEach” primitive (hidden in dev mode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objects.js | 13 ++++++++++++- threads.js | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'threads.js') diff --git a/objects.js b/objects.js index 4ad376c..7277719 100644 --- a/objects.js +++ b/objects.js @@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2014-December-03'; +modules.objects = '2014-December-04'; var SpriteMorph; var StageMorph; @@ -1151,6 +1151,13 @@ SpriteMorph.prototype.initBlocks = function () { category: 'lists', spec: 'map %repRing over %l' }, + doForEach: { + dev: true, + type: 'command', + category: 'lists', + spec: 'for %upvar in %l %cs', + defaults: [localize('each item')] + }, // Code mapping - experimental doMapCodeOrHeader: { // experimental @@ -2063,6 +2070,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(txt); blocks.push('-'); blocks.push(block('reportMap')); + blocks.push('-'); + blocks.push(block('doForEach')); } ///////////////////////////////// @@ -5198,6 +5207,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(txt); blocks.push('-'); blocks.push(block('reportMap')); + blocks.push('-'); + blocks.push(block('doForEach')); } ///////////////////////////////// diff --git a/threads.js b/threads.js index 9f9a221..c21375e 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-December-03'; +modules.threads = '2014-December-04'; var ThreadManager; var Process; @@ -1679,6 +1679,26 @@ Process.prototype.reportMap = function (reporter, list) { } }; +Process.prototype.doForEach = function (upvar, list, script) { + // perform a script for each element of a list, assigning the + // current iteration's element to a variable with the name + // specified in the "upvar" parameter, so it can be referenced + // within the script. Uses the context's - unused - fourth + // element as temporary storage for the current list index + + if (isNil(this.context.inputs[3])) {this.context.inputs[3] = 1; } + var index = this.context.inputs[3]; + this.context.outerContext.variables.addVar(upvar); + this.context.outerContext.variables.setVar( + upvar, + list.at(index) + ); + if (index > list.length()) {return; } + this.context.inputs[3] += 1; + this.pushContext(); + this.evaluate(script, new List(), true); +}; + // Process interpolated primitives Process.prototype.doWait = function (secs) { -- cgit v1.3.1 From 0a239b703c8c7fde5540ce63c4236522a22ee049 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 11 Dec 2014 14:17:29 +0100 Subject: yield after each cycle in the experimental “forEach” primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thanks, Bernat, for reporting this bug! --- history.txt | 4 ++++ threads.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'threads.js') diff --git a/history.txt b/history.txt index ff38bfb..edbf4d0 100755 --- a/history.txt +++ b/history.txt @@ -2390,3 +2390,7 @@ ______ 141206 ------ * Store: Fixed #668 + +141211 +------ +* Threads: yield after each cycle in the experimental “forEach” primitive diff --git a/threads.js b/threads.js index c21375e..b80ee9e 100644 --- a/threads.js +++ b/threads.js @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-December-04'; +modules.threads = '2014-December-11'; var ThreadManager; var Process; @@ -1695,6 +1695,7 @@ Process.prototype.doForEach = function (upvar, list, script) { ); if (index > list.length()) {return; } this.context.inputs[3] += 1; + this.pushContext('doYield'); this.pushContext(); this.evaluate(script, new List(), true); }; -- cgit v1.3.1