Regex.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. /*
  2. * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "LibRegex/RegexMatcher.h"
  7. #include <LibTest/TestCase.h> // import first, to prevent warning of VERIFY* redefinition
  8. #include <AK/StringBuilder.h>
  9. #include <LibRegex/Regex.h>
  10. #include <LibRegex/RegexDebug.h>
  11. #include <stdio.h>
  12. static ECMAScriptOptions match_test_api_options(const ECMAScriptOptions options)
  13. {
  14. return options;
  15. }
  16. static PosixOptions match_test_api_options(const PosixOptions options)
  17. {
  18. return options;
  19. }
  20. template<typename... Flags>
  21. static constexpr ECMAScriptFlags combine_flags(Flags&&... flags) requires((IsSame<Flags, ECMAScriptFlags> && ...))
  22. {
  23. return static_cast<ECMAScriptFlags>((static_cast<regex::FlagsUnderlyingType>(flags) | ...));
  24. }
  25. TEST_CASE(regex_options_ecmascript)
  26. {
  27. ECMAScriptOptions eo;
  28. eo |= ECMAScriptFlags::Global;
  29. EXPECT(eo & ECMAScriptFlags::Global);
  30. EXPECT(!(eo & ECMAScriptFlags::Insensitive));
  31. eo = match_test_api_options(ECMAScriptFlags::Global | ECMAScriptFlags::Insensitive | ECMAScriptFlags::Sticky);
  32. EXPECT(eo & ECMAScriptFlags::Global);
  33. EXPECT(eo & ECMAScriptFlags::Insensitive);
  34. EXPECT(eo & ECMAScriptFlags::Sticky);
  35. EXPECT(!(eo & ECMAScriptFlags::Unicode));
  36. EXPECT(!(eo & ECMAScriptFlags::Multiline));
  37. EXPECT(!(eo & ECMAScriptFlags::SingleLine));
  38. eo &= ECMAScriptFlags::Insensitive;
  39. EXPECT(!(eo & ECMAScriptFlags::Global));
  40. EXPECT(eo & ECMAScriptFlags::Insensitive);
  41. EXPECT(!(eo & ECMAScriptFlags::Multiline));
  42. eo &= ECMAScriptFlags::Sticky;
  43. EXPECT(!(eo & ECMAScriptFlags::Global));
  44. EXPECT(!(eo & ECMAScriptFlags::Insensitive));
  45. EXPECT(!(eo & ECMAScriptFlags::Multiline));
  46. EXPECT(!(eo & ECMAScriptFlags::Sticky));
  47. eo = ~ECMAScriptFlags::Insensitive;
  48. EXPECT(eo & ECMAScriptFlags::Global);
  49. EXPECT(!(eo & ECMAScriptFlags::Insensitive));
  50. EXPECT(eo & ECMAScriptFlags::Multiline);
  51. EXPECT(eo & ECMAScriptFlags::Sticky);
  52. }
  53. TEST_CASE(regex_options_posix)
  54. {
  55. PosixOptions eo;
  56. eo |= PosixFlags::Global;
  57. EXPECT(eo & PosixFlags::Global);
  58. EXPECT(!(eo & PosixFlags::Insensitive));
  59. eo = match_test_api_options(PosixFlags::Global | PosixFlags::Insensitive | PosixFlags::MatchNotBeginOfLine);
  60. EXPECT(eo & PosixFlags::Global);
  61. EXPECT(eo & PosixFlags::Insensitive);
  62. EXPECT(eo & PosixFlags::MatchNotBeginOfLine);
  63. EXPECT(!(eo & PosixFlags::Unicode));
  64. EXPECT(!(eo & PosixFlags::Multiline));
  65. eo &= PosixFlags::Insensitive;
  66. EXPECT(!(eo & PosixFlags::Global));
  67. EXPECT(eo & PosixFlags::Insensitive);
  68. EXPECT(!(eo & PosixFlags::Multiline));
  69. eo &= PosixFlags::MatchNotBeginOfLine;
  70. EXPECT(!(eo & PosixFlags::Global));
  71. EXPECT(!(eo & PosixFlags::Insensitive));
  72. EXPECT(!(eo & PosixFlags::Multiline));
  73. eo = ~PosixFlags::Insensitive;
  74. EXPECT(eo & PosixFlags::Global);
  75. EXPECT(!(eo & PosixFlags::Insensitive));
  76. EXPECT(eo & PosixFlags::Multiline);
  77. }
  78. TEST_CASE(regex_lexer)
  79. {
  80. Lexer l("/[.*+?^${}()|[\\]\\\\]/g");
  81. EXPECT(l.next().type() == regex::TokenType::Slash);
  82. EXPECT(l.next().type() == regex::TokenType::LeftBracket);
  83. EXPECT(l.next().type() == regex::TokenType::Period);
  84. EXPECT(l.next().type() == regex::TokenType::Asterisk);
  85. EXPECT(l.next().type() == regex::TokenType::Plus);
  86. EXPECT(l.next().type() == regex::TokenType::Questionmark);
  87. EXPECT(l.next().type() == regex::TokenType::Circumflex);
  88. EXPECT(l.next().type() == regex::TokenType::Dollar);
  89. EXPECT(l.next().type() == regex::TokenType::LeftCurly);
  90. EXPECT(l.next().type() == regex::TokenType::RightCurly);
  91. EXPECT(l.next().type() == regex::TokenType::LeftParen);
  92. EXPECT(l.next().type() == regex::TokenType::RightParen);
  93. EXPECT(l.next().type() == regex::TokenType::Pipe);
  94. EXPECT(l.next().type() == regex::TokenType::LeftBracket);
  95. EXPECT(l.next().type() == regex::TokenType::EscapeSequence);
  96. EXPECT(l.next().type() == regex::TokenType::EscapeSequence);
  97. EXPECT(l.next().type() == regex::TokenType::RightBracket);
  98. EXPECT(l.next().type() == regex::TokenType::Slash);
  99. EXPECT(l.next().type() == regex::TokenType::Char);
  100. }
  101. TEST_CASE(parser_error_parens)
  102. {
  103. String pattern = "test()test";
  104. Lexer l(pattern);
  105. PosixExtendedParser p(l);
  106. p.parse();
  107. EXPECT(p.has_error());
  108. EXPECT(p.error() == Error::EmptySubExpression);
  109. }
  110. TEST_CASE(parser_error_special_characters_used_at_wrong_place)
  111. {
  112. String pattern;
  113. Vector<char, 5> chars = { '*', '+', '?', '{' };
  114. StringBuilder b;
  115. Lexer l;
  116. PosixExtended p(l);
  117. for (auto& ch : chars) {
  118. // First in ere
  119. b.clear();
  120. b.append(ch);
  121. pattern = b.build();
  122. l.set_source(pattern);
  123. p.parse();
  124. EXPECT(p.has_error());
  125. EXPECT(p.error() == Error::InvalidRepetitionMarker);
  126. // After vertical line
  127. b.clear();
  128. b.append("a|");
  129. b.append(ch);
  130. pattern = b.build();
  131. l.set_source(pattern);
  132. p.parse();
  133. EXPECT(p.has_error());
  134. EXPECT(p.error() == Error::InvalidRepetitionMarker);
  135. // After circumflex
  136. b.clear();
  137. b.append("^");
  138. b.append(ch);
  139. pattern = b.build();
  140. l.set_source(pattern);
  141. p.parse();
  142. EXPECT(p.has_error());
  143. EXPECT(p.error() == Error::InvalidRepetitionMarker);
  144. // After dollar
  145. b.clear();
  146. b.append("$");
  147. b.append(ch);
  148. pattern = b.build();
  149. l.set_source(pattern);
  150. p.parse();
  151. EXPECT(p.has_error());
  152. EXPECT(p.error() == Error::InvalidRepetitionMarker);
  153. // After left parens
  154. b.clear();
  155. b.append("(");
  156. b.append(ch);
  157. b.append(")");
  158. pattern = b.build();
  159. l.set_source(pattern);
  160. p.parse();
  161. EXPECT(p.has_error());
  162. EXPECT(p.error() == Error::InvalidRepetitionMarker);
  163. }
  164. }
  165. TEST_CASE(parser_error_vertical_line_used_at_wrong_place)
  166. {
  167. Lexer l;
  168. PosixExtended p(l);
  169. // First in ere
  170. l.set_source("|asdf");
  171. p.parse();
  172. EXPECT(p.has_error());
  173. EXPECT(p.error() == Error::EmptySubExpression);
  174. // Last in ere
  175. l.set_source("asdf|");
  176. p.parse();
  177. EXPECT(p.has_error());
  178. EXPECT(p.error() == Error::EmptySubExpression);
  179. // After left parens
  180. l.set_source("(|asdf)");
  181. p.parse();
  182. EXPECT(p.has_error());
  183. EXPECT(p.error() == Error::EmptySubExpression);
  184. // Proceed right parens
  185. l.set_source("(asdf)|");
  186. p.parse();
  187. EXPECT(p.has_error());
  188. EXPECT(p.error() == Error::EmptySubExpression);
  189. }
  190. TEST_CASE(catch_all_first)
  191. {
  192. Regex<PosixExtended> re("^.*$");
  193. RegexResult m;
  194. re.match("Hello World", m);
  195. EXPECT(m.count == 1);
  196. EXPECT(re.match("Hello World", m));
  197. }
  198. TEST_CASE(catch_all)
  199. {
  200. Regex<PosixExtended> re("^.*$", PosixFlags::Global);
  201. EXPECT(re.has_match("Hello World"));
  202. EXPECT(re.match("Hello World").success);
  203. EXPECT(re.match("Hello World").count == 1);
  204. EXPECT(has_match("Hello World", re));
  205. auto res = match("Hello World", re);
  206. EXPECT(res.success);
  207. EXPECT(res.count == 1);
  208. EXPECT(res.matches.size() == 1);
  209. EXPECT(res.matches.first().view == "Hello World");
  210. }
  211. TEST_CASE(catch_all_again)
  212. {
  213. Regex<PosixExtended> re("^.*$", PosixFlags::Extra);
  214. EXPECT_EQ(has_match("Hello World", re), true);
  215. }
  216. TEST_CASE(char_utf8)
  217. {
  218. Regex<PosixExtended> re("😀");
  219. RegexResult result;
  220. EXPECT_EQ((result = match(Utf8View { "Привет, мир! 😀 γειά σου κόσμος 😀 こんにちは世界" }, re, PosixFlags::Global)).success, true);
  221. EXPECT_EQ(result.count, 2u);
  222. }
  223. TEST_CASE(catch_all_newline)
  224. {
  225. Regex<PosixExtended> re("^.*$", PosixFlags::Multiline | PosixFlags::StringCopyMatches);
  226. RegexResult result;
  227. auto lambda = [&result, &re]() {
  228. String aaa = "Hello World\nTest\n1234\n";
  229. result = match(aaa, re);
  230. EXPECT_EQ(result.success, true);
  231. };
  232. lambda();
  233. EXPECT_EQ(result.count, 3u);
  234. EXPECT_EQ(result.matches.at(0).view, "Hello World");
  235. EXPECT_EQ(result.matches.at(1).view, "Test");
  236. EXPECT_EQ(result.matches.at(2).view, "1234");
  237. }
  238. TEST_CASE(catch_all_newline_view)
  239. {
  240. Regex<PosixExtended> re("^.*$", PosixFlags::Multiline);
  241. RegexResult result;
  242. String aaa = "Hello World\nTest\n1234\n";
  243. result = match(aaa, re);
  244. EXPECT_EQ(result.success, true);
  245. EXPECT_EQ(result.count, 3u);
  246. String str = "Hello World";
  247. EXPECT_EQ(result.matches.at(0).view, str.view());
  248. EXPECT_EQ(result.matches.at(1).view, "Test");
  249. EXPECT_EQ(result.matches.at(2).view, "1234");
  250. }
  251. TEST_CASE(catch_all_newline_2)
  252. {
  253. Regex<PosixExtended> re("^.*$");
  254. RegexResult result;
  255. result = match("Hello World\nTest\n1234\n", re, PosixFlags::Multiline | PosixFlags::StringCopyMatches);
  256. EXPECT_EQ(result.success, true);
  257. EXPECT_EQ(result.count, 3u);
  258. EXPECT_EQ(result.matches.at(0).view, "Hello World");
  259. EXPECT_EQ(result.matches.at(1).view, "Test");
  260. EXPECT_EQ(result.matches.at(2).view, "1234");
  261. result = match("Hello World\nTest\n1234\n", re);
  262. EXPECT_EQ(result.success, true);
  263. EXPECT_EQ(result.count, 1u);
  264. EXPECT_EQ(result.matches.at(0).view, "Hello World\nTest\n1234\n");
  265. }
  266. TEST_CASE(match_all_character_class)
  267. {
  268. Regex<PosixExtended> re("[[:alpha:]]");
  269. String str = "[Window]\nOpacity=255\nAudibleBeep=0\n";
  270. RegexResult result = match(str, re, PosixFlags::Global | PosixFlags::StringCopyMatches);
  271. EXPECT_EQ(result.success, true);
  272. EXPECT_EQ(result.count, 24u);
  273. EXPECT_EQ(result.matches.at(0).view, "W");
  274. EXPECT_EQ(result.matches.at(1).view, "i");
  275. EXPECT_EQ(result.matches.at(2).view, "n");
  276. }
  277. TEST_CASE(match_character_class_with_assertion)
  278. {
  279. Regex<PosixExtended> re("[[:alpha:]]+$");
  280. String str = "abcdef";
  281. RegexResult result = match(str, re);
  282. EXPECT_EQ(result.success, true);
  283. EXPECT_EQ(result.count, 1u);
  284. }
  285. TEST_CASE(example_for_git_commit)
  286. {
  287. Regex<PosixExtended> re("^.*$");
  288. auto result = re.match("Well, hello friends!\nHello World!");
  289. EXPECT(result.success);
  290. EXPECT(result.count == 1);
  291. EXPECT(result.matches.at(0).view.starts_with("Well"));
  292. EXPECT(result.matches.at(0).view.length() == 33);
  293. EXPECT(re.has_match("Well,...."));
  294. result = re.match("Well, hello friends!\nHello World!", PosixFlags::Multiline);
  295. EXPECT(result.success);
  296. EXPECT(result.count == 2);
  297. EXPECT(result.matches.at(0).view == "Well, hello friends!");
  298. EXPECT(result.matches.at(1).view == "Hello World!");
  299. }
  300. TEST_CASE(email_address)
  301. {
  302. Regex<PosixExtended> re("^[A-Z0-9a-z._%+-]{1,64}@([A-Za-z0-9-]{1,63}\\.){1,125}[A-Za-z]{2,63}$");
  303. EXPECT(re.has_match("hello.world@domain.tld"));
  304. EXPECT(re.has_match("this.is.a.very_long_email_address@world.wide.web"));
  305. }
  306. TEST_CASE(ini_file_entries)
  307. {
  308. Regex<PosixExtended> re("[[:alpha:]]*=([[:digit:]]*)|\\[(.*)\\]");
  309. RegexResult result;
  310. if constexpr (REGEX_DEBUG) {
  311. RegexDebug regex_dbg(stderr);
  312. regex_dbg.print_raw_bytecode(re);
  313. regex_dbg.print_header();
  314. regex_dbg.print_bytecode(re);
  315. }
  316. String haystack = "[Window]\nOpacity=255\nAudibleBeep=0\n";
  317. EXPECT_EQ(re.search(haystack.view(), result, PosixFlags::Multiline), true);
  318. EXPECT_EQ(result.count, 3u);
  319. if constexpr (REGEX_DEBUG) {
  320. for (auto& v : result.matches)
  321. fprintf(stderr, "%s\n", v.view.to_string().characters());
  322. }
  323. EXPECT_EQ(result.matches.at(0).view, "[Window]");
  324. EXPECT_EQ(result.capture_group_matches.at(0).at(0).view, "Window");
  325. EXPECT_EQ(result.matches.at(1).view, "Opacity=255");
  326. EXPECT_EQ(result.matches.at(1).line, 1u);
  327. EXPECT_EQ(result.matches.at(1).column, 0u);
  328. EXPECT_EQ(result.capture_group_matches.at(1).at(0).view, "255");
  329. EXPECT_EQ(result.capture_group_matches.at(1).at(0).line, 1u);
  330. EXPECT_EQ(result.capture_group_matches.at(1).at(0).column, 8u);
  331. EXPECT_EQ(result.matches.at(2).view, "AudibleBeep=0");
  332. EXPECT_EQ(result.capture_group_matches.at(2).at(0).view, "0");
  333. EXPECT_EQ(result.capture_group_matches.at(2).at(0).line, 2u);
  334. EXPECT_EQ(result.capture_group_matches.at(2).at(0).column, 12u);
  335. }
  336. TEST_CASE(ini_file_entries2)
  337. {
  338. Regex<PosixExtended> re("[[:alpha:]]*=([[:digit:]]*)");
  339. RegexResult result;
  340. String haystack = "ViewMode=Icon";
  341. EXPECT_EQ(re.match(haystack.view(), result), false);
  342. EXPECT_EQ(result.count, 0u);
  343. EXPECT_EQ(re.search(haystack.view(), result), true);
  344. EXPECT_EQ(result.count, 1u);
  345. }
  346. TEST_CASE(named_capture_group)
  347. {
  348. Regex<PosixExtended> re("[[:alpha:]]*=(?<Test>[[:digit:]]*)");
  349. RegexResult result;
  350. if constexpr (REGEX_DEBUG) {
  351. RegexDebug regex_dbg(stderr);
  352. regex_dbg.print_raw_bytecode(re);
  353. regex_dbg.print_header();
  354. regex_dbg.print_bytecode(re);
  355. }
  356. String haystack = "[Window]\nOpacity=255\nAudibleBeep=0\n";
  357. EXPECT_EQ(re.search(haystack, result, PosixFlags::Multiline), true);
  358. EXPECT_EQ(result.count, 2u);
  359. EXPECT_EQ(result.matches.at(0).view, "Opacity=255");
  360. EXPECT_EQ(result.capture_group_matches.at(0).at(0).view, "255");
  361. EXPECT_EQ(result.capture_group_matches.at(0).at(0).capture_group_name, "Test");
  362. EXPECT_EQ(result.matches.at(1).view, "AudibleBeep=0");
  363. EXPECT_EQ(result.capture_group_matches.at(1).at(0).view, "0");
  364. EXPECT_EQ(result.capture_group_matches.at(1).at(0).capture_group_name, "Test");
  365. }
  366. TEST_CASE(ecma262_named_capture_group_with_dollar_sign)
  367. {
  368. Regex<ECMA262> re("[a-zA-Z]*=(?<$Test$>[0-9]*)");
  369. RegexResult result;
  370. if constexpr (REGEX_DEBUG) {
  371. RegexDebug regex_dbg(stderr);
  372. regex_dbg.print_raw_bytecode(re);
  373. regex_dbg.print_header();
  374. regex_dbg.print_bytecode(re);
  375. }
  376. String haystack = "[Window]\nOpacity=255\nAudibleBeep=0\n";
  377. EXPECT_EQ(re.search(haystack, result, ECMAScriptFlags::Multiline), true);
  378. EXPECT_EQ(result.count, 2u);
  379. EXPECT_EQ(result.matches.at(0).view, "Opacity=255");
  380. EXPECT_EQ(result.capture_group_matches.at(0).at(0).view, "255");
  381. EXPECT_EQ(result.capture_group_matches.at(0).at(0).capture_group_name, "$Test$");
  382. EXPECT_EQ(result.matches.at(1).view, "AudibleBeep=0");
  383. EXPECT_EQ(result.capture_group_matches.at(1).at(0).view, "0");
  384. EXPECT_EQ(result.capture_group_matches.at(1).at(0).capture_group_name, "$Test$");
  385. }
  386. TEST_CASE(a_star)
  387. {
  388. Regex<PosixExtended> re("a*");
  389. RegexResult result;
  390. if constexpr (REGEX_DEBUG) {
  391. RegexDebug regex_dbg(stderr);
  392. regex_dbg.print_raw_bytecode(re);
  393. regex_dbg.print_header();
  394. regex_dbg.print_bytecode(re);
  395. }
  396. String haystack = "[Window]\nOpacity=255\nAudibleBeep=0\n";
  397. EXPECT_EQ(re.search(haystack.view(), result, PosixFlags::Multiline), true);
  398. EXPECT_EQ(result.count, 32u);
  399. if (result.count == 32u) {
  400. EXPECT_EQ(result.matches.at(0).view.length(), 0u);
  401. EXPECT_EQ(result.matches.at(10).view.length(), 1u);
  402. EXPECT_EQ(result.matches.at(10).view, "a");
  403. EXPECT_EQ(result.matches.at(31).view.length(), 0u);
  404. }
  405. }
  406. TEST_CASE(simple_period_end_benchmark)
  407. {
  408. Regex<PosixExtended> re("hello.$");
  409. RegexResult m;
  410. EXPECT_EQ(re.search("Hello1", m), false);
  411. EXPECT_EQ(re.search("hello1hello1", m), true);
  412. EXPECT_EQ(re.search("hello2hell", m), false);
  413. EXPECT_EQ(re.search("hello?", m), true);
  414. }
  415. TEST_CASE(ECMA262_parse)
  416. {
  417. struct _test {
  418. StringView pattern;
  419. regex::Error expected_error { regex::Error::NoError };
  420. regex::ECMAScriptFlags flags {};
  421. };
  422. constexpr _test tests[] {
  423. { "^hello.$"sv },
  424. { "^(hello.)$"sv },
  425. { "^h{0,1}ello.$"sv },
  426. { "^hello\\W$"sv },
  427. { "^hell\\w.$"sv },
  428. { "^hell\\x6f1$"sv }, // ^hello1$
  429. { "^hel(?:l\\w).$"sv },
  430. { "^hel(?<LO>l\\w).$"sv },
  431. { "^[-a-zA-Z\\w\\s]+$"sv },
  432. { "\\bhello\\B"sv },
  433. { "^[\\w+/_-]+[=]{0,2}$"sv }, // #4189
  434. { "^(?:[^<]*(<[\\w\\W]+>)[^>]*$|#([\\w\\-]*)$)"sv }, // #4189
  435. { "\\/"sv }, // #4189
  436. { ",/=-:"sv }, // #4243
  437. { "\\x"sv }, // Even invalid escapes are allowed if ~unicode.
  438. { "\\x1"sv }, // Even invalid escapes are allowed if ~unicode.
  439. { "\\x1"sv, regex::Error::InvalidPattern, regex::ECMAScriptFlags::Unicode },
  440. { "\\x11"sv },
  441. { "\\x11"sv, regex::Error::NoError, regex::ECMAScriptFlags::Unicode },
  442. { "\\"sv, regex::Error::InvalidTrailingEscape },
  443. { "(?"sv, regex::Error::InvalidCaptureGroup },
  444. { "\\u1234"sv, regex::Error::NoError, regex::ECMAScriptFlags::Unicode },
  445. { "[\\u1234]"sv, regex::Error::NoError, regex::ECMAScriptFlags::Unicode },
  446. { "\\u1"sv, regex::Error::InvalidPattern, regex::ECMAScriptFlags::Unicode },
  447. { "[\\u1]"sv, regex::Error::InvalidPattern, regex::ECMAScriptFlags::Unicode },
  448. { ",(?"sv, regex::Error::InvalidCaptureGroup }, // #4583
  449. { "{1}"sv, regex::Error::InvalidPattern },
  450. { "{1,2}"sv, regex::Error::InvalidPattern },
  451. { "\\uxxxx"sv, regex::Error::NoError },
  452. { "\\uxxxx"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  453. { "\\ud83d"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  454. { "\\ud83d\\uxxxx"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  455. { "\\u{0}"sv },
  456. { "\\u{0}"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  457. { "\\u{10ffff}"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  458. { "\\u{10ffff"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  459. { "\\u{10ffffx"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  460. { "\\u{110000}"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  461. { "\\p"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  462. { "\\p{"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  463. { "\\p{}"sv, regex::Error::InvalidNameForProperty, ECMAScriptFlags::Unicode },
  464. { "\\p{AsCiI}"sv, regex::Error::InvalidNameForProperty, ECMAScriptFlags::Unicode },
  465. { "\\p{hello friends}"sv, regex::Error::InvalidNameForProperty, ECMAScriptFlags::Unicode },
  466. { "\\p{Prepended_Concatenation_Mark}"sv, regex::Error::InvalidNameForProperty, ECMAScriptFlags::Unicode },
  467. { "\\p{ASCII}"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  468. { "\\\\p{1}"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  469. { "\\\\p{AsCiI}"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  470. { "\\\\p{ASCII}"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  471. { "\\c"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  472. { "\\c"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  473. { "[\\c]"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  474. { "[\\c]"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  475. { "\\c`"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  476. { "\\c`"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  477. { "[\\c`]"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  478. { "[\\c`]"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  479. { "\\A"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  480. { "\\A"sv, regex::Error::InvalidCharacterClass, ECMAScriptFlags::Unicode },
  481. { "[\\A]"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  482. { "[\\A]"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  483. { "\\0"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  484. { "\\0"sv, regex::Error::NoError, combine_flags(ECMAScriptFlags::Unicode, ECMAScriptFlags::BrowserExtended) },
  485. { "\\00"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  486. { "\\00"sv, regex::Error::InvalidCharacterClass, combine_flags(ECMAScriptFlags::Unicode, ECMAScriptFlags::BrowserExtended) },
  487. { "[\\0]"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  488. { "[\\0]"sv, regex::Error::NoError, combine_flags(ECMAScriptFlags::Unicode, ECMAScriptFlags::BrowserExtended) },
  489. { "[\\00]"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  490. { "[\\00]"sv, regex::Error::InvalidPattern, combine_flags(ECMAScriptFlags::Unicode, ECMAScriptFlags::BrowserExtended) },
  491. { "\\^\\$\\\\\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\/"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  492. { "[\\^\\$\\\\\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\/]"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  493. { "]"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  494. { "]"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  495. { "\\]"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  496. { "}"sv, regex::Error::NoError, ECMAScriptFlags::BrowserExtended },
  497. { "}"sv, regex::Error::InvalidPattern, ECMAScriptFlags::Unicode },
  498. { "\\}"sv, regex::Error::NoError, ECMAScriptFlags::Unicode },
  499. { "a{9007199254740991}"sv }, // 2^53 - 1
  500. { "a{9007199254740991,}"sv },
  501. { "a{9007199254740991,9007199254740991}"sv },
  502. { "a{9007199254740992}"sv, regex::Error::InvalidBraceContent },
  503. { "a{9007199254740992,}"sv, regex::Error::InvalidBraceContent },
  504. { "a{9007199254740991,9007199254740992}"sv, regex::Error::InvalidBraceContent },
  505. { "a{9007199254740992,9007199254740991}"sv, regex::Error::InvalidBraceContent },
  506. { "a{9007199254740992,9007199254740992}"sv, regex::Error::InvalidBraceContent },
  507. };
  508. for (auto& test : tests) {
  509. Regex<ECMA262> re(test.pattern, test.flags);
  510. EXPECT_EQ(re.parser_result.error, test.expected_error);
  511. if constexpr (REGEX_DEBUG) {
  512. dbgln("\n");
  513. RegexDebug regex_dbg(stderr);
  514. regex_dbg.print_raw_bytecode(re);
  515. regex_dbg.print_header();
  516. regex_dbg.print_bytecode(re);
  517. dbgln("\n");
  518. }
  519. }
  520. }
  521. TEST_CASE(ECMA262_match)
  522. {
  523. struct _test {
  524. StringView pattern;
  525. StringView subject;
  526. bool matches { true };
  527. ECMAScriptFlags options {};
  528. };
  529. // clang-format off
  530. constexpr _test tests[] {
  531. { "^hello.$"sv, "hello1"sv },
  532. { "^(hello.)$"sv, "hello1"sv },
  533. { "^h{0,1}ello.$"sv, "ello1"sv },
  534. { "^hello\\W$"sv, "hello!"sv },
  535. { "^hell\\w.$"sv, "hellx!"sv },
  536. { "^hell\\x6f1$"sv, "hello1"sv },
  537. { "^hel(?<LO>l.)1$"sv, "hello1"sv },
  538. { "^hel(?<LO>l.)1*\\k<LO>.$"sv, "hello1lo1"sv },
  539. { "^[-a-z1-3\\s]+$"sv, "hell2 o1"sv },
  540. { "^[\\0-\\x1f]$"sv, "\n"sv },
  541. { .pattern = "\\bhello\\B"sv, .subject = "hello1"sv, .options = ECMAScriptFlags::Global },
  542. { "\\b.*\\b"sv, "hello1"sv },
  543. { "[^\\D\\S]{2}"sv, "1 "sv },
  544. { "bar(?=f.)foo"sv, "barfoo"sv },
  545. { "bar(?=foo)bar"sv, "barbar"sv, false },
  546. { "bar(?!foo)bar"sv, "barbar"sv, true },
  547. { "bar(?!bar)bar"sv, "barbar"sv, false },
  548. { "bar.*(?<=foo)"sv, "barbar"sv, false },
  549. { "bar.*(?<!foo)"sv, "barbar"sv, true },
  550. { "((...)X)+"sv, "fooXbarXbazX"sv, true },
  551. { "(?:)"sv, ""sv, true },
  552. { "\\^"sv, "^"sv },
  553. { "\\^\\$\\\\\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\/"sv, "^$\\.*+?()[]{}|/"sv, true, ECMAScriptFlags::Unicode },
  554. { "[\\^\\$\\\\\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\/]{15}"sv, "^$\\.*+?()[]{}|/"sv, true, ECMAScriptFlags::Unicode },
  555. { "(a{2}){3}"sv, "aaaaaa"sv },
  556. { "(a{2}){3}"sv, "aaaabaa"sv, false },
  557. { "(a{2}){4}"sv, "aaaaaaaa"sv },
  558. { "(a{2}){4}"sv, "aaaaaabaa"sv, false },
  559. { "(a{3}){2}"sv, "aaaaaa"sv },
  560. { "(a{3}){2}"sv, "aaaabaa"sv, false },
  561. { "(a{4}){2}"sv, "aaaaaaaa"sv },
  562. { "(a{4}){2}"sv, "aaaaaabaa"sv, false },
  563. { "\\u{4}"sv, "uuuu" },
  564. { "(?<=.{3})f"sv, "abcdef"sv, true, (ECMAScriptFlags)regex::AllFlags::Global },
  565. { "(?<=.{3})f"sv, "abc😀ef"sv, true, (ECMAScriptFlags)regex::AllFlags::Global },
  566. // ECMA262, B.1.4. Regular Expression Pattern extensions for browsers
  567. { "{"sv, "{"sv, true, ECMAScriptFlags::BrowserExtended },
  568. { "\\5"sv, "\5"sv, true, ECMAScriptFlags::BrowserExtended },
  569. { "\\05"sv, "\5"sv, true, ECMAScriptFlags::BrowserExtended },
  570. { "\\455"sv, "\45""5"sv, true, ECMAScriptFlags::BrowserExtended },
  571. { "\\314"sv, "\314"sv, true, ECMAScriptFlags::BrowserExtended },
  572. { "\\c"sv, "\\c"sv, true, ECMAScriptFlags::BrowserExtended },
  573. { "\\cf"sv, "\06"sv, true, ECMAScriptFlags::BrowserExtended },
  574. { "\\c1"sv, "\\c1"sv, true, ECMAScriptFlags::BrowserExtended },
  575. { "[\\c1]"sv, "\x11"sv, true, ECMAScriptFlags::BrowserExtended },
  576. { "[\\w-\\d]"sv, "-"sv, true, ECMAScriptFlags::BrowserExtended },
  577. { "^(?:^^\\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|,|-=|->|\\/|\\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\\^=|\\^\\^|\\^\\^=|{|\\||\\|=|\\|\\||\\|\\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*(\\/(?=[^*/])(?:[^/[\\\\]|\\\\[\\S\\s]|\\[(?:[^\\\\\\]]|\\\\[\\S\\s])*(?:]|$))+\\/)"sv,
  578. "return /xx/"sv, true, ECMAScriptFlags::BrowserExtended
  579. }, // #5517, appears to be matching JS expressions that involve regular expressions...
  580. { "a{2,}"sv, "aaaa"sv }, // #5518
  581. { "\\0"sv, "\0"sv, true, ECMAScriptFlags::BrowserExtended },
  582. { "\\0"sv, "\0"sv, true, combine_flags(ECMAScriptFlags::Unicode, ECMAScriptFlags::BrowserExtended) },
  583. { "\\01"sv, "\1"sv, true, ECMAScriptFlags::BrowserExtended },
  584. { "[\\0]"sv, "\0"sv, true, ECMAScriptFlags::BrowserExtended },
  585. { "[\\0]"sv, "\0"sv, true, combine_flags(ECMAScriptFlags::Unicode, ECMAScriptFlags::BrowserExtended) },
  586. { "[\\01]"sv, "\1"sv, true, ECMAScriptFlags::BrowserExtended },
  587. { "(\0|a)"sv, "a"sv, true }, // #9686, Should allow null bytes in pattern
  588. };
  589. // clang-format on
  590. for (auto& test : tests) {
  591. Regex<ECMA262> re(test.pattern, test.options);
  592. if constexpr (REGEX_DEBUG) {
  593. dbgln("\n");
  594. RegexDebug regex_dbg(stderr);
  595. regex_dbg.print_raw_bytecode(re);
  596. regex_dbg.print_header();
  597. regex_dbg.print_bytecode(re);
  598. dbgln("\n");
  599. }
  600. EXPECT_EQ(re.parser_result.error, Error::NoError);
  601. EXPECT_EQ(re.match(test.subject).success, test.matches);
  602. }
  603. }
  604. TEST_CASE(ECMA262_unicode_match)
  605. {
  606. struct _test {
  607. StringView pattern;
  608. StringView subject;
  609. bool matches { true };
  610. ECMAScriptFlags options {};
  611. };
  612. _test tests[] {
  613. { "\xf0\x9d\x8c\x86"sv, "abcdef"sv, false, ECMAScriptFlags::Unicode },
  614. { "[\xf0\x9d\x8c\x86]"sv, "abcdef"sv, false, ECMAScriptFlags::Unicode },
  615. { "\\ud83d"sv, "😀"sv, true },
  616. { "\\ud83d"sv, "😀"sv, false, ECMAScriptFlags::Unicode },
  617. { "\\ude00"sv, "😀"sv, true },
  618. { "\\ude00"sv, "😀"sv, false, ECMAScriptFlags::Unicode },
  619. { "\\ud83d\\ude00"sv, "😀"sv, true },
  620. { "\\ud83d\\ude00"sv, "😀"sv, true, ECMAScriptFlags::Unicode },
  621. { "\\u{1f600}"sv, "😀"sv, true, ECMAScriptFlags::Unicode },
  622. { "\\ud83d\\ud83d"sv, "\xed\xa0\xbd\xed\xa0\xbd"sv, true },
  623. { "\\ud83d\\ud83d"sv, "\xed\xa0\xbd\xed\xa0\xbd"sv, true, ECMAScriptFlags::Unicode },
  624. { "(?<=.{3})f"sv, "abcdef"sv, true, ECMAScriptFlags::Unicode },
  625. { "(?<=.{3})f"sv, "abc😀ef"sv, true, ECMAScriptFlags::Unicode },
  626. { "(?<𝓑𝓻𝓸𝔀𝓷>brown)"sv, "brown"sv, true, ECMAScriptFlags::Unicode },
  627. { "(?<\\u{1d4d1}\\u{1d4fb}\\u{1d4f8}\\u{1d500}\\u{1d4f7}>brown)"sv, "brown"sv, true, ECMAScriptFlags::Unicode },
  628. { "(?<\\ud835\\udcd1\\ud835\\udcfb\\ud835\\udcf8\\ud835\\udd00\\ud835\\udcf7>brown)"sv, "brown"sv, true, ECMAScriptFlags::Unicode },
  629. };
  630. for (auto& test : tests) {
  631. Regex<ECMA262> re(test.pattern, (ECMAScriptFlags)regex::AllFlags::Global | test.options);
  632. auto subject = AK::utf8_to_utf16(test.subject);
  633. Utf16View view { subject };
  634. if constexpr (REGEX_DEBUG) {
  635. dbgln("\n");
  636. RegexDebug regex_dbg(stderr);
  637. regex_dbg.print_raw_bytecode(re);
  638. regex_dbg.print_header();
  639. regex_dbg.print_bytecode(re);
  640. dbgln("\n");
  641. }
  642. EXPECT_EQ(re.parser_result.error, Error::NoError);
  643. EXPECT_EQ(re.match(view).success, test.matches);
  644. }
  645. }
  646. TEST_CASE(ECMA262_property_match)
  647. {
  648. struct _test {
  649. StringView pattern;
  650. StringView subject;
  651. bool matches { true };
  652. ECMAScriptFlags options {};
  653. };
  654. constexpr _test tests[] {
  655. { "\\p{ASCII}"sv, "a"sv, false },
  656. { "\\p{ASCII}"sv, "p{ASCII}"sv, true },
  657. { "\\p{ASCII}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  658. { "\\p{ASCII}"sv, "😀"sv, false, ECMAScriptFlags::Unicode },
  659. { "\\P{ASCII}"sv, "a"sv, false, ECMAScriptFlags::Unicode },
  660. { "\\P{ASCII}"sv, "😀"sv, true, ECMAScriptFlags::Unicode },
  661. { "\\p{ASCII_Hex_Digit}"sv, "1"sv, true, ECMAScriptFlags::Unicode },
  662. { "\\p{ASCII_Hex_Digit}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  663. { "\\p{ASCII_Hex_Digit}"sv, "x"sv, false, ECMAScriptFlags::Unicode },
  664. { "\\P{ASCII_Hex_Digit}"sv, "1"sv, false, ECMAScriptFlags::Unicode },
  665. { "\\P{ASCII_Hex_Digit}"sv, "a"sv, false, ECMAScriptFlags::Unicode },
  666. { "\\P{ASCII_Hex_Digit}"sv, "x"sv, true, ECMAScriptFlags::Unicode },
  667. { "\\p{Any}"sv, "\xcd\xb8"sv, true, ECMAScriptFlags::Unicode }, // U+0378, which is an unassigned code point.
  668. { "\\P{Any}"sv, "\xcd\xb8"sv, false, ECMAScriptFlags::Unicode }, // U+0378, which is an unassigned code point.
  669. { "\\p{Assigned}"sv, "\xcd\xb8"sv, false, ECMAScriptFlags::Unicode }, // U+0378, which is an unassigned code point.
  670. { "\\P{Assigned}"sv, "\xcd\xb8"sv, true, ECMAScriptFlags::Unicode }, // U+0378, which is an unassigned code point.
  671. { "\\p{Lu}"sv, "a"sv, false, ECMAScriptFlags::Unicode },
  672. { "\\p{Lu}"sv, "A"sv, true, ECMAScriptFlags::Unicode },
  673. { "\\p{Lu}"sv, "9"sv, false, ECMAScriptFlags::Unicode },
  674. { "\\p{Cased_Letter}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  675. { "\\p{Cased_Letter}"sv, "A"sv, true, ECMAScriptFlags::Unicode },
  676. { "\\p{Cased_Letter}"sv, "9"sv, false, ECMAScriptFlags::Unicode },
  677. { "\\P{Cased_Letter}"sv, "a"sv, false, ECMAScriptFlags::Unicode },
  678. { "\\P{Cased_Letter}"sv, "A"sv, false, ECMAScriptFlags::Unicode },
  679. { "\\P{Cased_Letter}"sv, "9"sv, true, ECMAScriptFlags::Unicode },
  680. { "\\p{General_Category=Cased_Letter}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  681. { "\\p{General_Category=Cased_Letter}"sv, "A"sv, true, ECMAScriptFlags::Unicode },
  682. { "\\p{General_Category=Cased_Letter}"sv, "9"sv, false, ECMAScriptFlags::Unicode },
  683. { "\\p{gc=Cased_Letter}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  684. { "\\p{gc=Cased_Letter}"sv, "A"sv, true, ECMAScriptFlags::Unicode },
  685. { "\\p{gc=Cased_Letter}"sv, "9"sv, false, ECMAScriptFlags::Unicode },
  686. { "\\p{Script=Latin}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  687. { "\\p{Script=Latin}"sv, "A"sv, true, ECMAScriptFlags::Unicode },
  688. { "\\p{Script=Latin}"sv, "9"sv, false, ECMAScriptFlags::Unicode },
  689. { "\\p{sc=Latin}"sv, "a"sv, true, ECMAScriptFlags::Unicode },
  690. { "\\p{sc=Latin}"sv, "A"sv, true, ECMAScriptFlags::Unicode },
  691. { "\\p{sc=Latin}"sv, "9"sv, false, ECMAScriptFlags::Unicode },
  692. { "\\p{Script_Extensions=Deva}"sv, "a"sv, false, ECMAScriptFlags::Unicode },
  693. { "\\p{Script_Extensions=Beng}"sv, "\xe1\xb3\x95"sv, true, ECMAScriptFlags::Unicode }, // U+01CD5
  694. { "\\p{Script_Extensions=Deva}"sv, "\xe1\xb3\x95"sv, true, ECMAScriptFlags::Unicode }, // U+01CD5
  695. { "\\p{scx=Deva}"sv, "a"sv, false, ECMAScriptFlags::Unicode },
  696. { "\\p{scx=Beng}"sv, "\xe1\xb3\x95"sv, true, ECMAScriptFlags::Unicode }, // U+01CD5
  697. { "\\p{scx=Deva}"sv, "\xe1\xb3\x95"sv, true, ECMAScriptFlags::Unicode }, // U+01CD5
  698. };
  699. for (auto& test : tests) {
  700. Regex<ECMA262> re(test.pattern, (ECMAScriptFlags)regex::AllFlags::Global | regex::ECMAScriptFlags::BrowserExtended | test.options);
  701. auto subject = AK::utf8_to_utf16(test.subject);
  702. Utf16View view { subject };
  703. if constexpr (REGEX_DEBUG) {
  704. dbgln("\n");
  705. RegexDebug regex_dbg(stderr);
  706. regex_dbg.print_raw_bytecode(re);
  707. regex_dbg.print_header();
  708. regex_dbg.print_bytecode(re);
  709. dbgln("\n");
  710. }
  711. EXPECT_EQ(re.parser_result.error, Error::NoError);
  712. EXPECT_EQ(re.match(view).success, test.matches);
  713. }
  714. }
  715. TEST_CASE(replace)
  716. {
  717. struct _test {
  718. StringView pattern;
  719. StringView replacement;
  720. StringView subject;
  721. StringView expected;
  722. ECMAScriptFlags options {};
  723. };
  724. constexpr _test tests[] {
  725. { "foo(.+)"sv, "aaa"sv, "test"sv, "test"sv },
  726. { "foo(.+)"sv, "test\\1"sv, "foobar"sv, "testbar"sv },
  727. { "foo(.+)"sv, "\\2\\1"sv, "foobar"sv, "\\2bar"sv },
  728. { "foo(.+)"sv, "\\\\\\1"sv, "foobar"sv, "\\bar"sv },
  729. { "foo(.)"sv, "a\\1"sv, "fooxfooy"sv, "axay"sv, ECMAScriptFlags::Multiline },
  730. };
  731. for (auto& test : tests) {
  732. Regex<ECMA262> re(test.pattern, test.options);
  733. if constexpr (REGEX_DEBUG) {
  734. dbgln("\n");
  735. RegexDebug regex_dbg(stderr);
  736. regex_dbg.print_raw_bytecode(re);
  737. regex_dbg.print_header();
  738. regex_dbg.print_bytecode(re);
  739. dbgln("\n");
  740. }
  741. EXPECT_EQ(re.parser_result.error, Error::NoError);
  742. EXPECT_EQ(re.replace(test.subject, test.replacement), test.expected);
  743. }
  744. }
  745. TEST_CASE(case_insensitive_match)
  746. {
  747. Regex<PosixExtended> re("cd", PosixFlags::Insensitive | PosixFlags::Global);
  748. auto result = re.match("AEKFCD");
  749. EXPECT_EQ(result.success, true);
  750. if (result.success) {
  751. EXPECT_EQ(result.matches.at(0).column, 4ul);
  752. }
  753. }
  754. TEST_CASE(extremely_long_fork_chain)
  755. {
  756. Regex<ECMA262> re("(?:aa)*");
  757. auto result = re.match(String::repeated('a', 1000));
  758. EXPECT_EQ(result.success, true);
  759. }
  760. TEST_CASE(theoretically_infinite_loop)
  761. {
  762. Array patterns {
  763. "(a*)*"sv, // Infinitely matching empty substrings, the outer loop should short-circuit.
  764. "(a*?)*"sv, // Infinitely matching empty substrings, the outer loop should short-circuit.
  765. "(a*)*?"sv, // Should match exactly nothing.
  766. "(?:)*?"sv, // Should not generate an infinite fork loop.
  767. };
  768. for (auto& pattern : patterns) {
  769. Regex<ECMA262> re(pattern);
  770. auto result = re.match("");
  771. EXPECT_EQ(result.success, true);
  772. }
  773. }
  774. static auto g_lots_of_a_s = String::repeated('a', 10'000'000);
  775. BENCHMARK_CASE(fork_performance)
  776. {
  777. Regex<ECMA262> re("(?:aa)*");
  778. auto result = re.match(g_lots_of_a_s);
  779. EXPECT_EQ(result.success, true);
  780. }