]> git.lizzy.rs Git - rust.git/blob - src/libregex_macros/lib.rs
syntax: Make quasiquoter use absolute paths
[rust.git] / src / libregex_macros / lib.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This crate provides the `regex!` macro. Its use is documented in the
12 //! `regex` crate.
13
14 #![crate_id = "regex_macros#0.11.0-pre"]
15 #![crate_type = "dylib"]
16 #![experimental]
17 #![license = "MIT/ASL2"]
18 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
19        html_favicon_url = "http://www.rust-lang.org/favicon.ico",
20        html_root_url = "http://doc.rust-lang.org/")]
21
22 #![feature(macro_registrar, managed_boxes, quote)]
23
24 extern crate regex;
25 extern crate syntax;
26
27 use std::rc::Rc;
28
29 use syntax::ast;
30 use syntax::codemap;
31 use syntax::ext::build::AstBuilder;
32 use syntax::ext::base::{
33     SyntaxExtension, ExtCtxt, MacResult, MacExpr, DummyResult,
34     NormalTT, BasicMacroExpander,
35 };
36 use syntax::parse;
37 use syntax::parse::token;
38 use syntax::print::pprust;
39
40 use regex::Regex;
41 use regex::native::{
42     OneChar, CharClass, Any, Save, Jump, Split,
43     Match, EmptyBegin, EmptyEnd, EmptyWordBoundary,
44     Program, Dynamic, Native,
45     FLAG_NOCASE, FLAG_MULTI, FLAG_DOTNL, FLAG_NEGATED,
46 };
47
48 /// For the `regex!` syntax extension. Do not use.
49 #[macro_registrar]
50 #[doc(hidden)]
51 pub fn macro_registrar(register: |ast::Name, SyntaxExtension|) {
52     let expander = box BasicMacroExpander { expander: native, span: None };
53     register(token::intern("regex"), NormalTT(expander, None))
54 }
55
56 /// Generates specialized code for the Pike VM for a particular regular
57 /// expression.
58 ///
59 /// There are two primary differences between the code generated here and the
60 /// general code in vm.rs.
61 ///
62 /// 1. All heap allocation is removed. Sized vector types are used instead.
63 ///    Care must be taken to make sure that these vectors are not copied
64 ///    gratuitously. (If you're not sure, run the benchmarks. They will yell
65 ///    at you if you do.)
66 /// 2. The main `match instruction { ... }` expressions are replaced with more
67 ///    direct `match pc { ... }`. The generators can be found in
68 ///    `step_insts` and `add_insts`.
69 ///
70 /// Other more minor changes include eliding code when possible (although this
71 /// isn't completely thorough at the moment), and translating character class
72 /// matching from using a binary search to a simple `match` expression (see
73 /// `match_class`).
74 ///
75 /// It is strongly recommended to read the dynamic implementation in vm.rs
76 /// first before trying to understand the code generator. The implementation
77 /// strategy is identical and vm.rs has comments and will be easier to follow.
78 #[allow(experimental)]
79 fn native(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree])
80           -> Box<MacResult> {
81     let regex = match parse(cx, tts) {
82         Some(r) => r,
83         // error is logged in 'parse' with cx.span_err
84         None => return DummyResult::any(sp),
85     };
86     let re = match Regex::new(regex.as_slice()) {
87         Ok(re) => re,
88         Err(err) => {
89             cx.span_err(sp, err.to_str().as_slice());
90             return DummyResult::any(sp)
91         }
92     };
93     let prog = match re {
94         Dynamic(Dynamic { ref prog, .. }) => prog.clone(),
95         Native(_) => unreachable!(),
96     };
97
98     let mut gen = NfaGen {
99         cx: &*cx, sp: sp, prog: prog,
100         names: re.names_iter().collect(), original: re.as_str().to_string(),
101     };
102     MacExpr::new(gen.code())
103 }
104
105 struct NfaGen<'a> {
106     cx: &'a ExtCtxt<'a>,
107     sp: codemap::Span,
108     prog: Program,
109     names: Vec<Option<String>>,
110     original: String,
111 }
112
113 impl<'a> NfaGen<'a> {
114     fn code(&mut self) -> @ast::Expr {
115         // Most or all of the following things are used in the quasiquoted
116         // expression returned.
117         let num_cap_locs = 2 * self.prog.num_captures();
118         let num_insts = self.prog.insts.len();
119         let cap_names = self.vec_expr(self.names.as_slice().iter(),
120             |cx, name| match *name {
121                 Some(ref name) => {
122                     let name = name.as_slice();
123                     quote_expr!(cx, Some($name))
124                 }
125                 None => cx.expr_none(self.sp),
126             }
127         );
128         let prefix_anchor =
129             match self.prog.insts.as_slice()[1] {
130                 EmptyBegin(flags) if flags & FLAG_MULTI == 0 => true,
131                 _ => false,
132             };
133         let init_groups = self.vec_expr(range(0, num_cap_locs),
134                                         |cx, _| cx.expr_none(self.sp));
135
136         let prefix_lit = Rc::new(Vec::from_slice(self.prog.prefix.as_slice().as_bytes()));
137         let prefix_bytes = self.cx.expr_lit(self.sp, ast::LitBinary(prefix_lit));
138
139         let check_prefix = self.check_prefix();
140         let step_insts = self.step_insts();
141         let add_insts = self.add_insts();
142         let regex = self.original.as_slice();
143
144         quote_expr!(self.cx, {
145 static CAP_NAMES: &'static [Option<&'static str>] = &$cap_names;
146 fn exec<'t>(which: ::regex::native::MatchKind, input: &'t str,
147             start: uint, end: uint) -> Vec<Option<uint>> {
148     #![allow(unused_imports)]
149     #![allow(unused_mut)]
150     use regex::native::{
151         MatchKind, Exists, Location, Submatches,
152         StepState, StepMatchEarlyReturn, StepMatch, StepContinue,
153         CharReader, find_prefix,
154     };
155
156     return Nfa {
157         which: which,
158         input: input,
159         ic: 0,
160         chars: CharReader::new(input),
161     }.run(start, end);
162
163     type Captures = [Option<uint>, ..$num_cap_locs];
164
165     struct Nfa<'t> {
166         which: MatchKind,
167         input: &'t str,
168         ic: uint,
169         chars: CharReader<'t>,
170     }
171
172     impl<'t> Nfa<'t> {
173         #[allow(unused_variable)]
174         fn run(&mut self, start: uint, end: uint) -> Vec<Option<uint>> {
175             let mut matched = false;
176             let prefix_bytes: &[u8] = &$prefix_bytes;
177             let mut clist = &mut Threads::new(self.which);
178             let mut nlist = &mut Threads::new(self.which);
179
180             let mut groups = $init_groups;
181
182             self.ic = start;
183             let mut next_ic = self.chars.set(start);
184             while self.ic <= end {
185                 if clist.size == 0 {
186                     if matched {
187                         break
188                     }
189                     $check_prefix
190                 }
191                 if clist.size == 0 || (!$prefix_anchor && !matched) {
192                     self.add(clist, 0, &mut groups)
193                 }
194
195                 self.ic = next_ic;
196                 next_ic = self.chars.advance();
197
198                 for i in range(0, clist.size) {
199                     let pc = clist.pc(i);
200                     let step_state = self.step(&mut groups, nlist,
201                                                clist.groups(i), pc);
202                     match step_state {
203                         StepMatchEarlyReturn =>
204                             return vec![Some(0u), Some(0u)],
205                         StepMatch => { matched = true; break },
206                         StepContinue => {},
207                     }
208                 }
209                 ::std::mem::swap(&mut clist, &mut nlist);
210                 nlist.empty();
211             }
212             match self.which {
213                 Exists if matched     => vec![Some(0u), Some(0u)],
214                 Exists                => vec![None, None],
215                 Location | Submatches => groups.iter().map(|x| *x).collect(),
216             }
217         }
218
219         // Sometimes `nlist` is never used (for empty regexes).
220         #[allow(unused_variable)]
221         #[inline]
222         fn step(&self, groups: &mut Captures, nlist: &mut Threads,
223                 caps: &mut Captures, pc: uint) -> StepState {
224             $step_insts
225             StepContinue
226         }
227
228         fn add(&self, nlist: &mut Threads, pc: uint,
229                groups: &mut Captures) {
230             if nlist.contains(pc) {
231                 return
232             }
233             $add_insts
234         }
235     }
236
237     struct Thread {
238         pc: uint,
239         groups: Captures,
240     }
241
242     struct Threads {
243         which: MatchKind,
244         queue: [Thread, ..$num_insts],
245         sparse: [uint, ..$num_insts],
246         size: uint,
247     }
248
249     impl Threads {
250         fn new(which: MatchKind) -> Threads {
251             Threads {
252                 which: which,
253                 // These unsafe blocks are used for performance reasons, as it
254                 // gives us a zero-cost initialization of a sparse set. The
255                 // trick is described in more detail here:
256                 // http://research.swtch.com/sparse
257                 // The idea here is to avoid initializing threads that never
258                 // need to be initialized, particularly for larger regexs with
259                 // a lot of instructions.
260                 queue: unsafe { ::std::mem::uninitialized() },
261                 sparse: unsafe { ::std::mem::uninitialized() },
262                 size: 0,
263             }
264         }
265
266         #[inline]
267         fn add(&mut self, pc: uint, groups: &Captures) {
268             let t = &mut self.queue[self.size];
269             t.pc = pc;
270             match self.which {
271                 Exists => {},
272                 Location => {
273                     t.groups[0] = groups[0];
274                     t.groups[1] = groups[1];
275                 }
276                 Submatches => {
277                     for (slot, val) in t.groups.mut_iter().zip(groups.iter()) {
278                         *slot = *val;
279                     }
280                 }
281             }
282             self.sparse[pc] = self.size;
283             self.size += 1;
284         }
285
286         #[inline]
287         fn add_empty(&mut self, pc: uint) {
288             self.queue[self.size].pc = pc;
289             self.sparse[pc] = self.size;
290             self.size += 1;
291         }
292
293         #[inline]
294         fn contains(&self, pc: uint) -> bool {
295             let s = self.sparse[pc];
296             s < self.size && self.queue[s].pc == pc
297         }
298
299         #[inline]
300         fn empty(&mut self) {
301             self.size = 0;
302         }
303
304         #[inline]
305         fn pc(&self, i: uint) -> uint {
306             self.queue[i].pc
307         }
308
309         #[inline]
310         fn groups<'r>(&'r mut self, i: uint) -> &'r mut Captures {
311             &'r mut self.queue[i].groups
312         }
313     }
314 }
315
316 ::regex::native::Native(::regex::native::Native {
317     original: $regex,
318     names: CAP_NAMES,
319     prog: exec,
320 })
321         })
322     }
323
324     // Generates code for the `add` method, which is responsible for adding
325     // zero-width states to the next queue of states to visit.
326     fn add_insts(&self) -> @ast::Expr {
327         let arms = self.prog.insts.iter().enumerate().map(|(pc, inst)| {
328             let nextpc = pc + 1;
329             let body = match *inst {
330                 EmptyBegin(flags) => {
331                     let cond =
332                         if flags & FLAG_MULTI > 0 {
333                             quote_expr!(self.cx,
334                                 self.chars.is_begin()
335                                 || self.chars.prev == Some('\n')
336                             )
337                         } else {
338                             quote_expr!(self.cx, self.chars.is_begin())
339                         };
340                     quote_expr!(self.cx, {
341                         nlist.add_empty($pc);
342                         if $cond { self.add(nlist, $nextpc, &mut *groups) }
343                     })
344                 }
345                 EmptyEnd(flags) => {
346                     let cond =
347                         if flags & FLAG_MULTI > 0 {
348                             quote_expr!(self.cx,
349                                 self.chars.is_end()
350                                 || self.chars.cur == Some('\n')
351                             )
352                         } else {
353                             quote_expr!(self.cx, self.chars.is_end())
354                         };
355                     quote_expr!(self.cx, {
356                         nlist.add_empty($pc);
357                         if $cond { self.add(nlist, $nextpc, &mut *groups) }
358                     })
359                 }
360                 EmptyWordBoundary(flags) => {
361                     let cond =
362                         if flags & FLAG_NEGATED > 0 {
363                             quote_expr!(self.cx, !self.chars.is_word_boundary())
364                         } else {
365                             quote_expr!(self.cx, self.chars.is_word_boundary())
366                         };
367                     quote_expr!(self.cx, {
368                         nlist.add_empty($pc);
369                         if $cond { self.add(nlist, $nextpc, &mut *groups) }
370                     })
371                 }
372                 Save(slot) => {
373                     let save = quote_expr!(self.cx, {
374                         let old = groups[$slot];
375                         groups[$slot] = Some(self.ic);
376                         self.add(nlist, $nextpc, &mut *groups);
377                         groups[$slot] = old;
378                     });
379                     let add = quote_expr!(self.cx, {
380                         self.add(nlist, $nextpc, &mut *groups);
381                     });
382                     // If this is saving a submatch location but we request
383                     // existence or only full match location, then we can skip
384                     // right over it every time.
385                     if slot > 1 {
386                         quote_expr!(self.cx, {
387                             nlist.add_empty($pc);
388                             match self.which {
389                                 Submatches => $save,
390                                 Exists | Location => $add,
391                             }
392                         })
393                     } else {
394                         quote_expr!(self.cx, {
395                             nlist.add_empty($pc);
396                             match self.which {
397                                 Submatches | Location => $save,
398                                 Exists => $add,
399                             }
400                         })
401                     }
402                 }
403                 Jump(to) => {
404                     quote_expr!(self.cx, {
405                         nlist.add_empty($pc);
406                         self.add(nlist, $to, &mut *groups);
407                     })
408                 }
409                 Split(x, y) => {
410                     quote_expr!(self.cx, {
411                         nlist.add_empty($pc);
412                         self.add(nlist, $x, &mut *groups);
413                         self.add(nlist, $y, &mut *groups);
414                     })
415                 }
416                 // For Match, OneChar, CharClass, Any
417                 _ => quote_expr!(self.cx, nlist.add($pc, &*groups)),
418             };
419             self.arm_inst(pc, body)
420         }).collect::<Vec<ast::Arm>>();
421
422         self.match_insts(arms)
423     }
424
425     // Generates the code for the `step` method, which processes all states
426     // in the current queue that consume a single character.
427     fn step_insts(&self) -> @ast::Expr {
428         let arms = self.prog.insts.iter().enumerate().map(|(pc, inst)| {
429             let nextpc = pc + 1;
430             let body = match *inst {
431                 Match => {
432                     quote_expr!(self.cx, {
433                         match self.which {
434                             Exists => {
435                                 return StepMatchEarlyReturn
436                             }
437                             Location => {
438                                 groups[0] = caps[0];
439                                 groups[1] = caps[1];
440                                 return StepMatch
441                             }
442                             Submatches => {
443                                 for (slot, val) in groups.mut_iter().zip(caps.iter()) {
444                                     *slot = *val;
445                                 }
446                                 return StepMatch
447                             }
448                         }
449                     })
450                 }
451                 OneChar(c, flags) => {
452                     if flags & FLAG_NOCASE > 0 {
453                         let upc = c.to_uppercase();
454                         quote_expr!(self.cx, {
455                             let upc = self.chars.prev.map(|c| c.to_uppercase());
456                             if upc == Some($upc) {
457                                 self.add(nlist, $nextpc, caps);
458                             }
459                         })
460                     } else {
461                         quote_expr!(self.cx, {
462                             if self.chars.prev == Some($c) {
463                                 self.add(nlist, $nextpc, caps);
464                             }
465                         })
466                     }
467                 }
468                 CharClass(ref ranges, flags) => {
469                     let negate = flags & FLAG_NEGATED > 0;
470                     let casei = flags & FLAG_NOCASE > 0;
471                     let get_char =
472                         if casei {
473                             quote_expr!(self.cx, self.chars.prev.unwrap().to_uppercase())
474                         } else {
475                             quote_expr!(self.cx, self.chars.prev.unwrap())
476                         };
477                     let negcond =
478                         if negate {
479                             quote_expr!(self.cx, !found)
480                         } else {
481                             quote_expr!(self.cx, found)
482                         };
483                     let mranges = self.match_class(casei, ranges.as_slice());
484                     quote_expr!(self.cx, {
485                         if self.chars.prev.is_some() {
486                             let c = $get_char;
487                             let found = $mranges;
488                             if $negcond {
489                                 self.add(nlist, $nextpc, caps);
490                             }
491                         }
492                     })
493                 }
494                 Any(flags) => {
495                     if flags & FLAG_DOTNL > 0 {
496                         quote_expr!(self.cx, self.add(nlist, $nextpc, caps))
497                     } else {
498                         quote_expr!(self.cx, {
499                             if self.chars.prev != Some('\n') {
500                                 self.add(nlist, $nextpc, caps)
501                             }
502                             ()
503                         })
504                     }
505                 }
506                 // EmptyBegin, EmptyEnd, EmptyWordBoundary, Save, Jump, Split
507                 _ => self.empty_block(),
508             };
509             self.arm_inst(pc, body)
510         }).collect::<Vec<ast::Arm>>();
511
512         self.match_insts(arms)
513     }
514
515     // Translates a character class into a match expression.
516     // This avoids a binary search (and is hopefully replaced by a jump
517     // table).
518     fn match_class(&self, casei: bool, ranges: &[(char, char)]) -> @ast::Expr {
519         let expr_true = quote_expr!(self.cx, true);
520
521         let mut arms = ranges.iter().map(|&(mut start, mut end)| {
522             if casei {
523                 start = start.to_uppercase();
524                 end = end.to_uppercase();
525             }
526             let pat = self.cx.pat(self.sp, ast::PatRange(quote_expr!(self.cx, $start),
527                                                          quote_expr!(self.cx, $end)));
528             self.cx.arm(self.sp, vec!(pat), expr_true)
529         }).collect::<Vec<ast::Arm>>();
530
531         arms.push(self.wild_arm_expr(quote_expr!(self.cx, false)));
532
533         let match_on = quote_expr!(self.cx, c);
534         self.cx.expr_match(self.sp, match_on, arms)
535     }
536
537     // Generates code for checking a literal prefix of the search string.
538     // The code is only generated if the regex *has* a literal prefix.
539     // Otherwise, a no-op is returned.
540     fn check_prefix(&self) -> @ast::Expr {
541         if self.prog.prefix.len() == 0 {
542             self.empty_block()
543         } else {
544             quote_expr!(self.cx,
545                 if clist.size == 0 {
546                     let haystack = self.input.as_bytes().slice_from(self.ic);
547                     match find_prefix(prefix_bytes, haystack) {
548                         None => break,
549                         Some(i) => {
550                             self.ic += i;
551                             next_ic = self.chars.set(self.ic);
552                         }
553                     }
554                 }
555             )
556         }
557     }
558
559     // Builds a `match pc { ... }` expression from a list of arms, specifically
560     // for matching the current program counter with an instruction.
561     // A wild-card arm is automatically added that executes a no-op. It will
562     // never be used, but is added to satisfy the compiler complaining about
563     // non-exhaustive patterns.
564     fn match_insts(&self, mut arms: Vec<ast::Arm>) -> @ast::Expr {
565         arms.push(self.wild_arm_expr(self.empty_block()));
566         self.cx.expr_match(self.sp, quote_expr!(self.cx, pc), arms)
567     }
568
569     fn empty_block(&self) -> @ast::Expr {
570         quote_expr!(self.cx, {})
571     }
572
573     // Creates a match arm for the instruction at `pc` with the expression
574     // `body`.
575     fn arm_inst(&self, pc: uint, body: @ast::Expr) -> ast::Arm {
576         let pc_pat = self.cx.pat_lit(self.sp, quote_expr!(self.cx, $pc));
577
578         self.cx.arm(self.sp, vec!(pc_pat), body)
579     }
580
581     // Creates a wild-card match arm with the expression `body`.
582     fn wild_arm_expr(&self, body: @ast::Expr) -> ast::Arm {
583         ast::Arm {
584             attrs: vec!(),
585             pats: vec!(@ast::Pat{
586                 id: ast::DUMMY_NODE_ID,
587                 span: self.sp,
588                 node: ast::PatWild,
589             }),
590             guard: None,
591             body: body,
592         }
593     }
594
595
596     // Converts `xs` to a `[x1, x2, .., xN]` expression by calling `to_expr`
597     // on each element in `xs`.
598     fn vec_expr<T, It: Iterator<T>>(&self, xs: It, to_expr: |&ExtCtxt, T| -> @ast::Expr)
599                   -> @ast::Expr {
600         let exprs = xs.map(|x| to_expr(self.cx, x)).collect();
601         self.cx.expr_vec(self.sp, exprs)
602     }
603 }
604
605 /// Looks for a single string literal and returns it.
606 /// Otherwise, logs an error with cx.span_err and returns None.
607 fn parse(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
608     let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
609                                                 Vec::from_slice(tts));
610     let entry = cx.expand_expr(parser.parse_expr());
611     let regex = match entry.node {
612         ast::ExprLit(lit) => {
613             match lit.node {
614                 ast::LitStr(ref s, _) => s.to_str().to_string(),
615                 _ => {
616                     cx.span_err(entry.span, format!(
617                         "expected string literal but got `{}`",
618                         pprust::lit_to_str(lit)).as_slice());
619                     return None
620                 }
621             }
622         }
623         _ => {
624             cx.span_err(entry.span, format!(
625                 "expected string literal but got `{}`",
626                 pprust::expr_to_str(entry)).as_slice());
627             return None
628         }
629     };
630     if !parser.eat(&token::EOF) {
631         cx.span_err(parser.span, "only one string literal allowed");
632         return None;
633     }
634     Some(regex)
635 }