I've been working on a parser combinator for a project in rust. I have a background in F#, so have a tendency to think about currying and partial application, which are a natural fit for this type of problem, but not necessarily rust.
After spending some time getting everything working I built something that seems to work quite well. Until you use it...
Here is the full code GitHub - neildanson/ngl at types.
The important part is this
pub trait Parser<'a, Output>: Clone {
fn parse(&self, input: ContinuationState<'a>) -> ParseResult<'a, Output>;
}
#[derive(Debug, Clone)]
struct ClosureParser<'a, Output, F, C>
where
F: Fn(ContinuationState<'a>, C) -> ParseResult<'a, Output>,
{
parser: F,
captures: C,
_phantom: std::marker::PhantomData<&'a Output>,
}
Where using it starts to look like this
fn pchar_impl<'a>(c: char, input: ContinuationState<'a>) -> ParseResult<char> {
let mut chars = input.remaining.chars();
match chars.next() {
Some(letter) if letter == c => {
let new_line = if letter == '\n' { 1 } else { 0 };
let parser_state = input.advance(1, new_line);
Ok((Token::new(c, input.position, 1), parser_state))
}
Some(letter) => Err(Error::new(
c.to_string(),
letter.to_string(),
input.position,
input.line_number,
input.line_position,
)),
None => Err(Error::new(
c.to_string(),
"".to_string(),
input.position,
input.line_number,
input.line_position,
)),
}
}
and
pub fn pchar<'a>(value: char) -> impl Parser<'a, Output = char> {
ClosureParser::new(move |input, value| pchar_impl(value, input), value)
}
If you were to pull that branch, the tests would pass and the benchmarks would run - great!
But it you try to build or run the project - you'll need some coffee and a lot of RAM. On my 5950X it will just about compile after about 15 minutes and sitting on over 100GB RAM (I have 128Gb).
If you extend the commented out from line 40 onwards in main.rs, the compile comes down to around 10s.
You'd think the compiler must be doing a lot in that time - and I'm sure it is, but the difference between the 2 exes (on windows) - 1kb. So it's not spending its time generating code!
I'm currently rethinking the approach, but I'd love feedback on quite why rustc is soooo unhappy with me!