You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
79 lines
2.6 KiB
79 lines
2.6 KiB
|
1 month ago
|
/**
|
||
|
|
* 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<string, {titulo: string, lineas: object[]}[]> }} 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;
|
||
|
|
}
|