Support both old and new sompyler AST slot names in parser

Pre-c3b51bc sompyler emits offset.stem_note/motif.stem_note/clause.note
while new sompyler emits line.stem_note/seq.note. Parser now accepts both
parentSlot values for stem_note, motif, and clause children so the app
works against both old and updated sompyler installations.

Also falls back to reading chain.clause directly from stem_note's children
when no stem_note.chain node is present (old format had no chain wrapper).
This commit is contained in:
c0dev0id
2026-06-27 20:08:31 +02:00
parent 859e62e143
commit b56d84befb
2 changed files with 38 additions and 8 deletions

View File

@@ -443,10 +443,10 @@ function buildOffset(node) {
unknownProps: collectUnknownProps(node.props, new Set(['tick'])),
};
for (const child of node.children) {
if (child.parentSlot === 'line' && child.slot === 'stem_note')
if ((child.parentSlot === 'line' || child.parentSlot === 'offset') && child.slot === 'stem_note')
offset.stemNotes.push(buildStemNote(child));
else if (child.parentSlot === 'line' && child.slot === 'motif')
offset.motifs.push({ label: child.positionals[0], chord: child.props.chord ?? null });
else if ((child.parentSlot === 'line' || child.parentSlot === 'offset') && child.slot === 'motif')
offset.motifs.push({ label: child.positionals[0] ?? child.props.label, chord: child.props.chord ?? null });
}
return offset;
}
@@ -465,7 +465,7 @@ function buildStemNote(node) {
weight: null,
chainText: chainNode?.props._tmp_string ?? '',
writeToName: writeToNode?.positionals[0] ?? null,
clauses: (chainNode?.children ?? [])
clauses: (chainNode ? chainNode.children : node.children)
.filter(c => c.parentSlot === 'chain' && c.slot === 'clause')
.map(buildClause),
isDirty: false,
@@ -478,9 +478,9 @@ function buildClause(node) {
type: 'clause',
index: node.positionals[0] ?? 0,
repeat: node.props.repeat ?? null,
notes: node.children.filter(c => c.slot === 'note' && c.parentSlot === 'seq').map(c => ({ ...c.props })),
pauses: node.children.filter(c => c.slot === 'pause' && c.parentSlot === 'seq').map(c => ({ length: c.props.length })),
stacks: node.children.filter(c => c.slot === 'stack' && c.parentSlot === 'seq').map(c => ({
notes: node.children.filter(c => c.slot === 'note' && (c.parentSlot === 'seq' || c.parentSlot === 'clause')).map(c => ({ ...c.props })),
pauses: node.children.filter(c => c.slot === 'pause' && (c.parentSlot === 'seq' || c.parentSlot === 'clause')).map(c => ({ length: c.props.length })),
stacks: node.children.filter(c => c.slot === 'stack' && (c.parentSlot === 'seq' || c.parentSlot === 'clause')).map(c => ({
length: c.props.length, netlength: c.props.netlength,
notes: c.children.filter(cn => cn.slot === 'note').map(cn => ({ ...cn.props })),
})),
@@ -495,7 +495,7 @@ function buildMotif(node) {
unknownProps: collectUnknownProps(node.props, new Set(['label'])),
};
for (const child of node.children) {
if (child.parentSlot === 'line' && child.slot === 'stem_note')
if ((child.parentSlot === 'line' || child.parentSlot === 'motif') && child.slot === 'stem_note')
m.stemNotes.push(buildStemNote(child));
}
m.isStatic = m.stemNotes.length > 0 && !m.stemNotes.some(sn => Number.isInteger(sn.pitch));