UNPKG

@dcoffey/espells

Version:

Pure JS/TS spellchecker, using Hunspell dictionaries. Based on Spylls.

156 lines 6.68 kB
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { iterate } from "iterare"; import { CONSTANTS as C } from "../constants.js"; import { re } from "../util.js"; /** Base class for the {@link Prefix}/{@link Suffix} classes. Won't work by itself. */ export class Affix { /** * @param flag - {@link Flag} that this affix will be labled with. * @param crossproduct - Sets the {@link Affix.crossproduct} state, with * the string "Y" meaning `true` and everything else meaning `false`. * @param strip - What this affix should strip from a word to be applied. * If given as the string "0", that means not to strip anything. * @param add - What to add to a word when this affix is applied. If * given as the string "0", that means to add nothing. * @param aff - The {@link Aff} data to use when parsing flags. */ constructor(flag, crossproduct, strip, add, aff) { let flags; [add, flags] = add.split("/"); add = aff.ignore(add); this.flag = flag; this.crossproduct = crossproduct === "Y"; this.strip = strip === "0" ? "" : strip; this.add = add === "0" ? "" : add; this.flags = flags ? aff.parseFlags(flags) : new Set(); } /** * Determines if a word matches the conditions of this affix. * * @param word - The word to check against this affix's conditions. */ relevant(word) { return this.conditionRegex.test(word); } /** * Determines if a word already has this affix applied to it. * * @param word - The word to check for if this affix is already present. */ on(word) { return this.lookupRegex.test(word); } /** * Applies this affix to a word, returning the word as a transformed string. * * @param word - The word to apply the transformation to. */ apply(word) { return word.replace(this.replaceRegex, this.strip); } /** * Determines if this affix has the given flag. * * @param flag - The flag to check for. Can be undefined, which will return false. */ has(flag) { if (flag === undefined) return false; return this.flags.has(flag); } /** * Determines if this affix is compatible with a set of flags, meaning * that the affix's flags are present in the flag set given. * * @param required - The flags this affix must have. * @param forbidden - An optional set of flags which has the inverse * effect, meaning that this affix's flags *cannot* be found in this set. */ compatible(required, forbidden) { // jump out early if there are no flags to check against if (required.size === 0 && (!forbidden || forbidden.size === 0)) return true; if (forbidden) { for (const flag of this.flags) { if (forbidden.has(flag)) return false; } } // special case of no required flags if (required.size === 0) return true; // jump out early if it isn't possible for this affix to be compatible if (this.flags.size < required.size) return false; for (const flag of required) { if (!this.flags.has(flag)) return false; } return true; } } /** An {@link Affix} that is applied to or found at the beginning of a stem. */ export class Prefix extends Affix { /** * @param flag - {@link Flag} that this affix will be labled with. * @param crossproduct - Sets the {@link Affix.crossproduct} state, with * the string "Y" meaning `true` and everything else meaning `false`. * @param strip - What this affix should strip from a word to be applied. * If given as the string "0", that means not to strip anything. * @param add - What to add to a word when this affix is applied. If * given as the string "0", that means to add nothing. * @param condition - A `RegExp` like pattern to check against a word to * see if this affix is relevant. * @param aff - The {@link Aff} data to use when parsing flags. */ constructor(flag, crossproduct, strip, add, condition, aff) { super(flag, crossproduct, strip, add, aff); let parts = iterate(condition.matchAll(C.SPLIT_CONDITION_REGEX)) .map(part => part.slice(1)) .flatten() .toArray(); if (parts.length && this.strip) parts = parts.slice(this.strip.length); let cond = ""; if (parts.length && !(parts.length === 1 && parts[0] === ".")) { cond = `(?=${parts.join("")})`.replaceAll("-", "\\-"); } this.conditionRegex = re `/^${condition.replaceAll("-", "\\-")}/`; this.lookupRegex = re `/^${this.add}${cond}/`; this.replaceRegex = re `/^${this.add}/`; } } /** An {@link Affix} that is applied to or can be found at the end of a stem. */ export class Suffix extends Affix { /** * @param flag - {@link Flag} that this affix will be labled with. * @param crossproduct - Sets the {@link Affix.crossproduct} state, with * the string "Y" meaning `true` and everything else meaning `false`. * @param strip - What this affix should strip from a word to be applied. * If given as the string "0", that means not to strip anything. * @param add - What to add to a word when this affix is applied. If * given as the string "0", that means to add nothing. * @param condition - A `RegExp` like pattern to check against a word to * see if this affix is relevant. * @param aff - The {@link Aff} data to use when parsing flags. */ constructor(flag, crossproduct, strip, add, condition, aff) { super(flag, crossproduct, strip, add, aff); let parts = iterate(condition.matchAll(C.SPLIT_CONDITION_REGEX)) .map(part => part.slice(1)) .flatten() .toArray(); let cond = ""; if (parts.length && !(parts.length === 1 && parts[0] === ".")) { if (this.strip) parts = parts.slice(0, -this.strip.length); cond = `(${parts.join("")})`.replaceAll("-", "\\-"); } this.conditionRegex = re `/${condition.replaceAll("-", "\\-")}$/`; this.lookupRegex = re `/${cond}${this.add}$/`; this.replaceRegex = re `/${this.add}$/`; } } //# sourceMappingURL=affix.js.map