diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index 44ab07f1..98c21a11 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -35,18 +35,18 @@ const right = function (n, x) { return result; }; -const _bjork = function (n, x) { +const _bjorklund = function (n, x) { const [ons, offs] = n; - return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x))); + return Math.min(ons, offs) <= 1 ? [n, x] : _bjorklund(...(ons > offs ? left(n, x) : right(n, x))); }; -export const bjork = function (ons, steps) { +export const bjorklund = function (ons, steps) { const inverted = ons < 0; const absOns = Math.abs(ons); const offs = steps - absOns; const ones = Array(absOns).fill([1]); const zeros = Array(offs).fill([0]); - const result = _bjork([absOns, offs], [ones, zeros]); + const result = _bjorklund([absOns, offs], [ones, zeros]); const pattern = flatten(result[1][0]).concat(flatten(result[1][1])); return inverted ? pattern.map((x) => 1 - x) : pattern; }; @@ -128,7 +128,7 @@ export const bjork = function (ons, steps) { */ const _euclidRot = function (pulses, steps, rotation) { - const b = bjork(pulses, steps); + const b = bjorklund(pulses, steps); if (rotation) { return rotate(b, -rotation); } @@ -139,7 +139,7 @@ export const euclid = register('euclid', function (pulses, steps, pat) { return pat.struct(_euclidRot(pulses, steps, 0)); }); -export const e = register('e', function (euc, pat) { +export const bjork = register('bjork', function (euc, pat) { if (!Array.isArray(euc)) { euc = [euc]; } @@ -216,6 +216,6 @@ export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, s * .pan(sine.slow(8)) */ export const { euclidish, eish } = register(['euclidish', 'eish'], function (pulses, steps, perc, pat) { - const morphed = _morph(bjork(pulses, steps), new Array(pulses).fill(1), perc); + const morphed = _morph(bjorklund(pulses, steps), new Array(pulses).fill(1), perc); return pat.struct(morphed).setSteps(steps); }); diff --git a/packages/core/test/euclid.test.js b/packages/core/test/euclid.test.js index a33ec951..6169abd7 100644 --- a/packages/core/test/euclid.test.js +++ b/packages/core/test/euclid.test.js @@ -1,14 +1,14 @@ -import { bjork } from '../euclid.mjs'; +import { bjorklund } from '../euclid.mjs'; import { describe, expect, it } from 'vitest'; import { fastcat } from '../pattern.mjs'; -describe('bjork', () => { - it('should apply bjorklund to ons and steps', () => { - expect(bjork(3, 8)).toStrictEqual([1, 0, 0, 1, 0, 0, 1, 0]); - expect(bjork(-3, 8)).toStrictEqual([0, 1, 1, 0, 1, 1, 0, 1]); - expect(bjork(8, 8)).toStrictEqual([1, 1, 1, 1, 1, 1, 1, 1]); - expect(bjork(-8, 8)).toStrictEqual([0, 0, 0, 0, 0, 0, 0, 0]); - expect(bjork(5, 8)).toStrictEqual([1, 0, 1, 1, 0, 1, 1, 0]); +describe('bjorklund', () => { + it('should apply bjorklundlund to ons and steps', () => { + expect(bjorklund(3, 8)).toStrictEqual([1, 0, 0, 1, 0, 0, 1, 0]); + expect(bjorklund(-3, 8)).toStrictEqual([0, 1, 1, 0, 1, 1, 0, 1]); + expect(bjorklund(8, 8)).toStrictEqual([1, 1, 1, 1, 1, 1, 1, 1]); + expect(bjorklund(-8, 8)).toStrictEqual([0, 0, 0, 0, 0, 0, 0, 0]); + expect(bjorklund(5, 8)).toStrictEqual([1, 0, 1, 1, 0, 1, 1, 0]); }); }); diff --git a/packages/mondo/mondo.mjs b/packages/mondo/mondo.mjs index b879f24b..b87a8b58 100644 --- a/packages/mondo/mondo.mjs +++ b/packages/mondo/mondo.mjs @@ -21,13 +21,14 @@ export class MondoParser { close_curly: /^\}/, number: /^-?[0-9]*\.?[0-9]+/, // before pipe! // TODO: better error handling when "-" is used as rest, e.g "s [- bd]" - op: /^[*/:!@%?+-]|^\.{2}/, // * / : ! @ % ? .. + op: /^[*/:!@%?+\-&]|^\.{2}/, // * / : ! @ % ? .. // dollar: /^\$/, pipe: /^#/, stack: /^[,$]/, or: /^[|]/, plain: /^[a-zA-Z0-9-~_^#]+/, }; + op_precedence = [['*', '/', ':', '!', '@', '%', '?', '+', '-', '..'], ['&']]; // matches next token next_token(code, offset = 0) { for (let type in this.token_types) { @@ -150,9 +151,9 @@ export class MondoParser { } return children; } - desugar_ops(children) { + desugar_ops(children, types) { while (true) { - let opIndex = children.findIndex((child) => child.type === 'op'); + let opIndex = children.findIndex((child) => child.type === 'op' && types.includes(child.value)); if (opIndex === -1) break; const op = { type: 'plain', value: children[opIndex].value }; if (opIndex === children.length - 1) { @@ -263,8 +264,10 @@ export class MondoParser { // the type we've removed before splitting needs to be added back children = [{ type: 'plain', value: type }, ...children]; } - children = this.desugar_ops(children); - // children = this.desugar_pipes(children, (children) => this.desugar_dollars(children)); + // for each precendence group, call desugar_ops once + this.op_precedence.forEach((ops) => { + children = this.desugar_ops(children, ops); + }); children = this.desugar_pipes(children); return children; }), diff --git a/packages/mondo/test/mondo.test.mjs b/packages/mondo/test/mondo.test.mjs index 6393b10f..a02c6fec 100644 --- a/packages/mondo/test/mondo.test.mjs +++ b/packages/mondo/test/mondo.test.mjs @@ -117,6 +117,7 @@ describe('mondo sugar', () => { it('should desugar x:y', () => expect(desguar('x:y')).toEqual('(: y x)')); it('should desugar x:y:z', () => expect(desguar('x:y:z')).toEqual('(: z (: y x))')); it('should desugar x:y*x', () => expect(desguar('bd:0*2')).toEqual('(* 2 (: 0 bd))')); + it('should desugar x&y:z', () => expect(desguar('bd&3:8')).toEqual('(& (: 8 3) bd)')); it('should desugar a..b', () => expect(desguar('0..2')).toEqual('(.. 2 0)')); /* it('should desugar x $ y', () => expect(desguar('x $ y')).toEqual('(x y)')); it('should desugar x $ y z', () => expect(desguar('x $ y z')).toEqual('(x (y z))')); diff --git a/packages/mondough/mondough.mjs b/packages/mondough/mondough.mjs index e2c6dc9a..a230c3cb 100644 --- a/packages/mondough/mondough.mjs +++ b/packages/mondough/mondough.mjs @@ -11,6 +11,7 @@ import { chooseIn, degradeBy, silence, + bjork, } from '@strudel/core'; import { registerLanguage } from '@strudel/transpiler'; import { MondoRunner } from 'mondolang'; @@ -40,6 +41,7 @@ lib['!'] = replicate; lib['@'] = expand; lib['%'] = pace; lib['?'] = degradeBy; // todo: default 0.5 not working.. +lib['&'] = bjork; lib[':'] = tail; lib['..'] = range; lib['def'] = () => silence;