jevstrudel.git / packages / tidal / tidal.mjs
1import { reify } from '@strudel/core';
2import { m } from '@strudel/mini';
3import { loadParser, evaluate } from 'hs2js';
4
5function getInfixOperators() {
6  let operators = {
7    '>': 'set',
8    '#': 'set',
9    '+': 'add',
10    '-': 'sub',
11    '*': 'mul',
12    '/': 'div',
13  };
14  let alignments = {
15    in: (s) => '|' + s,
16    out: (s) => s + '|',
17    mix: (s) => '|' + s + '|',
18  };
19  let ops = {};
20  Object.entries(operators).forEach(([o, name]) => {
21    // operator without alignment
22    ops[o] = (l, r) => reify(l)[name](reify(r));
23    Object.entries(alignments).forEach(([a, getSymbol]) => {
24      // get symbol with alignment
25      let symbol = getSymbol(o);
26      ops[symbol] = (l, r) => reify(l)[name][a](reify(r));
27    });
28  });
29  ops['~>'] = (l, r) => reify(l).late(reify(r));
30  ops['<~'] = (l, r) => reify(l).early(reify(r));
31  ops['<$>'] = (l, r) => reify(r).fmap(l).outerJoin(); // is this right?
32  return ops;
33}
34const ops = getInfixOperators();
35
36export async function initTidal() {
37  // TODO: implement this in regular land
38  window.d1 = (pat) => pat.p('d1');
39  window.d2 = (pat) => pat.p('d2');
40  window.d3 = (pat) => pat.p('d3');
41  window.d4 = (pat) => pat.p('d4');
42  window.d5 = (pat) => pat.p('d5');
43  window.d6 = (pat) => pat.p('d6');
44  window.d7 = (pat) => pat.p('d7');
45  window.d8 = (pat) => pat.p('d8');
46  window.d9 = (pat) => pat.p('d9');
47  return loadParser();
48}

offset is expected to be passed in from transpiler

  1. acorn parses JS to find location of tidal call
  2. haskell-tree-sitter calls "string" function with node (including location)
  3. m function sets locations for individual mini notation atom

so the location for a mini notation atom is:

js offset + hs offset + atom offset

60export function tidal(code, offset = 0) {
61  if (Array.isArray(code)) {
62    code = code.join('');
63  }
64  return evaluate(code, window, {
65    ...ops,
66    string: (node) => {
67      // parses strings as mini notation and passes location
68      const str = node.text.slice(1, -1);
69      const col = node.startIndex + offset;
70      return m(str, col);
71    },
72  });
73}