-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathly-pattern-parser.mjs
117 lines (93 loc) · 3.41 KB
/
ly-pattern-parser.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// https://peggyjs.org/documentation.html
// https://github.com/peggyjs/peggy
// https://github.com/peggyjs/peggy/tree/main/examples
import peggy from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm';
import { fetchText } from './utils.mjs';
export function getPatternsFromLy(lyFileContents) {
const patternRgx = /([a-z_]+) = \\drummode \{([^}]+)\}/gm;
let m;
const patterns = [];
do {
m = patternRgx.exec(lyFileContents);
if (m) {
const [_, name, contents] = m;
patterns.push({ name, contents: contents });
}
} while (m);
return patterns;
}
export async function parseLyPattern(lyPatternString) {
const definition = await fetchText('./grammars/ly.pegjs');
const parser = peggy.generate(definition, {
optimize: 'speed',
//optimize: 'size',
format: 'bare',
//format: 'es',
output: 'parser',
// output: 'ast',
// output: 'source',
grammarSource: definition,
cache: true,
//trace: true, // LISTS RULE APPLICATIONS
error: (stage, msg, loc, notes) => { console.log('ERROR', stage, msg, loc, notes); }, // NOT BEING CALLED?
warning: (stage, msg, loc, notes) => { console.log('WARN ', stage, msg, loc, notes); }, // NOT BEING CALLED?
//info: (stage, msg, loc, notes) => { console.log('INFO ', stage, msg, loc, notes); }, // WORKS
});
const output = parser.parse(lyPatternString, {
grammarSource: definition,
tracer: {
trace({ type, rule }) {
console.log('TRACER', type, rule);
}
}
});
return output;
}
// durations to dt
export const durationToDt = (dur) => {
switch (dur) {
case 32: return 1; //
case 16: return 2;
case 8: return 4;
case 4: return 8;
case 2: return 16;
case 1: return 32;
default: throw dur;
}
};
export async function lyPatternToAscii(lyPattern) {
const parsedLyPattern = await parseLyPattern(lyPattern);
let currentDur = 1;
let cursor = 0;
const pitches = {};
let pitchesNames = new Set();
for (let [kind, content, dur] of parsedLyPattern) {
//console.log(kind, content, dur);
if (kind === 'item') pitchesNames.add(content);
else if (kind === 'parallel') content.forEach(([kind2, content2, dur2]) => pitchesNames.add(content2));
if (dur != undefined) currentDur = durationToDt(dur);
cursor += currentDur;
}
pitchesNames = Array.from(pitchesNames);
pitchesNames.forEach((pn) => {
const arr = new Array(cursor);
arr.fill(false);
pitches[pn] = arr;
});
cursor = 0;
for (let [kind, content, dur] of parsedLyPattern) {
//console.log(kind, content, dur);
if (kind === 'item') pitches[content][cursor] = true;
else if (kind === 'parallel') content.forEach(([kind2, content2, dur2]) => { pitches[content2][cursor] = true; });
if (dur != undefined) currentDur = durationToDt(dur);
cursor += currentDur;
}
const maxPitchNameLength = pitchesNames.reduce((prev, curr) => Math.max(prev, curr.length), 0);
const ascii = Object.entries(pitches).map(([pn, arr]) => {
const pn2 = pn.padEnd(maxPitchNameLength);
const arr2 = arr.map((v) => v ? 'o' : ' ').join('');
return `${pn2}|${arr2}|`;
}).join('\n');
return `4/4 60
${ascii}`;
}