1#[cfg(feature = "trace")]
34use nom::IResult;
35pub use nom_tracable_macros::tracable_parser;
37use std::{collections::HashMap, io::Write};
38
39pub trait FragmentDisplay {
41 fn display(&self, width: usize) -> String;
42}
43
44impl FragmentDisplay for &[u8] {
45 fn display(&self, width: usize) -> String {
46 self.iter()
47 .take(width / 2)
48 .map(|x| format!("{:>02X}", x))
49 .collect()
50 }
51}
52
53impl FragmentDisplay for &str {
54 fn display(&self, width: usize) -> String {
55 self.lines()
56 .next()
57 .unwrap_or_else(|| "")
58 .chars()
59 .take(width)
60 .collect()
61 }
62}
63
64pub trait Tracable: HasTracableInfo {
66 fn inc_depth(self) -> Self;
67 fn dec_depth(self) -> Self;
68 fn format(&self) -> String;
69 fn header(&self) -> String;
70}
71
72pub trait HasTracableInfo {
74 fn get_tracable_info(&self) -> TracableInfo;
75 fn set_tracable_info(self, info: TracableInfo) -> Self;
76}
77
78#[derive(Clone, Copy, Debug, PartialEq)]
80pub struct TracableInfo {
81 #[cfg(feature = "trace")]
82 pub depth: usize,
83 #[cfg(feature = "trace")]
84 pub forward: bool,
85 #[cfg(feature = "trace")]
86 pub backward: bool,
87 #[cfg(feature = "trace")]
88 pub custom: bool,
89 #[cfg(feature = "trace")]
90 pub color: bool,
91 #[cfg(feature = "trace")]
92 pub count_width: usize,
93 #[cfg(feature = "trace")]
94 pub parser_width: usize,
95 #[cfg(feature = "trace")]
96 pub fragment_width: usize,
97 #[cfg(feature = "trace")]
98 pub fold: u64,
99}
100
101impl Default for TracableInfo {
102 fn default() -> Self {
103 TracableInfo {
104 #[cfg(feature = "trace")]
105 depth: 0,
106 #[cfg(feature = "trace")]
107 forward: true,
108 #[cfg(feature = "trace")]
109 backward: true,
110 #[cfg(feature = "trace")]
111 custom: true,
112 #[cfg(feature = "trace")]
113 color: true,
114 #[cfg(feature = "trace")]
115 count_width: 10,
116 #[cfg(feature = "trace")]
117 parser_width: 96,
118 #[cfg(feature = "trace")]
119 fragment_width: 96,
120 #[cfg(feature = "trace")]
121 fold: 0,
122 }
123 }
124}
125
126#[cfg(feature = "trace")]
127impl TracableInfo {
128 pub fn new() -> Self {
129 TracableInfo::default()
130 }
131
132 pub fn depth(mut self, x: usize) -> Self {
133 self.depth = x;
134 self
135 }
136
137 pub fn forward(mut self, x: bool) -> Self {
139 self.forward = x;
140 self
141 }
142
143 pub fn backward(mut self, x: bool) -> Self {
145 self.backward = x;
146 self
147 }
148
149 pub fn custom(mut self, x: bool) -> Self {
151 self.custom = x;
152 self
153 }
154
155 pub fn color(mut self, x: bool) -> Self {
157 self.color = x;
158 self
159 }
160
161 pub fn count_width(mut self, x: usize) -> Self {
163 self.count_width = x;
164 self
165 }
166
167 pub fn parser_width(mut self, x: usize) -> Self {
169 self.parser_width = x;
170 self
171 }
172
173 pub fn fragment_width(mut self, x: usize) -> Self {
175 self.fragment_width = x;
176 self
177 }
178
179 pub fn fold(mut self, x: &str) -> Self {
181 let index =
182 crate::TRACABLE_STORAGE.with(|storage| storage.borrow_mut().get_parser_index(x));
183
184 let val = 1u64 << index;
185 let mask = !(1u64 << index);
186
187 self.fold = (self.fold & mask) | val;
188 self
189 }
190
191 fn folded(self, x: &str) -> bool {
192 let index =
193 crate::TRACABLE_STORAGE.with(|storage| storage.borrow_mut().get_parser_index(x));
194
195 if index < 64 {
196 ((self.fold >> index) & 1u64) == 1u64
197 } else {
198 false
199 }
200 }
201}
202
203#[cfg(not(feature = "trace"))]
204impl TracableInfo {
205 pub fn new() -> Self {
206 TracableInfo::default()
207 }
208
209 pub fn forward(self, _x: bool) -> Self {
210 self
211 }
212
213 pub fn backward(self, _x: bool) -> Self {
214 self
215 }
216
217 pub fn custom(self, _x: bool) -> Self {
218 self
219 }
220
221 pub fn color(self, _x: bool) -> Self {
222 self
223 }
224
225 pub fn count_width(self, _x: usize) -> Self {
226 self
227 }
228
229 pub fn parser_width(self, _x: usize) -> Self {
230 self
231 }
232
233 pub fn fragment_width(self, _x: usize) -> Self {
234 self
235 }
236
237 pub fn fold(self, _x: &str) -> Self {
238 self
239 }
240}
241
242impl HasTracableInfo for TracableInfo {
243 fn get_tracable_info(&self) -> TracableInfo {
244 *self
245 }
246
247 fn set_tracable_info(self, info: TracableInfo) -> Self {
248 info
249 }
250}
251
252#[cfg(feature = "trace")]
253impl<T, U: HasTracableInfo> HasTracableInfo for nom_locate::LocatedSpan<T, U> {
254 fn get_tracable_info(&self) -> TracableInfo {
255 self.extra.get_tracable_info()
256 }
257
258 fn set_tracable_info(mut self, info: TracableInfo) -> Self {
259 self.extra = self.extra.set_tracable_info(info);
260 self
261 }
262}
263
264#[cfg(feature = "trace")]
265impl<T: FragmentDisplay + nom::AsBytes, U: HasTracableInfo> Tracable
266 for nom_locate::LocatedSpan<T, U>
267{
268 fn inc_depth(self) -> Self {
269 let info = self.get_tracable_info();
270 let info = info.depth(info.depth + 1);
271 self.set_tracable_info(info)
272 }
273
274 fn dec_depth(self) -> Self {
275 let info = self.get_tracable_info();
276 let info = info.depth(info.depth - 1);
277 self.set_tracable_info(info)
278 }
279
280 fn format(&self) -> String {
281 let info = self.get_tracable_info();
282 let fragment = self.fragment().display(info.fragment_width);
283 format!("{:<8} : {}", self.location_offset(), fragment)
284 }
285
286 fn header(&self) -> String {
287 format!("{:<8} : {}", "offset", "fragment")
288 }
289}
290
291#[derive(Debug, Default)]
292struct TracableStorage {
293 forward_count: usize,
294 backward_count: usize,
295 parser_indexes: HashMap<String, usize>,
296 parser_index_next: usize,
297 histogram: HashMap<String, usize>,
298 cumulative_histogram: HashMap<String, usize>,
299 cumulative_working: HashMap<(String, usize), usize>,
300}
301
302#[allow(dead_code)]
303impl TracableStorage {
304 fn new() -> Self {
305 TracableStorage::default()
306 }
307
308 fn init(&mut self) {
309 self.forward_count = 0;
310 self.backward_count = 0;
311 self.histogram.clear();
312 self.cumulative_histogram.clear();
313 self.cumulative_working.clear();
314 }
315
316 fn get_forward_count(&self) -> usize {
317 self.forward_count
318 }
319
320 fn get_backward_count(&self) -> usize {
321 self.backward_count
322 }
323
324 fn inc_forward_count(&mut self) {
325 self.forward_count += 1
326 }
327
328 fn inc_backward_count(&mut self) {
329 self.backward_count += 1
330 }
331
332 fn inc_histogram(&mut self, key: &str) {
333 let next = if let Some(x) = self.histogram.get(key) {
334 x + 1
335 } else {
336 1
337 };
338 self.histogram.insert(String::from(key), next);
339 }
340
341 fn inc_cumulative_histogram(&mut self, key: &str, cnt: usize) {
342 let next = if let Some(x) = self.cumulative_histogram.get(key) {
343 x + cnt
344 } else {
345 cnt
346 };
347 self.cumulative_histogram.insert(String::from(key), next);
348 }
349
350 fn add_cumulative(&mut self, key: &str, depth: usize) {
351 self.cumulative_working
352 .insert((String::from(key), depth), 0);
353 }
354
355 fn inc_cumulative(&mut self) {
356 for val in self.cumulative_working.values_mut() {
357 *val = *val + 1;
358 }
359 }
360
361 fn del_cumulative(&mut self, key: &str, depth: usize) {
362 self.cumulative_working.remove(&(key.to_string(), depth));
363 }
364
365 fn get_cumulative(&mut self, key: &str, depth: usize) -> Option<&usize> {
366 self.cumulative_working.get(&(key.to_string(), depth))
367 }
368
369 fn get_parser_index(&mut self, key: &str) -> usize {
370 if let Some(x) = self.parser_indexes.get(key) {
371 *x
372 } else {
373 let new_index = self.parser_index_next;
374 self.parser_index_next += 1;
375 self.parser_indexes.insert(String::from(key), new_index);
376 new_index
377 }
378 }
379}
380
381#[cfg(feature = "trace")]
382thread_local!(
383 static TRACABLE_STORAGE: core::cell::RefCell<crate::TracableStorage> = {
384 core::cell::RefCell::new(crate::TracableStorage::new())
385 }
386);
387
388pub fn histogram() {
418 histogram_internal();
419}
420
421#[cfg(feature = "trace")]
422fn histogram_internal() {
423 crate::TRACABLE_STORAGE.with(|storage| {
424 let storage = storage.borrow();
425 show_histogram("histogram", &storage.histogram);
426 });
427}
428
429#[cfg(not(feature = "trace"))]
430fn histogram_internal() {}
431
432pub fn cumulative_histogram() {
464 cumulative_histogram_internal();
465}
466
467#[cfg(feature = "trace")]
468fn cumulative_histogram_internal() {
469 crate::TRACABLE_STORAGE.with(|storage| {
470 let storage = storage.borrow();
471 show_histogram("cumulative histogram", &storage.cumulative_histogram);
472 });
473}
474
475#[cfg(not(feature = "trace"))]
476fn cumulative_histogram_internal() {}
477
478#[allow(dead_code)]
479fn show_histogram(title: &str, map: &HashMap<String, usize>) {
480 let mut result = Vec::new();
481 let mut max_parser_len = "parser".len();
482 let mut max_count = 0;
483 let mut max_count_len = "count".len();
484 for (p, c) in map {
485 result.push((p, c));
486 max_parser_len = max_parser_len.max(p.len());
487 max_count = max_count.max(*c);
488 max_count_len = max_count_len.max(format!("{}", c).len());
489 }
490
491 result.sort_by(|a, b| b.1.partial_cmp(a.1).unwrap());
492
493 let bar_length = 50;
494
495 let mut lock = if cfg!(feature = "stderr") {
496 Box::new(std::io::stderr().lock()) as Box<dyn Write>
497 } else {
498 Box::new(std::io::stdout().lock()) as Box<dyn Write>
499 };
500
501 writeln!(
502 lock,
503 "\n{:<parser$} | {:<bar$} | {}",
504 "parser",
505 title,
506 "count",
507 parser = max_parser_len,
508 bar = bar_length,
509 )
510 .unwrap();
511
512 writeln!(
513 lock,
514 "{:<parser$} | {:<bar$} | {}",
515 "-".repeat(max_parser_len),
516 "-".repeat(bar_length),
517 "-".repeat(max_count_len),
518 parser = max_parser_len,
519 bar = bar_length,
520 )
521 .unwrap();
522
523 for (p, c) in &result {
524 let bar = *c * bar_length / max_count;
525 if bar > 0 {
526 writeln!(
527 lock,
528 "{:<parser$} | {}{} | {}",
529 p,
530 ".".repeat(bar),
531 " ".repeat(bar_length - bar),
532 c,
533 parser = max_parser_len,
534 )
535 .unwrap();
536 }
537 }
538 writeln!(lock, "").unwrap()
539}
540
541#[cfg(feature = "trace")]
544pub fn forward_trace<T: Tracable>(input: T, name: &str) -> (TracableInfo, T) {
545 let info = input.get_tracable_info();
546 let depth = info.depth;
547
548 let mut lock = if cfg!(feature = "stderr") {
549 Box::new(std::io::stderr().lock()) as Box<dyn Write>
550 } else {
551 Box::new(std::io::stdout().lock()) as Box<dyn Write>
552 };
553
554 if depth == 0 {
555 crate::TRACABLE_STORAGE.with(|storage| {
556 storage.borrow_mut().init();
557 });
558 let forward_backword = if info.forward & info.backward {
559 format!(
560 "{:<count_width$} {:<count_width$}",
561 "forward",
562 "backward",
563 count_width = info.count_width
564 )
565 } else if info.forward {
566 format!(
567 "{:<count_width$}",
568 "forward",
569 count_width = info.count_width
570 )
571 } else {
572 format!(
573 "{:<count_width$}",
574 "backward",
575 count_width = info.count_width
576 )
577 };
578
579 let control_witdh = if info.color { 11 } else { 0 };
580
581 writeln!(
582 lock,
583 "\n{} : {:<parser_width$} : {}",
584 forward_backword,
585 "parser",
586 input.header(),
587 parser_width = info.parser_width - control_witdh,
588 )
589 .unwrap();
590 }
591
592 if info.forward {
593 let forward_count = crate::TRACABLE_STORAGE.with(|storage| {
594 storage.borrow_mut().inc_forward_count();
595 storage.borrow().get_forward_count()
596 });
597
598 let forward_backword = if info.backward {
599 format!(
600 "{:<count_width$} {:<count_width$}",
601 forward_count,
602 "",
603 count_width = info.count_width
604 )
605 } else {
606 format!(
607 "{:<count_width$}",
608 forward_count,
609 count_width = info.count_width
610 )
611 };
612
613 let color = if info.color { "\u{001b}[1;37m" } else { "" };
614 let reset = if info.color { "\u{001b}[0m" } else { "" };
615 let folded = if info.folded(name) { "+" } else { " " };
616
617 writeln!(
618 lock,
619 "{} : {:<parser_width$} : {}",
620 forward_backword,
621 format!(
622 "{}{}-> {} {}{}",
623 color,
624 " ".repeat(depth),
625 name,
626 folded,
627 reset
628 ),
629 input.format(),
630 parser_width = info.parser_width,
631 )
632 .unwrap();
633 }
634
635 crate::TRACABLE_STORAGE.with(|storage| {
636 storage.borrow_mut().inc_histogram(name);
637 storage.borrow_mut().add_cumulative(name, depth);
638 storage.borrow_mut().inc_cumulative();
639 });
640
641 let input = if info.folded(name) {
642 let info = info.forward(false).backward(false).custom(false);
643 input.set_tracable_info(info)
644 } else {
645 input
646 };
647
648 let input = input.inc_depth();
649 (info, input)
650}
651
652#[cfg(feature = "trace")]
655pub fn backward_trace<T: Tracable, U, V>(
656 input: IResult<T, U, V>,
657 name: &str,
658 info: TracableInfo,
659) -> IResult<T, U, V> {
660 let depth = info.depth;
661
662 crate::TRACABLE_STORAGE.with(|storage| {
663 let cnt = *storage.borrow_mut().get_cumulative(name, depth).unwrap();
664 storage.borrow_mut().inc_cumulative_histogram(name, cnt);
665 });
666
667 if info.backward {
668 let backward_count = crate::TRACABLE_STORAGE.with(|storage| {
669 storage.borrow_mut().inc_backward_count();
670 storage.borrow().get_backward_count()
671 });
672
673 let forward_backword = if info.forward {
674 format!(
675 "{:<count_width$} {:<count_width$}",
676 "",
677 backward_count,
678 count_width = info.count_width
679 )
680 } else {
681 format!(
682 "{:<count_width$}",
683 backward_count,
684 count_width = info.count_width
685 )
686 };
687
688 let color_ok = if info.color { "\u{001b}[1;32m" } else { "" };
689 let color_err = if info.color { "\u{001b}[1;31m" } else { "" };
690 let reset = if info.color { "\u{001b}[0m" } else { "" };
691 let folded = if info.folded(name) { "+" } else { " " };
692
693 let mut lock = if cfg!(feature = "stderr") {
694 Box::new(std::io::stderr().lock()) as Box<dyn Write>
695 } else {
696 Box::new(std::io::stdout().lock()) as Box<dyn Write>
697 };
698
699 match input {
700 Ok((s, x)) => {
701 writeln!(
702 lock,
703 "{} : {:<parser_width$} : {}",
704 forward_backword,
705 format!(
706 "{}{}<- {} {}{}",
707 color_ok,
708 " ".repeat(depth),
709 name,
710 folded,
711 reset
712 ),
713 s.format(),
714 parser_width = info.parser_width,
715 )
716 .unwrap();
717
718 let s = if info.folded(name) {
719 let info = s
720 .get_tracable_info()
721 .forward(info.forward)
722 .backward(info.backward)
723 .custom(info.custom);
724 s.set_tracable_info(info)
725 } else {
726 s
727 };
728
729 Ok((s.dec_depth(), x))
730 }
731 Err(x) => {
732 writeln!(
733 lock,
734 "{} : {:<parser_width$}",
735 forward_backword,
736 format!(
737 "{}{}<- {} {}{}",
738 color_err,
739 " ".repeat(depth),
740 name,
741 folded,
742 reset
743 ),
744 parser_width = info.parser_width,
745 )
746 .unwrap();
747 Err(x)
748 }
749 }
750 } else {
751 input
752 }
753}
754
755#[cfg(feature = "trace")]
757pub fn custom_trace<T: Tracable>(input: &T, name: &str, message: &str, color: &str) {
758 let info = input.get_tracable_info();
759
760 if info.custom {
761 let depth = info.depth;
762 let forward_backword = format!(
763 "{:<count_width$} {:<count_width$}",
764 "",
765 "",
766 count_width = info.count_width
767 );
768
769 let color = if info.color { color } else { "" };
770 let reset = if info.color { "\u{001b}[0m" } else { "" };
771
772 let mut lock = if cfg!(feature = "stderr") {
773 Box::new(std::io::stderr().lock()) as Box<dyn Write>
774 } else {
775 Box::new(std::io::stdout().lock()) as Box<dyn Write>
776 };
777
778 writeln!(
779 lock,
780 "{} : {:<parser_width$} : {}",
781 forward_backword,
782 format!("{}{} {}{}", color, " ".repeat(depth), name, reset),
783 message,
784 parser_width = info.parser_width,
785 )
786 .unwrap();
787 }
788}