Initial commit

This commit is contained in:
Brian McGonagill 2026-03-18 09:02:21 -05:00
commit b3a51a4115
10336 changed files with 2381973 additions and 0 deletions

View file

@ -0,0 +1,86 @@
export const Zero64 = { sign: 1, data: [0, 0] };
export const Unit64 = { sign: 1, data: [0, 1] };
export function isZero64(a) {
return a.data[0] === 0 && a.data[1] === 0;
}
export function isStrictlyNegative64(a) {
return a.sign === -1 && !isZero64(a);
}
export function isStrictlyPositive64(a) {
return a.sign === 1 && !isZero64(a);
}
export function isEqual64(a, b) {
if (a.data[0] === b.data[0] && a.data[1] === b.data[1]) {
return a.sign === b.sign || (a.data[0] === 0 && a.data[1] === 0);
}
return false;
}
function isStrictlySmaller64Internal(a, b) {
return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1]);
}
export function isStrictlySmaller64(a, b) {
if (a.sign === b.sign) {
return a.sign === 1
? isStrictlySmaller64Internal(a.data, b.data)
: isStrictlySmaller64Internal(b.data, a.data);
}
return a.sign === -1 && (!isZero64(a) || !isZero64(b));
}
export function clone64(a) {
return { sign: a.sign, data: [a.data[0], a.data[1]] };
}
function substract64DataInternal(a, b) {
let reminderLow = 0;
let low = a[1] - b[1];
if (low < 0) {
reminderLow = 1;
low = low >>> 0;
}
return [a[0] - b[0] - reminderLow, low];
}
function substract64Internal(a, b) {
if (a.sign === 1 && b.sign === -1) {
const low = a.data[1] + b.data[1];
const high = a.data[0] + b.data[0] + (low > 0xffffffff ? 1 : 0);
return { sign: 1, data: [high >>> 0, low >>> 0] };
}
return {
sign: 1,
data: a.sign === 1 ? substract64DataInternal(a.data, b.data) : substract64DataInternal(b.data, a.data),
};
}
export function substract64(arrayIntA, arrayIntB) {
if (isStrictlySmaller64(arrayIntA, arrayIntB)) {
const out = substract64Internal(arrayIntB, arrayIntA);
out.sign = -1;
return out;
}
return substract64Internal(arrayIntA, arrayIntB);
}
export function negative64(arrayIntA) {
return {
sign: -arrayIntA.sign,
data: [arrayIntA.data[0], arrayIntA.data[1]],
};
}
export function add64(arrayIntA, arrayIntB) {
if (isZero64(arrayIntB)) {
if (isZero64(arrayIntA)) {
return clone64(Zero64);
}
return clone64(arrayIntA);
}
return substract64(arrayIntA, negative64(arrayIntB));
}
export function halve64(a) {
return {
sign: a.sign,
data: [Math.floor(a.data[0] / 2), (a.data[0] % 2 === 1 ? 0x80000000 : 0) + Math.floor(a.data[1] / 2)],
};
}
export function logLike64(a) {
return {
sign: a.sign,
data: [0, Math.floor(Math.log(a.data[0] * 0x100000000 + a.data[1]) / Math.log(2))],
};
}

View file

@ -0,0 +1,32 @@
import { BigInt, String } from '../../../utils/globals.js';
const safeMathFloor = Math.floor;
const safeMathLog = Math.log;
export function integerLogLike(v) {
return safeMathFloor(safeMathLog(v) / safeMathLog(2));
}
export function bigIntLogLike(v) {
if (v === BigInt(0))
return BigInt(0);
return BigInt(String(v).length);
}
function biasNumericRange(min, max, logLike) {
if (min === max) {
return [{ min: min, max: max }];
}
if (min < 0 && max > 0) {
const logMin = logLike(-min);
const logMax = logLike(max);
return [
{ min: -logMin, max: logMax },
{ min: (max - logMax), max: max },
{ min: min, max: min + logMin },
];
}
const logGap = logLike((max - min));
const arbCloseToMin = { min: min, max: min + logGap };
const arbCloseToMax = { min: (max - logGap), max: max };
return min < 0
? [arbCloseToMax, arbCloseToMin]
: [arbCloseToMin, arbCloseToMax];
}
export { biasNumericRange };

View file

@ -0,0 +1,21 @@
import { SchedulerImplem } from '../implementations/SchedulerImplem.js';
function buildNextTaskIndex(ordering) {
let numTasks = 0;
return {
clone: () => buildNextTaskIndex(ordering),
nextTaskIndex: (scheduledTasks) => {
if (ordering.length <= numTasks) {
throw new Error(`Invalid schedulerFor defined: too many tasks have been scheduled`);
}
const taskIndex = scheduledTasks.findIndex((t) => t.taskId === ordering[numTasks]);
if (taskIndex === -1) {
throw new Error(`Invalid schedulerFor defined: unable to find next task`);
}
++numTasks;
return taskIndex;
},
};
}
export function buildSchedulerFor(act, ordering) {
return new SchedulerImplem(act, buildNextTaskIndex(ordering));
}

View file

@ -0,0 +1,8 @@
import { NoopSlicedGenerator } from '../implementations/NoopSlicedGenerator.js';
import { SlicedBasedGenerator } from '../implementations/SlicedBasedGenerator.js';
export function buildSlicedGenerator(arb, mrng, slices, biasFactor) {
if (biasFactor === undefined || slices.length === 0 || mrng.nextInt(1, biasFactor) !== 1) {
return new NoopSlicedGenerator(arb, mrng, biasFactor);
}
return new SlicedBasedGenerator(arb, mrng, slices, biasFactor);
}

View file

@ -0,0 +1,22 @@
import { safePush } from '../../../utils/globals.js';
export class CustomEqualSet {
constructor(isEqual) {
this.isEqual = isEqual;
this.data = [];
}
tryAdd(value) {
for (let idx = 0; idx !== this.data.length; ++idx) {
if (this.isEqual(this.data[idx], value)) {
return false;
}
}
safePush(this.data, value);
return true;
}
size() {
return this.data.length;
}
getData() {
return this.data;
}
}

View file

@ -0,0 +1,21 @@
import { safeMapGet, safeMapSet } from '../../../utils/globals.js';
const depthContextCache = new Map();
export function getDepthContextFor(contextMeta) {
if (contextMeta === undefined) {
return { depth: 0 };
}
if (typeof contextMeta !== 'string') {
return contextMeta;
}
const cachedContext = safeMapGet(depthContextCache, contextMeta);
if (cachedContext !== undefined) {
return cachedContext;
}
const context = { depth: 0 };
safeMapSet(depthContextCache, contextMeta, context);
return context;
}
export function createDepthIdentifier() {
const identifier = { depth: 0 };
return identifier;
}

View file

