Regex: Introduce RegexProgram::ThreadedExecutor and add line end/begin impl

This commit is contained in:
Maxime Coste 2017-09-17 19:48:23 +09:00
parent a448e1e222
commit f9dc6774b9

View File

@ -299,16 +299,21 @@ void dump(ConstArrayView<char> program)
} }
} }
struct StepResult struct ThreadedExecutor
{ {
ThreadedExecutor(ConstArrayView<char> program) : m_program{program} {}
struct StepResult
{
enum Result { Consumed, Matched, Failed } result; enum Result { Consumed, Matched, Failed } result;
const char* next = nullptr; const char* next = nullptr;
}; };
StepResult step_thread(const char* inst, char c, const char* start, Vector<const char*>& threads) StepResult step(const char* inst)
{ {
while (true) while (true)
{ {
auto c = m_pos == m_subject.end() ? 0 : *m_pos;
const RegexProgram::Op op = (RegexProgram::Op)*inst++; const RegexProgram::Op op = (RegexProgram::Op)*inst++;
switch (op) switch (op)
{ {
@ -319,79 +324,101 @@ StepResult step_thread(const char* inst, char c, const char* start, Vector<const
case RegexProgram::AnyChar: case RegexProgram::AnyChar:
return { StepResult::Consumed, inst }; return { StepResult::Consumed, inst };
case RegexProgram::Jump: case RegexProgram::Jump:
inst = start + *reinterpret_cast<const RegexProgram::Offset*>(inst); inst = m_program.begin() + *reinterpret_cast<const RegexProgram::Offset*>(inst);
break; break;
case RegexProgram::Split: case RegexProgram::Split:
{ {
const int count = *inst++; const int count = *inst++;
auto* offsets = reinterpret_cast<const RegexProgram::Offset*>(inst); auto* offsets = reinterpret_cast<const RegexProgram::Offset*>(inst);
for (int o = 1; o < count; ++o) for (int o = 1; o < count; ++o)
threads.push_back(start + offsets[o]); m_threads.push_back(m_program.begin() + offsets[o]);
inst = start + offsets[0]; inst = m_program.begin() + offsets[0];
break; break;
} }
case RegexProgram::LineStart: case RegexProgram::LineStart:
// TODO if (not is_line_start())
return { StepResult::Failed };
break; break;
case RegexProgram::LineEnd: case RegexProgram::LineEnd:
// TODO if (not is_line_end())
return { StepResult::Failed };
break; break;
case RegexProgram::Match: case RegexProgram::Match:
return { StepResult::Matched }; 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(); m_threads = Vector<const char*>{program.begin()};
Vector<const char*> threads = { start }; m_subject = data;
m_pos = data.begin();
for (auto c : data) for (m_pos = m_subject.begin(); m_pos != m_subject.end(); ++m_pos)
{ {
for (int i = 0; i < threads.size(); ++i) for (int i = 0; i < m_threads.size(); ++i)
{ {
auto res = step_thread(threads[i], c, start, threads); auto res = step(m_threads[i]);
threads[i] = res.next; m_threads[i] = res.next;
if (res.result == StepResult::Matched) if (res.result == StepResult::Matched)
return true; return true;
} }
threads.erase(std::remove(threads.begin(), threads.end(), nullptr), threads.end()); m_threads.erase(std::remove(m_threads.begin(), m_threads.end(), nullptr), m_threads.end());
if (threads.empty()) if (m_threads.empty())
break; break;
} }
// Step remaining threads to see if they match without consuming anything else // Step remaining threads to see if they match without consuming anything else
for (int i = 0; i < threads.size(); ++i) for (int i = 0; i < m_threads.size(); ++i)
{ {
if (step_thread(threads[i], 0, start, threads).result == StepResult::Matched) if (step(m_threads[i]).result == StepResult::Matched)
return true; return true;
} }
return false; return false;
} }
bool is_line_start() const
{
return m_pos == m_subject.begin() or *(m_pos-1) == '\n';
}
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"));
} }
}}; }};