yoastseo
Version:
Yoast client-side content analysis
347 lines (326 loc) • 12.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = stem;
exports.matchAndStemWord = matchAndStemWord;
exports.matchAndStemWordWithOneRegex = matchAndStemWordWithOneRegex;
exports.matchAndStemWordWithRegexArray = matchAndStemWordWithRegexArray;
/* eslint-disable max-statements,complexity */
/**
* MIT License
*
* Copyright (c) 2015 apmats <amatsagkas@gmail.com>
*
* 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 NONINFRINGEMENT. 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.
*
* https://github.com/Apmats/greekstemmerjs
*/
/**
* Matches word with an array of regexes and stems the word if there is any match. Further processes the stemmed word
* if it matches one of the regexes in the second array by attaching an additional ending.
*
* @param {string} word The word to check.
* @param {Array} regexes1 The first array of regexes to check.
* @param {Array} regexes2 The second array of regexes to check.
* @param {Array} endings The array of endings to attach to the stemmed word
* if the previously stemmed word matches one of the regexes in the second array.
* @returns {string} The stemmed word if there is any matches or otherwise the original word.
*/
function matchAndStemWordWithRegexArray(word, regexes1, regexes2, endings) {
let match;
for (let i = 0; i < regexes1.length; i++) {
if ((match = new RegExp(regexes1[i]).exec(word)) !== null) {
word = match[1];
if (new RegExp(regexes2[i]).test(word)) {
word += endings[i];
}
}
}
return word;
}
/**
* Matches word with a regex and stems the word if there is any match.
*
* @param {string} word The word to check.
* @param {string} regex The regex to match.
*
* @returns {string} The stemmed word if there is any matches or otherwise the original word.
*/
function matchAndStemWordWithOneRegex(word, regex) {
let match;
if ((match = new RegExp(regex).exec(word)) !== null) {
word = match[1];
}
return word;
}
/**
* Matches word with a regex and stems the word if there is any match. Further processes the stemmed word
* if it matches one of the two regexes in the second check by attaching an additional ending.
*
* @param {string} word The word to check.
* @param {string} regex1 The first regex to match.
* @param {string} regex2 The second regex to match.
* @param {string} regex3 The third regex to match.
* @param {string} ending The ending to attach to the stemmed word
* if the previously stemmed word matches one of the conditions in the second check.
* @returns {string} The stemmed word if there is any matches or otherwise the original word.
*/
function matchAndStemWord(word, regex1, regex2, regex3, ending) {
let match;
if ((match = new RegExp(regex1).exec(word)) !== null) {
word = match[1];
if (new RegExp(regex2).test(word) || new RegExp(regex3).test(word)) {
word += ending;
}
}
return word;
}
/**
* Checks if a word is in the exception list of step 1 stemming process.
*
* @param {string} word The word to check.
* @param {Object} morphologyData The Greek morphology data.
* @returns {string} The stem of the word.
*/
function checkExceptionStep1(word, morphologyData) {
const exceptions = morphologyData.externalStemmer.step1Exceptions;
const regex = new RegExp("(.*)(" + Object.keys(exceptions).join("|") + ")$");
const match = regex.exec(word);
if (match !== null) {
word = match[1] + exceptions[match[2]];
}
return word;
}
/**
* Stems suffixes from step 1.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Greek morphology data.
*
* @returns {string} The word without suffixes or the original word if no such suffix is found.
*/
function stemWordStep1(word, morphologyData) {
const regexesStep1 = morphologyData.externalStemmer.regexesStep1;
const regexesArrays = regexesStep1.regexesArrays;
let match;
if ((match = new RegExp(regexesStep1.regex1a).exec(word)) !== null) {
word = match[1];
if (!new RegExp(regexesStep1.regex1b).test(word)) {
word += "ΑΔ";
}
}
word = matchAndStemWordWithRegexArray(word, regexesArrays[0], regexesArrays[1], regexesArrays[2]);
return word;
}
/**
* Stems suffixes from step 2.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Greek morphology data.
*
* @returns {string} The word without suffixes or the original word if no such suffix is found.
*/
function stemWordStep2(word, morphologyData) {
const regexesStep2 = morphologyData.externalStemmer.regexesStep2;
const vowelRegex1 = new RegExp(morphologyData.externalStemmer.vowelRegex1);
let match;
if ((match = new RegExp(regexesStep2.regex2a).exec(word)) !== null && match[1].length > 4) {
word = match[1];
}
if ((match = new RegExp(regexesStep2.regex2b).exec(word)) !== null) {
word = match[1];
if (vowelRegex1.test(word) || word.length < 2 || new RegExp(regexesStep2.regex2c).test(match[1])) {
word += "Ι";
}
if (new RegExp(regexesStep2.regex2d).test(match[1])) {
word += "ΑΙ";
}
}
return word;
}
/**
* Stems suffixes from step 3.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Greek morphology data.
*
* @returns {string} The word without suffixes or the original word if no such suffix is found.
*/
function stemWordStep3(word, morphologyData) {
const vowelRegex1 = new RegExp(morphologyData.externalStemmer.vowelRegex1);
const regexesStep3 = morphologyData.externalStemmer.regexesStep3;
let match;
if ((match = new RegExp(regexesStep3.regex3a).exec(word)) !== null) {
word = match[1];
if (vowelRegex1.test(word) || new RegExp(regexesStep3.regex3b).test(word) || new RegExp(regexesStep3.regex3c).test(word)) {
word += "ΙΚ";
}
}
return word;
}
/**
* Stems verb and nouns suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Greek morphology data.
*
* @returns {string} The word without suffixes or the original word if no such suffix is found.
*/
function stemWordStep4(word, morphologyData) {
const regexesStep4 = morphologyData.externalStemmer.regexesStep4;
const regexesArrays = regexesStep4.regexesArrays;
const vowelRegex1 = morphologyData.externalStemmer.vowelRegex1;
const vowelRegex2 = morphologyData.externalStemmer.vowelRegex2;
let match;
if (word === "ΑΓΑΜΕ") {
return "ΑΓΑΜ";
}
word = matchAndStemWordWithOneRegex(word, regexesStep4.regex4a);
word = matchAndStemWordWithRegexArray(word, regexesArrays.arrays1[0], regexesArrays.arrays1[1], regexesArrays.arrays1[2]);
word = matchAndStemWord(word, regexesStep4.regex4b, vowelRegex2, regexesStep4.regex4c, "ΑΝ");
word = matchAndStemWordWithOneRegex(word, regexesStep4.regex4d);
if ((match = new RegExp(regexesStep4.regex4e).exec(word)) !== null) {
word = match[1];
if (new RegExp(vowelRegex2).test(word) || new RegExp(regexesStep4.regex4f).test(word) || new RegExp(regexesStep4.regex4g).test(word)) {
word += "ΕΤ";
}
}
if ((match = new RegExp(regexesStep4.regex4h).exec(word)) !== null) {
word = match[1];
if (new RegExp(regexesStep4.regex4i).test(match[1])) {
word += "ΟΝΤ";
} else if (new RegExp(regexesStep4.regex4j).test(match[1])) {
word += "ΩΝΤ";
}
}
word = matchAndStemWordWithRegexArray(word, regexesArrays.arrays2[0], regexesArrays.arrays2[1], regexesArrays.arrays2[2]);
word = matchAndStemWordWithOneRegex(word, regexesStep4.regex4k);
word = matchAndStemWord(word, regexesStep4.regex4l, regexesStep4.regex4m, regexesStep4.regex4n, "ΗΚ");
if ((match = new RegExp(regexesStep4.regex4o).exec(word)) !== null) {
word = match[1];
if (new RegExp(vowelRegex1).test(word) || new RegExp(regexesStep4.regex4p).test(match[1]) || new RegExp(regexesStep4.regex4q).test(match[1])) {
word += "ΟΥΣ";
}
}
if ((match = new RegExp(regexesStep4.regex4r).exec(word)) !== null) {
word = match[1];
if (new RegExp(regexesStep4.regex4s).test(word) || new RegExp(regexesStep4.regex4t).test(word) && !new RegExp(regexesStep4.regex4u).test(word)) {
word += "ΑΓ";
}
}
word = matchAndStemWordWithRegexArray(word, regexesArrays.arrays3[0], regexesArrays.arrays3[1], regexesArrays.arrays3[2]);
return word;
}
/**
* Stems adjective suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Greek morphology data.
*
* @returns {string} The word without suffixes or the original word if no such suffix is found.
*/
function stemWordStep5(word, morphologyData) {
const regexesStep5 = morphologyData.externalStemmer.regexesStep5;
let match;
if ((match = new RegExp(regexesStep5.regex5a).exec(word)) !== null) {
word = match[1] + "Μ";
if (new RegExp(regexesStep5.regex5b).test(match[1])) {
word += "Α";
} else if (new RegExp(regexesStep5.regex5c).test(match[1])) {
word += "ΑΤ";
}
}
if ((match = new RegExp(regexesStep5.regex5d).exec(word)) !== null) {
word = match[1] + "ΟΥ";
}
return word;
}
/**
* Stems superlative and comparative suffixes.
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The Greek morphology data.
*
* @returns {string} The word without suffixes or the original word if no such suffix is found.
*/
function stemWordStep6(word, morphologyData) {
const regexesStep6 = morphologyData.externalStemmer.regexesStep6;
let match;
if ((match = new RegExp(regexesStep6.regex6a).exec(word)) !== null) {
if (!new RegExp(regexesStep6.regex6b).test(match[1])) {
word = match[1];
}
if (new RegExp(regexesStep6.regex6c).test(match[1])) {
word += "ΥΤ";
}
}
return word;
}
/**
* Normalizes Greek by removing accents.
*
* @param {string} text The text that should be normalized.
*
* @returns {string} The normalized text.
*/
function normalizeGreek(text) {
return text.replace(/[ΆΑά]/g, "α").replace(/[ΈΕέ]/g, "ε").replace(/[ΉΗή]/g, "η").replace(/[ΊΪΙίΐϊ]/g, "ι").replace(/[ΌΟό]/g, "ο").replace(/[ΎΫΥύΰϋ]/g, "υ").replace(/[ΏΩώ]/g, "ω").replace(/[Σς]/g, "σ");
}
/**
* Stems Greek words
*
* @param {string} word The word to stem.
* @param {Object} morphologyData The object that contains regex-based rules and exception lists for Greek stemming.
*
* @returns {string} The stem of a Greek word.
*/
function stem(word, morphologyData) {
word = normalizeGreek(word);
// We process the word in uppercase to account for the character changing in lowercase depending on its position in the word.
word = word.toLocaleUpperCase("el");
const originalWord = word;
const doNotStemWords = morphologyData.externalStemmer.doNotStemWords;
if (word.length < 3 || doNotStemWords.includes(word)) {
return word.toLocaleLowerCase("el");
}
// Check for exceptions first before proceeding to the next step.
word = checkExceptionStep1(word, morphologyData);
// Step 1
word = stemWordStep1(word, morphologyData);
// Step 2
word = stemWordStep2(word, morphologyData);
// Step 3
word = stemWordStep3(word, morphologyData);
// Step 4
word = stemWordStep4(word, morphologyData);
// Step 5
word = stemWordStep5(word, morphologyData);
// Handle long words.
const longWordRegex = morphologyData.externalStemmer.longWordRegex;
if (originalWord.length === word.length) {
word = matchAndStemWordWithOneRegex(word, longWordRegex);
}
// Step 6
word = stemWordStep6(word, morphologyData);
// Make the word lowercase again after the stemming process complete.
word = word.toLocaleLowerCase("el");
return word;
}
//# sourceMappingURL=stem.js.map