@ -0,0 +1,85 @@
import { clone64, isEqual64 } from './ArrayInt64.js';
const safeNegativeInfinity = Number.NEGATIVE_INFINITY;
const safePositiveInfinity = Number.POSITIVE_INFINITY;
const safeEpsilon = Number.EPSILON;
const INDEX_POSITIVE_INFINITY = { sign: 1, data: [2146435072, 0] };
const INDEX_NEGATIVE_INFINITY = { sign: -1, data: [2146435072, 1] };
const f64 = new Float64Array(1);
const u32 = new Uint32Array(f64.buffer, f64.byteOffset);
function bitCastDoubleToUInt64(f) {
f64[0] = f;
return [u32[1], u32[0]];
}
export function decomposeDouble(d) {
const { 0: hi, 1: lo } = bitCastDoubleToUInt64(d);
const signBit = hi >>> 31;
const exponentBits = (hi >>> 20) & 0x7ff;
const significandBits = (hi & 0xfffff) * 0x100000000 + lo;
const exponent = exponentBits === 0 ? -1022 : exponentBits - 1023;
let significand = exponentBits === 0 ? 0 : 1;
significand += significandBits / 2 ** 52;
significand *= signBit === 0 ? 1 : -1;
return { exponent, significand };
}
function positiveNumberToInt64(n) {
return [~~(n / 0x100000000), n >>> 0];
}
function indexInDoubleFromDecomp(exponent, significand) {
if (exponent === -1022) {
const rescaledSignificand = significand * 2 ** 52;
return positiveNumberToInt64(rescaledSignificand);
}
const rescaledSignificand = (significand - 1) * 2 ** 52;
const exponentOnlyHigh = (exponent + 1023) * 2 ** 20;
const index = positiveNumberToInt64(rescaledSignificand);
index[0] += exponentOnlyHigh;
return index;
}
export function doubleToIndex(d) {
if (d === safePositiveInfinity) {
return clone64(INDEX_POSITIVE_INFINITY);
}
if (d === safeNegativeInfinity) {
return clone64(INDEX_NEGATIVE_INFINITY);
}
const decomp = decomposeDouble(d);
const exponent = decomp.exponent;
const significand = decomp.significand;
if (d > 0 || (d === 0 && 1 / d === safePositiveInfinity)) {
return { sign: 1, data: indexInDoubleFromDecomp(exponent, significand) };
}
else {
const indexOpposite = indexInDoubleFromDecomp(exponent, -significand);
if (indexOpposite[1] === 0xffffffff) {
indexOpposite[0] += 1;
indexOpposite[1] = 0;
}
else {
indexOpposite[1] += 1;
}
return { sign: -1, data: indexOpposite };
}
}
export function indexToDouble(index) {
if (index.sign === -1) {
const indexOpposite = { sign: 1, data: [index.data[0], index.data[1]] };
if (indexOpposite.data[1] === 0) {
indexOpposite.data[0] -= 1;
indexOpposite.data[1] = 0xffffffff;
}
else {
indexOpposite.data[1] -= 1;
}
return -indexToDouble(indexOpposite);
}
if (isEqual64(index, INDEX_POSITIVE_INFINITY)) {
return safePositiveInfinity;
}
if (index.data[0] < 0x200000) {
return (index.data[0] * 0x100000000 + index.data[1]) * 2 ** -1074;
}
const postIndexHigh = index.data[0] - 0x200000;
const exponent = -1021 + (postIndexHigh >> 20);
const significand = 1 + ((postIndexHigh & 0xfffff) * 2 ** 32 + index.data[1]) * safeEpsilon;
return significand * 2 ** exponent;
}

View file

@ -0,0 +1,25 @@
import { refineConstraintsForFloatingOnly } from './FloatingOnlyHelpers.js';
const safeNegativeInfinity = Number.NEGATIVE_INFINITY;
const safePositiveInfinity = Number.POSITIVE_INFINITY;
const safeMaxValue = Number.MAX_VALUE;
export const maxNonIntegerValue = 4503599627370495.5;
export const onlyIntegersAfterThisValue = 4503599627370496;
export function refineConstraintsForDoubleOnly(constraints) {
return refineConstraintsForFloatingOnly(constraints, safeMaxValue, maxNonIntegerValue, onlyIntegersAfterThisValue);
}
export function doubleOnlyMapper(value) {
return value === onlyIntegersAfterThisValue
? safePositiveInfinity
: value === -onlyIntegersAfterThisValue
? safeNegativeInfinity
: value;
}
export function doubleOnlyUnmapper(value) {
if (typeof value !== 'number')
throw new Error('Unsupported type');
return value === safePositiveInfinity
? onlyIntegersAfterThisValue
: value === safeNegativeInfinity
? -onlyIntegersAfterThisValue
: value;
}

View file

@ -0,0 +1,15 @@
const safeObjectKeys = Object.keys;
const safeObjectGetOwnPropertySymbols = Object.getOwnPropertySymbols;
const safeObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
export function extractEnumerableKeys(instance) {
const keys = safeObjectKeys(instance);
const symbols = safeObjectGetOwnPropertySymbols(instance);
for (let index = 0; index !== symbols.length; ++index) {
const symbol = symbols[index];
const descriptor = safeObjectGetOwnPropertyDescriptor(instance, symbol);
if (descriptor && descriptor.enumerable) {
keys.push(symbol);
}
}
return keys;
}

View file

@ -0,0 +1,62 @@
const safeNegativeInfinity = Number.NEGATIVE_INFINITY;
const safePositiveInfinity = Number.POSITIVE_INFINITY;
export const MIN_VALUE_32 = 2 ** -126 * 2 ** -23;
export const MAX_VALUE_32 = 2 ** 127 * (1 + (2 ** 23 - 1) / 2 ** 23);
export const EPSILON_32 = 2 ** -23;
const INDEX_POSITIVE_INFINITY = 2139095040;
const INDEX_NEGATIVE_INFINITY = -2139095041;
const f32 = new Float32Array(1);
const u32 = new Uint32Array(f32.buffer, f32.byteOffset);
function bitCastFloatToUInt32(f) {
f32[0] = f;
return u32[0];
}
export function decomposeFloat(f) {
const bits = bitCastFloatToUInt32(f);
const signBit = bits >>> 31;
const exponentBits = (bits >>> 23) & 0xff;
const significandBits = bits & 0x7fffff;
const exponent = exponentBits === 0 ? -126 : exponentBits - 127;
let significand = exponentBits === 0 ? 0 : 1;
significand += significandBits / 2 ** 23;
significand *= signBit === 0 ? 1 : -1;
return { exponent, significand };
}
function indexInFloatFromDecomp(exponent, significand) {
if (exponent === -126) {
return significand * 0x800000;
}
return (exponent + 127) * 0x800000 + (significand - 1) * 0x800000;
}
export function floatToIndex(f) {
if (f === safePositiveInfinity) {
return INDEX_POSITIVE_INFINITY;
}
if (f === safeNegativeInfinity) {
return INDEX_NEGATIVE_INFINITY;
}
const decomp = decomposeFloat(f);
const exponent = decomp.exponent;
const significand = decomp.significand;
if (f > 0 || (f === 0 && 1 / f === safePositiveInfinity)) {
return indexInFloatFromDecomp(exponent, significand);
}
else {
return -indexInFloatFromDecomp(exponent, -significand) - 1;
}
}
export function indexToFloat(index) {
if (index < 0) {
return -indexToFloat(-index - 1);
}
if (index === INDEX_POSITIVE_INFINITY) {
return safePositiveInfinity;
}
if (index < 0x1000000) {
return index * 2 ** -149;
}
const postIndex = index - 0x1000000;
const exponent = -125 + (postIndex >> 23);
const significand = 1 + (postIndex & 0x7fffff) / 0x800000;
return significand * 2 ** exponent;
}

