diff options
Diffstat (limited to 'threads.js')
| -rw-r--r-- | threads.js | 90 |
1 files changed, 66 insertions, 24 deletions
@@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-November-20'; +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(); } @@ -322,6 +332,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 +360,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(); @@ -684,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 @@ -765,12 +779,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 +965,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 +982,24 @@ Process.prototype.doReport = function (value) { }; Process.prototype.doStopBlock = function () { + var target = this.context.expression.exitTag; + if (isNil(target)) { + return this.doStopCustomBlock(); + } + 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 () { + // 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(); @@ -1010,7 +1038,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(), @@ -1022,6 +1051,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 +1098,19 @@ 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; - }); + 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 + // 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; + } } runnable.expression = runnable.expression.blockSequence(); } @@ -1269,7 +1308,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; @@ -2750,23 +2789,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( @@ -2791,6 +2832,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 () { |
