diff options
| author | jmoenig <jens@moenig.org> | 2013-06-18 18:43:15 +0200 |
|---|---|---|
| committer | jmoenig <jens@moenig.org> | 2013-06-18 18:43:15 +0200 |
| commit | acc40a6d2a06ab1fcfbd488030e091bd9215a1b2 (patch) | |
| tree | 57374704c2b908df2c1d8721f8a055a0f1378d1e | |
| parent | a7ebff7c5bb4a79b3a64441b50957e523fe686e0 (diff) | |
| download | snap-yow-acc40a6d2a06ab1fcfbd488030e091bd9215a1b2.tar.gz snap-yow-acc40a6d2a06ab1fcfbd488030e091bd9215a1b2.zip | |
Code mapping
generating text code from blocks, first experimental iteration
| -rw-r--r-- | blocks.js | 316 | ||||
| -rw-r--r-- | byob.js | 3 | ||||
| -rw-r--r-- | gui.js | 16 | ||||
| -rwxr-xr-x | history.txt | 4 | ||||
| -rw-r--r-- | objects.js | 45 | ||||
| -rw-r--r-- | threads.js | 49 |
6 files changed, 428 insertions, 5 deletions
@@ -153,7 +153,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2013-May-16'; +modules.blocks = '2013-June-18'; var SyntaxElementMorph; var BlockMorph; @@ -1061,6 +1061,34 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part = new ReporterSlotMorph(true); break; + // code mapping (experimental) + + case '%codeListPart': + part = new InputSlotMorph( + null, // text + false, // numeric? + { + 'list' : ['list'], + 'item' : ['item'], + 'delimiter' : ['delimiter'] + }, + true // read-only + ); + break; + case '%codeListKind': + part = new InputSlotMorph( + null, // text + false, // numeric? + { + 'collection' : ['collection'], + 'variables' : ['variables'], + 'parameters' : ['parameters'] + }, + true // read-only + ); + break; + + // symbols: case '%turtle': @@ -1479,6 +1507,22 @@ SyntaxElementMorph.prototype.showBubble = function (value) { ); }; +// SyntaxElementMorph code mapping + +/* + code mapping lets you use blocks to generate arbitrary text-based + source code that can be exported and compiled / embedded elsewhere, + it's not part of Snap's evaluator and not needed for Snap itself +*/ + +SyntaxElementMorph.prototype.mappedCode = function () { + var result = this.evaluate(); + if (result instanceof BlockMorph) { + return result.mappedCode(); + } + return result; +}; + // SyntaxElementMorph layout update optimization SyntaxElementMorph.prototype.startLayout = function () { @@ -1790,6 +1834,13 @@ BlockMorph.prototype.userMenu = function () { 'hidePrimitive' ); } + if (StageMorph.prototype.enableCodeMapping) { + menu.addLine(); + menu.addItem( + 'code mapping...', + 'mapToCode' + ); + } return menu; } menu.addLine(); @@ -1867,6 +1918,12 @@ BlockMorph.prototype.userMenu = function () { } menu.addLine(); menu.addItem("ringify", 'ringify'); + if (StageMorph.prototype.enableCodeMapping) { + menu.addItem( + 'code mapping...', + 'mapToCode' + ); + } return menu; }; @@ -2095,6 +2152,99 @@ BlockMorph.prototype.showHelp = function () { } }; +// BlockMorph code mapping + +/* + code mapping lets you use blocks to generate arbitrary text-based + source code that can be exported and compiled / embedded elsewhere, + it's not part of Snap's evaluator and not needed for Snap itself +*/ + +BlockMorph.prototype.mapToCode = function () { + // open a dialog box letting the user map code via the GUI + var key = this.selector.substr(0, 5) === 'reify' ? + 'reify' : this.selector, + block = this.codeMappingHeader(), + myself = this, + pic; + block.addShadow(new Point(3, 3)); + pic = block.fullImageClassic(); + new DialogBoxMorph( + this, + function (code) { + if (key === 'evaluateCustomBlock') { + myself.definition.codeMapping = code; + } else { + StageMorph.prototype.codeMappings[key] = code; + } + }, + this + ).prompt( + 'Code mapping', + key === 'evaluateCustomBlock' ? this.definition.codeMapping || '' + : StageMorph.prototype.codeMappings[key] || '', + this.world(), + pic + ); +}; + +BlockMorph.prototype.mapCode = function (aString, key) { + // primitive for programatically mapping code + var sel = key || this.selector.substr(0, 5) === 'reify' ? + 'reify' : this.selector; + if (aString) { + if (this.definition) { // custom block + this.definition.codeMapping = aString; + } else { + StageMorph.prototype.codeMappings[sel] = aString; + } + } +}; + +BlockMorph.prototype.mappedCode = function () { + var key = this.selector.substr(0, 5) === 'reify' ? + 'reify' : this.selector, + code, + count = 1, + parts = []; + code = key === 'reportGetVar' ? this.blockSpec + : this.definition ? this.definition.codeMapping || '' + : StageMorph.prototype.codeMappings[key] || ''; + this.inputs().forEach(function (input) { + parts.push(input.mappedCode()); + }); + parts.forEach(function (part) { + var rx = new RegExp('<#' + count + '>', 'g'); + code = code.replace(rx, part); + count += 1; + }); + if (this.nextBlock && this.nextBlock()) { // Command + code += this.nextBlock().mappedCode(); + } + return code; +}; + +BlockMorph.prototype.codeMappingHeader = function () { + var block = this.definition ? this.definition.blockInstance() + : SpriteMorph.prototype.blockForSelector(this.selector), + hat = new HatBlockMorph(), + count = 1; + + block.inputs().forEach(function (input) { + var part = new TemplateSlotMorph('<#' + count + '>'); + block.silentReplaceInput(input, part); + count += 1; + }); + block.isPrototype = true; + hat.setCategory("control"); + hat.setSpec('%s'); + hat.silentReplaceInput(hat.inputs()[0], block); + if (this.category === 'control') { + hat.alternateBlockColor(); + } + return hat; +}; + // BlockMorph drawing BlockMorph.prototype.eraseHoles = function (context) { @@ -5187,6 +5337,13 @@ CSlotMorph.prototype.getSpec = function () { return '%c'; }; +CSlotMorph.prototype.mappedCode = function () { + var code = StageMorph.prototype.codeMappings.reify || '', + part = this.nestedBlock(), + nestedCode = part ? part.mappedCode() : ''; + return code.replace(/<#1>/g, nestedCode); +}; + // CSlotMorph layout: CSlotMorph.prototype.fixLayout = function () { @@ -6113,6 +6270,60 @@ InputSlotMorph.prototype.reactToSliderEdit = function () { } }; +// InputSlotMorph menu: + +InputSlotMorph.prototype.userMenu = function () { + var menu = new MenuMorph(this); + if (!StageMorph.prototype.enableCodeMapping || this.isNumeric) { + return this.parent.userMenu(); + } + menu.addItem( + 'code string mapping...', + 'mapToCode' + ); + return menu; +}; + +// InputSlotMorph code mapping + +/* + code mapping lets you use blocks to generate arbitrary text-based + source code that can be exported and compiled / embedded elsewhere, + it's not part of Snap's evaluator and not needed for Snap itself +*/ + +InputSlotMorph.prototype.mapToCode = function () { + // private - open a dialog box letting the user map code via the GUI + new DialogBoxMorph( + this, + function (code) { + StageMorph.prototype.codeMappings.string = code; + }, + this + ).prompt( + 'Code mapping - String <#1>', + StageMorph.prototype.codeMappings.string || '', + this.world() + ); +}; + +InputSlotMorph.prototype.mappedCode = function () { + var code = StageMorph.prototype.codeMappings.string || '<#1>', + block = this.parentThatIsA(BlockMorph), + val = this.evaluate(); + + if (this.isNumeric) {return val; } + if (!isNaN(parseFloat(val))) {return val; } + if (!isString(val)) {return val; } + if (block && contains( + ['doSetVar', 'doChangeVar', 'doShowVar', 'doHideVar'], + block.selector + )) { + return val; + } + return code.replace(/<#1>/g, val); +}; + // InputSlotMorph evaluating: InputSlotMorph.prototype.evaluate = function () { @@ -8089,6 +8300,109 @@ MultiArgMorph.prototype.mouseClickLeft = function (pos) { this.endLayout(); }; +// MultiArgMorph menu: + +MultiArgMorph.prototype.userMenu = function () { + var menu = new MenuMorph(this), + block = this.parentThatIsA(BlockMorph), + key = '', + myself = this; + if (!StageMorph.prototype.enableCodeMapping) { + return this.parent.userMenu(); + } + if (block) { + if (block instanceof RingMorph) { + key = 'parms_'; + } else if (block.selector === 'doDeclareVariables') { + key = 'tempvars_'; + } + } + menu.addItem( + 'code list mapping...', + function () {myself.mapCodeList(key); } + ); + menu.addItem( + 'code item mapping...', + function () {myself.mapCodeItem(key); } + ); + menu.addItem( + 'code delimiter mapping...', + function () {myself.mapCodeDelimiter(key); } + ); + return menu; +}; + +// MultiArgMorph code mapping + +/* + code mapping lets you use blocks to generate arbitrary text-based + source code that can be exported and compiled / embedded elsewhere, + it's not part of Snap's evaluator and not needed for Snap itself +*/ + +MultiArgMorph.prototype.mapCodeDelimiter = function (key) { + this.mapToCode(key + 'delim', 'list item delimiter'); +}; + +MultiArgMorph.prototype.mapCodeList = function (key) { + this.mapToCode(key + 'list', 'list contents <#1>'); +}; + +MultiArgMorph.prototype.mapCodeItem = function (key) { + this.mapToCode(key + 'item', 'list item <#1>'); +}; + +MultiArgMorph.prototype.mapToCode = function (key, label) { + // private - open a dialog box letting the user map code via the GUI + new DialogBoxMorph( + this, + function (code) { + StageMorph.prototype.codeMappings[key] = code; + }, + this + ).prompt( + 'Code mapping - ' + label, + StageMorph.prototype.codeMappings[key] || '', + this.world() + ); +}; + +MultiArgMorph.prototype.mappedCode = function () { + var block = this.parentThatIsA(BlockMorph), + key = '', + code, + items = '', + itemCode, + delim, + count = 0, + parts = []; + + if (block) { + if (block instanceof RingMorph) { + key = 'parms_'; + } else if (block.selector === 'doDeclareVariables') { + key = 'tempvars_'; + } + } + + code = StageMorph.prototype.codeMappings[key + 'list'] || '<#1>'; + itemCode = StageMorph.prototype.codeMappings[key + 'item'] || '<#1>'; + delim = StageMorph.prototype.codeMappings[key + 'delim'] || ' '; + + this.inputs().forEach(function (input) { + parts.push(itemCode.replace(/<#1>/g, input.mappedCode())); + }); + parts.forEach(function (part) { + if (count) { + items += delim; + } + items += part; + count += 1; + }); + code = code.replace(/<#1>/g, items); + return code; +}; + // MultiArgMorph arity evaluating: MultiArgMorph.prototype.evaluate = function () { @@ -105,7 +105,7 @@ CommentMorph, localize, CSlotMorph, SpeechBubbleMorph, MorphicPreferences*/ // Global stuff //////////////////////////////////////////////////////// -modules.byob = '2013-June-06'; +modules.byob = '2013-June-18'; // Declarations @@ -138,6 +138,7 @@ function CustomBlockDefinition(spec, receiver) { this.spec = spec || ''; this.declarations = {}; // {'inputName' : [type, default]} this.comment = null; + this.codeMapping = null; // experimental, generate text code // don't serialize (not needed for functionality): this.receiver = receiver || null; // for serialization only (pointer) @@ -68,7 +68,7 @@ sb, CommentMorph, CommandBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2013-May-17'; +modules.gui = '2013-June-18'; // Declarations @@ -2148,6 +2148,20 @@ IDE_Morph.prototype.settingsMenu = function () { 'check for alternative\nGUI design', false ); + addPreference( + 'Code mapping', + function () { + StageMorph.prototype.enableCodeMapping = + !StageMorph.prototype.enableCodeMapping; + myself.currentSprite.blocksCache.variables = null; + myself.currentSprite.paletteCache.variables = null; + myself.refreshPalette(); + }, + StageMorph.prototype.enableCodeMapping, + 'uncheck to disable\nblock to text mapping features', + 'check for block\nto text mapping features', + false + ); menu.addLine(); // everything below this line is made persistent addPreference( 'Thread safe scripts', diff --git a/history.txt b/history.txt index 10b26c9..5e1d9c6 100755 --- a/history.txt +++ b/history.txt @@ -1733,3 +1733,7 @@ ______ * BYOB: Newly created custom reporters now have an initial default REPORT block as definition body * Morphic: focus World canvas on mouse down (otherwise prevent default) + +130618 +------ +* Code mapping (generating textual code from blocks), first iteration @@ -123,7 +123,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2013-June-05'; +modules.objects = '2013-June-18'; var SpriteMorph; var StageMorph; @@ -1016,6 +1016,29 @@ SpriteMorph.prototype.initBlocks = function () { category: 'lists', spec: 'replace item %idx of %l with %s', defaults: [1, null, localize('thing')] + }, + + // Code mapping - experimental + doMapCode: { // experimental + type: 'command', + category: 'other', + spec: 'map %cmdRing to code %s' + }, + doMapStringCode: { // experimental + type: 'command', + category: 'other', + spec: 'map String to code %s', + defaults: ['<#1>'] + }, + doMapListCode: { // experimental + type: 'command', + category: 'other', + spec: 'map %codeListPart of %codeListKind to code %s' + }, + reportMappedCode: { // experimental + type: 'reporter', + category: 'other', + spec: 'code of %cmdRing' } }; }; @@ -1771,6 +1794,15 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('='); + if (StageMorph.prototype.enableCodeMapping) { + blocks.push(block('doMapCode')); + blocks.push(block('doMapStringCode')); + blocks.push(block('doMapListCode')); + blocks.push('-'); + blocks.push(block('reportMappedCode')); + blocks.push('='); + } + button = new PushButtonMorph( null, function () { @@ -3189,6 +3221,8 @@ StageMorph.prototype.paletteTextColor = SpriteMorph.prototype.paletteTextColor; StageMorph.prototype.hiddenPrimitives = {}; +StageMorph.prototype.codeMappings = {}; +StageMorph.prototype.enableCodeMapping = false; // StageMorph instance creation @@ -4011,6 +4045,15 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('='); + if (StageMorph.prototype.enableCodeMapping) { + blocks.push(block('doMapCode')); + blocks.push(block('doMapStringCode')); + blocks.push(block('doMapListCode')); + blocks.push('-'); + blocks.push(block('reportMappedCode')); + blocks.push('='); + } + button = new PushButtonMorph( null, function () { @@ -83,7 +83,7 @@ ArgLabelMorph, localize*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2013-May-14'; +modules.threads = '2013-June-18'; var ThreadManager; var Process; @@ -2284,6 +2284,53 @@ Process.prototype.reportTimer = function () { return 0; }; +// Process code mapping + +/* + for generating textual source code using + blocks - not needed to run or debug Snap +*/ + +Process.prototype.doMapCode = function (aContext, aString) { + if (aContext instanceof Context) { + if (aContext.expression instanceof SyntaxElementMorph) { + return aContext.expression.mapCode(aString || ''); + } + } +}; + +Process.prototype.doMapStringCode = function (aString) { + StageMorph.prototype.codeMappings.string = aString || '<#1>'; +}; + +Process.prototype.doMapListCode = function (part, kind, aString) { + var key1 = '', + key2 = 'delim'; + + if (this.inputOption(kind) === 'parameters') { + key1 = 'parms_'; + } else if (this.inputOption(kind) === 'variables') { + key1 = 'tempvars_'; + } + + if (this.inputOption(part) === 'list') { + key2 = 'list'; + } else if (this.inputOption(part) === 'item') { + key2 = 'item'; + } + + StageMorph.prototype.codeMappings[key1 + key2] = aString || ''; +}; + +Process.prototype.reportMappedCode = function (aContext) { + if (aContext instanceof Context) { + if (aContext.expression instanceof SyntaxElementMorph) { + return aContext.expression.mappedCode(); + } + } + return ''; +}; + // Process music primitives Process.prototype.doRest = function (beats) { |