View file

@ -0,0 +1,26 @@
import { MAX_VALUE_32 } from './FloatHelpers.js';
import { refineConstraintsForFloatingOnly } from './FloatingOnlyHelpers.js';
const safeNegativeInfinity = Number.NEGATIVE_INFINITY;
const safePositiveInfinity = Number.POSITIVE_INFINITY;
const safeMaxValue = MAX_VALUE_32;
export const maxNonIntegerValue = 8388607.5;
export const onlyIntegersAfterThisValue = 8388608;
export function refineConstraintsForFloatOnly(constraints) {
return refineConstraintsForFloatingOnly(constraints, safeMaxValue, maxNonIntegerValue, onlyIntegersAfterThisValue);
}
export function floatOnlyMapper(value) {
return value === onlyIntegersAfterThisValue
? safePositiveInfinity
: value === -onlyIntegersAfterThisValue
? safeNegativeInfinity
: value;
}
export function floatOnlyUnmapper(value) {
if (typeof value !== 'number')
throw new Error('Unsupported type');
return value === safePositiveInfinity
? onlyIntegersAfterThisValue
: value === safeNegativeInfinity
? -onlyIntegersAfterThisValue
: value;
}

View file

@ -0,0 +1,30 @@
const safeNumberIsInteger = Number.isInteger;
const safeObjectIs = Object.is;
const safeNegativeInfinity = Number.NEGATIVE_INFINITY;
const safePositiveInfinity = Number.POSITIVE_INFINITY;
export function refineConstraintsForFloatingOnly(constraints, maxValue, maxNonIntegerValue, onlyIntegersAfterThisValue) {
const { noDefaultInfinity = false, minExcluded = false, maxExcluded = false, min = noDefaultInfinity ? -maxValue : safeNegativeInfinity, max = noDefaultInfinity ? maxValue : safePositiveInfinity, } = constraints;
const effectiveMin = minExcluded
? min < -maxNonIntegerValue
? -onlyIntegersAfterThisValue
: Math.max(min, -maxNonIntegerValue)
: min === safeNegativeInfinity
? Math.max(min, -onlyIntegersAfterThisValue)
: Math.max(min, -maxNonIntegerValue);
const effectiveMax = maxExcluded
? max > maxNonIntegerValue
? onlyIntegersAfterThisValue
: Math.min(max, maxNonIntegerValue)
: max === safePositiveInfinity
? Math.min(max, onlyIntegersAfterThisValue)
: Math.min(max, maxNonIntegerValue);
const fullConstraints = {
noDefaultInfinity: false,
minExcluded: minExcluded || ((min !== safeNegativeInfinity || minExcluded) && safeNumberIsInteger(effectiveMin)),
maxExcluded: maxExcluded || ((max !== safePositiveInfinity || maxExcluded) && safeNumberIsInteger(effectiveMax)),
min: safeObjectIs(effectiveMin, -0) ? 0 : effectiveMin,
max: safeObjectIs(effectiveMax, 0) ? -0 : effectiveMax,
noNaN: constraints.noNaN || false,
};
return fullConstraints;
}

View file

@ -0,0 +1,51 @@
import { safePop, safePush } from '../../../utils/globals.js';
const safeStringFromCodePoint = String.fromCodePoint;
const safeMathMin = Math.min;
const safeMathMax = Math.max;
export function convertGraphemeRangeToMapToConstantEntry(range) {
if (range.length === 1) {
const codePointString = safeStringFromCodePoint(range[0]);
return { num: 1, build: () => codePointString };
}
const rangeStart = range[0];
return { num: range[1] - range[0] + 1, build: (idInGroup) => safeStringFromCodePoint(rangeStart + idInGroup) };
}
export function intersectGraphemeRanges(rangesA, rangesB) {
const mergedRanges = [];
let cursorA = 0;
let cursorB = 0;
while (cursorA < rangesA.length && cursorB < rangesB.length) {
const rangeA = rangesA[cursorA];
const rangeAMin = rangeA[0];
const rangeAMax = rangeA.length === 1 ? rangeA[0] : rangeA[1];
const rangeB = rangesB[cursorB];
const rangeBMin = rangeB[0];
const rangeBMax = rangeB.length === 1 ? rangeB[0] : rangeB[1];
if (rangeAMax < rangeBMin) {
cursorA += 1;
}
else if (rangeBMax < rangeAMin) {
cursorB += 1;
}
else {
let min = safeMathMax(rangeAMin, rangeBMin);
const max = safeMathMin(rangeAMax, rangeBMax);
if (mergedRanges.length >= 1) {
const lastMergedRange = mergedRanges[mergedRanges.length - 1];
const lastMergedRangeMax = lastMergedRange.length === 1 ? lastMergedRange[0] : lastMergedRange[1];
if (lastMergedRangeMax + 1 === min) {
min = lastMergedRange[0];
safePop(mergedRanges);
}
}
safePush(mergedRanges, min === max ? [min] : [min, max]);
if (rangeAMax <= max) {
cursorA += 1;
}
if (rangeBMax <= max) {
cursorB += 1;
}
}
}
return mergedRanges;
}

View file

@ -0,0 +1,10 @@
export function filterInvalidSubdomainLabel(subdomainLabel) {
if (subdomainLabel.length > 63) {
return false;
}
return (subdomainLabel.length < 4 ||
subdomainLabel[0] !== 'x' ||
subdomainLabel[1] !== 'n' ||
subdomainLabel[2] !== '-' ||
subdomainLabel[3] !== '-');
}

View file

@ -0,0 +1,33 @@
import { Map, safeMapGet, safeMapSet } from '../../../utils/globals.js';
const safeObjectIs = Object.is;
export function isSubarrayOf(source, small) {
const countMap = new Map();
let countMinusZero = 0;
for (const sourceEntry of source) {
if (safeObjectIs(sourceEntry, -0)) {
++countMinusZero;
}
else {
const oldCount = safeMapGet(countMap, sourceEntry) || 0;
safeMapSet(countMap, sourceEntry, oldCount + 1);
}
}
for (let index = 0; index !== small.length; ++index) {
if (!(index in small)) {
return false;
}
const smallEntry = small[index];
if (safeObjectIs(smallEntry, -0)) {
if (countMinusZero === 0)
return false;
--countMinusZero;
}
else {
const oldCount = safeMapGet(countMap, smallEntry) || 0;
if (oldCount === 0)
return false;
safeMapSet(countMap, smallEntry, oldCount - 1);
}
}
return true;
}

View file

@ -0,0 +1,14 @@
import { boolean } from '../../boolean.js';
import { constant } from '../../constant.js';
import { double } from '../../double.js';
export function jsonConstraintsBuilder(stringArbitrary, constraints) {
const { depthSize, maxDepth } = constraints;
const key = stringArbitrary;
const values = [
boolean(),
double({ noDefaultInfinity: true, noNaN: true }),
stringArbitrary,
constant(null),
];
return { key, values, depthSize, maxDepth };
}

View file

