Skip to content

Commit

Permalink
Change regex! macro to expand to a constexpr, allowing to put it in a…
Browse files Browse the repository at this point in the history
… static
  • Loading branch information
Kimundi committed May 25, 2014
1 parent 759517c commit b997de5
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/libregex/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ pub mod native {
// undesirable consequences (such as requiring a dependency on
// `libsyntax`).
//
// Secondly, the code generated generated by `regex!` must *also* be able
// Secondly, the code generated by `regex!` must *also* be able
// to access various functions in this crate to reduce code duplication
// and to provide a value with precisely the same `Regex` type in this
// crate. This, AFAIK, is impossible to mitigate.
Expand Down
117 changes: 83 additions & 34 deletions src/libregex/re.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,38 +100,45 @@ pub fn is_match(regex: &str, text: &str) -> Result<bool, parse::Error> {
/// documentation.
#[deriving(Clone)]
#[allow(visible_private_types)]
pub struct Regex {
/// The representation of `Regex` is exported to support the `regex!`
/// syntax extension. Do not rely on it.
///
/// See the comments for the `program` module in `lib.rs` for a more
/// detailed explanation for what `regex!` requires.
pub enum Regex {
// The representation of `Regex` is exported to support the `regex!`
// syntax extension. Do not rely on it.
//
// See the comments for the `program` module in `lib.rs` for a more
// detailed explanation for what `regex!` requires.
#[doc(hidden)]
pub original: String,
Dynamic(Dynamic),
#[doc(hidden)]
pub names: Vec<Option<String>>,
Native(Native),
}

#[deriving(Clone)]
#[doc(hidden)]
pub struct Dynamic {
original: String,
names: Vec<Option<String>>,
#[doc(hidden)]
pub p: MaybeNative,
pub prog: Program
}

impl fmt::Show for Regex {
/// Shows the original regular expression.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.original)
}
#[doc(hidden)]
pub struct Native {
#[doc(hidden)]
pub original: &'static str,
#[doc(hidden)]
pub names: &'static [Option<&'static str>],
#[doc(hidden)]
pub prog: fn(MatchKind, &str, uint, uint) -> Vec<Option<uint>>
}

pub enum MaybeNative {
Dynamic(Program),
Native(fn(MatchKind, &str, uint, uint) -> Vec<Option<uint>>),
impl Clone for Native {
fn clone(&self) -> Native { *self }
}

impl Clone for MaybeNative {
fn clone(&self) -> MaybeNative {
match *self {
Dynamic(ref p) => Dynamic(p.clone()),
Native(fp) => Native(fp),
}
impl fmt::Show for Regex {
/// Shows the original regular expression.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}

Expand All @@ -146,10 +153,11 @@ impl Regex {
pub fn new(re: &str) -> Result<Regex, parse::Error> {
let ast = try!(parse::parse(re));
let (prog, names) = Program::new(ast);
Ok(Regex {
Ok(Dynamic(Dynamic {
original: re.to_strbuf(),
names: names, p: Dynamic(prog),
})
names: names,
prog: prog,
}))
}

/// Returns true if and only if the regex matches the string given.
Expand Down Expand Up @@ -495,6 +503,46 @@ impl Regex {
}
new.append(text.slice(last_match, text.len()))
}

/// Returns the original string of this regex.
pub fn as_str<'a>(&'a self) -> &'a str {
match *self {
Dynamic(Dynamic { ref original, .. }) => original.as_slice(),
Native(Native { ref original, .. }) => original.as_slice(),
}
}

#[doc(hidden)]
#[allow(visible_private_types)]
#[experimental]
pub fn names_iter<'a>(&'a self) -> NamesIter<'a> {
match *self {
Native(ref n) => NamesIterNative(n.names.iter()),
Dynamic(ref d) => NamesIterDynamic(d.names.iter())
}
}

fn names_len(&self) -> uint {
match *self {
Native(ref n) => n.names.len(),
Dynamic(ref d) => d.names.len()
}
}

}

enum NamesIter<'a> {
NamesIterNative(::std::slice::Items<'a, Option<&'static str>>),
NamesIterDynamic(::std::slice::Items<'a, Option<String>>)
}

impl<'a> Iterator<Option<String>> for NamesIter<'a> {
fn next(&mut self) -> Option<Option<String>> {
match *self {
NamesIterNative(ref mut i) => i.next().map(|x| x.map(|s| s.to_strbuf())),
NamesIterDynamic(ref mut i) => i.next().map(|x| x.as_ref().map(|s| s.to_strbuf())),
}
}
}

/// NoExpand indicates literal string replacement.
Expand Down Expand Up @@ -612,22 +660,23 @@ pub struct Captures<'t> {
}

