You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			610 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			610 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 months ago
										 | /* | ||
|  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||
|  | 	Author Tobias Koppers @sokra | ||
|  | */ | ||
|  | 
 | ||
|  | "use strict"; | ||
|  | 
 | ||
|  | /** @typedef {import("estree").Node} Node */ | ||
|  | /** @typedef {import("./JavascriptParser").Range} Range */ | ||
|  | /** @typedef {import("./JavascriptParser").VariableInfoInterface} VariableInfoInterface */ | ||
|  | 
 | ||
|  | const TypeUnknown = 0; | ||
|  | const TypeUndefined = 1; | ||
|  | const TypeNull = 2; | ||
|  | const TypeString = 3; | ||
|  | const TypeNumber = 4; | ||
|  | const TypeBoolean = 5; | ||
|  | const TypeRegExp = 6; | ||
|  | const TypeConditional = 7; | ||
|  | const TypeArray = 8; | ||
|  | const TypeConstArray = 9; | ||
|  | const TypeIdentifier = 10; | ||
|  | const TypeWrapped = 11; | ||
|  | const TypeTemplateString = 12; | ||
|  | const TypeBigInt = 13; | ||
|  | 
 | ||
|  | class BasicEvaluatedExpression { | ||
|  | 	constructor() { | ||
|  | 		this.type = TypeUnknown; | ||
|  | 		/** @type {[number, number] | undefined} */ | ||
|  | 		this.range = undefined; | ||
|  | 		/** @type {boolean} */ | ||
|  | 		this.falsy = false; | ||
|  | 		/** @type {boolean} */ | ||
|  | 		this.truthy = false; | ||
|  | 		/** @type {boolean | undefined} */ | ||
|  | 		this.nullish = undefined; | ||
|  | 		/** @type {boolean} */ | ||
|  | 		this.sideEffects = true; | ||
|  | 		/** @type {boolean | undefined} */ | ||
|  | 		this.bool = undefined; | ||
|  | 		/** @type {number | undefined} */ | ||
|  | 		this.number = undefined; | ||
|  | 		/** @type {bigint | undefined} */ | ||
|  | 		this.bigint = undefined; | ||
|  | 		/** @type {RegExp | undefined} */ | ||
|  | 		this.regExp = undefined; | ||
|  | 		/** @type {string | undefined} */ | ||
|  | 		this.string = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression[] | undefined} */ | ||
|  | 		this.quasis = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression[] | undefined} */ | ||
|  | 		this.parts = undefined; | ||
|  | 		/** @type {any[] | undefined} */ | ||
|  | 		this.array = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression[] | undefined} */ | ||
|  | 		this.items = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression[] | undefined} */ | ||
|  | 		this.options = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression | undefined | null} */ | ||
|  | 		this.prefix = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression | undefined | null} */ | ||
|  | 		this.postfix = undefined; | ||
|  | 		/** @type {BasicEvaluatedExpression[] | undefined} */ | ||
|  | 		this.wrappedInnerExpressions = undefined; | ||
|  | 		/** @type {string | VariableInfoInterface | undefined} */ | ||
|  | 		this.identifier = undefined; | ||
|  | 		/** @type {string | VariableInfoInterface | undefined} */ | ||
|  | 		this.rootInfo = undefined; | ||
|  | 		/** @type {(() => string[]) | undefined} */ | ||
|  | 		this.getMembers = undefined; | ||
|  | 		/** @type {(() => boolean[]) | undefined} */ | ||
|  | 		this.getMembersOptionals = undefined; | ||
|  | 		/** @type {(() => Range[]) | undefined} */ | ||
|  | 		this.getMemberRanges = undefined; | ||
|  | 		/** @type {Node | undefined} */ | ||
|  | 		this.expression = undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isUnknown() { | ||
|  | 		return this.type === TypeUnknown; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isNull() { | ||
|  | 		return this.type === TypeNull; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isUndefined() { | ||
|  | 		return this.type === TypeUndefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isString() { | ||
|  | 		return this.type === TypeString; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isNumber() { | ||
|  | 		return this.type === TypeNumber; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isBigInt() { | ||
|  | 		return this.type === TypeBigInt; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isBoolean() { | ||
|  | 		return this.type === TypeBoolean; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isRegExp() { | ||
|  | 		return this.type === TypeRegExp; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isConditional() { | ||
|  | 		return this.type === TypeConditional; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isArray() { | ||
|  | 		return this.type === TypeArray; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isConstArray() { | ||
|  | 		return this.type === TypeConstArray; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isIdentifier() { | ||
|  | 		return this.type === TypeIdentifier; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isWrapped() { | ||
|  | 		return this.type === TypeWrapped; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isTemplateString() { | ||
|  | 		return this.type === TypeTemplateString; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Is expression a primitive or an object type value? | ||
|  | 	 * @returns {boolean | undefined} true: primitive type, false: object type, undefined: unknown/runtime-defined | ||
|  | 	 */ | ||
|  | 	isPrimitiveType() { | ||
|  | 		switch (this.type) { | ||
|  | 			case TypeUndefined: | ||
|  | 			case TypeNull: | ||
|  | 			case TypeString: | ||
|  | 			case TypeNumber: | ||
|  | 			case TypeBoolean: | ||
|  | 			case TypeBigInt: | ||
|  | 			case TypeWrapped: | ||
|  | 			case TypeTemplateString: | ||
|  | 				return true; | ||
|  | 			case TypeRegExp: | ||
|  | 			case TypeArray: | ||
|  | 			case TypeConstArray: | ||
|  | 				return false; | ||
|  | 			default: | ||
|  | 				return undefined; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Is expression a runtime or compile-time value? | ||
|  | 	 * @returns {boolean} true: compile time value, false: runtime value | ||
|  | 	 */ | ||
|  | 	isCompileTimeValue() { | ||
|  | 		switch (this.type) { | ||
|  | 			case TypeUndefined: | ||
|  | 			case TypeNull: | ||
|  | 			case TypeString: | ||
|  | 			case TypeNumber: | ||
|  | 			case TypeBoolean: | ||
|  | 			case TypeRegExp: | ||
|  | 			case TypeConstArray: | ||
|  | 			case TypeBigInt: | ||
|  | 				return true; | ||
|  | 			default: | ||
|  | 				return false; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Gets the compile-time value of the expression | ||
|  | 	 * @returns {any} the javascript value | ||
|  | 	 */ | ||
|  | 	asCompileTimeValue() { | ||
|  | 		switch (this.type) { | ||
|  | 			case TypeUndefined: | ||
|  | 				return undefined; | ||
|  | 			case TypeNull: | ||
|  | 				return null; | ||
|  | 			case TypeString: | ||
|  | 				return this.string; | ||
|  | 			case TypeNumber: | ||
|  | 				return this.number; | ||
|  | 			case TypeBoolean: | ||
|  | 				return this.bool; | ||
|  | 			case TypeRegExp: | ||
|  | 				return this.regExp; | ||
|  | 			case TypeConstArray: | ||
|  | 				return this.array; | ||
|  | 			case TypeBigInt: | ||
|  | 				return this.bigint; | ||
|  | 			default: | ||
|  | 				throw new Error( | ||
|  | 					"asCompileTimeValue must only be called for compile-time values" | ||
|  | 				); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isTruthy() { | ||
|  | 		return this.truthy; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isFalsy() { | ||
|  | 		return this.falsy; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	isNullish() { | ||
|  | 		return this.nullish; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Can this expression have side effects? | ||
|  | 	 * @returns {boolean} false: never has side effects | ||
|  | 	 */ | ||
|  | 	couldHaveSideEffects() { | ||
|  | 		return this.sideEffects; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Creates a boolean representation of this evaluated expression. | ||
|  | 	 * @returns {boolean | undefined} true: truthy, false: falsy, undefined: unknown | ||
|  | 	 */ | ||
|  | 	asBool() { | ||
|  | 		if (this.truthy) return true; | ||
|  | 		if (this.falsy || this.nullish) return false; | ||
|  | 		if (this.isBoolean()) return this.bool; | ||
|  | 		if (this.isNull()) return false; | ||
|  | 		if (this.isUndefined()) return false; | ||
|  | 		if (this.isString()) return this.string !== ""; | ||
|  | 		if (this.isNumber()) return this.number !== 0; | ||
|  | 		if (this.isBigInt()) return this.bigint !== BigInt(0); | ||
|  | 		if (this.isRegExp()) return true; | ||
|  | 		if (this.isArray()) return true; | ||
|  | 		if (this.isConstArray()) return true; | ||
|  | 		if (this.isWrapped()) { | ||
|  | 			return (this.prefix && this.prefix.asBool()) || | ||
|  | 				(this.postfix && this.postfix.asBool()) | ||
|  | 				? true | ||
|  | 				: undefined; | ||
|  | 		} | ||
|  | 		if (this.isTemplateString()) { | ||
|  | 			const str = this.asString(); | ||
|  | 			if (typeof str === "string") return str !== ""; | ||
|  | 		} | ||
|  | 		return undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Creates a nullish coalescing representation of this evaluated expression. | ||
|  | 	 * @returns {boolean | undefined} true: nullish, false: not nullish, undefined: unknown | ||
|  | 	 */ | ||
|  | 	asNullish() { | ||
|  | 		const nullish = this.isNullish(); | ||
|  | 
 | ||
|  | 		if (nullish === true || this.isNull() || this.isUndefined()) return true; | ||
|  | 
 | ||
|  | 		if (nullish === false) return false; | ||
|  | 		if (this.isTruthy()) return false; | ||
|  | 		if (this.isBoolean()) return false; | ||
|  | 		if (this.isString()) return false; | ||
|  | 		if (this.isNumber()) return false; | ||
|  | 		if (this.isBigInt()) return false; | ||
|  | 		if (this.isRegExp()) return false; | ||
|  | 		if (this.isArray()) return false; | ||
|  | 		if (this.isConstArray()) return false; | ||
|  | 		if (this.isTemplateString()) return false; | ||
|  | 		if (this.isRegExp()) return false; | ||
|  | 
 | ||
|  | 		return undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Creates a string representation of this evaluated expression. | ||
|  | 	 * @returns {string | undefined} the string representation or undefined if not possible | ||
|  | 	 */ | ||
|  | 	asString() { | ||
|  | 		if (this.isBoolean()) return `${this.bool}`; | ||
|  | 		if (this.isNull()) return "null"; | ||
|  | 		if (this.isUndefined()) return "undefined"; | ||
|  | 		if (this.isString()) return this.string; | ||
|  | 		if (this.isNumber()) return `${this.number}`; | ||
|  | 		if (this.isBigInt()) return `${this.bigint}`; | ||
|  | 		if (this.isRegExp()) return `${this.regExp}`; | ||
|  | 		if (this.isArray()) { | ||
|  | 			let array = []; | ||
|  | 			for (const item of /** @type {BasicEvaluatedExpression[]} */ ( | ||
|  | 				this.items | ||
|  | 			)) { | ||
|  | 				const itemStr = item.asString(); | ||
|  | 				if (itemStr === undefined) return undefined; | ||
|  | 				array.push(itemStr); | ||
|  | 			} | ||
|  | 			return `${array}`; | ||
|  | 		} | ||
|  | 		if (this.isConstArray()) return `${this.array}`; | ||
|  | 		if (this.isTemplateString()) { | ||
|  | 			let str = ""; | ||
|  | 			for (const part of /** @type {BasicEvaluatedExpression[]} */ ( | ||
|  | 				this.parts | ||
|  | 			)) { | ||
|  | 				const partStr = part.asString(); | ||
|  | 				if (partStr === undefined) return undefined; | ||
|  | 				str += partStr; | ||
|  | 			} | ||
|  | 			return str; | ||
|  | 		} | ||
|  | 		return undefined; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {string} string value | ||
|  | 	 * @returns {BasicEvaluatedExpression} basic evaluated expression | ||
|  | 	 */ | ||
|  | 	setString(string) { | ||
|  | 		this.type = TypeString; | ||
|  | 		this.string = string; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setUndefined() { | ||
|  | 		this.type = TypeUndefined; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setNull() { | ||
|  | 		this.type = TypeNull; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to a number | ||
|  | 	 * @param {number} number number to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setNumber(number) { | ||
|  | 		this.type = TypeNumber; | ||
|  | 		this.number = number; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to a BigInt | ||
|  | 	 * @param {bigint} bigint bigint to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setBigInt(bigint) { | ||
|  | 		this.type = TypeBigInt; | ||
|  | 		this.bigint = bigint; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to a boolean | ||
|  | 	 * @param {boolean} bool boolean to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setBoolean(bool) { | ||
|  | 		this.type = TypeBoolean; | ||
|  | 		this.bool = bool; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to a regular expression | ||
|  | 	 * @param {RegExp} regExp regular expression to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setRegExp(regExp) { | ||
|  | 		this.type = TypeRegExp; | ||
|  | 		this.regExp = regExp; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to a particular identifier and its members. | ||
|  | 	 * | ||
|  | 	 * @param {string | VariableInfoInterface} identifier identifier to set | ||
|  | 	 * @param {string | VariableInfoInterface} rootInfo root info | ||
|  | 	 * @param {() => string[]} getMembers members | ||
|  | 	 * @param {() => boolean[]=} getMembersOptionals optional members | ||
|  | 	 * @param {() => Range[]=} getMemberRanges ranges of progressively increasing sub-expressions | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setIdentifier( | ||
|  | 		identifier, | ||
|  | 		rootInfo, | ||
|  | 		getMembers, | ||
|  | 		getMembersOptionals, | ||
|  | 		getMemberRanges | ||
|  | 	) { | ||
|  | 		this.type = TypeIdentifier; | ||
|  | 		this.identifier = identifier; | ||
|  | 		this.rootInfo = rootInfo; | ||
|  | 		this.getMembers = getMembers; | ||
|  | 		this.getMembersOptionals = getMembersOptionals; | ||
|  | 		this.getMemberRanges = getMemberRanges; | ||
|  | 		this.sideEffects = true; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Wraps an array of expressions with a prefix and postfix expression. | ||
|  | 	 * | ||
|  | 	 * @param {BasicEvaluatedExpression | null | undefined} prefix Expression to be added before the innerExpressions | ||
|  | 	 * @param {BasicEvaluatedExpression | null | undefined} postfix Expression to be added after the innerExpressions | ||
|  | 	 * @param {BasicEvaluatedExpression[]} innerExpressions Expressions to be wrapped | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setWrapped(prefix, postfix, innerExpressions) { | ||
|  | 		this.type = TypeWrapped; | ||
|  | 		this.prefix = prefix; | ||
|  | 		this.postfix = postfix; | ||
|  | 		this.wrappedInnerExpressions = innerExpressions; | ||
|  | 		this.sideEffects = true; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Stores the options of a conditional expression. | ||
|  | 	 * | ||
|  | 	 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setOptions(options) { | ||
|  | 		this.type = TypeConditional; | ||
|  | 		this.options = options; | ||
|  | 		this.sideEffects = true; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Adds options to a conditional expression. | ||
|  | 	 * | ||
|  | 	 * @param {BasicEvaluatedExpression[]} options optional (consequent/alternate) expressions to be added | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	addOptions(options) { | ||
|  | 		if (!this.options) { | ||
|  | 			this.type = TypeConditional; | ||
|  | 			this.options = []; | ||
|  | 			this.sideEffects = true; | ||
|  | 		} | ||
|  | 		for (const item of options) { | ||
|  | 			this.options.push(item); | ||
|  | 		} | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to an array of expressions. | ||
|  | 	 * | ||
|  | 	 * @param {BasicEvaluatedExpression[]} items expressions to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setItems(items) { | ||
|  | 		this.type = TypeArray; | ||
|  | 		this.items = items; | ||
|  | 		this.sideEffects = items.some(i => i.couldHaveSideEffects()); | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to an array of strings. | ||
|  | 	 * | ||
|  | 	 * @param {string[]} array array to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setArray(array) { | ||
|  | 		this.type = TypeConstArray; | ||
|  | 		this.array = array; | ||
|  | 		this.sideEffects = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of this expression to a processed/unprocessed template string. Used | ||
|  | 	 * for evaluating TemplateLiteral expressions in the JavaScript Parser. | ||
|  | 	 * | ||
|  | 	 * @param {BasicEvaluatedExpression[]} quasis template string quasis | ||
|  | 	 * @param {BasicEvaluatedExpression[]} parts template string parts | ||
|  | 	 * @param {"cooked" | "raw"} kind template string kind | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setTemplateString(quasis, parts, kind) { | ||
|  | 		this.type = TypeTemplateString; | ||
|  | 		this.quasis = quasis; | ||
|  | 		this.parts = parts; | ||
|  | 		this.templateStringKind = kind; | ||
|  | 		this.sideEffects = parts.some(p => p.sideEffects); | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setTruthy() { | ||
|  | 		this.falsy = false; | ||
|  | 		this.truthy = true; | ||
|  | 		this.nullish = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	setFalsy() { | ||
|  | 		this.falsy = true; | ||
|  | 		this.truthy = false; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the value of the expression to nullish. | ||
|  | 	 * | ||
|  | 	 * @param {boolean} value true, if the expression is nullish | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setNullish(value) { | ||
|  | 		this.nullish = value; | ||
|  | 
 | ||
|  | 		if (value) return this.setFalsy(); | ||
|  | 
 | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set's the range for the expression. | ||
|  | 	 * | ||
|  | 	 * @param {[number, number]} range range to set | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setRange(range) { | ||
|  | 		this.range = range; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set whether or not the expression has side effects. | ||
|  | 	 * | ||
|  | 	 * @param {boolean} sideEffects true, if the expression has side effects | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setSideEffects(sideEffects = true) { | ||
|  | 		this.sideEffects = sideEffects; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * Set the expression node for the expression. | ||
|  | 	 * | ||
|  | 	 * @param {Node | undefined} expression expression | ||
|  | 	 * @returns {this} this | ||
|  | 	 */ | ||
|  | 	setExpression(expression) { | ||
|  | 		this.expression = expression; | ||
|  | 		return this; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * @param {string} flags regexp flags | ||
|  |  * @returns {boolean} is valid flags | ||
|  |  */ | ||
|  | BasicEvaluatedExpression.isValidRegExpFlags = flags => { | ||
|  | 	const len = flags.length; | ||
|  | 
 | ||
|  | 	if (len === 0) return true; | ||
|  | 	if (len > 4) return false; | ||
|  | 
 | ||
|  | 	// cspell:word gimy
 | ||
|  | 	let remaining = 0b0000; // bit per RegExp flag: gimy
 | ||
|  | 
 | ||
|  | 	for (let i = 0; i < len; i++) | ||
|  | 		switch (flags.charCodeAt(i)) { | ||
|  | 			case 103 /* g */: | ||
|  | 				if (remaining & 0b1000) return false; | ||
|  | 				remaining |= 0b1000; | ||
|  | 				break; | ||
|  | 			case 105 /* i */: | ||
|  | 				if (remaining & 0b0100) return false; | ||
|  | 				remaining |= 0b0100; | ||
|  | 				break; | ||
|  | 			case 109 /* m */: | ||
|  | 				if (remaining & 0b0010) return false; | ||
|  | 				remaining |= 0b0010; | ||
|  | 				break; | ||
|  | 			case 121 /* y */: | ||
|  | 				if (remaining & 0b0001) return false; | ||
|  | 				remaining |= 0b0001; | ||
|  | 				break; | ||
|  | 			default: | ||
|  | 				return false; | ||
|  | 		} | ||
|  | 
 | ||
|  | 	return true; | ||
|  | }; | ||
|  | 
 | ||
|  | module.exports = BasicEvaluatedExpression; |