Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
Markus Westerlind
@Marwes
Cheaper error types may omit some of that information
william light
@wrl
@Marwes hey, hoping you can help with this. I want to write a macro (or similar) which works like choice!(), but which lets me inspect the result of the previous parser. basically, something like .then(|x| match x { a => parser_for_a(), b => parser_for_b(), ... })
however, I'm having a lot of difficulty getting the types to match up
the sub-parsers are all defined in functions which return impl Parser, but since they're different opaque types, rustc doesn't like it at all
is there a way to make this work without having a huge enum with the separate types?
Markus Westerlind
@Marwes
No, you either need an enum or you need to box the parsers. But if you use the 4.x beta there is the dispatch! parser which does exactly this https://docs.rs/combine/4.0.0-beta.1/src/combine/parser/choice.rs.html#800-816
Seems I forgot to add a doc comment to it however
If you don't need to pass any arguments to the parsers in the alternatives there is also the option of wrapping them with https://docs.rs/combine/4.0.0-beta.1/combine/parser/combinator/fn.opaque.html
That unifies their types to FnOpaque
william light
@wrl
Yeah, I'm on 4.x beta. Both dispatch! and opaque! look like they might work for me – I'll give them a shot and report back.
william light
@wrl
@Marwes dispatch! worked with a slight modification to add match guards. I've submitted a PR.
william light
@wrl
so, I have ended up with a lot of small parser functions returning impl Trait and which are generic over Input. my compile times have absolutely skyrocketed and i'm looking for a way to bring them down. i've isolated all of my parser logic into a separate crate, but that doesn't seem to help – the outer crate compile times are still brutal.
is there any way around this?
this is in debug mode, too – not release
Markus Westerlind
@Marwes
I would guess that it is because the parsers are generic by the input type maybe?
If it is, then rust is still force to monomorphize in the callsite of the outer crate which is likely most of the work
Using impl Trait shouldn't harm compiletimes much on its own
william light
@wrl
@Marwes yeah, that's what I'm thinking too. even though the function that I've exported from the inner crate isn't generic, I'm guessing it's just monomorphizing in the outer crate.
is there some way to, uh... fix this? or speed it up at all?
Markus Westerlind
@Marwes
Add a function to the inner crate which specializes and the call that
william light
@wrl
ah, I bet it know what it is
the inner function is generic over R: Read
Markus Westerlind
@Marwes
Running compilation with RUSTFLAGS=-Z time-passes could be helpful to get some insight
Only works on nightly however
william light
@wrl
I'll give that a shot
John Sullivan
@itsjohncs
I'm trying to produce a nice parser error in the event of an integer overflow while parsing, and I'm not really getting anywhere.
fn n_digits<Input>(min: usize, max: usize) -> impl Parser<Input, Output = i32>
where
    Input: Stream<Token = char>,
    // Necessary due to rust-lang/rust#24159
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    count_min_max(min, max, digit()).map(|s: String| {
        s.parse::<i32>().unwrap()
    })
}
I would like to not panic in the event that s.parse fails, but despite diving into a few codebases that use combine and trying many things, I'm not getting anywhere. Can anyone help me out?
Markus Westerlind
@Marwes
https://docs.rs/combine/4.0.1/combine/fn.from_str.html
from_str(count_min_max::<String, _, _>(min, max, digit()))
Easiest way, as long as the error message from parse::<i32> is acceptable
Otherwise you can use and_then like from_str does and substitute your own message https://github.com/Marwes/combine/blob/a358c88933bb58c4df0d9a0212b4a33d2b24be7b/src/parser/combinator.rs#L1165-L1169
John Sullivan
@itsjohncs
Lovely, thank you

I did stumble onto StreamErrorFor::<Input>::other eventually yesterday, and ended up with

fn n_digits<Input>(min: usize, max: usize) -> impl Parser<Input, Output = i32>
where
    Input: Stream<Token = char>,
    // Necessary due to rust-lang/rust#24159
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    count_min_max(min, max, digit()).and_then(|s: String| {
        s.parse().map_err(StreamErrorFor::<Input>::other)
    })
}

Which was acceptable. I'll try out from_str tonight though and compare the error messages that they generate, looks like they may arrive at different errors, though I'm not sure how they'll differ.

John Sullivan
@itsjohncs
I've been trying to get captures working, but I can't find an example of its use anywhere. Even using GitHub's search all public repos feature.
It works in a toy example with types inferred, but if I try to make a function using captures I get some incredible error spam that's hard to sort through.
In particular, this works fine:
fn main() {
    let mut p = 
        captures(Regex::new(r"<((?:\\\\|\\>|[^>\n])*)>").unwrap())
            .map(|captures: Vec<&str>| String::from(captures[1]));
    let input = "<hello>";
    let result = p.easy_parse(input);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}
But this gives me a great deal of errors:
fn custom_status<Input>() -> impl Parser<Input, Output = String>
where
    Input: Stream<Token = char>,
    // Necessary due to rust-lang/rust#24159
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    captures(Regex::new("<(\\\\|\\>|[^>\n])>").unwrap()).map(|captures: Vec<&str>| String::from(captures[1]))
}
John Sullivan
@itsjohncs

Goddam, I am not nearly familiar enough with Rust to be doing this, but I did figure out how to make my function compile:

/**
 * Parses a status.
 */
fn custom_status<'a, Input: 'a>() -> impl Parser<Input, Output = String> + 'a
where
    Input: Stream<Token = char>,
    Input: RangeStreamOnce<Range = &'a str>,
    // Necessary due to rust-lang/rust#24159
    Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
{
    captures(Regex::new(r"<((?:\\\\|\\>|[^>\n])*)>").unwrap()).map(|captures: Vec<&str>| String::from(captures[1]))
}

I am dubious that this is the easiest way though...

william light
@wrl

i have been increasingly writing parsers that have chains of the form

.then(|val| {
    value(val)
        .and(.........

and I figure that there could be a combinator here that passes in the output of the previous parser as an immutable ref for dispatching but then just forwards that value through without consuming it

Markus Westerlind
@Marwes
I suppose that could be added
william light
@wrl
I'd be happy to add it and submit a PR, but I'm not all that familiar with the innards of Then or ThenPartial
Markus Westerlind
@Marwes
You should be able to to copy the existing parser and tweak it a bit
william light
@wrl
if you'd be willing to provide a bit of guidance about what'd have to change, I'd appreciate the insight
yeah
Markus Westerlind
@Marwes
I think you can change the Output type to a tuple of the output and make the function take a ref, then fix the compile errors
william light
@wrl
so probably Output = (P::Output, N::Output);
Markus Westerlind
@Marwes
Yep
william light
@wrl
and then change the parse_committed_mode() call? or
Markus Westerlind
@Marwes
You should just need to change what is returned from fn parse_mode_impl in Then(Ref)