From f4e2a0471ffe53228b3bfee2bbdaf45c1322e960 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Thu, 30 Mar 2017 15:03:40 -0700 Subject: [PATCH] Part 1c: Add `Log`/`.log(...)` for logging parser progress. This is a terrible hack, but it sure helps to debug complicated nested parsers. I don't even know what a principled approach would look like; since our parser combinators are so frequently expressed in code, it's hard to imagine a data-driven interpreter that can help debug things. --- parser-utils/src/lib.rs | 10 +++-- parser-utils/src/log.rs | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 parser-utils/src/log.rs diff --git a/parser-utils/src/lib.rs b/parser-utils/src/lib.rs index 69b9163b..5e8cf923 100644 --- a/parser-utils/src/lib.rs +++ b/parser-utils/src/lib.rs @@ -11,17 +11,21 @@ extern crate combine; extern crate edn; -pub mod value_and_span; - use combine::{ ParseResult, - Stream, }; use combine::combinator::{ Expected, FnParser, }; +pub mod log; +pub mod value_and_span; + +pub use log::{ + LogParsing, +}; + /// A type definition for a function parser that either parses an `O` from an input stream of type /// `I`, or fails with an "expected" failure. /// See for more diff --git a/parser-utils/src/log.rs b/parser-utils/src/log.rs new file mode 100644 index 00000000..9d29cbe4 --- /dev/null +++ b/parser-utils/src/log.rs @@ -0,0 +1,87 @@ +// Copyright 2016 Mozilla +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use +// this file except in compliance with the License. You may obtain a copy of the +// License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +use std::io::Write; + +use combine::{ + ParseError, + Parser, + ParseResult, + Stream, +}; + +/// println!, but to stderr. +/// +/// Doesn't pollute stdout, which is useful when running tests under Emacs, which parses the output +/// of the test suite to format errors and can get confused when user output is interleaved into the +/// stdout stream. +/// +/// Cribbed from http://stackoverflow.com/a/27590832. +macro_rules! println_stderr( + ($($arg:tt)*) => { { + let r = writeln!(&mut ::std::io::stderr(), $($arg)*); + r.expect("failed printing to stderr"); + } } +); + +#[derive(Clone)] +pub struct Log(P, T) + where P: Parser, + T: ::std::fmt::Debug; + +impl Parser for Log + where I: Stream, + I::Item: ::std::fmt::Debug, + P: Parser, + P::Output: ::std::fmt::Debug, + T: ::std::fmt::Debug, +{ + type Input = I; + type Output = P::Output; + + fn parse_stream(&mut self, input: I) -> ParseResult { + let head = input.clone().uncons(); + let result = self.0.parse_stream(input.clone()); + match result { + Ok((ref value, _)) => println_stderr!("{:?}: [{:?} ...] => Ok({:?})", self.1, head.ok(), value), + Err(_) => println_stderr!("{:?}: [{:?} ...] => Err(_)", self.1, head.ok()), + } + result + } + + fn add_error(&mut self, errors: &mut ParseError) { + self.0.add_error(errors); + } +} + +#[inline(always)] +pub fn log(p: P, msg: T) -> Log + where P: Parser, + T: ::std::fmt::Debug, +{ + Log(p, msg) +} + +/// We need a trait to define `Parser.log` and have it live outside of the `combine` crate. +pub trait LogParsing: Parser + Sized { + fn log(self, msg: T) -> Log + where Self: Sized, + T: ::std::fmt::Debug; +} + +impl

LogParsing for P + where P: Parser, +{ + fn log(self, msg: T) -> Log + where T: ::std::fmt::Debug, + { + log(self, msg) + } +}