Regex: Introduce RegexProgram::ThreadedExecutor and add line end/begin impl
This commit is contained in:
parent
a448e1e222
commit
f9dc6774b9
|
@ -299,99 +299,126 @@ void dump(ConstArrayView<char> program)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StepResult
|
struct ThreadedExecutor
|
||||||
{
|
{
|
||||||
enum Result { Consumed, Matched, Failed } result;
|
ThreadedExecutor(ConstArrayView<char> program) : m_program{program} {}
|
||||||
const char* next = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
StepResult step_thread(const char* inst, char c, const char* start, Vector<const char*>& threads)
|
struct StepResult
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
const RegexProgram::Op op = (RegexProgram::Op)*inst++;
|
enum Result { Consumed, Matched, Failed } result;
|
||||||
switch (op)
|
const char* next = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
StepResult step(const char* inst)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
case RegexProgram::Literal:
|
auto c = m_pos == m_subject.end() ? 0 : *m_pos;
|
||||||
if (*inst++ == c)
|
const RegexProgram::Op op = (RegexProgram::Op)*inst++;
|
||||||
return { StepResult::Consumed, inst };
|
switch (op)
|
||||||
return { StepResult::Failed };
|
|
||||||
case RegexProgram::AnyChar:
|
|
||||||
return { StepResult::Consumed, inst };
|
|
||||||
case RegexProgram::Jump:
|
|
||||||
inst = start + *reinterpret_cast<const RegexProgram::Offset*>(inst);
|
|
||||||
break;
|
|
||||||
case RegexProgram::Split:
|
|
||||||
{
|
{
|
||||||
const int count = *inst++;
|
case RegexProgram::Literal:
|
||||||
auto* offsets = reinterpret_cast<const RegexProgram::Offset*>(inst);
|
if (*inst++ == c)
|
||||||
for (int o = 1; o < count; ++o)
|
return { StepResult::Consumed, inst };
|
||||||
threads.push_back(start + offsets[o]);
|
return { StepResult::Failed };
|
||||||
inst = start + offsets[0];
|
case RegexProgram::AnyChar:
|
||||||
break;
|
return { StepResult::Consumed, inst };
|
||||||
|
case RegexProgram::Jump:
|
||||||
|
inst = m_program.begin() + *reinterpret_cast<const RegexProgram::Offset*>(inst);
|
||||||
|
break;
|
||||||
|
case RegexProgram::Split:
|
||||||
|
{
|
||||||
|
const int count = *inst++;
|
||||||
|
auto* offsets = reinterpret_cast<const RegexProgram::Offset*>(inst);
|
||||||
|
for (int o = 1; o < count; ++o)
|
||||||
|
m_threads.push_back(m_program.begin() + offsets[o]);
|
||||||
|
inst = m_program.begin() + offsets[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RegexProgram::LineStart:
|
||||||
|
if (not is_line_start())
|
||||||
|
return { StepResult::Failed };
|
||||||
|
break;
|
||||||
|
case RegexProgram::LineEnd:
|
||||||
|
if (not is_line_end())
|
||||||
|
return { StepResult::Failed };
|
||||||
|
break;
|
||||||
|
case RegexProgram::Match:
|
||||||
|
return { StepResult::Matched };
|
||||||
}
|
}
|
||||||
case RegexProgram::LineStart:
|
|
||||||
// TODO
|
|
||||||
break;
|
|
||||||
case RegexProgram::LineEnd:
|
|
||||||
// TODO
|
|
||||||
break;
|
|
||||||
case RegexProgram::Match:
|
|
||||||
return { StepResult::Matched };
|
|
||||||
}
|
}
|
||||||
|
return { StepResult::Failed };
|
||||||
}
|
}
|
||||||
return { StepResult::Failed };
|
|
||||||
}
|
|
||||||
|
|
||||||
bool match(ConstArrayView<char> program, StringView data)
|
bool match(ConstArrayView<char> program, StringView data)
|
||||||
{
|
|
||||||
const char* start = program.begin();
|
|
||||||
Vector<const char*> threads = { start };
|
|
||||||
|
|
||||||
for (auto c : data)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < threads.size(); ++i)
|
m_threads = Vector<const char*>{program.begin()};
|
||||||
|
m_subject = data;
|
||||||
|
m_pos = data.begin();
|
||||||
|
|
||||||
|
for (m_pos = m_subject.begin(); m_pos != m_subject.end(); ++m_pos)
|
||||||
{
|
{
|
||||||
auto res = step_thread(threads[i], c, start, threads);
|
for (int i = 0; i < m_threads.size(); ++i)
|
||||||
threads[i] = res.next;
|
{
|
||||||
if (res.result == StepResult::Matched)
|
auto res = step(m_threads[i]);
|
||||||
|
m_threads[i] = res.next;
|
||||||
|
if (res.result == StepResult::Matched)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_threads.erase(std::remove(m_threads.begin(), m_threads.end(), nullptr), m_threads.end());
|
||||||
|
if (m_threads.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step remaining threads to see if they match without consuming anything else
|
||||||
|
for (int i = 0; i < m_threads.size(); ++i)
|
||||||
|
{
|
||||||
|
if (step(m_threads[i]).result == StepResult::Matched)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
threads.erase(std::remove(threads.begin(), threads.end(), nullptr), threads.end());
|
return false;
|
||||||
if (threads.empty())
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step remaining threads to see if they match without consuming anything else
|
bool is_line_start() const
|
||||||
for (int i = 0; i < threads.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if (step_thread(threads[i], 0, start, threads).result == StepResult::Matched)
|
return m_pos == m_subject.begin() or *(m_pos-1) == '\n';
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
bool is_line_end() const
|
||||||
|
{
|
||||||
|
return m_pos == m_subject.end() or *m_pos == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstArrayView<char> m_program;
|
||||||
|
Vector<const char*> m_threads;
|
||||||
|
StringView m_subject;
|
||||||
|
const char* m_pos;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto test_regex = UnitTest{[]{
|
auto test_regex = UnitTest{[]{
|
||||||
|
using Exec = RegexProgram::ThreadedExecutor;
|
||||||
{
|
{
|
||||||
StringView re = "a*b";
|
StringView re = "a*b";
|
||||||
auto program = RegexCompiler::compile(re.begin(), re.end());
|
auto program = RegexCompiler::compile(re.begin(), re.end());
|
||||||
RegexProgram::dump(program);
|
RegexProgram::dump(program);
|
||||||
kak_assert(RegexProgram::match(program, "b"));
|
Exec exec{program};
|
||||||
kak_assert(RegexProgram::match(program, "ab"));
|
kak_assert(exec.match(program, "b"));
|
||||||
kak_assert(RegexProgram::match(program, "aaab"));
|
kak_assert(exec.match(program, "ab"));
|
||||||
kak_assert(not RegexProgram::match(program, "acb"));
|
kak_assert(exec.match(program, "aaab"));
|
||||||
kak_assert(not RegexProgram::match(program, ""));
|
kak_assert(not exec.match(program, "acb"));
|
||||||
|
kak_assert(not exec.match(program, ""));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
StringView re = "^(foo|qux)+(bar)?baz$";
|
StringView re = "^(foo|qux)+(bar)?baz$";
|
||||||
auto program = RegexCompiler::compile(re.begin(), re.end());
|
auto program = RegexCompiler::compile(re.begin(), re.end());
|
||||||
RegexProgram::dump(program);
|
RegexProgram::dump(program);
|
||||||
kak_assert(RegexProgram::match(program, "fooquxbarbaz"));
|
Exec exec{program};
|
||||||
kak_assert(not RegexProgram::match(program, "quxbar"));
|
kak_assert(exec.match(program, "fooquxbarbaz"));
|
||||||
kak_assert(not RegexProgram::match(program, "blahblah"));
|
kak_assert(not exec.match(program, "quxbar"));
|
||||||
kak_assert(RegexProgram::match(program, "foobaz"));
|
kak_assert(not exec.match(program, "blahblah"));
|
||||||
kak_assert(RegexProgram::match(program, "quxbaz"));
|
kak_assert(exec.match(program, "foobaz"));
|
||||||
|
kak_assert(exec.match(program, "quxbaz"));
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user