Merge remote-tracking branch 'upstream/master'

pull/3/merge
Erik Olsson 2014-12-14 18:48:03 +01:00
commit 57bef2b736
15 zmienionych plików z 1799 dodań i 261 usunięć

Wyświetl plik

@ -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:

Wyświetl plik

@ -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;

BIN
favicon.ico 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 7.9 KiB

18
gui.js
Wyświetl plik

@ -69,7 +69,7 @@ SpeechBubbleMorph*/
// Global stuff ////////////////////////////////////////////////////////
modules.gui = '2014-October-06';
modules.gui = '2014-December-04';
// Declarations
@ -233,10 +233,6 @@ IDE_Morph.prototype.init = function (isAutoFill) {
IDE_Morph.prototype.openIn = function (world) {
var hash, usr, myself = this, urlLanguage = null;
this.buildPanes();
world.add(this);
world.userMenu = this.userMenu;
// get persistent user data, if any
if (localStorage) {
usr = localStorage['-snap-user'];
@ -245,10 +241,17 @@ IDE_Morph.prototype.openIn = function (world) {
if (usr) {
SnapCloud.username = usr.username || null;
SnapCloud.password = usr.password || null;
if (SnapCloud.username) {
this.source = 'cloud';
}
}
}
}
this.buildPanes();
world.add(this);
world.userMenu = this.userMenu;
// override SnapCloud's user message with Morphic
SnapCloud.message = function (string) {
var m = new MenuMorph(null, string),
@ -2511,8 +2514,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'

Wyświetl plik

@ -2309,3 +2309,88 @@ ______
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 BLOCKs lexical awareness
1411213
-------
* Threads: Fix “stop this block” primitive for tail-call-elimination
1411224
-------
* 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)
1411225
-------
* Threads: Evaluator optimizations (reducing the stack size for reporters)
* Threads: Full TCO (tail-call-elimination), now Snap! *is* Scheme :-)
1411225
-------
* Threads: Fixed #656
141201
------
* Objects: Hide hidden elements in the project thumbnail
* GUI: Point project dialog to cloud if already signed in, thanks, Michael!
* favicon: Transparent background, thanks, Michael!
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
141204
------
* Threads, Objects: Experimental “ForEach” primitive (hidden in dev mode)
* GUI: Another attempt at pointing the project dialog to the cloud if signed in
141205
------
* Morphic: Avoid auto-scaling artefacts in Safari on retina displays (resulting in “traces” when dragging items)
141206
------
* Store: Fixed #668
141211
------
* Threads: yield after each cycle in the experimental “forEach” primitive

1273
lang-kn.js 100644

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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':

Wyświetl plik

@ -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;

Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/
modules.locale = '2014-October-01';
modules.locale = '2014-December-02';
// Global stuff
@ -427,3 +427,15 @@ SnapTranslator.dict.bn = {
'last_changed':
'2014-07-02'
};
SnapTranslator.dict.kn = {
// translations meta information
'language_name':
'\u0C95\u0CA8\u0CCD\u0CA8\u0CA1',
'language_translator':
'Vinayakumar R',
'translator_e-mail':
'vnkmr7620@gmail.com',
'last_changed':
'2014-12-02'
};

Wyświetl plik

@ -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-December-05';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -1925,7 +1931,9 @@ Rectangle.prototype.round = function () {
Rectangle.prototype.spread = function () {
// round me by applying floor() to my origin and ceil() to my corner
return this.origin.floor().corner(this.corner.ceil());
// expand by 1 to be on the safe side, this eliminates rounding
// artefacts caused by Safari's auto-scaling on retina displays
return this.origin.floor().corner(this.corner.ceil()).expandBy(1);
};
Rectangle.prototype.amountToTranslateWithin = function (aRect) {
@ -3972,6 +3980,7 @@ PenMorph.prototype.init = function () {
this.size = 1;
this.wantsRedraw = false;
this.penPoint = 'tip'; // or 'center"
this.penBounds = null; // rect around the visible arrow shape
HandleMorph.uber.init.call(this);
this.setExtent(new Point(size, size));
@ -3994,11 +4003,9 @@ PenMorph.prototype.changed = function () {
// PenMorph display:
PenMorph.prototype.drawNew = function (facing) {
/*
my orientation can be overridden with the "facing" parameter to
implement Scratch-style rotation styles
// my orientation can be overridden with the "facing" parameter to
// implement Scratch-style rotation styles
*/
var context, start, dest, left, right, len,
direction = facing || this.heading;
@ -4021,6 +4028,15 @@ PenMorph.prototype.drawNew = function (facing) {
right = start.distanceAngle(len * 0.33, direction - 230);
}
// cache penBounds
this.penBounds = new Rectangle(
Math.min(start.x, dest.x, left.x, right.x),
Math.min(start.y, dest.y, left.y, right.y),
Math.max(start.x, dest.x, left.x, right.x),
Math.max(start.y, dest.y, left.y, right.y)
);
// draw arrow shape
context.fillStyle = this.color.toString();
context.beginPath();
@ -4037,7 +4053,6 @@ PenMorph.prototype.drawNew = function (facing) {
context.lineWidth = 1;
context.stroke();
context.fill();
};
// PenMorph access:
@ -5754,7 +5769,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 +9361,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 +9509,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 +9545,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 +9606,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 +9664,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);

Wyświetl plik

@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
modules.objects = '2014-October-08';
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
@ -1674,6 +1681,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 +1988,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',
@ -2057,6 +2070,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(txt);
blocks.push('-');
blocks.push(block('reportMap'));
blocks.push('-');
blocks.push(block('doForEach'));
}
/////////////////////////////////
@ -2164,8 +2179,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 +2219,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];
}
});
@ -3232,8 +3247,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());
@ -4858,6 +4876,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 +5131,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',
@ -5183,6 +5207,8 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(txt);
blocks.push('-');
blocks.push(block('reportMap'));
blocks.push('-');
blocks.push(block('doForEach'));
}
/////////////////////////////////
@ -5321,7 +5347,7 @@ StageMorph.prototype.thumbnail = function (extentPoint, excludedSprite) {
this.dimensions.y * this.scale
);
this.children.forEach(function (morph) {
if (morph !== excludedSprite) {
if (morph.isVisible && (morph !== excludedSprite)) {
fb = morph.fullBounds();
fimg = morph.fullImage();
if (fimg.width && fimg.height) {

Wyświetl plik

@ -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>

Wyświetl plik

@ -61,7 +61,7 @@ SyntaxElementMorph, Variable*/
// Global stuff ////////////////////////////////////////////////////////
modules.store = '2014-October-01';
modules.store = '2014-December-06';
// 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',
@ -1199,6 +1201,10 @@ SnapSerializer.prototype.loadValue = function (model) {
if (el) {
v.outerContext = this.loadValue(el);
}
if (v.outerContext && v.receiver &&
!v.outerContext.variables.parentFrame) {
v.outerContext.variables.parentFrame = v.receiver.variables;
}
return v;
case 'costume':
center = new Point();
@ -1824,9 +1830,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);

Wyświetl plik

@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff ////////////////////////////////////////////////////////
modules.threads = '2014-October-01';
modules.threads = '2014-December-11';
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;
@ -207,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();
@ -225,8 +235,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 +246,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 +310,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 +320,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 +341,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 +358,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();
@ -361,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
@ -435,8 +457,10 @@ 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);
}
@ -460,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);
}
@ -526,6 +550,35 @@ Process.prototype.reportAnd = function (block) {
}
};
Process.prototype.doReport = function (block) {
var outer = this.context.outerContext;
if (this.context.expression.partOfCustomCommand) {
this.doStopCustomBlock();
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;
}
}
}
// 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);
};
// Process: Non-Block evaluation
Process.prototype.evaluateMultiSlot = function (multiSlot, argCount) {
@ -586,7 +639,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 +649,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 +657,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 +674,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 +727,10 @@ Process.prototype.doYield = function () {
}
};
Process.prototype.expectReport = function () {
this.handleError(new Error("reporter didn't report"));
};
// Process Exception Handling
Process.prototype.handleError = function (error, element) {
@ -757,8 +809,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 (
@ -781,8 +833,9 @@ Process.prototype.evaluate = function (
}
var outer = new Context(null, null, context.outerContext),
caller = this.context.parentContext,
exit,
runnable,
extra,
parms = args.asArray(),
i,
value;
@ -796,25 +849,13 @@ Process.prototype.evaluate = function (
outer,
context.receiver
);
extra = new Context(runnable, 'doYield');
this.context.parentContext = runnable;
/*
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)) {
this.context.parentContext = extra;
} else {
this.context.parentContext = runnable;
if (context.expression instanceof ReporterBlockMorph) {
// auto-"warp" nested reporters
this.readyToYield = (Date.now() - this.lastYield > this.timeout);
}
runnable.isLambda = true;
runnable.isImplicitLambda = context.isImplicitLambda;
runnable.isCustomBlock = false;
// assign parameters if any were passed
if (parms.length > 0) {
@ -849,8 +890,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 +900,23 @@ Process.prototype.evaluate = function (
if (runnable.expression instanceof CommandBlockMorph) {
runnable.expression = runnable.expression.blockSequence();
if (!isCommand) {
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;
}
}
}
};
@ -867,6 +926,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 +941,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 +975,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 +994,48 @@ Process.prototype.fork = function (context, args) {
stage.threads.processes.push(proc);
};
Process.prototype.doReport = function (value, isCSlot) {
while (this.context && !this.context.isLambda) {
if (this.context.expression === 'doStopWarping') {
this.doStopWarping();
} else {
this.popContext();
}
}
if (this.context && this.context.isImplicitLambda) {
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 stopping blocks primitives
Process.prototype.doStopBlock = function () {
this.doReport();
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 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.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();
}
}
};
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 +1053,21 @@ 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,
extra,
exit,
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,15 +1075,10 @@ Process.prototype.evaluateCustomBlock = function () {
this.context.parentContext,
context.expression,
outer,
outer.receiver,
true // is custom block
outer.receiver
);
extra = new Context(runnable, 'doYield');
this.context.parentContext = extra;
runnable.isLambda = true;
runnable.isCustomBlock = true;
this.context.parentContext = runnable;
// passing parameters if any were passed
if (parms.length > 0) {
@ -1062,9 +1100,43 @@ Process.prototype.evaluateCustomBlock = function () {
}
}
if (runnable.expression instanceof CommandBlockMorph) {
runnable.expression = runnable.expression.blockSequence();
// tag return target
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;
}
// 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();
};
// Process variables primitives
@ -1148,7 +1220,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 +1331,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 +1380,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 +1395,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 +1410,6 @@ Process.prototype.doIfElse = function () {
}
}
if (this.context) {
this.context.isLambda = isLambda;
this.context.isImplicitLambda = isImplicitLambda;
this.context.isCustomBlock = isCustomBlock;
}
@ -1420,8 +1484,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 +1500,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 +1580,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();
};
@ -1631,6 +1679,27 @@ 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('doYield');
this.pushContext();
this.evaluate(script, new List(), true);
};
// Process interpolated primitives
Process.prototype.doWait = function (secs) {
@ -2134,15 +2203,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 +2222,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;
@ -2316,6 +2389,7 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
var myself = this,
those,
stage,
box,
mouse;
if (this.inputOption(name) === 'mouse-pointer') {
@ -2327,9 +2401,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())) {
@ -2760,25 +2839,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 +2880,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 () {
@ -2870,9 +2946,10 @@ Context.prototype.continuation = function () {
} else if (this.parentContext) {
cont = this.parentContext;
} else {
return new Context(null, 'doStop');
return new Context(null, 'doYield');
}
cont = cont.copyForContinuation();
cont.tag = null;
cont.isContinuation = true;
return cont;
};
@ -3003,9 +3080,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 +3147,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')
);
};

File diff suppressed because one or more lines are too long