nom_trace/
lib.rs

1//! # nom-trace
2//!
3//! This crate provides a way to trace a parser execution,
4//! storing positions in the input data, positions in the parser
5//! tree and parser results.
6//!
7//! As an example, if you run the following code:
8//!
9//! ```rust
10//! #[macro_use] extern crate nom;
11//! #[macro_use] extern crate nom-trace;
12//!
13//! pub fn main() {
14//!   named!(parser<Vec<&[u8]>>,
15//!     //wrap a parser with tr!() to add a trace point
16//!     tr!(preceded!(
17//!       tr!(tag!("data: ")),
18//!       tr!(delimited!(
19//!         tag!("("),
20//!         separated_list!(
21//!           tr!(tag!(",")),
22//!           tr!(digit)
23//!         ),
24//!         tr!(tag!(")"))
25//!       ))
26//!     ))
27//!   );
28//!
29//!   println!("parsed: {:?}", parser(&b"data: (1,2,3)"[..]));
30//!
31//!   // prints the last parser trace
32//!   print_trace!();
33//!
34//!   // the list of trace events can be cleared
35//!   reset_trace!();
36//! }
37//! ```
38//!
39//! You would get the following result
40//! ```
41//! parsed: Ok(("", ["1", "2", "3"]))
42//! preceded        "data: (1,2,3)"
43//!
44//!         tag     "data: (1,2,3)"
45//!
46//!         -> Ok("data: ")
47//!         delimited       "(1,2,3)"
48//!
49//!                 digit   "1,2,3)"
50//!
51//!                 -> Ok("1")
52//!                 tag     ",2,3)"
53//!
54//!                 -> Ok(",")
55//!                 digit   "2,3)"
56//!
57//!                 -> Ok("2")
58//!                 tag     ",3)"
59//!
60//!                 -> Ok(",")
61//!                 digit   "3)"
62//!
63//!                 -> Ok("3")
64//!                 tag     ")"
65//!
66//!                 -> Error(Code(")", Tag))
67//!                 tag     ")"
68//!
69//!                 -> Ok(")")
70//!         -> Ok(["1", "2", "3"])
71//! -> Ok(["1", "2", "3"])
72//! ```
73//!
74//! Parser level is indicated through indentation. For each trace point, we have:
75//!
76//! - indent level, then parser or combinator name, then input position
77//! - traces for sub parsers
78//! - `->` followed by the parser's result
79//!
80//! You can add intermediate names instead of combinator names for the trace,
81//! like this: `tr!(PARENS, delimited!( ... ))`
82//! this would replace the name `delimited` in the trace print, with `PARENS`
83//!
84//! This tracer works with parsers based on `&[u8]` and `&str` input types.
85//! For `&[u8]`, input positions will be displayed as a hexdump.
86//!
87//! # Recording multiple traces
88//!
89//! Used directly, macros will record a trace under the "default" tag. But
90//! if you want to record multiple traces at the same time, add a static string
91//! as first argument.
92//!
93//! As an example, in the following code, the root trace will record in "default",
94//! while traces inside the `separated_list` will go in the "in list" trace.
95//!
96//! You can then print it by doing `print_trace!("in list")`.
97//!
98//! ```rust,ignore
99//!   named!(parser<Vec<&[u8]>>,
100//!     //wrap a parser with tr!() to add a trace point
101//!     tr!(preceded!(
102//!       tag!("data: "),
103//!       delimited!(
104//!         tag!("("),
105//!         separated_list!(
106//!           tr!("in list", tag!(",")),
107//!           tr!("in list", digit)
108//!         ),
109//!         tag!(")")
110//!       )
111//!     ))
112//!   );
113//! ```
114//!
115//! # nom 5 functions support
116//!
117//! The [tr] function supports the same combinator design as introduced in nom 5.
118//! Unfortunately, it cannot manipulate its arguments directly from inside the
119//! code like macros do, so it must receive explicitely the `tag` argument,
120//! and a `name` for this trace point (in macros, that name is generated
121//! from a `stringify` call of the argument of `tr!`).
122//!
123//! So using `tr` directly, you would need to to tr("default", "name", parser1)`.
124//! It is recommended to make you own trace parser, as follows:
125//!
126//! ```rust,ignore
127//! fn t<I,O,E,F>(name: &'static str, f: F) -> impl Fn(I) -> IResult<I,O,E>
128//!   where Input: From<I>,
129//!         F: Fn(I) -> IResult<I,O,E>,
130//!         I: Clone,
131//!         O: Debug,
132//!         E: Debug {
133//!   tr(name, f)
134//! }
135//! ```
136#[macro_use]
137extern crate nom;
138
139use std::fmt::{self,Debug};
140use std::collections::HashMap;
141use nom::IResult;
142
143pub struct TraceList {
144  pub traces: HashMap<&'static str, Trace>,
145}
146
147impl TraceList {
148  pub fn new() -> Self {
149    let mut traces = HashMap::new();
150    traces.insert("default", Trace::new());
151
152    TraceList { traces }
153  }
154
155  pub fn reset(&mut self, tag: &'static str) {
156    let t = self.traces.entry(tag).or_insert(Trace::new());
157    t.reset();
158  }
159
160  pub fn print(&self, tag: &'static str) {
161    self.traces.get(tag).map(|t| t.print());
162  }
163
164  pub fn activate(&mut self, tag: &'static str) {
165    let t = self.traces.entry(tag).or_insert(Trace::new());
166    t.active = true;
167  }
168
169  pub fn deactivate(&mut self, tag: &'static str) {
170    let t = self.traces.entry(tag).or_insert(Trace::new());
171    t.active = false;
172  }
173
174  pub fn open<T>(&mut self, tag: &'static str, input: T, location: &'static str)
175    where Input: From<T> {
176    let t = self.traces.entry(tag).or_insert(Trace::new());
177    t.open(input, location);
178  }
179
180  pub fn close<I,O:Debug,E:Debug>(&mut self, tag: &'static str, input: I, location: &'static str, result: &nom::IResult<I,O,E>)
181    where Input: From<I> {
182    let t = self.traces.entry(tag).or_insert(Trace::new());
183    t.close(input, location, result);
184  }
185}
186
187/// the main structure hoding trace events. It is stored in a thread level
188/// storage variable
189pub struct Trace {
190  pub events: Vec<TraceEvent>,
191  pub level: usize,
192  pub active: bool,
193}
194
195impl Trace {
196  pub fn new() -> Self {
197    Trace {
198      events: Vec::new(),
199      level: 0,
200      active: true,
201    }
202  }
203
204  pub fn reset(&mut self) {
205    self.events.clear();
206    self.level = 0;
207  }
208
209  pub fn print(&self) {
210    for event in self.events.iter() {
211      event.print();
212    }
213  }
214
215  pub fn open<T>(&mut self, input: T, location: &'static str)
216    where Input: From<T> {
217    if self.active {
218      self.events.push(TraceEvent::new(
219        self.level,
220        input,
221        location,
222        TraceEventType::Open,
223      ));
224
225      self.level += 1;
226    }
227  }
228
229  pub fn close<I,O:Debug,E:Debug>(&mut self, input: I, location: &'static str, result: &nom::IResult<I,O,E>)
230    where Input: From<I> {
231    if self.active {
232      self.level -= 1;
233      let event_type = match result {
234        Ok((_,o)) => TraceEventType::CloseOk(format!("{:?}", o)),
235        Err(nom::Err::Error(e)) => TraceEventType::CloseError(format!("{:?}", e)),
236        Err(nom::Err::Failure(e)) => TraceEventType::CloseFailure(format!("{:?}", e)),
237        Err(nom::Err::Incomplete(i)) => TraceEventType::CloseIncomplete(i.clone()),
238      };
239      self.events.push(TraceEvent::new(
240        self.level,
241        input,
242        location,
243        event_type
244      ));
245    }
246  }
247}
248
249#[derive(Clone,Debug)]
250pub struct TraceEvent {
251  pub level: usize,
252  pub input: Input,
253  pub location: &'static str,
254  pub event: TraceEventType,
255}
256
257#[derive(Clone,Debug)]
258pub enum TraceEventType {
259  Open,
260  CloseOk(String),
261  CloseError(String),
262  CloseFailure(String),
263  CloseIncomplete(nom::Needed),
264}
265
266impl TraceEvent {
267  pub fn new<T>(level: usize, input: T, location: &'static str, event: TraceEventType) -> Self
268    where Input: From<T> {
269    TraceEvent {
270      level,
271      input: Input::from(input),
272      location,
273      event,
274    }
275  }
276
277  pub fn print(&self) {
278    let indent = std::iter::repeat('\t').take(self.level).collect::<String>();
279    match &self.event {
280      TraceEventType::Open => {
281        println!("{}{}\t{:?}\n", indent, self.location, self.input);
282      },
283      TraceEventType::CloseOk(result) => {
284        println!("{}-> Ok({})", indent, result);
285      },
286      TraceEventType::CloseError(e) => {
287        println!("{}-> Error({})", indent, e);
288      },
289      TraceEventType::CloseFailure(e) => {
290        println!("{}-> Failure({})", indent, e);
291      },
292      TraceEventType::CloseIncomplete(i) => {
293        println!("{}-> Incomplete({:?})", indent, i);
294      },
295    }
296  }
297}
298
299#[derive(Clone)]
300pub enum Input {
301  Bytes(*const u8, usize),
302  String(*const u8, usize),
303}
304
305impl From<&[u8]> for Input {
306  fn from(input: &[u8]) -> Self {
307    Input::Bytes(input.as_ptr(), input.len())
308  }
309}
310
311impl From<&str> for Input {
312  fn from(input: &str) -> Self {
313    Input::String(input.as_ptr(), input.len())
314  }
315}
316
317impl Debug for Input {
318  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319    match self {
320      Input::String(ptr, len) => {
321        let s = unsafe {
322          std::str::from_utf8_unchecked(std::slice::from_raw_parts(*ptr, *len))
323        };
324        write!(f, "\"{}\"", s)
325      },
326      Input::Bytes(ptr, len) => {
327        let s: &[u8] = unsafe {
328          std::slice::from_raw_parts(*ptr, *len)
329        };
330        write!(f, "{}", to_hex(s, 16))
331      }
332    }
333  }
334}
335
336fn to_hex(input: &[u8], chunk_size: usize) -> String {
337  let mut v = Vec::with_capacity(input.len() * 3);
338  let mut i = 0;
339
340  if input.len() <= chunk_size {
341    to_hex_chunk(input, i, input.len(), &mut v);
342  } else {
343    for chunk in input.chunks(chunk_size) {
344      //to_hex_chunk(&input[i..std::cmp::min(i+chunk_size, input.len())],
345      to_hex_chunk(chunk,
346        i, chunk_size, &mut v);
347      i += chunk_size;
348      v.push(b'\n');
349    }
350  }
351
352  String::from_utf8_lossy(&v[..]).into_owned()
353}
354
355static CHARS: &'static [u8] = b"0123456789abcdef";
356
357fn to_hex_chunk(chunk: &[u8], i: usize, chunk_size: usize, v: &mut Vec<u8>) {
358  let s = format!("{:08x}", i);
359  for &ch in s.as_bytes().iter() {
360    v.push(ch);
361  }
362  v.push(b'\t');
363
364  for &byte in chunk {
365    v.push(CHARS[(byte >> 4) as usize]);
366    v.push(CHARS[(byte & 0xf) as usize]);
367    v.push(b' ');
368  }
369  if chunk_size > chunk.len() {
370    for _ in 0..(chunk_size - chunk.len()) {
371      v.push(b' ');
372      v.push(b' ');
373      v.push(b' ');
374    }
375  }
376  v.push(b'\t');
377
378  for &byte in chunk {
379    if (byte >= 32 && byte <= 126) || byte >= 128 {
380      v.push(byte);
381    } else {
382      v.push(b'.');
383    }
384  }
385}
386
387thread_local! {
388  pub static NOM_TRACE: ::std::cell::RefCell<TraceList> = ::std::cell::RefCell::new(TraceList::new());
389}
390
391/// print the trace events to stdout
392#[macro_export]
393macro_rules! print_trace (
394 () => {
395  $crate::NOM_TRACE.with(|trace| {
396    trace.borrow().print("default");
397  });
398 };
399 ($tag:expr) => {
400  $crate::NOM_TRACE.with(|trace| {
401    trace.borrow().print($tag);
402  });
403 };
404);
405
406/// clears the list of events
407#[macro_export]
408macro_rules! reset_trace (
409 () => {
410  $crate::NOM_TRACE.with(|trace| {
411    trace.borrow_mut().reset("default");
412  });
413 };
414 ($tag:expr) => {
415  $crate::NOM_TRACE.with(|trace| {
416    trace.borrow_mut().reset($tag);
417  });
418 };
419);
420
421/// activates tracing (it is activated by default)
422#[macro_export]
423macro_rules! activate_trace (
424 () => {
425  $crate::NOM_TRACE.with(|trace| {
426    trace.borrow_mut().activate("default");
427  });
428 };
429 ($tag:expr) => {
430  $crate::NOM_TRACE.with(|trace| {
431    trace.borrow_mut().activate($tag);
432  });
433 };
434);
435
436/// deactivates tracing (it is activated by default)
437#[macro_export]
438macro_rules! deactivate_trace (
439 () => {
440  $crate::NOM_TRACE.with(|trace| {
441    trace.borrow_mut().deactivate("default");
442  });
443 };
444 ($tag:expr) => {
445  $crate::NOM_TRACE.with(|trace| {
446    trace.borrow_mut().deactivate($tag);
447  });
448 };
449);
450
451/// function tracer
452pub fn tr<I,O,E,F>(tag: &'static str, name: &'static str, f: F) -> impl Fn(I) -> IResult<I,O,E>
453  where Input: From<I>,
454        F: Fn(I) -> IResult<I,O,E>,
455        I: Clone,
456        O: Debug,
457        E: Debug {
458  move |i: I| {
459    let input1 = i.clone();
460    let input2 = i.clone();
461    NOM_TRACE.with(|trace| {
462      (*trace.borrow_mut()).open(tag, input1, name);
463    });
464
465    let res = f(i);
466
467    NOM_TRACE.with(|trace| {
468      (*trace.borrow_mut()).close(tag, input2, name, &res);
469    });
470
471    res
472  }
473}
474
475/// wrap a nom parser or combinator with this macro to add a trace point
476#[macro_export]
477macro_rules! tr (
478  ($i:expr, $name:ident, $submac:ident!( $($args:tt)* )) => (
479    tr!(__impl $i, "default", stringify!($name), $submac!($($args)*))
480  );
481  ($i:expr, $name:ident, $f:expr) => (
482    tr!(__impl $i, "default", stringify!($name), call!($f))
483  );
484  ($i:expr, $submac:ident!( $($args:tt)* )) => (
485    tr!(__impl $i, "default", stringify!($submac), $submac!($($args)*))
486  );
487  ($i:expr, $f:expr) => (
488    tr!(__impl $i, "default", stringify!($f), call!($f))
489  );
490  (__impl $i:expr, $name:expr, $submac:ident!( $($args:tt)* )) => (
491    tr!(__impl $i, "default", $name, $submac!($($args)*))
492  );
493  (__impl $i:expr, $submac:ident!( $($args:tt)* )) => (
494    tr!(__impl $i, "default", $submac!($($args)*))
495  );
496  (__impl $i:expr, $f:expr) => (
497    tr!(__impl $i, "default", $f)
498  );
499
500  ($i:expr, $tag:expr, $name:ident, $submac:ident!( $($args:tt)* )) => (
501    tr!(__impl $i, $tag, stringify!($name), $submac!($($args)*))
502  );
503  ($i:expr, $tag:expr, $name:ident, $f:expr) => (
504    tr!(__impl $i, $tag, stringify!($name), call!($f))
505  );
506  ($i:expr, $tag:expr, $submac:ident!( $($args:tt)* )) => (
507    tr!(__impl $i, $tag, stringify!($submac), $submac!($($args)*))
508  );
509  ($i:expr, $tag:expr, $f:expr) => (
510    tr!(__impl $i, $tag, stringify!($f), call!($f))
511  );
512  (__impl $i:expr, $tag:expr, $name:expr, $submac:ident!( $($args:tt)* )) => (
513    {
514      let input = $i;
515      $crate::NOM_TRACE.with(|trace| {
516        (*trace.borrow_mut()).open($tag, input, $name);
517      });
518
519      let res = $submac!(input, $($args)*);
520      $crate::NOM_TRACE.with(|trace| {
521        (*trace.borrow_mut()).close($tag, input, $name, &res);
522      });
523
524      res
525    }
526  );
527  (__impl $i:expr, $tag:expr, $submac:ident!( $($args:tt)* )) => (
528    {
529      let input = $i;
530      $crate::NOM_TRACE.with(|trace| {
531        (*trace.borrow_mut()).open($tag, input, stringify!($submac));
532      });
533
534      let res = $submac!(input, $($args)*);
535      $crate::NOM_TRACE.with(|trace| {
536        (*trace.borrow_mut()).close($tag, input, $name, &res);
537      });
538
539      res
540    }
541  );
542  (__impl $i:expr, $tag:expr, $f:expr) => (
543    {
544      let input = $i;
545      $crate::NOM_TRACE.with(|trace| {
546        (*trace.borrow_mut()).open($tag, input, stringify!($f));
547      });
548
549      let res = $f(input);
550      $crate::NOM_TRACE.with(|trace| {
551        (*trace.borrow_mut()).close($tag, input, $name, &res);
552      });
553
554      res
555    }
556  );
557);
558
559
560#[cfg(test)]
561mod tests {
562  use super::*;
563  use nom::character::complete::digit1 as digit;
564
565  #[test]
566  pub fn trace_bytes_parser() {
567    named!(parser<Vec<&[u8]>>,
568      tr!(preceded!(
569        tr!(tag!("data: ")),
570        tr!(delimited!(
571          tag!("("),
572          separated_list!(
573            tr!(tag!(",")),
574            tr!(digit)
575          ),
576          tr!(tag!(")"))
577        ))
578      ))
579    );
580
581    println!("parsed: {:?}", parser(&b"data: (1,2,3)"[..]));
582
583    print_trace!();
584    reset_trace!();
585    panic!();
586  }
587
588  #[test]
589  pub fn trace_str_parser() {
590    named!(parser<&str, Vec<&str>>,
591      tr!(ROOT, preceded!(
592        tr!(tag!("data: ")),
593        tr!(PARENS, delimited!(
594          tag!("("),
595          separated_list!(
596            tr!(tag!(",")),
597            tr!(digit)
598          ),
599          tr!(tag!(")"))
600        ))
601      ))
602    );
603
604    deactivate_trace!();
605    activate_trace!();
606    println!("parsed: {:?}", parser("data: (1,2,3)"));
607
608    print_trace!();
609    reset_trace!();
610    panic!();
611  }
612}