![Nick Alexander](/assets/img/avatar_default.png)
* Pre: Expose more in edn. * Pre: Make it easier to work with ValueAndSpan. with_spans() is a temporary hack, needed only because I don't care to parse the bootstrap assertions from text right now. * Part 1a: Add `value_and_span` for parsing nested `edn::ValueAndSpan` instances. I wasn't able to abstract over `edn::Value` and `edn::ValueAndSpan`; there are multiple obstacles. I chose to roll with `edn::ValueAndSpan` since it exposes the additional span information that we will want to form good error messages in the future. * Part 1b: Add keyword_map() parsing an `edn::Value::Vector` into an `edn::Value::map`. * 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. * Part 2: Use `value_and_span` apparatus in tx-parser/. I break an abstraction boundary by returning a value column `edn::ValueAndSpan` rather than just an `edn::Value`. That is, the transaction processor shouldn't care where the `edn::Value` it is processing arose -- even we care to track that information we should bake it into the `Entity` type. We do this because we need to dynamically parse the value column to support nested maps, and parsing requires a full `edn::ValueAndSpan`. Alternately, we could cheat and fake the spans when parsing nested maps, but that's potentially expensive. * Part 3: Use `value_and_span` apparatus in query-parser/. * Part 4: Use `value_and_span` apparatus in root crate. * Review comment: Make Span and SpanPosition Copy. * Review comment: nits. * Review comment: Make `or` be `or_exactly`. I baked the eof checking directly into the parser, rather than using the skip and eof parsers. I also took the time to restore some tests that were mistakenly commented out. * Review comment: Extract and use def_matches_* macros. * Review comment: .map() as late as possible.
88 lines
2.5 KiB
Rust
88 lines
2.5 KiB
Rust
// 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>(P, T)
|
|
where P: Parser,
|
|
T: ::std::fmt::Debug;
|
|
|
|
impl<I, P, T> Parser for Log<P, T>
|
|
where I: Stream,
|
|
I::Item: ::std::fmt::Debug,
|
|
P: Parser<Input = I>,
|
|
P::Output: ::std::fmt::Debug,
|
|
T: ::std::fmt::Debug,
|
|
{
|
|
type Input = I;
|
|
type Output = P::Output;
|
|
|
|
fn parse_stream(&mut self, input: I) -> ParseResult<Self::Output, I> {
|
|
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::Input>) {
|
|
self.0.add_error(errors);
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn log<P, T>(p: P, msg: T) -> Log<P, T>
|
|
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<T>(self, msg: T) -> Log<Self, T>
|
|
where Self: Sized,
|
|
T: ::std::fmt::Debug;
|
|
}
|
|
|
|
impl<P> LogParsing for P
|
|
where P: Parser,
|
|
{
|
|
fn log<T>(self, msg: T) -> Log<Self, T>
|
|
where T: ::std::fmt::Debug,
|
|
{
|
|
log(self, msg)
|
|
}
|
|
}
|