use super::QueryError;
mod atoms;
mod classes;
mod emitter;
mod quantifier;
use emitter::Emitter;
pub(crate) const MAX_REPEAT: u32 = 10_000;
const MAX_INSTS: usize = 200_000;
const MAX_DEPTH: u32 = 256;
#[derive(Debug, Clone)]
pub(crate) enum Inst {
Consume(Class),
Split {
prefer: u32,
alt: u32,
},
Jmp(u32),
Save(usize),
Guard(usize),
LineStart,
LineEnd,
BufStart,
BufEnd,
WordStart,
WordEnd,
Backref {
group: usize,
},
Fail,
Match,
}
#[derive(Debug, Clone)]
pub(crate) enum Class {
Str {
bytes: Vec<u8>,
fold: bool,
},
Any {
nl: bool,
},
Break,
Set(CharSet),
}
#[derive(Debug, Clone)]
pub(crate) struct CharSet {
pub(super) ascii: u128,
pub(super) negated: bool,
pub(super) nl: bool,
pub(super) empty: bool,
pub(super) unicode_ranges: Vec<std::ops::RangeInclusive<char>>,
pub(crate) fold: bool,
}
impl CharSet {
fn new() -> Self {
Self {
ascii: 0,
negated: false,
nl: false,
empty: true,
unicode_ranges: Vec::new(),
fold: false,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct Program {
pub(crate) insts: Vec<Inst>,
pub(crate) n_slots: usize,
pub(crate) n_loops: usize,
pub(crate) fold: bool,
pub(crate) whole_word: bool,
pub(crate) literal: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
enum Node {
Seq(Vec<Node>),
Alt(Vec<Node>),
Rep {
node: Box<Node>,
min: u32,
max: Option<u32>,
greedy: bool,
},
Group {
idx: Option<usize>,
node: Box<Node>,
},
PrefixOpt(Vec<Node>),
Literal(Vec<u8>),
Set(CharSet),
Any {
nl: bool,
},
Break,
Backref(usize),
LineStart,
LineEnd,
BufStart,
BufEnd,
WordStart,
WordEnd,
SetStart,
SetEnd,
}
#[derive(Clone, Copy, PartialEq)]
enum Magic {
Magic,
VeryMagic,
}
struct Parser<'a> {
pat: &'a [u8],
at: usize,
mode: Magic,
ngroups: usize,
fold: Option<bool>,
depth: u32,
branch_start: bool,
}
pub(super) fn compile(pattern: &str, whole_word: bool) -> Result<Program, QueryError> {
let mut p = Parser {
pat: pattern.as_bytes(),
at: 0,
mode: Magic::Magic,
ngroups: 0,
fold: None,
depth: 0,
branch_start: true,
};
if p.pat.starts_with(b"\\v") {
p.mode = Magic::VeryMagic;
p.at = 2;
} else if p.pat.starts_with(b"\\m") {
p.at = 2;
}
let node = p.parse_alt()?;
if p.at < p.pat.len() {
return Err(QueryError::UnbalancedGroup { at: p.at });
}
let fold = p.fold.unwrap_or(false);
let mut e = Emitter {
insts: Vec::new(),
n_slots: 2 + 2 * p.ngroups,
n_loops: 0,
fold,
};
e.node(&node)?;
e.insts.push(Inst::Match);
let literal = if whole_word || fold || e.insts.len() != 2 {
None
} else {
match (&e.insts[0], &e.insts[1]) {
(Inst::Consume(Class::Str { bytes, fold: false }), Inst::Match) => Some(bytes.clone()),
_ => None,
}
};
Ok(Program {
insts: e.insts,
n_slots: e.n_slots,
n_loops: e.n_loops,
fold,
whole_word,
literal,
})
}
impl<'a> Parser<'a> {
fn peek(&self) -> Option<u8> {
self.pat.get(self.at).copied()
}
fn peek2(&self) -> Option<u8> {
self.pat.get(self.at + 1).copied()
}
fn starts_with(&self, s: &[u8]) -> bool {
self.pat[self.at.min(self.pat.len())..].starts_with(s)
}
fn char_at(&self, at: usize) -> Result<char, QueryError> {
let lead = *self.pat.get(at).ok_or(QueryError::Unsupported {
construct: "invalid UTF-8 in pattern",
at,
})?;
let len = utf8_len(lead);
let end = (at + len).min(self.pat.len());
std::str::from_utf8(&self.pat[at..end])
.ok()
.and_then(|s| s.chars().next())
.ok_or(QueryError::Unsupported {
construct: "invalid UTF-8 in pattern",
at,
})
}
fn parse_alt(&mut self) -> Result<Node, QueryError> {
self.depth += 1;
if self.depth > MAX_DEPTH {
return Err(QueryError::TooComplex);
}
let mut branches = vec![self.parse_seq()?];
loop {
let sep: &[u8] = match self.mode {
Magic::Magic => b"\\|",
Magic::VeryMagic => b"|",
};
if !self.starts_with(sep) {
break;
}
self.at += sep.len();
self.branch_start = true;
branches.push(self.parse_seq()?);
}
self.depth -= 1;
Ok(if branches.len() == 1 {
branches.pop().unwrap_or(Node::Seq(vec![]))
} else {
Node::Alt(branches)
})
}
fn parse_seq(&mut self) -> Result<Node, QueryError> {
let mut items: Vec<Node> = Vec::new();
while let Some(c) = self.peek() {
match self.mode {
Magic::Magic => {
if self.starts_with(b"\\|") || self.starts_with(b"\\)") {
break;
}
}
Magic::VeryMagic => {
if c == b'|' || c == b')' {
break;
}
}
}
let atom = self.parse_atom()?;
let atom = self.parse_quantifier(atom)?;
self.branch_start = Self::is_break_atom(&atom);
if let Node::Literal(bytes) = &atom {
if let Some(Node::Literal(prev)) = items.last_mut() {
prev.extend_from_slice(bytes);
continue;
}
}
items.push(atom);
}
Ok(Node::Seq(items))
}
}
fn find_sub(hay: &[u8], needle: &[u8]) -> Option<usize> {
hay.windows(needle.len()).position(|w| w == needle)
}
fn utf8_len(b: u8) -> usize {
match b {
0x00..=0x7f => 1,
0xc0..=0xdf => 2,
0xe0..=0xef => 3,
_ => 4,
}
}