├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── json.rs └── json_str.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nom-trace" 3 | version = "0.2.1" 4 | authors = ["Geoffroy Couprie "] 5 | edition = "2018" 6 | description = "A tracer for nom parsers" 7 | license = "MIT" 8 | repository = "https://github.com/Geal/nom-trace" 9 | readme = "README.md" 10 | documentation = "https://docs.rs/nom-trace" 11 | keywords = ["parser", "parser-combinators", "parsing", "streaming", "bit"] 12 | categories = ["parsing"] 13 | 14 | [dependencies] 15 | nom = "^5" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016 Geoffroy Couprie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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<&str, Vec<&str>>, 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!(nom::digit) 23 | ), 24 | tr!(tag!(")")) 25 | )) 26 | )) 27 | ); 28 | 29 | println!("parsed: {:?}", parser("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 | 88 | # Recording multiple traces 89 | 90 | Used directly, macros will record a trace under the "default" tag. But 91 | if you want to record multiple traces at the same time, add a static string 92 | as first argument. 93 | 94 | As an example, in the following code, the root trace will record in "default", 95 | while traces inside the `separated_list` will go in the "in list" trace. 96 | 97 | You can then print it by doing `print_trace!("in list")`. 98 | 99 | ```rust,ignore 100 | named!(parser>, 101 | //wrap a parser with tr!() to add a trace point 102 | tr!(preceded!( 103 | tag!("data: "), 104 | delimited!( 105 | tag!("("), 106 | separated_list!( 107 | tr!("in list", tag!(",")), 108 | tr!("in list", digit) 109 | ), 110 | tag!(")") 111 | ) 112 | )) 113 | ); 114 | ``` 115 | 116 | # nom 5 functions support 117 | 118 | The `tr` function supports the same combinator design as introduced in nom 5. 119 | Unfortunately, it cannot manipulate its arguments directly from inside the 120 | code like macros do, so it must receive explicitely the `tag` argument, 121 | and a `name` for this trace point (in macros, that name is generated 122 | from a `stringify` call of the argument of `tr!`). 123 | 124 | So using `tr` directly, you would need to to tr("default", "name", parser1)`. 125 | It is recommended to make you own trace parser, as follows: 126 | 127 | ```rust,ignore 128 | fn t(name: &'static str, f: F) -> impl Fn(I) -> IResult 129 | where Input: From, 130 | F: Fn(I) -> IResult, 131 | I: Clone, 132 | O: Debug, 133 | E: Debug { 134 | tr(name, f) 135 | } 136 | ``` 137 | -------------------------------------------------------------------------------- /examples/json.rs: -------------------------------------------------------------------------------- 1 | //#![feature(trace_macros)] 2 | #[macro_use] 3 | extern crate nom; 4 | #[macro_use] 5 | extern crate nom_trace; 6 | 7 | use nom::{number::complete::recognize_float, character::complete::space0 as sp}; 8 | 9 | use std::str; 10 | use std::collections::HashMap; 11 | 12 | pub fn is_string_character(c: u8) -> bool { 13 | //FIXME: should validate unicode character 14 | c != b'"' && c != b'\\' 15 | } 16 | 17 | #[derive(Debug, PartialEq)] 18 | pub enum JsonValue { 19 | Str(String), 20 | Boolean(bool), 21 | Num(f64), 22 | Array(Vec), 23 | Object(HashMap), 24 | } 25 | 26 | named!(float, flat_map!(recognize_float, parse_to!(f64))); 27 | 28 | //FIXME: handle the cases like \u1234 29 | named!( 30 | string<&str>, 31 | delimited!( 32 | char!('\"'), 33 | map_res!( 34 | escaped!(take_while1!(is_string_character), '\\', one_of!("\"bfnrt\\")), 35 | str::from_utf8 36 | ), 37 | char!('\"') 38 | ) 39 | ); 40 | 41 | named!( 42 | boolean, 43 | alt!(value!(false, tag!("false")) | value!(true, tag!("true"))) 44 | ); 45 | 46 | named!( 47 | array>, 48 | preceded!(sp, delimited!( 49 | char!('['), 50 | separated_list!(char!(','), value), 51 | preceded!(sp, char!(']')) 52 | )) 53 | ); 54 | 55 | named!( 56 | key_value<(&str, JsonValue)>, 57 | preceded!(sp, separated_pair!(string, char!(':'), value)) 58 | ); 59 | 60 | named!( 61 | hash>, 62 | preceded!(sp, map!( 63 | delimited!( 64 | char!('{'), 65 | separated_list!(char!(','), key_value), 66 | preceded!(sp, char!('}')) 67 | ), 68 | |tuple_vec| tuple_vec 69 | .into_iter() 70 | .map(|(k, v)| (String::from(k), v)) 71 | .collect() 72 | )) 73 | ); 74 | 75 | named!( 76 | value, 77 | preceded!(sp, tr!(alt!( 78 | tr!(hash) => { |h| JsonValue::Object(h) } | 79 | tr!(array) => { |v| JsonValue::Array(v) } | 80 | tr!(string) => { |s| JsonValue::Str(String::from(s)) } | 81 | tr!(float) => { |f| JsonValue::Num(f) } | 82 | tr!(boolean) => { |b| JsonValue::Boolean(b) } 83 | ))) 84 | ); 85 | 86 | named!( 87 | root, 88 | tr!(delimited!( 89 | call!(sp), 90 | tr!(alt!( 91 | map!(tr!(hash), JsonValue::Object) | 92 | map!(tr!(array), JsonValue::Array) 93 | )), 94 | not!(complete!(sp)) 95 | )) 96 | ); 97 | 98 | fn main() { 99 | let data = b" { \"a\"\t: 42, 100 | \"b\": [ \"x\", \"y\", 12 ] , 101 | \"c\": { \"hello\" : \"world\" 102 | } 103 | } "; 104 | 105 | match root(data) { 106 | Ok(val) => println!("parsed value: {:#?}", val), 107 | Err(e) => { 108 | println!("parse error: {:?}", e); 109 | print_trace!(); 110 | } 111 | } 112 | reset_trace!(); 113 | } 114 | 115 | -------------------------------------------------------------------------------- /examples/json_str.rs: -------------------------------------------------------------------------------- 1 | //#![feature(trace_macros)] 2 | #[macro_use] 3 | extern crate nom; 4 | #[macro_use] 5 | extern crate nom_trace; 6 | 7 | use nom::{number::complete::recognize_float, character::complete::space0 as sp}; 8 | 9 | use std::str; 10 | use std::collections::HashMap; 11 | 12 | pub fn is_string_character(c: char) -> bool { 13 | c != '"' && c != '\\' 14 | } 15 | 16 | #[derive(Debug, PartialEq)] 17 | pub enum JsonValue { 18 | Str(String), 19 | Boolean(bool), 20 | Num(f64), 21 | Array(Vec), 22 | Object(HashMap), 23 | } 24 | 25 | named!(float<&str,f64>, flat_map!(recognize_float, parse_to!(f64))); 26 | 27 | //FIXME: handle the cases like \u1234 28 | named!( 29 | string<&str,&str>, 30 | delimited!( 31 | tr!(char!('\"')), 32 | escaped!(take_while1!(is_string_character), '\\', one_of!("\"bfnrt\\")), 33 | char!('\"') 34 | ) 35 | ); 36 | 37 | named!( 38 | boolean<&str,bool>, 39 | alt!(value!(false, tag!("false")) | value!(true, tag!("true"))) 40 | ); 41 | 42 | named!( 43 | array<&str,Vec>, 44 | preceded!(sp, delimited!( 45 | char!('['), 46 | separated_list!(char!(','), value), 47 | preceded!(sp, char!(']')) 48 | )) 49 | ); 50 | 51 | named!( 52 | key_value<&str,(&str, JsonValue)>, 53 | preceded!(sp, separated_pair!(tr!(string), tr!(char!(':')), tr!(value))) 54 | ); 55 | 56 | named!( 57 | hash<&str,HashMap>, 58 | preceded!(sp, map!( 59 | delimited!( 60 | char!('{'), 61 | separated_list!(char!(','), tr!(key_value)), 62 | preceded!(sp, char!('}')) 63 | ), 64 | |tuple_vec| tuple_vec 65 | .into_iter() 66 | .map(|(k, v)| (String::from(k), v)) 67 | .collect() 68 | )) 69 | ); 70 | 71 | named!( 72 | value<&str,JsonValue>, 73 | preceded!(sp, tr!(alt!( 74 | tr!(hash) => { |h| JsonValue::Object(h) } | 75 | tr!(array) => { |v| JsonValue::Array(v) } | 76 | tr!(string) => { |s| JsonValue::Str(String::from(s)) } | 77 | tr!(float) => { |f| JsonValue::Num(f) } | 78 | tr!(boolean) => { |b| JsonValue::Boolean(b) } 79 | ))) 80 | ); 81 | 82 | named!( 83 | root<&str,JsonValue>, 84 | tr!(delimited!( 85 | call!(sp), 86 | tr!(alt!( 87 | map!(tr!(hash), JsonValue::Object) | 88 | map!(tr!(array), JsonValue::Array) 89 | )), 90 | not!(complete!(sp)) 91 | )) 92 | ); 93 | 94 | fn main() { 95 | let data = " { \"a\"\t: 42, \"b\": [ \"x\", \"y\", 12 ] , \"c\": { \"hello\" : \"world\" } } "; 96 | 97 | match root(data) { 98 | Ok(val) => println!("parsed value: {:#?}", val), 99 | Err(e) => { 100 | println!("parse error: {:?}", e); 101 | print_trace!(); 102 | } 103 | } 104 | reset_trace!(); 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/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>, 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>, 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(name: &'static str, f: F) -> impl Fn(I) -> IResult 128 | //! where Input: From, 129 | //! F: Fn(I) -> IResult, 130 | //! I: Clone, 131 | //! O: Debug, 132 | //! E: Debug { 133 | //! tr(name, f) 134 | //! } 135 | //! ``` 136 | #[macro_use] 137 | extern crate nom; 138 | 139 | use std::fmt::{self,Debug}; 140 | use std::collections::HashMap; 141 | use nom::IResult; 142 | 143 | pub struct TraceList { 144 | pub traces: HashMap<&'static str, Trace>, 145 | } 146 | 147 | impl 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(&mut self, tag: &'static str, input: T, location: &'static str) 175 | where Input: From { 176 | let t = self.traces.entry(tag).or_insert(Trace::new()); 177 | t.open(input, location); 178 | } 179 | 180 | pub fn close(&mut self, tag: &'static str, input: I, location: &'static str, result: &nom::IResult) 181 | where Input: From { 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 189 | pub struct Trace { 190 | pub events: Vec, 191 | pub level: usize, 192 | pub active: bool, 193 | } 194 | 195 | impl 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(&mut self, input: T, location: &'static str) 216 | where Input: From { 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(&mut self, input: I, location: &'static str, result: &nom::IResult) 230 | where Input: From { 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)] 250 | pub 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)] 258 | pub enum TraceEventType { 259 | Open, 260 | CloseOk(String), 261 | CloseError(String), 262 | CloseFailure(String), 263 | CloseIncomplete(nom::Needed), 264 | } 265 | 266 | impl TraceEvent { 267 | pub fn new(level: usize, input: T, location: &'static str, event: TraceEventType) -> Self 268 | where Input: From { 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::(); 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)] 300 | pub enum Input { 301 | Bytes(*const u8, usize), 302 | String(*const u8, usize), 303 | } 304 | 305 | impl From<&[u8]> for Input { 306 | fn from(input: &[u8]) -> Self { 307 | Input::Bytes(input.as_ptr(), input.len()) 308 | } 309 | } 310 | 311 | impl From<&str> for Input { 312 | fn from(input: &str) -> Self { 313 | Input::String(input.as_ptr(), input.len()) 314 | } 315 | } 316 | 317 | impl 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 | 336 | fn 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 | 355 | static CHARS: &'static [u8] = b"0123456789abcdef"; 356 | 357 | fn to_hex_chunk(chunk: &[u8], i: usize, chunk_size: usize, v: &mut Vec) { 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 | 387 | thread_local! { 388 | pub static NOM_TRACE: ::std::cell::RefCell = ::std::cell::RefCell::new(TraceList::new()); 389 | } 390 | 391 | /// print the trace events to stdout 392 | #[macro_export] 393 | macro_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] 408 | macro_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] 423 | macro_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] 438 | macro_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 452 | pub fn tr(tag: &'static str, name: &'static str, f: F) -> impl Fn(I) -> IResult 453 | where Input: From, 454 | F: Fn(I) -> IResult, 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] 477 | macro_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)] 561 | mod tests { 562 | use super::*; 563 | use nom::character::complete::digit1 as digit; 564 | 565 | #[test] 566 | pub fn trace_bytes_parser() { 567 | named!(parser>, 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 | } 613 | --------------------------------------------------------------------------------