#[macro_use] extern crate lazy_static;
extern crate regex;
extern crate bit_set;
extern crate memchr;
use std::usize;
use bit_set::BitSet;
pub mod parse;
pub mod analyze;
pub mod compile;
pub mod vm;
use parse::Parser;
use analyze::Analysis;
use compile::compile;
use vm::Prog;
const MAX_RECURSION: usize = 64;
pub type Result<T> = ::std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
ParseError,
UnclosedOpenParen,
InvalidRepeat,
RecursionExceeded,
LookBehindNotConst,
TrailingBackslash,
InvalidEscape,
UnclosedUnicodeName,
InvalidHex,
InvalidCodepointValue,
InvalidClass,
UnknownFlag,
NonUnicodeUnsupported,
InvalidBackref,
InnerError(regex::Error),
StackOverflow,
}
pub enum Regex {
Wrap {
inner: regex::Regex,
inner1: Option<Box<regex::Regex>>,
},
Impl {
prog: Prog,
n_groups: usize,
}
}
pub enum Captures<'t> {
Wrap {
inner: regex::Captures<'t>,
offset: usize,
enclosing_groups: usize,
},
Impl {
text: &'t str,
saves: Vec<usize>,
}
}
pub struct SubCaptures<'t> {
caps: &'t Captures<'t>,
i: usize,
}
impl Regex {
pub fn new(re: &str) -> Result<Regex> {
let (raw_e, backrefs) = try!(Expr::parse(re));
let e = Expr::Concat(vec![
Expr::Repeat {
child: Box::new(Expr::Any { newline: true }),
lo: 0, hi: usize::MAX, greedy: false
},
Expr::Group(Box::new(
raw_e
))
]);
let a = Analysis::analyze(&e, &backrefs);
let inner_info = &a.infos[4]; if !inner_info.hard {
let mut re_cooked = String::new();
let raw_e = match e {
Expr::Concat(ref v) =>
match v[1] {
Expr::Group(ref child) => child,
_ => unreachable!()
},
_ => unreachable!()
};
raw_e.to_str(&mut re_cooked, 0);
let inner = try!(compile::compile_inner(&re_cooked));
let inner1 = if inner_info.looks_left {
let re1 = ["^(?s:.)+?(", re_cooked.as_str(), ")"].concat();
let compiled = try!(compile::compile_inner(&re1));
Some(Box::new(compiled))
} else {
None
};
return Ok(Regex::Wrap {
inner: inner,
inner1: inner1,
});
}
let p = try!(compile(&a));
Ok(Regex::Impl {
prog: p,
n_groups: a.n_groups(),
})
}
pub fn is_match(&self, text: &str) -> Result<bool> {
match *self {
Regex::Wrap { ref inner, .. } => Ok(inner.is_match(text)),
Regex::Impl { ref prog, .. } => {
let result = try!(vm::run(prog, text, 0, 0));
Ok(result.is_some())
}
}
}
pub fn find(&self, text: &str) -> Result<Option<(usize, usize)>> {
match *self {
Regex::Wrap { ref inner, .. } => {
Ok(inner.find(text).map(|m| (m.start(), m.end())))
}
Regex::Impl { ref prog, .. } => {
let result = try!(vm::run(prog, text, 0, 0));
Ok(result.map(|saves| (saves[0], saves[1])))
}
}
}
pub fn captures<'t>(&self, text: &'t str) -> Result<Option<Captures<'t>>> {
match *self {
Regex::Wrap { ref inner, .. } =>
Ok(inner.captures(text).map(|caps| Captures::Wrap {
inner: caps,
offset: 0,
enclosing_groups: 0,
})),
Regex::Impl { ref prog, n_groups } => {
let result = try!(vm::run(prog, text, 0, 0));
Ok(result.map(|mut saves| {
saves.truncate(n_groups * 2);
Captures::Impl {
text: text,
saves: saves
}
}))
}
}
}
pub fn captures_from_pos<'t>(&self, text: &'t str, pos: usize) ->
Result<Option<Captures<'t>>> {
match *self {
Regex::Wrap { ref inner, ref inner1 } => {
if inner1.is_none() || pos == 0 {
Ok(inner.captures(&text[pos..]).map(|caps| Captures::Wrap {
inner: caps,
offset: pos,
enclosing_groups: 0,
}))
} else {
let ix = prev_codepoint_ix(text, pos);
let inner1 = inner1.as_ref().unwrap();
Ok(inner1.captures(&text[ix..]).map(|caps| Captures::Wrap {
inner: caps,
offset: ix,
enclosing_groups: 1,
}))
}
}
Regex::Impl { ref prog, n_groups } => {
let result = try!(vm::run(prog, text, pos, 0));
Ok(result.map(|mut saves| {
saves.truncate(n_groups * 2);
Captures::Impl {
text: text,
saves: saves
}
}))
}
}
}
pub fn debug_print(&self) {
match *self {
Regex::Wrap { ref inner, .. } => println!("wrapped {:?}", inner),
Regex::Impl { ref prog, .. } => prog.debug_print()
}
}
}
impl<'t> Captures<'t> {
pub fn pos(&self, i: usize) -> Option<(usize, usize)> {
match *self {
Captures::Wrap { ref inner, ref offset, enclosing_groups } => {
inner.get(i + enclosing_groups).map((|m|
(m.start() + offset, m.end() + offset)))
}
Captures::Impl { ref saves, .. } => {
if i >= saves.len() {
return None;
}
let lo = saves[i * 2];
if lo == std::usize::MAX {
return None;
}
let hi = saves[i * 2 + 1];
Some((lo, hi))
}
}
}
pub fn at(&self, i: usize) -> Option<&'t str> {
match *self {
Captures::Wrap { ref inner, enclosing_groups, .. } => {
inner.get(i + enclosing_groups).map(|m| m.as_str())
}
Captures::Impl { text, .. } => {
self.pos(i).map(|(lo, hi)|
&text[lo..hi]
)
}
}
}
pub fn iter(&'t self) -> SubCaptures<'t> {
SubCaptures { caps: self, i: 0 }
}
pub fn len(&self) -> usize {
match *self {
Captures::Wrap { ref inner, enclosing_groups, .. } => {
inner.len() - enclosing_groups
}
Captures::Impl { ref saves, .. } => saves.len() / 2
}
}
pub fn is_empty(&self) -> bool {
match *self {
Captures::Wrap { ref inner, enclosing_groups, .. } =>
inner.len() == enclosing_groups,
Captures::Impl { ref saves, .. } => saves.is_empty()
}
}
}
impl<'t> Iterator for SubCaptures<'t> {
type Item = Option<&'t str>;
fn next(&mut self) -> Option<Option<&'t str>> {
if self.i < self.caps.len() {
let result = self.caps.at(self.i);
self.i += 1;
Some(result)
} else {
None
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Expr {
Empty,
Any { newline: bool },
StartText,
EndText,
StartLine,
EndLine,
Literal {
val: String,
casei: bool,
},
Concat(Vec<Expr>),
Alt(Vec<Expr>),
Group(Box<Expr>),
LookAround(Box<Expr>, LookAround),
Repeat {
child: Box<Expr>,
lo: usize,
hi: usize,
greedy: bool,
},
Delegate {
inner: String,
size: usize, },
Backref(usize),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LookAround {
LookAhead,
LookAheadNeg,
LookBehind,
LookBehindNeg,
}
fn push_usize(s: &mut String, x: usize) {
if x >= 10 {
push_usize(s, x / 10);
s.push((b'0' + (x % 10) as u8) as char);
} else {
s.push((b'0' + (x as u8)) as char);
}
}
fn push_quoted(buf: &mut String, s: &str) {
for c in s.chars() {
match c {
'\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' |
'[' | ']' | '{' | '}' | '^' | '$' | '#' => buf.push('\\'),
_ => ()
}
buf.push(c);
}
}
impl Expr {
pub fn parse(re: &str) -> Result<(Expr, BitSet)> {
Parser::parse(re)
}
pub fn to_str(&self, buf: &mut String, precedence: u8) {
match *self {
Expr::Empty => (),
Expr::Any { newline } => buf.push_str(
if newline { "(?s:.)" } else { "." }
),
Expr::Literal{ ref val, casei } => {
if casei { buf.push_str("(?i:"); }
push_quoted(buf, val);
if casei { buf.push_str(")"); }
}
Expr::StartText => buf.push('^'),
Expr::EndText => buf.push('$'),
Expr::StartLine => buf.push_str("(?m:^)"),
Expr::EndLine => buf.push_str("(?m:$)"),
Expr::Concat(ref children) => {
if precedence > 1 {
buf.push_str("(?:");
}
for child in children {
child.to_str(buf, 2);
}
if precedence > 1 {
buf.push(')')
}
}
Expr::Alt(ref children) => {
if precedence > 0 {
buf.push_str("(?:");
}
children[0].to_str(buf, 1);
for child in &children[1..] {
buf.push('|');
child.to_str(buf, 1);
}
if precedence > 0 {
buf.push(')');
}
}
Expr::Group(ref child) => {
buf.push('(');
child.to_str(buf, 0);
buf.push(')');
}
Expr::Repeat{ ref child, lo, hi, greedy } => {
if precedence > 2 {
buf.push_str("(?:");
}
child.to_str(buf, 3);
buf.push('{');
push_usize(buf, lo);
buf.push(',');
if hi != usize::MAX {
push_usize(buf, hi);
}
buf.push('}');
if !greedy {
buf.push('?');
}
if precedence > 2 {
buf.push(')');
}
}
Expr::Delegate{ ref inner, .. } => {
buf.push_str(inner);
}
_ => panic!("attempting to format hard expr")
}
}
}
fn prev_codepoint_ix(s: &str, mut ix: usize) -> usize {
let bytes = s.as_bytes();
loop {
ix -= 1;
if (bytes[ix] as i8) >= -0x40 {
break;
}
}
ix
}
fn codepoint_len(b: u8) -> usize {
match b {
b if b < 0x80 => 1,
b if b < 0xe0 => 2,
b if b < 0xf0 => 3,
_ => 4
}
}
#[cfg(test)]
mod tests {
use Expr;
use parse::make_literal;
#[test]
fn to_str_concat_alt() {
let mut s = String::new();
let e = Expr::Concat(vec![
Expr::Alt(vec![
make_literal("a"),
make_literal("b"),
]),
make_literal("c"),
]);
e.to_str(&mut s, 0);
assert_eq!(s, "(?:a|b)c");
}
#[test]
fn to_str_rep_concat() {
let mut s = String::new();
let e = Expr::Repeat{ child: Box::new(
Expr::Concat(vec![
make_literal("a"),
make_literal("b"),
])),
lo: 2, hi: 3, greedy: true
};
e.to_str(&mut s, 0);
assert_eq!(s, "(?:ab){2,3}");
}
#[test]
fn to_str_group_alt() {
let mut s = String::new();
let e = Expr::Group(Box::new(
Expr::Alt(vec![
make_literal("a"),
make_literal("b"),
])
));
e.to_str(&mut s, 0);
assert_eq!(s, "(a|b)");
}
}