This commit is contained in:
Felix Roos
2022-03-21 19:50:47 +01:00
parent 98f91607cd
commit 4487834591
15 changed files with 452 additions and 520 deletions
@@ -41771,13 +41771,13 @@ function useRepl({ tune , defaultSynth , autolink =true , onEvent , onDraw }) {
const activateCode = async (_code = code)=>{
if (activeCode && !dirty) {
setError(undefined);
!cycle1.started && cycle1.start();
cycle1.start();
return;
}
try {
setPending(true);
const parsed = await _evaluate.evaluate(_code);
!cycle1.started && cycle1.start();
cycle1.start();
broadcast({
type: 'start',
from: id
@@ -42018,20 +42018,14 @@ hackLiteral(String, [
// this will add everything to global scope, which is accessed by eval
Object.assign(globalThis, bootstrapped, _tone1, _tone, _voicings, _drawMjs, _uiMjs, {
gist: _gistJsDefault.default,
euclid: _euclidMjsDefault.default
euclid: _euclidMjsDefault.default,
mini: _parse.mini
});
const evaluate = async (code)=>{
const shapeshifted = _shapeshifterDefault.default(code); // transform syntactically correct js code to semantically usable code
// console.log('shapeshifted', shapeshifted);
_drawMjs.cleanup();
_uiMjs.cleanup();
let evaluated = await eval(shapeshifted);
if (typeof evaluated === 'function') evaluated = evaluated();
if (typeof evaluated === 'string') evaluated = _strudelMjs.withLocationOffset(_parse.minify(evaluated), {
start: {
line: 1,
column: -1
}
});
if (evaluated?.constructor?.name !== 'Pattern') {
const message = `got "${typeof evaluated}" instead of pattern`;
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
@@ -42153,8 +42147,6 @@ parcelHelpers.export(exports, "invert", ()=>invert
);
parcelHelpers.export(exports, "inv", ()=>inv
);
parcelHelpers.export(exports, "withLocationOffset", ()=>withLocationOffset
);
var _fractionMjs = require("./fraction.mjs");
var _fractionMjsDefault = parcelHelpers.interopDefault(_fractionMjs);
var _ramda = require("ramda"); // will remove this as soon as compose is implemented here
@@ -42382,7 +42374,19 @@ class Pattern {
})
);
}
withLocation(location) {
withLocation(start, end) {
const location = {
start: {
line: start[0],
column: start[1],
offset: start[2]
},
end: {
line: end[0],
column: end[1],
offset: end[2]
}
};
return this._withContext((context)=>{
const locations = (context.locations || []).concat([
location
@@ -42393,6 +42397,42 @@ class Pattern {
};
});
}
withMiniLocation(start1, end1) {
const offset = {
start: {
line: start1[0],
column: start1[1],
offset: start1[2]
},
end: {
line: end1[0],
column: end1[1],
offset: end1[2]
}
};
return this._withContext((context)=>{
let locations = context.locations || [];
locations = locations.map(({ start , end })=>{
const colOffset = start.line === 1 ? offset.start.column : 0;
return {
start: {
...start,
line: start.line - 1 + (offset.start.line - 1) + 1,
column: start.column - 1 + colOffset
},
end: {
...end,
line: end.line - 1 + (offset.start.line - 1) + 1,
column: end.column - 1 + colOffset
}
};
});
return {
...context,
locations
};
});
}
withValue(func) {
// Returns a new pattern, with the function applied to the value of
// each event. It has the alias 'fmap'.
@@ -43118,31 +43158,6 @@ Pattern.prototype.define('bypass', (pat)=>pat.bypass(on)
patternified: true,
composable: true
});
// this is wrapped around mini patterns to offset krill parser location into the global js code space
function withLocationOffset(pat, offset) {
return pat._withContext((context)=>{
let locations = context.locations || [];
locations = locations.map(({ start , end })=>{
const colOffset = start.line === 1 ? offset.start.column : 0;
return {
start: {
...start,
line: start.line - 1 + (offset.start.line - 1) + 1,
column: start.column - 1 + colOffset
},
end: {
...end,
line: end.line - 1 + (offset.start.line - 1) + 1,
column: end.column - 1 + colOffset
}
};
});
return {
...context,
locations
};
});
}
},{"./fraction.mjs":"8Ovmi","ramda":"10uzi","./util.mjs":"9Z602","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"8Ovmi":[function(require,module,exports) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
@@ -59647,7 +59662,10 @@ Pattern.prototype.midi = function(output, channel = 1) {
if (!_tone.isNote(note)) throw new Error('not a note: ' + note);
if (!WebMidi.enabled) throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`);
if (!WebMidi.outputs.length) throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
const device = output ? outputByName(output) : WebMidi.outputs[0];
let device;
if (typeof output === 'number') device = WebMidi.outputs[output];
else if (typeof output === 'string') device = outputByName(output);
else device = WebMidi.outputs[0];
if (!device) throw new Error(`🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs.map((o)=>`'${o.name}'`
).join(' | ')}`);
// console.log('midi', value, output);
@@ -136381,6 +136399,7 @@ _strudelMjs.Pattern.prototype.pianoroll = function({ timeframe =10 , inactive ='
const height = h / midiRange;
this.draw((ctx, events, t)=>{
ctx.fillStyle = background;
ctx.clearRect(0, 0, w, h);
ctx.fillRect(0, 0, w, h);
events.forEach((event)=>{
const isActive = event.whole.begin <= t && event.whole.end >= t;
@@ -136413,7 +136432,7 @@ const getDrawContext = (id = 'test-canvas')=>{
canvas.id = id;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style = 'pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0';
canvas.style = 'pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0;z-index:5';
document.body.prepend(canvas);
}
return canvas.getContext('2d');
@@ -136455,6 +136474,8 @@ parcelHelpers.export(exports, "hideHeader", ()=>hideHeader
);
parcelHelpers.export(exports, "backgroundImage", ()=>backgroundImage
);
parcelHelpers.export(exports, "cleanup", ()=>cleanup
);
var _tone = require("tone");
const hideHeader = ()=>{
document.getElementById('header').style = 'display:none';
@@ -136493,6 +136514,11 @@ const backgroundImage = function(src, animateOptions = {
})
);
};
const cleanup = ()=>{
const container = document.getElementById('code');
container.style = '';
container.className = 'grow relative'; // has to match App.tsx
};
},{"tone":"2tCfN","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"fYZxP":[function(require,module,exports) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
@@ -136520,7 +136546,8 @@ const isNote = (name)=>/^[a-gC-G][bs]?[0-9]$/.test(name)
;
const addLocations = true;
const addMiniLocations = true;
exports.default = (code)=>{
exports.default = (_code)=>{
const { code , addReturn } = wrapAsync(_code);
const ast = _indexJs.parseScriptWithLocation(code);
const artificialNodes = [];
const parents = [];
@@ -136530,27 +136557,24 @@ exports.default = (code)=>{
const isSynthetic = parents.some((p)=>artificialNodes.includes(p)
);
if (isSynthetic) return node;
// replace template string `xxx` with 'xxx'.m
if (isBackTickString(node)) {
const minified = getMinified(node.elements[0].rawValue);
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
}
// replace template string `xxx` with mini(`xxx`)
if (isBackTickString(node)) return minifyWithLocation(node, node, ast.locations, artificialNodes);
// allows to use top level strings, which are normally directives... but we don't need directives
if (node.type === 'Script' && node.directives.length === 1 && !node.statements.length) {
const minified = getMinified(node.directives[0].rawValue);
const wrapped = wrapLocationOffset(minified, node.directives[0], ast.locations, artificialNodes);
return new _shiftAst.Script({
if (node.directives?.length === 1 && !node.statements?.length) {
const str = new _shiftAst.LiteralStringExpression({
value: node.directives[0].rawValue
});
const wrapped = minifyWithLocation(str, node.directives[0], ast.locations, artificialNodes);
return {
...node,
directives: [],
statements: [
wrapped
]
});
}
// replace double quote string "xxx" with 'xxx'.m
if (isStringWithDoubleQuotes(node, ast.locations, code)) {
const minified = getMinified(node.value);
return wrapLocationOffset(minified, node, ast.locations, artificialNodes);
};
}
// replace double quote string "xxx" with mini('xxx')
if (isStringWithDoubleQuotes(node, ast.locations, code)) return minifyWithLocation(node, node, ast.locations, artificialNodes);
// operator overloading => still not done
const operators = {
'*': 'fast',
@@ -136580,7 +136604,6 @@ exports.default = (code)=>{
if (node.type === 'CallExpression' && node.callee.name === 'pure') {
const literal = node.arguments[0];
// const value = literal[{ LiteralNumericExpression: 'value', LiteralStringExpression: 'name' }[literal.type]];
// console.log('value',value);
return reifyWithLocation(literal, node.arguments[0], ast.locations, artificialNodes);
}
// replace pseudo note variables
@@ -136598,34 +136621,61 @@ exports.default = (code)=>{
name: 'silence'
});
}
if (addLocations && node.type === 'LiteralStringExpression' && isMarkable) // console.log('add', node);
if (addLocations && [
'LiteralStringExpression' /* , 'LiteralNumericExpression' */
].includes(node.type) && isMarkable) // TODO: to make LiteralNumericExpression work, we need to make sure we're not inside timeCat...
return reifyWithLocation(node, node, ast.locations, artificialNodes);
if (!addMiniLocations) return wrapFunction('reify', node);
// mini notation location handling
const miniFunctions = [
'mini',
'm'
];
const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset';
if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name) && !isAlreadyWrapped) {
// mini('c3')
if (node.arguments.length > 1) {
// TODO: transform mini(...args) to cat(...args.map(mini)) ?
console.warn('multi arg mini locations not supported yet...');
return node;
}
return wrapLocationOffset(node, node.arguments, ast.locations, artificialNodes);
}
if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property) && !isAlreadyWrapped) // 'c3'.mini or 'c3'.m
return wrapLocationOffset(node, node.object, ast.locations, artificialNodes);
if (addMiniLocations) return addMiniNotationLocations(node, ast.locations, artificialNodes);
return node;
},
leave () {
parents.pop();
}
});
return _shiftCodegenDefault.default(shifted);
// add return to last statement (because it's wrapped in an async function artificially)
addReturn(shifted);
const generated = _shiftCodegenDefault.default(shifted);
return generated;
};
function wrapAsync(code) {
// wrap code in async to make await work on top level => this will create 1 line offset to locations
// this is why line offset is -1 in getLocationObject calls below
code = `(async () => {
${code}
})()`;
const addReturn = (ast)=>{
const body = ast.statements[0].expression.callee.body; // actual code ast inside async function body
body.statements = body.statements.slice(0, -1).concat([
new _shiftAst.ReturnStatement({
expression: body.statements.slice(-1)[0]
})
]);
};
return {
code,
addReturn
};
}
function addMiniNotationLocations(node, locations, artificialNodes) {
const miniFunctions = [
'mini',
'm'
];
// const isAlreadyWrapped = parent?.type === 'CallExpression' && parent.callee.name === 'withLocationOffset';
if (node.type === 'CallExpression' && miniFunctions.includes(node.callee.name)) {
// mini('c3')
if (node.arguments.length > 1) {
// TODO: transform mini(...args) to cat(...args.map(mini)) ?
console.warn('multi arg mini locations not supported yet...');
return node;
}
const str = node.arguments[0];
return minifyWithLocation(str, str, locations, artificialNodes);
}
if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property)) // 'c3'.mini or 'c3'.m
return minifyWithLocation(node.object, node, locations, artificialNodes);
return node;
}
function wrapFunction(name, ...args) {
return new _shiftAst.CallExpression({
callee: new _shiftAst.IdentifierExpression({
@@ -136634,14 +136684,6 @@ function wrapFunction(name, ...args) {
arguments: args
});
}
function getMinified(value) {
return new _shiftAst.StaticMemberExpression({
object: new _shiftAst.LiteralStringExpression({
value
}),
property: 'm'
});
}
function isBackTickString(node) {
return node.type === 'TemplateExpression' && node.elements.length === 1;
}
@@ -136673,145 +136715,64 @@ function canBeOverloaded(node) {
return node.type === 'IdentifierExpression' && isNote(node.name) || isPatternFactory(node);
// TODO: support sequence(c3).transpose(3).x.y.z
}
// turn node into withLocationOffset(node, location)
function wrapLocationOffset(node, stringNode, locations, artificialNodes) {
// console.log('wrapppp', stringNode);
const expression = {
type: 'CallExpression',
callee: {
type: 'IdentifierExpression',
name: 'withLocationOffset'
},
arguments: [
node,
getLocationObject(stringNode, locations)
]
};
artificialNodes.push(expression);
// console.log('wrapped', codegen(expression));
return expression;
}
// turns node in reify(value).withLocation(location), where location is the node's location in the source code
// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active
function reifyWithLocation(literalNode, node, locations, artificialNodes) {
const args = getLocationArguments(node, locations);
const withLocation = new _shiftAst.CallExpression({
callee: new _shiftAst.StaticMemberExpression({
object: wrapFunction('reify', literalNode),
property: 'withLocation'
}),
arguments: [
getLocationObject(node, locations)
]
arguments: args
});
artificialNodes.push(withLocation);
return withLocation;
}
// returns ast for source location object
function getLocationObject(node, locations) {
/*const locationAST = parseScript(
"x=" + JSON.stringify(ast.locations.get(node))
).statements[0].expression.expression;
console.log("locationAST", locationAST);*/ /*const callAST = parseScript(
`reify(${node.name}).withLocation(${JSON.stringify(
ast.locations.get(node)
)})`
).statements[0].expression;*/ const loc = locations.get(node);
return {
type: 'ObjectExpression',
properties: [
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'start'
},
expression: {
type: 'ObjectExpression',
properties: [
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'line'
},
expression: {
type: 'LiteralNumericExpression',
value: loc.start.line
}
},
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'column'
},
expression: {
type: 'LiteralNumericExpression',
value: loc.start.column
}
},
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'offset'
},
expression: {
type: 'LiteralNumericExpression',
value: loc.start.offset
}
},
]
}
},
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'end'
},
expression: {
type: 'ObjectExpression',
properties: [
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'line'
},
expression: {
type: 'LiteralNumericExpression',
value: loc.end.line
}
},
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'column'
},
expression: {
type: 'LiteralNumericExpression',
value: loc.end.column
}
},
{
type: 'DataProperty',
name: {
type: 'StaticPropertyName',
value: 'offset'
},
expression: {
type: 'LiteralNumericExpression',
value: loc.end.offset
}
},
]
}
},
]
};
// turns node in reify(value).withLocation(location), where location is the node's location in the source code
// with this, the reified pattern can pass its location to the event, to know where to highlight when it's active
function minifyWithLocation(literalNode, node, locations, artificialNodes) {
const args = getLocationArguments(node, locations);
const withLocation = new _shiftAst.CallExpression({
callee: new _shiftAst.StaticMemberExpression({
object: wrapFunction('mini', literalNode),
property: 'withMiniLocation'
}),
arguments: args
});
artificialNodes.push(withLocation);
return withLocation;
}
function getLocationArguments(node, locations) {
const loc = locations.get(node);
return [
new _shiftAst.ArrayExpression({
elements: [
new _shiftAst.LiteralNumericExpression({
value: loc.start.line - 1
}),
new _shiftAst.LiteralNumericExpression({
value: loc.start.column
}),
new _shiftAst.LiteralNumericExpression({
value: loc.start.offset
}),
]
}),
new _shiftAst.ArrayExpression({
elements: [
new _shiftAst.LiteralNumericExpression({
value: loc.end.line - 1
}),
new _shiftAst.LiteralNumericExpression({
value: loc.end.column
}),
new _shiftAst.LiteralNumericExpression({
value: loc.end.offset
}),
]
}),
];
}
},{"./shift-parser/index.js":"1kFzJ","./shift-traverser":"bogJs","shift-ast":"ig2Ca","shift-codegen":"1GOrI","../../strudel.mjs":"ggZqJ","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"1kFzJ":[function(require,module,exports) {
@@ -169941,15 +169902,18 @@ function patternifyAST(ast) {
return ast.source_;
}
const { start , end } = ast.location_;
// should we really parse as number here? strings wont be fareyed...
const value = !isNaN(Number(ast.source_)) ? Number(ast.source_) : ast.source_;
// return ast.source_;
// the following line expects the shapeshifter to wrap this in withLocationOffset
// the following line expects the shapeshifter append .withMiniLocation
// because location_ is only relative to the mini string, but we need it relative to whole code
return pure(value).withLocation({
start,
end
});
return pure(value).withLocation([
start.line,
start.column,
start.offset
], [
end.line,
end.column,
end.offset
]);
}
return patternifyAST(ast.source_);
case 'stretch':
@@ -171967,18 +171931,17 @@ function useCycle(props) {
_tone.getTransport().start('+0.1');
};
const stop = ()=>{
console.log('stop');
setStarted(false);
_tone.getTransport().pause();
setStarted(false);
};
const toggle = ()=>started ? stop() : start()
;
return {
start,
stop,
setStarted,
onEvent,
started,
setStarted,
toggle,
query,
activeCycle
@@ -183344,4 +183307,4 @@ exports.default = cx;
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["3uVTb"], "3uVTb", "parcelRequire94c2")
//# sourceMappingURL=index.f0b57381.js.map
//# sourceMappingURL=index.ea7b3aa1.js.map
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -11,6 +11,6 @@
<body>
<div id="root"></div>
<noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/tutorial/index.f0b57381.js" defer=""></script>
<script src="/tutorial/index.ea7b3aa1.js" defer=""></script>
</body>
</html>