impl<'t> Captures<'t> {
#[allow(experimental)]
fn new(re: &Regex, search: &'t str, locs: CaptureLocs)
-> Option<Captures<'t>> {
if !has_match(&locs) {
return None
}

let named =
if re.names.len() == 0 {
if re.names_len() == 0 {
None
} else {
let mut named = HashMap::new();
for (i, name) in re.names.iter().enumerate() {
for (i, name) in re.names_iter().enumerate() {
match name {
&None => {},
&Some(ref name) => {
named.insert(name.to_strbuf(), i);
None => {},
Some(name) => {
named.insert(name, i);
}
}
}
Expand Down Expand Up @@ -862,9 +911,9 @@ fn exec(re: &Regex, which: MatchKind, input: &str) -> CaptureLocs {

fn exec_slice(re: &Regex, which: MatchKind,
input: &str, s: uint, e: uint) -> CaptureLocs {
match re.p {
Dynamic(ref prog) => vm::run(which, prog, input, s, e),
Native(exec) => exec(which, input, s, e),
match *re {
Dynamic(Dynamic { ref prog, .. }) => vm::run(which, prog, input, s, e),
Native(Native { prog, .. }) => prog(which, input, s, e),
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/libregex/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ mod native_bench;
#[path = "tests.rs"]
mod native_tests;

#[cfg(not(stage1))]
mod native_static;

// Due to macro scoping rules, this definition only applies for the modules
// defined below. Effectively, it allows us to use the same tests for both
// native and dynamic regexes.
Expand Down
26 changes: 26 additions & 0 deletions src/libregex/test/native_static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use regex::Regex;
static RE: Regex = regex!(r"\d+");

#[test]
fn static_splitn() {
let text = "cauchy123plato456tyler789binx";
let subs: Vec<&str> = RE.splitn(text, 2).collect();
assert_eq!(subs, vec!("cauchy", "plato456tyler789binx"));
}

#[test]
fn static_split() {
let text = "cauchy123plato456tyler789binx";
let subs: Vec<&str> = RE.split(text).collect();
assert_eq!(subs, vec!("cauchy", "plato", "tyler", "binx"));
}
21 changes: 12 additions & 9 deletions src/libregex_macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub fn macro_registrar(register: |ast::Name, SyntaxExtension|) {
/// It is strongly recommended to read the dynamic implementation in vm.rs
/// first before trying to understand the code generator. The implementation
/// strategy is identical and vm.rs has comments and will be easier to follow.
#[allow(experimental)]
fn native(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree])
-> Box<MacResult> {
let regex = match parse(cx, tts) {
Expand All @@ -89,14 +90,14 @@ fn native(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree])
return DummyResult::any(sp)
}
};
let prog = match re.p {
Dynamic(ref prog) => prog.clone(),
let prog = match re {
Dynamic(Dynamic { ref prog, .. }) => prog.clone(),
Native(_) => unreachable!(),
};

let mut gen = NfaGen {
cx: &*cx, sp: sp, prog: prog,
names: re.names.clone(), original: re.original.clone(),
names: re.names_iter().collect(), original: re.as_str().to_strbuf(),
};
MacExpr::new(gen.code())
}
Expand All @@ -119,7 +120,7 @@ impl<'a> NfaGen<'a> {
|cx, name| match *name {
Some(ref name) => {
let name = name.as_slice();
quote_expr!(cx, Some($name.to_strbuf()))
quote_expr!(cx, Some($name))
}
None => cx.expr_none(self.sp),
}
Expand All @@ -141,9 +142,11 @@ impl<'a> NfaGen<'a> {
let regex = self.original.as_slice();

quote_expr!(self.cx, {
static CAP_NAMES: &'static [Option<&'static str>] = &$cap_names;
fn exec<'t>(which: ::regex::native::MatchKind, input: &'t str,
start: uint, end: uint) -> Vec<Option<uint>> {
#![allow(unused_imports)]
#![allow(unused_mut)]
use regex::native::{
MatchKind, Exists, Location, Submatches,
StepState, StepMatchEarlyReturn, StepMatch, StepContinue,
Expand Down Expand Up @@ -310,11 +313,11 @@ fn exec<'t>(which: ::regex::native::MatchKind, input: &'t str,
}
}

::regex::Regex {
original: $regex.to_strbuf(),
names: vec!$cap_names,
p: ::regex::native::Native(exec),
}
::regex::native::Native(::regex::native::Native {
original: $regex,
names: CAP_NAMES,
prog: exec,
})
})
}

Expand Down

5 comments on commit b997de5

@bors
Copy link
Contributor

@bors bors commented on b997de5 May 25, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from alexcrichton
at Kimundi@b997de5

@bors
Copy link
Contributor

@bors bors commented on b997de5 May 25, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging Kimundi/rust/static_regex = b997de5 into auto

@bors
Copy link
Contributor

@bors bors commented on b997de5 May 25, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kimundi/rust/static_regex = b997de5 merged ok, testing candidate = db2ddb1

@bors
Copy link
Contributor

@bors bors commented on b997de5 May 26, 2014

@bors
Copy link
Contributor

@bors bors commented on b997de5 May 26, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = db2ddb1

Please sign in to comment.