/** * Comprueba que la escansión de un verso cuadre. * * Vive aparte del sembrador porque es la única garantía de que el taller no * enseñe una cuenta falsa, y eso hay que poder probarlo sin una base de datos * delante. Un ejemplo mal escandido es peor que ningún ejemplo: quien aprende * a contar sílabas con él aprende mal. * * La ley del acento final, que es de donde sale todo esto: un verso se mide * hasta su última sílaba tónica más una. Si acaba en palabra llana —lo * normal— la cuenta es la que se ve; si acaba en aguda suma una, y si acaba en * esdrújula resta una. Por eso «yo respondo con el son» tiene siete casillas y * es un octosílabo. */ /** * @param {{ silabas: string[], tonicas: number[], medida: number, texto?: string }} linea * @returns {string | null} qué falla, o `null` si cuadra. */ export function fallosDeEscansion(linea) { const casillas = linea.silabas.length; if (casillas === 0) return 'no tiene sílabas'; if (linea.tonicas.length === 0) return 'no tiene ninguna sílaba tónica'; if (linea.tonicas.some((t) => !Number.isInteger(t) || t < 1 || t > casillas)) { return `una tónica cae fuera de las ${casillas} sílabas`; } if ([...linea.tonicas].sort((a, b) => a - b).join() !== linea.tonicas.join()) { return 'las tónicas no van en orden'; } const ultima = linea.tonicas.at(-1); const porElAcento = { [casillas]: casillas + 1, // aguda [casillas - 1]: casillas, // llana [casillas - 2]: casillas - 1 // esdrújula }; // Con la tónica más atrás no hay verso: en castellano no hay palabras // sobresdrújulas al final de verso que se cuenten así. if (!(ultima in porElAcento)) { return `la última tónica está en ${ultima} y hay ${casillas} sílabas: demasiado atrás`; } if (porElAcento[ultima] !== linea.medida) { return ( `${casillas} casillas con la última tónica en ${ultima} ` + `miden ${porElAcento[ultima]}, no ${linea.medida}` ); } return null; } /** * Recorre la semilla entera y devuelve todo lo que no cuadra, con su sitio. * * @param {{ generos: Record }} semilla * @returns {string[]} */ export function revisarSemilla(semilla) { const problemas = []; for (const [genero, ejemplos] of Object.entries(semilla.generos)) { for (const ejemplo of ejemplos) { for (const [i, linea] of ejemplo.lineas.entries()) { const fallo = fallosDeEscansion(linea); if (fallo) { problemas.push( `${genero} · «${ejemplo.titulo}» · verso ${i + 1}: ${fallo} — «${linea.texto}»` ); } } } } return problemas; }