@ -0,0 +1,83 @@
import { readConfigureGlobal } from '../../../check/runner/configuration/GlobalParameters.js';
import { safeIndexOf } from '../../../utils/globals.js';
const safeMathFloor = Math.floor;
const safeMathMin = Math.min;
export const MaxLengthUpperBound = 0x7fffffff;
const orderedSize = ['xsmall', 'small', 'medium', 'large', 'xlarge'];
const orderedRelativeSize = ['-4', '-3', '-2', '-1', '=', '+1', '+2', '+3', '+4'];
export const DefaultSize = 'small';
export function maxLengthFromMinLength(minLength, size) {
switch (size) {
case 'xsmall':
return safeMathFloor(1.1 * minLength) + 1;
case 'small':
return 2 * minLength + 10;
case 'medium':
return 11 * minLength + 100;
case 'large':
return 101 * minLength + 1000;
case 'xlarge':
return 1001 * minLength + 10000;
default:
throw new Error(`Unable to compute lengths based on received size: ${size}`);
}
}
export function relativeSizeToSize(size, defaultSize) {
const sizeInRelative = safeIndexOf(orderedRelativeSize, size);
if (sizeInRelative === -1) {
return size;
}
const defaultSizeInSize = safeIndexOf(orderedSize, defaultSize);
if (defaultSizeInSize === -1) {
throw new Error(`Unable to offset size based on the unknown defaulted one: ${defaultSize}`);
}
const resultingSizeInSize = defaultSizeInSize + sizeInRelative - 4;
return resultingSizeInSize < 0
? orderedSize[0]
: resultingSizeInSize >= orderedSize.length
? orderedSize[orderedSize.length - 1]
: orderedSize[resultingSizeInSize];
}
export function maxGeneratedLengthFromSizeForArbitrary(size, minLength, maxLength, specifiedMaxLength) {
const { baseSize: defaultSize = DefaultSize, defaultSizeToMaxWhenMaxSpecified } = readConfigureGlobal() || {};
const definedSize = size !== undefined ? size : specifiedMaxLength && defaultSizeToMaxWhenMaxSpecified ? 'max' : defaultSize;
if (definedSize === 'max') {
return maxLength;
}
const finalSize = relativeSizeToSize(definedSize, defaultSize);
return safeMathMin(maxLengthFromMinLength(minLength, finalSize), maxLength);
}
export function depthBiasFromSizeForArbitrary(depthSizeOrSize, specifiedMaxDepth) {
if (typeof depthSizeOrSize === 'number') {
return 1 / depthSizeOrSize;
}
const { baseSize: defaultSize = DefaultSize, defaultSizeToMaxWhenMaxSpecified } = readConfigureGlobal() || {};
const definedSize = depthSizeOrSize !== undefined
? depthSizeOrSize
: specifiedMaxDepth && defaultSizeToMaxWhenMaxSpecified
? 'max'
: defaultSize;
if (definedSize === 'max') {
return 0;
}
const finalSize = relativeSizeToSize(definedSize, defaultSize);
switch (finalSize) {
case 'xsmall':
return 1;
case 'small':
return 0.5;
case 'medium':
return 0.25;
case 'large':
return 0.125;
case 'xlarge':
return 0.0625;
}
}
export function resolveSize(size) {
const { baseSize: defaultSize = DefaultSize } = readConfigureGlobal() || {};
if (size === undefined) {
return defaultSize;
}
return relativeSizeToSize(size, defaultSize);
}

View file

@ -0,0 +1,11 @@
import { Value } from '../../../check/arbitrary/definition/Value.js';
export const UndefinedContextPlaceholder = Symbol('UndefinedContextPlaceholder');
export function noUndefinedAsContext(value) {
if (value.context !== undefined) {
return value;
}
if (value.hasToBeCloned) {
return new Value(value.value_, UndefinedContextPlaceholder, () => value.value);
}
return new Value(value.value_, UndefinedContextPlaceholder);
}

View file

@ -0,0 +1,46 @@
import { boolean } from '../../boolean.js';
import { constant } from '../../constant.js';
import { double } from '../../double.js';
import { fullUnicodeString } from '../../fullUnicodeString.js';
import { maxSafeInteger } from '../../maxSafeInteger.js';
import { oneof } from '../../oneof.js';
import { string } from '../../string.js';
import { boxedArbitraryBuilder } from '../builders/BoxedArbitraryBuilder.js';
function defaultValues(constraints, stringArbitrary) {
return [
boolean(),
maxSafeInteger(),
double(),
stringArbitrary(constraints),
oneof(stringArbitrary(constraints), constant(null), constant(undefined)),
];
}
function boxArbitraries(arbs) {
return arbs.map((arb) => boxedArbitraryBuilder(arb));
}
function boxArbitrariesIfNeeded(arbs, boxEnabled) {
return boxEnabled ? boxArbitraries(arbs).concat(arbs) : arbs;
}
export function toQualifiedObjectConstraints(settings = {}) {
function orDefault(optionalValue, defaultValue) {
return optionalValue !== undefined ? optionalValue : defaultValue;
}
const stringArbitrary = 'stringUnit' in settings ? string : settings.withUnicodeString ? fullUnicodeString : string;
const valueConstraints = { size: settings.size, unit: settings.stringUnit };
return {
key: orDefault(settings.key, stringArbitrary(valueConstraints)),
values: boxArbitrariesIfNeeded(orDefault(settings.values, defaultValues(valueConstraints, stringArbitrary)), orDefault(settings.withBoxedValues, false)),
depthSize: settings.depthSize,
maxDepth: settings.maxDepth,
maxKeys: settings.maxKeys,
size: settings.size,
withSet: orDefault(settings.withSet, false),
withMap: orDefault(settings.withMap, false),
withObjectString: orDefault(settings.withObjectString, false),
withNullPrototype: orDefault(settings.withNullPrototype, false),
withBigInt: orDefault(settings.withBigInt, false),
withDate: orDefault(settings.withDate, false),
withTypedArray: orDefault(settings.withTypedArray, false),
withSparseArray: orDefault(settings.withSparseArray, false),
};
}

View file

@ -0,0 +1,207 @@
function charSizeAt(text, pos) {
return text[pos] >= '\uD800' && text[pos] <= '\uDBFF' && text[pos + 1] >= '\uDC00' && text[pos + 1] <= '\uDFFF'
? 2
: 1;
}
function isHexaDigit(char) {
return (char >= '0' && char <= '9') || (char >= 'a' && char <= 'f') || (char >= 'A' && char <= 'F');
}
function isDigit(char) {
return char >= '0' && char <= '9';
}
function squaredBracketBlockContentEndFrom(text, from) {
for (let index = from; index !== text.length; ++index) {
const char = text[index];
if (char === '\\') {
index += 1;
}
else if (char === ']') {
return index;
}
}
throw new Error(`Missing closing ']'`);
}
function parenthesisBlockContentEndFrom(text, from) {
let numExtraOpened = 0;
for (let index = from; index !== text.length; ++index) {
const char = text[index];
if (char === '\\') {
index += 1;
}
else if (char === ')') {
if (numExtraOpened === 0) {
return index;
}
numExtraOpened -= 1;
}
else if (char === '[') {
index = squaredBracketBlockContentEndFrom(text, index);
}
else if (char === '(') {
numExtraOpened += 1;
}
}
throw new Error(`Missing closing ')'`);
}
function curlyBracketBlockContentEndFrom(text, from) {
let foundComma = false;
for (let index = from; index !== text.length; ++index) {
const char = text[index];
if (isDigit(char)) {
}
else if (from === index) {
return -1;
}
else if (char === ',') {
if (foundComma) {
return -1;
}
foundComma = true;
}
else if (char === '}') {
return index;
}
else {
return -1;
}
}
return -1;
}
export var TokenizerBlockMode;
(function (TokenizerBlockMode) {
TokenizerBlockMode[TokenizerBlockMode["Full"] = 0] = "Full";
TokenizerBlockMode[TokenizerBlockMode["Character"] = 1] = "Character";
})(TokenizerBlockMode || (TokenizerBlockMode = {}));
function blockEndFrom(text, from, unicodeMode, mode) {
switch (text[from]) {
case '[': {
if (mode === TokenizerBlockMode.Character) {
return from + 1;
}
return squaredBracketBlockContentEndFrom(text, from + 1) + 1;
}
case '{': {
if (mode === TokenizerBlockMode.Character) {
return from + 1;
}
const foundEnd = curlyBracketBlockContentEndFrom(text, from + 1);
if (foundEnd === -1) {
return from + 1;
}
return foundEnd + 1;
}
case '(': {
if (mode === TokenizerBlockMode.Character) {
return from + 1;
}
return parenthesisBlockContentEndFrom(text, from + 1) + 1;
}
case ']':
case '}':
case ')':
return from + 1;
case '\\': {
const next1 = text[from + 1];
switch (next1) {
case 'x':
if (isHexaDigit(text[from + 2]) && isHexaDigit(text[from + 3])) {
return from + 4;
}
throw new Error(`Unexpected token '${text.substring(from, from + 4)}' found`);
case 'u':
if (text[from + 2] === '{') {
if (!unicodeMode) {
return from + 2;
}
if (text[from + 4] === '}') {
if (isHexaDigit(text[from + 3])) {
return from + 5;
}
throw new Error(`Unexpected token '${text.substring(from, from + 5)}' found`);
}
if (text[from + 5] === '}') {
if (isHexaDigit(text[from + 3]) && isHexaDigit(text[from + 4])) {
return from + 6;
}
throw new Error(`Unexpected token '${text.substring(from, from + 6)}' found`);
}
if (text[from + 6] === '}') {
if (isHexaDigit(text[from + 3]) && isHexaDigit(text[from + 4]) && isHexaDigit(text[from + 5])) {
return from + 7;
}
throw new Error(`Unexpected token '${text.substring(from, from + 7)}' found`);
}
if (text[from + 7] === '}') {
if (isHexaDigit(text[from + 3]) &&
isHexaDigit(text[from + 4]) &&
isHexaDigit(text[from + 5]) &&
isHexaDigit(text[from + 6])) {
return from + 8;
}
throw new Error(`Unexpected token '${text.substring(from, from + 8)}' found`);
}
if (text[from + 8] === '}' &&
isHexaDigit(text[from + 3]) &&
isHexaDigit(text[from + 4]) &&
isHexaDigit(text[from + 5]) &&
isHexaDigit(text[from + 6]) &&
isHexaDigit(text[from + 7])) {
return from + 9;
}
throw new Error(`Unexpected token '${text.substring(from, from + 9)}' found`);
}
if (isHexaDigit(text[from + 2]) &&
isHexaDigit(text[from + 3]) &&
isHexaDigit(text[from + 4]) &&
isHexaDigit(text[from + 5])) {
return from + 6;
}
throw new Error(`Unexpected token '${text.substring(from, from + 6)}' found`);
case 'p':
case 'P': {
if (!unicodeMode) {
return from + 2;
}
let subIndex = from + 2;
for (; subIndex < text.length && text[subIndex] !== '}'; subIndex += text[subIndex] === '\\' ? 2 : 1) {
}
if (text[subIndex] !== '}') {
throw new Error(`Invalid \\P definition`);
}
return subIndex + 1;
}
case 'k': {
let subIndex = from + 2;
for (; subIndex < text.length && text[subIndex] !== '>'; ++subIndex) {
}
if (text[subIndex] !== '>') {
if (!unicodeMode) {
return from + 2;
}
throw new Error(`Invalid \\k definition`);
}
return subIndex + 1;
}
default: {
if (isDigit(next1)) {
const maxIndex = unicodeMode ? text.length : Math.min(from + 4, text.length);
let subIndex = from + 2;
for (; subIndex < maxIndex && isDigit(text[subIndex]); ++subIndex) {
}
return subIndex;
}
const charSize = unicodeMode ? charSizeAt(text, from + 1) : 1;
return from + charSize + 1;
}
}
}
default: {
const charSize = unicodeMode ? charSizeAt(text, from) : 1;
return from + charSize;
}
}
}
export function readFrom(text, from, unicodeMode, mode) {
const to = blockEndFrom(text, from, unicodeMode, mode);
return text.substring(from, to);
}

View file

@ -0,0 +1,34 @@
import { Set, safeAdd, safePush } from '../../../utils/globals.js';
const safeObjectIs = Object.is;
export class SameValueSet {
constructor(selector) {
this.selector = selector;
this.selectedItemsExceptMinusZero = new Set();
this.data = [];
this.hasMinusZero = false;
}
tryAdd(value) {
const selected = this.selector(value);
if (safeObjectIs(selected, -0)) {
if (this.hasMinusZero) {
return false;
}
safePush(this.data, value);
this.hasMinusZero = true;
return true;
}
const sizeBefore = this.selectedItemsExceptMinusZero.size;
safeAdd(this.selectedItemsExceptMinusZero, selected);
if (sizeBefore !== this.selectedItemsExceptMinusZero.size) {
safePush(this.data, value);
return true;
}
return false;
}
size() {
return this.data.length;
}
getData() {
return this.data;
}
}

View file

@ -0,0 +1,24 @@
import { Set, safeAdd, safePush } from '../../../utils/globals.js';
export class SameValueZeroSet {
constructor(selector) {
this.selector = selector;
this.selectedItems = new Set();
this.data = [];
}
tryAdd(value) {
const selected = this.selector(value);
const sizeBefore = this.selectedItems.size;
safeAdd(this.selectedItems, selected);
if (sizeBefore !== this.selectedItems.size) {
safePush(this.data, value);
return true;
}
return false;
}
size() {
return this.data.length;
}
getData() {
return this.data;
}
}

View file

@ -0,0 +1,81 @@
import { stringify } from '../../../utils/stringify.js';
function raiseUnsupportedASTNode(astNode) {
return new Error(`Unsupported AST node! Received: ${stringify(astNode)}`);
}
function addMissingDotStarTraversalAddMissing(astNode, isFirst, isLast) {
if (!isFirst && !isLast) {
return astNode;
}
const traversalResults = { hasStart: false, hasEnd: false };
const revampedNode = addMissingDotStarTraversal(astNode, isFirst, isLast, traversalResults);
const missingStart = isFirst && !traversalResults.hasStart;
const missingEnd = isLast && !traversalResults.hasEnd;
if (!missingStart && !missingEnd) {
return revampedNode;
}
const expressions = [];
if (missingStart) {
expressions.push({ type: 'Assertion', kind: '^' });
expressions.push({
type: 'Repetition',
quantifier: { type: 'Quantifier', kind: '*', greedy: true },
expression: { type: 'Char', kind: 'meta', symbol: '.', value: '.', codePoint: Number.NaN },
});
}
expressions.push(revampedNode);
if (missingEnd) {
expressions.push({
type: 'Repetition',
quantifier: { type: 'Quantifier', kind: '*', greedy: true },
expression: { type: 'Char', kind: 'meta', symbol: '.', value: '.', codePoint: Number.NaN },
});
expressions.push({ type: 'Assertion', kind: '$' });
}
return { type: 'Group', capturing: false, expression: { type: 'Alternative', expressions } };
}
function addMissingDotStarTraversal(astNode, isFirst, isLast, traversalResults) {
switch (astNode.type) {
case 'Char':
return astNode;
case 'Repetition':
return astNode;
case 'Quantifier':
throw new Error(`Wrongly defined AST tree, Quantifier nodes not supposed to be scanned!`);
case 'Alternative':
traversalResults.hasStart = true;
traversalResults.hasEnd = true;
return Object.assign(Object.assign({}, astNode), { expressions: astNode.expressions.map((node, index) => addMissingDotStarTraversalAddMissing(node, isFirst && index === 0, isLast && index === astNode.expressions.length - 1)) });
case 'CharacterClass':
return astNode;
case 'ClassRange':
return astNode;
case 'Group': {
return Object.assign(Object.assign({}, astNode), { expression: addMissingDotStarTraversal(astNode.expression, isFirst, isLast, traversalResults) });
}
case 'Disjunction': {
traversalResults.hasStart = true;
traversalResults.hasEnd = true;
return Object.assign(Object.assign({}, astNode), { left: astNode.left !== null ? addMissingDotStarTraversalAddMissing(astNode.left, isFirst, isLast) : null, right: astNode.right !== null ? addMissingDotStarTraversalAddMissing(astNode.right, isFirst, isLast) : null });
}
case 'Assertion': {
if (astNode.kind === '^' || astNode.kind === 'Lookahead') {
traversalResults.hasStart = true;
return astNode;
}
else if (astNode.kind === '$' || astNode.kind === 'Lookbehind') {
traversalResults.hasEnd = true;
return astNode;
}
else {
throw new Error(`Assertions of kind ${astNode.kind} not implemented yet!`);
}
}
case 'Backreference':
return astNode;
default:
throw raiseUnsupportedASTNode(astNode);
}
}
export function addMissingDotStar(astNode) {
return addMissingDotStarTraversalAddMissing(astNode, true, true);
}

View file

@ -0,0 +1,28 @@
import { stream } from '../../../stream/Stream.js';
import { Value } from '../../../check/arbitrary/definition/Value.js';
import { BigInt } from '../../../utils/globals.js';
function halveBigInt(n) {
return n / BigInt(2);
}
export function shrinkBigInt(current, target, tryTargetAsap) {
const realGap = current - target;
function* shrinkDecr() {
let previous = tryTargetAsap ? undefined : target;
const gap = tryTargetAsap ? realGap : halveBigInt(realGap);
for (let toremove = gap; toremove > 0; toremove = halveBigInt(toremove)) {
const next = current - toremove;
yield new Value(next, previous);
previous = next;
}
}
function* shrinkIncr() {
let previous = tryTargetAsap ? undefined : target;
const gap = tryTargetAsap ? realGap : halveBigInt(realGap);
for (let toremove = gap; toremove < 0; toremove = halveBigInt(toremove)) {
const next = current - toremove;
yield new Value(next, previous);
previous = next;
}
}
return realGap > 0 ? stream(shrinkDecr()) : stream(shrinkIncr());
}

View file

@ -0,0 +1,32 @@
import { Value } from '../../../check/arbitrary/definition/Value.js';
import { stream } from '../../../stream/Stream.js';
const safeMathCeil = Math.ceil;
const safeMathFloor = Math.floor;
function halvePosInteger(n) {
return safeMathFloor(n / 2);
}
function halveNegInteger(n) {
return safeMathCeil(n / 2);
}
export function shrinkInteger(current, target, tryTargetAsap) {
const realGap = current - target;
function* shrinkDecr() {
let previous = tryTargetAsap ? undefined : target;
const gap = tryTargetAsap ? realGap : halvePosInteger(realGap);
for (let toremove = gap; toremove > 0; toremove = halvePosInteger(toremove)) {
const next = toremove === realGap ? target : current - toremove;
yield new Value(next, previous);
previous = next;
}
}
function* shrinkIncr() {
let previous = tryTargetAsap ? undefined : target;
const gap = tryTargetAsap ? realGap : halveNegInteger(realGap);
for (let toremove = gap; toremove < 0; toremove = halveNegInteger(toremove)) {
const next = toremove === realGap ? target : current - toremove;
yield new Value(next, previous);
previous = next;
}
}
return realGap > 0 ? stream(shrinkDecr()) : stream(shrinkIncr());
}

View file

@ -0,0 +1,78 @@
import { safeGet, safePush, safeSet } from '../../../utils/globals.js';
import { patternsToStringUnmapperIsValidLength } from '../mappers/PatternsToString.js';
import { MaxLengthUpperBound } from './MaxLengthFromMinLength.js';
import { tokenizeString } from './TokenizeString.js';
const dangerousStrings = [
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__',
'constructor',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf',
'apply',
'arguments',
'bind',
'call',
'caller',
'length',
'name',
'prototype',
'key',
'ref',
];
function computeCandidateStringLegacy(dangerous, charArbitrary, stringSplitter) {
let candidate;
try {
candidate = stringSplitter(dangerous);
}
catch (err) {
return undefined;
}
for (const entry of candidate) {
if (!charArbitrary.canShrinkWithoutContext(entry)) {
return undefined;
}
}
return candidate;
}
export function createSlicesForStringLegacy(charArbitrary, stringSplitter) {
const slicesForString = [];
for (const dangerous of dangerousStrings) {
const candidate = computeCandidateStringLegacy(dangerous, charArbitrary, stringSplitter);
if (candidate !== undefined) {
safePush(slicesForString, candidate);
}
}
return slicesForString;
}
const slicesPerArbitrary = new WeakMap();
function createSlicesForStringNoConstraints(charArbitrary) {
const slicesForString = [];
for (const dangerous of dangerousStrings) {
const candidate = tokenizeString(charArbitrary, dangerous, 0, MaxLengthUpperBound);
if (candidate !== undefined) {
safePush(slicesForString, candidate);
}
}
return slicesForString;
}
export function createSlicesForString(charArbitrary, constraints) {
let slices = safeGet(slicesPerArbitrary, charArbitrary);
if (slices === undefined) {
slices = createSlicesForStringNoConstraints(charArbitrary);
safeSet(slicesPerArbitrary, charArbitrary, slices);
}
const slicesForConstraints = [];
for (const slice of slices) {
if (patternsToStringUnmapperIsValidLength(slice, constraints)) {
safePush(slicesForConstraints, slice);
}
}
return slicesForConstraints;
}

