diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index aa5e1060f..a84934984 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1457,6 +1457,27 @@ export function stack(...pats) { return result; } + +/** + * The given items are played at the same time at the same length. + * + * @tags temporal + * @return {Pattern} + * @synonyms polyrhythm, pr + * @example + * mute_stack("g3", "b3", ["e4", "d4"]).note() + * // "g3,b3,[e4 d4]".note() + * + * @example + * // As a chained function: + * s("hh*4").mute_stack( + * note("c4(5,8)") + * ) + */ +export function mute_stack(...pats) { + return silence; +} + function _stackWith(func, pats) { pats = pats.map((pat) => (Array.isArray(pat) ? sequence(...pat) : reify(pat))); if (pats.length === 0) { diff --git a/packages/mondo/mondo.mjs b/packages/mondo/mondo.mjs index 0255ef78f..390e20477 100644 --- a/packages/mondo/mondo.mjs +++ b/packages/mondo/mondo.mjs @@ -5,6 +5,12 @@ This program is free software: you can redistribute it and/or modify it under th */ // evolved from https://garten.salat.dev/lisp/parser.html + +let recurse = 0 +function lrec(...args) { + recurse += 1 + console.info(recurse, ...args) +} export class MondoParser { // these are the tokens we expect token_types = { @@ -25,15 +31,16 @@ export class MondoParser { // dollar: /^\$/, pipe: /^#/, // Matches _$ or _$BASS - mute: /^_\$([a-zA-Z0-9_]+)?/, + mute_stack: /^_\$([a-zA-Z0-9_]+)?/, // Matches S$ or S$VOCALS - solo: /^S\$([a-zA-Z0-9_]+)?/, + solo_stack: /^S\$([a-zA-Z0-9_]+)?/, // stack: /^[,$]/, stack: /^,|^\$([a-zA-Z0-9_]+)?/, or: /^[|]/, plain: /^[a-zA-Z0-9-~_^#]+/, - + }; + solo_enabled = false; op_precedence = [['*', '/', ':', '!', '@', '%', '?', '+', '-', '..'], ['&']]; // matches next token next_token(code, offset = 0) { @@ -68,7 +75,7 @@ export class MondoParser { offset += token.value.length; tokens.push(token); } - console.info("TOKENS", tokens[0], tokens[1]) + lrec("TOKENS", tokens[0], tokens[1]) return tokens; } // take code, return abstract syntax tree @@ -80,19 +87,22 @@ export class MondoParser { while (this.tokens.length) { expressions.push(this.parse_expr()); } + let parsed = expressions[0] if (expressions.length === 0) { // empty case - return { type: 'list', children: [] }; - } + parsed = { type: 'list', children: [] }; + } else if (expressions.length > 1 || expressions[0].type !== 'list') // do we have multiple top level expressions or a single non list? - if (expressions.length > 1 || expressions[0].type !== 'list') { - return { + { + parsed = { type: 'list', children: this.desugar(expressions), }; } + + lrec("PARSED", parsed) // we have a single list - return expressions[0]; + return parsed; } // parses any valid expression parse_expr() { @@ -127,8 +137,11 @@ export class MondoParser { children = children.slice(splitIndex + 1); } chunks.push(children); + lrec("chunks", chunks) return chunks; } + + desugar_split(children, split_type, next) { const chunks = this.split_children(children, split_type); if (chunks.length === 1) { @@ -260,12 +273,15 @@ export class MondoParser { } return node; } + desugar(children, type) { // if type is given, the first element is expected to contain it as plain value // e.g. with (square a b, c), we want to split (a b, c) and ignore "square" children = type ? children.slice(1) : children; - children = this.desugar_split(children, 'stack', (children) => - this.desugar_split(children, 'or', (children) => { + + const desugar_split_children = (children) => { + return this.desugar_split(children, 'or', (children) => { + console.info("TYPE", type) // chunks of multiple args if (type) { // the type we've removed before splitting needs to be added back @@ -276,16 +292,34 @@ export class MondoParser { children = this.desugar_ops(children, ops); }); children = this.desugar_pipes(children); - console.info("STACK CHILDREN", children) + lrec("STACK CHILDREN", children) return children; - }), - ); - console.info("CHILDREN", children) + }) + + } + + console.info("PRE_CHILDREN", children) + // children = this.desugar_split(children, 'mute_stack', (children) => { + // const x = desugar_split_children(children) + // // lrec({x}) + // return x + // }) + + lrec({ children }) + + + children = this.desugar_split(children, 'stack', (children) => { + return desugar_split_children(children) + }) + + + lrec("CHILDREN", children) return children; } parse_list() { let node = this.parse_pair('open_list', 'close_list'); node.children = this.desugar(node.children); + lrec("node", node) return node; } parse_angle() { @@ -334,9 +368,8 @@ export function printAst(ast, compact = false, lvl = 0) { const br = compact ? '' : '\n'; const spaces = compact ? '' : Array(lvl).fill(' ').join(''); if (ast.type === 'list') { - return `${lvl ? br : ''}${spaces}(${ast.children.map((child) => printAst(child, compact, lvl + 1)).join(' ')}${ - ast.children.find((child) => child.type === 'list') ? `${br}${spaces})` : ')' - }`; + return `${lvl ? br : ''}${spaces}(${ast.children.map((child) => printAst(child, compact, lvl + 1)).join(' ')}${ast.children.find((child) => child.type === 'list') ? `${br}${spaces})` : ')' + }`; } return `${ast.value}`; } diff --git a/packages/mondough/mondough.mjs b/packages/mondough/mondough.mjs index a230c3cb9..f683401d6 100644 --- a/packages/mondough/mondough.mjs +++ b/packages/mondough/mondough.mjs @@ -49,10 +49,11 @@ lib['or'] = (...children) => chooseIn(...children); // always has structure but //lib['or'] = (...children) => chooseOut(...children); // "s oh*8.dec[.04 | .5]" is better but "dec[.04 | .5].s oh*8" has no struct function evaluator(node, scope) { - const { type } = node; - // node is list - if (type === 'list') { - const { children } = node; + const { type,children } = node; + // node is list] + if (type === 'list' && children.length) { + // const { children } = node; + const [name, ...args] = children; // some functions wont be reified to make sure they work (e.g. see extend below) if (typeof name === 'function') { @@ -65,6 +66,7 @@ function evaluator(node, scope) { const first = name.firstCycle(true)[0]; const type = typeof first?.value; if (type !== 'function') { + console.error("first", first) throw new Error(`[mondough] expected function, got "${first?.value}"`); } return name @@ -76,12 +78,16 @@ function evaluator(node, scope) { }) .innerJoin(); } + + console.info("NODE", node) // node is leaf let { value } = node; if (type === 'plain' && scope[value]) { return reify(scope[value]); // -> local scope has no location } const variable = lib[value] ?? strudelScope[value]; + + console.info("VARIABLE", variable) // problem: collisions when we want a string that happens to also be a variable name // example: "s sine" -> sine is also a variable let pat; @@ -107,6 +113,7 @@ export function mondo(code, offset = 0) { code = code.join(''); } const pat = runner.run(code, undefined, offset); + console.info("MONDO_PAT", pat) return pat.markcss('color: var(--caret,--foreground);text-decoration:underline'); }