use crate::nfa::StateId;
use std::collections::{BinaryHeap, HashMap};
#[derive(Debug)]
pub struct PendingThread {
pub pos: usize,
pub seq: u64,
pub thread: Thread,
}
impl PartialEq for PendingThread {
fn eq(&self, other: &Self) -> bool {
self.pos == other.pos && self.thread.start == other.thread.start && self.seq == other.seq
}
}
impl Eq for PendingThread {}
impl PartialOrd for PendingThread {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PendingThread {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other
.pos
.cmp(&self.pos)
.then_with(|| other.thread.start.cmp(&self.thread.start))
.then_with(|| other.seq.cmp(&self.seq))
}
}
#[derive(Debug)]
pub struct PikeVmContext {
pub current_threads: Vec<Thread>,
pub future_threads: BinaryHeap<PendingThread>,
pub visited: Vec<usize>,
pub generation: usize,
pub seq_counter: u64,
pub epsilon_stack: Vec<Thread>,
pub lookaround_cache: HashMap<(StateId, usize), bool>,
pub capture_arena: CaptureArena,
}
impl PikeVmContext {
pub fn new(state_count: usize) -> Self {
Self {
current_threads: Vec::with_capacity(32),
future_threads: BinaryHeap::new(),
visited: vec![0; state_count],
generation: 0,
seq_counter: 0,
epsilon_stack: Vec::with_capacity(32),
lookaround_cache: HashMap::new(),
capture_arena: Vec::with_capacity(64),
}
}
#[inline]
pub fn reset(&mut self) {
self.current_threads.clear();
self.future_threads.clear();
self.epsilon_stack.clear();
self.generation = self.generation.wrapping_add(1);
self.seq_counter = 0;
self.lookaround_cache.clear();
self.capture_arena.clear();
}
#[inline]
pub fn ensure_state_capacity(&mut self, state_count: usize) {
if self.visited.len() < state_count {
self.visited.resize(state_count, 0);
}
}
}
#[derive(Debug, Clone)]
pub enum CaptureAction {
Start(u32, usize),
End(u32, usize),
}
#[derive(Debug, Clone)]
pub struct CaptureNode {
pub action: CaptureAction,
pub parent: Option<u32>,
}
pub type CaptureArena = Vec<CaptureNode>;
#[derive(Debug, Clone, Copy)]
pub struct Thread {
pub state: StateId,
pub capture_head: Option<u32>,
pub capture_count: usize,
pub start: usize,
}
impl Thread {
#[inline]
pub fn new(state: StateId, capture_count: usize, start: usize) -> Self {
Self {
state,
capture_head: None,
capture_count,
start,
}
}
#[inline]
pub fn clone_with_state(&self, state: StateId) -> Self {
Self { state, ..*self }
}
#[inline]
pub fn record_capture_start(&mut self, arena: &mut CaptureArena, group_idx: u32, pos: usize) {
self.push_action(arena, CaptureAction::Start(group_idx, pos));
}
#[inline]
pub fn record_capture_end(&mut self, arena: &mut CaptureArena, group_idx: u32, pos: usize) {
self.push_action(arena, CaptureAction::End(group_idx, pos));
}
#[inline]
fn push_action(&mut self, arena: &mut CaptureArena, action: CaptureAction) {
let Ok(index) = u32::try_from(arena.len()) else {
return;
};
arena.push(CaptureNode {
action,
parent: self.capture_head,
});
self.capture_head = Some(index);
}
pub fn reconstruct_captures(&self, arena: &CaptureArena) -> Vec<Option<(usize, usize)>> {
let mut captures = vec![None; self.capture_count + 1];
let mut actions = Vec::new();
let mut current = self.capture_head;
while let Some(index) = current {
let Some(node) = arena.get(index as usize) else {
break;
};
actions.push(&node.action);
current = node.parent;
}
for action in actions.into_iter().rev() {
match action {
CaptureAction::Start(idx, pos) => {
let idx = *idx as usize;
if idx < captures.len() {
captures[idx] = Some((*pos, *pos));
}
}
CaptureAction::End(idx, pos) => {
let idx = *idx as usize;
if idx < captures.len() {
if let Some((start, _)) = captures[idx] {
captures[idx] = Some((start, *pos));
}
}
}
}
}
captures
}
pub fn get_capture(&self, arena: &CaptureArena, group_idx: u32) -> Option<(usize, usize)> {
let mut start: Option<usize> = None;
let mut end: Option<usize> = None;
let mut current = self.capture_head;
while let Some(index) = current {
let Some(node) = arena.get(index as usize) else {
break;
};
match &node.action {
CaptureAction::Start(idx, pos) if *idx == group_idx && start.is_none() => {
start = Some(*pos);
}
CaptureAction::End(idx, pos) if *idx == group_idx && end.is_none() => {
end = Some(*pos);
}
_ => {}
}
if start.is_some() && end.is_some() {
break;
}
current = node.parent;
}
match (start, end) {
(Some(s), Some(e)) => Some((s, e)),
_ => None,
}
}
}
pub enum InstructionResult {
Continue,
Kill,
Jump(usize),
}
#[inline]
pub fn decode_utf8_codepoint(bytes: &[u8]) -> Option<(u32, usize)> {
if bytes.is_empty() {
return None;
}
let first = bytes[0];
if first < 0x80 {
return Some((first as u32, 1));
}
if first < 0xC0 {
return None;
}
if first < 0xE0 {
if bytes.len() < 2 {
return None;
}
let b1 = bytes[1];
if (b1 & 0xC0) != 0x80 {
return None;
}
let cp = ((first as u32 & 0x1F) << 6) | (b1 as u32 & 0x3F);
return Some((cp, 2));
}
if first < 0xF0 {
if bytes.len() < 3 {
return None;
}
let b1 = bytes[1];
let b2 = bytes[2];
if (b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 {
return None;
}
let cp = ((first as u32 & 0x0F) << 12) | ((b1 as u32 & 0x3F) << 6) | (b2 as u32 & 0x3F);
return Some((cp, 3));
}
if first < 0xF8 {
if bytes.len() < 4 {
return None;
}
let b1 = bytes[1];
let b2 = bytes[2];
let b3 = bytes[3];
if (b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 {
return None;
}
let cp = ((first as u32 & 0x07) << 18)
| ((b1 as u32 & 0x3F) << 12)
| ((b2 as u32 & 0x3F) << 6)
| (b3 as u32 & 0x3F);
return Some((cp, 4));
}
None
}