From acc40a6d2a06ab1fcfbd488030e091bd9215a1b2 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 18 Jun 2013 18:43:15 +0200 Subject: Code mapping generating text code from blocks, first experimental iteration --- blocks.js | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 315 insertions(+), 1 deletion(-) (limited to 'blocks.js') diff --git a/blocks.js b/blocks.js index 4f1f093..1a325a8 100644 --- a/blocks.js +++ b/blocks.js @@ -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 () { -- cgit v1.3.1