%{ const root = (type, data) => ({__prototype: "MesaureLayout", type, data}); const singleLayout = n => ({__prototype: "SingleMLayout", measure: Number(n)}); const blockLayout = seq => ({__prototype: "BlockMLayout", seq}); const voltaBlock = (times, body, alternates) => ({__prototype: "VoltaMLayout", times: Number(times), body, alternates}); const abaBlock = (main, rest) => ({__prototype: "ABAMLayout", main, rest}); const segment = n => ({segment: true, length: Number(n)}); const alternates = items => items.map(item => { if (item.__prototype === "BlockMLayout") return item.seq; return [item]; }); const range = (start, end) => { start = Number(start); end = Number(end); if (!(end >= start)) throw new Error(`invalid measure range: ${start}..${end}`); return Array(end + 1 - start).fill(0).map((_, i) => singleLayout(start + i)); }; const serializeSeq = (item, options) => { if (item.segment) { const index = options.index; options.index += item.length; return Array(item.length).fill(0).map((_, i) => singleLayout(index + i)); } return [serialize(item, options)]; }; const serialize = (item, options = {index: 1}) => { const speard = seq => [].concat(...seq.map(it => serializeSeq(it, options))); switch (item.__prototype) { case "BlockMLayout": item.seq = speard(item.seq); break; case "VoltaMLayout": item.body = speard(item.body); item.alternates = item.alternates && item.alternates.map(speard); break; case "ABAMLayout": item.main = serialize(item.main, options); item.rest = speard(item.rest); break; } return item; }; %} %lex %option flex unicode A [a-z] N [1-9] N0 [0-9] UNSIGNED {N}{N0}* WORD {A}+ SPECIAL [*,\[\]<>{}] %% \s+ {} // spaces {SPECIAL} return yytext; {UNSIGNED} return 'UNSIGNED' {WORD}":" return yytext ".." return yytext <> return 'EOF'; /lex %start start_symbol %% start_symbol : measure_layout EOF { return $1; } ; measure_layout : index_wise_measure_layout {$$ = root(null, $1);} | 'i:' index_wise_measure_layout {$$ = root("index-wise", $2);} | 's:' segment_wise_measure_layout {$$ = root("segment-wise", serialize($2));} ; index_wise_measure_layout : iw_sequence { if ($1.length === 1 && $1[0].__prototype === "BlockMLayout") $$ = $1[0]; else $$ = blockLayout($1); } ; iw_sequence : iw_item {$$ = [$1];} | range {$$ = $1;} | iw_sequence ',' iw_item {$$ = [...$1, $3];} | iw_sequence ',' range {$$ = [...$1, ...$3];} ; range : UNSIGNED '..' UNSIGNED {$$ = range($1, $3);} ; iw_item : single {$$ = $1;} | iw_block_item {$$ = $1;} | iw_volta {$$ = $1;} | iw_aba {$$ = $1;} ; single : UNSIGNED {$$ = singleLayout($1);} ; iw_block_item : iw_block {$$ = blockLayout($1);} ; iw_block : '[' iw_sequence ']' {$$ = $2;} ; iw_volta : UNSIGNED '*' iw_block iw_optional_alternates {$$ = voltaBlock($1, $3, $4);} ; iw_optional_alternates : %empty {$$ = null;} | iw_alternates {$$ = $1;} ; iw_alternates : '{' iw_sequence '}' {$$ = alternates($2);} ; iw_aba : '<' iw_item ',' iw_sequence '>' {$$ = abaBlock($2, $4);} ; segment_wise_measure_layout : sw_sequence { if ($1.length === 1 && $1[0].__prototype === "BlockMLayout") $$ = $1[0]; else $$ = blockLayout($1); } ; sw_sequence : sw_item {$$ = [$1];} | sw_sequence sw_item {$$ = [...$1, $2];} ; sw_item : segment {$$ = blockLayout([$1]);} | sw_block_item {$$ = $1;} | sw_volta {$$ = $1;} | sw_aba {$$ = $1;} ; segment : UNSIGNED {$$ = segment($1);} ; sw_block_item : sw_block {$$ = blockLayout($1);} ; sw_block : '[' sw_sequence ']' {$$ = $2;} ; sw_volta : UNSIGNED '*' sw_block sw_optional_alternates {$$ = voltaBlock($1, $3, $4);} ; sw_optional_alternates : %empty {$$ = null;} | sw_alternates {$$ = $1;} ; sw_alternates : '{' sw_sequence '}' {$$ = alternates($2);} ; sw_aba : '<' sw_item sw_sequence '>' {$$ = abaBlock($2, $3);} ;