kopia lustrzana https://github.com/backface/turtlestitch
Code mapping
generating text code from blocks, first experimental iterationpull/3/merge
rodzic
a7ebff7c5b
commit
acc40a6d2a
316
blocks.js
316
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 () {
|
||||
|
|
|
|||
3
byob.js
3
byob.js
|
|
@ -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)
|
||||
|
|
|
|||
16
gui.js
16
gui.js
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
45
objects.js
45
objects.js
|
|
@ -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 () {
|
||||
|
|
|
|||
49
threads.js
49
threads.js
|
|
@ -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) {
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue