diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 4ed4d318fd746..2dfe110075e86 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -106,6 +106,7 @@ The PR that reveals the implementation of the `$(_XHarnessArgs) --symbol-patterns wasm-symbol-patterns.txt <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbolicator WasmSymbolicator.dll,Microsoft.WebAssembly.Internal.SymbolicatorWrapperForXHarness <_XHarnessArgs Condition="'$(_WasmBrowserPathForTests)' != ''" >$(_XHarnessArgs) "--browser-path=$(_WasmBrowserPathForTests)" - <_XHarnessArgs Condition="'$(_XHarnessTestsTimeout)' != '' " >$(_XHarnessArgs) "--timeout=$(_XHarnessTestsTimeout)" + <_XHarnessArgs Condition="'$(WasmXHarnessTestsTimeout)' != ''" >$(_XHarnessArgs) "--timeout=$(WasmXHarnessTestsTimeout)" <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 1841dd20ae9a0..87dbe5f5a8e59 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -40,7 +40,7 @@ <_XHarnessArgs Condition="'$(WasmSingleFileBundle)' != 'true'" >$(_XHarnessArgs) --engine-arg=--dir=. <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) - <_XHarnessArgs Condition="'$(_XHarnessTestsTimeout)' != '' " >$(_XHarnessArgs) "--timeout=$(_XHarnessTestsTimeout)" + <_XHarnessArgs Condition="'$(WasmXHarnessTestsTimeout)' != ''" >$(_XHarnessArgs) "--timeout=$(WasmXHarnessTestsTimeout)" <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) <_InvariantGlobalization Condition="'$(InvariantGlobalization)' == 'true'">--env=DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index dab07a052796a..9c019508e4732 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -16,7 +16,7 @@ <_ShellCommandSeparator Condition="'$(OS)' == 'Windows_NT'">&& <_ShellCommandSeparator Condition="'$(OS)' != 'Windows_NT'">&& true - <_XHarnessTestsTimeout>00:30:00 + 00:30:00 $(BundleDir) diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index f9fa62eb8cbb6..0890f13f5bded 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -783,8 +783,8 @@ void FASTCALL decodeCallPattern(int pattern, #define DENORMALIZE_STACK_SLOT(x) ((x)<<3) #define NORMALIZE_CODE_LENGTH(x) ((x)>>2) // All Instructions are 4 bytes long #define DENORMALIZE_CODE_LENGTH(x) ((x)<<2) -#define NORMALIZE_STACK_BASE_REGISTER(x) ((x)^22) // Encode Frame pointer fp=$22 as zero -#define DENORMALIZE_STACK_BASE_REGISTER(x) ((x)^22) +#define NORMALIZE_STACK_BASE_REGISTER(x) ((x) == 22 ? 0 : 1) // Encode Frame pointer fp=$22 as zero +#define DENORMALIZE_STACK_BASE_REGISTER(x) ((x) == 0 ? 22 : 3) #define NORMALIZE_SIZE_OF_STACK_AREA(x) ((x)>>3) #define DENORMALIZE_SIZE_OF_STACK_AREA(x) ((x)<<3) #define CODE_OFFSETS_NEED_NORMALIZATION 0 @@ -804,8 +804,7 @@ void FASTCALL decodeCallPattern(int pattern, #define CODE_LENGTH_ENCBASE 8 #define SIZE_OF_RETURN_KIND_IN_SLIM_HEADER 2 #define SIZE_OF_RETURN_KIND_IN_FAT_HEADER 4 -////TODO for LOONGARCH64. -// FP/SP encoded as 0 or 2 ?? +// FP/SP encoded as 0 or 1. #define STACK_BASE_REGISTER_ENCBASE 2 #define SIZE_OF_STACK_AREA_ENCBASE 3 #define SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE 4 diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 2d4671b166e54..960db9b96f039 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -11982,7 +11982,14 @@ void Compiler::gtDispTree(GenTree* tree, { switch (tree->AsBlk()->gtBlkOpKind) { + case GenTreeBlk::BlkOpKindCpObjUnroll: + printf(" (CpObjUnroll)"); + break; #ifdef TARGET_XARCH + case GenTreeBlk::BlkOpKindCpObjRepInstr: + printf(" (CpObjRepInstr)"); + break; + case GenTreeBlk::BlkOpKindRepInstr: printf(" (RepInstr)"); break; @@ -19588,6 +19595,11 @@ GenTree* Compiler::gtNewSimdBinOpNode( assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); intrinsic = NI_AVX_Divide; } + else if (simdSize == 64) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); + intrinsic = NI_AVX512F_Divide; + } else if (simdBaseType == TYP_FLOAT) { intrinsic = NI_SSE_Divide; @@ -19718,6 +19730,11 @@ GenTree* Compiler::gtNewSimdBinOpNode( assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); intrinsic = NI_AVX2_MultiplyLow; } + else if (simdSize == 64) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX512BW)); + intrinsic = NI_AVX512BW_MultiplyLow; + } else { intrinsic = NI_SSE2_MultiplyLow; @@ -19733,6 +19750,11 @@ GenTree* Compiler::gtNewSimdBinOpNode( assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); intrinsic = NI_AVX2_MultiplyLow; } + else if (simdSize == 64) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); + intrinsic = NI_AVX512F_MultiplyLow; + } else if (compOpportunisticallyDependsOn(InstructionSet_SSE41)) { intrinsic = NI_SSE41_MultiplyLow; @@ -19782,10 +19804,18 @@ GenTree* Compiler::gtNewSimdBinOpNode( case TYP_LONG: case TYP_ULONG: { - assert((simdSize == 16) || (simdSize == 32)); + assert((simdSize == 16) || (simdSize == 32) || (simdSize == 64)); assert(compIsaSupportedDebugOnly(InstructionSet_AVX512DQ_VL)); - intrinsic = NI_AVX512DQ_VL_MultiplyLow; + if (simdSize != 64) + { + intrinsic = NI_AVX512DQ_VL_MultiplyLow; + } + else + { + intrinsic = NI_AVX512DQ_MultiplyLow; + } + break; } @@ -19796,6 +19826,11 @@ GenTree* Compiler::gtNewSimdBinOpNode( assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); intrinsic = NI_AVX_Multiply; } + else if (simdSize == 64) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); + intrinsic = NI_AVX512F_Multiply; + } else { intrinsic = NI_SSE_Multiply; @@ -19810,6 +19845,11 @@ GenTree* Compiler::gtNewSimdBinOpNode( assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); intrinsic = NI_AVX_Multiply; } + else if (simdSize == 64) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); + intrinsic = NI_AVX512F_Multiply; + } else { intrinsic = NI_SSE2_Multiply; diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 57476944fed88..cb7039116612f 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -263,6 +263,9 @@ HARDWARE_INTRINSIC(Vector512, BitwiseOr, HARDWARE_INTRINSIC(Vector512, Create, 64, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, CreateScalar, 64, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, CreateScalarUnsafe, 64, 1, {INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movd, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_NoRMWSemantics) +HARDWARE_INTRINSIC(Vector512, ConvertToSingle, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector512, ConvertToInt32, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(Vector512, Divide, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, Equals, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, EqualsAll, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, EqualsAny, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) @@ -278,14 +281,17 @@ HARDWARE_INTRINSIC(Vector512, Load, HARDWARE_INTRINSIC(Vector512, LoadAligned, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, LoadAlignedNonTemporal, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, LoadUnsafe, 64, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector512, Multiply, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, Narrow, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, OnesComplement, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, op_Addition, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, op_BitwiseAnd, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) HARDWARE_INTRINSIC(Vector512, op_BitwiseOr, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) +HARDWARE_INTRINSIC(Vector512, op_Division, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, op_Equality, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) HARDWARE_INTRINSIC(Vector512, op_ExclusiveOr, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, op_Inequality, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_Commutative) +HARDWARE_INTRINSIC(Vector512, op_Multiply, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, op_OnesComplement, 64, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, op_Subtraction, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, Store, 64, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index e32befa5c637c..b6a6901ba52e0 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -1004,12 +1004,25 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector128_ConvertToInt32: case NI_Vector256_ConvertToInt32: + case NI_Vector512_ConvertToInt32: { assert(sig->numArgs == 1); assert(simdBaseType == TYP_FLOAT); - intrinsic = (simdSize == 32) ? NI_AVX_ConvertToVector256Int32WithTruncation - : NI_SSE2_ConvertToVector128Int32WithTruncation; + switch (simdSize) + { + case 16: + intrinsic = NI_SSE2_ConvertToVector128Int32WithTruncation; + break; + case 32: + intrinsic = NI_AVX_ConvertToVector256Int32WithTruncation; + break; + case 64: + intrinsic = NI_AVX512F_ConvertToVector512Int32WithTruncation; + break; + default: + unreached(); + } op1 = impSIMDPopStack(retType); retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize); @@ -1018,12 +1031,26 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector128_ConvertToSingle: case NI_Vector256_ConvertToSingle: + case NI_Vector512_ConvertToSingle: { assert(sig->numArgs == 1); if (simdBaseType == TYP_INT) { - intrinsic = (simdSize == 32) ? NI_AVX_ConvertToVector256Single : NI_SSE2_ConvertToVector128Single; + switch (simdSize) + { + case 16: + intrinsic = NI_SSE2_ConvertToVector128Single; + break; + case 32: + intrinsic = NI_AVX_ConvertToVector256Single; + break; + case 64: + intrinsic = NI_AVX512F_ConvertToVector512Single; + break; + default: + unreached(); + } op1 = impSIMDPopStack(retType); retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize); @@ -1279,8 +1306,10 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector128_Divide: case NI_Vector256_Divide: + case NI_Vector512_Divide: case NI_Vector128_op_Division: case NI_Vector256_op_Division: + case NI_Vector512_op_Division: { assert(sig->numArgs == 2); @@ -1950,8 +1979,10 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector128_Multiply: case NI_Vector256_Multiply: + case NI_Vector512_Multiply: case NI_Vector128_op_Multiply: case NI_Vector256_op_Multiply: + case NI_Vector512_op_Multiply: { assert(sig->numArgs == 2); @@ -1961,6 +1992,8 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + assert(simdSize != 64 || IsBaselineVector512IsaSupportedDebugOnly()); + if ((simdBaseType == TYP_BYTE) || (simdBaseType == TYP_UBYTE)) { // TODO-XARCH-CQ: We should support byte/sbyte multiplication @@ -1969,13 +2002,12 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, if (varTypeIsLong(simdBaseType)) { - assert((simdSize == 16) || (simdSize == 32)); - - if (!compOpportunisticallyDependsOn(InstructionSet_AVX512DQ_VL)) + if (simdSize != 64 && !compOpportunisticallyDependsOn(InstructionSet_AVX512DQ_VL)) { // TODO-XARCH-CQ: We should support long/ulong multiplication break; } +// else if simdSize == 64 then above assert would check if baseline isa supported #if defined(TARGET_X86) // TODO-XARCH-CQ: We need to support 64-bit CreateBroadcast diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 89e9d42dccec6..7b710aab8eeca 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -2254,7 +2254,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) if (cmp->OperIsCompare() && CheckImmedAndMakeContained(cmp, op1)) { std::swap(cmp->gtOp1, cmp->gtOp2); - cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper)); + cmp->SetOper(cmp->SwapRelop(cmp->gtOper)); return; } @@ -2271,7 +2271,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) { MakeSrcContained(cmp, op1); std::swap(cmp->gtOp1, cmp->gtOp2); - cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper)); + cmp->SetOper(cmp->SwapRelop(cmp->gtOper)); return; } } diff --git a/src/coreclr/vm/loongarch64/thunktemplates.S b/src/coreclr/vm/loongarch64/thunktemplates.S index eeaf61752d067..e53d2b9626a8e 100644 --- a/src/coreclr/vm/loongarch64/thunktemplates.S +++ b/src/coreclr/vm/loongarch64/thunktemplates.S @@ -4,20 +4,20 @@ #include "unixasmmacros.inc" #include "asmconstants.h" -; Note that the offsets specified in pcaddi must match the behavior of GetStubCodePageSize() on this architecture/os. +// Note that the offsets specified in pcaddi must match the behavior of GetStubCodePageSize() on this architecture/os. LEAF_ENTRY StubPrecodeCode - pcaddi $r21, 0x4004 //4, for Type encoding. + pcaddi $r21, 0x1004 //4, for Type encoding. ld.d $t2,$r21, (StubPrecodeData__MethodDesc - 4*4) ld.d $r21,$r21, (StubPrecodeData__Target - 4*4) jirl $r0,$r21,0 LEAF_END_MARKED StubPrecodeCode LEAF_ENTRY FixupPrecodeCode - pcaddi $r21, 0x4003 //3, for Type encoding. + pcaddi $r21, 0x1003 //3, for Type encoding. ld.d $r21,$r21, (FixupPrecodeData__Target - 4*3) jirl $r0,$r21,0 - pcaddi $r21, 0x4003 + pcaddi $r21, 0x1003 ld.d $t2,$r21, (FixupPrecodeData__MethodDesc - 4*3 -4*3) ld.d $r21,$r21, (FixupPrecodeData__PrecodeFixupThunk - 4*3 - 4*3) jirl $r0,$r21,0 @@ -26,7 +26,7 @@ LEAF_END_MARKED FixupPrecodeCode // NOTE: For LoongArch64 `CallCountingStubData__RemainingCallCountCell` must be zero !!! // Because the stub-identifying token is $t1 within the `OnCallCountThresholdReachedStub`. LEAF_ENTRY CallCountingStubCode - pcaddi $t2, 0x4000 + pcaddi $t2, 0x1000 ld.d $t1, $t2, CallCountingStubData__RemainingCallCountCell ld.h $r21, $t1, 0 addi.w $r21, $r21, -1 diff --git a/src/libraries/Common/src/System/Obsoletions.cs b/src/libraries/Common/src/System/Obsoletions.cs index 3f35696bfe430..668e38d151854 100644 --- a/src/libraries/Common/src/System/Obsoletions.cs +++ b/src/libraries/Common/src/System/Obsoletions.cs @@ -165,5 +165,8 @@ internal static class Obsoletions internal const string LegacyFormatterImplMessage = "This API supports obsolete formatter-based serialization. It should not be called or extended by application code."; internal const string LegacyFormatterImplDiagId = "SYSLIB0051"; + + internal const string RegexExtensibilityImplMessage = "This API supports obsolete mechanisms for Regex extensibility. It is not supported."; + internal const string RegexExtensibilityDiagId = "SYSLIB0052"; } } diff --git a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/HPackDecoderTest.cs b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/HPackDecoderTest.cs index 0e94e8c7c6d6e..28e7a97107366 100644 --- a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/HPackDecoderTest.cs +++ b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/HPackDecoderTest.cs @@ -413,7 +413,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_SingleBuffer() _decoder.Decode(encoded, endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -429,7 +429,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameLengthBrokenIntoSe _decoder.Decode(encoded[..1], endHeaders: false, handler: _handler); _decoder.Decode(encoded[1..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -445,7 +445,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameBrokenIntoSeparate _decoder.Decode(encoded[..(_literalHeaderNameString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[(_literalHeaderNameString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -461,7 +461,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameAndValueBrokenInto _decoder.Decode(encoded[..^_headerValue.Length], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^_headerValue.Length..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -477,7 +477,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueLengthBrokenIntoS _decoder.Decode(encoded[..^(_headerValue.Length - 1)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValue.Length - 1)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -493,7 +493,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueBrokenIntoSeparat _decoder.Decode(encoded[..^(_headerValueString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValueString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } diff --git a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs index 8db70d84ee05e..931ecbcd9bd2e 100644 --- a/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs +++ b/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs @@ -183,7 +183,7 @@ public void LiteralFieldWithoutNameReference_SingleBuffer() _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); _decoder.Decode(encoded, endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -199,7 +199,7 @@ public void LiteralFieldWithoutNameReference_NameLengthBrokenIntoSeparateBuffers _decoder.Decode(encoded[..1], endHeaders: false, handler: _handler); _decoder.Decode(encoded[1..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -215,7 +215,7 @@ public void LiteralFieldWithoutNameReference_NameBrokenIntoSeparateBuffers() _decoder.Decode(encoded[..(_literalHeaderFieldString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[(_literalHeaderFieldString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -231,7 +231,7 @@ public void LiteralFieldWithoutNameReference_NameAndValueBrokenIntoSeparateBuffe _decoder.Decode(encoded[..^_headerValue.Length], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^_headerValue.Length..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -247,7 +247,7 @@ public void LiteralFieldWithoutNameReference_ValueLengthBrokenIntoSeparateBuffer _decoder.Decode(encoded[..^(_headerValue.Length - 1)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValue.Length - 1)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -263,7 +263,7 @@ public void LiteralFieldWithoutNameReference_ValueBrokenIntoSeparateBuffers() _decoder.Decode(encoded[..^(_headerValueString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValueString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableListTestBase.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableListTestBase.cs index 1144529e79076..7fc27620ad48a 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableListTestBase.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableListTestBase.cs @@ -419,6 +419,7 @@ public void BinarySearch() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85012", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public void BinarySearchPartialSortedList() { ImmutableArray reverseSorted = ImmutableArray.CreateRange(Enumerable.Range(1, 150).Select(n => n * 2).Reverse()); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs index b3b5a8495cf5b..d2776c1c577bb 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlDictionaryWriterTest.cs @@ -495,6 +495,7 @@ void AssertBytesWritten(Action action, XmlBinaryNodeType no } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85013", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] public static void XmlBaseWriter_WriteString() { const byte Chars8Text = 152; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index f0bb547502280..f2f1f5a130fe2 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -639,6 +639,7 @@ internal JsonTypeInfo? AncestorPolymorphicType get { Debug.Assert(IsConfigured); + Debug.Assert(Type != typeof(object)); if (!_isAncestorPolymorphicTypeResolved) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs index e81103b9c8bd4..b1bd5541ef4c8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs @@ -252,7 +252,7 @@ public static bool IsSupportedDerivedType(Type baseType, Type? derivedType) => // First, walk up the class hierarchy for any supported types. for (Type? candidate = typeInfo.Type.BaseType; candidate != null; candidate = candidate.BaseType) { - JsonTypeInfo? candidateInfo = typeInfo.Options.GetTypeInfoInternal(candidate, ensureNotNull: null); + JsonTypeInfo? candidateInfo = ResolveAncestorTypeInfo(candidate, typeInfo.Options); if (candidateInfo?.PolymorphismOptions != null) { // stop on the first ancestor that has a match @@ -264,7 +264,7 @@ public static bool IsSupportedDerivedType(Type baseType, Type? derivedType) => // Now, walk the interface hierarchy for any polymorphic interface declarations. foreach (Type interfaceType in typeInfo.Type.GetInterfaces()) { - JsonTypeInfo? candidateInfo = typeInfo.Options.GetTypeInfoInternal(interfaceType, ensureNotNull: null); + JsonTypeInfo? candidateInfo = ResolveAncestorTypeInfo(interfaceType, typeInfo.Options); if (candidateInfo?.PolymorphismOptions != null) { if (matchingResult != null) @@ -294,6 +294,20 @@ public static bool IsSupportedDerivedType(Type baseType, Type? derivedType) => } return matchingResult; + + static JsonTypeInfo? ResolveAncestorTypeInfo(Type type, JsonSerializerOptions options) + { + try + { + return options.GetTypeInfoInternal(type, ensureNotNull: null); + } + catch + { + // The resolver produced an exception when resolving the ancestor type. + // Eat the exception and report no result instead. + return null; + } + } } /// diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs index 56158ea03b192..46f2202b1420e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs @@ -519,6 +519,36 @@ public async Task AnonymousType() Assert.Equal(Expected, json); } + [Fact] + public async Task CustomResolverWithFailingAncestorType_DoesNotSurfaceException() + { + var options = new JsonSerializerOptions + { + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + static typeInfo => + { + if (typeInfo.Type == typeof(MyThing) || + typeInfo.Type == typeof(IList)) + { + throw new InvalidOperationException("some latent custom resolution bug"); + } + } + } + } + }; + + object value = new MyDerivedThing { Number = 42 }; + string json = await Serializer.SerializeWrapper(value, options); + Assert.Equal("""{"Number":42}""", json); + + value = new int[] { 1, 2, 3 }; + json = await Serializer.SerializeWrapper(value, options); + Assert.Equal("[1,2,3]", json); + } + class MyClass { public string Value { get; set; } @@ -535,6 +565,10 @@ class MyThing : IThing public int Number { get; set; } } + class MyDerivedThing : MyThing + { + } + class MyThingCollection : List { } class MyThingDictionary : Dictionary { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 565f6a8ab3daf..ac3753c325db0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -15,7 +15,7 @@ true true - $(WasmXHarnessArgs) --timeout=1800 + 00:45:00 true diff --git a/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs b/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs index 71800c81baeb1..949f4939062bf 100644 --- a/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs +++ b/src/libraries/System.Text.RegularExpressions/ref/System.Text.RegularExpressions.cs @@ -183,6 +183,8 @@ public static void CompileToAssembly(System.Text.RegularExpressions.RegexCompila public int[] GetGroupNumbers() { throw null; } public string GroupNameFromNumber(int i) { throw null; } public int GroupNumberFromName(string name) { throw null; } + [System.ObsoleteAttribute("This API supports obsolete mechanisms for Regex extensibility. It is not supported.", DiagnosticId = "SYSLIB0052", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] protected void InitializeReferences() { } public bool IsMatch(System.ReadOnlySpan input) { throw null; } public bool IsMatch(System.ReadOnlySpan input, int startat) { throw null; } @@ -226,8 +228,12 @@ protected void InitializeReferences() { } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } public override string ToString() { throw null; } public static string Unescape(string str) { throw null; } + [System.ObsoleteAttribute("This API supports obsolete mechanisms for Regex extensibility. It is not supported.", DiagnosticId = "SYSLIB0052", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] protected bool UseOptionC() { throw null; } - protected internal bool UseOptionR() { throw null; } + [System.ObsoleteAttribute("This API supports obsolete mechanisms for Regex extensibility. It is not supported.", DiagnosticId = "SYSLIB0052", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + protected bool UseOptionR() { throw null; } protected internal static void ValidateMatchTimeout(System.TimeSpan matchTimeout) { } public ref partial struct ValueMatchEnumerator { @@ -336,6 +342,7 @@ private RegexParseException() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public abstract partial class RegexRunner { protected internal int[]? runcrawl; @@ -355,6 +362,7 @@ public abstract partial class RegexRunner protected internal RegexRunner() { } protected void Capture(int capnum, int start, int end) { } public static bool CharInClass(char ch, string charClass) { throw null; } + [System.ObsoleteAttribute("This API supports obsolete mechanisms for Regex extensibility. It is not supported.", DiagnosticId = "SYSLIB0052", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] protected static bool CharInSet(char ch, string @set, string category) { throw null; } protected void CheckTimeout() { } protected void Crawl(int i) { } @@ -372,12 +380,15 @@ protected void EnsureStorage() { } protected int MatchIndex(int cap) { throw null; } protected int MatchLength(int cap) { throw null; } protected int Popcrawl() { throw null; } + [System.ObsoleteAttribute("This API supports obsolete mechanisms for Regex extensibility. It is not supported.", DiagnosticId = "SYSLIB0052", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] protected internal System.Text.RegularExpressions.Match? Scan(System.Text.RegularExpressions.Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick) { throw null; } + [System.ObsoleteAttribute("This API supports obsolete mechanisms for Regex extensibility. It is not supported.", DiagnosticId = "SYSLIB0052", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] protected internal System.Text.RegularExpressions.Match? Scan(System.Text.RegularExpressions.Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick, System.TimeSpan timeout) { throw null; } protected internal virtual void Scan(System.ReadOnlySpan text) { throw null; } protected void TransferCapture(int capnum, int uncapnum, int start, int end) { } protected void Uncapture() { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public abstract partial class RegexRunnerFactory { protected RegexRunnerFactory() { } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs index d41516f57bde6..3053eb2055099 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs @@ -367,6 +367,8 @@ public int GroupNumberFromName(string name) Interlocked.CompareExchange(ref _replref, new WeakReference(null), null) ?? _replref; + [Obsolete(Obsoletions.RegexExtensibilityImplMessage, DiagnosticId = Obsoletions.RegexExtensibilityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [EditorBrowsable(EditorBrowsableState.Never)] protected void InitializeReferences() { // This method no longer has anything to initialize. It continues to exist @@ -615,9 +617,13 @@ private RegexRunner CreateRunner() => factory!.CreateInstance(); /// True if the option was set. + [Obsolete(Obsoletions.RegexExtensibilityImplMessage, DiagnosticId = Obsoletions.RegexExtensibilityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [EditorBrowsable(EditorBrowsableState.Never)] protected bool UseOptionC() => (roptions & RegexOptions.Compiled) != 0; /// True if the option was set. - protected internal bool UseOptionR() => RightToLeft; + [Obsolete(Obsoletions.RegexExtensibilityImplMessage, DiagnosticId = Obsoletions.RegexExtensibilityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [EditorBrowsable(EditorBrowsableState.Never)] + protected bool UseOptionR() => RightToLeft; } } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs index dd1097a0d51e2..c1ea7cb3be905 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs @@ -1,24 +1,25 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// This RegexRunner class is a base class for compiled regex code. +// This RegexRunner class is a base class for source-generated regex extensibility +// (and the old CompileToAssembly extensibility). It's not intended to be used +// by anything else. // Implementation notes: -// It provides the driver code that call's the subclass's Go() +// It provides the driver code that call's the subclass's Scan // method for either scanning or direct execution. -// // It also maintains memory allocation for the backtracking stack, // the grouping stack and the longjump crawlstack, and provides // methods to push new subpattern match results into (or remove // backtracked results from) the Match instance. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; using System.Runtime.CompilerServices; namespace System.Text.RegularExpressions { + [EditorBrowsable(EditorBrowsableState.Never)] public abstract class RegexRunner { protected internal int runtextbeg; // Beginning of text to search. We now always use a sliced span of the input @@ -118,15 +119,15 @@ protected internal virtual void Scan(ReadOnlySpan text) InternalScan(runregex!, beginning, beginning + text.Length); } - // TODO https://github.com/dotnet/runtime/issues/62573: Obsolete this. + [Obsolete(Obsoletions.RegexExtensibilityImplMessage, DiagnosticId = Obsoletions.RegexExtensibilityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] protected Match? Scan(Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick) => Scan(regex, text, textbeg, textend, textstart, prevlen, quick, regex.MatchTimeout); - // TODO https://github.com/dotnet/runtime/issues/62573: Obsolete this. /// /// This method's body is only kept since it is a protected member that could be called by someone outside /// the assembly. /// + [Obsolete(Obsoletions.RegexExtensibilityImplMessage, DiagnosticId = Obsoletions.RegexExtensibilityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] protected internal Match? Scan(Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick, TimeSpan timeout) { InitializeTimeout(timeout); @@ -393,6 +394,7 @@ internal static bool IsECMABoundary(ReadOnlySpan inputSpan, int index) ((uint)index < (uint)inputSpan.Length && RegexCharClass.IsECMAWordChar(inputSpan[index])); } + [Obsolete(Obsoletions.RegexExtensibilityImplMessage, DiagnosticId = Obsoletions.RegexExtensibilityDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] protected static bool CharInSet(char ch, string set, string category) { string charClass = RegexCharClass.ConvertOldStringsToClass(set, category); diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunnerFactory.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunnerFactory.cs index 93860f20cdf47..4fd0c89cb040f 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunnerFactory.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunnerFactory.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; + namespace System.Text.RegularExpressions { + [EditorBrowsable(EditorBrowsableState.Never)] public abstract class RegexRunnerFactory { protected RegexRunnerFactory() { } diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs index 850d0ac01264f..829432b466302 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs @@ -28,377 +28,336 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// .dat files from http://gsf.cococlyde.org/download converted to [InputData(...)] with: -// ------------------------------------- -// using System; -// using System.Collections.Generic; -// using System.IO; -// using System.Linq; -// -// class Program -// { -// static void Main() -// { -// using var writer = new StreamWriter(@"output.txt"); -// string[][] tests = File.ReadLines(@"C:\Users\stoub\Desktop\New folder\interpretation.dat") -// .Select(s => s.Trim()) -// .Where(s => s.Length > 0 && !s.StartsWith("NOTE") && !s.StartsWith("#")) -// .Select(s => s.Split('\t', StringSplitOptions.RemoveEmptyEntries)) -// .Where(s => s.Length >= 4 && !s[3].Contains("?")) -// .Where(s => !s[1].StartsWith("[[")) -// .ToArray(); -// -// var seen = new HashSet(); -// foreach (string[] test in tests) -// { -// string pattern = test[1].Replace("\\", "\\\\").Replace("\\\\n", "\\n").Replace("?-u", "?-i"); -// string input = test[2].Replace("\\", "\\\\").Replace("\\\\n", "\\n").Replace("\\\\x", "\\x00"); -// string captures = test[3]; -// if (seen.Add(pattern + input + captures)) -// { -// writer.WriteLine($"yield return new object[] { \"{pattern}\", \"{input}\", \"{captures}\" };"); -// } -// } -// } -// } -// ------------------------------------- +// Tests sourced from .dat files at http://gsf.cococlyde.org/download. // Then some inputs were deleted / tweaked based on expected differences in behavior. using System.Collections.Generic; +using System.Globalization; using System.Linq; -using System.Threading.Tasks; using Xunit; namespace System.Text.RegularExpressions.Tests { public class AttRegexTests { - public static IEnumerable Inputs() + public static IEnumerable Test_MemberData() { foreach (RegexEngine engine in RegexHelpers.AvailableEngines) { - foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Multiline }) + (RegexOptions Options, string Pattern, string Input, string Expected)[] cases = Match_MemberData_Cases(engine).ToArray(); + Regex[] regexes = RegexHelpers.GetRegexesAsync(engine, cases.Select(c => (c.Pattern, (CultureInfo?)null, (RegexOptions?)c.Options, (TimeSpan?)null)).ToArray()).Result; + for (int i = 0; i < regexes.Length; i++) { - // basic.dat - yield return new object[] { engine, options, "abracadabra$", "abracadabracadabra", "(7,18)" }; - yield return new object[] { engine, options, "a...b", "abababbb", "(2,7)" }; - yield return new object[] { engine, options, "XXXXXX", "..XXXXXX", "(2,8)" }; - yield return new object[] { engine, options, "\\)", "()", "(1,2)" }; - yield return new object[] { engine, options, "a]", "a]a", "(0,2)" }; - yield return new object[] { engine, options, "}", "}", "(0,1)" }; - yield return new object[] { engine, options, "\\}", "}", "(0,1)" }; - yield return new object[] { engine, options, "\\]", "]", "(0,1)" }; - yield return new object[] { engine, options, "]", "]", "(0,1)" }; - yield return new object[] { engine, options, "{", "{", "(0,1)" }; - yield return new object[] { engine, options, "^a", "ax", "(0,1)" }; - yield return new object[] { engine, options, "\\^a", "a^a", "(1,3)" }; - yield return new object[] { engine, options, "a\\^", "a^", "(0,2)" }; - yield return new object[] { engine, options, "a$", "aa", "(1,2)" }; - yield return new object[] { engine, options, "a\\$", "a$", "(0,2)" }; - yield return new object[] { engine, options, "^$", "NULL", "(0,0)" }; - yield return new object[] { engine, options, "$^", "NULL", "(0,0)" }; - yield return new object[] { engine, options, "a($)", "aa", "(1,2)(2,2)" }; - yield return new object[] { engine, options, "a*(^a)", "aa", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(..)*(...)*", "a", "(0,0)" }; - yield return new object[] { engine, options, "(..)*(...)*", "abcd", "(0,4)(2,4)" }; - yield return new object[] { engine, options, "(ab|a)(bc|c)", "abc", "(0,3)(0,2)(2,3)" }; - yield return new object[] { engine, options, "(ab)c|abc", "abc", "(0,3)(0,2)" }; - yield return new object[] { engine, options, "a{0}b", "ab", "(1,2)" }; - yield return new object[] { engine, options, "(a*)(b?)(b+)b{3}", "aaabbbbbbb", "(0,10)(0,3)(3,4)(4,7)" }; - yield return new object[] { engine, options, "(a*)(b{0,1})(b{1,})b{3}", "aaabbbbbbb", "(0,10)(0,3)(3,4)(4,7)" }; - yield return new object[] { engine, options, "a{9876543210}", "NULL", "BADBR" }; - yield return new object[] { engine, options, "((a|a)|a)", "a", "(0,1)(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a*)(a|aa)", "aaaa", "(0,4)(0,3)(3,4)" }; - yield return new object[] { engine, options, "a*(a.|aa)", "aaaa", "(0,4)(2,4)" }; - yield return new object[] { engine, options, "(a|b)?.*", "b", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a|b)c|a(b|c)", "ac", "(0,2)(0,1)" }; - yield return new object[] { engine, options, "(a|b)*c|(a|ab)*c", "abc", "(0,3)(1,2)" }; - yield return new object[] { engine, options, "(a|b)*c|(a|ab)*c", "xc", "(1,2)" }; - yield return new object[] { engine, options, "(.a|.b).*|.*(.a|.b)", "xa", "(0,2)(0,2)" }; - yield return new object[] { engine, options, "a?(ab|ba)ab", "abab", "(0,4)(0,2)" }; - yield return new object[] { engine, options, "a?(ac{0}b|ba)ab", "abab", "(0,4)(0,2)" }; - yield return new object[] { engine, options, "ab|abab", "abbabab", "(0,2)" }; - yield return new object[] { engine, options, "aba|bab|bba", "baaabbbaba", "(5,8)" }; - yield return new object[] { engine, options, "aba|bab", "baaabbbaba", "(6,9)" }; - yield return new object[] { engine, options, "(aa|aaa)*|(a|aaaaa)", "aa", "(0,2)(0,2)" }; - yield return new object[] { engine, options, "(a.|.a.)*|(a|.a...)", "aa", "(0,2)(0,2)" }; - yield return new object[] { engine, options, "ab|a", "xabc", "(1,3)" }; - yield return new object[] { engine, options, "ab|a", "xxabc", "(2,4)" }; - yield return new object[] { engine, options, "(?i)(Ab|cD)*", "aBcD", "(0,4)(2,4)" }; - yield return new object[] { engine, options, "[^-]", "--a", "(2,3)" }; - yield return new object[] { engine, options, "[a-]*", "--a", "(0,3)" }; - yield return new object[] { engine, options, "[a-m-]*", "--amoma--", "(0,4)" }; - yield return new object[] { engine, options, ":::1:::0:|:::1:1:0:", ":::0:::1:::1:::0:", "(8,17)" }; - yield return new object[] { engine, options, ":::1:::0:|:::1:1:1:", ":::0:::1:::1:::0:", "(8,17)" }; - yield return new object[] { engine, options, "\n", "\n", "(0,1)" }; - yield return new object[] { engine, options, "[^a]", "\n", "(0,1)" }; - yield return new object[] { engine, options, "\na", "\na", "(0,2)" }; - yield return new object[] { engine, options, "(a)(b)(c)", "abc", "(0,3)(0,1)(1,2)(2,3)" }; - yield return new object[] { engine, options, "xxx", "xxx", "(0,3)" }; - yield return new object[] { engine, options, "(^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\\* */?)0*[6-7]))([^0-9]|$)", "feb 6,", "(0,6)" }; - yield return new object[] { engine, options, "(^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\\* */?)0*[6-7]))([^0-9]|$)", "2/7", "(0,3)" }; - yield return new object[] { engine, options, "(^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\\* */?)0*[6-7]))([^0-9]|$)", "feb 1,Feb 6", "(5,11)" }; - yield return new object[] { engine, options, "((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))", "x", "(0,1)(0,1)(0,1)" }; - yield return new object[] { engine, options, "((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))*", "xx", "(0,2)(1,2)(1,2)" }; - yield return new object[] { engine, options, "a?(ab|ba)*", "ababababababababababababababababababababababababababababababababababababababababa", "(0,81)(79,81)" }; - yield return new object[] { engine, options, "abaa|abbaa|abbbaa|abbbbaa", "ababbabbbabbbabbbbabbbbaa", "(18,25)" }; - yield return new object[] { engine, options, "abaa|abbaa|abbbaa|abbbbaa", "ababbabbbabbbabbbbabaa", "(18,22)" }; - yield return new object[] { engine, options, "aaac|aabc|abac|abbc|baac|babc|bbac|bbbc", "baaabbbabac", "(7,11)" }; - yield return new object[] { engine, options, ".*", "\x0001\x00ff", "(0,2)" }; - yield return new object[] { engine, options, "aaaa|bbbb|cccc|ddddd|eeeeee|fffffff|gggg|hhhh|iiiii|jjjjj|kkkkk|llll", "XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa", "(53,57)" }; - yield return new object[] { engine, options, "aaaa\nbbbb\ncccc\nddddd\neeeeee\nfffffff\ngggg\nhhhh\niiiii\njjjjj\nkkkkk\nllll", "XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa", "NOMATCH" }; - yield return new object[] { engine, options, "a*a*a*a*a*b", "aaaaaaaaab", "(0,10)" }; - yield return new object[] { engine, options, "^", "NULL", "(0,0)" }; - yield return new object[] { engine, options, "$", "NULL", "(0,0)" }; - yield return new object[] { engine, options, "^a$", "a", "(0,1)" }; - yield return new object[] { engine, options, "abc", "abc", "(0,3)" }; - yield return new object[] { engine, options, "abc", "xabcy", "(1,4)" }; - yield return new object[] { engine, options, "abc", "ababc", "(2,5)" }; - yield return new object[] { engine, options, "ab*c", "abc", "(0,3)" }; - yield return new object[] { engine, options, "ab*bc", "abc", "(0,3)" }; - yield return new object[] { engine, options, "ab*bc", "abbc", "(0,4)" }; - yield return new object[] { engine, options, "ab*bc", "abbbbc", "(0,6)" }; - yield return new object[] { engine, options, "ab+bc", "abbc", "(0,4)" }; - yield return new object[] { engine, options, "ab+bc", "abbbbc", "(0,6)" }; - yield return new object[] { engine, options, "ab?bc", "abbc", "(0,4)" }; - yield return new object[] { engine, options, "ab?bc", "abc", "(0,3)" }; - yield return new object[] { engine, options, "ab?c", "abc", "(0,3)" }; - yield return new object[] { engine, options, "^abc$", "abc", "(0,3)" }; - yield return new object[] { engine, options, "^abc", "abcc", "(0,3)" }; - yield return new object[] { engine, options, "abc$", "aabc", "(1,4)" }; - yield return new object[] { engine, options, "^", "abc", "(0,0)" }; - yield return new object[] { engine, options, "$", "abc", "(3,3)" }; - yield return new object[] { engine, options, "a.c", "abc", "(0,3)" }; - yield return new object[] { engine, options, "a.c", "axc", "(0,3)" }; - yield return new object[] { engine, options, "a.*c", "axyzc", "(0,5)" }; - yield return new object[] { engine, options, "a[bc]d", "abd", "(0,3)" }; - yield return new object[] { engine, options, "a[b-d]e", "ace", "(0,3)" }; - yield return new object[] { engine, options, "a[b-d]", "aac", "(1,3)" }; - yield return new object[] { engine, options, "a[-b]", "a-", "(0,2)" }; - yield return new object[] { engine, options, "a[b-]", "a-", "(0,2)" }; - yield return new object[] { engine, options, "a]", "a]", "(0,2)" }; - yield return new object[] { engine, options, "a[]]b", "a]b", "(0,3)" }; - yield return new object[] { engine, options, "a[^bc]d", "aed", "(0,3)" }; - yield return new object[] { engine, options, "a[^-b]c", "adc", "(0,3)" }; - yield return new object[] { engine, options, "a[^]b]c", "adc", "(0,3)" }; - yield return new object[] { engine, options, "ab|cd", "abc", "(0,2)" }; - yield return new object[] { engine, options, "ab|cd", "abcd", "(0,2)" }; - yield return new object[] { engine, options, "a\\(b", "a(b", "(0,3)" }; - yield return new object[] { engine, options, "a\\(*b", "ab", "(0,2)" }; - yield return new object[] { engine, options, "a\\(*b", "a((b", "(0,4)" }; - yield return new object[] { engine, options, "((a))", "abc", "(0,1)(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a)b(c)", "abc", "(0,3)(0,1)(2,3)" }; - yield return new object[] { engine, options, "a+b+c", "aabbabc", "(4,7)" }; - yield return new object[] { engine, options, "a*", "aaa", "(0,3)" }; - yield return new object[] { engine, options, "(a*)*", "-", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "(a*)+", "-", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "(a*|b)*", "-", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "(a+|b)*", "ab", "(0,2)(1,2)" }; - yield return new object[] { engine, options, "(a+|b)+", "ab", "(0,2)(1,2)" }; - yield return new object[] { engine, options, "(a+|b)?", "ab", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "[^ab]*", "cde", "(0,3)" }; - yield return new object[] { engine, options, "(^)*", "-", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "a*", "NULL", "(0,0)" }; - yield return new object[] { engine, options, "([abc])*d", "abbbcd", "(0,6)(4,5)" }; - yield return new object[] { engine, options, "([abc])*bcd", "abcd", "(0,4)(0,1)" }; - yield return new object[] { engine, options, "a|b|c|d|e", "e", "(0,1)" }; - yield return new object[] { engine, options, "(a|b|c|d|e)f", "ef", "(0,2)(0,1)" }; - yield return new object[] { engine, options, "((a*|b))*", "-", "(0,0)(0,0)(0,0)" }; - yield return new object[] { engine, options, "abcd*efg", "abcdefg", "(0,7)" }; - yield return new object[] { engine, options, "ab*", "xabyabbbz", "(1,3)" }; - yield return new object[] { engine, options, "ab*", "xayabbbz", "(1,2)" }; - yield return new object[] { engine, options, "(ab|cd)e", "abcde", "(2,5)(2,4)" }; - yield return new object[] { engine, options, "[abhgefdc]ij", "hij", "(0,3)" }; - yield return new object[] { engine, options, "(a|b)c*d", "abcd", "(1,4)(1,2)" }; - yield return new object[] { engine, options, "(ab|ab*)bc", "abc", "(0,3)(0,1)" }; - yield return new object[] { engine, options, "a([bc]*)c*", "abc", "(0,3)(1,3)" }; - yield return new object[] { engine, options, "a([bc]*)(c*d)", "abcd", "(0,4)(1,3)(3,4)" }; - yield return new object[] { engine, options, "a([bc]+)(c*d)", "abcd", "(0,4)(1,3)(3,4)" }; - yield return new object[] { engine, options, "a([bc]*)(c+d)", "abcd", "(0,4)(1,2)(2,4)" }; - yield return new object[] { engine, options, "a[bcd]*dcdcde", "adcdcde", "(0,7)" }; - yield return new object[] { engine, options, "(ab|a)b*c", "abc", "(0,3)(0,2)" }; - yield return new object[] { engine, options, "((a)(b)c)(d)", "abcd", "(0,4)(0,3)(0,1)(1,2)(3,4)" }; - yield return new object[] { engine, options, "[A-Za-z_][A-Za-z0-9_]*", "alpha", "(0,5)" }; - yield return new object[] { engine, options, "^a(bc+|b[eh])g|.h$", "abh", "(1,3)" }; - yield return new object[] { engine, options, "(bc+d$|ef*g.|h?i(j|k))", "effgz", "(0,5)(0,5)" }; - yield return new object[] { engine, options, "(bc+d$|ef*g.|h?i(j|k))", "ij", "(0,2)(0,2)(1,2)" }; - yield return new object[] { engine, options, "(bc+d$|ef*g.|h?i(j|k))", "reffgz", "(1,6)(1,6)" }; - yield return new object[] { engine, options, "(((((((((a)))))))))", "a", "(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)" }; - yield return new object[] { engine, options, "multiple words", "multiple words yeah", "(0,14)" }; - yield return new object[] { engine, options, "(.*)c(.*)", "abcde", "(0,5)(0,2)(3,5)" }; - yield return new object[] { engine, options, "abcd", "abcd", "(0,4)" }; - yield return new object[] { engine, options, "a(bc)d", "abcd", "(0,4)(1,3)" }; - yield return new object[] { engine, options, "a[-]?c", "ac", "(0,3)" }; - yield return new object[] { engine, options, "a+(b|c)*d+", "aabcdd", "(0,6)(3,4)" }; - yield return new object[] { engine, options, "^.+$", "vivi", "(0,4)" }; - yield return new object[] { engine, options, "^(.+)$", "vivi", "(0,4)(0,4)" }; - yield return new object[] { engine, options, "^([^!.]+).att.com!(.+)$", "gryphon.att.com!eby", "(0,19)(0,7)(16,19)" }; - yield return new object[] { engine, options, "^([^!]+!)?([^!]+)$", "bar!bas", "(0,7)(0,4)(4,7)" }; - yield return new object[] { engine, options, "^([^!]+!)?([^!]+)$", "foo!bas", "(0,7)(0,4)(4,7)" }; - yield return new object[] { engine, options, "^.+!([^!]+!)([^!]+)$", "foo!bar!bas", "(0,11)(4,8)(8,11)" }; - yield return new object[] { engine, options, "((foo)|(bar))!bas", "foo!bas", "(0,7)(0,3)(0,3)" }; - yield return new object[] { engine, options, "((foo)|bar)!bas", "bar!bas", "(0,7)(0,3)" }; - yield return new object[] { engine, options, "((foo)|bar)!bas", "foo!bar!bas", "(4,11)(4,7)" }; - yield return new object[] { engine, options, "((foo)|bar)!bas", "foo!bas", "(0,7)(0,3)(0,3)" }; - yield return new object[] { engine, options, "(foo|(bar))!bas", "bar!bas", "(0,7)(0,3)(0,3)" }; - yield return new object[] { engine, options, "(foo|(bar))!bas", "foo!bar!bas", "(4,11)(4,7)(4,7)" }; - yield return new object[] { engine, options, "(foo|(bar))!bas", "foo!bas", "(0,7)(0,3)" }; - yield return new object[] { engine, options, "(foo|bar)!bas", "bar!bas", "(0,7)(0,3)" }; - yield return new object[] { engine, options, "(foo|bar)!bas", "foo!bar!bas", "(4,11)(4,7)" }; - yield return new object[] { engine, options, "(foo|bar)!bas", "foo!bas", "(0,7)(0,3)" }; - yield return new object[] { engine, options, "^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$", "bar!bas", "(0,7)(0,4)(4,7)" }; - yield return new object[] { engine, options, "^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$", "foo!bas", "(0,7)(0,4)(4,7)" }; - yield return new object[] { engine, options, "^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$", "bar!bas", "(0,7)(0,7)(0,4)(4,7)" }; - yield return new object[] { engine, options, "^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$", "foo!bas", "(0,7)(0,7)(0,4)(4,7)" }; - yield return new object[] { engine, options, ".*(/XXX).*", "/XXX", "(0,4)(0,4)" }; - yield return new object[] { engine, options, ".*(\\\\XXX).*", "\\XXX", "(0,4)(0,4)" }; - yield return new object[] { engine, options, "\\\\XXX", "\\XXX", "(0,4)" }; - yield return new object[] { engine, options, ".*(/000).*", "/000", "(0,4)(0,4)" }; - yield return new object[] { engine, options, ".*(\\\\000).*", "\\000", "(0,4)(0,4)" }; - yield return new object[] { engine, options, "\\\\000", "\\000", "(0,4)" }; - - // repetition.dat - yield return new object[] { engine, options, "((..)|(.))", "NULL", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.))((..)|(.))", "NULL", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.))((..)|(.))((..)|(.))", "NULL", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.)){1}", "NULL", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.)){2}", "NULL", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.)){3}", "NULL", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.))*", "NULL", "(0,0)" }; - yield return new object[] { engine, options, "((..)|(.))((..)|(.))", "a", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.))((..)|(.))((..)|(.))", "a", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.)){2}", "a", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.)){3}", "a", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.))((..)|(.))((..)|(.))", "aa", "NOMATCH" }; - yield return new object[] { engine, options, "((..)|(.)){3}", "aa", "NOMATCH" }; - yield return new object[] { engine, options, "X(.?){0,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){1,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){2,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){3,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){4,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){5,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){6,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){7,}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){8,}Y", "X1234567Y", "(0,9)(8,8)" }; - yield return new object[] { engine, options, "X(.?){0,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){1,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){2,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){3,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){4,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){5,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){6,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){7,8}Y", "X1234567Y", "(0,9)(8,8)" }; // was "(0,9)(7,8)" - yield return new object[] { engine, options, "X(.?){8,8}Y", "X1234567Y", "(0,9)(8,8)" }; - yield return new object[] { engine, options, "(a|ab|c|bcd){0,}(d*)", "ababcd", "(0,1)(1,1)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(a|ab|c|bcd){1,}(d*)", "ababcd", "(0,1)(1,1)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(a|ab|c|bcd){2,}(d*)", "ababcd", "(0,6)(3,6)(6,6)" }; - yield return new object[] { engine, options, "(a|ab|c|bcd){3,}(d*)", "ababcd", "(0,6)(3,6)(6,6)" }; - yield return new object[] { engine, options, "(a|ab|c|bcd){4,}(d*)", "ababcd", "NOMATCH" }; - yield return new object[] { engine, options, "(a|ab|c|bcd){0,10}(d*)", "ababcd", "(0,1)(1,1)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(a|ab|c|bcd){1,10}(d*)", "ababcd", "(0,1)(1,1)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(a|ab|c|bcd){2,10}(d*)", "ababcd", "(0,6)(3,6)(6,6)" }; - yield return new object[] { engine, options, "(a|ab|c|bcd){3,10}(d*)", "ababcd", "(0,6)(3,6)(6,6)" }; - yield return new object[] { engine, options, "(a|ab|c|bcd){4,10}(d*)", "ababcd", "NOMATCH" }; - yield return new object[] { engine, options, "(a|ab|c|bcd)*(d*)", "ababcd", "(0,1)(1,1)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(a|ab|c|bcd)+(d*)", "ababcd", "(0,1)(1,1)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){0,}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){1,}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){2,}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){3,}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){4,}(d*)", "ababcd", "NOMATCH" }; - yield return new object[] { engine, options, "(ab|a|c|bcd){0,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){1,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){2,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){3,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd){4,10}(d*)", "ababcd", "NOMATCH" }; - yield return new object[] { engine, options, "(ab|a|c|bcd)*(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - yield return new object[] { engine, options, "(ab|a|c|bcd)+(d*)", "ababcd", "(0,6)(4,5)(5,6)" }; // was "(0,6)(3,6)(6,6)" - - // unknownassoc.dat - yield return new object[] { engine, options, "(a|ab)(c|bcd)(d*)", "abcd", "(0,4)(0,1)(1,4)(4,4)" }; - yield return new object[] { engine, options, "(a|ab)(bcd|c)(d*)", "abcd", "(0,4)(0,1)(1,4)(4,4)" }; - yield return new object[] { engine, options, "(ab|a)(c|bcd)(d*)", "abcd", "(0,4)(0,2)(2,3)(3,4)" }; - yield return new object[] { engine, options, "(ab|a)(bcd|c)(d*)", "abcd", "(0,4)(0,2)(2,3)(3,4)" }; - yield return new object[] { engine, options, "(a*)(b|abc)(c*)", "abc", "(0,3)(0,1)(1,2)(2,3)" }; - yield return new object[] { engine, options, "(a*)(abc|b)(c*)", "abc", "(0,3)(0,1)(1,2)(2,3)" }; - yield return new object[] { engine, options, "(a|ab)(c|bcd)(d|.*)", "abcd", "(0,4)(0,1)(1,4)(4,4)" }; - yield return new object[] { engine, options, "(a|ab)(bcd|c)(d|.*)", "abcd", "(0,4)(0,1)(1,4)(4,4)" }; - yield return new object[] { engine, options, "(ab|a)(c|bcd)(d|.*)", "abcd", "(0,4)(0,2)(2,3)(3,4)" }; - yield return new object[] { engine, options, "(ab|a)(bcd|c)(d|.*)", "abcd", "(0,4)(0,2)(2,3)(3,4)" }; - - // nullsubexpr.dat - yield return new object[] { engine, options, "(a*)*", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a*)*", "x", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "(a*)*", "aaaaaa", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "(a*)*", "aaaaaax", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "(a*)+", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a+)*", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a+)*", "x", "(0,0)" }; - yield return new object[] { engine, options, "(a+)+", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a+)+", "x", "NOMATCH" }; - yield return new object[] { engine, options, "([a]*)*", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "([a]*)+", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "([^b]*)*", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "([^b]*)*", "b", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "([^b]*)*", "aaaaaab", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "([ab]*)*", "a", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "([ab]*)*", "ababab", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "([ab]*)*", "bababa", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "([ab]*)*", "b", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "([ab]*)*", "bbbbbb", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "([ab]*)*", "aaaabcde", "(0,5)(0,5)" }; - yield return new object[] { engine, options, "([^a]*)*", "b", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "([^a]*)*", "aaaaaa", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "([^ab]*)*", "ccccxx", "(0,6)(0,6)" }; - yield return new object[] { engine, options, "([^ab]*)*", "ababab", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "((z)+|a)*", "zabcde", "(0,2)(1,2)" }; - yield return new object[] { engine, options, "a+?", "aaaaaa", "(0,1)" }; - yield return new object[] { engine, options, "(a)", "aaa", "(0,1)(0,1)" }; - yield return new object[] { engine, options, "(a*?)", "aaa", "(0,0)(0,0)" }; - yield return new object[] { engine, options, "(a)*?", "aaa", "(0,0)" }; - yield return new object[] { engine, options, "(a*?)*?", "aaa", "(0,0)" }; - yield return new object[] { engine, options, "(a*)*(x)", "x", "(0,1)(0,0)(0,1)" }; - yield return new object[] { engine, options, "(a*)*(x)(\\1)", "x", "(0,1)(0,0)(0,1)(1,1)", true }; - yield return new object[] { engine, options, "(a*)*(x)(\\1)", "ax", "(0,2)(1,1)(1,2)(2,2)", true }; - yield return new object[] { engine, options, "(a*)*(x)(\\1)", "axa", "(0,2)(1,1)(1,2)(2,2)", true }; // was "(0,3)(0,1)(1,2)(2,3)" - yield return new object[] { engine, options, "(a*)*(x)(\\1)(x)", "axax", "(0,4)(0,1)(1,2)(2,3)(3,4)", true }; - yield return new object[] { engine, options, "(a*)*(x)(\\1)(x)", "axxa", "(0,3)(1,1)(1,2)(2,2)(2,3)", true }; - yield return new object[] { engine, options, "(a*)*(x)", "ax", "(0,2)(1,1)(1,2)" }; - yield return new object[] { engine, options, "(a*)*(x)", "axa", "(0,2)(1,1)(1,2)" }; // was "(0,2)(0,1)(1,2)" - yield return new object[] { engine, options, "(a*)+(x)", "x", "(0,1)(0,0)(0,1)" }; - yield return new object[] { engine, options, "(a*)+(x)", "ax", "(0,2)(1,1)(1,2)" }; // was "(0,2)(0,1)(1,2)" - yield return new object[] { engine, options, "(a*)+(x)", "axa", "(0,2)(1,1)(1,2)" }; // was "(0,2)(0,1)(1,2)" - yield return new object[] { engine, options, "(a*){2}(x)", "x", "(0,1)(0,0)(0,1)" }; - yield return new object[] { engine, options, "(a*){2}(x)", "ax", "(0,2)(1,1)(1,2)" }; - yield return new object[] { engine, options, "(a*){2}(x)", "axa", "(0,2)(1,1)(1,2)" }; + yield return new object[] { regexes[i], cases[i].Input, cases[i].Expected }; } } } - [Theory] - [MemberData(nameof(Inputs))] - public async Task Test(RegexEngine engine, RegexOptions options, string pattern, string input, string expected, bool skipNonBacktracking = false) + private static IEnumerable<(RegexOptions Options, string Pattern, string Input, string Expected)> Match_MemberData_Cases(RegexEngine engine) { - if (input == "NULL") + foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Multiline }) { - input = ""; - } + // basic.dat + yield return (options, "abracadabra$", "abracadabracadabra", "(7,18)"); + yield return (options, "a...b", "abababbb", "(2,7)"); + yield return (options, "XXXXXX", "..XXXXXX", "(2,8)"); + yield return (options, "\\)", "()", "(1,2)"); + yield return (options, "a]", "a]a", "(0,2)"); + yield return (options, "}", "}", "(0,1)"); + yield return (options, "\\}", "}", "(0,1)"); + yield return (options, "\\]", "]", "(0,1)"); + yield return (options, "]", "]", "(0,1)"); + yield return (options, "{", "{", "(0,1)"); + yield return (options, "^a", "ax", "(0,1)"); + yield return (options, "\\^a", "a^a", "(1,3)"); + yield return (options, "a\\^", "a^", "(0,2)"); + yield return (options, "a$", "aa", "(1,2)"); + yield return (options, "a\\$", "a$", "(0,2)"); + yield return (options, "^$", "", "(0,0)"); + yield return (options, "$^", "", "(0,0)"); + yield return (options, "a($)", "aa", "(1,2)(2,2)"); + yield return (options, "a*(^a)", "aa", "(0,1)(0,1)"); + yield return (options, "(..)*(...)*", "a", "(0,0)"); + yield return (options, "(..)*(...)*", "abcd", "(0,4)(2,4)"); + yield return (options, "(ab|a)(bc|c)", "abc", "(0,3)(0,2)(2,3)"); + yield return (options, "(ab)c|abc", "abc", "(0,3)(0,2)"); + yield return (options, "a{0}b", "ab", "(1,2)"); + yield return (options, "(a*)(b?)(b+)b{3}", "aaabbbbbbb", "(0,10)(0,3)(3,4)(4,7)"); + yield return (options, "(a*)(b{0,1})(b{1,})b{3}", "aaabbbbbbb", "(0,10)(0,3)(3,4)(4,7)"); + yield return (options, "((a|a)|a)", "a", "(0,1)(0,1)(0,1)"); + yield return (options, "(a*)(a|aa)", "aaaa", "(0,4)(0,3)(3,4)"); + yield return (options, "a*(a.|aa)", "aaaa", "(0,4)(2,4)"); + yield return (options, "(a|b)?.*", "b", "(0,1)(0,1)"); + yield return (options, "(a|b)c|a(b|c)", "ac", "(0,2)(0,1)"); + yield return (options, "(a|b)*c|(a|ab)*c", "abc", "(0,3)(1,2)"); + yield return (options, "(a|b)*c|(a|ab)*c", "xc", "(1,2)"); + yield return (options, "(.a|.b).*|.*(.a|.b)", "xa", "(0,2)(0,2)"); + yield return (options, "a?(ab|ba)ab", "abab", "(0,4)(0,2)"); + yield return (options, "a?(ac{0}b|ba)ab", "abab", "(0,4)(0,2)"); + yield return (options, "ab|abab", "abbabab", "(0,2)"); + yield return (options, "aba|bab|bba", "baaabbbaba", "(5,8)"); + yield return (options, "aba|bab", "baaabbbaba", "(6,9)"); + yield return (options, "(aa|aaa)*|(a|aaaaa)", "aa", "(0,2)(0,2)"); + yield return (options, "(a.|.a.)*|(a|.a...)", "aa", "(0,2)(0,2)"); + yield return (options, "ab|a", "xabc", "(1,3)"); + yield return (options, "ab|a", "xxabc", "(2,4)"); + yield return (options, "(?i)(Ab|cD)*", "aBcD", "(0,4)(2,4)"); + yield return (options, "[^-]", "--a", "(2,3)"); + yield return (options, "[a-]*", "--a", "(0,3)"); + yield return (options, "[a-m-]*", "--amoma--", "(0,4)"); + yield return (options, ":::1:::0:|:::1:1:0:", ":::0:::1:::1:::0:", "(8,17)"); + yield return (options, ":::1:::0:|:::1:1:1:", ":::0:::1:::1:::0:", "(8,17)"); + yield return (options, "\n", "\n", "(0,1)"); + yield return (options, "[^a]", "\n", "(0,1)"); + yield return (options, "\na", "\na", "(0,2)"); + yield return (options, "(a)(b)(c)", "abc", "(0,3)(0,1)(1,2)(2,3)"); + yield return (options, "xxx", "xxx", "(0,3)"); + yield return (options, "(^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\\* */?)0*[6-7]))([^0-9]|$)", "feb 6,", "(0,6)"); + yield return (options, "(^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\\* */?)0*[6-7]))([^0-9]|$)", "2/7", "(0,3)"); + yield return (options, "(^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\\* */?)0*[6-7]))([^0-9]|$)", "feb 1,Feb 6", "(5,11)"); + yield return (options, "((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))", "x", "(0,1)(0,1)(0,1)"); + yield return (options, "((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))*", "xx", "(0,2)(1,2)(1,2)"); + yield return (options, "a?(ab|ba)*", "ababababababababababababababababababababababababababababababababababababababababa", "(0,81)(79,81)"); + yield return (options, "abaa|abbaa|abbbaa|abbbbaa", "ababbabbbabbbabbbbabbbbaa", "(18,25)"); + yield return (options, "abaa|abbaa|abbbaa|abbbbaa", "ababbabbbabbbabbbbabaa", "(18,22)"); + yield return (options, "aaac|aabc|abac|abbc|baac|babc|bbac|bbbc", "baaabbbabac", "(7,11)"); + yield return (options, ".*", "\x0001\x00ff", "(0,2)"); + yield return (options, "aaaa|bbbb|cccc|ddddd|eeeeee|fffffff|gggg|hhhh|iiiii|jjjjj|kkkkk|llll", "XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa", "(53,57)"); + yield return (options, "aaaa\nbbbb\ncccc\nddddd\neeeeee\nfffffff\ngggg\nhhhh\niiiii\njjjjj\nkkkkk\nllll", "XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa", "NOMATCH"); + yield return (options, "a*a*a*a*a*b", "aaaaaaaaab", "(0,10)"); + yield return (options, "^", "", "(0,0)"); + yield return (options, "$", "", "(0,0)"); + yield return (options, "^a$", "a", "(0,1)"); + yield return (options, "abc", "abc", "(0,3)"); + yield return (options, "abc", "xabcy", "(1,4)"); + yield return (options, "abc", "ababc", "(2,5)"); + yield return (options, "ab*c", "abc", "(0,3)"); + yield return (options, "ab*bc", "abc", "(0,3)"); + yield return (options, "ab*bc", "abbc", "(0,4)"); + yield return (options, "ab*bc", "abbbbc", "(0,6)"); + yield return (options, "ab+bc", "abbc", "(0,4)"); + yield return (options, "ab+bc", "abbbbc", "(0,6)"); + yield return (options, "ab?bc", "abbc", "(0,4)"); + yield return (options, "ab?bc", "abc", "(0,3)"); + yield return (options, "ab?c", "abc", "(0,3)"); + yield return (options, "^abc$", "abc", "(0,3)"); + yield return (options, "^abc", "abcc", "(0,3)"); + yield return (options, "abc$", "aabc", "(1,4)"); + yield return (options, "^", "abc", "(0,0)"); + yield return (options, "$", "abc", "(3,3)"); + yield return (options, "a.c", "abc", "(0,3)"); + yield return (options, "a.c", "axc", "(0,3)"); + yield return (options, "a.*c", "axyzc", "(0,5)"); + yield return (options, "a[bc]d", "abd", "(0,3)"); + yield return (options, "a[b-d]e", "ace", "(0,3)"); + yield return (options, "a[b-d]", "aac", "(1,3)"); + yield return (options, "a[-b]", "a-", "(0,2)"); + yield return (options, "a[b-]", "a-", "(0,2)"); + yield return (options, "a]", "a]", "(0,2)"); + yield return (options, "a[]]b", "a]b", "(0,3)"); + yield return (options, "a[^bc]d", "aed", "(0,3)"); + yield return (options, "a[^-b]c", "adc", "(0,3)"); + yield return (options, "a[^]b]c", "adc", "(0,3)"); + yield return (options, "ab|cd", "abc", "(0,2)"); + yield return (options, "ab|cd", "abcd", "(0,2)"); + yield return (options, "a\\(b", "a(b", "(0,3)"); + yield return (options, "a\\(*b", "ab", "(0,2)"); + yield return (options, "a\\(*b", "a((b", "(0,4)"); + yield return (options, "((a))", "abc", "(0,1)(0,1)(0,1)"); + yield return (options, "(a)b(c)", "abc", "(0,3)(0,1)(2,3)"); + yield return (options, "a+b+c", "aabbabc", "(4,7)"); + yield return (options, "a*", "aaa", "(0,3)"); + yield return (options, "(a*)*", "-", "(0,0)(0,0)"); + yield return (options, "(a*)+", "-", "(0,0)(0,0)"); + yield return (options, "(a*|b)*", "-", "(0,0)(0,0)"); + yield return (options, "(a+|b)*", "ab", "(0,2)(1,2)"); + yield return (options, "(a+|b)+", "ab", "(0,2)(1,2)"); + yield return (options, "(a+|b)?", "ab", "(0,1)(0,1)"); + yield return (options, "[^ab]*", "cde", "(0,3)"); + yield return (options, "(^)*", "-", "(0,0)(0,0)"); + yield return (options, "a*", "", "(0,0)"); + yield return (options, "([abc])*d", "abbbcd", "(0,6)(4,5)"); + yield return (options, "([abc])*bcd", "abcd", "(0,4)(0,1)"); + yield return (options, "a|b|c|d|e", "e", "(0,1)"); + yield return (options, "(a|b|c|d|e)f", "ef", "(0,2)(0,1)"); + yield return (options, "((a*|b))*", "-", "(0,0)(0,0)(0,0)"); + yield return (options, "abcd*efg", "abcdefg", "(0,7)"); + yield return (options, "ab*", "xabyabbbz", "(1,3)"); + yield return (options, "ab*", "xayabbbz", "(1,2)"); + yield return (options, "(ab|cd)e", "abcde", "(2,5)(2,4)"); + yield return (options, "[abhgefdc]ij", "hij", "(0,3)"); + yield return (options, "(a|b)c*d", "abcd", "(1,4)(1,2)"); + yield return (options, "(ab|ab*)bc", "abc", "(0,3)(0,1)"); + yield return (options, "a([bc]*)c*", "abc", "(0,3)(1,3)"); + yield return (options, "a([bc]*)(c*d)", "abcd", "(0,4)(1,3)(3,4)"); + yield return (options, "a([bc]+)(c*d)", "abcd", "(0,4)(1,3)(3,4)"); + yield return (options, "a([bc]*)(c+d)", "abcd", "(0,4)(1,2)(2,4)"); + yield return (options, "a[bcd]*dcdcde", "adcdcde", "(0,7)"); + yield return (options, "(ab|a)b*c", "abc", "(0,3)(0,2)"); + yield return (options, "((a)(b)c)(d)", "abcd", "(0,4)(0,3)(0,1)(1,2)(3,4)"); + yield return (options, "[A-Za-z_][A-Za-z0-9_]*", "alpha", "(0,5)"); + yield return (options, "^a(bc+|b[eh])g|.h$", "abh", "(1,3)"); + yield return (options, "(bc+d$|ef*g.|h?i(j|k))", "effgz", "(0,5)(0,5)"); + yield return (options, "(bc+d$|ef*g.|h?i(j|k))", "ij", "(0,2)(0,2)(1,2)"); + yield return (options, "(bc+d$|ef*g.|h?i(j|k))", "reffgz", "(1,6)(1,6)"); + yield return (options, "(((((((((a)))))))))", "a", "(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)"); + yield return (options, "multiple words", "multiple words yeah", "(0,14)"); + yield return (options, "(.*)c(.*)", "abcde", "(0,5)(0,2)(3,5)"); + yield return (options, "abcd", "abcd", "(0,4)"); + yield return (options, "a(bc)d", "abcd", "(0,4)(1,3)"); + yield return (options, "a[-]?c", "ac", "(0,3)"); + yield return (options, "a+(b|c)*d+", "aabcdd", "(0,6)(3,4)"); + yield return (options, "^.+$", "vivi", "(0,4)"); + yield return (options, "^(.+)$", "vivi", "(0,4)(0,4)"); + yield return (options, "^([^!.]+).att.com!(.+)$", "gryphon.att.com!eby", "(0,19)(0,7)(16,19)"); + yield return (options, "^([^!]+!)?([^!]+)$", "bar!bas", "(0,7)(0,4)(4,7)"); + yield return (options, "^([^!]+!)?([^!]+)$", "foo!bas", "(0,7)(0,4)(4,7)"); + yield return (options, "^.+!([^!]+!)([^!]+)$", "foo!bar!bas", "(0,11)(4,8)(8,11)"); + yield return (options, "((foo)|(bar))!bas", "foo!bas", "(0,7)(0,3)(0,3)"); + yield return (options, "((foo)|bar)!bas", "bar!bas", "(0,7)(0,3)"); + yield return (options, "((foo)|bar)!bas", "foo!bar!bas", "(4,11)(4,7)"); + yield return (options, "((foo)|bar)!bas", "foo!bas", "(0,7)(0,3)(0,3)"); + yield return (options, "(foo|(bar))!bas", "bar!bas", "(0,7)(0,3)(0,3)"); + yield return (options, "(foo|(bar))!bas", "foo!bar!bas", "(4,11)(4,7)(4,7)"); + yield return (options, "(foo|(bar))!bas", "foo!bas", "(0,7)(0,3)"); + yield return (options, "(foo|bar)!bas", "bar!bas", "(0,7)(0,3)"); + yield return (options, "(foo|bar)!bas", "foo!bar!bas", "(4,11)(4,7)"); + yield return (options, "(foo|bar)!bas", "foo!bas", "(0,7)(0,3)"); + yield return (options, "^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$", "bar!bas", "(0,7)(0,4)(4,7)"); + yield return (options, "^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$", "foo!bas", "(0,7)(0,4)(4,7)"); + yield return (options, "^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$", "bar!bas", "(0,7)(0,7)(0,4)(4,7)"); + yield return (options, "^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$", "foo!bas", "(0,7)(0,7)(0,4)(4,7)"); + yield return (options, ".*(/XXX).*", "/XXX", "(0,4)(0,4)"); + yield return (options, ".*(\\\\XXX).*", "\\XXX", "(0,4)(0,4)"); + yield return (options, "\\\\XXX", "\\XXX", "(0,4)"); + yield return (options, ".*(/000).*", "/000", "(0,4)(0,4)"); + yield return (options, ".*(\\\\000).*", "\\000", "(0,4)(0,4)"); + yield return (options, "\\\\000", "\\000", "(0,4)"); - if (expected == "BADBR") - { - await Assert.ThrowsAnyAsync(async () => await RegexHelpers.GetRegexAsync(engine, pattern, options)); - return; - } + // repetition.dat + yield return (options, "((..)|(.))", "", "NOMATCH"); + yield return (options, "((..)|(.))((..)|(.))", "", "NOMATCH"); + yield return (options, "((..)|(.))((..)|(.))((..)|(.))", "", "NOMATCH"); + yield return (options, "((..)|(.)){1}", "", "NOMATCH"); + yield return (options, "((..)|(.)){2}", "", "NOMATCH"); + yield return (options, "((..)|(.)){3}", "", "NOMATCH"); + yield return (options, "((..)|(.))*", "", "(0,0)"); + yield return (options, "((..)|(.))((..)|(.))", "a", "NOMATCH"); + yield return (options, "((..)|(.))((..)|(.))((..)|(.))", "a", "NOMATCH"); + yield return (options, "((..)|(.)){2}", "a", "NOMATCH"); + yield return (options, "((..)|(.)){3}", "a", "NOMATCH"); + yield return (options, "((..)|(.))((..)|(.))((..)|(.))", "aa", "NOMATCH"); + yield return (options, "((..)|(.)){3}", "aa", "NOMATCH"); + yield return (options, "X(.?){0,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){1,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){2,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){3,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){4,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){5,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){6,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){7,}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){8,}Y", "X1234567Y", "(0,9)(8,8)"); + yield return (options, "X(.?){0,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){1,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){2,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){3,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){4,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){5,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){6,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){7,8}Y", "X1234567Y", "(0,9)(8,8)"); // was "(0,9)(7,8)" + yield return (options, "X(.?){8,8}Y", "X1234567Y", "(0,9)(8,8)"); + yield return (options, "(a|ab|c|bcd){0,}(d*)", "ababcd", "(0,1)(1,1)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(a|ab|c|bcd){1,}(d*)", "ababcd", "(0,1)(1,1)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(a|ab|c|bcd){2,}(d*)", "ababcd", "(0,6)(3,6)(6,6)"); + yield return (options, "(a|ab|c|bcd){3,}(d*)", "ababcd", "(0,6)(3,6)(6,6)"); + yield return (options, "(a|ab|c|bcd){4,}(d*)", "ababcd", "NOMATCH"); + yield return (options, "(a|ab|c|bcd){0,10}(d*)", "ababcd", "(0,1)(1,1)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(a|ab|c|bcd){1,10}(d*)", "ababcd", "(0,1)(1,1)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(a|ab|c|bcd){2,10}(d*)", "ababcd", "(0,6)(3,6)(6,6)"); + yield return (options, "(a|ab|c|bcd){3,10}(d*)", "ababcd", "(0,6)(3,6)(6,6)"); + yield return (options, "(a|ab|c|bcd){4,10}(d*)", "ababcd", "NOMATCH"); + yield return (options, "(a|ab|c|bcd)*(d*)", "ababcd", "(0,1)(1,1)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(a|ab|c|bcd)+(d*)", "ababcd", "(0,1)(1,1)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){0,}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){1,}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){2,}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){3,}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){4,}(d*)", "ababcd", "NOMATCH"); + yield return (options, "(ab|a|c|bcd){0,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){1,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){2,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){3,10}(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd){4,10}(d*)", "ababcd", "NOMATCH"); + yield return (options, "(ab|a|c|bcd)*(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" + yield return (options, "(ab|a|c|bcd)+(d*)", "ababcd", "(0,6)(4,5)(5,6)"); // was "(0,6)(3,6)(6,6)" - if (engine == RegexEngine.NonBacktracking && skipNonBacktracking) - { - // In particular: backreferences are not supported in NonBacktracking mode - await Assert.ThrowsAnyAsync(() => RegexHelpers.GetRegexAsync(engine, pattern, options)); - return; - } + // unknownassoc.dat + yield return (options, "(a|ab)(c|bcd)(d*)", "abcd", "(0,4)(0,1)(1,4)(4,4)"); + yield return (options, "(a|ab)(bcd|c)(d*)", "abcd", "(0,4)(0,1)(1,4)(4,4)"); + yield return (options, "(ab|a)(c|bcd)(d*)", "abcd", "(0,4)(0,2)(2,3)(3,4)"); + yield return (options, "(ab|a)(bcd|c)(d*)", "abcd", "(0,4)(0,2)(2,3)(3,4)"); + yield return (options, "(a*)(b|abc)(c*)", "abc", "(0,3)(0,1)(1,2)(2,3)"); + yield return (options, "(a*)(abc|b)(c*)", "abc", "(0,3)(0,1)(1,2)(2,3)"); + yield return (options, "(a|ab)(c|bcd)(d|.*)", "abcd", "(0,4)(0,1)(1,4)(4,4)"); + yield return (options, "(a|ab)(bcd|c)(d|.*)", "abcd", "(0,4)(0,1)(1,4)(4,4)"); + yield return (options, "(ab|a)(c|bcd)(d|.*)", "abcd", "(0,4)(0,2)(2,3)(3,4)"); + yield return (options, "(ab|a)(bcd|c)(d|.*)", "abcd", "(0,4)(0,2)(2,3)(3,4)"); - Regex r = await RegexHelpers.GetRegexAsync(engine, pattern, options); + // nullsubexpr.dat + yield return (options, "(a*)*", "a", "(0,1)(0,1)"); + yield return (options, "(a*)*", "x", "(0,0)(0,0)"); + yield return (options, "(a*)*", "aaaaaa", "(0,6)(0,6)"); + yield return (options, "(a*)*", "aaaaaax", "(0,6)(0,6)"); + yield return (options, "(a*)+", "a", "(0,1)(0,1)"); + yield return (options, "(a+)*", "a", "(0,1)(0,1)"); + yield return (options, "(a+)*", "x", "(0,0)"); + yield return (options, "(a+)+", "a", "(0,1)(0,1)"); + yield return (options, "(a+)+", "x", "NOMATCH"); + yield return (options, "([a]*)*", "a", "(0,1)(0,1)"); + yield return (options, "([a]*)+", "a", "(0,1)(0,1)"); + yield return (options, "([^b]*)*", "a", "(0,1)(0,1)"); + yield return (options, "([^b]*)*", "b", "(0,0)(0,0)"); + yield return (options, "([^b]*)*", "aaaaaab", "(0,6)(0,6)"); + yield return (options, "([ab]*)*", "a", "(0,1)(0,1)"); + yield return (options, "([ab]*)*", "ababab", "(0,6)(0,6)"); + yield return (options, "([ab]*)*", "bababa", "(0,6)(0,6)"); + yield return (options, "([ab]*)*", "b", "(0,1)(0,1)"); + yield return (options, "([ab]*)*", "bbbbbb", "(0,6)(0,6)"); + yield return (options, "([ab]*)*", "aaaabcde", "(0,5)(0,5)"); + yield return (options, "([^a]*)*", "b", "(0,1)(0,1)"); + yield return (options, "([^a]*)*", "aaaaaa", "(0,0)(0,0)"); + yield return (options, "([^ab]*)*", "ccccxx", "(0,6)(0,6)"); + yield return (options, "([^ab]*)*", "ababab", "(0,0)(0,0)"); + yield return (options, "((z)+|a)*", "zabcde", "(0,2)(1,2)"); + yield return (options, "a+?", "aaaaaa", "(0,1)"); + yield return (options, "(a)", "aaa", "(0,1)(0,1)"); + yield return (options, "(a*?)", "aaa", "(0,0)(0,0)"); + yield return (options, "(a)*?", "aaa", "(0,0)"); + yield return (options, "(a*?)*?", "aaa", "(0,0)"); + yield return (options, "(a*)*(x)", "x", "(0,1)(0,0)(0,1)"); + yield return (options, "(a*)*(x)", "ax", "(0,2)(1,1)(1,2)"); + yield return (options, "(a*)*(x)", "axa", "(0,2)(1,1)(1,2)"); // was "(0,2)(0,1)(1,2)" + yield return (options, "(a*)+(x)", "x", "(0,1)(0,0)(0,1)"); + yield return (options, "(a*)+(x)", "ax", "(0,2)(1,1)(1,2)"); // was "(0,2)(0,1)(1,2)" + yield return (options, "(a*)+(x)", "axa", "(0,2)(1,1)(1,2)"); // was "(0,2)(0,1)(1,2)" + yield return (options, "(a*){2}(x)", "x", "(0,1)(0,0)(0,1)"); + yield return (options, "(a*){2}(x)", "ax", "(0,2)(1,1)(1,2)"); + yield return (options, "(a*){2}(x)", "axa", "(0,2)(1,1)(1,2)"); + if (!RegexHelpers.IsNonBacktracking(engine)) + { + yield return (options, "(a*)*(x)(\\1)", "x", "(0,1)(0,0)(0,1)(1,1)"); + yield return (options, "(a*)*(x)(\\1)", "ax", "(0,2)(1,1)(1,2)(2,2)"); + yield return (options, "(a*)*(x)(\\1)", "axa", "(0,2)(1,1)(1,2)(2,2)"); // was "(0,3)(0,1)(1,2)(2,3)" + yield return (options, "(a*)*(x)(\\1)(x)", "axax", "(0,4)(0,1)(1,2)(2,3)(3,4)"); + yield return (options, "(a*)*(x)(\\1)(x)", "axxa", "(0,3)(1,1)(1,2)(2,2)(2,3)"); + } + } + } + [Theory] + [MemberData(nameof(Test_MemberData))] + public void Test(Regex r, string input, string expected) + { if (expected == "NOMATCH") { Assert.False(r.IsMatch(input)); diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CustomDerivedRegexScenarioTest.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CustomDerivedRegexScenarioTest.cs index b2659ae08bf4e..50149bf5e3e51 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CustomDerivedRegexScenarioTest.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CustomDerivedRegexScenarioTest.cs @@ -54,7 +54,9 @@ internal class CustomRegexRunnerFactory : RegexRunnerFactory internal class CustomRegexRunner : RegexRunner { public Match? CallScanDirectly(Regex regex, string text, int textbeg, int textend, int textstart, int prevlen, bool quick) +#pragma warning disable SYSLIB0052 // Type or member is obsolete => Scan(regex, text, textbeg, textend, textstart, prevlen, quick); +#pragma warning restore SYSLIB0052 // Type or member is obsolete protected override void InitTrackCount() => base.runtrackcount = 12; diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs index 55854df2094d9..768e0b4ec1ff4 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs @@ -120,7 +120,9 @@ public RegexTestClass() capslist[1] = "1"; capslist[2] = "output"; capsize = 3; +#pragma warning disable SYSLIB0052 // Type or member is obsolete base.InitializeReferences(); +#pragma warning restore SYSLIB0052 // Type or member is obsolete } public RegexTestClass(TimeSpan timeSpan) : this() diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Ctor.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Ctor.Tests.cs index 1311f15aa8bb5..17adb8aa72320 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Ctor.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Ctor.Tests.cs @@ -213,7 +213,9 @@ private sealed class DerivedRegex : Regex public DerivedRegex() { } public DerivedRegex(string pattern) : base(pattern) { } +#pragma warning disable SYSLIB0052 // Type or member is obsolete public new void InitializeReferences() => base.InitializeReferences(); +#pragma warning restore SYSLIB0052 // Type or member is obsolete public new IDictionary Caps { get => base.Caps; set => base.Caps = value; } public new IDictionary CapNames { get => base.CapNames; set => base.CapNames = value; } diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Groups.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Groups.Tests.cs index ddee764c5e132..9aed94346faee 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Groups.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Groups.Tests.cs @@ -3,983 +3,1030 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Tests; -using System.Threading.Tasks; using Xunit; namespace System.Text.RegularExpressions.Tests { public class RegexGroupTests { - public static IEnumerable Groups_Basic_TestData() + public static IEnumerable Groups_MemberData() { foreach (RegexEngine engine in RegexHelpers.AvailableEngines) { - // (A - B) B is a subset of A(ie B only contains chars that are in A) - yield return new object[] { engine, null, "[abcd-[d]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } }; - - yield return new object[] { engine, null, @"[\d-[357]]+", "33312468955", RegexOptions.None, new string[] { "124689" } }; - yield return new object[] { engine, null, @"[\d-[357]]+", "51246897", RegexOptions.None, new string[] { "124689" } }; - yield return new object[] { engine, null, @"[\d-[357]]+", "3312468977", RegexOptions.None, new string[] { "124689" } }; - - yield return new object[] { engine, null, @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } }; - - yield return new object[] { engine, null, @"[\w-[\d]]+", "0AZaz9", RegexOptions.None, new string[] { "AZaz" } }; - yield return new object[] { engine, null, @"[\w-[\p{Ll}]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" } }; - - yield return new object[] { engine, null, @"[\d-[13579]]+", "1024689", RegexOptions.ECMAScript, new string[] { "02468" } }; - yield return new object[] { engine, null, @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } }; - yield return new object[] { engine, null, @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } }; - - yield return new object[] { engine, null, @"[\p{Ll}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } }; - yield return new object[] { engine, null, @"[\p{Nd}-[2468]]+", "20135798", RegexOptions.None, new string[] { "013579" } }; - - yield return new object[] { engine, null, @"[\P{Lu}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } }; - yield return new object[] { engine, null, @"[\P{Nd}-[\p{Ll}]]+", "az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" } }; - - // (A - B) B is a superset of A (ie B contains chars that are in A plus other chars that are not in A) - yield return new object[] { engine, null, "[abcd-[def]]+", "fedddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } }; - - yield return new object[] { engine, null, @"[\d-[357a-z]]+", "az33312468955", RegexOptions.None, new string[] { "124689" } }; - yield return new object[] { engine, null, @"[\d-[de357fgA-Z]]+", "AZ51246897", RegexOptions.None, new string[] { "124689" } }; - yield return new object[] { engine, null, @"[\d-[357\p{Ll}]]+", "az3312468977", RegexOptions.None, new string[] { "124689" } }; - - yield return new object[] { engine, null, @"[\w-[b-y\s]]+", " \tbbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } }; + (CultureInfo Culture, string Pattern, string Input, RegexOptions Options, string[] Expected)[] cases = Groups_MemberData_Cases(engine).ToArray(); + Regex[] regexes = RegexHelpers.GetRegexesAsync(engine, cases.Select(c => (c.Pattern, c.Culture, (RegexOptions?)c.Options, (TimeSpan?)null)).ToArray()).Result; + for (int i = 0; i < regexes.Length; i++) + { + yield return new object[] { regexes[i], cases[i].Culture, cases[i].Input, cases[i].Expected }; + } + } + } - yield return new object[] { engine, null, @"[\w-[\d\p{Po}]]+", "!#0AZaz9", RegexOptions.None, new string[] { "AZaz" } }; - yield return new object[] { engine, null, @"[\w-[\p{Ll}\s]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" } }; + private static IEnumerable<(CultureInfo Culture, string Pattern, string Input, RegexOptions Options, string[] Expected)> Groups_MemberData_Cases(RegexEngine engine) + { + CultureInfo enUS = new CultureInfo("en-US"); + CultureInfo csCZ = new CultureInfo("cs-CZ"); + CultureInfo daDK = new CultureInfo("da-DK"); + CultureInfo trTR = new CultureInfo("tr-TR"); + CultureInfo azLatnAZ = new CultureInfo("az-Latn-AZ"); - yield return new object[] { engine, null, @"[\d-[13579a-zA-Z]]+", "AZ1024689", RegexOptions.ECMAScript, new string[] { "02468" } }; - yield return new object[] { engine, null, @"[\d-[13579abcd]]+", "abcd\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } }; - yield return new object[] { engine, null, @"[\d-[13579\s]]+", " \t\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } }; + // (A - B) B is a subset of A(ie B only contains chars that are in A) + yield return (enUS, "[abcd-[d]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" }); - yield return new object[] { engine, null, @"[\w-[b-y\p{Po}]]+", "!#bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } }; + yield return (enUS, @"[\d-[357]]+", "33312468955", RegexOptions.None, new string[] { "124689" }); + yield return (enUS, @"[\d-[357]]+", "51246897", RegexOptions.None, new string[] { "124689" }); + yield return (enUS, @"[\d-[357]]+", "3312468977", RegexOptions.None, new string[] { "124689" }); - yield return new object[] { engine, null, @"[\w-[b-y!.,]]+", "!.,bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } }; - yield return new object[] { engine, null, "[\\w-[b-y\x00-\x0F]]+", "\0bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } }; + yield return (enUS, @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" }); - yield return new object[] { engine, null, @"[\p{Ll}-[ae-z0-9]]+", "09aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } }; - yield return new object[] { engine, null, @"[\p{Nd}-[2468az]]+", "az20135798", RegexOptions.None, new string[] { "013579" } }; + yield return (enUS, @"[\w-[\d]]+", "0AZaz9", RegexOptions.None, new string[] { "AZaz" }); + yield return (enUS, @"[\w-[\p{Ll}]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" }); - yield return new object[] { engine, null, @"[\P{Lu}-[ae-zA-Z]]+", "AZaaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } }; - yield return new object[] { engine, null, @"[\P{Nd}-[\p{Ll}0123456789]]+", "09az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" } }; + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported + { + yield return (enUS, @"[\d-[13579]]+", "1024689", RegexOptions.ECMAScript, new string[] { "02468" }); + yield return (enUS, @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" }); + } + yield return (enUS, @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" }); - // (A - B) B only contains chars that are not in A - yield return new object[] { engine, null, "[abc-[defg]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } }; + yield return (enUS, @"[\p{Ll}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" }); + yield return (enUS, @"[\p{Nd}-[2468]]+", "20135798", RegexOptions.None, new string[] { "013579" }); - yield return new object[] { engine, null, @"[\d-[abc]]+", "abc09abc", RegexOptions.None, new string[] { "09" } }; - yield return new object[] { engine, null, @"[\d-[a-zA-Z]]+", "az09AZ", RegexOptions.None, new string[] { "09" } }; - yield return new object[] { engine, null, @"[\d-[\p{Ll}]]+", "az09az", RegexOptions.None, new string[] { "09" } }; + yield return (enUS, @"[\P{Lu}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" }); + yield return (enUS, @"[\P{Nd}-[\p{Ll}]]+", "az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" }); - yield return new object[] { engine, null, @"[\w-[\x00-\x0F]]+", "bbbaaaABYZ09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABYZ09zzzyyy" } }; + // (A - B) B is a superset of A (ie B contains chars that are in A plus other chars that are not in A) + yield return (enUS, "[abcd-[def]]+", "fedddaabbccddd", RegexOptions.None, new string[] { "aabbcc" }); - yield return new object[] { engine, null, @"[\w-[\s]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" } }; - yield return new object[] { engine, null, @"[\w-[\W]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" } }; - yield return new object[] { engine, null, @"[\w-[\p{Po}]]+", "#a09AZz!", RegexOptions.None, new string[] { "a09AZz" } }; + yield return (enUS, @"[\d-[357a-z]]+", "az33312468955", RegexOptions.None, new string[] { "124689" }); + yield return (enUS, @"[\d-[de357fgA-Z]]+", "AZ51246897", RegexOptions.None, new string[] { "124689" }); + yield return (enUS, @"[\d-[357\p{Ll}]]+", "az3312468977", RegexOptions.None, new string[] { "124689" }); - yield return new object[] { engine, null, @"[\d-[\D]]+", "azAZ1024689", RegexOptions.ECMAScript, new string[] { "1024689" } }; - yield return new object[] { engine, null, @"[\d-[a-zA-Z]]+", "azAZ\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } }; - yield return new object[] { engine, null, @"[\d-[\p{Ll}]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } }; + yield return (enUS, @"[\w-[b-y\s]]+", " \tbbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" }); - yield return new object[] { engine, null, @"[a-zA-Z0-9-[\s]]+", " \tazAZ09", RegexOptions.None, new string[] { "azAZ09" } }; + yield return (enUS, @"[\w-[\d\p{Po}]]+", "!#0AZaz9", RegexOptions.None, new string[] { "AZaz" }); + yield return (enUS, @"[\w-[\p{Ll}\s]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" }); - yield return new object[] { engine, null, @"[a-zA-Z0-9-[\W]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" } }; - yield return new object[] { engine, null, @"[a-zA-Z0-9-[^a-zA-Z0-9]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" } }; + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported + { + yield return (enUS, @"[\d-[13579a-zA-Z]]+", "AZ1024689", RegexOptions.ECMAScript, new string[] { "02468" }); + yield return (enUS, @"[\d-[13579abcd]]+", "abcd\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" }); + } + yield return (enUS, @"[\d-[13579\s]]+", " \t\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" }); - yield return new object[] { engine, null, @"[\p{Ll}-[A-Z]]+", "AZaz09", RegexOptions.None, new string[] { "az" } }; - yield return new object[] { engine, null, @"[\p{Nd}-[a-z]]+", "az09", RegexOptions.None, new string[] { "09" } }; + yield return (enUS, @"[\w-[b-y\p{Po}]]+", "!#bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" }); - yield return new object[] { engine, null, @"[\P{Lu}-[\p{Lu}]]+", "AZazAZ", RegexOptions.None, new string[] { "az" } }; - yield return new object[] { engine, null, @"[\P{Lu}-[A-Z]]+", "AZazAZ", RegexOptions.None, new string[] { "az" } }; - yield return new object[] { engine, null, @"[\P{Nd}-[\p{Nd}]]+", "azAZ09", RegexOptions.None, new string[] { "azAZ" } }; - yield return new object[] { engine, null, @"[\P{Nd}-[2-8]]+", "1234567890azAZ1234567890", RegexOptions.None, new string[] { "azAZ" } }; + yield return (enUS, @"[\w-[b-y!.,]]+", "!.,bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" }); + yield return (enUS, "[\\w-[b-y\x00-\x0F]]+", "\0bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" }); - // Alternating construct - yield return new object[] { engine, null, @"([ ]|[\w-[0-9]])+", "09az AZ90", RegexOptions.None, new string[] { "az AZ", "Z" } }; - yield return new object[] { engine, null, @"([0-9-[02468]]|[0-9-[13579]])+", "az1234567890za", RegexOptions.None, new string[] { "1234567890", "0" } }; - yield return new object[] { engine, null, @"([^0-9-[a-zAE-Z]]|[\w-[a-zAF-Z]])+", "azBCDE1234567890BCDEFza", RegexOptions.None, new string[] { "BCDE1234567890BCDE", "E" } }; - yield return new object[] { engine, null, @"([\p{Ll}-[aeiou]]|[^\w-[\s]])+", "aeiobcdxyz!@#aeio", RegexOptions.None, new string[] { "bcdxyz!@#", "#" } }; - yield return new object[] { engine, null, @"(?:hello|hi){1,3}", "hello", RegexOptions.None, new string[] { "hello" } }; - yield return new object[] { engine, null, @"(hello|hi){1,3}", "hellohihey", RegexOptions.None, new string[] { "hellohi", "hi" } }; - yield return new object[] { engine, null, @"(?:hello|hi){1,3}", "hellohihey", RegexOptions.None, new string[] { "hellohi" } }; - yield return new object[] { engine, null, @"(?:hello|hi){2,2}", "hellohihey", RegexOptions.None, new string[] { "hellohi" } }; - yield return new object[] { engine, null, @"(?:hello|hi){2,2}?", "hellohihihello", RegexOptions.None, new string[] { "hellohi" } }; - yield return new object[] { engine, null, @"(?:abc|def|ghi|hij|klm|no){1,4}", "this is a test nonoabcxyz this is only a test", RegexOptions.None, new string[] { "nonoabc" } }; - yield return new object[] { engine, null, @"xyz(abc|def)xyz", "abcxyzdefxyzabc", RegexOptions.None, new string[] { "xyzdefxyz", "def" } }; - yield return new object[] { engine, null, @"abc|(?:def|ghi)", "ghi", RegexOptions.None, new string[] { "ghi" } }; - yield return new object[] { engine, null, @"abc|(def|ghi)", "def", RegexOptions.None, new string[] { "def", "def" } }; + yield return (enUS, @"[\p{Ll}-[ae-z0-9]]+", "09aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" }); + yield return (enUS, @"[\p{Nd}-[2468az]]+", "az20135798", RegexOptions.None, new string[] { "013579" }); - // Multiple character classes using character class subtraction - yield return new object[] { engine, null, @"98[\d-[9]][\d-[8]][\d-[0]]", "98911 98881 98870 98871", RegexOptions.None, new string[] { "98871" } }; - yield return new object[] { engine, null, @"m[\w-[^aeiou]][\w-[^aeiou]]t", "mbbt mect meet", RegexOptions.None, new string[] { "meet" } }; + yield return (enUS, @"[\P{Lu}-[ae-zA-Z]]+", "AZaaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" }); + yield return (enUS, @"[\P{Nd}-[\p{Ll}0123456789]]+", "09az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" }); - // Negation with character class subtraction - yield return new object[] { engine, null, "[abcdef-[^bce]]+", "adfbcefda", RegexOptions.None, new string[] { "bce" } }; - yield return new object[] { engine, null, "[^cde-[ag]]+", "agbfxyzga", RegexOptions.None, new string[] { "bfxyz" } }; + // (A - B) B only contains chars that are not in A + yield return (enUS, "[abc-[defg]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" }); - // Misc The idea here is come up with real world examples of char class subtraction. Things that - // would be difficult to define without it - yield return new object[] { engine, null, @"[\p{L}-[^\p{Lu}]]+", "09',.abcxyzABCXYZ", RegexOptions.None, new string[] { "ABCXYZ" } }; + yield return (enUS, @"[\d-[abc]]+", "abc09abc", RegexOptions.None, new string[] { "09" }); + yield return (enUS, @"[\d-[a-zA-Z]]+", "az09AZ", RegexOptions.None, new string[] { "09" }); + yield return (enUS, @"[\d-[\p{Ll}]]+", "az09az", RegexOptions.None, new string[] { "09" }); - yield return new object[] { engine, null, @"[\p{IsGreek}-[\P{Lu}]]+", "\u0390\u03FE\u0386\u0388\u03EC\u03EE\u0400", RegexOptions.None, new string[] { "\u03FE\u0386\u0388\u03EC\u03EE" } }; - yield return new object[] { engine, null, @"[\p{IsBasicLatin}-[G-L]]+", "GAFMZL", RegexOptions.None, new string[] { "AFMZ" } }; + yield return (enUS, @"[\w-[\x00-\x0F]]+", "bbbaaaABYZ09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABYZ09zzzyyy" }); - yield return new object[] { engine, null, "[a-zA-Z-[aeiouAEIOU]]+", "aeiouAEIOUbcdfghjklmnpqrstvwxyz", RegexOptions.None, new string[] { "bcdfghjklmnpqrstvwxyz" } }; + yield return (enUS, @"[\w-[\s]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" }); + yield return (enUS, @"[\w-[\W]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" }); + yield return (enUS, @"[\w-[\p{Po}]]+", "#a09AZz!", RegexOptions.None, new string[] { "a09AZz" }); - // The following is an overly complex way of matching an ip address using char class subtraction - yield return new object[] { engine, null, @"^ - (?^ - ( - ( - (?[\d-[013-9]]) - | - [\d-[2-9]] - ) - (?(Octet2xx) - ( - (?[\d-[01-46-9]]) - | - [\d-[5-9]] - ) + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported + { + yield return (enUS, @"[\d-[\D]]+", "azAZ1024689", RegexOptions.ECMAScript, new string[] { "1024689" }); + yield return (enUS, @"[\d-[a-zA-Z]]+", "azAZ\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" }); + } + yield return (enUS, @"[\d-[\p{Ll}]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" }); + + yield return (enUS, @"[a-zA-Z0-9-[\s]]+", " \tazAZ09", RegexOptions.None, new string[] { "azAZ09" }); + + yield return (enUS, @"[a-zA-Z0-9-[\W]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" }); + yield return (enUS, @"[a-zA-Z0-9-[^a-zA-Z0-9]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" }); + + yield return (enUS, @"[\p{Ll}-[A-Z]]+", "AZaz09", RegexOptions.None, new string[] { "az" }); + yield return (enUS, @"[\p{Nd}-[a-z]]+", "az09", RegexOptions.None, new string[] { "09" }); + + yield return (enUS, @"[\P{Lu}-[\p{Lu}]]+", "AZazAZ", RegexOptions.None, new string[] { "az" }); + yield return (enUS, @"[\P{Lu}-[A-Z]]+", "AZazAZ", RegexOptions.None, new string[] { "az" }); + yield return (enUS, @"[\P{Nd}-[\p{Nd}]]+", "azAZ09", RegexOptions.None, new string[] { "azAZ" }); + yield return (enUS, @"[\P{Nd}-[2-8]]+", "1234567890azAZ1234567890", RegexOptions.None, new string[] { "azAZ" }); + + // Alternating construct + yield return (enUS, @"([ ]|[\w-[0-9]])+", "09az AZ90", RegexOptions.None, new string[] { "az AZ", "Z" }); + yield return (enUS, @"([0-9-[02468]]|[0-9-[13579]])+", "az1234567890za", RegexOptions.None, new string[] { "1234567890", "0" }); + yield return (enUS, @"([^0-9-[a-zAE-Z]]|[\w-[a-zAF-Z]])+", "azBCDE1234567890BCDEFza", RegexOptions.None, new string[] { "BCDE1234567890BCDE", "E" }); + yield return (enUS, @"([\p{Ll}-[aeiou]]|[^\w-[\s]])+", "aeiobcdxyz!@#aeio", RegexOptions.None, new string[] { "bcdxyz!@#", "#" }); + yield return (enUS, @"(?:hello|hi){1,3}", "hello", RegexOptions.None, new string[] { "hello" }); + yield return (enUS, @"(hello|hi){1,3}", "hellohihey", RegexOptions.None, new string[] { "hellohi", "hi" }); + yield return (enUS, @"(?:hello|hi){1,3}", "hellohihey", RegexOptions.None, new string[] { "hellohi" }); + yield return (enUS, @"(?:hello|hi){2,2}", "hellohihey", RegexOptions.None, new string[] { "hellohi" }); + yield return (enUS, @"(?:hello|hi){2,2}?", "hellohihihello", RegexOptions.None, new string[] { "hellohi" }); + yield return (enUS, @"(?:abc|def|ghi|hij|klm|no){1,4}", "this is a test nonoabcxyz this is only a test", RegexOptions.None, new string[] { "nonoabc" }); + yield return (enUS, @"xyz(abc|def)xyz", "abcxyzdefxyzabc", RegexOptions.None, new string[] { "xyzdefxyz", "def" }); + yield return (enUS, @"abc|(?:def|ghi)", "ghi", RegexOptions.None, new string[] { "ghi" }); + yield return (enUS, @"abc|(def|ghi)", "def", RegexOptions.None, new string[] { "def", "def" }); + + // Multiple character classes using character class subtraction + yield return (enUS, @"98[\d-[9]][\d-[8]][\d-[0]]", "98911 98881 98870 98871", RegexOptions.None, new string[] { "98871" }); + yield return (enUS, @"m[\w-[^aeiou]][\w-[^aeiou]]t", "mbbt mect meet", RegexOptions.None, new string[] { "meet" }); + + // Negation with character class subtraction + yield return (enUS, "[abcdef-[^bce]]+", "adfbcefda", RegexOptions.None, new string[] { "bce" }); + yield return (enUS, "[^cde-[ag]]+", "agbfxyzga", RegexOptions.None, new string[] { "bfxyz" }); + + // Misc The idea here is come up with real world examples of char class subtraction. Things that + // would be difficult to define without it + yield return (enUS, @"[\p{L}-[^\p{Lu}]]+", "09',.abcxyzABCXYZ", RegexOptions.None, new string[] { "ABCXYZ" }); + + yield return (enUS, @"[\p{IsGreek}-[\P{Lu}]]+", "\u0390\u03FE\u0386\u0388\u03EC\u03EE\u0400", RegexOptions.None, new string[] { "\u03FE\u0386\u0388\u03EC\u03EE" }); + yield return (enUS, @"[\p{IsBasicLatin}-[G-L]]+", "GAFMZL", RegexOptions.None, new string[] { "AFMZ" }); + + yield return (enUS, "[a-zA-Z-[aeiouAEIOU]]+", "aeiouAEIOUbcdfghjklmnpqrstvwxyz", RegexOptions.None, new string[] { "bcdfghjklmnpqrstvwxyz" }); + + // The following is an overly complex way of matching an ip address using char class subtraction + if (!RegexHelpers.IsNonBacktracking(engine)) // conditionals not supported + { + yield return (enUS, @"^ + (?^ ( - (?(Octet25x) - [\d-[6-9]] + ( + (?[\d-[013-9]]) | - [\d] + [\d-[2-9]] + ) + (?(Octet2xx) + ( + (?[\d-[01-46-9]]) + | + [\d-[5-9]] + ) + ( + (?(Octet25x) + [\d-[6-9]] + | + [\d] + ) + ) + | + [\d]{2} ) ) | - [\d]{2} - ) - ) - | - ([\d][\d]) - | - [\d] - )$" - , "255", RegexOptions.IgnorePatternWhitespace, new string[] { "255", "255", "2", "5", "5", "", "255", "2", "5" } }; - - // Character Class Subtraction - yield return new object[] { engine, null, @"[abcd\-d-[bc]]+", "bbbaaa---dddccc", RegexOptions.None, new string[] { "aaa---ddd" } }; - yield return new object[] { engine, null, @"[^a-f-[\x00-\x60\u007B-\uFFFF]]+", "aaafffgggzzz{{{", RegexOptions.None, new string[] { "gggzzz" } }; - yield return new object[] { engine, null, @"[\[\]a-f-[[]]+", "gggaaafff]]][[[", RegexOptions.None, new string[] { "aaafff]]]" } }; - yield return new object[] { engine, null, @"[\[\]a-f-[]]]+", "gggaaafff[[[]]]", RegexOptions.None, new string[] { "aaafff[[[" } }; - - yield return new object[] { engine, null, @"[ab\-\[cd-[-[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[-[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[-[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[-[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" } }; - - yield return new object[] { engine, null, @"[ab\-\[cd-[[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" } }; - yield return new object[] { engine, null, @"[ab\-\[cd-[[]]]]", "-]]", RegexOptions.None, new string[] { "-]]" } }; - - yield return new object[] { engine, null, @"[a-[c-e]]+", "bbbaaaccc", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"[a-[c-e]]+", "```aaaccc", RegexOptions.None, new string[] { "aaa" } }; - - yield return new object[] { engine, null, @"[a-d\--[bc]]+", "cccaaa--dddbbb", RegexOptions.None, new string[] { "aaa--ddd" } }; - - // Not Character class subtraction - yield return new object[] { engine, null, @"[\0- [bc]+", "!!!\0\0\t\t [[[[bbbcccaaa", RegexOptions.None, new string[] { "\0\0\t\t [[[[bbbccc" } }; - yield return new object[] { engine, null, "[[abcd]-[bc]]+", "a-b]", RegexOptions.None, new string[] { "a-b]" } }; - yield return new object[] { engine, null, "[-[e-g]+", "ddd[[[---eeefffggghhh", RegexOptions.None, new string[] { "[[[---eeefffggg" } }; - yield return new object[] { engine, null, "[-e-g]+", "ddd---eeefffggghhh", RegexOptions.None, new string[] { "---eeefffggg" } }; - yield return new object[] { engine, null, "[a-e - m-p]+", "---a b c d e m n o p---", RegexOptions.None, new string[] { "a b c d e m n o p" } }; - yield return new object[] { engine, null, "[^-[bc]]", "b] c] -] aaaddd]", RegexOptions.None, new string[] { "d]" } }; - yield return new object[] { engine, null, "[^-[bc]]", "b] c] -] aaa]ddd]", RegexOptions.None, new string[] { "a]" } }; - - // Make sure we correctly handle \- - yield return new object[] { engine, null, @"[a\-[bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } }; - yield return new object[] { engine, null, @"[a\-[\-\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } }; - yield return new object[] { engine, null, @"[a\-\[\-\[\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } }; - yield return new object[] { engine, null, @"[abc\--[b]]+", "[[[```bbbaaa---cccddd", RegexOptions.None, new string[] { "aaa---ccc" } }; - yield return new object[] { engine, null, @"[abc\-z-[b]]+", "```aaaccc---zzzbbb", RegexOptions.None, new string[] { "aaaccc---zzz" } }; - yield return new object[] { engine, null, @"[a-d\-[b]+", "```aaabbbcccddd----[[[[]]]", RegexOptions.None, new string[] { "aaabbbcccddd----[[[[" } }; - yield return new object[] { engine, null, @"[abcd\-d\-[bc]+", "bbbaaa---[[[dddccc", RegexOptions.None, new string[] { "bbbaaa---[[[dddccc" } }; - - // Everything works correctly with option RegexOptions.IgnorePatternWhitespace - yield return new object[] { engine, null, "[a - c - [ b ] ]+", "dddaaa ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { " ]]]" } }; - yield return new object[] { engine, null, "[a - c - [ b ] +", "dddaaa ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { "aaa ccc [[[[ bbb " } }; - - // Unicode Char Classes - yield return new object[] { engine, null, @"(\p{Lu}\w*)\s(\p{Lu}\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } }; - yield return new object[] { engine, null, @"(\p{Lu}\p{Ll}*)\s(\p{Lu}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } }; - yield return new object[] { engine, null, @"(\P{Ll}\p{Ll}*)\s(\P{Ll}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } }; - yield return new object[] { engine, null, @"(\P{Lu}+\p{Lu})\s(\P{Lu}+\p{Lu})", "hellO worlD", RegexOptions.None, new string[] { "hellO worlD", "hellO", "worlD" } }; - yield return new object[] { engine, null, @"(\p{Lt}\w*)\s(\p{Lt}*\w*)", "\u01C5ello \u01C5orld", RegexOptions.None, new string[] { "\u01C5ello \u01C5orld", "\u01C5ello", "\u01C5orld" } }; - yield return new object[] { engine, null, @"(\P{Lt}\w*)\s(\P{Lt}*\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } }; - - // Character ranges IgnoreCase - yield return new object[] { engine, null, @"[@-D]+", "eE?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { "@ABCDabcd" } }; - yield return new object[] { engine, null, @"[>-D]+", "eE=>?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { ">?@ABCDabcd" } }; - yield return new object[] { engine, null, @"[\u0554-\u0557]+", "\u0583\u0553\u0554\u0555\u0556\u0584\u0585\u0586\u0557\u0558", RegexOptions.IgnoreCase, new string[] { "\u0554\u0555\u0556\u0584\u0585\u0586\u0557" } }; - yield return new object[] { engine, null, @"[X-\]]+", "wWXYZxyz[\\]^", RegexOptions.IgnoreCase, new string[] { "XYZxyz[\\]" } }; - yield return new object[] { engine, null, @"[X-\u0533]+", "\u0551\u0554\u0560AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563\u0564", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563" } }; - yield return new object[] { engine, null, @"[X-a]+", "wWAXYZaxyz", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz" } }; - yield return new object[] { engine, null, @"[X-c]+", "wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "ABCXYZabcxyz" } }; - yield return new object[] { engine, null, @"[X-\u00C0]+", "\u00C1\u00E1\u00C0\u00E0wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "\u00C0\u00E0wWABCXYZabcxyz" } }; - yield return new object[] { engine, null, @"[\u0100\u0102\u0104]+", "\u00FF \u0100\u0102\u0104\u0101\u0103\u0105\u0106", RegexOptions.IgnoreCase, new string[] { "\u0100\u0102\u0104\u0101\u0103\u0105" } }; - yield return new object[] { engine, null, @"[B-D\u0130]+", "aAeE\u0129\u0131\u0068 BCDbcD\u0130\u0069\u0070", RegexOptions.IgnoreCase, new string[] { "BCDbcD\u0130\u0069" } }; - yield return new object[] { engine, null, @"[\u013B\u013D\u013F]+", "\u013A\u013B\u013D\u013F\u013C\u013E\u0140\u0141", RegexOptions.IgnoreCase, new string[] { "\u013B\u013D\u013F\u013C\u013E\u0140" } }; - - // Escape Chars - yield return new object[] { engine, null, "(Cat)\r(Dog)", "Cat\rDog", RegexOptions.None, new string[] { "Cat\rDog", "Cat", "Dog" } }; - yield return new object[] { engine, null, "(Cat)\t(Dog)", "Cat\tDog", RegexOptions.None, new string[] { "Cat\tDog", "Cat", "Dog" } }; - yield return new object[] { engine, null, "(Cat)\f(Dog)", "Cat\fDog", RegexOptions.None, new string[] { "Cat\fDog", "Cat", "Dog" } }; - - // Miscellaneous { witout matching } - yield return new object[] { engine, null, @"{5", "hello {5 world", RegexOptions.None, new string[] { "{5" } }; - yield return new object[] { engine, null, @"{5,", "hello {5, world", RegexOptions.None, new string[] { "{5," } }; - yield return new object[] { engine, null, @"{5,6", "hello {5,6 world", RegexOptions.None, new string[] { "{5,6" } }; - - // Miscellaneous inline options - yield return new object[] { engine, null, @"(?n:(?cat)(\s+)(?dog))", "cat dog", RegexOptions.None, new string[] { "cat dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?n:(cat)(\s+)(dog))", "cat dog", RegexOptions.None, new string[] { "cat dog" } }; - yield return new object[] { engine, null, @"(?n:(cat)(?\s+)(dog))", "cat dog", RegexOptions.None, new string[] { "cat dog", " " } }; - yield return new object[] { engine, null, @"(?x: + ([\d][\d]) + | + [\d] + )$" + , "255", RegexOptions.IgnorePatternWhitespace, new string[] { "255", "255", "2", "5", "5", "", "255", "2", "5" }); + } + + // Character Class Subtraction + yield return (enUS, @"[abcd\-d-[bc]]+", "bbbaaa---dddccc", RegexOptions.None, new string[] { "aaa---ddd" }); + yield return (enUS, @"[^a-f-[\x00-\x60\u007B-\uFFFF]]+", "aaafffgggzzz{{{", RegexOptions.None, new string[] { "gggzzz" }); + yield return (enUS, @"[\[\]a-f-[[]]+", "gggaaafff]]][[[", RegexOptions.None, new string[] { "aaafff]]]" }); + yield return (enUS, @"[\[\]a-f-[]]]+", "gggaaafff[[[]]]", RegexOptions.None, new string[] { "aaafff[[[" }); + + yield return (enUS, @"[ab\-\[cd-[-[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" }); + yield return (enUS, @"[ab\-\[cd-[-[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" }); + yield return (enUS, @"[ab\-\[cd-[-[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" }); + yield return (enUS, @"[ab\-\[cd-[-[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" }); + + yield return (enUS, @"[ab\-\[cd-[[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" }); + yield return (enUS, @"[ab\-\[cd-[[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" }); + yield return (enUS, @"[ab\-\[cd-[[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" }); + yield return (enUS, @"[ab\-\[cd-[[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" }); + yield return (enUS, @"[ab\-\[cd-[[]]]]", "-]]", RegexOptions.None, new string[] { "-]]" }); + + yield return (enUS, @"[a-[c-e]]+", "bbbaaaccc", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"[a-[c-e]]+", "```aaaccc", RegexOptions.None, new string[] { "aaa" }); + + yield return (enUS, @"[a-d\--[bc]]+", "cccaaa--dddbbb", RegexOptions.None, new string[] { "aaa--ddd" }); + + // Not Character class subtraction + yield return (enUS, @"[\0- [bc]+", "!!!\0\0\t\t [[[[bbbcccaaa", RegexOptions.None, new string[] { "\0\0\t\t [[[[bbbccc" }); + yield return (enUS, "[[abcd]-[bc]]+", "a-b]", RegexOptions.None, new string[] { "a-b]" }); + yield return (enUS, "[-[e-g]+", "ddd[[[---eeefffggghhh", RegexOptions.None, new string[] { "[[[---eeefffggg" }); + yield return (enUS, "[-e-g]+", "ddd---eeefffggghhh", RegexOptions.None, new string[] { "---eeefffggg" }); + yield return (enUS, "[a-e - m-p]+", "---a b c d e m n o p---", RegexOptions.None, new string[] { "a b c d e m n o p" }); + yield return (enUS, "[^-[bc]]", "b] c] -] aaaddd]", RegexOptions.None, new string[] { "d]" }); + yield return (enUS, "[^-[bc]]", "b] c] -] aaa]ddd]", RegexOptions.None, new string[] { "a]" }); + + // Make sure we correctly handle \- + yield return (enUS, @"[a\-[bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" }); + yield return (enUS, @"[a\-[\-\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" }); + yield return (enUS, @"[a\-\[\-\[\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" }); + yield return (enUS, @"[abc\--[b]]+", "[[[```bbbaaa---cccddd", RegexOptions.None, new string[] { "aaa---ccc" }); + yield return (enUS, @"[abc\-z-[b]]+", "```aaaccc---zzzbbb", RegexOptions.None, new string[] { "aaaccc---zzz" }); + yield return (enUS, @"[a-d\-[b]+", "```aaabbbcccddd----[[[[]]]", RegexOptions.None, new string[] { "aaabbbcccddd----[[[[" }); + yield return (enUS, @"[abcd\-d\-[bc]+", "bbbaaa---[[[dddccc", RegexOptions.None, new string[] { "bbbaaa---[[[dddccc" }); + + // Everything works correctly with option RegexOptions.IgnorePatternWhitespace + yield return (enUS, "[a - c - [ b ] ]+", "dddaaa ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { " ]]]" }); + yield return (enUS, "[a - c - [ b ] +", "dddaaa ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { "aaa ccc [[[[ bbb " }); + + // Unicode Char Classes + yield return (enUS, @"(\p{Lu}\w*)\s(\p{Lu}\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" }); + yield return (enUS, @"(\p{Lu}\p{Ll}*)\s(\p{Lu}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" }); + yield return (enUS, @"(\P{Ll}\p{Ll}*)\s(\P{Ll}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" }); + yield return (enUS, @"(\P{Lu}+\p{Lu})\s(\P{Lu}+\p{Lu})", "hellO worlD", RegexOptions.None, new string[] { "hellO worlD", "hellO", "worlD" }); + yield return (enUS, @"(\p{Lt}\w*)\s(\p{Lt}*\w*)", "\u01C5ello \u01C5orld", RegexOptions.None, new string[] { "\u01C5ello \u01C5orld", "\u01C5ello", "\u01C5orld" }); + yield return (enUS, @"(\P{Lt}\w*)\s(\P{Lt}*\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" }); + + // Character ranges IgnoreCase + yield return (enUS, @"[@-D]+", "eE?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { "@ABCDabcd" }); + yield return (enUS, @"[>-D]+", "eE=>?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { ">?@ABCDabcd" }); + yield return (enUS, @"[\u0554-\u0557]+", "\u0583\u0553\u0554\u0555\u0556\u0584\u0585\u0586\u0557\u0558", RegexOptions.IgnoreCase, new string[] { "\u0554\u0555\u0556\u0584\u0585\u0586\u0557" }); + yield return (enUS, @"[X-\]]+", "wWXYZxyz[\\]^", RegexOptions.IgnoreCase, new string[] { "XYZxyz[\\]" }); + yield return (enUS, @"[X-\u0533]+", "\u0551\u0554\u0560AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563\u0564", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563" }); + yield return (enUS, @"[X-a]+", "wWAXYZaxyz", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz" }); + yield return (enUS, @"[X-c]+", "wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "ABCXYZabcxyz" }); + yield return (enUS, @"[X-\u00C0]+", "\u00C1\u00E1\u00C0\u00E0wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "\u00C0\u00E0wWABCXYZabcxyz" }); + yield return (enUS, @"[\u0100\u0102\u0104]+", "\u00FF \u0100\u0102\u0104\u0101\u0103\u0105\u0106", RegexOptions.IgnoreCase, new string[] { "\u0100\u0102\u0104\u0101\u0103\u0105" }); + yield return (enUS, @"[B-D\u0130]+", "aAeE\u0129\u0131\u0068 BCDbcD\u0130\u0069\u0070", RegexOptions.IgnoreCase, new string[] { "BCDbcD\u0130\u0069" }); + yield return (enUS, @"[\u013B\u013D\u013F]+", "\u013A\u013B\u013D\u013F\u013C\u013E\u0140\u0141", RegexOptions.IgnoreCase, new string[] { "\u013B\u013D\u013F\u013C\u013E\u0140" }); + + // Escape Chars + yield return (enUS, "(Cat)\r(Dog)", "Cat\rDog", RegexOptions.None, new string[] { "Cat\rDog", "Cat", "Dog" }); + yield return (enUS, "(Cat)\t(Dog)", "Cat\tDog", RegexOptions.None, new string[] { "Cat\tDog", "Cat", "Dog" }); + yield return (enUS, "(Cat)\f(Dog)", "Cat\fDog", RegexOptions.None, new string[] { "Cat\fDog", "Cat", "Dog" }); + + // Miscellaneous { witout matching } + yield return (enUS, @"{5", "hello {5 world", RegexOptions.None, new string[] { "{5" }); + yield return (enUS, @"{5,", "hello {5, world", RegexOptions.None, new string[] { "{5," }); + yield return (enUS, @"{5,6", "hello {5,6 world", RegexOptions.None, new string[] { "{5,6" }); + + // Miscellaneous inline options + yield return (enUS, @"(?n:(?cat)(\s+)(?dog))", "cat dog", RegexOptions.None, new string[] { "cat dog", "cat", "dog" }); + yield return (enUS, @"(?n:(cat)(\s+)(dog))", "cat dog", RegexOptions.None, new string[] { "cat dog" }); + yield return (enUS, @"(?n:(cat)(?\s+)(dog))", "cat dog", RegexOptions.None, new string[] { "cat dog", " " }); + yield return (enUS, @"(?x: (?cat) # Cat statement (\s+) # Whitespace chars (?dog # Dog statement - ))", "cat dog", RegexOptions.None, new string[] { "cat dog", " ", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?+i:cat)", "CAT", RegexOptions.None, new string[] { "CAT" } }; - - // \d, \D, \s, \S, \w, \W, \P, \p inside character range - yield return new object[] { engine, null, @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.None, new string[] { "cat230927dog", "230927" } }; - yield return new object[] { engine, null, @"([\D]*)dog", "65498catdog58719", RegexOptions.None, new string[] { "catdog", "cat" } }; - yield return new object[] { engine, null, @"cat([\s]*)dog", "wiocat dog3270", RegexOptions.None, new string[] { "cat dog", " " } }; - yield return new object[] { engine, null, @"cat([\S]*)", "sfdcatdog 3270", RegexOptions.None, new string[] { "catdog", "dog" } }; - yield return new object[] { engine, null, @"cat([\w]*)", "sfdcatdog 3270", RegexOptions.None, new string[] { "catdog", "dog" } }; - yield return new object[] { engine, null, @"cat([\W]*)dog", "wiocat dog3270", RegexOptions.None, new string[] { "cat dog", " " } }; - yield return new object[] { engine, null, @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } }; - yield return new object[] { engine, null, @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } }; - - // \x, \u, \a, \b, \e, \f, \n, \r, \t, \v, \c, inside character range - yield return new object[] { engine, null, @"(cat)([\x41]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\u0041]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\a]*)(dog)", "cat\a\a\adog", RegexOptions.None, new string[] { "cat\a\a\adog", "cat", "\a\a\a", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\b]*)(dog)", "cat\b\b\bdog", RegexOptions.None, new string[] { "cat\b\b\bdog", "cat", "\b\b\b", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\e]*)(dog)", "cat\u001B\u001B\u001Bdog", RegexOptions.None, new string[] { "cat\u001B\u001B\u001Bdog", "cat", "\u001B\u001B\u001B", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\f]*)(dog)", "cat\f\f\fdog", RegexOptions.None, new string[] { "cat\f\f\fdog", "cat", "\f\f\f", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\r]*)(dog)", "cat\r\r\rdog", RegexOptions.None, new string[] { "cat\r\r\rdog", "cat", "\r\r\r", "dog" } }; - yield return new object[] { engine, null, @"(cat)([\v]*)(dog)", "cat\v\v\vdog", RegexOptions.None, new string[] { "cat\v\v\vdog", "cat", "\v\v\v", "dog" } }; - + ))", "cat dog", RegexOptions.None, new string[] { "cat dog", " ", "cat", "dog" }); + yield return (enUS, @"(?+i:cat)", "CAT", RegexOptions.None, new string[] { "CAT" }); + + // \d, \D, \s, \S, \w, \W, \P, \p inside character range + yield return (enUS, @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.None, new string[] { "cat230927dog", "230927" }); + yield return (enUS, @"([\D]*)dog", "65498catdog58719", RegexOptions.None, new string[] { "catdog", "cat" }); + yield return (enUS, @"cat([\s]*)dog", "wiocat dog3270", RegexOptions.None, new string[] { "cat dog", " " }); + yield return (enUS, @"cat([\S]*)", "sfdcatdog 3270", RegexOptions.None, new string[] { "catdog", "dog" }); + yield return (enUS, @"cat([\w]*)", "sfdcatdog 3270", RegexOptions.None, new string[] { "catdog", "dog" }); + yield return (enUS, @"cat([\W]*)dog", "wiocat dog3270", RegexOptions.None, new string[] { "cat dog", " " }); + yield return (enUS, @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" }); + yield return (enUS, @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" }); + + // \x, \u, \a, \b, \e, \f, \n, \r, \t, \v, \c, inside character range + yield return (enUS, @"(cat)([\x41]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" }); + yield return (enUS, @"(cat)([\u0041]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" }); + yield return (enUS, @"(cat)([\a]*)(dog)", "cat\a\a\adog", RegexOptions.None, new string[] { "cat\a\a\adog", "cat", "\a\a\a", "dog" }); + yield return (enUS, @"(cat)([\b]*)(dog)", "cat\b\b\bdog", RegexOptions.None, new string[] { "cat\b\b\bdog", "cat", "\b\b\b", "dog" }); + yield return (enUS, @"(cat)([\e]*)(dog)", "cat\u001B\u001B\u001Bdog", RegexOptions.None, new string[] { "cat\u001B\u001B\u001Bdog", "cat", "\u001B\u001B\u001B", "dog" }); + yield return (enUS, @"(cat)([\f]*)(dog)", "cat\f\f\fdog", RegexOptions.None, new string[] { "cat\f\f\fdog", "cat", "\f\f\f", "dog" }); + yield return (enUS, @"(cat)([\r]*)(dog)", "cat\r\r\rdog", RegexOptions.None, new string[] { "cat\r\r\rdog", "cat", "\r\r\r", "dog" }); + yield return (enUS, @"(cat)([\v]*)(dog)", "cat\v\v\vdog", RegexOptions.None, new string[] { "cat\v\v\vdog", "cat", "\v\v\v", "dog" }); + + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported + { // \d, \D, \s, \S, \w, \W, \P, \p inside character range ([0-5]) with ECMA Option - yield return new object[] { engine, null, @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "230927" } }; - yield return new object[] { engine, null, @"([\D]*)dog", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } }; - yield return new object[] { engine, null, @"cat([\s]*)dog", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", " " } }; - yield return new object[] { engine, null, @"cat([\S]*)", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } }; - yield return new object[] { engine, null, @"cat([\w]*)", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } }; - yield return new object[] { engine, null, @"cat([\W]*)dog", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", " " } }; - yield return new object[] { engine, null, @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" } }; - yield return new object[] { engine, null, @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" } }; + yield return (enUS, @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "230927" }); + yield return (enUS, @"([\D]*)dog", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "cat" }); + yield return (enUS, @"cat([\s]*)dog", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", " " }); + yield return (enUS, @"cat([\S]*)", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" }); + yield return (enUS, @"cat([\w]*)", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" }); + yield return (enUS, @"cat([\W]*)dog", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", " " }); + yield return (enUS, @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" }); + yield return (enUS, @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" }); // \d, \D, \s, \S, \w, \W, \P, \p outside character range ([0-5]) with ECMA Option - yield return new object[] { engine, null, @"(cat)\d*dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "cat" } }; - yield return new object[] { engine, null, @"\D*(dog)", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s*(dog)", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\S*", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } }; - yield return new object[] { engine, null, @"(cat)\w*", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } }; - yield return new object[] { engine, null, @"(cat)\W*(dog)", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"\p{Lu}(\w*)\s\p{Lu}(\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "ello", "orld" } }; - yield return new object[] { engine, null, @"\P{Ll}\p{Ll}*\s\P{Ll}\p{Ll}*", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World" } }; - - // Use < in a group - yield return new object[] { engine, null, @"cat(?dog)", "catcatdogdogcat", RegexOptions.None, new string[] { "catdog", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s*(?dog)", "catcat dogdogcat", RegexOptions.None, new string[] { "cat dog", "dog" } }; - yield return new object[] { engine, null, @"(?<1>cat)\s*(?<1>dog)", "catcat dogdogcat", RegexOptions.None, new string[] { "cat dog", "dog" } }; - yield return new object[] { engine, null, @"(?<2048>cat)\s*(?<2048>dog)", "catcat dogdogcat", RegexOptions.None, new string[] { "cat dog", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\w+(?dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } }; - yield return new object[] { engine, null, @"(?cat)\w+(?<-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "" } }; - yield return new object[] { engine, null, @"(?cat)\w+(?dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "_Hello_World_" } }; - yield return new object[] { engine, null, @"(?<1>cat)\w+(?dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } }; - yield return new object[] { engine, null, @"(?cat)\w+(?<2-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } }; - yield return new object[] { engine, null, @"(?<1>cat)\w+(?<2-1>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } }; - - // Quantifiers - yield return new object[] { engine, null, @"(?cat){", "STARTcat{", RegexOptions.None, new string[] { "cat{", "cat" } }; - yield return new object[] { engine, null, @"(?cat){fdsa", "STARTcat{fdsa", RegexOptions.None, new string[] { "cat{fdsa", "cat" } }; - yield return new object[] { engine, null, @"(?cat){1", "STARTcat{1", RegexOptions.None, new string[] { "cat{1", "cat" } }; - yield return new object[] { engine, null, @"(?cat){1END", "STARTcat{1END", RegexOptions.None, new string[] { "cat{1END", "cat" } }; - yield return new object[] { engine, null, @"(?cat){1,", "STARTcat{1,", RegexOptions.None, new string[] { "cat{1,", "cat" } }; - yield return new object[] { engine, null, @"(?cat){1,END", "STARTcat{1,END", RegexOptions.None, new string[] { "cat{1,END", "cat" } }; - yield return new object[] { engine, null, @"(?cat){1,2", "STARTcat{1,2", RegexOptions.None, new string[] { "cat{1,2", "cat" } }; - yield return new object[] { engine, null, @"(?cat){1,2END", "STARTcat{1,2END", RegexOptions.None, new string[] { "cat{1,2END", "cat" } }; - - // Use IgnorePatternWhitespace - yield return new object[] { engine, null, @"(cat) #cat + yield return (enUS, @"(cat)\d*dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "cat" }); + yield return (enUS, @"\D*(dog)", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "dog" }); + yield return (enUS, @"(cat)\s*(dog)", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", "cat", "dog" }); + yield return (enUS, @"(cat)\S*", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" }); + yield return (enUS, @"(cat)\w*", "sfdcatdog 3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" }); + yield return (enUS, @"(cat)\W*(dog)", "wiocat dog3270", RegexOptions.ECMAScript, new string[] { "cat dog", "cat", "dog" }); + yield return (enUS, @"\p{Lu}(\w*)\s\p{Lu}(\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "ello", "orld" }); + yield return (enUS, @"\P{Ll}\p{Ll}*\s\P{Ll}\p{Ll}*", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World" }); + } + + // Use < in a group + yield return (enUS, @"cat(?dog)", "catcatdogdogcat", RegexOptions.None, new string[] { "catdog", "dog" }); + yield return (enUS, @"(?cat)\s*(?dog)", "catcat dogdogcat", RegexOptions.None, new string[] { "cat dog", "dog" }); + yield return (enUS, @"(?<1>cat)\s*(?<1>dog)", "catcat dogdogcat", RegexOptions.None, new string[] { "cat dog", "dog" }); + yield return (enUS, @"(?<2048>cat)\s*(?<2048>dog)", "catcat dogdogcat", RegexOptions.None, new string[] { "cat dog", "dog" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // balancing groups not supported + { + yield return (enUS, @"(?cat)\w+(?dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" }); + yield return (enUS, @"(?cat)\w+(?<-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "" }); + yield return (enUS, @"(?cat)\w+(?dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "_Hello_World_" }); + yield return (enUS, @"(?<1>cat)\w+(?dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" }); + yield return (enUS, @"(?cat)\w+(?<2-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" }); + yield return (enUS, @"(?<1>cat)\w+(?<2-1>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" }); + } + + // Quantifiers + yield return (enUS, @"(?cat){", "STARTcat{", RegexOptions.None, new string[] { "cat{", "cat" }); + yield return (enUS, @"(?cat){fdsa", "STARTcat{fdsa", RegexOptions.None, new string[] { "cat{fdsa", "cat" }); + yield return (enUS, @"(?cat){1", "STARTcat{1", RegexOptions.None, new string[] { "cat{1", "cat" }); + yield return (enUS, @"(?cat){1END", "STARTcat{1END", RegexOptions.None, new string[] { "cat{1END", "cat" }); + yield return (enUS, @"(?cat){1,", "STARTcat{1,", RegexOptions.None, new string[] { "cat{1,", "cat" }); + yield return (enUS, @"(?cat){1,END", "STARTcat{1,END", RegexOptions.None, new string[] { "cat{1,END", "cat" }); + yield return (enUS, @"(?cat){1,2", "STARTcat{1,2", RegexOptions.None, new string[] { "cat{1,2", "cat" }); + yield return (enUS, @"(?cat){1,2END", "STARTcat{1,2END", RegexOptions.None, new string[] { "cat{1,2END", "cat" }); + + // Use IgnorePatternWhitespace + yield return (enUS, @"(cat) #cat \s+ #followed by 1 or more whitespace (dog) #followed by dog - ", "cat dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat) #cat + ", "cat dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat dog", "cat", "dog" }); + yield return (enUS, @"(cat) #cat \s+ #followed by 1 or more whitespace - (dog) #followed by dog", "cat dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat) (?#cat) \s+ (?#followed by 1 or more whitespace) (dog) (?#followed by dog)", "cat dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat dog", "cat", "dog" } }; - - // Back Reference - yield return new object[] { engine, null, @"(?cat)(?dog)\k", "asdfcatdogcatdog", RegexOptions.None, new string[] { "catdogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\k", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\k'cat'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\'cat'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\k<1>", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\k'1'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\<1>", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\'1'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\1", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\1", "asdfcat dogcat dog", RegexOptions.ECMAScript, new string[] { "cat dogcat", "cat", "dog" } }; - - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\k", "asdfcat dogdog dog", RegexOptions.None, new string[] { "cat dogdog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\2", "asdfcat dogdog dog", RegexOptions.None, new string[] { "cat dogdog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(?cat)\s+(?dog)\2", "asdfcat dogdog dog", RegexOptions.ECMAScript, new string[] { "cat dogdog", "cat", "dog" } }; - - // Octal - yield return new object[] { engine, null, @"(cat)(\077)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" } }; - yield return new object[] { engine, null, @"(cat)(\77)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" } }; - yield return new object[] { engine, null, @"(cat)(\176)", "hellocat~dogworld", RegexOptions.None, new string[] { "cat~", "cat", "~" } }; - yield return new object[] { engine, null, @"(cat)(\400)", "hellocat\0dogworld", RegexOptions.None, new string[] { "cat\0", "cat", "\0" } }; - yield return new object[] { engine, null, @"(cat)(\300)", "hellocat\u00C0dogworld", RegexOptions.None, new string[] { "cat\u00C0", "cat", "\u00C0" } }; - yield return new object[] { engine, null, @"(cat)(\477)", "hellocat\u003Fdogworld", RegexOptions.None, new string[] { "cat\u003F", "cat", "\u003F" } }; - yield return new object[] { engine, null, @"(cat)(\777)", "hellocat\u00FFdogworld", RegexOptions.None, new string[] { "cat\u00FF", "cat", "\u00FF" } }; - yield return new object[] { engine, null, @"(cat)(\7770)", "hellocat\u00FF0dogworld", RegexOptions.None, new string[] { "cat\u00FF0", "cat", "\u00FF0" } }; - - yield return new object[] { engine, null, @"(cat)(\077)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" } }; - yield return new object[] { engine, null, @"(cat)(\77)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" } }; - yield return new object[] { engine, null, @"(cat)(\7)", "hellocat\adogworld", RegexOptions.ECMAScript, new string[] { "cat\a", "cat", "\a" } }; - yield return new object[] { engine, null, @"(cat)(\40)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " } }; - yield return new object[] { engine, null, @"(cat)(\040)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " } }; - yield return new object[] { engine, null, @"(cat)(\176)", "hellocatcat76dogworld", RegexOptions.ECMAScript, new string[] { "catcat76", "cat", "cat76" } }; - yield return new object[] { engine, null, @"(cat)(\377)", "hellocat\u00FFdogworld", RegexOptions.ECMAScript, new string[] { "cat\u00FF", "cat", "\u00FF" } }; - yield return new object[] { engine, null, @"(cat)(\400)", "hellocat 0Fdogworld", RegexOptions.ECMAScript, new string[] { "cat 0", "cat", " 0" } }; - - // Decimal - yield return new object[] { engine, null, @"(cat)\s+(?<2147483646>dog)", "asdlkcat dogiwod", RegexOptions.None, new string[] { "cat dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(?<2147483647>dog)", "asdlkcat dogiwod", RegexOptions.None, new string[] { "cat dog", "cat", "dog" } }; - - // Hex - yield return new object[] { engine, null, @"(cat)(\x2a*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2b*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2c*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2d*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2e*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2f*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" } }; - - yield return new object[] { engine, null, @"(cat)(\x2A*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2B*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2C*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2D*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2E*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\x2F*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" } }; - - // ScanControl - yield return new object[] { engine, null, @"(cat)(\c@*)(dog)", "asdlkcat\0\0dogiwod", RegexOptions.None, new string[] { "cat\0\0dog", "cat", "\0\0", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\cA*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\ca*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" } }; - - yield return new object[] { engine, null, @"(cat)(\cC*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\cc*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" } }; - - yield return new object[] { engine, null, @"(cat)(\cD*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\cd*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" } }; - - yield return new object[] { engine, null, @"(cat)(\cX*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\cx*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" } }; - - yield return new object[] { engine, null, @"(cat)(\cZ*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" } }; - yield return new object[] { engine, null, @"(cat)(\cz*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" } }; - - if (!PlatformDetection.IsNetFramework) // `\c[` was not handled in .NET Framework. See https://github.com/dotnet/runtime/issues/24759. - { - yield return new object[] { engine, null, @"(cat)(\c[*)(dog)", "asdlkcat\u001bdogiwod", RegexOptions.None, new string[] { "cat\u001bdog", "cat", "\u001b", "dog" } }; - } + (dog) #followed by dog", "cat dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat dog", "cat", "dog" }); + yield return (enUS, @"(cat) (?#cat) \s+ (?#followed by 1 or more whitespace) (dog) (?#followed by dog)", "cat dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat dog", "cat", "dog" }); - // Atomic Zero-Width Assertions \A \G ^ \Z \z \b \B - //\A - yield return new object[] { engine, null, @"\Acat\s+dog", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"\Acat\s+dog", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"\A(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"\A(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - - //\G - yield return new object[] { engine, null, @"\Gcat\s+dog", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"\Gcat\s+dog", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"\Gcat\s+dog", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"\G(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"\G(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"\G(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - - //^ - yield return new object[] { engine, null, @"^cat\s+dog", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"^cat\s+dog", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"mouse\s\n^cat\s+dog", "mouse\n\ncat \n\n\n dog", RegexOptions.Multiline, new string[] { "mouse\n\ncat \n\n\n dog" } }; - yield return new object[] { engine, null, @"^cat\s+dog", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"^(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"^(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(mouse)\s\n^(cat)\s+(dog)", "mouse\n\ncat \n\n\n dog", RegexOptions.Multiline, new string[] { "mouse\n\ncat \n\n\n dog", "mouse", "cat", "dog" } }; - yield return new object[] { engine, null, @"^(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - - //\Z - yield return new object[] { engine, null, @"cat\s+dog\Z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\Z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\Z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\Z", "cat \n\n\n dog\n", RegexOptions.None, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\Z", "cat \n\n\n dog\n", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\Z", "cat \n\n\n dog\n", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\Z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\Z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\Z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\Z", "cat \n\n\n dog\n", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\Z", "cat \n\n\n dog\n", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\Z", "cat \n\n\n dog\n", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - - //\z - yield return new object[] { engine, null, @"cat\s+dog\z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"cat\s+dog\z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - yield return new object[] { engine, null, @"(cat)\s+(dog)\z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" } }; - - //\b - yield return new object[] { engine, null, @"\bcat\b", "cat", RegexOptions.None, new string[] { "cat" } }; - yield return new object[] { engine, null, @"\bcat\b", "dog cat mouse", RegexOptions.None, new string[] { "cat" } }; - yield return new object[] { engine, null, @"\bcat\b", "cat", RegexOptions.ECMAScript, new string[] { "cat" } }; - yield return new object[] { engine, null, @"\bcat\b", "dog cat mouse", RegexOptions.ECMAScript, new string[] { "cat" } }; - yield return new object[] { engine, null, @".*\bcat\b", "cat", RegexOptions.None, new string[] { "cat" } }; - yield return new object[] { engine, null, @".*\bcat\b", "dog cat mouse", RegexOptions.None, new string[] { "dog cat" } }; - yield return new object[] { engine, null, @".*\bcat\b", "cat", RegexOptions.ECMAScript, new string[] { "cat" } }; - yield return new object[] { engine, null, @".*\bcat\b", "dog cat mouse", RegexOptions.ECMAScript, new string[] { "dog cat" } }; - yield return new object[] { engine, null, @"\b@cat", "123START123@catEND", RegexOptions.None, new string[] { "@cat" } }; - yield return new object[] { engine, null, @"\b\cat)\s+(?dog)\s+\123\s+\234", "asdfcat dog cat23 dog34eia", RegexOptions.ECMAScript, new string[] { "cat dog cat23 dog34", "cat", "dog" } }; - - // Balanced Matching - yield return new object[] { engine, null, @"
- (?> -
(?) | -
(?<-DEPTH>) | - .? - )*? - (?(DEPTH)(?!)) -
", "
this is some
red
text
", RegexOptions.IgnorePatternWhitespace, new string[] { "
this is some
red
text
", "" } }; - - yield return new object[] { engine, null, @"( - ((?'open'<+)[^<>]*)+ - ((?'close-open'>+)[^<>]*)+ - )+", "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", "<02deep_03<03deep_03>>>", "<03deep_03", ">>>", "<", "03deep_03" } }; - - yield return new object[] { engine, null, @"( - (?<)? - [^<>]? - (?>)? - )*", "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", "", "", "01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>" } }; - - yield return new object[] { engine, null, @"( - (?<[^/<>]*>)? - [^<>]? - (?]*>)? - )*", "Cat", RegexOptions.IgnorePatternWhitespace, new string[] { "Cat", "", "", "Cat" } }; - - yield return new object[] { engine, null, @"( - (?<(?[^/<>]*)>)? - [^<>]? - (?>)? - )*", "catdog", RegexOptions.IgnorePatternWhitespace, new string[] { "catdog", "", "", "a", "dog" } }; + // Back Reference + if (!RegexHelpers.IsNonBacktracking(engine)) // back references not supported + { + yield return (enUS, @"(?cat)(?dog)\k", "asdfcatdogcatdog", RegexOptions.None, new string[] { "catdogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\k", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\k'cat'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\'cat'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + + yield return (enUS, @"(?cat)\s+(?dog)\k<1>", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\k'1'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\<1>", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\'1'", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\1", "asdfcat dogcat dog", RegexOptions.None, new string[] { "cat dogcat", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\1", "asdfcat dogcat dog", RegexOptions.ECMAScript, new string[] { "cat dogcat", "cat", "dog" }); + + yield return (enUS, @"(?cat)\s+(?dog)\k", "asdfcat dogdog dog", RegexOptions.None, new string[] { "cat dogdog", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\2", "asdfcat dogdog dog", RegexOptions.None, new string[] { "cat dogdog", "cat", "dog" }); + yield return (enUS, @"(?cat)\s+(?dog)\2", "asdfcat dogdog dog", RegexOptions.ECMAScript, new string[] { "cat dogdog", "cat", "dog" }); + } - // Balanced Matching With Backtracking - yield return new object[] { engine, null, @"( - (?<[^/<>]*>)? - .? - (?]*>)? - )* - (?(start)(?!)) ", "Cat<<<>>><><<<>>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "Cat<<<>>><><<<>>>>", "", "", "Cat" } }; - - // Character Classes and Lazy quantifier - yield return new object[] { engine, null, @"([0-9]+?)([\w]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55", "5", "5" } }; - yield return new object[] { engine, null, @"([0-9]+?)([a-z]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55488a", "55488", "a" } }; - - // Miscellaneous/Regression scenarios - yield return new object[] { engine, null, @"(?1)(?.*?)(?=2)", "1" + Environment.NewLine + "" + Environment.NewLine + "2", RegexOptions.Singleline | RegexOptions.ExplicitCapture, - new string[] { "1" + Environment.NewLine + "" + Environment.NewLine, "1", Environment.NewLine + ""+ Environment.NewLine } }; - - yield return new object[] { engine, null, @"\G<%#(?.*?)?%>", @"<%# DataBinder.Eval(this, ""MyNumber"") %>", RegexOptions.Singleline, new string[] { @"<%# DataBinder.Eval(this, ""MyNumber"") %>", @" DataBinder.Eval(this, ""MyNumber"") " } }; - - // Nested Quantifiers - yield return new object[] { engine, null, @"^[abcd]{0,0x10}*$", "a{0,0x10}}}", RegexOptions.None, new string[] { "a{0,0x10}}}" } }; - - // Lazy operator Backtracking - yield return new object[] { engine, null, @"http://([a-zA-z0-9\-]*\.?)*?(:[0-9]*)??/", "http://www.msn.com/", RegexOptions.IgnoreCase, new string[] { "http://www.msn.com/", "com", string.Empty } }; - yield return new object[] { engine, null, @"http://([a-zA-Z0-9\-]*\.?)*?/", @"http://www.google.com/", RegexOptions.IgnoreCase, new string[] { "http://www.google.com/", "com" } }; - - yield return new object[] { engine, null, @"([a-z]*?)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "c", string.Empty, "c" } }; - yield return new object[] { engine, null, @"^([a-z]*?)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } }; - - // Backtracking - yield return new object[] { engine, null, @"([a-z]*)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } }; - yield return new object[] { engine, null, @"^([a-z]*)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } }; - - // Backtracking with multiple (.*) groups -- important ASP.NET scenario - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "/.aspx", RegexOptions.None, new string[] { "/.aspx", string.Empty, string.Empty } }; - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "/homepage.aspx", RegexOptions.None, new string[] { "/homepage.aspx", string.Empty, "homepage" } }; - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "pages/.aspx", RegexOptions.None, new string[] { "pages/.aspx", "pages", string.Empty } }; - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" } }; - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" } }; - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" } }; - yield return new object[] { engine, null, @"(.*)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" } }; - yield return new object[] { engine, null, @"(.*)/(.*)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" } }; - - // Backtracking with multiple (.+) groups - yield return new object[] { engine, null, @"(.+)/(.+).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" } }; - yield return new object[] { engine, null, @"(.+)/(.+).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" } }; - yield return new object[] { engine, null, @"(.+)/(.+).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" } }; - yield return new object[] { engine, null, @"(.+)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" } }; - yield return new object[] { engine, null, @"(.+)/(.+)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" } }; - - // Backtracking with (.+) group followed by (.*) - yield return new object[] { engine, null, @"(.+)/(.*).aspx", "pages/.aspx", RegexOptions.None, new string[] { "pages/.aspx", "pages", string.Empty } }; - yield return new object[] { engine, null, @"(.+)/(.*).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" } }; - yield return new object[] { engine, null, @"(.+)/(.*).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" } }; - yield return new object[] { engine, null, @"(.+)/(.*).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" } }; - yield return new object[] { engine, null, @"(.+)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" } }; - yield return new object[] { engine, null, @"(.+)/(.*)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" } }; - - // Backtracking with (.*) group followed by (.+) - yield return new object[] { engine, null, @"(.*)/(.+).aspx", "/homepage.aspx", RegexOptions.None, new string[] { "/homepage.aspx", string.Empty, "homepage" } }; - yield return new object[] { engine, null, @"(.*)/(.+).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" } }; - yield return new object[] { engine, null, @"(.*)/(.+).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" } }; - yield return new object[] { engine, null, @"(.*)/(.+).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" } }; - yield return new object[] { engine, null, @"(.*)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" } }; - yield return new object[] { engine, null, @"(.*)/(.+)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" } }; - - // Captures inside varying constructs with backtracking needing to uncapture - yield return new object[] { engine, null, @"a(bc)d|abc(e)", "abce", RegexOptions.None, new string[] { "abce", "", "e" } }; // alternation - yield return new object[] { engine, null, @"((ab){2}cd)*", "ababcdababcdababc", RegexOptions.None, new string[] { "ababcdababcd", "ababcd", "ab" } }; // loop - yield return new object[] { engine, null, @"(ab(?=(\w)\w))*a", "aba", RegexOptions.None, new string[] { "a", "", "" } }; // positive lookahead in a loop - yield return new object[] { engine, null, @"(ab(?=(\w)\w))*a", "ababa", RegexOptions.None, new string[] { "aba", "ab", "a" } }; // positive lookahead in a loop - yield return new object[] { engine, null, @"(ab(?=(\w)\w))*a", "abababa", RegexOptions.None, new string[] { "ababa", "ab", "a" } }; // positive lookahead in a loop - yield return new object[] { engine, null, @"\w\w(?!(\d)\d)", "aa..", RegexOptions.None, new string[] { "aa", "" } }; // negative lookahead - yield return new object[] { engine, null, @"\w\w(?!(\d)\d)", "aa.3", RegexOptions.None, new string[] { "aa", "" } }; // negative lookahead - - // Quantifiers - yield return new object[] { engine, null, @"a*", "", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"a*", "a", RegexOptions.None, new string[] { "a" } }; - yield return new object[] { engine, null, @"a*", "aa", RegexOptions.None, new string[] { "aa" } }; - yield return new object[] { engine, null, @"a*", "aaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"a*?", "", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"a*?", "a", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"a*?", "aa", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"a+?", "aa", RegexOptions.None, new string[] { "a" } }; - yield return new object[] { engine, null, @"a{1,", "a{1,", RegexOptions.None, new string[] { "a{1," } }; - yield return new object[] { engine, null, @"a{1,3}", "aaaaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"a{1,3}?", "aaaaa", RegexOptions.None, new string[] { "a" } }; - yield return new object[] { engine, null, @"a{2,2}", "aaaaa", RegexOptions.None, new string[] { "aa" } }; - yield return new object[] { engine, null, @"a{2,2}?", "aaaaa", RegexOptions.None, new string[] { "aa" } }; - yield return new object[] { engine, null, @".{1,3}", "bb\nba", RegexOptions.None, new string[] { "bb" } }; - yield return new object[] { engine, null, @".{1,3}?", "bb\nba", RegexOptions.None, new string[] { "b" } }; - yield return new object[] { engine, null, @".{2,2}", "bbb\nba", RegexOptions.None, new string[] { "bb" } }; - yield return new object[] { engine, null, @".{2,2}?", "bbb\nba", RegexOptions.None, new string[] { "bb" } }; - yield return new object[] { engine, null, @"[abc]{1,3}", "ccaba", RegexOptions.None, new string[] { "cca" } }; - yield return new object[] { engine, null, @"[abc]{1,3}?", "ccaba", RegexOptions.None, new string[] { "c" } }; - yield return new object[] { engine, null, @"[abc]{2,2}", "ccaba", RegexOptions.None, new string[] { "cc" } }; - yield return new object[] { engine, null, @"[abc]{2,2}?", "ccaba", RegexOptions.None, new string[] { "cc" } }; - yield return new object[] { engine, null, @"(?:[abc]def){1,3}xyz", "cdefxyz", RegexOptions.None, new string[] { "cdefxyz" } }; - yield return new object[] { engine, null, @"(?:[abc]def){1,3}xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "adefbdefcdefxyz" } }; - yield return new object[] { engine, null, @"(?:[abc]def){1,3}?xyz", "cdefxyz", RegexOptions.None, new string[] { "cdefxyz" } }; - yield return new object[] { engine, null, @"(?:[abc]def){1,3}?xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "adefbdefcdefxyz" } }; - yield return new object[] { engine, null, @"(?:[abc]def){2,2}xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "bdefcdefxyz" } }; - yield return new object[] { engine, null, @"(?:[abc]def){2,2}?xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "bdefcdefxyz" } }; - foreach (string prefix in new[] { "", "xyz" }) - { - yield return new object[] { engine, null, prefix + @"(?:[abc]def){1,3}", prefix + "cdef", RegexOptions.None, new string[] { prefix + "cdef" } }; - yield return new object[] { engine, null, prefix + @"(?:[abc]def){1,3}", prefix + "cdefadefbdef", RegexOptions.None, new string[] { prefix + "cdefadefbdef" } }; - yield return new object[] { engine, null, prefix + @"(?:[abc]def){1,3}", prefix + "cdefadefbdefadef", RegexOptions.None, new string[] { prefix + "cdefadefbdef" } }; - yield return new object[] { engine, null, prefix + @"(?:[abc]def){1,3}?", prefix + "cdef", RegexOptions.None, new string[] { prefix + "cdef" } }; - yield return new object[] { engine, null, prefix + @"(?:[abc]def){1,3}?", prefix + "cdefadefbdef", RegexOptions.None, new string[] { prefix + "cdef" } }; - yield return new object[] { engine, null, prefix + @"(?:[abc]def){2,2}", prefix + "cdefadefbdefadef", RegexOptions.None, new string[] { prefix + "cdefadef" } }; - yield return new object[] { engine, null, prefix + @"(?:[abc]def){2,2}?", prefix + "cdefadefbdefadef", RegexOptions.None, new string[] { prefix + "cdefadef" } }; - } - yield return new object[] { engine, null, @"(cat){", "cat{", RegexOptions.None, new string[] { "cat{", "cat" } }; - yield return new object[] { engine, null, @"(cat){}", "cat{}", RegexOptions.None, new string[] { "cat{}", "cat" } }; - yield return new object[] { engine, null, @"(cat){,", "cat{,", RegexOptions.None, new string[] { "cat{,", "cat" } }; - yield return new object[] { engine, null, @"(cat){,}", "cat{,}", RegexOptions.None, new string[] { "cat{,}", "cat" } }; - yield return new object[] { engine, null, @"(cat){cat}", "cat{cat}", RegexOptions.None, new string[] { "cat{cat}", "cat" } }; - yield return new object[] { engine, null, @"(cat){cat,5}", "cat{cat,5}", RegexOptions.None, new string[] { "cat{cat,5}", "cat" } }; - yield return new object[] { engine, null, @"(cat){5,dog}", "cat{5,dog}", RegexOptions.None, new string[] { "cat{5,dog}", "cat" } }; - yield return new object[] { engine, null, @"(cat){cat,dog}", "cat{cat,dog}", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" } }; - yield return new object[] { engine, null, @"(cat){,}?", "cat{,}?", RegexOptions.None, new string[] { "cat{,}", "cat" } }; - yield return new object[] { engine, null, @"(cat){cat}?", "cat{cat}?", RegexOptions.None, new string[] { "cat{cat}", "cat" } }; - yield return new object[] { engine, null, @"(cat){cat,5}?", "cat{cat,5}?", RegexOptions.None, new string[] { "cat{cat,5}", "cat" } }; - yield return new object[] { engine, null, @"(cat){5,dog}?", "cat{5,dog}?", RegexOptions.None, new string[] { "cat{5,dog}", "cat" } }; - yield return new object[] { engine, null, @"(cat){cat,dog}?", "cat{cat,dog}?", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" } }; - - // Atomic subexpressions - // Implicitly upgrading (or not) oneloop to be atomic - yield return new object[] { engine, null, @"a*b", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*b+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*b+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*(?>b+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*[^a]", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*[^a]+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*[^a]+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*(?>[^a]+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*bcd", "aaabcd", RegexOptions.None, new string[] { "aaabcd" } }; - yield return new object[] { engine, null, @"a*[bcd]", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*[bcd]+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*[bcd]+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*(?>[bcd]+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*[bcd]{1,3}", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"a*([bcd]ab|[bef]cd){1,3}", "aaababecdcac", RegexOptions.ExplicitCapture, new string[] { "aaababecd" } }; - yield return new object[] { engine, null, @"a*([bcd]|[aef]){1,3}", "befb", RegexOptions.ExplicitCapture, new string[] { "bef" } }; // can't upgrade - yield return new object[] { engine, null, @"a*$", "aaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"a*$", "aaa", RegexOptions.Multiline, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"a*\b", "aaa bbb", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"a*\b", "aaa bbb", RegexOptions.ECMAScript, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"@*\B", "@@@", RegexOptions.None, new string[] { "@@@" } }; - yield return new object[] { engine, null, @"@*\B", "@@@", RegexOptions.ECMAScript, new string[] { "@@@" } }; - yield return new object[] { engine, null, @"(?:abcd*|efgh)i", "efghi", RegexOptions.None, new string[] { "efghi" } }; - yield return new object[] { engine, null, @"(?:abcd|efgh*)i", "efgi", RegexOptions.None, new string[] { "efgi" } }; - yield return new object[] { engine, null, @"(?:abcd|efghj{2,}|j[klm]o+)i", "efghjjjjji", RegexOptions.None, new string[] { "efghjjjjji" } }; - yield return new object[] { engine, null, @"(?:abcd|efghi{2,}|j[klm]o+)i", "efghiii", RegexOptions.None, new string[] { "efghiii" } }; - yield return new object[] { engine, null, @"(?:abcd|efghi{2,}|j[klm]o+)i", "efghiiiiiiii", RegexOptions.None, new string[] { "efghiiiiiiii" } }; - yield return new object[] { engine, null, @"a?ba?ba?ba?b", "abbabab", RegexOptions.None, new string[] { "abbabab" } }; - yield return new object[] { engine, null, @"a?ba?ba?ba?b", "abBAbab", RegexOptions.IgnoreCase, new string[] { "abBAbab" } }; - // Implicitly upgrading (or not) notoneloop to be atomic - yield return new object[] { engine, null, @"[^b]*b", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[^b]*b+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[^b]*b+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[^b]*(?>b+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[^b]*bac", "aaabac", RegexOptions.None, new string[] { "aaabac" } }; - yield return new object[] { engine, null, @"[^b]*", "aaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"(?:abc[^b]*|efgh)i", "efghi", RegexOptions.None, new string[] { "efghi" } }; // can't upgrade - yield return new object[] { engine, null, @"(?:abcd|efg[^b]*)b", "efgb", RegexOptions.None, new string[] { "efgb" } }; - yield return new object[] { engine, null, @"(?:abcd|efg[^b]*)i", "efgi", RegexOptions.None, new string[] { "efgi" } }; // can't upgrade - yield return new object[] { engine, null, @"[^a]?a[^a]?a[^a]?a[^a]?a", "baababa", RegexOptions.None, new string[] { "baababa" } }; - yield return new object[] { engine, null, @"[^a]?a[^a]?a[^a]?a[^a]?a", "BAababa", RegexOptions.IgnoreCase, new string[] { "BAababa" } }; - // Implicitly upgrading (or not) setloop to be atomic - yield return new object[] { engine, null, @"[ac]*", "aaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"[ac]*b", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*b+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*b+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*(?>b+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*[^a]", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*[^a]+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*[^a]+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*(?>[^a]+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*bcd", "aaabcd", RegexOptions.None, new string[] { "aaabcd" } }; - yield return new object[] { engine, null, @"[ac]*[bd]", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*[bd]+", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*[bd]+?", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*(?>[bd]+)", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*[bd]{1,3}", "aaab", RegexOptions.None, new string[] { "aaab" } }; - yield return new object[] { engine, null, @"[ac]*$", "aaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"[ac]*$", "aaa", RegexOptions.Multiline, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"[ac]*\b", "aaa bbb", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"[ac]*\b", "aaa bbb", RegexOptions.ECMAScript, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"[@']*\B", "@@@", RegexOptions.None, new string[] { "@@@" } }; - yield return new object[] { engine, null, @"[@']*\B", "@@@", RegexOptions.ECMAScript, new string[] { "@@@" } }; - yield return new object[] { engine, null, @".*.", "@@@", RegexOptions.Singleline, new string[] { "@@@" } }; - yield return new object[] { engine, null, @"(?:abcd|efg[hij]*)h", "efgh", RegexOptions.None, new string[] { "efgh" } }; // can't upgrade - yield return new object[] { engine, null, @"(?:abcd|efg[hij]*)ih", "efgjih", RegexOptions.None, new string[] { "efgjih" } }; // can't upgrade - yield return new object[] { engine, null, @"(?:abcd|efg[hij]*)k", "efgjk", RegexOptions.None, new string[] { "efgjk" } }; - yield return new object[] { engine, null, @"[ace]?b[ace]?b[ace]?b[ace]?b", "cbbabeb", RegexOptions.None, new string[] { "cbbabeb" } }; - yield return new object[] { engine, null, @"[ace]?b[ace]?b[ace]?b[ace]?b", "cBbAbEb", RegexOptions.IgnoreCase, new string[] { "cBbAbEb" } }; - yield return new object[] { engine, null, @"a[^wz]*w", "abcdcdcdwz", RegexOptions.None, new string[] { "abcdcdcdw" } }; - yield return new object[] { engine, null, @"a[^wyz]*w", "abcdcdcdwz", RegexOptions.None, new string[] { "abcdcdcdw" } }; - yield return new object[] { engine, null, @"a[^wyz]*W", "abcdcdcdWz", RegexOptions.IgnoreCase, new string[] { "abcdcdcdW" } }; - // Implicitly upgrading (or not) concat loops to be atomic - yield return new object[] { engine, null, @"(?:[ab]c[de]f)*", "", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?:[ab]c[de]f)*", "acdf", RegexOptions.None, new string[] { "acdf" } }; - yield return new object[] { engine, null, @"(?:[ab]c[de]f)*", "acdfbcef", RegexOptions.None, new string[] { "acdfbcef" } }; - yield return new object[] { engine, null, @"(?:[ab]c[de]f)*", "cdfbcef", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?:[ab]c[de]f)+", "cdfbcef", RegexOptions.None, new string[] { "bcef" } }; - yield return new object[] { engine, null, @"(?:[ab]c[de]f)*", "bcefbcdfacfe", RegexOptions.None, new string[] { "bcefbcdf" } }; - // Implicitly upgrading (or not) nested loops to be atomic - yield return new object[] { engine, null, @"(?:a){3}", "aaaaaaaaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"(?:a){3}?", "aaaaaaaaa", RegexOptions.None, new string[] { "aaa" } }; - yield return new object[] { engine, null, @"(?:a{2}){3}", "aaaaaaaaa", RegexOptions.None, new string[] { "aaaaaa" } }; - yield return new object[] { engine, null, @"(?:a{2}?){3}?", "aaaaaaaaa", RegexOptions.None, new string[] { "aaaaaa" } }; - yield return new object[] { engine, null, @"(?:(?:[ab]c[de]f){3}){2}", "acdfbcdfacefbcefbcefbcdfacdef", RegexOptions.None, new string[] { "acdfbcdfacefbcefbcefbcdf" } }; - yield return new object[] { engine, null, @"(?:(?:[ab]c[de]f){3}hello){2}", "aaaaaacdfbcdfacefhellobcefbcefbcdfhellooooo", RegexOptions.None, new string[] { "acdfbcdfacefhellobcefbcefbcdfhello" } }; - yield return new object[] { engine, null, @"CN=(.*[^,]+).*", "CN=localhost", RegexOptions.Singleline, new string[] { "CN=localhost", "localhost" } }; - // Nested atomic - yield return new object[] { engine, null, @"(?>abc[def]gh(i*))", "123abceghiii456", RegexOptions.None, new string[] { "abceghiii", "iii" } }; - yield return new object[] { engine, null, @"(?>(?:abc)*)", "abcabcabc", RegexOptions.None, new string[] { "abcabcabc" } }; - - // Anchoring loops beginning with .* / .+ - yield return new object[] { engine, null, @".*", "", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @".*", "\n\n\n\n", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @".*", "\n\n\n\n", RegexOptions.Singleline, new string[] { "\n\n\n\n" } }; - yield return new object[] { engine, null, @".*[1a]", "\n\n\n\n1", RegexOptions.None, new string[] { "1" } }; - yield return new object[] { engine, null, @"(?s).*(?-s)[1a]", "1\n\n\n\n", RegexOptions.None, new string[] { "1" } }; - yield return new object[] { engine, null, @"(?s).*(?-s)[1a]", "\n\n\n\n1", RegexOptions.None, new string[] { "\n\n\n\n1" } }; - yield return new object[] { engine, null, @".*|.*|.*", "", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @".*123|abc", "abc\n123", RegexOptions.None, new string[] { "abc" } }; - yield return new object[] { engine, null, @".*123|abc", "abc\n123", RegexOptions.Singleline, new string[] { "abc\n123" } }; - yield return new object[] { engine, null, @"abc|.*123", "abc\n123", RegexOptions.Singleline, new string[] { "abc" } }; - yield return new object[] { engine, null, @".*", "\n", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @".*\n", "\n", RegexOptions.None, new string[] { "\n" } }; - yield return new object[] { engine, null, @".*", "\n", RegexOptions.Singleline, new string[] { "\n" } }; - yield return new object[] { engine, null, @".*\n", "\n", RegexOptions.Singleline, new string[] { "\n" } }; - yield return new object[] { engine, null, @".*", "abc", RegexOptions.None, new string[] { "abc" } }; - yield return new object[] { engine, null, @".*abc", "abc", RegexOptions.None, new string[] { "abc" } }; - yield return new object[] { engine, null, @".*abc|ghi", "ghi", RegexOptions.None, new string[] { "ghi" } }; - yield return new object[] { engine, null, @".*abc|.*ghi", "abcghi", RegexOptions.None, new string[] { "abc" } }; - yield return new object[] { engine, null, @".*ghi|.*abc", "abcghi", RegexOptions.None, new string[] { "abcghi" } }; - yield return new object[] { engine, null, @".*abc|.*ghi", "bcghi", RegexOptions.None, new string[] { "bcghi" } }; - yield return new object[] { engine, null, @".*abc|.+c", " \n \n bc", RegexOptions.None, new string[] { " bc" } }; - yield return new object[] { engine, null, @".*abc", "12345 abc", RegexOptions.None, new string[] { "12345 abc" } }; - yield return new object[] { engine, null, @".*abc", "12345\n abc", RegexOptions.None, new string[] { " abc" } }; - yield return new object[] { engine, null, @".*abc", "12345\n abc", RegexOptions.Singleline, new string[] { "12345\n abc" } }; - yield return new object[] { engine, null, @"(.*)abc\1", "\n12345abc12345", RegexOptions.Singleline, new string[] { "12345abc12345", "12345" } }; - yield return new object[] { engine, null, @".*\nabc", "\n123\nabc", RegexOptions.None, new string[] { "123\nabc" } }; - yield return new object[] { engine, null, @".*\nabc", "\n123\nabc", RegexOptions.Singleline, new string[] { "\n123\nabc" } }; - yield return new object[] { engine, null, @".*abc", "abc abc abc \nabc", RegexOptions.None, new string[] { "abc abc abc" } }; - yield return new object[] { engine, null, @".*abc", "abc abc abc \nabc", RegexOptions.Singleline, new string[] { "abc abc abc \nabc" } }; - yield return new object[] { engine, null, @".*?abc", "abc abc abc \nabc", RegexOptions.None, new string[] { "abc" } }; - yield return new object[] { engine, null, @"[^\n]*abc", "123abc\n456abc\n789abc", RegexOptions.None, new string[] { "123abc" } }; - yield return new object[] { engine, null, @"[^\n]*abc", "123abc\n456abc\n789abc", RegexOptions.Singleline, new string[] { "123abc" } }; - yield return new object[] { engine, null, @"[^\n]*abc", "123ab\n456abc\n789abc", RegexOptions.None, new string[] { "456abc" } }; - yield return new object[] { engine, null, @"[^\n]*abc", "123ab\n456abc\n789abc", RegexOptions.Singleline, new string[] { "456abc" } }; - yield return new object[] { engine, null, @".+", "a", RegexOptions.None, new string[] { "a" } }; - yield return new object[] { engine, null, @".+", "\nabc", RegexOptions.None, new string[] { "abc" } }; - yield return new object[] { engine, null, @".+", "\n", RegexOptions.Singleline, new string[] { "\n" } }; - yield return new object[] { engine, null, @".+", "\nabc", RegexOptions.Singleline, new string[] { "\nabc" } }; - yield return new object[] { engine, null, @".+abc", "aaaabc", RegexOptions.None, new string[] { "aaaabc" } }; - yield return new object[] { engine, null, @".+abc", "12345 abc", RegexOptions.None, new string[] { "12345 abc" } }; - yield return new object[] { engine, null, @".+abc", "12345\n abc", RegexOptions.None, new string[] { " abc" } }; - yield return new object[] { engine, null, @".+abc", "12345\n abc", RegexOptions.Singleline, new string[] { "12345\n abc" } }; - yield return new object[] { engine, null, @"(.+)abc\1", "\n12345abc12345", RegexOptions.Singleline, new string[] { "12345abc12345", "12345" } }; - - // Unanchored .* - yield return new object[] { engine, null, @"\A\s*(?\w+)(\s*\((?.*)\))?\s*\Z", "Match(Name)", RegexOptions.None, new string[] { "Match(Name)", "(Name)", "Match", "Name" } }; - yield return new object[] { engine, null, @"\A\s*(?\w+)(\s*\((?.*)\))?\s*\Z", "Match(Na\nme)", RegexOptions.Singleline, new string[] { "Match(Na\nme)", "(Na\nme)", "Match", "Na\nme" } }; - foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Singleline }) - { - yield return new object[] { engine, null, @"abcd.*", @"abcabcd", options, new string[] { "abcd" } }; - yield return new object[] { engine, null, @"abcd.*", @"abcabcde", options, new string[] { "abcde" } }; - yield return new object[] { engine, null, @"abcd.*", @"abcabcdefg", options, new string[] { "abcdefg" } }; - yield return new object[] { engine, null, @"abcd(.*)", @"ababcd", options, new string[] { "abcd", "" } }; - yield return new object[] { engine, null, @"abcd(.*)", @"aabcde", options, new string[] { "abcde", "e" } }; - yield return new object[] { engine, null, @"abcd(.*)", @"abcabcdefg", options, new string[] { "abcdefg", "efg" } }; - yield return new object[] { engine, null, @"abcd(.*)e", @"abcabcdefg", options, new string[] { "abcde", "" } }; - yield return new object[] { engine, null, @"abcd(.*)f", @"abcabcdefg", options, new string[] { "abcdef", "e" } }; - } + // Octal + yield return (enUS, @"(cat)(\077)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" }); + yield return (enUS, @"(cat)(\77)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" }); + yield return (enUS, @"(cat)(\176)", "hellocat~dogworld", RegexOptions.None, new string[] { "cat~", "cat", "~" }); + yield return (enUS, @"(cat)(\400)", "hellocat\0dogworld", RegexOptions.None, new string[] { "cat\0", "cat", "\0" }); + yield return (enUS, @"(cat)(\300)", "hellocat\u00C0dogworld", RegexOptions.None, new string[] { "cat\u00C0", "cat", "\u00C0" }); + yield return (enUS, @"(cat)(\477)", "hellocat\u003Fdogworld", RegexOptions.None, new string[] { "cat\u003F", "cat", "\u003F" }); + yield return (enUS, @"(cat)(\777)", "hellocat\u00FFdogworld", RegexOptions.None, new string[] { "cat\u00FF", "cat", "\u00FF" }); + yield return (enUS, @"(cat)(\7770)", "hellocat\u00FF0dogworld", RegexOptions.None, new string[] { "cat\u00FF0", "cat", "\u00FF0" }); + + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported + { + yield return (enUS, @"(cat)(\077)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" }); + yield return (enUS, @"(cat)(\77)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" }); + yield return (enUS, @"(cat)(\7)", "hellocat\adogworld", RegexOptions.ECMAScript, new string[] { "cat\a", "cat", "\a" }); + yield return (enUS, @"(cat)(\40)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " }); + yield return (enUS, @"(cat)(\040)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " }); + yield return (enUS, @"(cat)(\176)", "hellocatcat76dogworld", RegexOptions.ECMAScript, new string[] { "catcat76", "cat", "cat76" }); + yield return (enUS, @"(cat)(\377)", "hellocat\u00FFdogworld", RegexOptions.ECMAScript, new string[] { "cat\u00FF", "cat", "\u00FF" }); + yield return (enUS, @"(cat)(\400)", "hellocat 0Fdogworld", RegexOptions.ECMAScript, new string[] { "cat 0", "cat", " 0" }); + } - // Grouping Constructs - yield return new object[] { engine, null, @"()", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } }; - yield return new object[] { engine, null, @"(?)", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } }; - yield return new object[] { engine, null, @"(?'cat')", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } }; - yield return new object[] { engine, null, @"(?:)", "cat", RegexOptions.None, new string[] { string.Empty } }; - yield return new object[] { engine, null, @"(?imn)", "cat", RegexOptions.None, new string[] { string.Empty } }; - yield return new object[] { engine, null, @"(?imn)cat", "(?imn)cat", RegexOptions.None, new string[] { "cat" } }; - yield return new object[] { engine, null, @"(?=)", "cat", RegexOptions.None, new string[] { string.Empty } }; - yield return new object[] { engine, null, @"(?<=)", "cat", RegexOptions.None, new string[] { string.Empty } }; - yield return new object[] { engine, null, @"(?>)", "cat", RegexOptions.None, new string[] { string.Empty } }; - - // Alternation construct - yield return new object[] { engine, null, @"(?()|)", "(?()|)", RegexOptions.None, new string[] { "" } }; - - yield return new object[] { engine, null, @"(?(cat)|)", "cat", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?(cat)|)", "dog", RegexOptions.None, new string[] { "" } }; - - yield return new object[] { engine, null, @"(?(cat)catdog|)", "catdog", RegexOptions.None, new string[] { "catdog" } }; - yield return new object[] { engine, null, @"(?(cat)cat\w\w\w)*", "catdogcathog", RegexOptions.None, new string[] { "catdogcathog" } }; - yield return new object[] { engine, null, @"(?(?=cat)cat\w\w\w)*", "catdogcathog", RegexOptions.None, new string[] { "catdogcathog" } }; - yield return new object[] { engine, null, @"(?(cat)catdog|)", "dog", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?(cat)dog|)", "dog", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?(cat)dog|)", "cat", RegexOptions.None, new string[] { "" } }; - - yield return new object[] { engine, null, @"(?(cat)|catdog)", "cat", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?(cat)|catdog)", "catdog", RegexOptions.None, new string[] { "" } }; - yield return new object[] { engine, null, @"(?(cat)|dog)", "dog", RegexOptions.None, new string[] { "dog" } }; - - yield return new object[] { engine, null, @"(?((\w{3}))\1\1|no)", "dogdogdog", RegexOptions.None, new string[] { "dogdog", "dog" } }; - yield return new object[] { engine, null, @"(?((\w{3}))\1\1|no)", "no", RegexOptions.None, new string[] { "no", "" } }; - - // Invalid unicode - yield return new object[] { engine, null, "([\u0000-\uFFFF-[azAZ09]]|[\u0000-\uFFFF-[^azAZ09]])+", "azAZBCDE1234567890BCDEFAZza", RegexOptions.None, new string[] { "azAZBCDE1234567890BCDEFAZza", "a" } }; - yield return new object[] { engine, null, "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]+", "abcxyzABCXYZ123890", RegexOptions.None, new string[] { "bcxyzABCXYZ123890" } }; - yield return new object[] { engine, null, "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]]+", "bcxyzABCXYZ123890a", RegexOptions.None, new string[] { "a" } }; - yield return new object[] { engine, null, "[\u0000-\uFFFF-[\\p{P}\\p{S}\\p{C}]]+", "!@`';.,$+<>=\x0001\x001FazAZ09", RegexOptions.None, new string[] { "azAZ09" } }; - - yield return new object[] { engine, null, @"[\uFFFD-\uFFFF]+", "\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFD\uFFFE\uFFFF" } }; - yield return new object[] { engine, null, @"[\uFFFC-\uFFFE]+", "\uFFFB\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFC\uFFFD\uFFFE" } }; - - // Empty Match - yield return new object[] { engine, null, @"([a*]*)+?$", "ab", RegexOptions.None, new string[] { "", "" } }; - yield return new object[] { engine, null, @"(a*)+?$", "b", RegexOptions.None, new string[] { "", "" } }; + // Decimal + yield return (enUS, @"(cat)\s+(?<2147483646>dog)", "asdlkcat dogiwod", RegexOptions.None, new string[] { "cat dog", "cat", "dog" }); + yield return (enUS, @"(cat)\s+(?<2147483647>dog)", "asdlkcat dogiwod", RegexOptions.None, new string[] { "cat dog", "cat", "dog" }); + + // Hex + yield return (enUS, @"(cat)(\x2a*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" }); + yield return (enUS, @"(cat)(\x2b*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" }); + yield return (enUS, @"(cat)(\x2c*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" }); + yield return (enUS, @"(cat)(\x2d*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" }); + yield return (enUS, @"(cat)(\x2e*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" }); + yield return (enUS, @"(cat)(\x2f*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" }); + + yield return (enUS, @"(cat)(\x2A*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" }); + yield return (enUS, @"(cat)(\x2B*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" }); + yield return (enUS, @"(cat)(\x2C*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" }); + yield return (enUS, @"(cat)(\x2D*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" }); + yield return (enUS, @"(cat)(\x2E*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" }); + yield return (enUS, @"(cat)(\x2F*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" }); + + // ScanControl + yield return (enUS, @"(cat)(\c@*)(dog)", "asdlkcat\0\0dogiwod", RegexOptions.None, new string[] { "cat\0\0dog", "cat", "\0\0", "dog" }); + yield return (enUS, @"(cat)(\cA*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" }); + yield return (enUS, @"(cat)(\ca*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" }); + + yield return (enUS, @"(cat)(\cC*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" }); + yield return (enUS, @"(cat)(\cc*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" }); + + yield return (enUS, @"(cat)(\cD*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" }); + yield return (enUS, @"(cat)(\cd*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" }); + + yield return (enUS, @"(cat)(\cX*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" }); + yield return (enUS, @"(cat)(\cx*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" }); + + yield return (enUS, @"(cat)(\cZ*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" }); + yield return (enUS, @"(cat)(\cz*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" }); + + if (!PlatformDetection.IsNetFramework) // `\c[` was not handled in .NET Framework. See https://github.com/dotnet/runtime/issues/24759. + { + yield return (enUS, @"(cat)(\c[*)(dog)", "asdlkcat\u001bdogiwod", RegexOptions.None, new string[] { "cat\u001bdog", "cat", "\u001b", "dog" }); } - } - public static IEnumerable Groups_CustomCulture_TestData_enUS() - { - foreach (RegexEngine engine in RegexHelpers.AvailableEngines) + // Atomic Zero-Width Assertions \A \G ^ \Z \z \b \B + //\A + yield return (enUS, @"\Acat\s+dog", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"\Acat\s+dog", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"\A(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"\A(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" }); + + //\G + if (!RegexHelpers.IsNonBacktracking(engine)) // contiguous matches nor ECMAScript supported { - yield return new object[] { engine, "en-US", "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } }; - yield return new object[] { engine, "en-US", "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } }; - yield return new object[] { engine, "en-US", "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } }; - yield return new object[] { engine, "en-US", "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } }; - yield return new object[] { engine, "en-US", "\u0130", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } }; - yield return new object[] { engine, "en-US", "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } }; + yield return (enUS, @"\Gcat\s+dog", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"\Gcat\s+dog", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"\G(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"\G(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"\Gcat\s+dog", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"\G(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" }); } - } - public static IEnumerable Groups_CustomCulture_TestData_Czech() - { - foreach (RegexEngine engine in RegexHelpers.AvailableEngines) + //^ + yield return (enUS, @"^cat\s+dog", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"^cat\s+dog", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"mouse\s\n^cat\s+dog", "mouse\n\ncat \n\n\n dog", RegexOptions.Multiline, new string[] { "mouse\n\ncat \n\n\n dog" }); + yield return (enUS, @"^(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"^(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"(mouse)\s\n^(cat)\s+(dog)", "mouse\n\ncat \n\n\n dog", RegexOptions.Multiline, new string[] { "mouse\n\ncat \n\n\n dog", "mouse", "cat", "dog" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported { - yield return new object[] { engine, "cs-CZ", "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } }; - yield return new object[] { engine, "cs-CZ", "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } }; + yield return (enUS, @"^cat\s+dog", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"^(cat)\s+(dog)", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" }); } - } + //\Z + yield return (enUS, @"cat\s+dog\Z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"cat\s+dog\Z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"cat\s+dog\Z", "cat \n\n\n dog\n", RegexOptions.None, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"cat\s+dog\Z", "cat \n\n\n dog\n", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"(cat)\s+(dog)\Z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"(cat)\s+(dog)\Z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"(cat)\s+(dog)\Z", "cat \n\n\n dog\n", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"(cat)\s+(dog)\Z", "cat \n\n\n dog\n", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported + { + yield return (enUS, @"cat\s+dog\Z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"cat\s+dog\Z", "cat \n\n\n dog\n", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"(cat)\s+(dog)\Z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"(cat)\s+(dog)\Z", "cat \n\n\n dog\n", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" }); + } - public static IEnumerable Groups_CustomCulture_TestData_Danish() - { - foreach (RegexEngine engine in RegexHelpers.AvailableEngines) + //\z + yield return (enUS, @"cat\s+dog\z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"cat\s+dog\z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"(cat)\s+(dog)\z", "cat \n\n\n dog", RegexOptions.None, new string[] { "cat \n\n\n dog", "cat", "dog" }); + yield return (enUS, @"(cat)\s+(dog)\z", "cat \n\n\n dog", RegexOptions.Multiline, new string[] { "cat \n\n\n dog", "cat", "dog" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported { - yield return new object[] { engine, "da-DK", "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } }; - yield return new object[] { engine, "da-DK", "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } }; + yield return (enUS, @"cat\s+dog\z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog" }); + yield return (enUS, @"(cat)\s+(dog)\z", "cat \n\n\n dog", RegexOptions.ECMAScript, new string[] { "cat \n\n\n dog", "cat", "dog" }); } - } - public static IEnumerable Groups_CustomCulture_TestData_Turkish() - { - foreach (RegexEngine engine in RegexHelpers.AvailableEngines) + //\b + yield return (enUS, @"\bcat\b", "cat", RegexOptions.None, new string[] { "cat" }); + yield return (enUS, @"\bcat\b", "dog cat mouse", RegexOptions.None, new string[] { "cat" }); + yield return (enUS, @".*\bcat\b", "cat", RegexOptions.None, new string[] { "cat" }); + yield return (enUS, @".*\bcat\b", "dog cat mouse", RegexOptions.None, new string[] { "dog cat" }); + yield return (enUS, @"\b@cat", "123START123@catEND", RegexOptions.None, new string[] { "@cat" }); + yield return (enUS, @"\b\ Groups_CustomCulture_TestData_AzeriLatin() - { - foreach (RegexEngine engine in RegexHelpers.AvailableEngines) + //\B + yield return (enUS, @"\Bcat\B", "dogcatmouse", RegexOptions.None, new string[] { "cat" }); + yield return (enUS, @"dog\Bcat\B", "dogcatmouse", RegexOptions.None, new string[] { "dogcat" }); + yield return (enUS, @".*\Bcat\B", "dogcatmouse", RegexOptions.None, new string[] { "dogcat" }); + yield return (enUS, @"\B@cat", "123START123;@catEND", RegexOptions.None, new string[] { "@cat" }); + yield return (enUS, @"\B\cat)\s+(?dog)\s+\123\s+\234", "asdfcat dog cat23 dog34eia", RegexOptions.ECMAScript, new string[] { "cat dog cat23 dog34", "cat", "dog" }); } - using var _ = new ThreadCultureChange(cultureName); + // Balanced Matching + if (!RegexHelpers.IsNonBacktracking(engine)) // balancing groups not supported + { + yield return (enUS, @"
+ (?> +
(?) | +
(?<-DEPTH>) | + .? + )*? + (?(DEPTH)(?!)) +
", "
this is some
red
text
", RegexOptions.IgnorePatternWhitespace, new string[] { "
this is some
red
text
", "" }); + + yield return (enUS, @"( + ((?'open'<+)[^<>]*)+ + ((?'close-open'>+)[^<>]*)+ + )+", "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", "<02deep_03<03deep_03>>>", "<03deep_03", ">>>", "<", "03deep_03" }); + + yield return (enUS, @"( + (?<)? + [^<>]? + (?>)? + )*", "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", "", "", "01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>" }); + + yield return (enUS, @"( + (?<[^/<>]*>)? + [^<>]? + (?]*>)? + )*", "
Cat", RegexOptions.IgnorePatternWhitespace, new string[] { "Cat", "", "", "Cat" }); + + yield return (enUS, @"( + (?<(?[^/<>]*)>)? + [^<>]? + (?>)? + )*", "catdog", RegexOptions.IgnorePatternWhitespace, new string[] { "catdog", "", "", "a", "dog" }); + + // Balanced Matching With Backtracking + yield return (enUS, @"( + (?<[^/<>]*>)? + .? + (?]*>)? + )* + (?(start)(?!)) ", "Cat<<<>>><><<<>>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "Cat<<<>>><><<<>>>>", "", "", "Cat" }); + } - Regex regex; - try + // Character Classes and Lazy quantifier + if (!RegexHelpers.IsNonBacktracking(engine)) // ECMAScript not supported { - regex = await RegexHelpers.GetRegexAsync(engine, pattern, options, CultureInfo.GetCultureInfo(cultureName)); + yield return (enUS, @"([0-9]+?)([\w]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55", "5", "5" }); + yield return (enUS, @"([0-9]+?)([a-z]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55488a", "55488", "a" }); } - catch (Exception e) when (e is NotSupportedException or ArgumentOutOfRangeException && RegexHelpers.IsNonBacktracking(engine)) + + // Miscellaneous/Regression scenarios + if (!RegexHelpers.IsNonBacktracking(engine)) // lookarounds not supported { - // Some constructs are not supported in NonBacktracking mode, such as: if-then-else, lookaround, and backreferences - return; + yield return (enUS, @"(?1)(?.*?)(?=2)", "1" + Environment.NewLine + "" + Environment.NewLine + "2", RegexOptions.Singleline | RegexOptions.ExplicitCapture, + new string[] { "1" + Environment.NewLine + "" + Environment.NewLine, "1", Environment.NewLine + "" + Environment.NewLine }); + + yield return (enUS, @"\G<%#(?.*?)?%>", @"<%# DataBinder.Eval(this, ""MyNumber"") %>", RegexOptions.Singleline, new string[] { @"<%# DataBinder.Eval(this, ""MyNumber"") %>", @" DataBinder.Eval(this, ""MyNumber"") " }); } - foreach (string prefix in new[] { "", "IGNORETHIS" }) + // Nested Quantifiers + yield return (enUS, @"^[abcd]{0,0x10}*$", "a{0,0x10}}}", RegexOptions.None, new string[] { "a{0,0x10}}}" }); + + // Lazy operator Backtracking + yield return (enUS, @"http://([a-zA-z0-9\-]*\.?)*?(:[0-9]*)??/", "http://www.msn.com/", RegexOptions.IgnoreCase, new string[] { "http://www.msn.com/", "com", string.Empty }); + yield return (enUS, @"http://([a-zA-Z0-9\-]*\.?)*?/", @"http://www.google.com/", RegexOptions.IgnoreCase, new string[] { "http://www.google.com/", "com" }); + + yield return (enUS, @"([a-z]*?)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "c", string.Empty, "c" }); + yield return (enUS, @"^([a-z]*?)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" }); + + // Backtracking + yield return (enUS, @"([a-z]*)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" }); + yield return (enUS, @"^([a-z]*)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" }); + + // Backtracking with multiple (.*) groups -- important ASP.NET scenario + yield return (enUS, @"(.*)/(.*).aspx", "/.aspx", RegexOptions.None, new string[] { "/.aspx", string.Empty, string.Empty }); + yield return (enUS, @"(.*)/(.*).aspx", "/homepage.aspx", RegexOptions.None, new string[] { "/homepage.aspx", string.Empty, "homepage" }); + yield return (enUS, @"(.*)/(.*).aspx", "pages/.aspx", RegexOptions.None, new string[] { "pages/.aspx", "pages", string.Empty }); + yield return (enUS, @"(.*)/(.*).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" }); + yield return (enUS, @"(.*)/(.*).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" }); + yield return (enUS, @"(.*)/(.*).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" }); + yield return (enUS, @"(.*)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" }); + yield return (enUS, @"(.*)/(.*)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" }); + + // Backtracking with multiple (.+) groups + yield return (enUS, @"(.+)/(.+).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" }); + yield return (enUS, @"(.+)/(.+).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" }); + yield return (enUS, @"(.+)/(.+).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" }); + yield return (enUS, @"(.+)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" }); + yield return (enUS, @"(.+)/(.+)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" }); + + // Backtracking with (.+) group followed by (.*) + yield return (enUS, @"(.+)/(.*).aspx", "pages/.aspx", RegexOptions.None, new string[] { "pages/.aspx", "pages", string.Empty }); + yield return (enUS, @"(.+)/(.*).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" }); + yield return (enUS, @"(.+)/(.*).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" }); + yield return (enUS, @"(.+)/(.*).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" }); + yield return (enUS, @"(.+)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" }); + yield return (enUS, @"(.+)/(.*)/(.*).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" }); + + // Backtracking with (.*) group followed by (.+) + yield return (enUS, @"(.*)/(.+).aspx", "/homepage.aspx", RegexOptions.None, new string[] { "/homepage.aspx", string.Empty, "homepage" }); + yield return (enUS, @"(.*)/(.+).aspx", "pages/homepage.aspx", RegexOptions.None, new string[] { "pages/homepage.aspx", "pages", "homepage" }); + yield return (enUS, @"(.*)/(.+).aspx", "/pages/homepage.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx", "/pages", "homepage" }); + yield return (enUS, @"(.*)/(.+).aspx", "/pages/homepage/index.aspx", RegexOptions.None, new string[] { "/pages/homepage/index.aspx", "/pages/homepage", "index" }); + yield return (enUS, @"(.*)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages/homepage.aspx", "index" }); + yield return (enUS, @"(.*)/(.+)/(.+).aspx", "/pages/homepage.aspx/index.aspx", RegexOptions.None, new string[] { "/pages/homepage.aspx/index.aspx", "/pages", "homepage.aspx", "index" }); + + // Captures inside varying constructs with backtracking needing to uncapture + yield return (enUS, @"a(bc)d|abc(e)", "abce", RegexOptions.None, new string[] { "abce", "", "e" }); // alternation + yield return (enUS, @"((ab){2}cd)*", "ababcdababcdababc", RegexOptions.None, new string[] { "ababcdababcd", "ababcd", "ab" }); // loop + if (!RegexHelpers.IsNonBacktracking(engine)) // lookarounds not supported) { - Match match = prefix.Length == 0 ? - regex.Match(input) : // validate the original input - regex.Match(prefix + input, prefix.Length, input.Length); // validate we handle groups and beginning/length correctly + yield return (enUS, @"(ab(?=(\w)\w))*a", "aba", RegexOptions.None, new string[] { "a", "", "" }); // positive lookahead in a loop + yield return (enUS, @"(ab(?=(\w)\w))*a", "ababa", RegexOptions.None, new string[] { "aba", "ab", "a" }); // positive lookahead in a loop + yield return (enUS, @"(ab(?=(\w)\w))*a", "abababa", RegexOptions.None, new string[] { "ababa", "ab", "a" }); // positive lookahead in a loop + yield return (enUS, @"\w\w(?!(\d)\d)", "aa..", RegexOptions.None, new string[] { "aa", "" }); // negative lookahead + yield return (enUS, @"\w\w(?!(\d)\d)", "aa.3", RegexOptions.None, new string[] { "aa", "" }); // negative lookahead + } - Assert.True(match.Success); - Assert.Equal(expectedGroups[0], match.Value); - Assert.Equal(expectedGroups.Length, match.Groups.Count); + // Quantifiers + yield return (enUS, @"a*", "", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"a*", "a", RegexOptions.None, new string[] { "a" }); + yield return (enUS, @"a*", "aa", RegexOptions.None, new string[] { "aa" }); + yield return (enUS, @"a*", "aaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"a*?", "", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"a*?", "a", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"a*?", "aa", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"a+?", "aa", RegexOptions.None, new string[] { "a" }); + yield return (enUS, @"a{1,", "a{1,", RegexOptions.None, new string[] { "a{1," }); + yield return (enUS, @"a{1,3}", "aaaaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"a{1,3}?", "aaaaa", RegexOptions.None, new string[] { "a" }); + yield return (enUS, @"a{2,2}", "aaaaa", RegexOptions.None, new string[] { "aa" }); + yield return (enUS, @"a{2,2}?", "aaaaa", RegexOptions.None, new string[] { "aa" }); + yield return (enUS, @".{1,3}", "bb\nba", RegexOptions.None, new string[] { "bb" }); + yield return (enUS, @".{1,3}?", "bb\nba", RegexOptions.None, new string[] { "b" }); + yield return (enUS, @".{2,2}", "bbb\nba", RegexOptions.None, new string[] { "bb" }); + yield return (enUS, @".{2,2}?", "bbb\nba", RegexOptions.None, new string[] { "bb" }); + yield return (enUS, @"[abc]{1,3}", "ccaba", RegexOptions.None, new string[] { "cca" }); + yield return (enUS, @"[abc]{1,3}?", "ccaba", RegexOptions.None, new string[] { "c" }); + yield return (enUS, @"[abc]{2,2}", "ccaba", RegexOptions.None, new string[] { "cc" }); + yield return (enUS, @"[abc]{2,2}?", "ccaba", RegexOptions.None, new string[] { "cc" }); + yield return (enUS, @"(?:[abc]def){1,3}xyz", "cdefxyz", RegexOptions.None, new string[] { "cdefxyz" }); + yield return (enUS, @"(?:[abc]def){1,3}xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "adefbdefcdefxyz" }); + yield return (enUS, @"(?:[abc]def){1,3}?xyz", "cdefxyz", RegexOptions.None, new string[] { "cdefxyz" }); + yield return (enUS, @"(?:[abc]def){1,3}?xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "adefbdefcdefxyz" }); + yield return (enUS, @"(?:[abc]def){2,2}xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "bdefcdefxyz" }); + yield return (enUS, @"(?:[abc]def){2,2}?xyz", "adefbdefcdefxyz", RegexOptions.None, new string[] { "bdefcdefxyz" }); + foreach (string prefix in new[] { "", "xyz" }) + { + yield return (enUS, prefix + @"(?:[abc]def){1,3}", prefix + "cdef", RegexOptions.None, new string[] { prefix + "cdef" }); + yield return (enUS, prefix + @"(?:[abc]def){1,3}", prefix + "cdefadefbdef", RegexOptions.None, new string[] { prefix + "cdefadefbdef" }); + yield return (enUS, prefix + @"(?:[abc]def){1,3}", prefix + "cdefadefbdefadef", RegexOptions.None, new string[] { prefix + "cdefadefbdef" }); + yield return (enUS, prefix + @"(?:[abc]def){1,3}?", prefix + "cdef", RegexOptions.None, new string[] { prefix + "cdef" }); + yield return (enUS, prefix + @"(?:[abc]def){1,3}?", prefix + "cdefadefbdef", RegexOptions.None, new string[] { prefix + "cdef" }); + yield return (enUS, prefix + @"(?:[abc]def){2,2}", prefix + "cdefadefbdefadef", RegexOptions.None, new string[] { prefix + "cdefadef" }); + yield return (enUS, prefix + @"(?:[abc]def){2,2}?", prefix + "cdefadefbdefadef", RegexOptions.None, new string[] { prefix + "cdefadef" }); + } + yield return (enUS, @"(cat){", "cat{", RegexOptions.None, new string[] { "cat{", "cat" }); + yield return (enUS, @"(cat){}", "cat{}", RegexOptions.None, new string[] { "cat{}", "cat" }); + yield return (enUS, @"(cat){,", "cat{,", RegexOptions.None, new string[] { "cat{,", "cat" }); + yield return (enUS, @"(cat){,}", "cat{,}", RegexOptions.None, new string[] { "cat{,}", "cat" }); + yield return (enUS, @"(cat){cat}", "cat{cat}", RegexOptions.None, new string[] { "cat{cat}", "cat" }); + yield return (enUS, @"(cat){cat,5}", "cat{cat,5}", RegexOptions.None, new string[] { "cat{cat,5}", "cat" }); + yield return (enUS, @"(cat){5,dog}", "cat{5,dog}", RegexOptions.None, new string[] { "cat{5,dog}", "cat" }); + yield return (enUS, @"(cat){cat,dog}", "cat{cat,dog}", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" }); + yield return (enUS, @"(cat){,}?", "cat{,}?", RegexOptions.None, new string[] { "cat{,}", "cat" }); + yield return (enUS, @"(cat){cat}?", "cat{cat}?", RegexOptions.None, new string[] { "cat{cat}", "cat" }); + yield return (enUS, @"(cat){cat,5}?", "cat{cat,5}?", RegexOptions.None, new string[] { "cat{cat,5}", "cat" }); + yield return (enUS, @"(cat){5,dog}?", "cat{5,dog}?", RegexOptions.None, new string[] { "cat{5,dog}", "cat" }); + yield return (enUS, @"(cat){cat,dog}?", "cat{cat,dog}?", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" }); + + // Atomic subexpressions + // Implicitly upgrading (or not) oneloop to be atomic + yield return (enUS, @"a*b", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*b+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*b+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*[^a]", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*[^a]+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*[^a]+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*bcd", "aaabcd", RegexOptions.None, new string[] { "aaabcd" }); + yield return (enUS, @"a*[bcd]", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*[bcd]+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*[bcd]+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*[bcd]{1,3}", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*([bcd]ab|[bef]cd){1,3}", "aaababecdcac", RegexOptions.ExplicitCapture, new string[] { "aaababecd" }); + yield return (enUS, @"a*([bcd]|[aef]){1,3}", "befb", RegexOptions.ExplicitCapture, new string[] { "bef" }); // can't upgrade + yield return (enUS, @"a*$", "aaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"a*$", "aaa", RegexOptions.Multiline, new string[] { "aaa" }); + yield return (enUS, @"a*\b", "aaa bbb", RegexOptions.None, new string[] { "aaa" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // atomic nor ECMAScript supported + { + yield return (enUS, @"a*(?>b+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*(?>[^a]+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*(?>[bcd]+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"a*\b", "aaa bbb", RegexOptions.ECMAScript, new string[] { "aaa" }); + yield return (enUS, @"@*\B", "@@@", RegexOptions.ECMAScript, new string[] { "@@@" }); + } + yield return (enUS, @"@*\B", "@@@", RegexOptions.None, new string[] { "@@@" }); + yield return (enUS, @"(?:abcd*|efgh)i", "efghi", RegexOptions.None, new string[] { "efghi" }); + yield return (enUS, @"(?:abcd|efgh*)i", "efgi", RegexOptions.None, new string[] { "efgi" }); + yield return (enUS, @"(?:abcd|efghj{2,}|j[klm]o+)i", "efghjjjjji", RegexOptions.None, new string[] { "efghjjjjji" }); + yield return (enUS, @"(?:abcd|efghi{2,}|j[klm]o+)i", "efghiii", RegexOptions.None, new string[] { "efghiii" }); + yield return (enUS, @"(?:abcd|efghi{2,}|j[klm]o+)i", "efghiiiiiiii", RegexOptions.None, new string[] { "efghiiiiiiii" }); + yield return (enUS, @"a?ba?ba?ba?b", "abbabab", RegexOptions.None, new string[] { "abbabab" }); + yield return (enUS, @"a?ba?ba?ba?b", "abBAbab", RegexOptions.IgnoreCase, new string[] { "abBAbab" }); + // Implicitly upgrading (or not) notoneloop to be atomic + yield return (enUS, @"[^b]*b", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[^b]*b+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[^b]*b+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // atomic not supported + { + yield return (enUS, @"[^b]*(?>b+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + } + yield return (enUS, @"[^b]*bac", "aaabac", RegexOptions.None, new string[] { "aaabac" }); + yield return (enUS, @"[^b]*", "aaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"(?:abc[^b]*|efgh)i", "efghi", RegexOptions.None, new string[] { "efghi" }); // can't upgrade + yield return (enUS, @"(?:abcd|efg[^b]*)b", "efgb", RegexOptions.None, new string[] { "efgb" }); + yield return (enUS, @"(?:abcd|efg[^b]*)i", "efgi", RegexOptions.None, new string[] { "efgi" }); // can't upgrade + yield return (enUS, @"[^a]?a[^a]?a[^a]?a[^a]?a", "baababa", RegexOptions.None, new string[] { "baababa" }); + yield return (enUS, @"[^a]?a[^a]?a[^a]?a[^a]?a", "BAababa", RegexOptions.IgnoreCase, new string[] { "BAababa" }); + // Implicitly upgrading (or not) setloop to be atomic + yield return (enUS, @"[ac]*", "aaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"[ac]*b", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*b+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*b+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*[^a]", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*[^a]+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*[^a]+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*bcd", "aaabcd", RegexOptions.None, new string[] { "aaabcd" }); + yield return (enUS, @"[ac]*[bd]", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*[bd]+", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*[bd]+?", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*[bd]{1,3}", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*$", "aaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"[ac]*$", "aaa", RegexOptions.Multiline, new string[] { "aaa" }); + yield return (enUS, @"[ac]*\b", "aaa bbb", RegexOptions.None, new string[] { "aaa" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // atomic nor ECMAScript are supported + { + yield return (enUS, @"[ac]*(?>b+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*(?>[^a]+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*(?>[bd]+)", "aaab", RegexOptions.None, new string[] { "aaab" }); + yield return (enUS, @"[ac]*\b", "aaa bbb", RegexOptions.ECMAScript, new string[] { "aaa" }); + yield return (enUS, @"[@']*\B", "@@@", RegexOptions.ECMAScript, new string[] { "@@@" }); + } + yield return (enUS, @"[@']*\B", "@@@", RegexOptions.None, new string[] { "@@@" }); + yield return (enUS, @".*.", "@@@", RegexOptions.Singleline, new string[] { "@@@" }); + yield return (enUS, @"(?:abcd|efg[hij]*)h", "efgh", RegexOptions.None, new string[] { "efgh" }); // can't upgrade + yield return (enUS, @"(?:abcd|efg[hij]*)ih", "efgjih", RegexOptions.None, new string[] { "efgjih" }); // can't upgrade + yield return (enUS, @"(?:abcd|efg[hij]*)k", "efgjk", RegexOptions.None, new string[] { "efgjk" }); + yield return (enUS, @"[ace]?b[ace]?b[ace]?b[ace]?b", "cbbabeb", RegexOptions.None, new string[] { "cbbabeb" }); + yield return (enUS, @"[ace]?b[ace]?b[ace]?b[ace]?b", "cBbAbEb", RegexOptions.IgnoreCase, new string[] { "cBbAbEb" }); + yield return (enUS, @"a[^wz]*w", "abcdcdcdwz", RegexOptions.None, new string[] { "abcdcdcdw" }); + yield return (enUS, @"a[^wyz]*w", "abcdcdcdwz", RegexOptions.None, new string[] { "abcdcdcdw" }); + yield return (enUS, @"a[^wyz]*W", "abcdcdcdWz", RegexOptions.IgnoreCase, new string[] { "abcdcdcdW" }); + // Implicitly upgrading (or not) concat loops to be atomic + yield return (enUS, @"(?:[ab]c[de]f)*", "", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?:[ab]c[de]f)*", "acdf", RegexOptions.None, new string[] { "acdf" }); + yield return (enUS, @"(?:[ab]c[de]f)*", "acdfbcef", RegexOptions.None, new string[] { "acdfbcef" }); + yield return (enUS, @"(?:[ab]c[de]f)*", "cdfbcef", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?:[ab]c[de]f)+", "cdfbcef", RegexOptions.None, new string[] { "bcef" }); + yield return (enUS, @"(?:[ab]c[de]f)*", "bcefbcdfacfe", RegexOptions.None, new string[] { "bcefbcdf" }); + // Implicitly upgrading (or not) nested loops to be atomic + yield return (enUS, @"(?:a){3}", "aaaaaaaaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"(?:a){3}?", "aaaaaaaaa", RegexOptions.None, new string[] { "aaa" }); + yield return (enUS, @"(?:a{2}){3}", "aaaaaaaaa", RegexOptions.None, new string[] { "aaaaaa" }); + yield return (enUS, @"(?:a{2}?){3}?", "aaaaaaaaa", RegexOptions.None, new string[] { "aaaaaa" }); + yield return (enUS, @"(?:(?:[ab]c[de]f){3}){2}", "acdfbcdfacefbcefbcefbcdfacdef", RegexOptions.None, new string[] { "acdfbcdfacefbcefbcefbcdf" }); + yield return (enUS, @"(?:(?:[ab]c[de]f){3}hello){2}", "aaaaaacdfbcdfacefhellobcefbcefbcdfhellooooo", RegexOptions.None, new string[] { "acdfbcdfacefhellobcefbcefbcdfhello" }); + yield return (enUS, @"CN=(.*[^,]+).*", "CN=localhost", RegexOptions.Singleline, new string[] { "CN=localhost", "localhost" }); + // Nested atomic + if (!RegexHelpers.IsNonBacktracking(engine)) // atomic not supported + { + yield return (enUS, @"(?>abc[def]gh(i*))", "123abceghiii456", RegexOptions.None, new string[] { "abceghiii", "iii" }); + yield return (enUS, @"(?>(?:abc)*)", "abcabcabc", RegexOptions.None, new string[] { "abcabcabc" }); + } - int[] groupNumbers = regex.GetGroupNumbers(); - string[] groupNames = regex.GetGroupNames(); - for (int i = 0; i < expectedGroups.Length; i++) - { - Assert.Equal(expectedGroups[i], match.Groups[groupNumbers[i]].Value); - Assert.Equal(match.Groups[groupNumbers[i]], match.Groups[groupNames[i]]); + // Anchoring loops beginning with .* / .+ + yield return (enUS, @".*", "", RegexOptions.None, new string[] { "" }); + yield return (enUS, @".*", "\n\n\n\n", RegexOptions.None, new string[] { "" }); + yield return (enUS, @".*", "\n\n\n\n", RegexOptions.Singleline, new string[] { "\n\n\n\n" }); + yield return (enUS, @".*[1a]", "\n\n\n\n1", RegexOptions.None, new string[] { "1" }); + yield return (enUS, @"(?s).*(?-s)[1a]", "1\n\n\n\n", RegexOptions.None, new string[] { "1" }); + yield return (enUS, @"(?s).*(?-s)[1a]", "\n\n\n\n1", RegexOptions.None, new string[] { "\n\n\n\n1" }); + yield return (enUS, @".*|.*|.*", "", RegexOptions.None, new string[] { "" }); + yield return (enUS, @".*123|abc", "abc\n123", RegexOptions.None, new string[] { "abc" }); + yield return (enUS, @".*123|abc", "abc\n123", RegexOptions.Singleline, new string[] { "abc\n123" }); + yield return (enUS, @"abc|.*123", "abc\n123", RegexOptions.Singleline, new string[] { "abc" }); + yield return (enUS, @".*", "\n", RegexOptions.None, new string[] { "" }); + yield return (enUS, @".*\n", "\n", RegexOptions.None, new string[] { "\n" }); + yield return (enUS, @".*", "\n", RegexOptions.Singleline, new string[] { "\n" }); + yield return (enUS, @".*\n", "\n", RegexOptions.Singleline, new string[] { "\n" }); + yield return (enUS, @".*", "abc", RegexOptions.None, new string[] { "abc" }); + yield return (enUS, @".*abc", "abc", RegexOptions.None, new string[] { "abc" }); + yield return (enUS, @".*abc|ghi", "ghi", RegexOptions.None, new string[] { "ghi" }); + yield return (enUS, @".*abc|.*ghi", "abcghi", RegexOptions.None, new string[] { "abc" }); + yield return (enUS, @".*ghi|.*abc", "abcghi", RegexOptions.None, new string[] { "abcghi" }); + yield return (enUS, @".*abc|.*ghi", "bcghi", RegexOptions.None, new string[] { "bcghi" }); + yield return (enUS, @".*abc|.+c", " \n \n bc", RegexOptions.None, new string[] { " bc" }); + yield return (enUS, @".*abc", "12345 abc", RegexOptions.None, new string[] { "12345 abc" }); + yield return (enUS, @".*abc", "12345\n abc", RegexOptions.None, new string[] { " abc" }); + yield return (enUS, @".*abc", "12345\n abc", RegexOptions.Singleline, new string[] { "12345\n abc" }); + yield return (enUS, @".*\nabc", "\n123\nabc", RegexOptions.None, new string[] { "123\nabc" }); + yield return (enUS, @".*\nabc", "\n123\nabc", RegexOptions.Singleline, new string[] { "\n123\nabc" }); + yield return (enUS, @".*abc", "abc abc abc \nabc", RegexOptions.None, new string[] { "abc abc abc" }); + yield return (enUS, @".*abc", "abc abc abc \nabc", RegexOptions.Singleline, new string[] { "abc abc abc \nabc" }); + yield return (enUS, @".*?abc", "abc abc abc \nabc", RegexOptions.None, new string[] { "abc" }); + yield return (enUS, @"[^\n]*abc", "123abc\n456abc\n789abc", RegexOptions.None, new string[] { "123abc" }); + yield return (enUS, @"[^\n]*abc", "123abc\n456abc\n789abc", RegexOptions.Singleline, new string[] { "123abc" }); + yield return (enUS, @"[^\n]*abc", "123ab\n456abc\n789abc", RegexOptions.None, new string[] { "456abc" }); + yield return (enUS, @"[^\n]*abc", "123ab\n456abc\n789abc", RegexOptions.Singleline, new string[] { "456abc" }); + yield return (enUS, @".+", "a", RegexOptions.None, new string[] { "a" }); + yield return (enUS, @".+", "\nabc", RegexOptions.None, new string[] { "abc" }); + yield return (enUS, @".+", "\n", RegexOptions.Singleline, new string[] { "\n" }); + yield return (enUS, @".+", "\nabc", RegexOptions.Singleline, new string[] { "\nabc" }); + yield return (enUS, @".+abc", "aaaabc", RegexOptions.None, new string[] { "aaaabc" }); + yield return (enUS, @".+abc", "12345 abc", RegexOptions.None, new string[] { "12345 abc" }); + yield return (enUS, @".+abc", "12345\n abc", RegexOptions.None, new string[] { " abc" }); + yield return (enUS, @".+abc", "12345\n abc", RegexOptions.Singleline, new string[] { "12345\n abc" }); + if (!RegexHelpers.IsNonBacktracking(engine)) // backreferences not supported + { + yield return (enUS, @"(.*)abc\1", "\n12345abc12345", RegexOptions.Singleline, new string[] { "12345abc12345", "12345" }); + yield return (enUS, @"(.+)abc\1", "\n12345abc12345", RegexOptions.Singleline, new string[] { "12345abc12345", "12345" }); + } + + // Unanchored .* + yield return (enUS, @"\A\s*(?\w+)(\s*\((?.*)\))?\s*\Z", "Match(Name)", RegexOptions.None, new string[] { "Match(Name)", "(Name)", "Match", "Name" }); + yield return (enUS, @"\A\s*(?\w+)(\s*\((?.*)\))?\s*\Z", "Match(Na\nme)", RegexOptions.Singleline, new string[] { "Match(Na\nme)", "(Na\nme)", "Match", "Na\nme" }); + foreach (RegexOptions options in new[] { RegexOptions.None, RegexOptions.Singleline }) + { + yield return (enUS, @"abcd.*", @"abcabcd", options, new string[] { "abcd" }); + yield return (enUS, @"abcd.*", @"abcabcde", options, new string[] { "abcde" }); + yield return (enUS, @"abcd.*", @"abcabcdefg", options, new string[] { "abcdefg" }); + yield return (enUS, @"abcd(.*)", @"ababcd", options, new string[] { "abcd", "" }); + yield return (enUS, @"abcd(.*)", @"aabcde", options, new string[] { "abcde", "e" }); + yield return (enUS, @"abcd(.*)", @"abcabcdefg", options, new string[] { "abcdefg", "efg" }); + yield return (enUS, @"abcd(.*)e", @"abcabcdefg", options, new string[] { "abcde", "" }); + yield return (enUS, @"abcd(.*)f", @"abcabcdefg", options, new string[] { "abcdef", "e" }); + } + + // Grouping Constructs + yield return (enUS, @"()", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty }); + yield return (enUS, @"(?)", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty }); + yield return (enUS, @"(?'cat')", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty }); + yield return (enUS, @"(?:)", "cat", RegexOptions.None, new string[] { string.Empty }); + yield return (enUS, @"(?imn)", "cat", RegexOptions.None, new string[] { string.Empty }); + yield return (enUS, @"(?imn)cat", "(?imn)cat", RegexOptions.None, new string[] { "cat" }); + yield return (enUS, @"(?=)", "cat", RegexOptions.None, new string[] { string.Empty }); + yield return (enUS, @"(?<=)", "cat", RegexOptions.None, new string[] { string.Empty }); + if (!RegexHelpers.IsNonBacktracking(engine)) // atomic not supported + { + yield return (enUS, @"(?>)", "cat", RegexOptions.None, new string[] { string.Empty }); + } + + // Alternation construct + if (!RegexHelpers.IsNonBacktracking(engine)) // conditionals not supported + { + yield return (enUS, @"(?()|)", "(?()|)", RegexOptions.None, new string[] { "" }); - Assert.Equal(groupNumbers[i], regex.GroupNumberFromName(groupNames[i])); - Assert.Equal(groupNames[i], regex.GroupNameFromNumber(groupNumbers[i])); + yield return (enUS, @"(?(cat)|)", "cat", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?(cat)|)", "dog", RegexOptions.None, new string[] { "" }); + + yield return (enUS, @"(?(cat)catdog|)", "catdog", RegexOptions.None, new string[] { "catdog" }); + yield return (enUS, @"(?(cat)cat\w\w\w)*", "catdogcathog", RegexOptions.None, new string[] { "catdogcathog" }); + yield return (enUS, @"(?(?=cat)cat\w\w\w)*", "catdogcathog", RegexOptions.None, new string[] { "catdogcathog" }); + yield return (enUS, @"(?(cat)catdog|)", "dog", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?(cat)dog|)", "dog", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?(cat)dog|)", "cat", RegexOptions.None, new string[] { "" }); + + yield return (enUS, @"(?(cat)|catdog)", "cat", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?(cat)|catdog)", "catdog", RegexOptions.None, new string[] { "" }); + yield return (enUS, @"(?(cat)|dog)", "dog", RegexOptions.None, new string[] { "dog" }); + + yield return (enUS, @"(?((\w{3}))\1\1|no)", "dogdogdog", RegexOptions.None, new string[] { "dogdog", "dog" }); + yield return (enUS, @"(?((\w{3}))\1\1|no)", "no", RegexOptions.None, new string[] { "no", "" }); + } + + // Invalid unicode + yield return (enUS, "([\u0000-\uFFFF-[azAZ09]]|[\u0000-\uFFFF-[^azAZ09]])+", "azAZBCDE1234567890BCDEFAZza", RegexOptions.None, new string[] { "azAZBCDE1234567890BCDEFAZza", "a" }); + yield return (enUS, "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]+", "abcxyzABCXYZ123890", RegexOptions.None, new string[] { "bcxyzABCXYZ123890" }); + yield return (enUS, "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]]+", "bcxyzABCXYZ123890a", RegexOptions.None, new string[] { "a" }); + yield return (enUS, "[\u0000-\uFFFF-[\\p{P}\\p{S}\\p{C}]]+", "!@`';.,$+<>=\x0001\x001FazAZ09", RegexOptions.None, new string[] { "azAZ09" }); + + yield return (enUS, @"[\uFFFD-\uFFFF]+", "\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFD\uFFFE\uFFFF" }); + yield return (enUS, @"[\uFFFC-\uFFFE]+", "\uFFFB\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFC\uFFFD\uFFFE" }); + + // Empty Match + yield return (enUS, @"([a*]*)+?$", "ab", RegexOptions.None, new string[] { "", "" }); + yield return (enUS, @"(a*)+?$", "b", RegexOptions.None, new string[] { "", "" }); + + // en-US + yield return (enUS, "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" }); + yield return (enUS, "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" }); + yield return (enUS, "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" }); + yield return (enUS, "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" }); + yield return (enUS, "\u0130", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" }); + yield return (enUS, "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" }); + + // cs-CZ + yield return (csCZ, "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" }); + yield return (csCZ, "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" }); + + // da-DK + yield return (daDK, "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" }); + yield return (daDK, "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" }); + + // tr-TR + yield return (trTR, "\u0131", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" }); + yield return (trTR, "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" }); + + // az-Latn-AZ + if (PlatformDetection.IsNotBrowser) + { + yield return (azLatnAZ, "\u0131", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" }); + yield return (azLatnAZ, "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" }); + } + } + + [Theory] + [MemberData(nameof(Groups_MemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56407", TestPlatforms.Android)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/36900", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + public void Groups(Regex regex, CultureInfo culture, string input, string[] expectedGroups) + { + using (new ThreadCultureChange(culture)) + { + foreach (string prefix in new[] { "", "IGNORETHIS" }) + { + Match match = prefix.Length == 0 ? + regex.Match(input) : // validate the original input + regex.Match(prefix + input, prefix.Length, input.Length); // validate we handle groups and beginning/length correctly + + Assert.True(match.Success); + Assert.Equal(expectedGroups[0], match.Value); + Assert.Equal(expectedGroups.Length, match.Groups.Count); + + int[] groupNumbers = regex.GetGroupNumbers(); + string[] groupNames = regex.GetGroupNames(); + for (int i = 0; i < expectedGroups.Length; i++) + { + Assert.Equal(expectedGroups[i], match.Groups[groupNumbers[i]].Value); + Assert.Equal(match.Groups[groupNumbers[i]], match.Groups[groupNames[i]]); + + Assert.Equal(groupNumbers[i], regex.GroupNumberFromName(groupNames[i])); + Assert.Equal(groupNames[i], regex.GroupNameFromNumber(groupNumbers[i])); + } } } } diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Tests.Common.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Tests.Common.cs index 97a683e1708d0..8af83b14c4645 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Tests.Common.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Tests.Common.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; +using System.Tests; using System.Threading.Tasks; using Xunit; @@ -96,16 +97,13 @@ public static IEnumerable AvailableEngines PlatformDetection.IsNotBrowser) { yield return RegexEngine.SourceGenerated; - - // TODO-NONBACKTRACKING: - // yield return RegexEngine.NonBacktrackingSourceGenerated; } } } } public static bool IsNonBacktracking(RegexEngine engine) => - engine is RegexEngine.NonBacktracking or RegexEngine.NonBacktrackingSourceGenerated; + engine is RegexEngine.NonBacktracking; public static async Task GetRegexAsync(RegexEngine engine, [StringSyntax(StringSyntaxAttribute.Regex)] string pattern, RegexOptions? options = null, TimeSpan? matchTimeout = null) { @@ -119,9 +117,6 @@ public static async Task GetRegexAsync(RegexEngine engine, [StringSyntax( return await RegexGeneratorHelper.SourceGenRegexAsync(pattern, null, options, matchTimeout); } - // TODO-NONBACKTRACKING - // - Handle NonBacktrackingSourceGenerated - return options is null ? new Regex(pattern, OptionsFromEngine(engine)) : matchTimeout is null ? new Regex(pattern, options.Value | OptionsFromEngine(engine)) : @@ -135,17 +130,29 @@ public static async Task GetRegexesAsync(RegexEngine engine, params (st return await RegexGeneratorHelper.SourceGenRegexAsync(regexes); } - // TODO-NONBACKTRACKING - // - Handle NonBacktrackingSourceGenerated - var results = new Regex[regexes.Length]; for (int i = 0; i < regexes.Length; i++) { (string pattern, CultureInfo? culture, RegexOptions? options, TimeSpan? matchTimeout) = regexes[i]; - results[i] = - options is null ? new Regex(pattern, OptionsFromEngine(engine)) : - matchTimeout is null ? new Regex(pattern, options.Value | OptionsFromEngine(engine)) : - new Regex(pattern, options.Value | OptionsFromEngine(engine), matchTimeout.Value); + + using (new ThreadCultureChange(culture)) + { + try + { + results[i] = + options is null ? new Regex(pattern, OptionsFromEngine(engine)) : + matchTimeout is null ? new Regex(pattern, options.Value | OptionsFromEngine(engine)) : + new Regex(pattern, options.Value | OptionsFromEngine(engine), matchTimeout.Value); + } + catch (ArgumentOutOfRangeException aoore) + { + throw new ArgumentOutOfRangeException($"{engine}, {pattern}, {options}", aoore); + } + catch (NotSupportedException nse) + { + throw new NotSupportedException($"{engine}, {pattern}, {options}", nse); + } + } } return results; @@ -157,7 +164,6 @@ public static async Task GetRegexesAsync(RegexEngine engine, params (st RegexEngine.Compiled => RegexOptions.Compiled, RegexEngine.SourceGenerated => RegexOptions.Compiled, RegexEngine.NonBacktracking => RegexOptionNonBacktracking, - RegexEngine.NonBacktrackingSourceGenerated => RegexOptionNonBacktracking | RegexOptions.Compiled, _ => throw new ArgumentException($"Unknown engine: {engine}"), }; @@ -184,7 +190,6 @@ public enum RegexEngine Compiled, NonBacktracking, SourceGenerated, - NonBacktrackingSourceGenerated, } public class CaptureData diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs index e3b1c95eeb253..ab7602ccb78bf 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCharacterSetTests.cs @@ -508,7 +508,9 @@ public DerivedRunner(string text) public new bool IsBoundary(int index, int startpos, int endpos) => base.IsBoundary(index, startpos, endpos); +#pragma warning disable SYSLIB0052 // Type or member is obsolete public static new bool CharInSet(char ch, string set, string category) => RegexRunner.CharInSet(ch, set, category); +#pragma warning restore SYSLIB0052 // Type or member is obsolete protected override bool FindFirstChar() => throw new NotImplementedException(); protected override void Go() => throw new NotImplementedException(); diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexRustTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexRustTests.cs index 102e1e9679541..1a6a807a57cfb 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexRustTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexRustTests.cs @@ -1,13 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; -using System.IO; +using System.Globalization; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; using Xunit; namespace System.Text.RegularExpressions.Tests @@ -17,408 +13,417 @@ namespace System.Text.RegularExpressions.Tests ///
public class RegexRustTests { - public static IEnumerable MatchStartAndEndPositionsTestData() + public static IEnumerable MatchStartAndEndPositions_MemberData() { foreach (RegexEngine engine in RegexHelpers.AvailableEngines) { - yield return new object[] { engine, @"^$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^$^$^$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^^^$$$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"$^", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"(?:^$)*", "a\nb\nc", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) } }; - yield return new object[] { engine, @"(?:$^)*", "a\nb\nc", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) } }; - yield return new object[] { engine, @"", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()*", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()+", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()?", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()()", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()+|z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"z|()+", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"()+|b", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"b|()+", "abc", new[] { (0, 0), (1, 2), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"|b", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"b|", "abc", new[] { (0, 0), (1, 2), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"|z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"z|", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"|", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"||", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"||z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"(?:)|b", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"b|(?:)", "abc", new[] { (0, 0), (1, 2), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"(?:|)", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"(?:|)|z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"a(?:)|b", "abc", new[] { (0, 1), (1, 2) } }; - yield return new object[] { engine, @"a$", "a", new[] { (0, 1) } }; - yield return new object[] { engine, @"(?m)^[a-z]+$", "abc\ndef\nxyz", new[] { (0, 3), (4, 7), (8, 11) } }; - yield return new object[] { engine, @"(?m)^$", "abc\ndef\nxyz", new ValueTuple[] { } }; - yield return new object[] { engine, @"(?m)^", "abc\ndef\nxyz", new[] { (0, 0), (4, 4), (8, 8) } }; - yield return new object[] { engine, @"(?m)$", "abc\ndef\nxyz", new[] { (3, 3), (7, 7), (11, 11) } }; - yield return new object[] { engine, @"(?m)^[a-z]", "abc\ndef\nxyz", new[] { (0, 1), (4, 5), (8, 9) } }; - yield return new object[] { engine, @"(?m)[a-z]^", "abc\ndef\nxyz", new ValueTuple[] { } }; - yield return new object[] { engine, @"(?m)[a-z]$", "abc\ndef\nxyz", new[] { (2, 3), (6, 7), (10, 11) } }; - yield return new object[] { engine, @"(?m)$[a-z]", "abc\ndef\nxyz", new ValueTuple[] { } }; - yield return new object[] { engine, @"(?m)^$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"(?m)(?:^$)*", "a\nb\nc", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) } }; - yield return new object[] { engine, @"(?m)(?:^|a)+", "a\naaa\n", new[] { (0, 0), (2, 2), (3, 5), (6, 6) } }; - yield return new object[] { engine, @"(?m)(?:^|a)*", "a\naaa\n", new[] { (0, 0), (1, 1), (2, 2), (3, 5), (5, 5), (6, 6) } }; - yield return new object[] { engine, @"(?m)(?:^[a-z])+", "abc\ndef\nxyz", new[] { (0, 1), (4, 5), (8, 9) } }; - yield return new object[] { engine, @"(?m)(?:^[a-z]{3}\n?)+", "abc\ndef\nxyz", new[] { (0, 11) } }; - yield return new object[] { engine, @"(?m)(?:^[a-z]{3}\n?)*", "abc\ndef\nxyz", new[] { (0, 11), (11, 11) } }; - yield return new object[] { engine, @"(?m)(?:\n?[a-z]{3}$)+", "abc\ndef\nxyz", new[] { (0, 11) } }; - yield return new object[] { engine, @"(?m)(?:\n?[a-z]{3}$)*", "abc\ndef\nxyz", new[] { (0, 11), (11, 11) } }; - yield return new object[] { engine, @"(?m)^*", "\naa\n", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4) } }; - yield return new object[] { engine, @"(?m)^+", "\naa\n", new[] { (0, 0), (1, 1), (4, 4) } }; - yield return new object[] { engine, @"(?m)$*", "\naa\n", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4) } }; - yield return new object[] { engine, @"(?m)$+", "\naa\n", new[] { (0, 0), (3, 3), (4, 4) } }; - yield return new object[] { engine, @"(?m)(?:$\n)+", "\n\naaa\n\n", new[] { (0, 2), (5, 7) } }; - yield return new object[] { engine, @"(?m)(?:$\n)*", "\n\naaa\n\n", new[] { (0, 2), (2, 2), (3, 3), (4, 4), (5, 7), (7, 7) } }; - yield return new object[] { engine, @"(?m)(?:$\n^)+", "\n\naaa\n\n", new[] { (0, 2), (5, 7) } }; - yield return new object[] { engine, @"(?m)(?:^|$)+", "\n\naaa\n\n", new[] { (0, 0), (1, 1), (2, 2), (5, 5), (6, 6), (7, 7) } }; - yield return new object[] { engine, @"\b", "a b c", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) } }; - yield return new object[] { engine, @"^a|b", "ba", new[] { (0, 1) } }; - yield return new object[] { engine, @"[0-9][0-9][0-9]000", "153.230000\n", new[] { (4, 10) } }; - yield return new object[] { engine, @"((?i)foo)|Bar", "foo Foo bar Bar", new[] { (0, 3), (4, 7), (12, 15) } }; - yield return new object[] { engine, @"()?01", "z?01", new[] { (2, 4) } }; - yield return new object[] { engine, @"\b", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"\b", "a", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"\b", "ab", new[] { (0, 0), (2, 2) } }; - yield return new object[] { engine, @"^\b", "ab", new[] { (0, 0) } }; - yield return new object[] { engine, @"\b$", "ab", new[] { (2, 2) } }; - yield return new object[] { engine, @"^\b$", "ab", new ValueTuple[] { } }; - yield return new object[] { engine, @"\bbar\b", "nobar bar foo bar", new[] { (6, 9), (14, 17) } }; - yield return new object[] { engine, @"a\b", "faoa x", new[] { (3, 4) } }; - yield return new object[] { engine, @"\bbar", "bar x", new[] { (0, 3) } }; - yield return new object[] { engine, @"\bbar", "foo\nbar x", new[] { (4, 7) } }; - yield return new object[] { engine, @"bar\b", "foobar", new[] { (3, 6) } }; - yield return new object[] { engine, @"bar\b", "foobar\nxxx", new[] { (3, 6) } }; - yield return new object[] { engine, @"(foo|bar|[A-Z])\b", "foo", new[] { (0, 3) } }; - yield return new object[] { engine, @"(foo|bar|[A-Z])\b", "foo\n", new[] { (0, 3) } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])", "foo", new[] { (0, 3) } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])\b", "X", new[] { (0, 1) } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])\b", "XY", new ValueTuple[] { } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])\b", "bar", new[] { (0, 3) } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])\b", "foo", new[] { (0, 3) } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])\b", "foo\n", new[] { (0, 3) } }; - yield return new object[] { engine, @"\b(foo|bar|[A-Z])\b", "ffoo bbar N x", new[] { (10, 11) } }; - yield return new object[] { engine, @"\b(fo|foo)\b", "fo", new[] { (0, 2) } }; - yield return new object[] { engine, @"\b(fo|foo)\b", "foo", new[] { (0, 3) } }; - yield return new object[] { engine, @"\b\b", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"\b\b", "a", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"\b$", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"\b$", "x", new[] { (1, 1) } }; - yield return new object[] { engine, @"\b$", "y x", new[] { (3, 3) } }; - yield return new object[] { engine, @"\b.$", "x", new[] { (0, 1) } }; - yield return new object[] { engine, @"^\b(fo|foo)\b", "fo", new[] { (0, 2) } }; - yield return new object[] { engine, @"^\b(fo|foo)\b", "foo", new[] { (0, 3) } }; - yield return new object[] { engine, @"^\b$", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\b$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\b.$", "x", new[] { (0, 1) } }; - yield return new object[] { engine, @"^\b.\b$", "x", new[] { (0, 1) } }; - yield return new object[] { engine, @"^^^^^\b$$$$$", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"^^^^^\b.$$$$$", "x", new[] { (0, 1) } }; - yield return new object[] { engine, @"^^^^^\b$$$$$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^^^^^\b\b\b.\b\b\b$$$$$", "x", new[] { (0, 1) } }; - yield return new object[] { engine, @"\b.+\b", "$$abc$$", new[] { (2, 5) } }; - yield return new object[] { engine, @"\b", "a b c", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) } }; - yield return new object[] { engine, @"\Bfoo\B", "n foo xfoox that", new[] { (7, 10) } }; - yield return new object[] { engine, @"a\B", "faoa x", new[] { (1, 2) } }; - yield return new object[] { engine, @"\Bbar", "bar x", new ValueTuple[] { } }; - yield return new object[] { engine, @"\Bbar", "foo\nbar x", new ValueTuple[] { } }; - yield return new object[] { engine, @"bar\B", "foobar", new ValueTuple[] { } }; - yield return new object[] { engine, @"bar\B", "foobar\nxxx", new ValueTuple[] { } }; - yield return new object[] { engine, @"(foo|bar|[A-Z])\B", "foox", new[] { (0, 3) } }; - yield return new object[] { engine, @"(foo|bar|[A-Z])\B", "foo\n", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"\B", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])", "foo", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "xXy", new[] { (1, 2) } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "XY", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "XYZ", new[] { (1, 2) } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "abara", new[] { (1, 4) } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "xfoo_", new[] { (1, 4) } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "xfoo\n", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B(foo|bar|[A-Z])\B", "foo bar vNX", new[] { (9, 10) } }; - yield return new object[] { engine, @"\B(fo|foo)\B", "xfoo", new[] { (1, 3) } }; - yield return new object[] { engine, @"\B(foo|fo)\B", "xfooo", new[] { (1, 4) } }; - yield return new object[] { engine, @"\B\B", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"\B\B", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"\B$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B$", "y x", new ValueTuple[] { } }; - yield return new object[] { engine, @"\B.$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B(fo|foo)\B", "fo", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B(fo|foo)\B", "foo", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^\B", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B\B", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^\B\B", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^\B$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B.$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^\B.\B$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^^^^^\B$$$$$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^^^^^\B.$$$$$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"^^^^^\B$$$$$", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"\bx\b", "\u00ABx", new[] { (1, 2) } }; - yield return new object[] { engine, @"\bx\b", "x\u00BB", new[] { (0, 1) } }; - yield return new object[] { engine, @"\bx\b", "\u00E1x\u00DF", new ValueTuple[] { } }; - yield return new object[] { engine, @"\Bx\B", "\u00E1x\u00DF", new[] { (1, 2) } }; - yield return new object[] { engine, @" \b", " \u03B4", new[] { (0, 1) } }; - yield return new object[] { engine, @" \B", " \u03B4", new ValueTuple[] { } }; - yield return new object[] { engine, @"\w+", "a\u03B4", new[] { (0, 2) } }; - yield return new object[] { engine, @"\d+", "1\u0968\u09699", new[] { (0, 4) } }; - yield return new object[] { engine, @"[^a]", "\u03B4", new[] { (0, 1) } }; - yield return new object[] { engine, @"a", "\xFFa", new ValueTuple[] { } }; - yield return new object[] { engine, @"a", "a", new[] { (0, 1) } }; - yield return new object[] { engine, @"[-+]?[0-9]*\.?[0-9]+", "0.1", new[] { (0, 3) } }; - yield return new object[] { engine, @"[-+]?[0-9]*\.?[0-9]+", "0.1.2", new[] { (0, 3), (3, 5) } }; - yield return new object[] { engine, @"[-+]?[0-9]*\.?[0-9]+", "a1.2", new[] { (1, 4) } }; - yield return new object[] { engine, @"^[-+]?[0-9]*\.?[0-9]+$", "1.a", new ValueTuple[] { } }; - yield return new object[] { engine, @"[^ac]", "acx", new[] { (2, 3) } }; - yield return new object[] { engine, @"[^a,]", "a,x", new[] { (2, 3) } }; - yield return new object[] { engine, @"[^,]", ",,x", new[] { (2, 3) } }; - yield return new object[] { engine, @"((?:.*)*?)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.?)*?)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*)+?)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.?)+?)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*){1,}?)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*){1,2}?)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*)*)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.?)*)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*)+)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.?)+)=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*){1,})=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"((?:.*){1,2})=", "a=b", new[] { (0, 2) } }; - yield return new object[] { engine, @"abracadabra$", "abracadabracadabra", new[] { (7, 18) } }; - yield return new object[] { engine, @"a...b", "abababbb", new[] { (2, 7) } }; - yield return new object[] { engine, @"XXXXXX", "..XXXXXX", new[] { (2, 8) } }; - yield return new object[] { engine, @"\)", "()", new[] { (1, 2) } }; - yield return new object[] { engine, @"a]", "a]a", new[] { (0, 2) } }; - yield return new object[] { engine, @"\}", "}", new[] { (0, 1) } }; - yield return new object[] { engine, @"\]", "]", new[] { (0, 1) } }; - yield return new object[] { engine, @"]", "]", new[] { (0, 1) } }; - yield return new object[] { engine, @"^a", "ax", new[] { (0, 1) } }; - yield return new object[] { engine, @"\^a", "a^a", new[] { (1, 3) } }; - yield return new object[] { engine, @"a\^", "a^", new[] { (0, 2) } }; - yield return new object[] { engine, @"a$", "aa", new[] { (1, 2) } }; - yield return new object[] { engine, @"a\$", "a$", new[] { (0, 2) } }; - yield return new object[] { engine, @"^$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"$^", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"a($)", "aa", new[] { (1, 2) } }; - yield return new object[] { engine, @"a*(^a)", "aa", new[] { (0, 1) } }; - yield return new object[] { engine, @"(..)*(...)*", "a", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(..)*(...)*", "abcd", new[] { (0, 4), (4, 4) } }; - yield return new object[] { engine, @"(ab)c|abc", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a{0}b", "ab", new[] { (1, 2) } }; - yield return new object[] { engine, @"a*(a.|aa)", "aaaa", new[] { (0, 4) } }; - yield return new object[] { engine, @"(a|b)?.*", "b", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"(a|b)c|a(b|c)", "ac", new[] { (0, 2) } }; - yield return new object[] { engine, @"(a|b)*c|(a|ab)*c", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"(a|b)*c|(a|ab)*c", "xc", new[] { (1, 2) } }; - yield return new object[] { engine, @"a?(ab|ba)ab", "abab", new[] { (0, 4) } }; - yield return new object[] { engine, @"a?(ac{0}b|ba)ab", "abab", new[] { (0, 4) } }; - yield return new object[] { engine, @"ab|abab", "abbabab", new[] { (0, 2), (3, 5), (5, 7) } }; - yield return new object[] { engine, @"aba|bab|bba", "baaabbbaba", new[] { (5, 8) } }; - yield return new object[] { engine, @"aba|bab", "baaabbbaba", new[] { (6, 9) } }; - yield return new object[] { engine, @"ab|a", "xabc", new[] { (1, 3) } }; - yield return new object[] { engine, @"ab|a", "xxabc", new[] { (2, 4) } }; - yield return new object[] { engine, @"[^-]", "--a", new[] { (2, 3) } }; - yield return new object[] { engine, @"[a-]*", "--a", new[] { (0, 3), (3, 3) } }; - yield return new object[] { engine, @"[a-m-]*", "--amoma--", new[] { (0, 4), (4, 4), (5, 9), (9, 9) } }; - yield return new object[] { engine, @"[\p{Lu}]", "A", new[] { (0, 1) } }; - yield return new object[] { engine, @"[\p{Ll}]+", "`az{", new[] { (1, 3) } }; - yield return new object[] { engine, @"[\p{Lu}]+", "@AZ[", new[] { (1, 3) } }; - yield return new object[] { engine, @"xxx", "xxx", new[] { (0, 3) } }; - yield return new object[] { engine, @".*", "\u263A\u007F", new[] { (0, 2), (2, 2) } }; - yield return new object[] { engine, @"a*a*a*a*a*b", "aaaaaaaaab", new[] { (0, 10) } }; - yield return new object[] { engine, @"^", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^$", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"^a$", "a", new[] { (0, 1) } }; - yield return new object[] { engine, @"abc", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"abc", "xabcy", new[] { (1, 4) } }; - yield return new object[] { engine, @"abc", "ababc", new[] { (2, 5) } }; - yield return new object[] { engine, @"ab*c", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"ab*bc", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"ab*bc", "abbc", new[] { (0, 4) } }; - yield return new object[] { engine, @"ab*bc", "abbbbc", new[] { (0, 6) } }; - yield return new object[] { engine, @"ab+bc", "abbc", new[] { (0, 4) } }; - yield return new object[] { engine, @"ab+bc", "abbbbc", new[] { (0, 6) } }; - yield return new object[] { engine, @"ab?bc", "abbc", new[] { (0, 4) } }; - yield return new object[] { engine, @"ab?bc", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"ab?c", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"^abc$", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"^abc", "abcc", new[] { (0, 3) } }; - yield return new object[] { engine, @"abc$", "aabc", new[] { (1, 4) } }; - yield return new object[] { engine, @"^", "abc", new[] { (0, 0) } }; - yield return new object[] { engine, @"$", "abc", new[] { (3, 3) } }; - yield return new object[] { engine, @"a.c", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a.c", "axc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a.*c", "axyzc", new[] { (0, 5) } }; - yield return new object[] { engine, @"a[bc]d", "abd", new[] { (0, 3) } }; - yield return new object[] { engine, @"a[b-d]e", "ace", new[] { (0, 3) } }; - yield return new object[] { engine, @"a[b-d]", "aac", new[] { (1, 3) } }; - yield return new object[] { engine, @"a[-b]", "a-", new[] { (0, 2) } }; - yield return new object[] { engine, @"a[b-]", "a-", new[] { (0, 2) } }; - yield return new object[] { engine, @"a]", "a]", new[] { (0, 2) } }; - yield return new object[] { engine, @"a[]]b", "a]b", new[] { (0, 3) } }; - yield return new object[] { engine, @"a[^bc]d", "aed", new[] { (0, 3) } }; - yield return new object[] { engine, @"a[^-b]c", "adc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a[^]b]c", "adc", new[] { (0, 3) } }; - yield return new object[] { engine, @"ab|cd", "abc", new[] { (0, 2) } }; - yield return new object[] { engine, @"ab|cd", "abcd", new[] { (0, 2), (2, 4) } }; - yield return new object[] { engine, @"a\(b", "a(b", new[] { (0, 3) } }; - yield return new object[] { engine, @"a\(*b", "ab", new[] { (0, 2) } }; - yield return new object[] { engine, @"a\(*b", "a((b", new[] { (0, 4) } }; - yield return new object[] { engine, @"a+b+c", "aabbabc", new[] { (4, 7) } }; - yield return new object[] { engine, @"a*", "aaa", new[] { (0, 3), (3, 3) } }; - yield return new object[] { engine, @"(a*)*", "-", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(a*)+", "-", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(a*|b)*", "-", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(a+|b)*", "ab", new[] { (0, 2), (2, 2) } }; - yield return new object[] { engine, @"(a+|b)+", "ab", new[] { (0, 2) } }; - yield return new object[] { engine, @"(a+|b)?", "ab", new[] { (0, 1), (1, 2), (2, 2) } }; - yield return new object[] { engine, @"[^ab]*", "cde", new[] { (0, 3), (3, 3) } }; - yield return new object[] { engine, @"(^)*", "-", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"a*", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"([abc])*d", "abbbcd", new[] { (0, 6) } }; - yield return new object[] { engine, @"([abc])*bcd", "abcd", new[] { (0, 4) } }; - yield return new object[] { engine, @"a|b|c|d|e", "e", new[] { (0, 1) } }; - yield return new object[] { engine, @"(a|b|c|d|e)f", "ef", new[] { (0, 2) } }; - yield return new object[] { engine, @"((a*|b))*", "-", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"abcd*efg", "abcdefg", new[] { (0, 7) } }; - yield return new object[] { engine, @"ab*", "xabyabbbz", new[] { (1, 3), (4, 8) } }; - yield return new object[] { engine, @"ab*", "xayabbbz", new[] { (1, 2), (3, 7) } }; - yield return new object[] { engine, @"(ab|cd)e", "abcde", new[] { (2, 5) } }; - yield return new object[] { engine, @"[abhgefdc]ij", "hij", new[] { (0, 3) } }; - yield return new object[] { engine, @"(a|b)c*d", "abcd", new[] { (1, 4) } }; - yield return new object[] { engine, @"(ab|ab*)bc", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a([bc]*)c*", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a[bcd]*dcdcde", "adcdcde", new[] { (0, 7) } }; - yield return new object[] { engine, @"(ab|a)b*c", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"[A-Za-z_][A-Za-z0-9_]*", "alpha", new[] { (0, 5) } }; - yield return new object[] { engine, @"^a(bc+|b[eh])g|.h$", "abh", new[] { (1, 3) } }; - yield return new object[] { engine, @"abcd", "abcd", new[] { (0, 4) } }; - yield return new object[] { engine, @"a(bc)d", "abcd", new[] { (0, 4) } }; - yield return new object[] { engine, "a[\u263A-\u2665]?c", "a\u263bc", new[] { (0, 3) } }; - yield return new object[] { engine, @"a+(b|c)*d+", "aabcdd", new[] { (0, 6) } }; - yield return new object[] { engine, @"^.+$", "vivi", new[] { (0, 4) } }; - yield return new object[] { engine, @"^(.+)$", "vivi", new[] { (0, 4) } }; - yield return new object[] { engine, @".*(/XXX).*", "/XXX", new[] { (0, 4) } }; - yield return new object[] { engine, @".*(/000).*", "/000", new[] { (0, 4) } }; - yield return new object[] { engine, @".*(\\000).*", "\000", new ValueTuple[] { } }; - yield return new object[] { engine, @"\\000", "\000", new ValueTuple[] { } }; - yield return new object[] { engine, @"(a*)*", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"(a*)*", "x", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(a*)*", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"(a*)*", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) } }; - yield return new object[] { engine, @"(a*)+", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"(a*)+", "x", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(a*)+", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"(a*)+", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) } }; - yield return new object[] { engine, @"(a+)*", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"(a+)*", "x", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"(a+)*", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"(a+)*", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) } }; - yield return new object[] { engine, @"(a+)+", "a", new[] { (0, 1) } }; - yield return new object[] { engine, @"(a+)+", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"(a+)+", "aaaaaa", new[] { (0, 6) } }; - yield return new object[] { engine, @"(a+)+", "aaaaaax", new[] { (0, 6) } }; - yield return new object[] { engine, @"([a]*)*", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"([a]*)*", "x", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"([a]*)*", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([a]*)*", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) } }; - yield return new object[] { engine, @"([a]*)+", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"([a]*)+", "x", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"([a]*)+", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([a]*)+", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) } }; - yield return new object[] { engine, @"([^b]*)*", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"([^b]*)*", "b", new[] { (0, 0), (1, 1) } }; - yield return new object[] { engine, @"([^b]*)*", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([ab]*)*", "a", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"([ab]*)*", "aaaaaa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([ab]*)*", "ababab", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([ab]*)*", "bababa", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([ab]*)*", "b", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"([ab]*)*", "bbbbbb", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([^a]*)*", "b", new[] { (0, 1), (1, 1) } }; - yield return new object[] { engine, @"([^a]*)*", "bbbbbb", new[] { (0, 6), (6, 6) } }; - yield return new object[] { engine, @"([^a]*)*", "aaaaaa", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) } }; - yield return new object[] { engine, @"([^ab]*)*", "ababab", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) } }; - yield return new object[] { engine, @"((..)|(.))", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.))((..)|(.))", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.))((..)|(.))((..)|(.))", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.)){1}", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.)){2}", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.)){3}", "", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.))*", "", new[] { (0, 0) } }; - yield return new object[] { engine, @"((..)|(.))((..)|(.))", "a", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.))((..)|(.))((..)|(.))", "a", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.)){2}", "a", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.)){3}", "a", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.))((..)|(.))((..)|(.))", "aa", new ValueTuple[] { } }; - yield return new object[] { engine, @"((..)|(.)){3}", "aa", new ValueTuple[] { } }; - yield return new object[] { engine, @"(a|ab|c|bcd){4,}(d*)", "ababcd", new ValueTuple[] { } }; - yield return new object[] { engine, @"(a|ab|c|bcd){4,10}(d*)", "ababcd", new ValueTuple[] { } }; - yield return new object[] { engine, @"(ab|a|c|bcd){4,}(d*)", "ababcd", new ValueTuple[] { } }; - yield return new object[] { engine, @"(ab|a|c|bcd){4,10}(d*)", "ababcd", new ValueTuple[] { } }; - yield return new object[] { engine, @"^abc", "abc", new[] { (0, 3) } }; - yield return new object[] { engine, @"^abc", "zabc", new ValueTuple[] { } }; - yield return new object[] { engine, @"abc", "xxxxxab", new ValueTuple[] { } }; - yield return new object[] { engine, @"(?i)[^x]", "x", new ValueTuple[] { } }; - yield return new object[] { engine, @"(?i)[^x]", "X", new ValueTuple[] { } }; - yield return new object[] { engine, @"[[:word:]]", "_", new ValueTuple[] { } }; - yield return new object[] { engine, @"ab?|$", "az", new[] { (0, 1), (2, 2) } }; - yield return new object[] { engine, @"^(.*?)(\n|\r\n?|$)", "ab\rcd", new[] { (0, 3) } }; - yield return new object[] { engine, @"z*azb", "azb", new[] { (0, 3) } }; - yield return new object[] { engine, @"(?i)\p{Ll}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 5) } }; - yield return new object[] { engine, @"1|2|3|4|5|6|7|8|9|10|int", "int", new[] { (0, 3) } }; - yield return new object[] { engine, @"^a[[:^space:]]", "a ", new ValueTuple[] { } }; - yield return new object[] { engine, @"^a[[:^space:]]", "foo boo a ", new ValueTuple[] { } }; - yield return new object[] { engine, @"^-[a-z]", "r-f", new ValueTuple[] { } }; - yield return new object[] { engine, @"(ABC|CDA|BC)X", "CDAX", new[] { (0, 4) } }; - yield return new object[] { engine, @"(aa$)?", "aaz", new[] { (0, 0), (1, 1), (2, 2), (3, 3) } }; - yield return new object[] { engine, @"ab??", "ab", new[] { (0, 1) } }; - yield return new object[] { engine, @".*abcd", "abcd", new[] { (0, 4) } }; - yield return new object[] { engine, @".*(?:abcd)+", "abcd", new[] { (0, 4) } }; - yield return new object[] { engine, @".*(?:abcd)+", "abcdabcd", new[] { (0, 8) } }; - yield return new object[] { engine, @".*(?:abcd)+", "abcdxabcd", new[] { (0, 9) } }; - yield return new object[] { engine, @".*x(?:abcd)+", "abcdxabcd", new[] { (0, 9) } }; - yield return new object[] { engine, @"[^abcd]*x(?:abcd)+", "abcdxabcd", new[] { (4, 9) } }; - yield return new object[] { engine, @".", "\xD4\xC2\x65\x2B\x0E\xFE", new[] { (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6) } }; - yield return new object[] { engine, "${2}\u00E4", "\xD4\xC2\x65\x2B\x0E\xFE", new ValueTuple[] { } }; - yield return new object[] { engine, "\u2603", "\u2603", new[] { (0, 1) } }; - yield return new object[] { engine, "\u2603+", "\u2603", new[] { (0, 1) } }; - yield return new object[] { engine, "(?i)\u2603+", "\u2603", new[] { (0, 1) } }; - yield return new object[] { engine, "[\u2603\u2160]+", "\u2603", new[] { (0, 1) } }; - yield return new object[] { engine, "(?i)\u0394", "\u03B4", new[] { (0, 1) } }; - yield return new object[] { engine, @"\p{Lu}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 4) } }; - yield return new object[] { engine, @"(?i)\p{Lu}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 5) } }; - yield return new object[] { engine, @"\p{L}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 5) } }; - yield return new object[] { engine, @"\p{Ll}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (4, 5) } }; - yield return new object[] { engine, @"\w+", "d\u03B4d", new[] { (0, 3) } }; - yield return new object[] { engine, @"\w+", "\u2961", new ValueTuple[] { } }; - yield return new object[] { engine, @"\W+", "\u2961", new[] { (0, 1) } }; - yield return new object[] { engine, @"\d+", "1\u0968\u09699", new[] { (0, 4) } }; - yield return new object[] { engine, @"\d+", "\u2161", new ValueTuple[] { } }; - yield return new object[] { engine, @"\D+", "\u2161", new[] { (0, 1) } }; - yield return new object[] { engine, @"\s+", "\u1680", new[] { (0, 1) } }; - yield return new object[] { engine, @"\s+", "\u2603", new ValueTuple[] { } }; - yield return new object[] { engine, @"\S+", "\u2603", new[] { (0, 1) } }; - yield return new object[] { engine, @"\d\b", "6\u03B4", new ValueTuple[] { } }; - yield return new object[] { engine, @"\d\b", "6\u1680", new[] { (0, 1) } }; - yield return new object[] { engine, @"\d\B", "6\u03B4", new[] { (0, 1) } }; - yield return new object[] { engine, @"\d\B", "6\u1680", new ValueTuple[] { } }; + (string Pattern, string Input, IEnumerable<(int, int)> MatchBoundaries)[] cases = MatchStartAndEndPositions_MemberData_Cases().ToArray(); + Regex[] regexes = RegexHelpers.GetRegexesAsync(engine, cases.Select(c => (c.Pattern, (CultureInfo?)null, (RegexOptions?)RegexOptions.None, (TimeSpan?)null)).ToArray()).Result; + for (int i = 0; i < regexes.Length; i++) + { + yield return new object[] { regexes[i], cases[i].Input, cases[i].MatchBoundaries }; + } } } + public static IEnumerable<(string Pattern, string Input, IEnumerable<(int, int)>? MatchBoundaries)> MatchStartAndEndPositions_MemberData_Cases() + { + yield return (@"^$", "", new[] { (0, 0) }); + yield return (@"^$^$^$", "", new[] { (0, 0) }); + yield return (@"^^^$$$", "", new[] { (0, 0) }); + yield return (@"$^", "", new[] { (0, 0) }); + yield return (@"(?:^$)*", "a\nb\nc", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) }); + yield return (@"(?:$^)*", "a\nb\nc", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) }); + yield return (@"", "", new[] { (0, 0) }); + yield return (@"", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()*", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()+", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()?", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()()", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()+|z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"z|()+", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"()+|b", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"b|()+", "abc", new[] { (0, 0), (1, 2), (2, 2), (3, 3) }); + yield return (@"|b", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"b|", "abc", new[] { (0, 0), (1, 2), (2, 2), (3, 3) }); + yield return (@"|z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"z|", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"|", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"||", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"||z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"(?:)|b", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"b|(?:)", "abc", new[] { (0, 0), (1, 2), (2, 2), (3, 3) }); + yield return (@"(?:|)", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"(?:|)|z", "abc", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"a(?:)|b", "abc", new[] { (0, 1), (1, 2) }); + yield return (@"a$", "a", new[] { (0, 1) }); + yield return (@"(?m)^[a-z]+$", "abc\ndef\nxyz", new[] { (0, 3), (4, 7), (8, 11) }); + yield return (@"(?m)^$", "abc\ndef\nxyz", new ValueTuple[] { }); + yield return (@"(?m)^", "abc\ndef\nxyz", new[] { (0, 0), (4, 4), (8, 8) }); + yield return (@"(?m)$", "abc\ndef\nxyz", new[] { (3, 3), (7, 7), (11, 11) }); + yield return (@"(?m)^[a-z]", "abc\ndef\nxyz", new[] { (0, 1), (4, 5), (8, 9) }); + yield return (@"(?m)[a-z]^", "abc\ndef\nxyz", new ValueTuple[] { }); + yield return (@"(?m)[a-z]$", "abc\ndef\nxyz", new[] { (2, 3), (6, 7), (10, 11) }); + yield return (@"(?m)$[a-z]", "abc\ndef\nxyz", new ValueTuple[] { }); + yield return (@"(?m)^$", "", new[] { (0, 0) }); + yield return (@"(?m)(?:^$)*", "a\nb\nc", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) }); + yield return (@"(?m)(?:^|a)+", "a\naaa\n", new[] { (0, 0), (2, 2), (3, 5), (6, 6) }); + yield return (@"(?m)(?:^|a)*", "a\naaa\n", new[] { (0, 0), (1, 1), (2, 2), (3, 5), (5, 5), (6, 6) }); + yield return (@"(?m)(?:^[a-z])+", "abc\ndef\nxyz", new[] { (0, 1), (4, 5), (8, 9) }); + yield return (@"(?m)(?:^[a-z]{3}\n?)+", "abc\ndef\nxyz", new[] { (0, 11) }); + yield return (@"(?m)(?:^[a-z]{3}\n?)*", "abc\ndef\nxyz", new[] { (0, 11), (11, 11) }); + yield return (@"(?m)(?:\n?[a-z]{3}$)+", "abc\ndef\nxyz", new[] { (0, 11) }); + yield return (@"(?m)(?:\n?[a-z]{3}$)*", "abc\ndef\nxyz", new[] { (0, 11), (11, 11) }); + yield return (@"(?m)^*", "\naa\n", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4) }); + yield return (@"(?m)^+", "\naa\n", new[] { (0, 0), (1, 1), (4, 4) }); + yield return (@"(?m)$*", "\naa\n", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4) }); + yield return (@"(?m)$+", "\naa\n", new[] { (0, 0), (3, 3), (4, 4) }); + yield return (@"(?m)(?:$\n)+", "\n\naaa\n\n", new[] { (0, 2), (5, 7) }); + yield return (@"(?m)(?:$\n)*", "\n\naaa\n\n", new[] { (0, 2), (2, 2), (3, 3), (4, 4), (5, 7), (7, 7) }); + yield return (@"(?m)(?:$\n^)+", "\n\naaa\n\n", new[] { (0, 2), (5, 7) }); + yield return (@"(?m)(?:^|$)+", "\n\naaa\n\n", new[] { (0, 0), (1, 1), (2, 2), (5, 5), (6, 6), (7, 7) }); + yield return (@"\b", "a b c", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) }); + yield return (@"^a|b", "ba", new[] { (0, 1) }); + yield return (@"[0-9][0-9][0-9]000", "153.230000\n", new[] { (4, 10) }); + yield return (@"((?i)foo)|Bar", "foo Foo bar Bar", new[] { (0, 3), (4, 7), (12, 15) }); + yield return (@"()?01", "z?01", new[] { (2, 4) }); + yield return (@"\b", "", new ValueTuple[] { }); + yield return (@"\b", "a", new[] { (0, 0), (1, 1) }); + yield return (@"\b", "ab", new[] { (0, 0), (2, 2) }); + yield return (@"^\b", "ab", new[] { (0, 0) }); + yield return (@"\b$", "ab", new[] { (2, 2) }); + yield return (@"^\b$", "ab", new ValueTuple[] { }); + yield return (@"\bbar\b", "nobar bar foo bar", new[] { (6, 9), (14, 17) }); + yield return (@"a\b", "faoa x", new[] { (3, 4) }); + yield return (@"\bbar", "bar x", new[] { (0, 3) }); + yield return (@"\bbar", "foo\nbar x", new[] { (4, 7) }); + yield return (@"bar\b", "foobar", new[] { (3, 6) }); + yield return (@"bar\b", "foobar\nxxx", new[] { (3, 6) }); + yield return (@"(foo|bar|[A-Z])\b", "foo", new[] { (0, 3) }); + yield return (@"(foo|bar|[A-Z])\b", "foo\n", new[] { (0, 3) }); + yield return (@"\b(foo|bar|[A-Z])", "foo", new[] { (0, 3) }); + yield return (@"\b(foo|bar|[A-Z])\b", "X", new[] { (0, 1) }); + yield return (@"\b(foo|bar|[A-Z])\b", "XY", new ValueTuple[] { }); + yield return (@"\b(foo|bar|[A-Z])\b", "bar", new[] { (0, 3) }); + yield return (@"\b(foo|bar|[A-Z])\b", "foo", new[] { (0, 3) }); + yield return (@"\b(foo|bar|[A-Z])\b", "foo\n", new[] { (0, 3) }); + yield return (@"\b(foo|bar|[A-Z])\b", "ffoo bbar N x", new[] { (10, 11) }); + yield return (@"\b(fo|foo)\b", "fo", new[] { (0, 2) }); + yield return (@"\b(fo|foo)\b", "foo", new[] { (0, 3) }); + yield return (@"\b\b", "", new ValueTuple[] { }); + yield return (@"\b\b", "a", new[] { (0, 0), (1, 1) }); + yield return (@"\b$", "", new ValueTuple[] { }); + yield return (@"\b$", "x", new[] { (1, 1) }); + yield return (@"\b$", "y x", new[] { (3, 3) }); + yield return (@"\b.$", "x", new[] { (0, 1) }); + yield return (@"^\b(fo|foo)\b", "fo", new[] { (0, 2) }); + yield return (@"^\b(fo|foo)\b", "foo", new[] { (0, 3) }); + yield return (@"^\b$", "", new ValueTuple[] { }); + yield return (@"^\b$", "x", new ValueTuple[] { }); + yield return (@"^\b.$", "x", new[] { (0, 1) }); + yield return (@"^\b.\b$", "x", new[] { (0, 1) }); + yield return (@"^^^^^\b$$$$$", "", new ValueTuple[] { }); + yield return (@"^^^^^\b.$$$$$", "x", new[] { (0, 1) }); + yield return (@"^^^^^\b$$$$$", "x", new ValueTuple[] { }); + yield return (@"^^^^^\b\b\b.\b\b\b$$$$$", "x", new[] { (0, 1) }); + yield return (@"\b.+\b", "$$abc$$", new[] { (2, 5) }); + yield return (@"\b", "a b c", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5) }); + yield return (@"\Bfoo\B", "n foo xfoox that", new[] { (7, 10) }); + yield return (@"a\B", "faoa x", new[] { (1, 2) }); + yield return (@"\Bbar", "bar x", new ValueTuple[] { }); + yield return (@"\Bbar", "foo\nbar x", new ValueTuple[] { }); + yield return (@"bar\B", "foobar", new ValueTuple[] { }); + yield return (@"bar\B", "foobar\nxxx", new ValueTuple[] { }); + yield return (@"(foo|bar|[A-Z])\B", "foox", new[] { (0, 3) }); + yield return (@"(foo|bar|[A-Z])\B", "foo\n", new ValueTuple[] { }); + yield return (@"\B", "", new[] { (0, 0) }); + yield return (@"\B", "x", new ValueTuple[] { }); + yield return (@"\B(foo|bar|[A-Z])", "foo", new ValueTuple[] { }); + yield return (@"\B(foo|bar|[A-Z])\B", "xXy", new[] { (1, 2) }); + yield return (@"\B(foo|bar|[A-Z])\B", "XY", new ValueTuple[] { }); + yield return (@"\B(foo|bar|[A-Z])\B", "XYZ", new[] { (1, 2) }); + yield return (@"\B(foo|bar|[A-Z])\B", "abara", new[] { (1, 4) }); + yield return (@"\B(foo|bar|[A-Z])\B", "xfoo_", new[] { (1, 4) }); + yield return (@"\B(foo|bar|[A-Z])\B", "xfoo\n", new ValueTuple[] { }); + yield return (@"\B(foo|bar|[A-Z])\B", "foo bar vNX", new[] { (9, 10) }); + yield return (@"\B(fo|foo)\B", "xfoo", new[] { (1, 3) }); + yield return (@"\B(foo|fo)\B", "xfooo", new[] { (1, 4) }); + yield return (@"\B\B", "", new[] { (0, 0) }); + yield return (@"\B\B", "x", new ValueTuple[] { }); + yield return (@"\B$", "", new[] { (0, 0) }); + yield return (@"\B$", "x", new ValueTuple[] { }); + yield return (@"\B$", "y x", new ValueTuple[] { }); + yield return (@"\B.$", "x", new ValueTuple[] { }); + yield return (@"^\B(fo|foo)\B", "fo", new ValueTuple[] { }); + yield return (@"^\B(fo|foo)\B", "foo", new ValueTuple[] { }); + yield return (@"^\B", "", new[] { (0, 0) }); + yield return (@"^\B", "x", new ValueTuple[] { }); + yield return (@"^\B\B", "", new[] { (0, 0) }); + yield return (@"^\B\B", "x", new ValueTuple[] { }); + yield return (@"^\B$", "", new[] { (0, 0) }); + yield return (@"^\B$", "x", new ValueTuple[] { }); + yield return (@"^\B.$", "x", new ValueTuple[] { }); + yield return (@"^\B.\B$", "x", new ValueTuple[] { }); + yield return (@"^^^^^\B$$$$$", "", new[] { (0, 0) }); + yield return (@"^^^^^\B.$$$$$", "x", new ValueTuple[] { }); + yield return (@"^^^^^\B$$$$$", "x", new ValueTuple[] { }); + yield return (@"\bx\b", "\u00ABx", new[] { (1, 2) }); + yield return (@"\bx\b", "x\u00BB", new[] { (0, 1) }); + yield return (@"\bx\b", "\u00E1x\u00DF", new ValueTuple[] { }); + yield return (@"\Bx\B", "\u00E1x\u00DF", new[] { (1, 2) }); + yield return (@" \b", " \u03B4", new[] { (0, 1) }); + yield return (@" \B", " \u03B4", new ValueTuple[] { }); + yield return (@"\w+", "a\u03B4", new[] { (0, 2) }); + yield return (@"\d+", "1\u0968\u09699", new[] { (0, 4) }); + yield return (@"[^a]", "\u03B4", new[] { (0, 1) }); + yield return (@"a", "\xFFa", new ValueTuple[] { }); + yield return (@"a", "a", new[] { (0, 1) }); + yield return (@"[-+]?[0-9]*\.?[0-9]+", "0.1", new[] { (0, 3) }); + yield return (@"[-+]?[0-9]*\.?[0-9]+", "0.1.2", new[] { (0, 3), (3, 5) }); + yield return (@"[-+]?[0-9]*\.?[0-9]+", "a1.2", new[] { (1, 4) }); + yield return (@"^[-+]?[0-9]*\.?[0-9]+$", "1.a", new ValueTuple[] { }); + yield return (@"[^ac]", "acx", new[] { (2, 3) }); + yield return (@"[^a,]", "a,x", new[] { (2, 3) }); + yield return (@"[^,]", ",,x", new[] { (2, 3) }); + yield return (@"((?:.*)*?)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.?)*?)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*)+?)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.?)+?)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*){1,}?)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*){1,2}?)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*)*)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.?)*)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*)+)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.?)+)=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*){1,})=", "a=b", new[] { (0, 2) }); + yield return (@"((?:.*){1,2})=", "a=b", new[] { (0, 2) }); + yield return (@"abracadabra$", "abracadabracadabra", new[] { (7, 18) }); + yield return (@"a...b", "abababbb", new[] { (2, 7) }); + yield return (@"XXXXXX", "..XXXXXX", new[] { (2, 8) }); + yield return (@"\)", "()", new[] { (1, 2) }); + yield return (@"a]", "a]a", new[] { (0, 2) }); + yield return (@"\}", "}", new[] { (0, 1) }); + yield return (@"\]", "]", new[] { (0, 1) }); + yield return (@"]", "]", new[] { (0, 1) }); + yield return (@"^a", "ax", new[] { (0, 1) }); + yield return (@"\^a", "a^a", new[] { (1, 3) }); + yield return (@"a\^", "a^", new[] { (0, 2) }); + yield return (@"a$", "aa", new[] { (1, 2) }); + yield return (@"a\$", "a$", new[] { (0, 2) }); + yield return (@"^$", "", new[] { (0, 0) }); + yield return (@"$^", "", new[] { (0, 0) }); + yield return (@"a($)", "aa", new[] { (1, 2) }); + yield return (@"a*(^a)", "aa", new[] { (0, 1) }); + yield return (@"(..)*(...)*", "a", new[] { (0, 0), (1, 1) }); + yield return (@"(..)*(...)*", "abcd", new[] { (0, 4), (4, 4) }); + yield return (@"(ab)c|abc", "abc", new[] { (0, 3) }); + yield return (@"a{0}b", "ab", new[] { (1, 2) }); + yield return (@"a*(a.|aa)", "aaaa", new[] { (0, 4) }); + yield return (@"(a|b)?.*", "b", new[] { (0, 1), (1, 1) }); + yield return (@"(a|b)c|a(b|c)", "ac", new[] { (0, 2) }); + yield return (@"(a|b)*c|(a|ab)*c", "abc", new[] { (0, 3) }); + yield return (@"(a|b)*c|(a|ab)*c", "xc", new[] { (1, 2) }); + yield return (@"a?(ab|ba)ab", "abab", new[] { (0, 4) }); + yield return (@"a?(ac{0}b|ba)ab", "abab", new[] { (0, 4) }); + yield return (@"ab|abab", "abbabab", new[] { (0, 2), (3, 5), (5, 7) }); + yield return (@"aba|bab|bba", "baaabbbaba", new[] { (5, 8) }); + yield return (@"aba|bab", "baaabbbaba", new[] { (6, 9) }); + yield return (@"ab|a", "xabc", new[] { (1, 3) }); + yield return (@"ab|a", "xxabc", new[] { (2, 4) }); + yield return (@"[^-]", "--a", new[] { (2, 3) }); + yield return (@"[a-]*", "--a", new[] { (0, 3), (3, 3) }); + yield return (@"[a-m-]*", "--amoma--", new[] { (0, 4), (4, 4), (5, 9), (9, 9) }); + yield return (@"[\p{Lu}]", "A", new[] { (0, 1) }); + yield return (@"[\p{Ll}]+", "`az{", new[] { (1, 3) }); + yield return (@"[\p{Lu}]+", "@AZ[", new[] { (1, 3) }); + yield return (@"xxx", "xxx", new[] { (0, 3) }); + yield return (@".*", "\u263A\u007F", new[] { (0, 2), (2, 2) }); + yield return (@"a*a*a*a*a*b", "aaaaaaaaab", new[] { (0, 10) }); + yield return (@"^", "", new[] { (0, 0) }); + yield return (@"$", "", new[] { (0, 0) }); + yield return (@"^$", "", new[] { (0, 0) }); + yield return (@"^a$", "a", new[] { (0, 1) }); + yield return (@"abc", "abc", new[] { (0, 3) }); + yield return (@"abc", "xabcy", new[] { (1, 4) }); + yield return (@"abc", "ababc", new[] { (2, 5) }); + yield return (@"ab*c", "abc", new[] { (0, 3) }); + yield return (@"ab*bc", "abc", new[] { (0, 3) }); + yield return (@"ab*bc", "abbc", new[] { (0, 4) }); + yield return (@"ab*bc", "abbbbc", new[] { (0, 6) }); + yield return (@"ab+bc", "abbc", new[] { (0, 4) }); + yield return (@"ab+bc", "abbbbc", new[] { (0, 6) }); + yield return (@"ab?bc", "abbc", new[] { (0, 4) }); + yield return (@"ab?bc", "abc", new[] { (0, 3) }); + yield return (@"ab?c", "abc", new[] { (0, 3) }); + yield return (@"^abc$", "abc", new[] { (0, 3) }); + yield return (@"^abc", "abcc", new[] { (0, 3) }); + yield return (@"abc$", "aabc", new[] { (1, 4) }); + yield return (@"^", "abc", new[] { (0, 0) }); + yield return (@"$", "abc", new[] { (3, 3) }); + yield return (@"a.c", "abc", new[] { (0, 3) }); + yield return (@"a.c", "axc", new[] { (0, 3) }); + yield return (@"a.*c", "axyzc", new[] { (0, 5) }); + yield return (@"a[bc]d", "abd", new[] { (0, 3) }); + yield return (@"a[b-d]e", "ace", new[] { (0, 3) }); + yield return (@"a[b-d]", "aac", new[] { (1, 3) }); + yield return (@"a[-b]", "a-", new[] { (0, 2) }); + yield return (@"a[b-]", "a-", new[] { (0, 2) }); + yield return (@"a]", "a]", new[] { (0, 2) }); + yield return (@"a[]]b", "a]b", new[] { (0, 3) }); + yield return (@"a[^bc]d", "aed", new[] { (0, 3) }); + yield return (@"a[^-b]c", "adc", new[] { (0, 3) }); + yield return (@"a[^]b]c", "adc", new[] { (0, 3) }); + yield return (@"ab|cd", "abc", new[] { (0, 2) }); + yield return (@"ab|cd", "abcd", new[] { (0, 2), (2, 4) }); + yield return (@"a\(b", "a(b", new[] { (0, 3) }); + yield return (@"a\(*b", "ab", new[] { (0, 2) }); + yield return (@"a\(*b", "a((b", new[] { (0, 4) }); + yield return (@"a+b+c", "aabbabc", new[] { (4, 7) }); + yield return (@"a*", "aaa", new[] { (0, 3), (3, 3) }); + yield return (@"(a*)*", "-", new[] { (0, 0), (1, 1) }); + yield return (@"(a*)+", "-", new[] { (0, 0), (1, 1) }); + yield return (@"(a*|b)*", "-", new[] { (0, 0), (1, 1) }); + yield return (@"(a+|b)*", "ab", new[] { (0, 2), (2, 2) }); + yield return (@"(a+|b)+", "ab", new[] { (0, 2) }); + yield return (@"(a+|b)?", "ab", new[] { (0, 1), (1, 2), (2, 2) }); + yield return (@"[^ab]*", "cde", new[] { (0, 3), (3, 3) }); + yield return (@"(^)*", "-", new[] { (0, 0), (1, 1) }); + yield return (@"a*", "", new[] { (0, 0) }); + yield return (@"([abc])*d", "abbbcd", new[] { (0, 6) }); + yield return (@"([abc])*bcd", "abcd", new[] { (0, 4) }); + yield return (@"a|b|c|d|e", "e", new[] { (0, 1) }); + yield return (@"(a|b|c|d|e)f", "ef", new[] { (0, 2) }); + yield return (@"((a*|b))*", "-", new[] { (0, 0), (1, 1) }); + yield return (@"abcd*efg", "abcdefg", new[] { (0, 7) }); + yield return (@"ab*", "xabyabbbz", new[] { (1, 3), (4, 8) }); + yield return (@"ab*", "xayabbbz", new[] { (1, 2), (3, 7) }); + yield return (@"(ab|cd)e", "abcde", new[] { (2, 5) }); + yield return (@"[abhgefdc]ij", "hij", new[] { (0, 3) }); + yield return (@"(a|b)c*d", "abcd", new[] { (1, 4) }); + yield return (@"(ab|ab*)bc", "abc", new[] { (0, 3) }); + yield return (@"a([bc]*)c*", "abc", new[] { (0, 3) }); + yield return (@"a[bcd]*dcdcde", "adcdcde", new[] { (0, 7) }); + yield return (@"(ab|a)b*c", "abc", new[] { (0, 3) }); + yield return (@"[A-Za-z_][A-Za-z0-9_]*", "alpha", new[] { (0, 5) }); + yield return (@"^a(bc+|b[eh])g|.h$", "abh", new[] { (1, 3) }); + yield return (@"abcd", "abcd", new[] { (0, 4) }); + yield return (@"a(bc)d", "abcd", new[] { (0, 4) }); + yield return ("a[\u263A-\u2665]?c", "a\u263bc", new[] { (0, 3) }); + yield return (@"a+(b|c)*d+", "aabcdd", new[] { (0, 6) }); + yield return (@"^.+$", "vivi", new[] { (0, 4) }); + yield return (@"^(.+)$", "vivi", new[] { (0, 4) }); + yield return (@".*(/XXX).*", "/XXX", new[] { (0, 4) }); + yield return (@".*(/000).*", "/000", new[] { (0, 4) }); + yield return (@".*(\\000).*", "\000", new ValueTuple[] { }); + yield return (@"\\000", "\000", new ValueTuple[] { }); + yield return (@"(a*)*", "a", new[] { (0, 1), (1, 1) }); + yield return (@"(a*)*", "x", new[] { (0, 0), (1, 1) }); + yield return (@"(a*)*", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"(a*)*", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) }); + yield return (@"(a*)+", "a", new[] { (0, 1), (1, 1) }); + yield return (@"(a*)+", "x", new[] { (0, 0), (1, 1) }); + yield return (@"(a*)+", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"(a*)+", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) }); + yield return (@"(a+)*", "a", new[] { (0, 1), (1, 1) }); + yield return (@"(a+)*", "x", new[] { (0, 0), (1, 1) }); + yield return (@"(a+)*", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"(a+)*", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) }); + yield return (@"(a+)+", "a", new[] { (0, 1) }); + yield return (@"(a+)+", "x", new ValueTuple[] { }); + yield return (@"(a+)+", "aaaaaa", new[] { (0, 6) }); + yield return (@"(a+)+", "aaaaaax", new[] { (0, 6) }); + yield return (@"([a]*)*", "a", new[] { (0, 1), (1, 1) }); + yield return (@"([a]*)*", "x", new[] { (0, 0), (1, 1) }); + yield return (@"([a]*)*", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"([a]*)*", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) }); + yield return (@"([a]*)+", "a", new[] { (0, 1), (1, 1) }); + yield return (@"([a]*)+", "x", new[] { (0, 0), (1, 1) }); + yield return (@"([a]*)+", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"([a]*)+", "aaaaaax", new[] { (0, 6), (6, 6), (7, 7) }); + yield return (@"([^b]*)*", "a", new[] { (0, 1), (1, 1) }); + yield return (@"([^b]*)*", "b", new[] { (0, 0), (1, 1) }); + yield return (@"([^b]*)*", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"([ab]*)*", "a", new[] { (0, 1), (1, 1) }); + yield return (@"([ab]*)*", "aaaaaa", new[] { (0, 6), (6, 6) }); + yield return (@"([ab]*)*", "ababab", new[] { (0, 6), (6, 6) }); + yield return (@"([ab]*)*", "bababa", new[] { (0, 6), (6, 6) }); + yield return (@"([ab]*)*", "b", new[] { (0, 1), (1, 1) }); + yield return (@"([ab]*)*", "bbbbbb", new[] { (0, 6), (6, 6) }); + yield return (@"([^a]*)*", "b", new[] { (0, 1), (1, 1) }); + yield return (@"([^a]*)*", "bbbbbb", new[] { (0, 6), (6, 6) }); + yield return (@"([^a]*)*", "aaaaaa", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) }); + yield return (@"([^ab]*)*", "ababab", new[] { (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) }); + yield return (@"((..)|(.))", "", new ValueTuple[] { }); + yield return (@"((..)|(.))((..)|(.))", "", new ValueTuple[] { }); + yield return (@"((..)|(.))((..)|(.))((..)|(.))", "", new ValueTuple[] { }); + yield return (@"((..)|(.)){1}", "", new ValueTuple[] { }); + yield return (@"((..)|(.)){2}", "", new ValueTuple[] { }); + yield return (@"((..)|(.)){3}", "", new ValueTuple[] { }); + yield return (@"((..)|(.))*", "", new[] { (0, 0) }); + yield return (@"((..)|(.))((..)|(.))", "a", new ValueTuple[] { }); + yield return (@"((..)|(.))((..)|(.))((..)|(.))", "a", new ValueTuple[] { }); + yield return (@"((..)|(.)){2}", "a", new ValueTuple[] { }); + yield return (@"((..)|(.)){3}", "a", new ValueTuple[] { }); + yield return (@"((..)|(.))((..)|(.))((..)|(.))", "aa", new ValueTuple[] { }); + yield return (@"((..)|(.)){3}", "aa", new ValueTuple[] { }); + yield return (@"(a|ab|c|bcd){4,}(d*)", "ababcd", new ValueTuple[] { }); + yield return (@"(a|ab|c|bcd){4,10}(d*)", "ababcd", new ValueTuple[] { }); + yield return (@"(ab|a|c|bcd){4,}(d*)", "ababcd", new ValueTuple[] { }); + yield return (@"(ab|a|c|bcd){4,10}(d*)", "ababcd", new ValueTuple[] { }); + yield return (@"^abc", "abc", new[] { (0, 3) }); + yield return (@"^abc", "zabc", new ValueTuple[] { }); + yield return (@"abc", "xxxxxab", new ValueTuple[] { }); + yield return (@"(?i)[^x]", "x", new ValueTuple[] { }); + yield return (@"(?i)[^x]", "X", new ValueTuple[] { }); + yield return (@"[[:word:]]", "_", new ValueTuple[] { }); + yield return (@"ab?|$", "az", new[] { (0, 1), (2, 2) }); + yield return (@"^(.*?)(\n|\r\n?|$)", "ab\rcd", new[] { (0, 3) }); + yield return (@"z*azb", "azb", new[] { (0, 3) }); + yield return (@"(?i)\p{Ll}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 5) }); + yield return (@"1|2|3|4|5|6|7|8|9|10|int", "int", new[] { (0, 3) }); + yield return (@"^a[[:^space:]]", "a ", new ValueTuple[] { }); + yield return (@"^a[[:^space:]]", "foo boo a ", new ValueTuple[] { }); + yield return (@"^-[a-z]", "r-f", new ValueTuple[] { }); + yield return (@"(ABC|CDA|BC)X", "CDAX", new[] { (0, 4) }); + yield return (@"(aa$)?", "aaz", new[] { (0, 0), (1, 1), (2, 2), (3, 3) }); + yield return (@"ab??", "ab", new[] { (0, 1) }); + yield return (@".*abcd", "abcd", new[] { (0, 4) }); + yield return (@".*(?:abcd)+", "abcd", new[] { (0, 4) }); + yield return (@".*(?:abcd)+", "abcdabcd", new[] { (0, 8) }); + yield return (@".*(?:abcd)+", "abcdxabcd", new[] { (0, 9) }); + yield return (@".*x(?:abcd)+", "abcdxabcd", new[] { (0, 9) }); + yield return (@"[^abcd]*x(?:abcd)+", "abcdxabcd", new[] { (4, 9) }); + yield return (@".", "\xD4\xC2\x65\x2B\x0E\xFE", new[] { (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6) }); + yield return ("${2}\u00E4", "\xD4\xC2\x65\x2B\x0E\xFE", new ValueTuple[] { }); + yield return ("\u2603", "\u2603", new[] { (0, 1) }); + yield return ("\u2603+", "\u2603", new[] { (0, 1) }); + yield return ("(?i)\u2603+", "\u2603", new[] { (0, 1) }); + yield return ("[\u2603\u2160]+", "\u2603", new[] { (0, 1) }); + yield return ("(?i)\u0394", "\u03B4", new[] { (0, 1) }); + yield return (@"\p{Lu}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 4) }); + yield return (@"(?i)\p{Lu}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 5) }); + yield return (@"\p{L}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (0, 5) }); + yield return (@"\p{Ll}+", "\u039B\u0398\u0393\u0394\u03B1", new[] { (4, 5) }); + yield return (@"\w+", "d\u03B4d", new[] { (0, 3) }); + yield return (@"\w+", "\u2961", new ValueTuple[] { }); + yield return (@"\W+", "\u2961", new[] { (0, 1) }); + yield return (@"\d+", "1\u0968\u09699", new[] { (0, 4) }); + yield return (@"\d+", "\u2161", new ValueTuple[] { }); + yield return (@"\D+", "\u2161", new[] { (0, 1) }); + yield return (@"\s+", "\u1680", new[] { (0, 1) }); + yield return (@"\s+", "\u2603", new ValueTuple[] { }); + yield return (@"\S+", "\u2603", new[] { (0, 1) }); + yield return (@"\d\b", "6\u03B4", new ValueTuple[] { }); + yield return (@"\d\b", "6\u1680", new[] { (0, 1) }); + yield return (@"\d\B", "6\u03B4", new[] { (0, 1) }); + yield return (@"\d\B", "6\u1680", new ValueTuple[] { }); + } + [Theory] - [MemberData(nameof(MatchStartAndEndPositionsTestData))] - public async Task MatchStartAndEndPositions(RegexEngine engine, string pattern, string input, IEnumerable<(int, int)>? matchBoundaries) + [MemberData(nameof(MatchStartAndEndPositions_MemberData))] + public void MatchStartAndEndPositions(Regex regex, string input, IEnumerable<(int, int)>? matchBoundaries) { - Regex regex = await RegexHelpers.GetRegexAsync(engine, pattern); Match match = regex.Match(input); foreach ((int start, int end) in matchBoundaries) { diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 0e3a627d8f961..709f018ad2d47 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -280,6 +280,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/WebWorkerEventLoop.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/WebWorkerEventLoop.Browser.Threads.Mono.cs new file mode 100644 index 0000000000000..5a49c076271d1 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/WebWorkerEventLoop.Browser.Threads.Mono.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Tracing; +using System.Runtime.CompilerServices; + +namespace System.Threading; + +/// +/// Keep a pthread alive in its WebWorker after its pthread start function returns. +/// +internal static class WebWorkerEventLoop +{ + // FIXME: these keepalive calls could be qcalls with a SuppressGCTransitionAttribute + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void KeepalivePushInternal(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void KeepalivePopInternal(); + + /// + /// A keepalive token prevents a thread from shutting down even if it returns to the JS event + /// loop. A thread may want a keepalive token if it needs to allow JS code to run to settle JS + /// promises or execute JS timeout callbacks. + /// + internal sealed class KeepaliveToken + { + public bool Valid {get; private set; } + + private KeepaliveToken() { Valid = true; } + + /// + /// Decrement the Emscripten keepalive count. A thread with a zero keepalive count will + /// terminate when it returns from its start function or from an async invocation from the + /// JS event loop. + /// + internal void Pop() { + if (!Valid) + throw new InvalidOperationException(); + Valid = false; + KeepalivePopInternal(); + } + + internal static KeepaliveToken Create() + { + KeepalivePushInternal(); + return new KeepaliveToken(); + } + } + + /// + /// Increment the Emscripten keepalive count. A thread with a positive keepalive can return from its + /// thread start function or a JS event loop invocation and continue running in the JS event + /// loop. + /// + internal static KeepaliveToken KeepalivePush() => KeepaliveToken.Create(); + + /// + /// Start a thread that may be kept alive on its webworker after the start function returns, + /// if the emscripten keepalive count is positive. Once the thread returns to the JS event + /// loop it will be able to settle JS promises as well as run any queued managed async + /// callbacks. + /// + internal static void StartExitable(Thread thread, bool captureContext) + { + // don't support captureContext == true, for now, since it's + // not needed by PortableThreadPool.WorkerThread + if (captureContext) + throw new InvalidOperationException(); + // hack: threadpool threads are exitable, and nothing else is. + // see create_thread() in mono/metadata/threads.c + if (!thread.IsThreadPoolThread) + throw new InvalidOperationException(); + thread.UnsafeStart(); + } + + /// returns true if the current thread has unsettled JS Interop promises + private static bool HasUnsettledInteropPromises => HasUnsettledInteropPromisesNative(); + + // FIXME: this could be a qcall with a SuppressGCTransitionAttribute + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool HasUnsettledInteropPromisesNative(); + + /// returns true if the current WebWorker has JavaScript objects that depend on the + /// current managed thread. + /// + /// If this returns false, the runtime is allowed to allow the current managed thread + /// to exit and for the WebWorker to be recycled by Emscripten for another managed + /// thread. + internal static bool HasJavaScriptInteropDependents + { + // + // FIXME: + // https://github.com/dotnet/runtime/issues/85052 - unsettled promises are not the only relevant + // reasons for keeping a worker thread alive. We will need to add other conditions here. + get => HasUnsettledInteropPromises; + } +} diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index 5947224866181..7d59362968905 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -184,6 +184,13 @@ ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoSemaphore_DeleteInt ICALL_EXPORT gint32 ves_icall_System_Threading_LowLevelLifoSemaphore_TimedWaitInternal (gpointer sem_ptr, gint32 timeout_ms); ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal (gpointer sem_ptr, gint32 count); +/* include these declarations if we're in the threaded wasm runtime, or if we're building a wasm-targeting cross compiler and we need to support --print-icall-table */ +#if (defined(HOST_BROWSER) && !defined(DISABLE_THREADS)) || (defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP)) +ICALL_EXPORT MonoBoolean ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative(void); +ICALL_EXPORT void ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal (void); +ICALL_EXPORT void ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal (void); +#endif + #ifdef TARGET_AMD64 ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int abcd[4], int function_id, int subfunction_id); #endif diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 33e6de4f92780..33bf8de3e0fe3 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -573,6 +573,7 @@ NOHANDLES(ICALL(LIFOSEM_2, "InitInternal", ves_icall_System_Threading_LowLevelLi NOHANDLES(ICALL(LIFOSEM_3, "ReleaseInternal", ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal)) NOHANDLES(ICALL(LIFOSEM_4, "TimedWaitInternal", ves_icall_System_Threading_LowLevelLifoSemaphore_TimedWaitInternal)) + ICALL_TYPE(MONIT, "System.Threading.Monitor", MONIT_0) HANDLES(MONIT_0, "Enter", ves_icall_System_Threading_Monitor_Monitor_Enter, void, 1, (MonoObject)) HANDLES(MONIT_1, "InternalExit", mono_monitor_exit_icall, void, 1, (MonoObject)) @@ -597,6 +598,14 @@ HANDLES(THREAD_10, "SetState", ves_icall_System_Threading_Thread_SetState, void, HANDLES(THREAD_13, "StartInternal", ves_icall_System_Threading_Thread_StartInternal, void, 2, (MonoThreadObject, gint32)) NOHANDLES(ICALL(THREAD_14, "YieldInternal", ves_icall_System_Threading_Thread_YieldInternal)) +/* include these icalls if we're in the threaded wasm runtime, or if we're building a wasm-targeting cross compiler and we need to support --print-icall-table */ +#if (defined(HOST_BROWSER) && !defined(DISABLE_THREADS)) || (defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP)) +ICALL_TYPE(WEBWORKERLOOP, "System.Threading.WebWorkerEventLoop", WEBWORKERLOOP_1) +NOHANDLES(ICALL(WEBWORKERLOOP_1, "HasUnsettledInteropPromisesNative", ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative)) +NOHANDLES(ICALL(WEBWORKERLOOP_2, "KeepalivePopInternal", ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal)) +NOHANDLES(ICALL(WEBWORKERLOOP_3, "KeepalivePushInternal", ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal)) +#endif + ICALL_TYPE(TYPE, "System.Type", TYPE_1) HANDLES(TYPE_1, "internal_from_handle", ves_icall_System_Type_internal_from_handle, MonoReflectionType, 1, (MonoType_ref)) diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index a6bb38f55fea8..abe6cae20c51e 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -91,6 +91,11 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); #include #endif +#if defined(HOST_BROWSER) && !defined(DISABLE_THREADS) +#include +#include +#endif + #include "icall-decl.h" /*#define THREAD_DEBUG(a) do { a; } while (0)*/ @@ -1110,6 +1115,7 @@ fire_attach_profiler_events (MonoNativeThreadId tid) "Handle Stack")); } + static guint32 WINAPI start_wrapper_internal (StartInfo *start_info, gsize *stack_ptr) { @@ -4963,3 +4969,50 @@ ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal (gpointer sem_p LifoSemaphore *sem = (LifoSemaphore *)sem_ptr; mono_lifo_semaphore_release (sem, count); } + +#if defined(HOST_BROWSER) && !defined(DISABLE_THREADS) +void +ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal (void) +{ + emscripten_runtime_keepalive_push(); +} + +void +ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal (void) +{ + emscripten_runtime_keepalive_pop(); +} + +extern int mono_wasm_eventloop_has_unsettled_interop_promises(void); + +MonoBoolean +ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative(void) +{ + return !!mono_wasm_eventloop_has_unsettled_interop_promises(); +} + +#endif /* HOST_BROWSER && !DISABLE_THREADS */ + +/* for the AOT cross compiler with --print-icall-table these don't need to be callable, they just + * need to be defined */ +#if defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP) +void +ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal (void) +{ + g_assert_not_reached(); +} + +void +ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal (void) +{ + g_assert_not_reached(); +} + +MonoBoolean +ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative(void) +{ + g_assert_not_reached(); +} + +#endif /* defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP) */ + diff --git a/src/mono/mono/mini/cpu-arm64.mdesc b/src/mono/mono/mini/cpu-arm64.mdesc index a2af821a08171..cfb6dc74e4efd 100644 --- a/src/mono/mono/mini/cpu-arm64.mdesc +++ b/src/mono/mono/mini/cpu-arm64.mdesc @@ -533,6 +533,8 @@ create_scalar_unsafe_int: dest:x src1:i len:4 create_scalar_unsafe_float: dest:x src1:f len:4 arm64_bic: dest:x src1:x src2:x len:4 bitwise_select: dest:x src1:x src2:x src3:x len:12 +xextract_i8: dest:i src1:x src2:i len:16 +xextract_r8: dest:f src1:x src2:i len:16 arm64_xtn: dest:x src1:x len:4 arm64_xtn2: dest:x src1:x src2:x len:4 clob:1 arm64_fcvtn: dest:x src1:x len:4 diff --git a/src/mono/mono/mini/mini-arm64.c b/src/mono/mono/mini/mini-arm64.c index 062eeb7dd76cc..938b351d3f067 100644 --- a/src/mono/mono/mini/mini-arm64.c +++ b/src/mono/mono/mini/mini-arm64.c @@ -926,6 +926,42 @@ mono_arm_emit_ldrx (guint8 *code, int rt, int rn, int imm) return emit_ldrx (code, rt, rn, imm); } +static guint8* +emit_xextract_i8 (guint8* code, int dreg, int sreg1, int sreg2) +{ + guint8* ret = code; + /* code: */ + arm_cbnzw (ret, sreg2, code + 12 /*upper*/); + arm_neon_umov (ret, TYPE_I64, dreg, sreg1, 0); + arm_b (ret, code + 16 /*done*/); + /* upper: */ + arm_neon_umov (ret, TYPE_I64, dreg, sreg1, 1); + /* done: */ + return ret; +} + +static guint8* +emit_xextract_r8 (guint8* code, int dreg, int sreg1, int sreg2) +{ + guint8* ret = code; + + if (dreg == sreg1) { + /* code: */ + arm_cbzw (ret, sreg2, code + 8 /*done*/); + arm_neon_fdup_e (ret, VREG_FULL, TYPE_F64, dreg, sreg1, 1); + /* done: */ + } else { + /* code: */ + arm_cbnzw (ret, sreg2, code + 12 /*upper*/); + arm_neon_fdup_e (ret, VREG_FULL, TYPE_F64, dreg, sreg1, 0); + arm_b (ret, code + 16 /*done*/); + /* upper: */ + arm_neon_fdup_e (ret, VREG_FULL, TYPE_F64, dreg, sreg1, 1); + /* done: */ + } + return ret; +} + static guint8* emit_call (MonoCompile *cfg, guint8* code, MonoJumpInfoType patch_type, gconstpointer data) { @@ -3829,6 +3865,22 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) arm_neon_fdup_e (code, VREG_FULL, t, dreg, sreg1, ins->inst_c0); } break; + + case OP_XEXTRACT_I8: + code = emit_xextract_i8 (code, dreg, sreg1, sreg2); + break; + + case OP_XEXTRACT_R8: + code = emit_xextract_r8 (code, dreg, sreg1, sreg2); + break; + + case OP_XEXTRACT_I1: + case OP_XEXTRACT_I2: + case OP_XEXTRACT_I4: + case OP_XEXTRACT_R4: + g_assert_not_reached (); + break; + case OP_INSERT_I1: case OP_INSERT_I2: case OP_INSERT_I4: diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index a01b2a7a2c095..6e6afa17eb34e 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -874,6 +874,38 @@ type_to_insert_op (MonoTypeEnum type) } } +static int +type_to_width_log2 (MonoTypeEnum type) +{ + switch (type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + return 0; + case MONO_TYPE_I2: + case MONO_TYPE_U2: + return 1; + case MONO_TYPE_I4: + case MONO_TYPE_U4: + return 2; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + return 3; + case MONO_TYPE_R4: + return 2; + case MONO_TYPE_R8: + return 3; + case MONO_TYPE_I: + case MONO_TYPE_U: +#if TARGET_SIZEOF_VOID_P == 8 + return 3; +#else + return 2; +#endif + default: + g_assert_not_reached (); + } +} + typedef struct { const char *name; MonoCPUFeatures feature; @@ -1324,7 +1356,6 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi case SN_ConvertToUInt32: case SN_ConvertToUInt64: case SN_Create: - case SN_GetElement: case SN_GetLower: case SN_GetUpper: case SN_Shuffle: @@ -1702,10 +1733,49 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi elems = 4; } + if (args [1]->opcode == OP_ICONST) { + // If the index is provably a constant, we can generate vastly better code. + int index = args[1]->inst_c0; + + if (index < 0 || index >= elems) { + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, args [1]->dreg, elems); + MONO_EMIT_NEW_COND_EXC (cfg, GE_UN, "ArgumentOutOfRangeException"); + } + + // Bounds check is elided if we know the index is safe. + int extract_op = type_to_extract_op (arg0_type); + MonoInst* ret = emit_simd_ins (cfg, args [0]->klass, extract_op, args [0]->dreg, -1); + ret->inst_c0 = index; + ret->inst_c1 = fsig->ret->type; + return ret; + } + + // Bounds check needed in non-const case. MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, args [1]->dreg, elems); MONO_EMIT_NEW_COND_EXC (cfg, GE_UN, "ArgumentOutOfRangeException"); - int extract_op = type_to_xextract_op (arg0_type); - return emit_simd_ins_for_sig (cfg, klass, extract_op, -1, arg0_type, fsig, args); + + if (COMPILE_LLVM(cfg) || type_to_width_log2 (arg0_type) == 3) { + // Use optimized paths for 64-bit extractions or whatever LLVM yields if enabled. + int extract_op = type_to_xextract_op (arg0_type); + return emit_simd_ins_for_sig (cfg, klass, extract_op, -1, arg0_type, fsig, args); + } else { + // Spill the vector reg. + // Load back from spilled + index << elem_size_log2 + // TODO: on x86, use a LEA + MonoInst* spilled; + NEW_VARLOADA_VREG (cfg, spilled, args [0]->dreg, fsig->params [0]); + MONO_ADD_INS (cfg->cbb, spilled); + int offset_reg = alloc_lreg (cfg); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, args [1]->dreg, type_to_width_log2 (arg0_type)); + int addr_reg = alloc_preg (cfg); + MONO_EMIT_NEW_BIALU(cfg, OP_PADD, addr_reg, spilled->dreg, offset_reg); + MonoInst* ret; + int dreg = arg0_type == MONO_TYPE_R4 ? alloc_freg (cfg) : alloc_ireg (cfg); + NEW_LOAD_MEMBASE (cfg, ret, mono_type_to_load_membase (cfg, fsig->ret), dreg, addr_reg, 0); + MONO_ADD_INS (cfg->cbb, ret); + return ret; + } + break; } case SN_GetLower: case SN_GetUpper: { diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index e6139d3537519..2403db26b3205 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -109,6 +109,8 @@ if (monoWasmThreads) { linked_functions = [...linked_functions, /// mono-threads-wasm.c "mono_wasm_pthread_on_pthread_attached", + // threads.c + "mono_wasm_eventloop_has_unsettled_interop_promises", // diagnostics_server.c "mono_wasm_diagnostic_server_on_server_thread_created", "mono_wasm_diagnostic_server_on_runtime_server_init", diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts index ae0305551a42c..ed75ea9df9a66 100644 --- a/src/mono/wasm/runtime/exports-linker.ts +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -11,6 +11,7 @@ import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter"; import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call"; import { mono_wasm_marshal_promise } from "./marshal-to-js"; +import { mono_wasm_eventloop_has_unsettled_interop_promises } from "./pthreads/shared/eventloop"; import { mono_wasm_pthread_on_pthread_attached } from "./pthreads/worker"; import { mono_set_timeout, schedule_background_exec } from "./scheduling"; import { mono_wasm_asm_loaded } from "./startup"; @@ -33,6 +34,8 @@ import { mono_wasm_change_case, mono_wasm_change_case_invariant, mono_wasm_compa const mono_wasm_threads_exports = !MonoWasmThreads ? undefined : { // mono-threads-wasm.c mono_wasm_pthread_on_pthread_attached, + // threads.c + mono_wasm_eventloop_has_unsettled_interop_promises, // diagnostics_server.c mono_wasm_diagnostic_server_on_server_thread_created, mono_wasm_diagnostic_server_on_runtime_server_init, diff --git a/src/mono/wasm/runtime/gc-handles.ts b/src/mono/wasm/runtime/gc-handles.ts index 465bfc9264c07..ce83039e4e366 100644 --- a/src/mono/wasm/runtime/gc-handles.ts +++ b/src/mono/wasm/runtime/gc-handles.ts @@ -49,8 +49,8 @@ export function mono_wasm_get_js_handle(js_obj: any): JSHandle { js_obj[cs_owned_js_handle_symbol] = js_handle; } // else - // The consequence of not adding the cs_owned_js_handle_symbol is, that we could have multiple JSHandles and multiple proxy instances. - // Throwing exception would prevent us from creating any proxy of non-extensible things. + // The consequence of not adding the cs_owned_js_handle_symbol is, that we could have multiple JSHandles and multiple proxy instances. + // Throwing exception would prevent us from creating any proxy of non-extensible things. // If we have weakmap instead, we would pay the price of the lookup for all proxies, not just non-extensible objects. return js_handle as JSHandle; @@ -131,3 +131,4 @@ export function _lookup_js_owned_object(gc_handle: GCHandle): any { } return null; } + diff --git a/src/mono/wasm/runtime/marshal-to-cs.ts b/src/mono/wasm/runtime/marshal-to-cs.ts index 607cfdcb85586..2e1ff002741a2 100644 --- a/src/mono/wasm/runtime/marshal-to-cs.ts +++ b/src/mono/wasm/runtime/marshal-to-cs.ts @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import monoWasmThreads from "consts:monoWasmThreads"; import { isThenable } from "./cancelable-promise"; import cwraps from "./cwraps"; import { assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles"; @@ -18,6 +19,8 @@ import { _zero_region } from "./memory"; import { js_string_to_mono_string_root } from "./strings"; import { mono_assert, GCHandle, GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types"; import { TypedArray } from "./types/emscripten"; +import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads/shared/eventloop"; + export function initialize_marshalers_to_cs(): void { if (js_to_cs_marshalers.size == 0) { @@ -306,10 +309,17 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: const holder = new TaskCallbackHolder(value); setup_managed_proxy(holder, gc_handle); + if (monoWasmThreads) + addUnsettledPromise(); + value.then(data => { + if (monoWasmThreads) + settleUnsettledPromise(); runtimeHelpers.javaScriptExports.complete_task(gc_handle, null, data, res_converter || _marshal_cs_object_to_cs); teardown_managed_proxy(holder, gc_handle); // this holds holder alive for finalizer, until the promise is freed, (holding promise instead would not work) }).catch(reason => { + if (monoWasmThreads) + settleUnsettledPromise(); runtimeHelpers.javaScriptExports.complete_task(gc_handle, reason, null, undefined); teardown_managed_proxy(holder, gc_handle); // this holds holder alive for finalizer, until the promise is freed }); diff --git a/src/mono/wasm/runtime/pthreads/shared/eventloop.ts b/src/mono/wasm/runtime/pthreads/shared/eventloop.ts new file mode 100644 index 0000000000000..19533a27ee5f8 --- /dev/null +++ b/src/mono/wasm/runtime/pthreads/shared/eventloop.ts @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +let _per_thread_unsettled_promise_count = 0; + +export function addUnsettledPromise() { + _per_thread_unsettled_promise_count++; +} + +export function settleUnsettledPromise() { + _per_thread_unsettled_promise_count--; +} + +/// Called from the C# threadpool worker loop to find out if there are any +/// unsettled JS promises that need to keep the worker alive +export function mono_wasm_eventloop_has_unsettled_interop_promises(): boolean { + return _per_thread_unsettled_promise_count > 0; +} diff --git a/src/tools/illink/src/analyzer/README.md b/src/tools/illink/src/analyzer/README.md index d4a7e0624a707..89371f2f375a6 100644 --- a/src/tools/illink/src/analyzer/README.md +++ b/src/tools/illink/src/analyzer/README.md @@ -13,7 +13,15 @@ etc. The edges represent the dependencies. The ILLink analyzer needs a ILLink dependencies file as an input. It can be retrieved by enabling dependencies dumping during trimming of a -Xamarin.Android, Xamarin.iOS, or .NET SDK style project. +project. + +For console .NET projects you need to publish the application +with trimming enabled and use the `_TrimmerDumpDependencies` property: + +```dotnet publish /p:PublishTrimmed=true /p:_TrimmerDumpDependencies=true``` + +In this case the dependencies file will be in +`obj////linked/linker-dependencies.xml`. For Xamarin.Android and Xamarin.iOS, that can be done on the command line by setting `LinkerDumpDependencies` property to `true` and building the