Accessing numbering::InputPosition::position via map_err
dashed opened this issue · 2 comments
I have a usecase where I'd like to somehow pass numbering::InputPosition::position
to an Error
type as a way of reporting parsing errors at a location (e.g. line/column location).
The issue is that I'm unable to access numbering::InputPosition::position
from within chomp::types::ParseResult::map_err
function.
I adapted map_err
into map_err2
as follows: dashed@3f1998b
This enables me to do this:
type ESParseResult<I, T> = ParseResult<I, T, ParseError>;
fn some_parser<I: U8Input>(i: InputPosition<I, CurrentPosition>)
-> ESParseResult<InputPosition<I, CurrentPosition>, ()> {
parse!{i;
let _var = (i -> {
string(i, b"var")
.map_err2(|_, i| {
let loc = i.position();
ParseError::Expected(loc, "Expected var here.")
})
});
// ...
ret {()}
}
}
I'd love to hear any feedback on this, especially for any better alternative approaches. 👍
Appendix
CurrentPosition
type for reference:
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct CurrentPosition(
// The current line, zero-indexed.
u64,
// The current col, zero-indexed.
u64
);
impl CurrentPosition {
// Creates a new (line, col) counter with zero.
pub fn new() -> Self {
CurrentPosition(0, 0)
}
}
impl Numbering for CurrentPosition {
type Token = u8;
fn update<'a, B>(&mut self, b: &'a B)
where B: Buffer<Token=Self::Token> {
b.iterate(|c| if c == b'\n' {
self.0 += 1; // line num
self.1 = 0; // col num
} else {
self.1 += 1; // col num
});
}
fn add(&mut self, t: Self::Token) {
if t == b'\n' {
self.0 += 1; // line num
self.1 = 0; // col num
} else {
self.1 += 1; // col num
}
}
}
pub trait Input: Sized {
// ...
#[inline]
pub fn map_err2<V, F>(self, f: F) -> ParseResult<I, T, V>
where F: FnOnce(E, &I) -> V {
match self {
ParseResult(i, Ok(t)) => ParseResult(i, Ok(t)),
ParseResult(i, Err(e)) => {
let err = f(e, &i);
ParseResult(i, Err(err))
},
}
}
// ...
}
I might settle on a higher-order function as follows:
fn on_error<I: Input, T, E, F, V, G>(i: I, f: F, g: G) -> ParseResult<I, T, V>
where F: FnOnce(I) -> ParseResult<I, T, E>,
G: FnOnce(E, &I) -> V {
match f(i).into_inner() {
(i, Ok(t)) => i.ret(t),
(i, Err(e)) => {
let err_val = g(e, &i);
i.err(err_val)
}
}
}
Usage:
fn some_parser<I: U8Input>(i: InputPosition<I, CurrentPosition>)
-> ESParseResult<InputPosition<I, CurrentPosition>, ()> {
parse!{i;
let var = on_error(
|i| string(i, b"var"),
|_err, i| {
let loc = i.position();
ParseError::Expected(loc, "Expected var keyword.".to_string())
}
);
// ...
ret {()}
}
}
That looks pretty good, when I have the time I will see if I can include something like that in Chomp proper. Would be useful to have a higher-order function and/or error type to carry position-information for errors.