RegexByteCode.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. /*
  2. * Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "RegexByteCode.h"
  27. #include "AK/StringBuilder.h"
  28. #include "RegexDebug.h"
  29. #include <ctype.h>
  30. namespace regex {
  31. const char* OpCode::name(OpCodeId opcode_id)
  32. {
  33. switch (opcode_id) {
  34. #define __ENUMERATE_OPCODE(x) \
  35. case OpCodeId::x: \
  36. return #x;
  37. ENUMERATE_OPCODES
  38. #undef __ENUMERATE_OPCODE
  39. default:
  40. ASSERT_NOT_REACHED();
  41. return "<Unknown>";
  42. }
  43. }
  44. const char* OpCode::name() const
  45. {
  46. return name(opcode_id());
  47. }
  48. const char* execution_result_name(ExecutionResult result)
  49. {
  50. switch (result) {
  51. #define __ENUMERATE_EXECUTION_RESULT(x) \
  52. case ExecutionResult::x: \
  53. return #x;
  54. ENUMERATE_EXECUTION_RESULTS
  55. #undef __ENUMERATE_EXECUTION_RESULT
  56. default:
  57. ASSERT_NOT_REACHED();
  58. return "<Unknown>";
  59. }
  60. }
  61. const char* character_compare_type_name(CharacterCompareType ch_compare_type)
  62. {
  63. switch (ch_compare_type) {
  64. #define __ENUMERATE_CHARACTER_COMPARE_TYPE(x) \
  65. case CharacterCompareType::x: \
  66. return #x;
  67. ENUMERATE_CHARACTER_COMPARE_TYPES
  68. #undef __ENUMERATE_CHARACTER_COMPARE_TYPE
  69. default:
  70. ASSERT_NOT_REACHED();
  71. return "<Unknown>";
  72. }
  73. }
  74. static const char* character_class_name(CharClass ch_class)
  75. {
  76. switch (ch_class) {
  77. #define __ENUMERATE_CHARACTER_CLASS(x) \
  78. case CharClass::x: \
  79. return #x;
  80. ENUMERATE_CHARACTER_CLASSES
  81. #undef __ENUMERATE_CHARACTER_CLASS
  82. default:
  83. ASSERT_NOT_REACHED();
  84. return "<Unknown>";
  85. }
  86. }
  87. HashMap<u32, OwnPtr<OpCode>> ByteCode::s_opcodes {};
  88. ALWAYS_INLINE OpCode* ByteCode::get_opcode_by_id(OpCodeId id) const
  89. {
  90. if (!s_opcodes.size()) {
  91. for (u32 i = (u32)OpCodeId::First; i <= (u32)OpCodeId::Last; ++i) {
  92. switch ((OpCodeId)i) {
  93. case OpCodeId::Exit:
  94. s_opcodes.set(i, make<OpCode_Exit>(*const_cast<ByteCode*>(this)));
  95. break;
  96. case OpCodeId::Jump:
  97. s_opcodes.set(i, make<OpCode_Jump>(*const_cast<ByteCode*>(this)));
  98. break;
  99. case OpCodeId::Compare:
  100. s_opcodes.set(i, make<OpCode_Compare>(*const_cast<ByteCode*>(this)));
  101. break;
  102. case OpCodeId::CheckEnd:
  103. s_opcodes.set(i, make<OpCode_CheckEnd>(*const_cast<ByteCode*>(this)));
  104. break;
  105. case OpCodeId::ForkJump:
  106. s_opcodes.set(i, make<OpCode_ForkJump>(*const_cast<ByteCode*>(this)));
  107. break;
  108. case OpCodeId::ForkStay:
  109. s_opcodes.set(i, make<OpCode_ForkStay>(*const_cast<ByteCode*>(this)));
  110. break;
  111. case OpCodeId::CheckBegin:
  112. s_opcodes.set(i, make<OpCode_CheckBegin>(*const_cast<ByteCode*>(this)));
  113. break;
  114. case OpCodeId::SaveLeftCaptureGroup:
  115. s_opcodes.set(i, make<OpCode_SaveLeftCaptureGroup>(*const_cast<ByteCode*>(this)));
  116. break;
  117. case OpCodeId::SaveRightCaptureGroup:
  118. s_opcodes.set(i, make<OpCode_SaveRightCaptureGroup>(*const_cast<ByteCode*>(this)));
  119. break;
  120. case OpCodeId::SaveLeftNamedCaptureGroup:
  121. s_opcodes.set(i, make<OpCode_SaveLeftNamedCaptureGroup>(*const_cast<ByteCode*>(this)));
  122. break;
  123. case OpCodeId::SaveRightNamedCaptureGroup:
  124. s_opcodes.set(i, make<OpCode_SaveRightNamedCaptureGroup>(*const_cast<ByteCode*>(this)));
  125. break;
  126. }
  127. }
  128. }
  129. if (id > OpCodeId::Last)
  130. return nullptr;
  131. return const_cast<OpCode*>(s_opcodes.get((u32)id).value())->set_bytecode(*const_cast<ByteCode*>(this));
  132. }
  133. OpCode* ByteCode::get_opcode(MatchState& state) const
  134. {
  135. OpCode* op_code;
  136. if (state.instruction_position >= size()) {
  137. op_code = get_opcode_by_id(OpCodeId::Exit);
  138. } else
  139. op_code = get_opcode_by_id((OpCodeId)at(state.instruction_position));
  140. if (op_code)
  141. op_code->set_state(state);
  142. return op_code;
  143. }
  144. ALWAYS_INLINE ExecutionResult OpCode_Exit::execute(const MatchInput& input, MatchState& state, MatchOutput&) const
  145. {
  146. if (state.string_position > input.view.length() || state.instruction_position >= m_bytecode->size())
  147. return ExecutionResult::Succeeded;
  148. return ExecutionResult::Failed;
  149. }
  150. ALWAYS_INLINE ExecutionResult OpCode_Jump::execute(const MatchInput&, MatchState& state, MatchOutput&) const
  151. {
  152. state.instruction_position += offset();
  153. return ExecutionResult::Continue;
  154. }
  155. ALWAYS_INLINE ExecutionResult OpCode_ForkJump::execute(const MatchInput&, MatchState& state, MatchOutput&) const
  156. {
  157. state.fork_at_position = state.instruction_position + size() + offset();
  158. return ExecutionResult::Fork_PrioHigh;
  159. }
  160. ALWAYS_INLINE ExecutionResult OpCode_ForkStay::execute(const MatchInput&, MatchState& state, MatchOutput&) const
  161. {
  162. state.fork_at_position = state.instruction_position + size() + offset();
  163. return ExecutionResult::Fork_PrioLow;
  164. }
  165. ALWAYS_INLINE ExecutionResult OpCode_CheckBegin::execute(const MatchInput& input, MatchState& state, MatchOutput&) const
  166. {
  167. if (0 == state.string_position && (input.regex_options & AllFlags::MatchNotBeginOfLine))
  168. return ExecutionResult::Failed_ExecuteLowPrioForks;
  169. if ((0 == state.string_position && !(input.regex_options & AllFlags::MatchNotBeginOfLine))
  170. || (0 != state.string_position && (input.regex_options & AllFlags::MatchNotBeginOfLine))
  171. || (0 == state.string_position && (input.regex_options & AllFlags::Global)))
  172. return ExecutionResult::Continue;
  173. return ExecutionResult::Failed_ExecuteLowPrioForks;
  174. }
  175. ALWAYS_INLINE ExecutionResult OpCode_CheckEnd::execute(const MatchInput& input, MatchState& state, MatchOutput&) const
  176. {
  177. if (state.string_position == input.view.length() && (input.regex_options & AllFlags::MatchNotEndOfLine))
  178. return ExecutionResult::Failed_ExecuteLowPrioForks;
  179. if ((state.string_position == input.view.length() && !(input.regex_options & AllFlags::MatchNotEndOfLine))
  180. || (state.string_position != input.view.length() && (input.regex_options & AllFlags::MatchNotEndOfLine || input.regex_options & AllFlags::MatchNotBeginOfLine)))
  181. return ExecutionResult::Continue;
  182. return ExecutionResult::Failed_ExecuteLowPrioForks;
  183. }
  184. ALWAYS_INLINE ExecutionResult OpCode_SaveLeftCaptureGroup::execute(const MatchInput& input, MatchState& state, MatchOutput& output) const
  185. {
  186. if (input.match_index >= output.capture_group_matches.size()) {
  187. output.capture_group_matches.ensure_capacity(input.match_index);
  188. auto capacity = output.capture_group_matches.capacity();
  189. for (size_t i = output.capture_group_matches.size(); i <= capacity; ++i)
  190. output.capture_group_matches.empend();
  191. }
  192. if (id() >= output.capture_group_matches.at(input.match_index).size()) {
  193. output.capture_group_matches.at(input.match_index).ensure_capacity(id());
  194. auto capacity = output.capture_group_matches.at(input.match_index).capacity();
  195. for (size_t i = output.capture_group_matches.at(input.match_index).size(); i <= capacity; ++i)
  196. output.capture_group_matches.at(input.match_index).empend();
  197. }
  198. output.capture_group_matches.at(input.match_index).at(id()).left_column = state.string_position;
  199. return ExecutionResult::Continue;
  200. }
  201. ALWAYS_INLINE ExecutionResult OpCode_SaveRightCaptureGroup::execute(const MatchInput& input, MatchState& state, MatchOutput& output) const
  202. {
  203. auto& match = output.capture_group_matches.at(input.match_index).at(id());
  204. auto start_position = match.left_column;
  205. auto length = state.string_position - start_position;
  206. if (start_position < match.column)
  207. return ExecutionResult::Continue;
  208. ASSERT(start_position + length <= input.view.length());
  209. auto view = input.view.substring_view(start_position, length);
  210. if (input.regex_options & AllFlags::StringCopyMatches) {
  211. match = { view.to_string(), input.line, start_position, input.global_offset + start_position }; // create a copy of the original string
  212. } else {
  213. match = { view, input.line, start_position, input.global_offset + start_position }; // take view to original string
  214. }
  215. return ExecutionResult::Continue;
  216. }
  217. ALWAYS_INLINE ExecutionResult OpCode_SaveLeftNamedCaptureGroup::execute(const MatchInput& input, MatchState& state, MatchOutput& output) const
  218. {
  219. if (input.match_index >= output.named_capture_group_matches.size()) {
  220. output.named_capture_group_matches.ensure_capacity(input.match_index);
  221. auto capacity = output.named_capture_group_matches.capacity();
  222. for (size_t i = output.named_capture_group_matches.size(); i <= capacity; ++i)
  223. output.named_capture_group_matches.empend();
  224. }
  225. output.named_capture_group_matches.at(input.match_index).ensure(name()).column = state.string_position;
  226. return ExecutionResult::Continue;
  227. }
  228. ALWAYS_INLINE ExecutionResult OpCode_SaveRightNamedCaptureGroup::execute(const MatchInput& input, MatchState& state, MatchOutput& output) const
  229. {
  230. StringView capture_group_name = name();
  231. if (output.named_capture_group_matches.at(input.match_index).contains(capture_group_name)) {
  232. auto start_position = output.named_capture_group_matches.at(input.match_index).ensure(capture_group_name).column;
  233. auto length = state.string_position - start_position;
  234. auto& map = output.named_capture_group_matches.at(input.match_index);
  235. #ifdef REGEX_DEBUG
  236. ASSERT(start_position + length <= input.view.length());
  237. dbg() << "Save named capture group with name=" << capture_group_name << " and content: " << input.view.substring_view(start_position, length).to_string();
  238. #endif
  239. ASSERT(start_position + length <= input.view.length());
  240. auto view = input.view.substring_view(start_position, length);
  241. if (input.regex_options & AllFlags::StringCopyMatches) {
  242. map.set(capture_group_name, { view.to_string(), input.line, start_position, input.global_offset + start_position }); // create a copy of the original string
  243. } else {
  244. map.set(capture_group_name, { view, input.line, start_position, input.global_offset + start_position }); // take view to original string
  245. }
  246. } else {
  247. fprintf(stderr, "Didn't find corresponding capture group match for name=%s, match_index=%lu\n", capture_group_name.to_string().characters(), input.match_index);
  248. }
  249. return ExecutionResult::Continue;
  250. }
  251. ALWAYS_INLINE ExecutionResult OpCode_Compare::execute(const MatchInput& input, MatchState& state, MatchOutput&) const
  252. {
  253. bool inverse { false };
  254. size_t string_position = state.string_position;
  255. bool inverse_matched { false };
  256. size_t offset { state.instruction_position + 3 };
  257. for (size_t i = 0; i < arguments_count(); ++i) {
  258. if (state.string_position > string_position)
  259. break;
  260. auto compare_type = (CharacterCompareType)m_bytecode->at(offset++);
  261. if (compare_type == CharacterCompareType::Inverse)
  262. inverse = true;
  263. else if (compare_type == CharacterCompareType::Char) {
  264. char ch = m_bytecode->at(offset++);
  265. // We want to compare a string that is longer or equal in length to the available string
  266. if (input.view.length() - state.string_position < 1)
  267. return ExecutionResult::Failed_ExecuteLowPrioForks;
  268. compare_char(input, state, ch, inverse, inverse_matched);
  269. } else if (compare_type == CharacterCompareType::AnyChar) {
  270. // We want to compare a string that is definitely longer than the available string
  271. if (input.view.length() - state.string_position < 1)
  272. return ExecutionResult::Failed_ExecuteLowPrioForks;
  273. ASSERT(!inverse);
  274. ++state.string_position;
  275. } else if (compare_type == CharacterCompareType::String) {
  276. ASSERT(!inverse);
  277. char* str = reinterpret_cast<char*>(m_bytecode->at(offset++));
  278. auto& length = m_bytecode->at(offset++);
  279. // We want to compare a string that is definitely longer than the available string
  280. if (input.view.length() - state.string_position < length)
  281. return ExecutionResult::Failed_ExecuteLowPrioForks;
  282. if (!compare_string(input, state, str, length))
  283. return ExecutionResult::Failed_ExecuteLowPrioForks;
  284. } else if (compare_type == CharacterCompareType::CharClass) {
  285. if (input.view.length() - state.string_position < 1)
  286. return ExecutionResult::Failed_ExecuteLowPrioForks;
  287. auto character_class = (CharClass)m_bytecode->at(offset++);
  288. auto ch = input.view[state.string_position];
  289. compare_character_class(input, state, character_class, ch, inverse, inverse_matched);
  290. } else if (compare_type == CharacterCompareType::CharRange) {
  291. auto value = (CharRange)m_bytecode->at(offset++);
  292. auto from = value.from;
  293. auto to = value.to;
  294. auto ch = input.view[state.string_position];
  295. compare_character_range(input, state, from, to, ch, inverse, inverse_matched);
  296. } else {
  297. fprintf(stderr, "Undefined comparison: %i\n", (int)compare_type);
  298. ASSERT_NOT_REACHED();
  299. break;
  300. }
  301. }
  302. if (inverse && !inverse_matched)
  303. ++state.string_position;
  304. if (string_position == state.string_position || state.string_position > input.view.length())
  305. return ExecutionResult::Failed_ExecuteLowPrioForks;
  306. return ExecutionResult::Continue;
  307. }
  308. ALWAYS_INLINE void OpCode_Compare::compare_char(const MatchInput& input, MatchState& state, u32 ch1, bool inverse, bool& inverse_matched)
  309. {
  310. u32 ch2 = input.view[state.string_position];
  311. if (input.regex_options & AllFlags::Insensitive) {
  312. ch1 = tolower(ch1);
  313. ch2 = tolower(ch2);
  314. }
  315. if (ch1 == ch2) {
  316. if (inverse)
  317. inverse_matched = true;
  318. else
  319. ++state.string_position;
  320. }
  321. }
  322. ALWAYS_INLINE bool OpCode_Compare::compare_string(const MatchInput& input, MatchState& state, const char* str, size_t length)
  323. {
  324. if (input.view.is_u8_view()) {
  325. auto str_view1 = StringView(str, length);
  326. auto str_view2 = StringView(&input.view.u8view()[state.string_position], length);
  327. String str1, str2;
  328. if (input.regex_options & AllFlags::Insensitive) {
  329. str1 = str_view1.to_string().to_lowercase();
  330. str2 = str_view2.to_string().to_lowercase();
  331. str_view1 = str1.view();
  332. str_view2 = str2.view();
  333. }
  334. if (str_view1 == str_view2) {
  335. state.string_position += length;
  336. return true;
  337. }
  338. }
  339. return false;
  340. }
  341. ALWAYS_INLINE void OpCode_Compare::compare_character_class(const MatchInput& input, MatchState& state, CharClass character_class, u32 ch, bool inverse, bool& inverse_matched)
  342. {
  343. switch (character_class) {
  344. case CharClass::Alnum:
  345. if (isalnum(ch)) {
  346. if (inverse)
  347. inverse_matched = true;
  348. else
  349. ++state.string_position;
  350. }
  351. break;
  352. case CharClass::Alpha:
  353. if (isalpha(ch))
  354. ++state.string_position;
  355. break;
  356. case CharClass::Blank:
  357. if (ch == ' ' || ch == '\t') {
  358. if (inverse)
  359. inverse_matched = true;
  360. else
  361. ++state.string_position;
  362. }
  363. break;
  364. case CharClass::Cntrl:
  365. if (iscntrl(ch)) {
  366. if (inverse)
  367. inverse_matched = true;
  368. else
  369. ++state.string_position;
  370. }
  371. break;
  372. case CharClass::Digit:
  373. if (isdigit(ch)) {
  374. if (inverse)
  375. inverse_matched = true;
  376. else
  377. ++state.string_position;
  378. }
  379. break;
  380. case CharClass::Graph:
  381. if (isgraph(ch)) {
  382. if (inverse)
  383. inverse_matched = true;
  384. else
  385. ++state.string_position;
  386. }
  387. break;
  388. case CharClass::Lower:
  389. if (islower(ch) || ((input.regex_options & AllFlags::Insensitive) && isupper(ch))) {
  390. if (inverse)
  391. inverse_matched = true;
  392. else
  393. ++state.string_position;
  394. }
  395. break;
  396. case CharClass::Print:
  397. if (isprint(ch)) {
  398. if (inverse)
  399. inverse_matched = true;
  400. else
  401. ++state.string_position;
  402. }
  403. break;
  404. case CharClass::Punct:
  405. if (ispunct(ch)) {
  406. if (inverse)
  407. inverse_matched = true;
  408. else
  409. ++state.string_position;
  410. }
  411. break;
  412. case CharClass::Space:
  413. if (isspace(ch)) {
  414. if (inverse)
  415. inverse_matched = true;
  416. else
  417. ++state.string_position;
  418. }
  419. break;
  420. case CharClass::Upper:
  421. if (isupper(ch) || ((input.regex_options & AllFlags::Insensitive) && islower(ch))) {
  422. if (inverse)
  423. inverse_matched = true;
  424. else
  425. ++state.string_position;
  426. }
  427. break;
  428. case CharClass::Xdigit:
  429. if (isxdigit(ch)) {
  430. if (inverse)
  431. inverse_matched = true;
  432. else
  433. ++state.string_position;
  434. }
  435. break;
  436. }
  437. }
  438. ALWAYS_INLINE void OpCode_Compare::compare_character_range(const MatchInput& input, MatchState& state, u32 from, u32 to, u32 ch, bool inverse, bool& inverse_matched)
  439. {
  440. if (input.regex_options & AllFlags::Insensitive) {
  441. from = tolower(from);
  442. to = tolower(to);
  443. ch = tolower(ch);
  444. }
  445. if (ch >= from && ch <= to) {
  446. if (inverse)
  447. inverse_matched = true;
  448. else
  449. ++state.string_position;
  450. }
  451. }
  452. const String OpCode_Compare::arguments_string() const
  453. {
  454. return String::format("argc=%lu, args=%lu ", arguments_count(), arguments_size());
  455. }
  456. const Vector<String> OpCode_Compare::variable_arguments_to_string(Optional<MatchInput> input) const
  457. {
  458. Vector<String> result;
  459. size_t offset { state().instruction_position + 3 };
  460. RegexStringView view = ((input.has_value()) ? input.value().view : nullptr);
  461. for (size_t i = 0; i < arguments_count(); ++i) {
  462. auto compare_type = (CharacterCompareType)m_bytecode->at(offset++);
  463. result.empend(String::format("type=%lu [%s]", (size_t)compare_type, character_compare_type_name(compare_type)));
  464. if (compare_type == CharacterCompareType::Char) {
  465. char ch = m_bytecode->at(offset++);
  466. result.empend(String::format("value='%c'", ch));
  467. if (!view.is_null())
  468. result.empend(String::format("compare against: '%s'", view.substring_view(state().string_position, state().string_position + 1 > view.length() ? 0 : 1).to_string().characters()));
  469. } else if (compare_type == CharacterCompareType::String) {
  470. char* str = reinterpret_cast<char*>(m_bytecode->at(offset++));
  471. auto& length = m_bytecode->at(offset++);
  472. result.empend(String::format("value=\"%.*s\"", length, str));
  473. if (!view.is_null())
  474. result.empend(String::format("compare against: \"%s\"", input.value().view.substring_view(state().string_position, state().string_position + length > view.length() ? 0 : length).to_string().characters()));
  475. } else if (compare_type == CharacterCompareType::CharClass) {
  476. auto character_class = (CharClass)m_bytecode->at(offset++);
  477. result.empend(String::format("ch_class=%lu [%s]", (size_t)character_class, character_class_name(character_class)));
  478. if (!view.is_null())
  479. result.empend(String::format("compare against: '%s'", input.value().view.substring_view(state().string_position, state().string_position + 1 > view.length() ? 0 : 1).to_string().characters()));
  480. } else if (compare_type == CharacterCompareType::CharRange) {
  481. auto value = (CharRange)m_bytecode->at(offset++);
  482. result.empend(String::format("ch_range='%c'-'%c'", value.from, value.to));
  483. if (!view.is_null())
  484. result.empend(String::format("compare against: '%s'", input.value().view.substring_view(state().string_position, state().string_position + 1 > view.length() ? 0 : 1).to_string().characters()));
  485. }
  486. }
  487. return result;
  488. }
  489. }