Apply RFC-compliant ast-parser and exporter

- ast-parser: railsbackCurve moved to variation level, FM shape child
  parsing, buildLabelSpec direct A/S/R handling, bar IDs as opaque strings
- exporter: RFC-compact shape format, correct instrument NAME: key,
  patchScore regex fixed, RAILSBACK_CURVE emitted from variationLines
- .gitignore: exclude test-parser.mjs pending author confirmation
This commit is contained in:
c0dev0id
2026-06-22 10:52:32 +02:00
parent 822e2c9f42
commit c8b8d03e3f
3 changed files with 132 additions and 109 deletions

View File

@@ -178,7 +178,6 @@ function buildInstrument(node) {
isDirty: false,
variations: [],
basicProperties: null,
railsbackCurve: null,
volumes: null,
timbre: null,
fmModulations: [],
@@ -193,9 +192,6 @@ function buildInstrument(node) {
case 'character.basic_properties':
instr.basicProperties = buildBasicProperties(child);
break;
case 'RAILSBACK_CURVE.shape':
instr.railsbackCurve = buildShape(child);
break;
case 'VOLUMES.shape':
instr.volumes = buildShape(child);
break;
@@ -221,6 +217,7 @@ function buildVariation(node) {
labelSpecs: [],
subvariations: [],
spread: null,
railsbackCurve: null,
rawChildren: [],
};
@@ -239,6 +236,9 @@ function buildVariation(node) {
case 'variation.SPREAD':
v.spread = child.positionals;
break;
case 'RAILSBACK_CURVE.shape':
v.railsbackCurve = buildShape(child);
break;
default:
v.rawChildren.push(buildGeneric(child));
}
@@ -267,7 +267,10 @@ function buildBasicProperties(node) {
} else if (child.parentSlot === 'variation' && child.slot === 'O') {
bp.oscillator = child.props.ref ?? child.positionals[0];
} else if (child.parentSlot === 'FM' && child.slot === 'modulation') {
bp.fmModulations.push({ ...child.props });
const fm = { ...child.props };
const envChild = child.children.find(c => c.slot === 'shape');
if (envChild) fm.shape = buildShape(envChild);
bp.fmModulations.push(fm);
} else {
bp.rawChildren.push(buildGeneric(child));
}
@@ -284,13 +287,23 @@ function buildLabelSpec(node) {
rawChildren: [],
};
const directBpChildren = [];
for (const child of node.children) {
if (child.parentSlot === 'variation' && child.slot === 'basic_properties') {
ls.basicProperties = buildBasicProperties(child);
} else if (
(child.slot === 'shape' && (child.parentSlot === 'A' || child.parentSlot === 'S' || child.parentSlot === 'R')) ||
(child.parentSlot === 'variation' && child.slot === 'O') ||
(child.parentSlot === 'FM' && child.slot === 'modulation')
) {
directBpChildren.push(child);
} else {
ls.rawChildren.push(buildGeneric(child));
}
}
if (!ls.basicProperties && directBpChildren.length > 0) {
ls.basicProperties = buildBasicProperties({ children: directBpChildren });
}
return ls;
}
@@ -300,7 +313,6 @@ function buildShape(node) {
type: 'shape',
length: node.props.length,
start: node.props.start,
z: node.props.z ?? 1,
coords: node.children
.filter(c => c.slot === 'coords')
.map(c => ({
@@ -313,15 +325,9 @@ function buildShape(node) {
}
function buildBar(node) {
const id = node.positionals[0] ?? '';
const idMatch = id.match(/^(\w?)(\d+)P(\d+)L(\d+)M(\d+)$/);
const bar = {
type: 'bar',
id,
movement: idMatch ? idMatch[1] : '',
part: idMatch ? parseInt(idMatch[2]) : 0,
line: idMatch ? parseInt(idMatch[3]) : 0,
measure: idMatch ? parseInt(idMatch[4]) : 0,
id: node.positionals[0] ?? '',
stressor: null,
tempoShape: null,
tempoLevels: null,