Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Choice with different parser types #63

Closed
donhcd opened this issue Jun 17, 2016 · 4 comments
Closed

Choice with different parser types #63

donhcd opened this issue Jun 17, 2016 · 4 comments

Comments

@donhcd
Copy link

donhcd commented Jun 17, 2016

I saw this mentioned in #54, but I'm not sure exactly how to get this to work. I'm a bit new to rust, so that's also probably related.

I'm trying to write something that can parse an escaped string, e.g. "Hello \n \"World\"" and for this one of my helpers is this:

    let esc_char = char('\\').with(choice([
        char('\\').with(value('\\')),
        char('"').with(value('"')),
        char('0').with(value('\0')),
        char('n').with(value('\n')),
        char('t').with(value('\t')),
        char('r').with(value('\r')),
    ]));

You'll notice that the first 2 with method calls are completely useless functionally, but they're the only thing making this expression type-check. I've been trying with

    let esc_char = char('\\').with(choice::<&mut [&mut Parser<Input = &str, Output = char>; 6], _>(&mut [
        char('\\').with(value('\\')),
        char('"').with(value('"')),

but I get these errors:

src/main.rs:36:9: 36:37 error: mismatched types:
 expected `_`,
    found `combine::combinator::With<combine::combinator::Token<_>, combine::combinator::Value<_, char>>`
(expected &-ptr,
    found struct `combine::combinator::With`) [E0308]
src/main.rs:36         char('\\').with(value('\\')),
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:36:9: 36:37 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:34:100: 42:6 error: mismatched types:
 expected `&mut [&mut combine::Parser<Input=&str, Output=char>; 6]`,
    found `&mut [combine::combinator::With<combine::combinator::Token<_>, combine::combinator::Value<_, char>>; 6]`
(expected &-ptr,
    found struct `combine::combinator::With`) [E0308]
src/main.rs:34     let esc_char = char('\\').with(choice::<&mut [&mut Parser<Input = &str, Output = char>; 6], _>(&mut [
src/main.rs:35         char('\\').with(value('\\')),
src/main.rs:36         char('"').with(value('"')),

Do you have any idea what I'm doing wrong?

@Marwes
Copy link
Owner

Marwes commented Jun 17, 2016

The first example you possible should typecheck fine, however it is possible you might have gotten a message that rust wasn't able to infer enough type information like this.

src/lib.rs:466:24: 466:28 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
src/lib.rs:466     let mut esc_char = char('\\').with(choice([
                                      ^~~~
src/lib.rs:466:24: 466:28 help: run `rustc --explain E0282` to see a detailed explanation
error: aborting due to previous error
Build failed, waiting for other jobs to finish...

If that is the case the only problem is that rust could not figure out what type is being parsed. So if you want to parse a raw string you can add a line.

esc_char.parse("Hello \n \"World\"");

Which shows to Rust that the parser should parse a string. (The problem is that rust needs to specialize the parser for a single input type but the parser could equivalently parse an iterator of chars instead of a &str). You could also define a generic function to make it work for both &str and iterators as in the expression example in https://marwes.github.io/combine/combine/index.html.

The reason your second example fails can be seen from the error message if you know what to look for. If you see this part it says it expected a reference (&) but got a type of With. The reason for this is that you didn't take the parsers by &mut which is necessary for them to be converted to a trait object.

expected &-ptr,
    found struct `combine::combinator::With`

So it should be:

    let esc_char = char('\\').with(choice::<&mut [&mut Parser<Input = &str, Output = char>; 6], _>(&mut [
        &mut char('\\').with(value('\\')),
        &mut char('"').with(value('"')),

(Since you are already specifying the length of the array you dont need to take that by &mut)

    let esc_char = char('\\').with(choice::<[&mut Parser<Input = &str, Output = char>; 6], _>([
        &mut char('\\').with(value('\\')),
        &mut char('"').with(value('"')),

@donhcd
Copy link
Author

donhcd commented Jun 17, 2016

Thanks a ton for your help! I did some reading on trait objects and this was the cleanest code I could come up with that compiled:

    let mut ch = [
        &mut char('\\') as &mut _,
        &mut char('"') as &mut _,
        &mut char('0').with(value('\0')) as &mut _,
        &mut char('n').with(value('\n')) as &mut _,
        &mut char('t').with(value('\t')) as &mut _,
        &mut char('r').with(value('\r')) as &mut _,
    ];
    let esc_char = char('\\').with(choice::<&mut [&mut Parser<Input = &str, Output = char>; 6], _>(&mut ch));

Is this what you had in mind, or is there anything left that I can clean up here?

@Marwes
Copy link
Owner

Marwes commented Jun 17, 2016

Glad to be of help!

You could remove the as &mut _ parts which aren't necessary as the &mut With<...> will be coerced into trait objects automatically. And you don't need to pass the array by &mut either if you are already specifying its length

   let mut ch = [
        &mut char('\\'),
        &mut char('"'),
        &mut char('0').with(value('\0')),
        &mut char('n').with(value('\n')),
        &mut char('t').with(value('\t')),
        &mut char('r').with(value('\r')),
    ];
    let esc_char = char('\\').with(choice::<[&mut Parser<Input = &str, Output = char>; 6], _>(ch));

Alternatively if you don't want to specify the length of the array you could just pass it as a slice (&mut [T])

   let mut ch = [
        &mut char('\\'),
        &mut char('"'),
        &mut char('0').with(value('\0')),
        &mut char('n').with(value('\n')),
        &mut char('t').with(value('\t')),
        &mut char('r').with(value('\r')),
    ];
    let esc_char = char('\\').with(choice::<&mut [&mut Parser<Input = &str, Output = char>], _>(&mut ch));

(Haven't compiled it but it should work)

@donhcd
Copy link
Author

donhcd commented Jun 20, 2016

okay, I managed to remove the casts with

let mut ch : &mut [&mut Parser<Input = &str, Output = char>] = &mut [
    &mut char('\\'),
    &mut char('0').with(value('\0')),
    &mut char('n').with(value('\n')),
    &mut char('t').with(value('\t')),
    &mut char('r').with(value('\r')),
];
let esc_char = char('\\').with(choice(&mut esc_choices))

I'm going to close this issue since it was never really an issue on the repo, but thanks again!

@donhcd donhcd closed this as completed Jun 20, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants