diff --git a/repl/krill-parser.js b/repl/krill-parser.js index 568fc08b..56fc1820 100644 --- a/repl/krill-parser.js +++ b/repl/krill-parser.js @@ -1936,6 +1936,7 @@ function peg$parse(input, options) { this.type_ = "element"; this.source_ = source; this.options_ = options; + this.location_ = location(); } var CommandStub = function(name, options) diff --git a/repl/krill.pegjs b/repl/krill.pegjs index 132a82d7..84e7e8df 100644 --- a/repl/krill.pegjs +++ b/repl/krill.pegjs @@ -24,6 +24,7 @@ this.type_ = "element"; this.source_ = source; this.options_ = options; + this.location_ = location(); } var CommandStub = function(name, options) diff --git a/repl/src/parse.ts b/repl/src/parse.ts index e1904cad..c1ff3a9f 100644 --- a/repl/src/parse.ts +++ b/repl/src/parse.ts @@ -1,6 +1,7 @@ import * as krill from '../krill-parser'; import * as strudel from '../../strudel.mjs'; import { Scale, Note, Interval } from '@tonaljs/tonal'; +import { addMiniLocations } from './shapeshifter'; const { pure, Pattern, Fraction, stack, slowcat, sequence, timeCat, silence } = strudel; @@ -91,7 +92,18 @@ export function patternifyAST(ast: any): any { return silence; } if (typeof ast.source_ !== 'object') { - return ast.source_; + if (!addMiniLocations) { + return ast.source_; + } + if (!ast.location_) { + console.warn('no location for', ast); + return ast.source_; + } + const { start, end } = ast.location_; + // return ast.source_; + // the following line expects the shapeshifter to wrap this in withLocationOffset + // because location_ is only relative to the mini string, but we need it relative to whole code + return pure(ast.source_).withLocation({ start, end }); } return patternifyAST(ast.source_); case 'stretch': diff --git a/repl/src/shapeshifter.js b/repl/src/shapeshifter.js index 789645c1..4e4d3dbf 100644 --- a/repl/src/shapeshifter.js +++ b/repl/src/shapeshifter.js @@ -10,6 +10,7 @@ const { Pattern } = strudel; const isNote = (name) => /^[a-gC-G][bs]?[0-9]$/.test(name); const addLocations = true; +export const addMiniLocations = false; export default (code) => { const ast = parseScriptWithLocation(code); @@ -44,6 +45,25 @@ export default (code) => { // console.log('add', node); return addPureWithLocation(node.value, node, ast.locations, nodesWithLocation); } + if (!addMiniLocations) { + return 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, nodesWithLocation); + } + if (node.type === 'StaticMemberExpression' && miniFunctions.includes(node.property) && !isAlreadyWrapped) { + // 'c3'.mini or 'c3'.m + return wrapLocationOffset(node, node.object, ast.locations, nodesWithLocation); + } return node; }, leave() { @@ -53,6 +73,22 @@ export default (code) => { return codegen(shifted); }; +// turn node into withLocationOffset(node, location) +function wrapLocationOffset(node, stringNode, locations, nodesWithLocation) { + // console.log('wrapppp', stringNode); + const expression = { + type: 'CallExpression', + callee: { + type: 'IdentifierExpression', + name: 'withLocationOffset', + }, + arguments: [node, getLocationObject(stringNode, locations)], + }; + nodesWithLocation.push(expression); + // console.log('wrapped', codegen(expression)); + return expression; +} + // turns node in pure(value).withLocation(location), where location is the node's location in the source code // with this, the pure pattern can pass its location to the event, to know where to highlight when it's active function addPureWithLocation(value, node, locations, nodesWithLocation) { diff --git a/strudel.mjs b/strudel.mjs index f07faa39..6c6825a8 100644 --- a/strudel.mjs +++ b/strudel.mjs @@ -872,10 +872,33 @@ Pattern.prototype.bootstrap = () => { return bootstrapped; } +// this is wrapped around mini patterns to offset krill parser location into the global js code space +function withLocationOffset(pat, offset) { + // console.log('with offfset',pat,offset); + return pat.fmap((value) => { + value = typeof value === 'object' && !Array.isArray(value) ? value : { value }; + const locations = (value.locations || []).map(({ start, end }) => ({ + start: { + ...start, + line: start.line - 1 + (offset.start.line - 1) + 1, + column: start.column - 1 + offset.start.column, + }, + end: { + ...end, + line: end.line - 1 + (offset.start.line - 1) + 1, + column: end.column - 1 + offset.start.column, + }, + })); + // console.log(value.value, 'location', locations[0]); + return {...value, locations } + }); +} + export {Fraction, TimeSpan, Hap, Pattern, pure, stack, slowcat, fastcat, cat, timeCat, sequence, polymeter, pm, polyrhythm, pr, reify, silence, fast, slow, early, late, rev, add, sub, mul, div, union, every, when, off, jux, append, superimpose, - struct, mask, invert, inv + struct, mask, invert, inv, + withLocationOffset }