mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 09:58:11 +00:00
LibRegex: Remove backup file, remove BOM in RegexParser.cpp, run clang-format
This commit is contained in:
parent
3b7884ee8a
commit
6add8b9c05
9 changed files with 8 additions and 1069 deletions
|
@ -565,7 +565,7 @@ const Vector<String> OpCode_Compare::variable_arguments_to_string(Optional<Match
|
||||||
auto value = (CharRange)m_bytecode->at(offset++);
|
auto value = (CharRange)m_bytecode->at(offset++);
|
||||||
result.empend(String::format("ch_range='%c'-'%c'", value.from, value.to));
|
result.empend(String::format("ch_range='%c'-'%c'", value.from, value.to));
|
||||||
if (!view.is_null())
|
if (!view.is_null())
|
||||||
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()));
|
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "LibRegex/RegexMatcher.h"
|
|
||||||
#include "AK/StringBuilder.h"
|
#include "AK/StringBuilder.h"
|
||||||
|
#include "LibRegex/RegexMatcher.h"
|
||||||
|
|
||||||
//#define REGEX_DEBUG
|
//#define REGEX_DEBUG
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ public:
|
||||||
|
|
||||||
const char* characters_without_null_termination() const
|
const char* characters_without_null_termination() const
|
||||||
{
|
{
|
||||||
if(is_u8_view())
|
if (is_u8_view())
|
||||||
return u8view().characters_without_null_termination();
|
return u8view().characters_without_null_termination();
|
||||||
|
|
||||||
return to_string().characters(); // FIXME: it contains the null termination, does that actually matter?
|
return to_string().characters(); // FIXME: it contains the null termination, does that actually matter?
|
||||||
|
@ -201,14 +201,14 @@ public:
|
||||||
|
|
||||||
bool starts_with(const StringView& str) const
|
bool starts_with(const StringView& str) const
|
||||||
{
|
{
|
||||||
if(is_u32_view())
|
if (is_u32_view())
|
||||||
return false;
|
return false;
|
||||||
return u8view().starts_with(str);
|
return u8view().starts_with(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool starts_with(const Utf32View& str) const
|
bool starts_with(const Utf32View& str) const
|
||||||
{
|
{
|
||||||
if(is_u8_view())
|
if (is_u8_view())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
|
|
|
@ -201,7 +201,7 @@ RegexResult Matcher<Parser>::match(const Vector<RegexStringView> views, Optional
|
||||||
if (match_count) {
|
if (match_count) {
|
||||||
auto capture_groups_count = min(output.capture_group_matches.size(), output.matches.size());
|
auto capture_groups_count = min(output.capture_group_matches.size(), output.matches.size());
|
||||||
for (size_t i = 0; i < capture_groups_count; ++i) {
|
for (size_t i = 0; i < capture_groups_count; ++i) {
|
||||||
if(input.regex_options & AllFlags::SkipTrimEmptyMatches) {
|
if (input.regex_options & AllFlags::SkipTrimEmptyMatches) {
|
||||||
output_copy.capture_group_matches.append(output.capture_group_matches.at(i));
|
output_copy.capture_group_matches.append(output.capture_group_matches.at(i));
|
||||||
} else {
|
} else {
|
||||||
Vector<Match> capture_group_matches;
|
Vector<Match> capture_group_matches;
|
||||||
|
|
|
@ -216,7 +216,6 @@ RegexResult search(const Vector<RegexStringView> views, Regex<Parser>& pattern,
|
||||||
return pattern.search(views, regex_options);
|
return pattern.search(views, regex_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<class Parser>
|
template<class Parser>
|
||||||
bool search(const RegexStringView view, Regex<Parser>& pattern, RegexResult& res, Optional<typename ParserTraits<Parser>::OptionsType> regex_options = {})
|
bool search(const RegexStringView view, Regex<Parser>& pattern, RegexResult& res, Optional<typename ParserTraits<Parser>::OptionsType> regex_options = {})
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
|
* Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,803 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "RegexParser.h"
|
|
||||||
#include <AK/String.h>
|
|
||||||
#include <AK/StringBuilder.h>
|
|
||||||
|
|
||||||
namespace AK {
|
|
||||||
namespace regex {
|
|
||||||
|
|
||||||
const char* ByteCodeValue::name(OpCode type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
#define __ENUMERATE_OPCODE(x) \
|
|
||||||
case OpCode::x: \
|
|
||||||
return #x;
|
|
||||||
ENUMERATE_OPCODES
|
|
||||||
#undef __ENUMERATE_OPCODE
|
|
||||||
default:
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
return "<Unknown>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ByteCodeValue::name() const
|
|
||||||
{
|
|
||||||
return name(op_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool Parser<T>::set_error(Error error)
|
|
||||||
{
|
|
||||||
if (m_parser_state.error == Error::NoError) {
|
|
||||||
m_parser_state.error = error;
|
|
||||||
m_parser_state.error_token = m_parser_state.current_token;
|
|
||||||
}
|
|
||||||
return false; // always return false, that eases the API usage (return set_error(...)) :^)
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool Parser<T>::done() const
|
|
||||||
{
|
|
||||||
return match(TokenType::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool Parser<T>::match(TokenType type) const
|
|
||||||
{
|
|
||||||
return m_parser_state.current_token.type() == type;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
Token Parser<T>::consume()
|
|
||||||
{
|
|
||||||
auto old_token = m_parser_state.current_token;
|
|
||||||
m_parser_state.current_token = m_parser_state.lexer.next();
|
|
||||||
return old_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
Token Parser<T>::consume(TokenType type, Error error)
|
|
||||||
{
|
|
||||||
if (m_parser_state.current_token.type() != type) {
|
|
||||||
set_error(error);
|
|
||||||
#ifdef __serenity__
|
|
||||||
dbg() << "[PARSER] Error: Unexpected token " << m_parser_state.m_current_token.name() << ". Expected: " << Token::name(type);
|
|
||||||
#else
|
|
||||||
fprintf(stderr, "[PARSER] Error: Unexpected token %s. Expected %s\n", m_parser_state.current_token.name(), Token::name(type));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool Parser<T>::consume(const String& str)
|
|
||||||
{
|
|
||||||
size_t potentially_go_back { 1 };
|
|
||||||
for (auto ch : str) {
|
|
||||||
if (match(TokenType::OrdinaryCharacter)) {
|
|
||||||
if (m_parser_state.current_token.value()[0] != ch) {
|
|
||||||
m_parser_state.lexer.back(potentially_go_back);
|
|
||||||
m_parser_state.current_token = m_parser_state.lexer.next();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_parser_state.lexer.back(potentially_go_back);
|
|
||||||
m_parser_state.current_token = m_parser_state.lexer.next();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
consume(TokenType::OrdinaryCharacter);
|
|
||||||
++potentially_go_back;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::reset()
|
|
||||||
{
|
|
||||||
m_parser_state.bytecode.clear();
|
|
||||||
m_parser_state.lexer.reset();
|
|
||||||
m_parser_state.current_token = m_parser_state.lexer.next();
|
|
||||||
m_parser_state.error = Error::NoError;
|
|
||||||
m_parser_state.error_token = { TokenType::Eof, 0, StringView(nullptr) };
|
|
||||||
m_parser_state.regex_options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
ParserResult Parser<T>::parse(Optional<OptionsType> regex_options)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
if (regex_options.has_value())
|
|
||||||
m_parser_state.regex_options = regex_options.value();
|
|
||||||
if (parse_internal(m_parser_state.bytecode, m_parser_state.match_length_minimum))
|
|
||||||
consume(TokenType::Eof);
|
|
||||||
else
|
|
||||||
set_error(Error::InvalidPattern);
|
|
||||||
|
|
||||||
#ifdef REGEX_DEBUG
|
|
||||||
printf("[PARSER] Produced bytecode with %lu entries (opcodes + arguments)\n", m_parser_state.m_bytes.size());
|
|
||||||
#endif
|
|
||||||
return {
|
|
||||||
move(m_parser_state.bytecode),
|
|
||||||
move(m_parser_state.capture_groups_count),
|
|
||||||
move(m_parser_state.named_capture_groups_count),
|
|
||||||
move(m_parser_state.match_length_minimum),
|
|
||||||
move(m_parser_state.error),
|
|
||||||
move(m_parser_state.error_token)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_compare_values(Vector<ByteCodeValue>& stack, Vector<CompareTypeAndValuePair>&& pairs)
|
|
||||||
{
|
|
||||||
Vector<ByteCodeValue> bytecode;
|
|
||||||
|
|
||||||
bytecode.empend(OpCode::Compare);
|
|
||||||
bytecode.empend(pairs.size()); // number of arguments
|
|
||||||
|
|
||||||
for (auto& value : pairs) {
|
|
||||||
ASSERT(value.type != CharacterCompareType::RangeExpressionDummy);
|
|
||||||
ASSERT(value.type != CharacterCompareType::Undefined);
|
|
||||||
ASSERT(value.type != CharacterCompareType::OrdinaryCharacters);
|
|
||||||
|
|
||||||
bytecode.append(move(value.type));
|
|
||||||
if (value.type != CharacterCompareType::Inverse && value.type != CharacterCompareType::AnySingleCharacter)
|
|
||||||
bytecode.append(move(value.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.append(move(bytecode));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_group_capture_left(Vector<ByteCodeValue>& stack)
|
|
||||||
{
|
|
||||||
stack.empend(OpCode::SaveLeftCaptureGroup);
|
|
||||||
stack.empend(m_parser_state.capture_groups_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_group_capture_left(Vector<ByteCodeValue>& stack, const StringView& name)
|
|
||||||
{
|
|
||||||
stack.empend(OpCode::SaveLeftNamedCaptureGroup);
|
|
||||||
stack.empend(name.characters_without_null_termination());
|
|
||||||
stack.empend(name.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_group_capture_right(Vector<ByteCodeValue>& stack)
|
|
||||||
{
|
|
||||||
stack.empend(OpCode::SaveRightCaptureGroup);
|
|
||||||
stack.empend(m_parser_state.capture_groups_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_group_capture_right(Vector<ByteCodeValue>& stack, const StringView& name)
|
|
||||||
{
|
|
||||||
stack.empend(OpCode::SaveRightNamedCaptureGroup);
|
|
||||||
stack.empend(name.characters_without_null_termination());
|
|
||||||
stack.empend(name.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_alternation(Vector<ByteCodeValue>& stack, Vector<ByteCodeValue>&& left, Vector<ByteCodeValue>&& right)
|
|
||||||
{
|
|
||||||
|
|
||||||
// FORKSTAY _ALT
|
|
||||||
// REGEXP ALT1
|
|
||||||
// JUMP _END
|
|
||||||
// LABEL _ALT
|
|
||||||
// REGEXP ALT2
|
|
||||||
// LABEL _END
|
|
||||||
|
|
||||||
stack.empend(OpCode::ForkJump);
|
|
||||||
stack.empend(left.size() + 2); // Jump to the _ALT label
|
|
||||||
|
|
||||||
for (auto& op : left)
|
|
||||||
stack.append(move(op));
|
|
||||||
|
|
||||||
stack.empend(OpCode::Jump);
|
|
||||||
stack.empend(right.size()); // Jump to the _END label
|
|
||||||
|
|
||||||
// LABEL _ALT = bytecode.size() + 2
|
|
||||||
|
|
||||||
for (auto& op : right)
|
|
||||||
stack.append(move(op));
|
|
||||||
|
|
||||||
// LABEL _END = alterantive_bytecode.size
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_repetition_min_max(Vector<ByteCodeValue>& bytecode_to_repeat, size_t minimum, Optional<size_t> maximum)
|
|
||||||
{
|
|
||||||
Vector<ByteCodeValue> new_bytecode;
|
|
||||||
insert_bytecode_repetition_n(new_bytecode, bytecode_to_repeat, minimum);
|
|
||||||
|
|
||||||
if (maximum.has_value()) {
|
|
||||||
if (maximum.value() > minimum) {
|
|
||||||
auto diff = maximum.value() - minimum;
|
|
||||||
new_bytecode.empend(OpCode::ForkStay);
|
|
||||||
new_bytecode.empend(diff * (bytecode_to_repeat.size() + 2)); // Jump to the _END label
|
|
||||||
|
|
||||||
for (size_t i = 0; i < diff; ++i) {
|
|
||||||
new_bytecode.append(bytecode_to_repeat);
|
|
||||||
new_bytecode.empend(OpCode::ForkStay);
|
|
||||||
new_bytecode.empend((diff - i - 1) * (bytecode_to_repeat.size() + 2)); // Jump to the _END label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no maximum value set, repeat finding if possible
|
|
||||||
new_bytecode.empend(OpCode::ForkJump);
|
|
||||||
new_bytecode.empend(-bytecode_to_repeat.size() - 2); // Jump to the last iteration
|
|
||||||
}
|
|
||||||
|
|
||||||
bytecode_to_repeat = move(new_bytecode);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_repetition_n(Vector<ByteCodeValue>& stack, Vector<ByteCodeValue>& bytecode_to_repeat, size_t n)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < n; ++i)
|
|
||||||
stack.append(bytecode_to_repeat);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_repetition_min_one(Vector<ByteCodeValue>& bytecode_to_repeat, bool greedy)
|
|
||||||
{
|
|
||||||
// LABEL _START = -bytecode_to_repeat.size()
|
|
||||||
// REGEXP
|
|
||||||
// FORKJUMP _START (FORKSTAY -> Greedy)
|
|
||||||
|
|
||||||
if (greedy)
|
|
||||||
bytecode_to_repeat.empend(OpCode::ForkStay);
|
|
||||||
else
|
|
||||||
bytecode_to_repeat.empend(OpCode::ForkJump);
|
|
||||||
|
|
||||||
bytecode_to_repeat.empend(-bytecode_to_repeat.size() - 1); // Jump to the _START label
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_repetition_any(Vector<ByteCodeValue>& bytecode_to_repeat, bool greedy)
|
|
||||||
{
|
|
||||||
// LABEL _START
|
|
||||||
// FORKSTAY _END (FORKJUMP -> Greedy)
|
|
||||||
// REGEXP
|
|
||||||
// JUMP _START
|
|
||||||
// LABEL _END
|
|
||||||
|
|
||||||
// LABEL _START = stack.size();
|
|
||||||
Vector<ByteCodeValue> bytecode;
|
|
||||||
|
|
||||||
if (greedy)
|
|
||||||
bytecode.empend(OpCode::ForkJump);
|
|
||||||
else
|
|
||||||
bytecode.empend(OpCode::ForkStay);
|
|
||||||
|
|
||||||
bytecode.empend(bytecode_to_repeat.size() + 2); // Jump to the _END label
|
|
||||||
|
|
||||||
for (auto& op : bytecode_to_repeat)
|
|
||||||
bytecode.append(move(op));
|
|
||||||
|
|
||||||
bytecode.empend(OpCode::Jump);
|
|
||||||
bytecode.empend(-bytecode.size() - 1); // Jump to the _START label
|
|
||||||
// LABEL _END = bytecode.size()
|
|
||||||
|
|
||||||
bytecode_to_repeat = move(bytecode);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void Parser<T>::insert_bytecode_repetition_zero_or_one(Vector<ByteCodeValue>& bytecode_to_repeat, bool greedy)
|
|
||||||
{
|
|
||||||
// FORKSTAY _END (FORKJUMP -> Greedy)
|
|
||||||
// REGEXP
|
|
||||||
// LABEL _END
|
|
||||||
Vector<ByteCodeValue> bytecode;
|
|
||||||
|
|
||||||
if (greedy)
|
|
||||||
bytecode.empend(OpCode::ForkJump);
|
|
||||||
else
|
|
||||||
bytecode.empend(OpCode::ForkStay);
|
|
||||||
|
|
||||||
bytecode.empend(bytecode_to_repeat.size()); // Jump to the _END label
|
|
||||||
|
|
||||||
for (auto& op : bytecode_to_repeat)
|
|
||||||
bytecode.append(move(op));
|
|
||||||
// LABEL _END = bytecode.size()
|
|
||||||
|
|
||||||
bytecode_to_repeat = move(bytecode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================
|
|
||||||
// PosixExtended Parser
|
|
||||||
// =============================
|
|
||||||
|
|
||||||
bool PosixExtendedParser::parse_internal(Vector<ByteCodeValue>& stack, size_t& match_length_minimum)
|
|
||||||
{
|
|
||||||
return parse_root(stack, match_length_minimum);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PosixExtendedParser::match_repetition_symbol()
|
|
||||||
{
|
|
||||||
auto type = m_parser_state.current_token.type();
|
|
||||||
return (type == TokenType::Asterisk
|
|
||||||
|| type == TokenType::Plus
|
|
||||||
|| type == TokenType::Questionmark
|
|
||||||
|| type == TokenType::LeftCurly);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PosixExtendedParser::match_ordinary_characters()
|
|
||||||
{
|
|
||||||
// NOTE: This method must not be called during bracket and repetition parsing!
|
|
||||||
// FIXME: Add assertion for that?
|
|
||||||
auto type = m_parser_state.current_token.type();
|
|
||||||
return (type == TokenType::OrdinaryCharacter
|
|
||||||
|| type == TokenType::Comma
|
|
||||||
|| type == TokenType::Slash
|
|
||||||
|| type == TokenType::EqualSign
|
|
||||||
|| type == TokenType::HyphenMinus
|
|
||||||
|| type == TokenType::Colon);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PosixExtendedParser::parse_repetition_symbol(Vector<ByteCodeValue>& bytecode_to_repeat, size_t& match_length_minimum)
|
|
||||||
{
|
|
||||||
if (match(TokenType::LeftCurly)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
StringBuilder number_builder;
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
while (match(TokenType::OrdinaryCharacter)) {
|
|
||||||
number_builder.append(consume().value());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t minimum = number_builder.build().to_uint(ok);
|
|
||||||
if (!ok)
|
|
||||||
return set_error(Error::InvalidBraceContent);
|
|
||||||
|
|
||||||
match_length_minimum *= minimum;
|
|
||||||
|
|
||||||
if (match(TokenType::Comma)) {
|
|
||||||
consume();
|
|
||||||
} else {
|
|
||||||
Vector<ByteCodeValue> bytecode;
|
|
||||||
insert_bytecode_repetition_n(bytecode, bytecode_to_repeat, minimum);
|
|
||||||
bytecode_to_repeat = move(bytecode);
|
|
||||||
return !has_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<size_t> maximum {};
|
|
||||||
number_builder.clear();
|
|
||||||
while (match(TokenType::OrdinaryCharacter)) {
|
|
||||||
number_builder.append(consume().value());
|
|
||||||
}
|
|
||||||
if (!number_builder.is_empty()) {
|
|
||||||
maximum = number_builder.build().to_uint(ok);
|
|
||||||
if (!ok || minimum > maximum.value())
|
|
||||||
return set_error(Error::InvalidBraceContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
insert_bytecode_repetition_min_max(bytecode_to_repeat, minimum, maximum);
|
|
||||||
|
|
||||||
consume(TokenType::RightCurly, Error::BraceMismatch);
|
|
||||||
return !has_error();
|
|
||||||
|
|
||||||
} else if (match(TokenType::Plus)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
bool greedy = match(TokenType::Questionmark);
|
|
||||||
if (greedy)
|
|
||||||
consume();
|
|
||||||
|
|
||||||
// Note: dont touch match_length_minimum, it's already correct
|
|
||||||
insert_bytecode_repetition_min_one(bytecode_to_repeat, greedy);
|
|
||||||
return !has_error();
|
|
||||||
|
|
||||||
} else if (match(TokenType::Asterisk)) {
|
|
||||||
consume();
|
|
||||||
match_length_minimum = 0;
|
|
||||||
|
|
||||||
bool greedy = match(TokenType::Questionmark);
|
|
||||||
if (greedy)
|
|
||||||
consume();
|
|
||||||
|
|
||||||
insert_bytecode_repetition_any(bytecode_to_repeat, greedy);
|
|
||||||
|
|
||||||
return !has_error();
|
|
||||||
|
|
||||||
} else if (match(TokenType::Questionmark)) {
|
|
||||||
consume();
|
|
||||||
match_length_minimum = 0;
|
|
||||||
|
|
||||||
bool greedy = match(TokenType::Questionmark);
|
|
||||||
if (greedy)
|
|
||||||
consume();
|
|
||||||
|
|
||||||
insert_bytecode_repetition_zero_or_one(bytecode_to_repeat, greedy);
|
|
||||||
return !has_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PosixExtendedParser::parse_bracket_expression(Vector<ByteCodeValue>& stack, size_t& match_length_minimum)
|
|
||||||
{
|
|
||||||
Vector<CompareTypeAndValuePair> values;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
|
|
||||||
if (match(TokenType::HyphenMinus)) {
|
|
||||||
consume();
|
|
||||||
if (values.is_empty() || (values.size() == 1 && values.last().type == CharacterCompareType::Inverse)) {
|
|
||||||
// first in the bracket expression
|
|
||||||
values.append({ CharacterCompareType::OrdinaryCharacter, { '-' } });
|
|
||||||
|
|
||||||
} else if (match(TokenType::RightBracket)) {
|
|
||||||
// Last in the bracket expression
|
|
||||||
values.append({ CharacterCompareType::OrdinaryCharacter, { '-' } });
|
|
||||||
|
|
||||||
} else if (values.last().type == CharacterCompareType::OrdinaryCharacter) {
|
|
||||||
|
|
||||||
values.append({ CharacterCompareType::RangeExpressionDummy, 0 });
|
|
||||||
|
|
||||||
if (match(TokenType::HyphenMinus)) {
|
|
||||||
consume();
|
|
||||||
// Valid range, add ordinary character
|
|
||||||
values.append({ CharacterCompareType::OrdinaryCharacter, { '-' } });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return set_error(Error::InvalidRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (match(TokenType::OrdinaryCharacter) || match(TokenType::Period) || match(TokenType::Asterisk) || match(TokenType::EscapeSequence) || match(TokenType::Plus)) {
|
|
||||||
values.append({ CharacterCompareType::OrdinaryCharacter, { *consume().value().characters_without_null_termination() } });
|
|
||||||
|
|
||||||
} else if (match(TokenType::Circumflex)) {
|
|
||||||
auto t = consume();
|
|
||||||
|
|
||||||
if (values.is_empty())
|
|
||||||
values.append({ CharacterCompareType::Inverse, 0 });
|
|
||||||
else
|
|
||||||
values.append({ CharacterCompareType::OrdinaryCharacter, { *t.value().characters_without_null_termination() } });
|
|
||||||
|
|
||||||
} else if (match(TokenType::LeftBracket)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
if (match(TokenType::Period)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
// FIXME: Parse collating element, this is needed when we have locale support
|
|
||||||
// This could have impact on length parameter, I guess.
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
|
|
||||||
consume(TokenType::Period, Error::InvalidCollationElement);
|
|
||||||
consume(TokenType::RightBracket, Error::BracketMismatch);
|
|
||||||
|
|
||||||
} else if (match(TokenType::EqualSign)) {
|
|
||||||
consume();
|
|
||||||
// FIXME: Parse collating element, this is needed when we have locale support
|
|
||||||
// This could have impact on length parameter, I guess.
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
|
|
||||||
consume(TokenType::EqualSign, Error::InvalidCollationElement);
|
|
||||||
consume(TokenType::RightBracket, Error::BracketMismatch);
|
|
||||||
|
|
||||||
} else if (match(TokenType::Colon)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
CharacterClass ch_class;
|
|
||||||
// parse character class
|
|
||||||
if (match(TokenType::OrdinaryCharacter)) {
|
|
||||||
if (consume("alnum"))
|
|
||||||
ch_class = CharacterClass::Alnum;
|
|
||||||
else if (consume("alpha"))
|
|
||||||
ch_class = CharacterClass::Alpha;
|
|
||||||
else if (consume("blank"))
|
|
||||||
ch_class = CharacterClass::Blank;
|
|
||||||
else if (consume("cntrl"))
|
|
||||||
ch_class = CharacterClass::Cntrl;
|
|
||||||
else if (consume("digit"))
|
|
||||||
ch_class = CharacterClass::Digit;
|
|
||||||
else if (consume("graph"))
|
|
||||||
ch_class = CharacterClass::Graph;
|
|
||||||
else if (consume("lower"))
|
|
||||||
ch_class = CharacterClass::Lower;
|
|
||||||
else if (consume("print"))
|
|
||||||
ch_class = CharacterClass::Print;
|
|
||||||
else if (consume("punct"))
|
|
||||||
ch_class = CharacterClass::Punct;
|
|
||||||
else if (consume("space"))
|
|
||||||
ch_class = CharacterClass::Space;
|
|
||||||
else if (consume("upper"))
|
|
||||||
ch_class = CharacterClass::Upper;
|
|
||||||
else if (consume("xdigit"))
|
|
||||||
ch_class = CharacterClass::Xdigit;
|
|
||||||
else
|
|
||||||
return set_error(Error::InvalidCharacterClass);
|
|
||||||
|
|
||||||
values.append({ CharacterCompareType::CharacterClass, ch_class });
|
|
||||||
|
|
||||||
} else
|
|
||||||
return set_error(Error::InvalidCharacterClass);
|
|
||||||
|
|
||||||
// FIXME: we do not support locale specific character classes until locales are implemented
|
|
||||||
|
|
||||||
consume(TokenType::Colon, Error::InvalidCharacterClass);
|
|
||||||
consume(TokenType::RightBracket, Error::BracketMismatch);
|
|
||||||
}
|
|
||||||
} else if (match(TokenType::RightBracket)) {
|
|
||||||
|
|
||||||
if (values.is_empty() || (values.size() == 1 && values.last().type == CharacterCompareType::Inverse)) {
|
|
||||||
// handle bracket as ordinary character
|
|
||||||
values.append({ CharacterCompareType::OrdinaryCharacter, { *consume().value().characters_without_null_termination() } });
|
|
||||||
} else {
|
|
||||||
// closing bracket expression
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
// nothing matched, this is a failure, as at least the closing bracket must match...
|
|
||||||
return set_error(Error::BracketMismatch);
|
|
||||||
|
|
||||||
// check if range expression has to be completed...
|
|
||||||
if (values.size() >= 3 && values.at(values.size() - 2).type == CharacterCompareType::RangeExpressionDummy) {
|
|
||||||
if (values.last().type != CharacterCompareType::OrdinaryCharacter)
|
|
||||||
return set_error(Error::InvalidRange);
|
|
||||||
|
|
||||||
auto value2 = values.take_last();
|
|
||||||
values.take_last(); // RangeExpressionDummy
|
|
||||||
auto value1 = values.take_last();
|
|
||||||
|
|
||||||
values.append({ CharacterCompareType::RangeExpression, ByteCodeValue { value1.value.ch, value2.value.ch } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.size())
|
|
||||||
match_length_minimum = 1;
|
|
||||||
|
|
||||||
if (values.first().type == CharacterCompareType::Inverse)
|
|
||||||
match_length_minimum = 0;
|
|
||||||
|
|
||||||
insert_bytecode_compare_values(stack, move(values));
|
|
||||||
|
|
||||||
return !has_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PosixExtendedParser::parse_sub_expression(Vector<ByteCodeValue>& stack, size_t& match_length_minimum)
|
|
||||||
{
|
|
||||||
Vector<ByteCodeValue> bytecode;
|
|
||||||
size_t length = 0;
|
|
||||||
bool should_parse_repetition_symbol { false };
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (match_ordinary_characters()) {
|
|
||||||
Token start_token = m_parser_state.current_token;
|
|
||||||
Token last_token = m_parser_state.current_token;
|
|
||||||
for (;;) {
|
|
||||||
if (!match_ordinary_characters())
|
|
||||||
break;
|
|
||||||
++length;
|
|
||||||
last_token = consume();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length > 1) {
|
|
||||||
stack.empend(OpCode::Compare);
|
|
||||||
stack.empend(1ul); // number of arguments
|
|
||||||
stack.empend(CharacterCompareType::OrdinaryCharacters);
|
|
||||||
stack.empend(start_token.value().characters_without_null_termination());
|
|
||||||
stack.empend(length - ((match_repetition_symbol() && length > 1) ? 1 : 0)); // last character is inserted into 'bytecode' for duplication symbol handling
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((match_repetition_symbol() && length > 1) || length == 1) // Create own compare opcode for last character before duplication symbol
|
|
||||||
insert_bytecode_compare_values(bytecode, { { CharacterCompareType::OrdinaryCharacter, { last_token.value().characters_without_null_termination()[0] } } });
|
|
||||||
|
|
||||||
should_parse_repetition_symbol = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match_repetition_symbol())
|
|
||||||
return set_error(Error::InvalidRepetitionMarker);
|
|
||||||
|
|
||||||
if (match(TokenType::Period)) {
|
|
||||||
length = 1;
|
|
||||||
consume();
|
|
||||||
insert_bytecode_compare_values(bytecode, { { CharacterCompareType::AnySingleCharacter, { 0 } } });
|
|
||||||
should_parse_repetition_symbol = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::EscapeSequence)) {
|
|
||||||
length = 1;
|
|
||||||
Token t = consume();
|
|
||||||
#ifdef REGEX_DEBUG
|
|
||||||
printf("[PARSER] EscapeSequence with substring %s\n", String(t.value()).characters());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
insert_bytecode_compare_values(bytecode, { { CharacterCompareType::OrdinaryCharacter, { (char)t.value().characters_without_null_termination()[1] } } });
|
|
||||||
should_parse_repetition_symbol = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::LeftBracket)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
Vector<ByteCodeValue> sub_ops;
|
|
||||||
if (!parse_bracket_expression(sub_ops, length) || !sub_ops.size())
|
|
||||||
return set_error(Error::BracketMismatch);
|
|
||||||
|
|
||||||
bytecode.append(move(sub_ops));
|
|
||||||
|
|
||||||
consume(TokenType::RightBracket);
|
|
||||||
should_parse_repetition_symbol = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::RightBracket)) {
|
|
||||||
return set_error(Error::BracketMismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::RightCurly)) {
|
|
||||||
return set_error(Error::BraceMismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::Circumflex)) {
|
|
||||||
consume();
|
|
||||||
bytecode.empend(OpCode::CheckBegin);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::Dollar)) {
|
|
||||||
consume();
|
|
||||||
bytecode.empend(OpCode::CheckEnd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::LeftParen)) {
|
|
||||||
consume();
|
|
||||||
Optional<StringView> capture_group_name;
|
|
||||||
bool no_subexpression_match_qualifier = false;
|
|
||||||
if (match(TokenType::Questionmark)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
if (match(TokenType::Colon)) {
|
|
||||||
consume();
|
|
||||||
no_subexpression_match_qualifier = true;
|
|
||||||
} else if (consume("<")) { // named capturing group
|
|
||||||
|
|
||||||
Token start_token = m_parser_state.current_token;
|
|
||||||
Token last_token = m_parser_state.current_token;
|
|
||||||
size_t capture_group_name_length = 0;
|
|
||||||
for (;;) {
|
|
||||||
if (!match_ordinary_characters())
|
|
||||||
return set_error(Error::InvalidNameForCaptureGroup);
|
|
||||||
if (match(TokenType::OrdinaryCharacter) && m_parser_state.current_token.value()[0] == '>') {
|
|
||||||
consume();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++capture_group_name_length;
|
|
||||||
last_token = consume();
|
|
||||||
}
|
|
||||||
capture_group_name = StringView(start_token.value().characters_without_null_termination(), capture_group_name_length);
|
|
||||||
|
|
||||||
} else if (match(TokenType::EqualSign)) { // positive lookahead
|
|
||||||
consume();
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
} else if (consume("!")) { // negative lookahead
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
} else if (consume("<")) {
|
|
||||||
if (match(TokenType::EqualSign)) { // positive lookbehind
|
|
||||||
consume();
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
}
|
|
||||||
if (consume("!")) // negative lookbehind
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
} else {
|
|
||||||
return set_error(Error::InvalidRepetitionMarker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(m_parser_state.regex_options & (u8)AllFlags::NoSubExpressions || no_subexpression_match_qualifier)) {
|
|
||||||
if (capture_group_name.has_value())
|
|
||||||
insert_bytecode_group_capture_left(bytecode, capture_group_name.value());
|
|
||||||
else
|
|
||||||
insert_bytecode_group_capture_left(bytecode);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<ByteCodeValue> capture_group_bytecode;
|
|
||||||
|
|
||||||
bool res = !parse_root(capture_group_bytecode, length);
|
|
||||||
|
|
||||||
if (capture_group_bytecode.is_empty() && match(TokenType::RightParen))
|
|
||||||
return set_error(Error::ParenEmpty);
|
|
||||||
|
|
||||||
if (!res)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bytecode.append(move(capture_group_bytecode));
|
|
||||||
|
|
||||||
consume(TokenType::RightParen, Error::ParenMismatch);
|
|
||||||
|
|
||||||
if (!(m_parser_state.regex_options & (u8)AllFlags::NoSubExpressions || no_subexpression_match_qualifier)) {
|
|
||||||
if (capture_group_name.has_value()) {
|
|
||||||
insert_bytecode_group_capture_right(bytecode, capture_group_name.value());
|
|
||||||
++m_parser_state.named_capture_groups_count;
|
|
||||||
} else {
|
|
||||||
insert_bytecode_group_capture_right(bytecode);
|
|
||||||
++m_parser_state.capture_groups_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
should_parse_repetition_symbol = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match_repetition_symbol()) {
|
|
||||||
if (should_parse_repetition_symbol)
|
|
||||||
parse_repetition_symbol(bytecode, length);
|
|
||||||
else
|
|
||||||
return set_error(Error::InvalidRepetitionMarker);
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.append(move(bytecode));
|
|
||||||
match_length_minimum += length;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PosixExtendedParser::parse_root(Vector<ByteCodeValue>& stack, size_t& match_length_minimum)
|
|
||||||
{
|
|
||||||
Vector<ByteCodeValue> bytecode_left;
|
|
||||||
size_t match_length_minimum_left { 0 };
|
|
||||||
|
|
||||||
if (match_repetition_symbol())
|
|
||||||
return set_error(Error::InvalidRepetitionMarker);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (!parse_sub_expression(bytecode_left, match_length_minimum_left))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (match(TokenType::Pipe)) {
|
|
||||||
consume();
|
|
||||||
|
|
||||||
Vector<ByteCodeValue> bytecode_right;
|
|
||||||
size_t match_length_minimum_right { 0 };
|
|
||||||
|
|
||||||
if (!parse_root(bytecode_right, match_length_minimum_right) || bytecode_right.is_empty())
|
|
||||||
return set_error(Error::InvalidPattern);
|
|
||||||
|
|
||||||
Vector<ByteCodeValue> new_bytecode;
|
|
||||||
insert_bytecode_alternation(new_bytecode, move(bytecode_left), move(bytecode_right));
|
|
||||||
bytecode_left = move(new_bytecode);
|
|
||||||
match_length_minimum_left = min(match_length_minimum_right, match_length_minimum_left);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.append(move(bytecode_left));
|
|
||||||
match_length_minimum = match_length_minimum_left;
|
|
||||||
return !has_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,257 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "RegexError.h"
|
|
||||||
#include "RegexLexer.h"
|
|
||||||
#include "RegexOptions.h"
|
|
||||||
#include <AK/Forward.h>
|
|
||||||
#include <AK/Types.h>
|
|
||||||
#include <AK/Vector.h>
|
|
||||||
|
|
||||||
namespace AK {
|
|
||||||
namespace regex {
|
|
||||||
|
|
||||||
#define ENUMERATE_OPCODES \
|
|
||||||
__ENUMERATE_OPCODE(Compare) \
|
|
||||||
__ENUMERATE_OPCODE(Jump) \
|
|
||||||
__ENUMERATE_OPCODE(ForkJump) \
|
|
||||||
__ENUMERATE_OPCODE(ForkStay) \
|
|
||||||
__ENUMERATE_OPCODE(SaveLeftCaptureGroup) \
|
|
||||||
__ENUMERATE_OPCODE(SaveRightCaptureGroup) \
|
|
||||||
__ENUMERATE_OPCODE(SaveLeftNamedCaptureGroup) \
|
|
||||||
__ENUMERATE_OPCODE(SaveRightNamedCaptureGroup) \
|
|
||||||
__ENUMERATE_OPCODE(CheckBegin) \
|
|
||||||
__ENUMERATE_OPCODE(CheckEnd) \
|
|
||||||
__ENUMERATE_OPCODE(Exit)
|
|
||||||
|
|
||||||
enum class OpCode : u8 {
|
|
||||||
#define __ENUMERATE_OPCODE(x) x,
|
|
||||||
ENUMERATE_OPCODES
|
|
||||||
#undef __ENUMERATE_OPCODE
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CharacterCompareType : u8 {
|
|
||||||
Undefined,
|
|
||||||
Inverse,
|
|
||||||
AnySingleCharacter,
|
|
||||||
OrdinaryCharacter,
|
|
||||||
OrdinaryCharacters,
|
|
||||||
CharacterClass,
|
|
||||||
RangeExpression,
|
|
||||||
RangeExpressionDummy,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CharacterClass : u8 {
|
|
||||||
Alnum,
|
|
||||||
Cntrl,
|
|
||||||
Lower,
|
|
||||||
Space,
|
|
||||||
Alpha,
|
|
||||||
Digit,
|
|
||||||
Print,
|
|
||||||
Upper,
|
|
||||||
Blank,
|
|
||||||
Graph,
|
|
||||||
Punct,
|
|
||||||
Xdigit,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ByteCodeValue {
|
|
||||||
public:
|
|
||||||
union CompareValue {
|
|
||||||
CompareValue(const CharacterClass value)
|
|
||||||
: character_class(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
CompareValue(const char value1, const char value2)
|
|
||||||
: range_values { value1, value2 }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const CharacterClass character_class;
|
|
||||||
const struct {
|
|
||||||
const char from;
|
|
||||||
const char to;
|
|
||||||
} range_values;
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
const OpCode op_code;
|
|
||||||
const char* string;
|
|
||||||
const char ch;
|
|
||||||
const int number;
|
|
||||||
const size_t positive_number;
|
|
||||||
const CompareValue compare_value;
|
|
||||||
const CharacterCompareType compare_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* name() const;
|
|
||||||
static const char* name(OpCode);
|
|
||||||
|
|
||||||
ByteCodeValue(const OpCode value)
|
|
||||||
: op_code(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const char* value)
|
|
||||||
: string(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const char value)
|
|
||||||
: ch(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const int value)
|
|
||||||
: number(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const size_t value)
|
|
||||||
: positive_number(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const CharacterClass value)
|
|
||||||
: compare_value(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const char value1, const char value2)
|
|
||||||
: compare_value(value1, value2)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
ByteCodeValue(const CharacterCompareType value)
|
|
||||||
: compare_type(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~ByteCodeValue() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompareTypeAndValuePair {
|
|
||||||
CharacterCompareType type;
|
|
||||||
ByteCodeValue value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ParserResult {
|
|
||||||
Vector<ByteCodeValue> m_bytes;
|
|
||||||
size_t m_match_groups;
|
|
||||||
size_t m_min_match_length;
|
|
||||||
Error m_error;
|
|
||||||
Token m_error_token;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class Parser {
|
|
||||||
public:
|
|
||||||
explicit Parser(Lexer& lexer)
|
|
||||||
: m_parser_state(lexer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Parser(Lexer& lexer, T options)
|
|
||||||
: m_parser_state(lexer, options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Parser() = default;
|
|
||||||
|
|
||||||
virtual ParserResult parse(T options = {}, EngineOptions engine_options = {});
|
|
||||||
bool has_error() const { return m_parser_state.m_error != Error::NoError; }
|
|
||||||
Error error() const { return m_parser_state.m_error; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool parse_internal(Vector<ByteCodeValue>&, size_t& min_length) = 0;
|
|
||||||
|
|
||||||
bool match(TokenType type) const;
|
|
||||||
bool match(char ch) const;
|
|
||||||
Token consume();
|
|
||||||
Token consume(TokenType type, Error error = Error::InvalidPattern);
|
|
||||||
bool consume(const String&);
|
|
||||||
void reset();
|
|
||||||
bool done() const;
|
|
||||||
|
|
||||||
bool set_error(Error error);
|
|
||||||
|
|
||||||
void insert_bytecode_compare_values(Vector<ByteCodeValue>&, Vector<CompareTypeAndValuePair>&&);
|
|
||||||
void insert_bytecode_group_capture_left(Vector<ByteCodeValue>& stack);
|
|
||||||
void insert_bytecode_group_capture_right(Vector<ByteCodeValue>& stack);
|
|
||||||
void insert_bytecode_group_capture_left(Vector<ByteCodeValue>& stack, const StringView& name);
|
|
||||||
void insert_bytecode_group_capture_right(Vector<ByteCodeValue>& stack, const StringView& name);
|
|
||||||
void insert_bytecode_alternation(Vector<ByteCodeValue>& stack, Vector<ByteCodeValue>&&, Vector<ByteCodeValue>&&);
|
|
||||||
void insert_bytecode_repetition_min_max(Vector<ByteCodeValue>& bytecode_to_repeat, size_t minimum, Optional<size_t> maximum);
|
|
||||||
void insert_bytecode_repetition_n(Vector<ByteCodeValue>& stack, Vector<ByteCodeValue>& bytecode_to_repeat, size_t n);
|
|
||||||
void insert_bytecode_repetition_min_one(Vector<ByteCodeValue>& bytecode_to_repeat, bool greedy);
|
|
||||||
void insert_bytecode_repetition_any(Vector<ByteCodeValue>& bytecode_to_repeat, bool greedy);
|
|
||||||
void insert_bytecode_repetition_zero_or_one(Vector<ByteCodeValue>& bytecode_to_repeat, bool greedy);
|
|
||||||
|
|
||||||
struct ParserState {
|
|
||||||
Lexer& lexer;
|
|
||||||
Token current_token;
|
|
||||||
Error error = Error::NoError;
|
|
||||||
Token error_token { TokenType::Eof, 0, StringView(nullptr) };
|
|
||||||
Vector<ByteCodeValue> bytecode;
|
|
||||||
size_t capture_groups_count { 0 };
|
|
||||||
size_t named_capture_groups_count { 0 };
|
|
||||||
size_t match_length_minimum { 0 };
|
|
||||||
OptionsType regex_options;
|
|
||||||
explicit ParserState(Lexer& lexer)
|
|
||||||
: lexer(lexer)
|
|
||||||
, current_token(lexer.next())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
explicit ParserState(Lexer& lexer, Optional<OptionsType> regex_options)
|
|
||||||
: lexer(lexer)
|
|
||||||
, current_token(lexer.next())
|
|
||||||
, regex_options(regex_options.value_or({}))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ParserState m_parser_state;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PosixExtendedParser final : public Parser<PosixOptions> {
|
|
||||||
public:
|
|
||||||
explicit PosixExtendedParser(Lexer& lexer)
|
|
||||||
: Parser(lexer) {};
|
|
||||||
PosixExtendedParser(Lexer& lexer, Optional<OptionsType> regex_options)
|
|
||||||
: Parser(lexer, regex_options) {};
|
|
||||||
~PosixExtendedParser() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool match_repetition_symbol();
|
|
||||||
bool match_ordinary_characters();
|
|
||||||
|
|
||||||
bool parse_internal(Vector<ByteCodeValue>&, size_t&) override;
|
|
||||||
|
|
||||||
bool parse_root(Vector<ByteCodeValue>&, size_t&);
|
|
||||||
bool parse_sub_expression(Vector<ByteCodeValue>&, size_t&);
|
|
||||||
bool parse_bracket_expression(Vector<ByteCodeValue>&, size_t&);
|
|
||||||
bool parse_repetition_symbol(Vector<ByteCodeValue>&, size_t&);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using AK::regex::ParserResult;
|
|
||||||
using AK::regex::PosixExtendedParser;
|
|
|
@ -26,9 +26,9 @@
|
||||||
|
|
||||||
#include <AK/TestSuite.h> // import first, to prevent warning of ASSERT* redefinition
|
#include <AK/TestSuite.h> // import first, to prevent warning of ASSERT* redefinition
|
||||||
|
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibRegex/Regex.h>
|
#include <LibRegex/Regex.h>
|
||||||
#include <LibRegex/RegexDebug.h>
|
#include <LibRegex/RegexDebug.h>
|
||||||
#include <AK/StringBuilder.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static ECMAScriptOptions match_test_api_options(const ECMAScriptOptions options)
|
static ECMAScriptOptions match_test_api_options(const ECMAScriptOptions options)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue