Merge branch 'master' into cat5d

pull/89/head
Jens Mönig 2019-04-25 16:09:03 +02:00 zatwierdzone przez GitHub
commit 45c5794fcb
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 329 dodań i 87 usunięć

Wyświetl plik

@ -25,6 +25,8 @@
* new "get graphic effect" reporter
* new "get pen attribute" reporter
* new "write" command in pen category (used to be "label" in tools)
* new "map","keep", "combine" and "for each" primitives in list category
* new "for" loop and "if then else" reporter primitives in the Control category
* added "neg", "lg" (log2) and "2^" selectors to monadic function reporter in Operators
* added "^" reporter (power of) in the Operators category
* added "width" and "height" as attribute selectors of the OF primitive for the stage
@ -67,8 +69,26 @@
* German
* French
### 2019-04-24
* updated German translation (for new HOF prims)
### 2019-04-24
* Threads, Objects: new "combine" primitive in list category
* Threads: added type-assertions for the new HOF prims
* Threads, Objects: new "for" loop primitive in Control category
* Threads, Objects: new "if then else" reporter primitive in Control category
### 2019-04-23
* Threads: fixed JS stack overflow issue for MAP primitive
* Threads: new "map" and "for each" primitives in list category
* Threads: new "keep" primitive in list category
### 2019-04-22
* Threads: fixed variable binding for "arguments", turned dictionary key into a Symbol
### 2019-04-15
* Catalan translation update
### 2019-04-12
* Objects: enabled text-variables as inputs for graphic effects / pen attributes
* updated amination library with graphic effects and audio attributes

Wyświetl plik

