diff options
| author | Michael Ball <cycomachead@gmail.com> | 2014-11-24 15:13:51 -0800 |
|---|---|---|
| committer | Michael Ball <cycomachead@gmail.com> | 2014-11-24 15:13:51 -0800 |
| commit | 94f94467b5be3d05f108620d1a17f6d4179bf621 (patch) | |
| tree | 0d62b96277a2c919d28632850cb5d8938ec69092 | |
| parent | 9e5fb9a4cba980c977d4e11e5c9a4e7f9c8596db (diff) | |
| parent | 4be96bb240ea8518fa95218cba7111846af93baa (diff) | |
| download | snap-94f94467b5be3d05f108620d1a17f6d4179bf621.tar.gz snap-94f94467b5be3d05f108620d1a17f6d4179bf621.zip | |
Merge branch 'master' of git://github.com/jmoenig/Snap--Build-Your-Own-Blocks into shared-url
* 'master' of git://github.com/jmoenig/Snap--Build-Your-Own-Blocks: (34 commits)
tail-call-elimination for reporters - experiment
allow recursive reporters to be stopped by user
updated history
fixed #131
Fixed #34
Fixed #644
Fixed #372
Fixed #416
Fixed #318
Fix “stop this block” primitive for tail-call-elimination
Fix "stop this block"’s lexical awareness
Add a new Favicon to Snap! (Clearer Lambda)
integrate translation update
push morphic.js version date
fix ‘line’ option in ‘split’ block for Windows files
integrate percent sign fix for JSLint
Updated the “About” Dialog
renamed Process::callback to "onComplete"
Fixed #364 avoid “freezing” when calling LAUNCH on empty ring
Fixed #642, avoid “freezing” when calling CONS on non-list/null
...
| -rw-r--r-- | blocks.js | 43 | ||||
| -rw-r--r-- | byob.js | 5 | ||||
| -rw-r--r-- | favicon.ico | bin | 0 -> 32988 bytes | |||
| -rw-r--r-- | gui.js | 9 | ||||
| -rwxr-xr-x | history.txt | 44 | ||||
| -rwxr-xr-x | lang-pt.js | 23 | ||||
| -rw-r--r-- | lists.js | 5 | ||||
| -rw-r--r-- | morphic.js | 37 | ||||
| -rw-r--r-- | objects.js | 56 | ||||
| -rwxr-xr-x | snap.html | 4 | ||||
| -rw-r--r-- | store.js | 7 | ||||
| -rw-r--r-- | threads.js | 352 | ||||
| -rw-r--r-- | tools.xml | 2 |
13 files changed, 374 insertions, 213 deletions
@@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2014-October-01'; +modules.blocks = '2014-November-21'; var SyntaxElementMorph; @@ -394,10 +394,10 @@ 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. + // Used by the evaluator when binding implicit formal parameters + // to empty input slots var empty = []; if (!(this instanceof RingMorph) && (this.selector !== 'reportJSFunction')) { @@ -412,6 +412,26 @@ SyntaxElementMorph.prototype.allEmptySlots = function () { return empty; }; +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); + } + }); + } + } +}; + SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) { var scripts = this.parentThatIsA(ScriptsMorph), replacement = newArg, @@ -3169,9 +3189,14 @@ 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" indicators: + + partOfCustomCommand - temporary bool set by the evaluator + exitTag - temporary string or number set by the evaluator */ // CommandBlockMorph inherits from BlockMorph: @@ -3189,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: @@ -106,7 +106,7 @@ SymbolMorph, isNil*/ // Global stuff //////////////////////////////////////////////////////// -modules.byob = '2014-September-30'; +modules.byob = '2014-November-20'; // Declarations @@ -688,7 +688,8 @@ CustomCommandBlockMorph.prototype.labelPart = function (spec) { return CustomCommandBlockMorph.uber.labelPart.call(this, spec); } if ((spec[0] === '%') && (spec.length > 1)) { - part = new BlockInputFragmentMorph(spec.slice(1)); + // part = new BlockInputFragmentMorph(spec.slice(1)); + part = new BlockInputFragmentMorph(spec.replace(/%/g, '')); } else { part = new BlockLabelFragmentMorph(spec); part.fontSize = this.fontSize; diff --git a/favicon.ico b/favicon.ico Binary files differnew file mode 100644 index 0000000..46a1369 --- /dev/null +++ b/favicon.ico @@ -69,7 +69,7 @@ SpeechBubbleMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2014-October-06'; +modules.gui = '2014-November-20'; // Declarations @@ -190,7 +190,7 @@ IDE_Morph.prototype.init = function (isAutoFill) { // additional properties: this.cloudMsg = null; - this.source = 'local'; + this.source = SnapCloud.username ? 'cloud' : 'local'; this.serializer = new SnapSerializer(); this.globalVariables = new VariableFrame(); @@ -2511,8 +2511,9 @@ IDE_Morph.prototype.aboutSnap = function () { + 'jens@moenig.org, bh@cs.berkeley.edu\n\n' + 'Snap! is developed by the University of California, Berkeley\n' - + ' with support from the National Science Foundation ' - + 'and MioSoft. \n' + + ' with support from the National Science Foundation, ' + + 'MioSoft, \n' + + 'and the Communications Design Group at SAP Labs. \n' + 'The design of Snap! is influenced and inspired by Scratch,\n' + 'from the Lifelong Kindergarten group at the MIT Media Lab\n\n' diff --git a/history.txt b/history.txt index 28116b9..964686e 100755 --- a/history.txt +++ b/history.txt @@ -2309,3 +2309,47 @@ ______ 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 + +141117 +------ +* Threads, Blocks: Treat REPORT blocks inside custom command definitions as STOP THIS BLOCK / IGNORE INPUTS + +141120 +------ +* 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! +* 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! +* Morphic: fix slider range 1, thanks, @tonychenr ! +* translation update, thanks, Manuel! + +141121 +------ +* Threads, Blocks: Fix STOP THIS BLOCK’s lexical awareness + +1411213 +------- +* Threads: Fix “stop this block” primitive for tail-call-elimination + +1411214 +------- +* Threads: Fixed #318 +* Objects: Fixed #416 +* Objects: Fixed #372 +* Threads: Fixed #644 +* Store: Fixed #34 +* 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) @@ -650,11 +650,12 @@ SnapTranslator.dict.pt = { 'Save As...': 'Guardar este projecto como…', 'Import...': - 'Importar para este projecto…', + 'Importar…', 'file menu import hint': - 'Importar para este projecto\num projecto exportado,\n' - + 'uma biblioteca de blocos,\n' - + 'um traje ou um som.', + 'Abrir um projecto exportado,\n' + + 'substitundo o projecto corrente, ou\n' + + 'importar uma biblioteca de blocos, um\n' + + 'traje ou um som para o projecto corrente.', 'Export project as plain text...': 'Exportar este projecto como texto simples…', 'Export project...': @@ -1616,7 +1617,7 @@ SnapTranslator.dict.pt = { 'unshared.': 'deixado de partilhar.', 'Unshare': - 'Deixar de Partilhar', + 'Não Partilhar', 'password has been changed.': 'a sua palavra-passe foi alterada.', 'SVG costumes are\nnot yet fully supported\nin every browser': @@ -1641,6 +1642,18 @@ SnapTranslator.dict.pt = { 'Seleccionar um traje da biblioteca de média.', 'edit rotation point only...': 'editar apenas ponto de rotação…', + 'Export Project As...': + 'Exportar Projecto Como…', + 'a variable of name \'': + 'não existe uma variável «', + '\'\ndoes not exist in this context': + '»\nneste contexto', + '(temporary)': + '(temporária)', + 'expecting': + 'esperavam-se', + 'input(s), but getting': + 'argumento(s), mas foram passados', // produção de código 'map %cmdRing to %codeKind %code': @@ -61,7 +61,7 @@ PushButtonMorph, SyntaxElementMorph, Color, Point, WatcherMorph, StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph, MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/ -modules.lists = '2014-July-28'; +modules.lists = '2014-November-20'; var List; var ListWatcherMorph; @@ -125,6 +125,9 @@ List.prototype.changed = function () { List.prototype.cons = function (car, cdr) { var answer = new List(); + if (!(cdr instanceof List || isNil(cdr))) { + throw new Error("cdr isn't a list: " + cdr); + } answer.first = isNil(car) ? null : car; answer.rest = cdr || null; answer.isLinked = true; @@ -464,9 +464,15 @@ MyMorph.prototype.mouseMove = function(pos) {}; - The only optional parameter of such a method is a Point object + All of these methods have as optional parameter a Point object indicating the current position of the Hand inside the World's - coordinate system. + coordinate system. The + + mouseMove(pos, button) + + event method has an additional optional parameter indicating the + currently pressed mouse button, which is either 'left' or 'right'. + You can use this to let users interact with 3D environments. Events may be "bubbled" up a morph's owner chain by calling @@ -1035,7 +1041,7 @@ /*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio, FileList, getBlurredShadowSupport*/ -var morphicVersion = '2014-September-30'; +var morphicVersion = '2014-November-20'; var modules = {}; // keep track of additional loaded modules var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug @@ -5754,7 +5760,7 @@ SliderMorph.prototype.rangeSize = function () { }; SliderMorph.prototype.ratio = function () { - return this.size / this.rangeSize(); + return this.size / (this.rangeSize() + 1); }; SliderMorph.prototype.unitSize = function () { @@ -9346,11 +9352,11 @@ HandMorph.prototype.init = function (aWorld) { this.world = aWorld; this.mouseButton = null; this.mouseOverList = []; - this.mouseDownMorph = null; this.morphToGrab = null; this.grabOrigin = null; this.temporaries = []; this.touchHoldTimeout = null; + this.contextMenuEnabled = false; }; HandMorph.prototype.changed = function () { @@ -9494,9 +9500,10 @@ HandMorph.prototype.drop = function () { */ HandMorph.prototype.processMouseDown = function (event) { - var morph, expectedClick, actualClick; + var morph, actualClick; this.destroyTemporaries(); + this.contextMenuEnabled = true; this.morphToGrab = null; if (this.children.length !== 0) { this.drop(); @@ -9529,15 +9536,9 @@ HandMorph.prototype.processMouseDown = function (event) { if (event.button === 2 || event.ctrlKey) { this.mouseButton = 'right'; actualClick = 'mouseDownRight'; - expectedClick = 'mouseClickRight'; } else { this.mouseButton = 'left'; actualClick = 'mouseDownLeft'; - expectedClick = 'mouseClickLeft'; - } - this.mouseDownMorph = morph; - while (!this.mouseDownMorph[expectedClick]) { - this.mouseDownMorph = this.mouseDownMorph.parent; } while (!morph[actualClick]) { morph = morph.parent; @@ -9596,7 +9597,7 @@ HandMorph.prototype.processMouseUp = function () { expectedClick = 'mouseClickLeft'; } else { expectedClick = 'mouseClickRight'; - if (this.mouseButton) { + if (this.mouseButton && this.contextMenuEnabled) { context = morph; contextMenu = context.contextMenu(); while ((!contextMenu) && @@ -9654,16 +9655,18 @@ HandMorph.prototype.processMouseMove = function (event) { // mouseOverNew = this.allMorphsAtPointer(); mouseOverNew = this.morphAtPointer().allParents(); - if ((this.children.length === 0) && - (this.mouseButton === 'left')) { + if (!this.children.length && this.mouseButton) { topMorph = this.morphAtPointer(); morph = topMorph.rootForGrab(); if (topMorph.mouseMove) { - topMorph.mouseMove(pos); + topMorph.mouseMove(pos, this.mouseButton); + if (this.mouseButton === 'right') { + this.contextMenuEnabled = false; + } } // if a morph is marked for grabbing, just grab it - if (this.morphToGrab) { + if (this.mouseButton === 'left' && this.morphToGrab) { if (this.morphToGrab.isDraggable) { morph = this.morphToGrab; this.grab(morph); @@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2014-October-08'; +modules.objects = '2014-November-24'; var SpriteMorph; var StageMorph; @@ -1674,6 +1674,20 @@ SpriteMorph.prototype.blockTemplates = function (category) { return menu; } + function addVar(pair) { + if (pair) { + if (myself.variables.silentFind(pair[0])) { + myself.inform('that name is already in use'); + } else { + myself.addVariable(pair[0], pair[1]); + myself.toggleVariableWatcher(pair[0], pair[1]); + myself.blocksCache[cat] = null; + myself.paletteCache[cat] = null; + myself.parentThatIsA(IDE_Morph).refreshPalette(); + } + } + } + if (cat === 'motion') { blocks.push(block('forward')); @@ -1967,15 +1981,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { function () { new VariableDialogMorph( null, - function (pair) { - if (pair && !myself.variables.silentFind(pair[0])) { - myself.addVariable(pair[0], pair[1]); - myself.toggleVariableWatcher(pair[0], pair[1]); - myself.blocksCache[cat] = null; - myself.paletteCache[cat] = null; - myself.parentThatIsA(IDE_Morph).refreshPalette(); - } - }, + addVar, myself ).prompt( 'Variable name', @@ -2164,8 +2170,8 @@ SpriteMorph.prototype.freshPalette = function (category) { var defs = SpriteMorph.prototype.blocks, hiddens = StageMorph.prototype.hiddenPrimitives; return Object.keys(hiddens).some(function (any) { - return defs[any].category === category || - contains((more[category] || []), any); + return !isNil(defs[any]) && (defs[any].category === category + || contains((more[category] || []), any)); }); } @@ -2204,7 +2210,7 @@ SpriteMorph.prototype.freshPalette = function (category) { var hiddens = StageMorph.prototype.hiddenPrimitives, defs = SpriteMorph.prototype.blocks; Object.keys(hiddens).forEach(function (sel) { - if (defs[sel].category === category) { + if (defs[sel] && (defs[sel].category === category)) { delete StageMorph.prototype.hiddenPrimitives[sel]; } }); @@ -4858,6 +4864,20 @@ StageMorph.prototype.blockTemplates = function (category) { ); } + function addVar(pair) { + if (pair) { + if (myself.variables.silentFind(pair[0])) { + myself.inform('that name is already in use'); + } else { + myself.addVariable(pair[0], pair[1]); + myself.toggleVariableWatcher(pair[0], pair[1]); + myself.blocksCache[cat] = null; + myself.paletteCache[cat] = null; + myself.parentThatIsA(IDE_Morph).refreshPalette(); + } + } + } + if (cat === 'motion') { txt = new TextMorph(localize( @@ -5099,15 +5119,7 @@ StageMorph.prototype.blockTemplates = function (category) { function () { new VariableDialogMorph( null, - function (pair) { - if (pair && !myself.variables.silentFind(pair[0])) { - myself.addVariable(pair[0], pair[1]); - myself.toggleVariableWatcher(pair[0], pair[1]); - myself.blocksCache[cat] = null; - myself.paletteCache[cat] = null; - myself.parentThatIsA(IDE_Morph).refreshPalette(); - } - }, + addVar, myself ).prompt( 'Variable name', @@ -3,7 +3,7 @@ <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Snap! Build Your Own Blocks. Beta</title> - <link rel="shortcut icon" href="http://snap.berkeley.edu/fav3.gif" type="image/gif"> + <link rel="shortcut icon" href="favicon.ico"> <script type="text/javascript" src="morphic.js"></script> <script type="text/javascript" src="widgets.js"></script> <script type="text/javascript" src="blocks.js"></script> @@ -33,5 +33,5 @@ </head> <body style="margin: 0;"> <canvas id="world" tabindex="1" style="position: absolute;" /> - </body> + </body> </html> @@ -61,7 +61,7 @@ SyntaxElementMorph, Variable*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2014-October-01'; +modules.store = '2014-November-24'; // XML_Serializer /////////////////////////////////////////////////////// @@ -261,7 +261,9 @@ SnapSerializer.prototype.watcherLabels = { yPosition: 'y position', direction: 'direction', getScale: 'size', + getTempo: 'tempo', getLastAnswer: 'answer', + getLastMessage: 'message', getTimer: 'timer', getCostumeIdx: 'costume #', reportMouseX: 'mouse x', @@ -1824,9 +1826,8 @@ Context.prototype.toXML = function (serializer) { return ''; } return serializer.format( - '<context% ~><inputs>%</inputs><variables>%</variables>' + + '<context ~><inputs>%</inputs><variables>%</variables>' + '%<receiver>%</receiver>%</context>', - this.isLambda ? ' lambda="lambda"' : '', this.inputs.reduce( function (xml, input) { return xml + serializer.format('<input>$</input>', input); @@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-October-01'; +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(); } @@ -136,7 +146,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 +160,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; @@ -225,8 +236,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) { @@ -235,18 +247,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.onComplete instanceof Function) { + proc.onComplete(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 { @@ -295,9 +311,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 @@ -305,15 +321,20 @@ 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 + 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 = {}; @@ -321,7 +342,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, onComplete) { this.topBlock = topBlock || null; this.readyToYield = false; @@ -338,6 +359,8 @@ function Process(topBlock) { this.pauseOffset = null; this.frameCount = 0; this.exportResult = false; + this.onComplete = onComplete || null; + this.procedureCount = 0; if (topBlock) { this.homeContext.receiver = topBlock.receiver(); @@ -435,7 +458,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); @@ -526,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) { @@ -586,7 +636,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 +646,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 +654,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 +671,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 +724,11 @@ 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 Exception Handling Process.prototype.handleError = function (error, element) { @@ -757,8 +807,8 @@ 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 ( @@ -783,6 +833,7 @@ Process.prototype.evaluate = function ( var outer = new Context(null, null, context.outerContext), runnable, extra, + exit, parms = args.asArray(), i, value; @@ -798,23 +849,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) { @@ -849,8 +894,9 @@ 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 ); } } @@ -858,6 +904,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; + } } }; @@ -867,6 +925,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, @@ -879,8 +940,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) { @@ -915,8 +974,9 @@ 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 ); } } @@ -933,68 +993,62 @@ Process.prototype.fork = function (context, args) { stage.threads.processes.push(proc); }; -Process.prototype.doReport = function (value, isCSlot) { - while (this.context && !this.context.isLambda) { +// 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(); } } - if (this.context && this.context.isImplicitLambda) { + return 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(); } - 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.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(); + } else { 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) { @@ -1012,19 +1066,22 @@ 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(), runnable, + exit, extra, i, value, outer; if (!context) {return null; } + this.procedureCount += 1; 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 +1089,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) { @@ -1063,6 +1116,29 @@ 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') { + exit = new Context( + runnable.parentContext, + 'exitReporter', + outer, + outer.receiver + ); + runnable.parentContext = 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; + } + } runnable.expression = runnable.expression.blockSequence(); } }; @@ -1148,7 +1224,7 @@ Process.prototype.doShowVar = function (varName) { if (isGlobal || target.owner) { label = name; } else { - label = name + ' (temporary)'; + label = name + ' ' + localize('(temporary)'); } watcher = new WatcherMorph( label, @@ -1259,7 +1335,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; @@ -1308,16 +1384,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 +1399,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 +1414,6 @@ Process.prototype.doIfElse = function () { } } if (this.context) { - this.context.isLambda = isLambda; - this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; } @@ -1420,8 +1488,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 +1504,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 +1584,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(); }; @@ -2134,15 +2186,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'; + // 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 +2205,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; @@ -2760,25 +2816,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 - isLambda marker for return ops - isImplicitLambda marker for return ops 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( @@ -2801,22 +2857,19 @@ 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 + this.tag = null; // lexical catch-tag for custom blocks } 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 +2925,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; @@ -3003,9 +3059,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 +3126,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') ); }; @@ -1 +1 @@ -<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="map %'function' over %'lists'" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%mult%l"></input></inputs><script><block s="doWarp"><script><block s="doDeclareVariables"><list><l>mapone</l><l>mapmany</l></list></block><block s="doSetVar"><l>mapone</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="evaluate"><block var="mapone"/><list><block s="reportCDR"><block var="data"/></block></list></block></block></block></script><list><l>data</l></list></block></block><block s="doSetVar"><l>mapmany</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportListItem"><l>1</l><block var="data lists"/></block></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportListItem"><l>1</l><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></block><block s="evaluate"><block var="mapmany"/><list><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportCDR"><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></list></block></block></block></script><list><l>data lists</l></list></block></block><block s="doIfElse"><custom-block s="empty? %l"><block s="reportCDR"><block var="lists"/></block></custom-block><script><block s="doReport"><block s="evaluate"><block var="mapone"/><list><block s="reportListItem"><l>1</l><block var="lists"/></block></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="mapmany"/><list><block var="lists"/></list></block></block></script></block></script></block></script></block-definition><block-definition s="empty? %'data'" type="predicate" category="lists"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doReport"><block s="reportEquals"><block var="data"/><block s="reportNewList"><list></list></block></block></block></script></block-definition><block-definition s="keep items such that %'pred' from %'data'" type="reporter" category="lists"><header></header><code></code><inputs><input type="%predRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doIfElse"><block s="evaluate"><block var="pred"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><script><block s="doReport"><block s="reportCONS"><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></block></script><script><block s="doReport"><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></script></block></script></block></script></block-definition><block-definition s="combine with %'function' items of %'data'" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="data"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="data"/></block></block></script></block><block s="doReport"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="combine with %repRing items of %l"><block var="function"/><block s="reportCDR"><block var="data"/></block></custom-block></list></block></block></script></block></script></block-definition><block-definition s="if %'test' then %'true' else %'false'" type="reporter" category="control"><header></header><code></code><inputs><input type="%b"></input><input type="%anyUE"></input><input type="%anyUE"></input></inputs><script><block s="doIfElse"><block var="test"/><script><block s="doReport"><block s="evaluate"><block var="true"/><list></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="false"/><list></list></block></block></script></block></script></block-definition><block-definition s="for %'i' = %'start' to %'end' %'action'" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%n">1</input><input type="%n">10</input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>step</l><l>tester</l></list></block><block s="doIfElse"><block s="reportGreaterThan"><block var="start"/><block var="end"/></block><script><block s="doSetVar"><l>step</l><l>-1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportLessThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script><script><block s="doSetVar"><l>step</l><l>1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportGreaterThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script></block><block s="doSetVar"><l>i</l><block var="start"/></block><block s="doUntil"><block s="evaluate"><block var="tester"/><list></list></block><script><block s="doRun"><block var="action"/><list></list></block><block s="doChangeVar"><l>i</l><block var="step"/></block></script></block></script></block-definition><block-definition s="join words %'words'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%mult%txt"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="words"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="words"/></block></block></script></block><block s="doReport"><block s="reportJoinWords"><list><block s="reportListItem"><l>1</l><block var="words"/></block><block s="reportJoinWords"><list><l> </l><block s="evaluate"><block s="reifyReporter"><autolambda><custom-block s="join words %mult%txt"><block s="reportCDR"><block var="words"/></block></custom-block></autolambda><list></list></block><list></list></block></list></block></list></block></block></script></block></script></block-definition><block-definition s="list $arrowRight sentence %'data'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doReport"><custom-block s="combine with %repRing items of %l"><block s="reifyReporter"><autolambda><custom-block s="join words %mult%txt"><list><l></l><l></l></list></custom-block></autolambda><list></list></block><block var="data"/></custom-block></block></script></block></script></block-definition><block-definition s="sentence $arrowRight list %'text'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="text"/><l><option>whitespace</option></l></block></block></script></block></script></block-definition><block-definition s="catch %'tag' %'action'" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%cs"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doRun"><block var="action"/><list></list></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %'cont'" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input></inputs><script><block s="doRun"><block var="cont"/><list></list></block></script></block-definition><block-definition s="catch %'tag' %'value'" type="reporter" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%anyUE"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doReport"><block s="evaluate"><block var="value"/><list></list></block></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %'tag' %'value'" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input><input type="%s"></input></inputs><script><block s="doRun"><block var="tag"/><list><block var="value"/></list></block></script></block-definition><block-definition s="for each %'item' of %'data' %'action'" type="command" category="lists"><header></header><code></code><inputs><input type="%upvar"></input><input type="%l"></input><input type="%cs"></input></inputs><script><block s="doUntil"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doSetVar"><l>item</l><block s="reportListItem"><l>1</l><block var="data"/></block></block><block s="doRun"><block var="action"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="doSetVar"><l>data</l><block s="reportCDR"><block var="data"/></block></block></script></block></script></block-definition><block-definition s="if %'test' do %'action' and pause all $pause-1-255-220-0" type="command" category="control"><header></header><code></code><inputs><input type="%boolUE"></input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>breakpoint</l></list></block><block s="doIf"><block s="evaluate"><block var="test"/><list></list></block><script><block s="doSetVar"><l>breakpoint</l><block var="test"/></block><block s="doShowVar"><l>breakpoint</l></block><block s="doRun"><block var="action"/><list></list></block><block s="doPauseAll"></block><block s="doHideVar"><l></l></block></script></block></script></block-definition><block-definition s="word $arrowRight list %'word'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="word"/><l><option>letter</option></l></block></block></script></block></script></block-definition></blocks>
\ No newline at end of file +<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="map %'function' over %'lists'" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%mult%l"></input></inputs><script><block s="doWarp"><script><block s="doDeclareVariables"><list><l>mapone</l><l>mapmany</l></list></block><block s="doSetVar"><l>mapone</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="evaluate"><block var="mapone"/><list><block s="reportCDR"><block var="data"/></block></list></block></block></block></script><list><l>data</l></list></block></block><block s="doSetVar"><l>mapmany</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportListItem"><l>1</l><block var="data lists"/></block></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportListItem"><l>1</l><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></block><block s="evaluate"><block var="mapmany"/><list><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportCDR"><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></list></block></block></block></script><list><l>data lists</l></list></block></block><block s="doIfElse"><custom-block s="empty? %l"><block s="reportCDR"><block var="lists"/></block></custom-block><script><block s="doReport"><block s="evaluate"><block var="mapone"/><list><block s="reportListItem"><l>1</l><block var="lists"/></block></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="mapmany"/><list><block var="lists"/></list></block></block></script></block></script></block></script></block-definition><block-definition s="empty? %'data'" type="predicate" category="lists"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doReport"><block s="reportEquals"><block var="data"/><block s="reportNewList"><list></list></block></block></block></script></block-definition><block-definition s="keep items such that %'pred' from %'data'" type="reporter" category="lists"><header></header><code></code><inputs><input type="%predRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doIfElse"><block s="evaluate"><block var="pred"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><script><block s="doReport"><block s="reportCONS"><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></block></script><script><block s="doReport"><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></script></block></script></block></script></block-definition><block-definition s="combine with %'function' items of %'data'" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="data"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="data"/></block></block></script></block><block s="doReport"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="combine with %repRing items of %l"><block var="function"/><block s="reportCDR"><block var="data"/></block></custom-block></list></block></block></script></block></script></block-definition><block-definition s="if %'test' then %'true' else %'false'" type="reporter" category="control"><header></header><code></code><inputs><input type="%b"></input><input type="%anyUE"></input><input type="%anyUE"></input></inputs><script><block s="doIfElse"><block var="test"/><script><block s="doReport"><block s="evaluate"><block var="true"/><list></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="false"/><list></list></block></block></script></block></script></block-definition><block-definition s="for %'i' = %'start' to %'end' %'action'" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%n">1</input><input type="%n">10</input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>step</l><l>tester</l></list></block><block s="doIfElse"><block s="reportGreaterThan"><block var="start"/><block var="end"/></block><script><block s="doSetVar"><l>step</l><l>-1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportLessThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script><script><block s="doSetVar"><l>step</l><l>1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportGreaterThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script></block><block s="doSetVar"><l>i</l><block var="start"/></block><block s="doUntil"><block s="evaluate"><block var="tester"/><list></list></block><script><block s="doRun"><block var="action"/><list></list></block><block s="doChangeVar"><l>i</l><block var="step"/></block></script></block></script></block-definition><block-definition s="join words %'words'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%mult%txt"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="words"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="words"/></block></block></script></block><block s="doReport"><block s="reportJoinWords"><list><block s="reportListItem"><l>1</l><block var="words"/></block><block s="reportJoinWords"><list><l> </l><block s="evaluate"><block s="reifyReporter"><autolambda><custom-block s="join words %mult%txt"><block s="reportCDR"><block var="words"/></block></custom-block></autolambda><list></list></block><list></list></block></list></block></list></block></block></script></block></script></block-definition><block-definition s="list $arrowRight sentence %'data'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doReport"><custom-block s="combine with %repRing items of %l"><block s="reifyReporter"><autolambda><custom-block s="join words %mult%txt"><list><l></l><l></l></list></custom-block></autolambda><list></list></block><block var="data"/></custom-block></block></script></block></script></block-definition><block-definition s="sentence $arrowRight list %'text'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="text"/><l><option>whitespace</option></l></block></block></script></block></script></block-definition><block-definition s="catch %'tag' %'action'" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%cs"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doRun"><block var="action"/><list></list></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %'cont'" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input></inputs><script><block s="doRun"><block var="cont"/><list></list></block></script></block-definition><block-definition s="catch %'tag' %'value'" type="reporter" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%anyUE"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doReport"><block s="evaluate"><block var="value"/><list></list></block></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %'tag' %'value'" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input><input type="%s"></input></inputs><script><block s="doRun"><block var="tag"/><list><block var="value"/></list></block></script></block-definition><block-definition s="for each %'item' of %'data' %'action'" type="command" category="lists"><header></header><code></code><inputs><input type="%upvar"></input><input type="%l"></input><input type="%cs"></input></inputs><script><block s="doUntil"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doSetVar"><l>item</l><block s="reportListItem"><l>1</l><block var="data"/></block></block><block s="doRun"><block var="action"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="doSetVar"><l>data</l><block s="reportCDR"><block var="data"/></block></block></script></block></script></block-definition><block-definition s="if %'test' do %'action' and pause all $pause-1-255-220-0" type="command" category="control"><header></header><code></code><inputs><input type="%boolUE"></input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>breakpoint</l></list></block><block s="doIf"><block s="evaluate"><block var="test"/><list></list></block><script><block s="doSetVar"><l>breakpoint</l><block var="test"/></block><block s="doShowVar"><l>breakpoint</l></block><block s="doRun"><block var="action"/><list></list></block><block s="doPauseAll"></block><block s="doHideVar"><l></l></block></script></block></script></block-definition><block-definition s="word $arrowRight list %'word'" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="word"/><l><option>letter</option></l></block></block></script></block></script></block-definition><block-definition s="ignore %'x'" type="command" category="control"><header></header><code></code><inputs><input type="%s"></input></inputs></block-definition></blocks>
\ No newline at end of file |
