UNPKG

@zsnout/ithkuil

Version:

A set of tools which can generate and parse romanized Ithkuil text and which can generate Ithkuil script from text and JSON data.

226 lines (225 loc) 6.53 kB
/** A generic part of a regular expression. */ export class RegexPart { source; constructor( /** The source text for this RegexPart. */ source) { this.source = source; Object.freeze(this); } /** * Creates a new RegexPart matching the contents of this one in a capture * group. * * @returns The new RegexPart. */ asGroup() { return new AtomicRegexPart("(" + this.source + ")"); } /** * Creates a new RegexPart matching the contents of this one in a named * capture group. * * @returns The new RegexPart. */ asNamedGroup(name) { return new AtomicRegexPart("(?<" + name + ">" + this.source + ")"); } /** * Creates a new RegexPart that matches the same content as this one, but is * optional. * * @returns The new RegexPart. */ optional() { return new RegexPart("(?:" + this.source + ")?"); } /** * Creates a new RegexPart that matches zero or more repetitions of this * pattern. * * @returns The new RegexPart. */ zeroOrMore() { return new RegexPart("(?:" + this.source + ")*"); } /** * Creates a new RegexPart that matches one or more repetitions of this * pattern. * * @returns The new RegexPart. */ oneOrMore() { return new RegexPart("(?:" + this.source + ")+"); } /** * Creates a new RegexPart that matches this part's content, but only if it * will match the entire source string. * * @returns The new RegexPart. */ matchEntireText() { return new RegexPart("^" + this.source + "$"); } /** * Creates a new RegexPart that is a negative lookahead that prevents the * future text from matching the current pattern. * * @returns The new RegexPart. */ not() { return new AtomicRegexPart("(?!" + this.source + ")"); } /** * Creates a regular expression matching the contents of this RegexPart. * * @param flags The flags to compile with. * @returns A regular expression. */ compile(flags = "") { return new RegExp(this.source, flags); } /** * Gets the source text of this RegexPart. * * @returns The source text of this RegexPart. */ toString() { return this.source; } } /** * A subclass of `RegexPart` used for atomic matchers such as single characters, * character classes, groups, and so on. */ export class AtomicRegexPart extends RegexPart { /** * Creates a new RegexPart that matches the same content as this one, but is * optional. * * @returns The new RegexPart. */ optional() { return new RegexPart(this.source + "?"); } /** * Creates a new RegexPart that matches zero or more repetitions of this * pattern. * * @returns The new RegexPart. */ zeroOrMore() { return new RegexPart(this.source + "*"); } /** * Creates a new RegexPart that matches one or more repetitions of this * pattern. * * @returns The new RegexPart. */ oneOrMore() { return new RegexPart(this.source + "+"); } } /** A subclass of `RegexPart` used for matchers that include multiple paths. */ export class RegexPartWithAlternates extends RegexPart { /** * Creates a new RegexPart that matches this part's content, but only if it * will match the entire source string. * * @returns The new RegexPart. */ matchEntireText() { return new RegexPart("^(?:" + this.source + ")$"); } } /** * Escapes text for use in a regular expression (e.g. `hello$world.` becomes * `hello\$world\.`). * * @param text The text to be escaped. * @returns The escaped text. */ export function escapeRegex(text) { return text.replace(/[\^$\\.*+?()[\]{}|-]/g, "\\$&"); } /** A `RegexPart` matching the start of the source text. */ export const start = /* @__PURE__ */ new RegexPart("^"); /** A `RegexPart` matching the end of the source text. */ export const end = /* @__PURE__ */ new RegexPart("$"); /** * Creates a `RegexPart` matching a specified piece of text. * * @param text The text to be matched. * @returns A `RegexPart` matching the specified text. */ export function text(text) { text = escapeRegex(text); if (text.length == 1) { return new AtomicRegexPart(text); } else { return new RegexPart(text); } } /** * Creates a `RegexPart` matching any of the specified characters. * * @param chars The characters that may be matched. * @returns A `RegexPart` matching one of the specified characters. */ export function charIn(chars) { return new AtomicRegexPart("[" + escapeRegex(chars) + "]"); } /** * Creates a `RegexPart` matching any character except for those specified. * * @param chars The characters that may NOT be matched. * @returns A `RegexPart` matching any character except those specified. */ export function charNotIn(chars) { return new AtomicRegexPart("[^" + escapeRegex(chars) + "]"); } /** * Creates a `RegexPart` matching any of a list of parts, giving precedence to * matching the first one. * * @param parts The parts that may be matched. * @returns A `RegexPart` matching any of the passed parts. */ export function any(...parts) { return new RegexPartWithAlternates(parts.join("|")); } /** * Creates a `RegexPart` matching any of the passed texts, giving precedence to * the first one. * * @param texts The texts that may be matched. * @returns A `RegexPart` matching any of the passed texts. */ export function anyText(...texts) { return new RegexPartWithAlternates(texts.map((x) => escapeRegex(x)).join("|")); } /** * Creates a `RegexPart` matching any of the passed texts, giving precedence to * the first one. * * @param texts The texts that may be matched. * @returns A `RegexPart` matching any of the passed texts. */ export function anyTextArray(texts) { return new RegexPartWithAlternates(texts.map((x) => escapeRegex(x)).join("|")); } /** * Creates a `RegexPart` matching each of the passed parts in order. * * @param parts The parts that will be matched. * @returns A `RegexPart` matching all of the passed parts in order. */ export function seq(...parts) { return new RegexPart(parts .map((x) => x instanceof RegexPartWithAlternates ? "(?:" + x.source + ")" : x.source) .join("")); }