@ -185,7 +185,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org, jadga.huegle@sap.com', // optional
'last_changed':
'2019-04-11', // this, too, will appear in the Translators tab
'2019-04-25', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -529,10 +529,14 @@ SnapTranslator.dict.de = {
'wiederhole %n mal %loop',
'repeat until %b %loop':
'wiederhole bis %b %loop',
'for %upvar = %n to %n %cla':
'für %upvar = %n bis %n %cla',
'if %b %c':
'falls %b %c',
'if %b %c else %c':
'falls %b %c sonst %c',
'if %b then %s else %s':
'falls %b dann %s sonst %s',
'report %s':
'berichte %s',
'stop %stopChoices':
@ -763,6 +767,16 @@ SnapTranslator.dict.de = {
'%l enth\u00e4lt %s',
'thing':
'etwas',
'for each %upvar in %l %cla':
'für jedes %upvar von %l %cla',
'item':
'Element',
'map %repRing over %l':
'wende %repRing an auf %l',
'keep items such that %predRing from %l':
'behalte Elemente, die %predRing aus %l',
'combine with %repRing items of %l':
'kombiniere mit %repRing die Elemente von %l',
'add %s to %l':
'f\u00fcge %s zu %l hinzu',
'delete %ida of %l':

Wyświetl plik

@ -7,8 +7,8 @@
<script type="text/javascript" src="src/morphic.js?version=2019-02-07"></script>
<script type="text/javascript" src="src/widgets.js?version=2019-04-05"></script>
<script type="text/javascript" src="src/blocks.js?version=2019-04-11"></script>
<script type="text/javascript" src="src/threads.js?version=2019-04-11"></script>
<script type="text/javascript" src="src/objects.js?version=2019-04-12"></script>
<script type="text/javascript" src="src/threads.js?version=2019-04-24"></script>
<script type="text/javascript" src="src/objects.js?version=2019-04-24"></script>
<script type="text/javascript" src="src/gui.js?version=2019-04-10"></script>
<script type="text/javascript" src="src/paint.js?version=2019-02-22"></script>
<script type="text/javascript" src="src/lists.js?version=2019-02-07"></script>
@ -18,7 +18,7 @@
<script type="text/javascript" src="src/sketch.js?version=2019-02-22"></script>
<script type="text/javascript" src="src/xml.js?version=2018-11-12"></script>
<script type="text/javascript" src="src/store.js?version=2019-04-04"></script>
<script type="text/javascript" src="src/locale.js?version=2019-04-11"></script>
<script type="text/javascript" src="src/locale.js?version=2019-04-25"></script>
<script type="text/javascript" src="src/cloud.js?version=2019-03-25"></script>
<script type="text/javascript" src="src/sha512.js?version=2018-10-02"></script>
<script type="text/javascript" src="src/FileSaver.min.js?version=2018-10-02"></script>

Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/
modules.locale = '2019-April-15';
modules.locale = '2019-April-25';
// Global stuff
@ -160,7 +160,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org, jadga.huegle@sap.com',
'last_changed':
'2019-04-11'
'2019-04-25'
};
SnapTranslator.dict.it = {

Wyświetl plik

@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph, HandleMorph,
AlignmentMorph, Process, XML_Element, VectorPaintEditorMorph*/
modules.objects = '2019-April-12';
modules.objects = '2019-April-24';
var SpriteMorph;
var StageMorph;
@ -736,6 +736,12 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'control',
spec: 'repeat until %b %loop'
},
doFor: {
type: 'command',
category: 'control',
spec: 'for %upvar = %n to %n %cla',
defaults: ['i', 1, 10]
},
doIf: {
type: 'command',
category: 'control',
@ -746,6 +752,11 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'control',
spec: 'if %b %c else %c'
},
reportIfElse: {
type: 'reporter',
category: 'control',
spec: 'if %b then %s else %s'
},
doStopThis: {
type: 'command',
category: 'control',
@ -1270,6 +1281,16 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'lists',
spec: 'map %repRing over %l'
},
reportKeep: {
type: 'reporter',
category: 'lists',
spec: 'keep items such that %predRing from %l'
},
reportCombine: {
type: 'reporter',
category: 'lists',
spec: 'combine with %repRing items of %l'
},
doForEach: {
type: 'command',
category: 'lists',
@ -2162,9 +2183,11 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doForever'));
blocks.push(block('doRepeat'));
blocks.push(block('doUntil'));
blocks.push(block('doFor'));
blocks.push('-');
blocks.push(block('doIf'));
blocks.push(block('doIfElse'));
blocks.push(block('reportIfElse'));
blocks.push('-');
blocks.push(block('doReport'));
blocks.push(block('doStopThis'));
@ -2409,6 +2432,11 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('reportListLength'));
blocks.push(block('reportListContainsItem'));
blocks.push('-');
blocks.push(block('doForEach'));
blocks.push(block('reportMap'));
blocks.push(block('reportKeep'));
blocks.push(block('reportCombine'));
blocks.push('-');
blocks.push(block('doAddToList'));
blocks.push(block('doDeleteFromList'));
blocks.push(block('doInsertInList'));
@ -2425,9 +2453,6 @@ SpriteMorph.prototype.blockTemplates = function (category) {
txt.setColor(this.paletteTextColor);
blocks.push(txt);
blocks.push('-');
blocks.push(block('doForEach'));
blocks.push(block('reportMap'));
blocks.push('-');
blocks.push(block('doShowTable'));
}
@ -2590,8 +2615,10 @@ SpriteMorph.prototype.freshPalette = function (category) {
'reportCDR',
'reportListLength',
'reportListContainsItem',
// 'doForEach',
// 'reportMap',
'doForEach',
'reportMap',
'reportKeep',
'reportCombine',
'doAddToList',
'doDeleteFromList',
'doInsertInList',
@ -7733,9 +7760,11 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doForever'));
blocks.push(block('doRepeat'));
blocks.push(block('doUntil'));
blocks.push(block('doFor'));
blocks.push('-');
blocks.push(block('doIf'));
blocks.push(block('doIfElse'));
blocks.push(block('reportIfElse'));
blocks.push('-');
blocks.push(block('doReport'));
blocks.push(block('doStopThis'));
@ -7956,6 +7985,11 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('reportListLength'));
blocks.push(block('reportListContainsItem'));
blocks.push('-');
blocks.push(block('doForEach'));
blocks.push(block('reportMap'));
blocks.push(block('reportKeep'));
blocks.push(block('reportCombine'));
blocks.push('-');
blocks.push(block('doAddToList'));
blocks.push(block('doDeleteFromList'));
blocks.push(block('doInsertInList'));
@ -7972,9 +8006,6 @@ StageMorph.prototype.blockTemplates = function (category) {
txt.setColor(this.paletteTextColor);
blocks.push(txt);
blocks.push('-');
blocks.push(block('doForEach'));
blocks.push(block('reportMap'));
blocks.push('-');
blocks.push(block('doShowTable'));
}

Wyświetl plik

@ -60,9 +60,9 @@ degrees, detect, nop, radians, ReporterSlotMorph, CSlotMorph, RingMorph, Sound,
IDE_Morph, ArgLabelMorph, localize, XML_Element, hex_sha512, TableDialogMorph,
StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, Color,
TableFrameMorph, ColorSlotMorph, isSnapObject, Map, newCanvas*/
TableFrameMorph, ColorSlotMorph, isSnapObject, Map, newCanvas, Symbol*/
modules.threads = '2019-April-11';
modules.threads = '2019-April-24';
var ThreadManager;
var Process;
@ -716,6 +716,7 @@ Process.prototype.evaluateBlock = function (block, argCount) {
// check for special forms
if (selector === 'reportOr' ||
selector === 'reportAnd' ||
selector === 'reportIfElse' ||
selector === 'doReport') {
return this[selector](block);
}
@ -1018,7 +1019,7 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
context.expression.allEmptySlots().forEach(function (slot) {
i += 1;
if (slot instanceof MultiArgMorph) {
slot.bindingID = ['arguments'];
slot.bindingID = Symbol.for('arguments');
} else {
slot.bindingID = i;
}
@ -1075,11 +1076,9 @@ Process.prototype.evaluate = function (
) {
if (!context) {return null; }
if (context instanceof Function) {
/*
if (!this.enableJS) {
throw new Error('JavaScript is not enabled');
}
*/
// if (!this.enableJS) {
// throw new Error('JavaScript is not enabled');
// }
return context.apply(
this.blockReceiver(),
args.asArray().concat([this])
@ -1120,8 +1119,8 @@ Process.prototype.evaluate = function (
// assign arguments to parameters
// assign the actual arguments list to the special
// parameter ID ['arguments'], to be used for variadic inputs
outer.variables.addVar(['arguments'], args);
// parameter ID Symbol.for('arguments'), to be used for variadic inputs
outer.variables.addVar(Symbol.for('arguments'), args);
// assign arguments that are actually passed
if (parms.length > 0) {
@ -1220,8 +1219,8 @@ Process.prototype.initializeFor = function (context, args) {
// assign arguments to parameters
// assign the actual arguments list to the special
// parameter ID ['arguments'], to be used for variadic inputs
outer.variables.addVar(['arguments'], args);
// parameter ID Symbol.for('arguments'), to be used for variadic inputs
outer.variables.addVar(Symbol.for('arguments'), args);
// assign arguments that are actually passed
if (parms.length > 0) {
@ -1839,6 +1838,23 @@ Process.prototype.doIfElse = function () {
this.pushContext();
};
Process.prototype.reportIfElse = function (block) {
var inputs = this.context.inputs;
if (inputs.length < 1) {
this.evaluateNextInput(block);
} else if (inputs.length > 1) {
if (this.flashContext()) {return; }
this.returnValueToParentContext(inputs.pop());
this.popContext();
} else if (inputs[0]) {
this.evaluateNextInput(block);
} else {
inputs.push(null);
this.evaluateNextInput(block);
}
};
// Process process related primitives
Process.prototype.doStop = function () {
@ -2067,63 +2083,16 @@ Process.prototype.doWaitUntil = function (goalCondition) {
this.pushContext();
};
Process.prototype.reportMap = function (reporter, list) {
// answer a new list containing the results of the reporter applied
// to each value of the given list. Distinguish between linked and
// arrayed lists.
// Note: This method utilizes the current context's inputs array to
// manage temporary variables, whose allocation to which slot are
// documented in each of the variants' code (linked or arrayed) below
// Process interpolated iteration primitives
var next;
if (list.isLinked) {
// this.context.inputs:
// [0] - reporter
// [1] - list (original source)
// -----------------------------
// [2] - result list (target)
// [3] - currently last element of result list
// [4] - current source list (what's left to map)
// [5] - current value of last function call
if (this.context.inputs.length < 3) {
this.context.addInput(new List());
this.context.inputs[2].isLinked = true;
this.context.addInput(this.context.inputs[2]);
this.context.addInput(list);
}
if (this.context.inputs[4].length() === 0) {
this.context.inputs[3].rest = list.cons(this.context.inputs[5]);
this.returnValueToParentContext(this.context.inputs[2].cdr());
return;
}
if (this.context.inputs.length > 5) {
this.context.inputs[3].rest = list.cons(this.context.inputs[5]);
this.context.inputs[3] = this.context.inputs[3].rest;
this.context.inputs.splice(5);
}
next = this.context.inputs[4].at(1);
this.context.inputs[4] = this.context.inputs[4].cdr();
this.pushContext();
this.evaluate(reporter, new List([next]));
} else { // arrayed
// this.context.inputs:
// [0] - reporter
// [1] - list (original source)
// -----------------------------
// [2..n] - result values (target)
if (this.context.inputs.length - 2 === list.length()) {
this.returnValueToParentContext(
new List(this.context.inputs.slice(2))
);
return;
}
next = list.at(this.context.inputs.length - 1);
this.pushContext();
this.evaluate(reporter, new List([next]));
}
};
/*
these primitives can be - for the most part easily - written as
custom blocks by users themselves. They are, or used to be, in
libraries that could be loaded additionally. Making them available
as primitives has the benefit of getting novices acquainted to
using HOFs plus some performance advantages, however at the cost
of losing the ability to inspect how they're written.
*/
Process.prototype.doForEach = function (upvar, list, script) {
// perform a script for each element of a list, assigning the
@ -2132,13 +2101,11 @@ Process.prototype.doForEach = function (upvar, list, script) {
// within the script. Uses the context's - unused - fourth
// element as temporary storage for the current list index
this.assertType(list, 'list');
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)
);
this.context.outerContext.variables.setVar(upvar, list.at(index));
if (index > list.length()) {return; }
this.context.inputs[3] += 1;
this.pushContext('doYield');
@ -2146,6 +2113,214 @@ Process.prototype.doForEach = function (upvar, list, script) {
this.evaluate(script, new List(), true);
};
Process.prototype.doFor = function (upvar, start, end, script) {
// perform a script for every integer step between start and stop,
// assigning the current iteration index to a variable with the
// name specified in the "upvar" parameter, so it can be referenced
// within the script.
var dta;
if (this.context.aggregation === null) {
this.context.aggregation = {
idx : Math.floor(start),
test : start < end ?
function () {return this.idx > end; }
: function () {return this.idx < end; },
step : start < end ? 1 : -1,
parms : new List() // empty parameters, reusable to avoid GC
};
}
dta = this.context.aggregation;
this.context.outerContext.variables.addVar(upvar);
this.context.outerContext.variables.setVar(upvar, dta.idx);
if (dta.test()) {return; }
dta.idx += dta.step;
this.pushContext('doYield');
this.pushContext();
this.evaluate(script, dta.parms, true);
};
// Process interpolated HOF primitives
/*
this.context.inputs:
[0] - reporter
[1] - list (original source)
-----------------------------
[2] - last reporter evaluation result
these primitives used to store the aggregated data in the unused parts
of the context's input-array. For reasons obscure to me this led to
JS stack overflows when used on large lists (> 150 k items). As a remedy
aggregations are now accumulated in the "aggregation" property slot
of Context. Why this speeds up execution by orders of magnitude while
"fixing" the stack-overflow issue eludes me. -Jens
*/
Process.prototype.reportMap = function (reporter, list) {
// answer a new list containing the results of the reporter applied
// to each value of the given list. Distinguish between linked and
// arrayed lists.
var next;
this.assertType(list, 'list');
if (list.isLinked) {
if (this.context.aggregation === null) {
this.context.aggregation = {
source : list,
target : new List(),
end : null,
remaining : list.length()
};
this.context.aggregation.target.isLinked = true;
this.context.aggregation.end = this.context.aggregation.target;
} else if (this.context.inputs.length > 2) {
this.context.aggregation.end.rest = list.cons(
this.context.inputs.pop()
);
this.context.aggregation.end = this.context.aggregation.end.rest;
this.context.aggregation.remaining -= 1;
}
if (this.context.aggregation.remaining === 0) {
this.context.aggregation.end.rest = list.cons(
this.context.inputs[2]
).cdr();
this.returnValueToParentContext(
this.context.aggregation.target.cdr()
);
return;
}
next = this.context.aggregation.source.at(1);
this.context.aggregation.source = this.context.aggregation.source.cdr();
} else { // arrayed
if (this.context.aggregation === null) {
this.context.aggregation = [];
} else if (this.context.inputs.length > 2) {
this.context.aggregation.push(this.context.inputs.pop());
}
if (this.context.aggregation.length === list.length()) {
this.returnValueToParentContext(
new List(this.context.aggregation)
);
return;
}
next = list.at(this.context.aggregation.length + 1);
}
this.pushContext();
this.evaluate(reporter, new List([next]));
};
Process.prototype.reportKeep = function (predicate, list) {
// Filter - answer a new list containing the items of the list for which
// the predicate evaluates TRUE.
// Distinguish between linked and arrayed lists.
var next;
this.assertType(list, 'list');
if (list.isLinked) {
if (this.context.aggregation === null) {
this.context.aggregation = {
source : list,
target : new List(),
end : null,
remaining : list.length()
};
this.context.aggregation.target.isLinked = true;
this.context.aggregation.end = this.context.aggregation.target;
} else if (this.context.inputs.length > 2) {
if (this.context.inputs.pop() === true) {
this.context.aggregation.end.rest = list.cons(
this.context.aggregation.source.at(1)
);
this.context.aggregation.end =
this.context.aggregation.end.rest;
}
this.context.aggregation.remaining -= 1;
this.context.aggregation.source =
this.context.aggregation.source.cdr();
}
if (this.context.aggregation.remaining === 0) {
this.returnValueToParentContext(
this.context.aggregation.target.cdr()
);
return;
}
next = this.context.aggregation.source.at(1);
} else { // arrayed
if (this.context.aggregation === null) {
this.context.aggregation = {
idx : 0,
target : []
};
} else if (this.context.inputs.length > 2) {
if (this.context.inputs.pop() === true) {
this.context.aggregation.target.push(
list.at(this.context.aggregation.idx)
);
}
}
if (this.context.aggregation.idx === list.length()) {
this.returnValueToParentContext(
new List(this.context.aggregation.target)
);
return;
}
this.context.aggregation.idx += 1;
next = list.at(this.context.aggregation.idx);
}
this.pushContext();
this.evaluate(predicate, new List([next]));
};
Process.prototype.reportCombine = function (reporter, list) {
// Fold - answer an aggregation of all list items from "left to right"
// Distinguish between linked and arrayed lists.
var next, current;
this.assertType(list, 'list');
if (list.length() < 2) {
this.returnValueToParentContext(list.length() ? list.at(1) : 0);
return;
}
if (list.isLinked) {
if (this.context.aggregation === null) {
this.context.aggregation = {
source : list.cdr(),
target : list.at(1),
remaining : list.length() - 1
};
} else if (this.context.inputs.length > 2) {
this.context.aggregation.target = this.context.inputs.pop();
this.context.aggregation.remaining -= 1;
this.context.aggregation.source =
this.context.aggregation.source.cdr();
}
if (this.context.aggregation.remaining === 0) {
this.returnValueToParentContext(this.context.aggregation.target);
return;
}
next = this.context.aggregation.source.at(1);
} else { // arrayed
if (this.context.aggregation === null) {
this.context.aggregation = {
idx : 1,
target : list.at(1)
};
} else if (this.context.inputs.length > 2) {
this.context.aggregation.target = this.context.inputs.pop();
}
if (this.context.aggregation.idx === list.length()) {
this.returnValueToParentContext(this.context.aggregation.target);
return;
}
this.context.aggregation.idx += 1;
next = list.at(this.context.aggregation.idx);
}
current = this.context.aggregation.target;
this.pushContext();
this.evaluate(reporter, new List([current, next]));
};
// Process interpolated primitives
Process.prototype.doWait = function (secs) {
@ -4819,6 +4994,7 @@ Process.prototype.reportAtomicGroup = function (list, reporter) {
tag string or number to optionally identify the Context,
as a "return" target (for the "stop block" primitive)
isFlashing flag for single-stepping
aggregation slot for collecting data from reentrant visits
*/
function Context(
@ -4849,6 +5025,7 @@ function Context(
this.emptySlots = 0; // used for block reification
this.tag = null; // lexical catch-tag for custom blocks
this.isFlashing = false; // for single-stepping
this.aggregation = null;
}
Context.prototype.toString = function () {