From 64a6dacc1ed8975ff99c7022aac1495db412fb79 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 26 Mar 2025 09:27:02 +0100 Subject: [PATCH] mondo: patternable function names --- packages/mondo/mondo.mjs | 19 ++++------- packages/mondough/mondough.mjs | 37 +++++++++++++++------- website/src/pages/learn/mondo-notation.mdx | 4 +-- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/mondo/mondo.mjs b/packages/mondo/mondo.mjs index 5419d15b..d031af4e 100644 --- a/packages/mondo/mondo.mjs +++ b/packages/mondo/mondo.mjs @@ -280,7 +280,7 @@ export class MondoParser { get_locations(code, offset = 0) { let walk = (ast, locations = []) => { if (ast.type === 'list') { - return ast.children.slice(1).forEach((child) => walk(child, locations)); + return ast.children.forEach((child) => walk(child, locations)); } if (ast.loc) { locations.push(ast.loc); @@ -335,17 +335,11 @@ export class MondoRunner { } // is list - // the first element is expected to be the function name - const first = ast.children[0]; - let name; - if (first?.type !== 'list') { - name = first.value; // regular function call e.g. (fast 2 (s bd)) - } else { - // dynamic function name e.g. "( 2 (s bd))" - name = this.evaluate(first); + if (!ast.children.length) { + throw new Error(`empty list`); } - if (name === 'lambda') { + if (ast.children[0].value === 'lambda') { const [_, args, body] = ast.children; const argNames = args.children.map((child) => child.value); return (x) => { @@ -356,8 +350,9 @@ export class MondoRunner { }; } + const args = ast.children.map((arg) => this.evaluate(arg, scope)); + // we could short circuit arg[0] if its plain... // evaluate args - const args = ast.children.slice(1).map((arg) => this.evaluate(arg, scope)); - return this.lib.call(name, args, scope); + return this.lib.call(args[0], args.slice(1), scope); } } diff --git a/packages/mondough/mondough.mjs b/packages/mondough/mondough.mjs index 081d054b..01247e78 100644 --- a/packages/mondough/mondough.mjs +++ b/packages/mondough/mondough.mjs @@ -11,7 +11,6 @@ import { chooseIn, degradeBy, silence, - isPattern, } from '@strudel/core'; import { registerLanguage } from '@strudel/transpiler'; import { MondoRunner } from '../mondo/mondo.mjs'; @@ -24,7 +23,10 @@ const arrayRange = (start, stop, step = 1) => ); const range = (max, min) => min.squeezeBind((a) => max.bind((b) => seq(...arrayRange(a, b)))); +let nope = (...args) => args[args.length - 1]; + let lib = {}; +lib['nope'] = nope; lib['-'] = silence; lib['~'] = silence; lib.curly = stepcat; @@ -43,15 +45,19 @@ lib['or'] = (...children) => chooseIn(...children); // always has structure but let runner = new MondoRunner({ call(name, args, scope) { - if (isPattern(name)) { - // patterned function name, e.g. "s bd . 2" - return name.fmap((fn) => fn(...args)).innerJoin(); + // name is expected to be a pattern of functions! + const first = name.firstCycle(true)[0]; + if (typeof first?.value !== 'function') { + throw new Error(`[mondough] "${first}" is not a function`); } - const fn = lib[name] || strudelScope[name]; - if (!fn) { - throw new Error(`[moundough]: unknown function "${name}"`); - } - return fn(...args); + return name + .fmap((fn) => { + if (typeof fn !== 'function') { + throw new Error(`[mondough] "${fn}" is not a function`); + } + return fn(...args); + }) + .innerJoin(); }, leaf(token, scope) { let { value, type } = token; @@ -59,14 +65,21 @@ let runner = new MondoRunner({ if (type === 'plain' && scope[value]) { return reify(scope[value]); // -> local scope has no location } - const [from, to] = token.loc; const variable = lib[value] ?? strudelScope[value]; + let pat; if (type === 'plain' && typeof variable !== 'undefined') { // problem: collisions when we want a string that happens to also be a variable name // example: "s sine" -> sine is also a variable - return reify(strudelScope[value]).withLoc(from, to); + pat = reify(variable); + } else { + pat = reify(value); } - return reify(value).withLoc(from, to); + + if (token.loc) { + pat = pat.withLoc(token.loc[0], token.loc[1]); + } + pat.foo = true; + return pat; }, }); diff --git a/website/src/pages/learn/mondo-notation.mdx b/website/src/pages/learn/mondo-notation.mdx index aa790498..2bc95234 100644 --- a/website/src/pages/learn/mondo-notation.mdx +++ b/website/src/pages/learn/mondo-notation.mdx @@ -14,7 +14,7 @@ Here's an example: <8 16>) . *2 + tune={`$ note (c2 .euclid <3 6 3> <8 16>) . *2 .s "sine" .add (note [0 <12 24>]*2) .dec(sine .range .2 2) .room .5 .lpf(sine/3.range 120 400) @@ -27,7 +27,7 @@ $ s [bd bd bd bd] .bank tr909.clip.5 .ply<1 [1 [2 4]]> $ s oh*4 .press .bank tr909 .speed.8 -.dec <.02 .05>*2 .(add (saw/8.range 0 1))`} +.dec (<.02 .05>*2 .add (saw/8.range 0 1))`} /> ## Mondo in the REPL