View file

@ -0,0 +1,29 @@
import { safeAdd, safePush, Set } from '../../../utils/globals.js';
const safeNumberIsNaN = Number.isNaN;
export class StrictlyEqualSet {
constructor(selector) {
this.selector = selector;
this.selectedItemsExceptNaN = new Set();
this.data = [];
}
tryAdd(value) {
const selected = this.selector(value);
if (safeNumberIsNaN(selected)) {
safePush(this.data, value);
return true;
}
const sizeBefore = this.selectedItemsExceptNaN.size;
safeAdd(this.selectedItemsExceptNaN, selected);
if (sizeBefore !== this.selectedItemsExceptNaN.size) {
safePush(this.data, value);
return true;
}
return false;
}
size() {
return this.data.length;
}
getData() {
return this.data;
}
}

View file

@ -0,0 +1,6 @@
export function escapeForTemplateString(originalText) {
return originalText.replace(/([$`\\])/g, '\\$1').replace(/\r/g, '\\r');
}
export function escapeForMultilineComments(originalText) {
return originalText.replace(/\*\//g, '*\\/');
}

View file

@ -0,0 +1,46 @@
import { BigInt, safePush } from '../../../utils/globals.js';
export function countToggledBits(n) {
let count = 0;
while (n > BigInt(0)) {
if (n & BigInt(1))
++count;
n >>= BigInt(1);
}
return count;
}
export function computeNextFlags(flags, nextSize) {
const allowedMask = (BigInt(1) << BigInt(nextSize)) - BigInt(1);
const preservedFlags = flags & allowedMask;
let numMissingFlags = countToggledBits(flags - preservedFlags);
let nFlags = preservedFlags;
for (let mask = BigInt(1); mask <= allowedMask && numMissingFlags !== 0; mask <<= BigInt(1)) {
if (!(nFlags & mask)) {
nFlags |= mask;
--numMissingFlags;
}
}
return nFlags;
}
export function computeTogglePositions(chars, toggleCase) {
const positions = [];
for (let idx = chars.length - 1; idx !== -1; --idx) {
if (toggleCase(chars[idx]) !== chars[idx])
safePush(positions, idx);
}
return positions;
}
export function computeFlagsFromChars(untoggledChars, toggledChars, togglePositions) {
let flags = BigInt(0);
for (let idx = 0, mask = BigInt(1); idx !== togglePositions.length; ++idx, mask <<= BigInt(1)) {
if (untoggledChars[togglePositions[idx]] !== toggledChars[togglePositions[idx]]) {
flags |= mask;
}
}
return flags;
}
export function applyFlagsOnChars(chars, flags, togglePositions, toggleCase) {
for (let idx = 0, mask = BigInt(1); idx !== togglePositions.length; ++idx, mask <<= BigInt(1)) {
if (flags & mask)
chars[togglePositions[idx]] = toggleCase(chars[togglePositions[idx]]);
}
}

View file

@ -0,0 +1,320 @@
import { safeIndexOf } from '../../../utils/globals.js';
import { TokenizerBlockMode, readFrom } from './ReadRegex.js';
const safeStringFromCodePoint = String.fromCodePoint;
function safePop(tokens) {
const previous = tokens.pop();
if (previous === undefined) {
throw new Error('Unable to extract token preceeding the currently parsed one');
}
return previous;
}
function isDigit(char) {
return char >= '0' && char <= '9';
}
function simpleChar(char, escaped) {
return {
type: 'Char',
kind: 'simple',
symbol: char,
value: char,
codePoint: char.codePointAt(0) || -1,
escaped,
};
}
function metaEscapedChar(block, symbol) {
return {
type: 'Char',
kind: 'meta',
symbol,
value: block,
codePoint: symbol.codePointAt(0) || -1,
};
}
function toSingleToken(tokens, allowEmpty) {
if (tokens.length > 1) {
return {
type: 'Alternative',
expressions: tokens,
};
}
if (!allowEmpty && tokens.length === 0) {
throw new Error(`Unsupported no token`);
}
return tokens[0];
}
function blockToCharToken(block) {
if (block[0] === '\\') {
const next = block[1];
switch (next) {
case 'x': {
const allDigits = block.substring(2);
const codePoint = Number.parseInt(allDigits, 16);
const symbol = safeStringFromCodePoint(codePoint);
return { type: 'Char', kind: 'hex', symbol, value: block, codePoint };
}
case 'u': {
if (block === '\\u') {
return simpleChar('u', true);
}
const allDigits = block[2] === '{' ? block.substring(3, block.length - 1) : block.substring(2);
const codePoint = Number.parseInt(allDigits, 16);
const symbol = safeStringFromCodePoint(codePoint);
return { type: 'Char', kind: 'unicode', symbol, value: block, codePoint };
}
case '0': {
return metaEscapedChar(block, '\0');
}
case 'n': {
return metaEscapedChar(block, '\n');
}
case 'f': {
return metaEscapedChar(block, '\f');
}
case 'r': {
return metaEscapedChar(block, '\r');
}
case 't': {
return metaEscapedChar(block, '\t');
}
case 'v': {
return metaEscapedChar(block, '\v');
}
case 'w':
case 'W':
case 'd':
case 'D':
case 's':
case 'S':
case 'b':
case 'B': {
return { type: 'Char', kind: 'meta', symbol: undefined, value: block, codePoint: Number.NaN };
}
default: {
if (isDigit(next)) {
const allDigits = block.substring(1);
const codePoint = Number(allDigits);
const symbol = safeStringFromCodePoint(codePoint);
return { type: 'Char', kind: 'decimal', symbol, value: block, codePoint };
}
if (block.length > 2 && (next === 'p' || next === 'P')) {
throw new Error(`UnicodeProperty not implemented yet!`);
}
const char = block.substring(1);
return simpleChar(char, true);
}
}
}
return simpleChar(block);
}
function pushTokens(tokens, regexSource, unicodeMode, groups) {
let disjunctions = null;
for (let index = 0, block = readFrom(regexSource, index, unicodeMode, TokenizerBlockMode.Full); index !== regexSource.length; index += block.length, block = readFrom(regexSource, index, unicodeMode, TokenizerBlockMode.Full)) {
const firstInBlock = block[0];
switch (firstInBlock) {
case '|': {
if (disjunctions === null) {
disjunctions = [];
}
disjunctions.push(toSingleToken(tokens.splice(0), true) || null);
break;
}
case '.': {
tokens.push({ type: 'Char', kind: 'meta', symbol: block, value: block, codePoint: Number.NaN });
break;
}
case '*':
case '+': {
const previous = safePop(tokens);
tokens.push({
type: 'Repetition',
expression: previous,
quantifier: { type: 'Quantifier', kind: firstInBlock, greedy: true },
});
break;
}
case '?': {
const previous = safePop(tokens);
if (previous.type === 'Repetition') {
previous.quantifier.greedy = false;
tokens.push(previous);
}
else {
tokens.push({
type: 'Repetition',
expression: previous,
quantifier: { type: 'Quantifier', kind: firstInBlock, greedy: true },
});
}
break;
}
case '{': {
if (block === '{') {
tokens.push(simpleChar(block));
break;
}
const previous = safePop(tokens);
const quantifierText = block.substring(1, block.length - 1);
const quantifierTokens = quantifierText.split(',');
const from = Number(quantifierTokens[0]);
const to = quantifierTokens.length === 1
? from
: quantifierTokens[1].length !== 0
? Number(quantifierTokens[1])
: undefined;
tokens.push({
type: 'Repetition',
expression: previous,
quantifier: { type: 'Quantifier', kind: 'Range', greedy: true, from, to },
});
break;
}
case '[': {
const blockContent = block.substring(1, block.length - 1);
const subTokens = [];
let negative = undefined;
let previousWasSimpleDash = false;
for (let subIndex = 0, subBlock = readFrom(blockContent, subIndex, unicodeMode, TokenizerBlockMode.Character); subIndex !== blockContent.length; subIndex += subBlock.length,
subBlock = readFrom(blockContent, subIndex, unicodeMode, TokenizerBlockMode.Character)) {
if (subIndex === 0 && subBlock === '^') {
negative = true;
continue;
}
const newToken = blockToCharToken(subBlock);
if (subBlock === '-') {
subTokens.push(newToken);
previousWasSimpleDash = true;
}
else {
const operand1Token = subTokens.length >= 2 ? subTokens[subTokens.length - 2] : undefined;
if (previousWasSimpleDash && operand1Token !== undefined && operand1Token.type === 'Char') {
subTokens.pop();
subTokens.pop();
subTokens.push({ type: 'ClassRange', from: operand1Token, to: newToken });
}
else {
subTokens.push(newToken);
}
previousWasSimpleDash = false;
}
}
tokens.push({ type: 'CharacterClass', expressions: subTokens, negative });
break;
}
case '(': {
const blockContent = block.substring(1, block.length - 1);
const subTokens = [];
if (blockContent[0] === '?') {
if (blockContent[1] === ':') {
pushTokens(subTokens, blockContent.substring(2), unicodeMode, groups);
tokens.push({
type: 'Group',
capturing: false,
expression: toSingleToken(subTokens),
});
}
else if (blockContent[1] === '=' || blockContent[1] === '!') {
pushTokens(subTokens, blockContent.substring(2), unicodeMode, groups);
tokens.push({
type: 'Assertion',
kind: 'Lookahead',
negative: blockContent[1] === '!' ? true : undefined,
assertion: toSingleToken(subTokens),
});
}
else if (blockContent[1] === '<' && (blockContent[2] === '=' || blockContent[2] === '!')) {
pushTokens(subTokens, blockContent.substring(3), unicodeMode, groups);
tokens.push({
type: 'Assertion',
kind: 'Lookbehind',
negative: blockContent[2] === '!' ? true : undefined,
assertion: toSingleToken(subTokens),
});
}
else {
const chunks = blockContent.split('>');
if (chunks.length < 2 || chunks[0][1] !== '<') {
throw new Error(`Unsupported regex content found at ${JSON.stringify(block)}`);
}
const groupIndex = ++groups.lastIndex;
const nameRaw = chunks[0].substring(2);
groups.named.set(nameRaw, groupIndex);
pushTokens(subTokens, chunks.slice(1).join('>'), unicodeMode, groups);
tokens.push({
type: 'Group',
capturing: true,
nameRaw,
name: nameRaw,
number: groupIndex,
expression: toSingleToken(subTokens),
});
}
}
else {
const groupIndex = ++groups.lastIndex;
pushTokens(subTokens, blockContent, unicodeMode, groups);
tokens.push({
type: 'Group',
capturing: true,
number: groupIndex,
expression: toSingleToken(subTokens),
});
}
break;
}
default: {
if (block === '^') {
tokens.push({ type: 'Assertion', kind: block });
}
else if (block === '$') {
tokens.push({ type: 'Assertion', kind: block });
}
else if (block[0] === '\\' && isDigit(block[1])) {
const reference = Number(block.substring(1));
if (unicodeMode || reference <= groups.lastIndex) {
tokens.push({ type: 'Backreference', kind: 'number', number: reference, reference });
}
else {
tokens.push(blockToCharToken(block));
}
}
else if (block[0] === '\\' && block[1] === 'k' && block.length !== 2) {
const referenceRaw = block.substring(3, block.length - 1);
tokens.push({
type: 'Backreference',
kind: 'name',
number: groups.named.get(referenceRaw) || 0,
referenceRaw,
reference: referenceRaw,
});
}
else {
tokens.push(blockToCharToken(block));
}
break;
}
}
}
if (disjunctions !== null) {
disjunctions.push(toSingleToken(tokens.splice(0), true) || null);
let currentDisjunction = {
type: 'Disjunction',
left: disjunctions[0],
right: disjunctions[1],
};
for (let index = 2; index < disjunctions.length; ++index) {
currentDisjunction = {
type: 'Disjunction',
left: currentDisjunction,
right: disjunctions[index],
};
}
tokens.push(currentDisjunction);
}
}
export function tokenizeRegex(regex) {
const unicodeMode = safeIndexOf([...regex.flags], 'u') !== -1;
const regexSource = regex.source;
const tokens = [];
pushTokens(tokens, regexSource, unicodeMode, { lastIndex: 0, named: new Map() });
return toSingleToken(tokens);
}

View file

@ -0,0 +1,34 @@
import { safePop, safePush, safeSubstring } from '../../../utils/globals.js';
export function tokenizeString(patternsArb, value, minLength, maxLength) {
if (value.length === 0) {
if (minLength > 0) {
return undefined;
}
return [];
}
if (maxLength <= 0) {
return undefined;
}
const stack = [{ endIndexChunks: 0, nextStartIndex: 1, chunks: [] }];
while (stack.length > 0) {
const last = safePop(stack);
for (let index = last.nextStartIndex; index <= value.length; ++index) {
const chunk = safeSubstring(value, last.endIndexChunks, index);
if (patternsArb.canShrinkWithoutContext(chunk)) {
const newChunks = [...last.chunks, chunk];
if (index === value.length) {
if (newChunks.length < minLength) {
break;
}
return newChunks;
}
safePush(stack, { endIndexChunks: last.endIndexChunks, nextStartIndex: index + 1, chunks: last.chunks });
if (newChunks.length < maxLength) {
safePush(stack, { endIndexChunks: index, nextStartIndex: index + 1, chunks: newChunks });
}
break;
}
}
}
return undefined;
}

View file

@ -0,0 +1,27 @@
function initZippedValues(its) {
const vs = [];
for (let index = 0; index !== its.length; ++index) {
vs.push(its[index].next());
}
return vs;
}
function nextZippedValues(its, vs) {
for (let index = 0; index !== its.length; ++index) {
vs[index] = its[index].next();
}
}
function isDoneZippedValues(vs) {
for (let index = 0; index !== vs.length; ++index) {
if (vs[index].done) {
return true;
}
}
return false;
}
export function* zipIterableIterators(...its) {
const vs = initZippedValues(its);
while (!isDoneZippedValues(vs)) {
yield vs.map((v) => v.value);
nextZippedValues(its, vs);
}
}