yoastseo
Version:
Yoast client-side content analysis
322 lines (310 loc) • 11.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = stem;
/* eslint-disable complexity */
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Marek Šuppa
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Takes care of palatalisation.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The non-palatalised word or the original word if no such suffix is found.
*/
function palatalise(word, morphologyData) {
const palatalEndingsRegexes = morphologyData.externalStemmer.palatalEndingsRegexes;
// Check if word ends in a palatal ending and return the regex if it does.
const checkPalatalEnding = palatalEndingsRegexes.find(regex => new RegExp(regex[0]).test(word));
if (checkPalatalEnding) {
return word.replace(new RegExp(checkPalatalEnding[0]), checkPalatalEnding[1]);
}
return word.slice(0, -1);
}
/**
* Removes case suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The word without case suffixes or the original word if no such suffix is found.
*/
function removeCases(word, morphologyData) {
const caseSuffixes = morphologyData.externalStemmer.caseSuffixes;
const caseRegexes = morphologyData.externalStemmer.caseRegexes;
if (word.length > 7 && word.endsWith(caseSuffixes.caseSuffix1)) {
// Return the word without the suffix
return word.slice(0, -5);
}
if (word.length > 6 && word.endsWith(caseSuffixes.caseSuffix2)) {
return palatalise(word.slice(0, -3), morphologyData);
}
if (word.length > 5) {
if (caseSuffixes.caseSuffixes3.includes(word.slice(-3))) {
return palatalise(word.slice(0, -2), morphologyData);
} else if (caseSuffixes.caseSuffixes4.includes(word.slice(-3))) {
return word.slice(0, -3);
}
}
if (word.length > 4) {
if (word.endsWith(caseSuffixes.caseSuffix5)) {
return palatalise(word.slice(0, -1), morphologyData);
} else if (caseSuffixes.caseSuffixes6.includes(word.slice(-2))) {
return palatalise(word.slice(0, -2), morphologyData);
} else if (caseSuffixes.caseSuffixes7.includes(word.slice(-2))) {
return word.slice(0, -2);
}
}
if (word.length > 3) {
if (new RegExp(caseRegexes.caseRegex1).test(word)) {
return palatalise(word, morphologyData);
} else if (new RegExp(caseRegexes.caseRegex2).test(word)) {
return word.slice(0, -1);
}
}
return word;
}
/**
* Removes possessive suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The word without possessive suffixes or the original word if no such suffix is found.
*/
function removePossessives(word, morphologyData) {
const possessiveSuffixes = morphologyData.externalStemmer.possessiveSuffixes;
if (word.length > 5) {
if (word.endsWith(possessiveSuffixes.posSuffixOv)) {
return word.slice(0, -2);
}
if (word.endsWith(possessiveSuffixes.posSuffixIn)) {
return palatalise(word.slice(0, -1), morphologyData);
}
}
return word;
}
/**
* Removes comparative and superlative affixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The word without comparative and superlative affixes or the original word if no such suffix is found.
*/
function removeComparativeAndSuperlative(word, morphologyData) {
const superlativePrefix = morphologyData.externalStemmer.superlativePrefix;
if (word.length > 6 && word.startsWith(superlativePrefix)) {
word = word.slice(3, word.length);
}
if (word.length > 5) {
const comparativeSuffixes = morphologyData.externalStemmer.comparativeSuffixes;
if (comparativeSuffixes.includes(word.slice(-3))) {
word = palatalise(word.slice(0, -2), morphologyData);
}
}
return word;
}
/**
* Removes diminutive suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The word without diminutive suffixes or the original word if no such suffix is found.
*/
function removeDiminutives(word, morphologyData) {
const diminutiveSuffixes = morphologyData.externalStemmer.diminutiveSuffixes;
if (word.length > 7 && word.endsWith(diminutiveSuffixes.diminutiveSuffix1)) {
return word.slice(0, -5);
}
if (word.length > 6) {
if (diminutiveSuffixes.diminutiveSuffixes2.includes(word.slice(-4))) {
return palatalise(word.slice(0, -3), morphologyData);
}
if (diminutiveSuffixes.diminutiveSuffixes3.includes(word.slice(-4))) {
return palatalise(word.slice(0, -4), morphologyData);
}
}
if (word.length > 5) {
if (diminutiveSuffixes.diminutiveSuffixes4.includes(word.slice(-3))) {
return palatalise(word.slice(0, -3), morphologyData);
}
if (diminutiveSuffixes.diminutiveSuffixes5.includes(word.slice(-3))) {
return word.slice(0, -3);
}
}
if (word.length > 4) {
if (diminutiveSuffixes.diminutiveSuffixes6.includes(word.slice(-2))) {
return palatalise(word.slice(0, -1), morphologyData);
}
if (diminutiveSuffixes.diminutiveSuffixes7.includes(word.slice(-2))) {
return word.slice(0, -1);
}
}
if (word.length > 3 && word.endsWith("k") && !word.endsWith("isk")) {
return word.slice(0, -1);
}
return word;
}
/**
* Removes augmentative suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The word without augmentative suffixes or the original word if no such suffix is found.
*/
function removeAugmentatives(word, morphologyData) {
const augmentativeSuffixes = morphologyData.externalStemmer.augmentativeSuffixes;
if (word.length > 6 && word.endsWith(augmentativeSuffixes.augmentativeSuffix1)) {
return word.slice(0, -4);
}
if (word.length > 5 && augmentativeSuffixes.augmentativeSuffixes2.includes(word.slice(-3))) {
return palatalise(word.slice(0, -2), morphologyData);
}
return word;
}
/**
* Removes derivational suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The word without derivational suffixes or the original word if no such suffix is found.
*/
function stemDerivational(word, morphologyData) {
const derivationalSuffixes = morphologyData.externalStemmer.derivationalSuffixes;
if (word.length > 8 && word.endsWith(derivationalSuffixes.derivationalSuffix1)) {
return word.slice(0, -6);
}
if (word.length > 7) {
if (word.endsWith(derivationalSuffixes.derivationalSuffix2)) {
return palatalise(word.slice(0, -4), morphologyData);
}
if (derivationalSuffixes.derivationalSuffixes3.includes(word.slice(-5))) {
return word.slice(0, -5);
}
}
if (word.length > 6) {
if (derivationalSuffixes.derivationalSuffixes4.includes(word.slice(-4))) {
return word.slice(0, -4);
}
if (derivationalSuffixes.derivationalSuffixes5.includes(word.slice(-4))) {
return palatalise(word.slice(0, -3), morphologyData);
}
}
if (word.length > 5) {
if (word.endsWith(derivationalSuffixes.derivationalSuffix6)) {
return word.slice(0, -3);
}
if (derivationalSuffixes.derivationalSuffixes7.includes(word.slice(-3))) {
return palatalise(word.slice(0, -2), morphologyData);
}
if (derivationalSuffixes.derivationalSuffixes8.includes(word.slice(-3))) {
return word.slice(0, -3);
}
}
if (word.length > 4) {
if (derivationalSuffixes.derivationalSuffixes9.includes(word.slice(-2))) {
return word.slice(0, -2);
}
if (derivationalSuffixes.derivationalSuffixes10.includes(word.slice(-2))) {
return palatalise(word.slice(0, -1), morphologyData);
}
}
const derivationalRegex = new RegExp(morphologyData.externalStemmer.derivationalRegex);
if (word.length > 3 && derivationalRegex.test(word)) {
return word.slice(0, -1);
}
return word;
}
/**
* Checks whether a word is in the full-form exception list and if so returns the canonical stem.
*
* @param {string} word The word to be checked.
* @param {Object} exceptionListWithFullForms The exception list to check.
*
* @returns {string} The canonical stem if word was found on the list or the original word otherwise.
*/
const checkWordInFullFormExceptions = function (word, exceptionListWithFullForms) {
for (const paradigm of exceptionListWithFullForms) {
if (paradigm[1].includes(word)) {
return paradigm[0];
}
}
return null;
};
/**
* Checks whether a stem is in an exception list of words with multiple stems and if so returns the canonical stem.
*
* @param {string} stemmedWord The stemmed word to be checked.
* @param {Object} stemsThatBelongToOneWord The data that shows how non-canonical stems should be canonicalized.
*
* @returns {null|string} The canonical stem or null if nothing was found.
*/
const canonicalizeStem = function (stemmedWord, stemsThatBelongToOneWord) {
for (const paradigm of stemsThatBelongToOneWord) {
if (paradigm.includes(stemmedWord)) {
return paradigm[0];
}
}
return null;
};
/**
* Stems Slovak words.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Slovak morphology data.
*
* @returns {string} The stemmed word.
*/
function stem(word, morphologyData) {
// Return stem of words on the full forms exception list.
const stemFromExceptionListWithFullForms = checkWordInFullFormExceptions(word, morphologyData.exceptionLists.exceptionStemsWithFullForms);
if (stemFromExceptionListWithFullForms) {
return stemFromExceptionListWithFullForms;
}
// Remove case suffixes
word = removeCases(word, morphologyData);
// Remove possessive suffixes
word = removePossessives(word, morphologyData);
// Remove comparative and superlative affixes
word = removeComparativeAndSuperlative(word, morphologyData);
// Remove diminutive suffixes
word = removeDiminutives(word, morphologyData);
// Remove augmentative suffixes
word = removeAugmentatives(word, morphologyData);
// Remove derivational suffixes
word = stemDerivational(word, morphologyData);
// Return canonical stem of words that get a few different stems depending on the form.
const canonicalStem = canonicalizeStem(word, morphologyData.exceptionLists.stemsThatBelongToOneWord);
if (canonicalStem) {
return canonicalStem;
}
return word;
}
//# sourceMappingURL=stem.js.map