Nom parser definition - fold_many1 combined with map!


#1
fn is_digit(chr: char) -> bool {
  chr as u8 >= 0x30 && chr as u8 <= 0x39
}

fn gap(input: &str) -> IResult<&str,()> {
    let output : String = input.char_indices().skip_while(|p| is_space(p.1)).map(|p| p.1).collect();
    if output.len() > 0 {
        let offset = input.len() - output.len();
        return IResult::Done(&input[offset..], ());
    }
    return IResult::Incomplete(Needed::Unknown);
}

named!(duration(&str) -> time::Duration,
    do_parse!(
        call!(gap) >>
        numeric : map!(fold_many1!(take_while_s!(is_digit),
                        String::new(),
                        |mut acc: String, item| { acc.push_str(item); acc },
            |numeric_str| numeric_str.parse::<u64>().expect("Expected timeout numeric value"))) >>
        opt!(call!(gap)) >>
        d : switch!(take_while!(is_alphabetic),
            "s" => time::Duration::new(numeric, 0)
        ) >>
        ({
            d
            // let numeric : u64 = numeric_str.parse::<u64>().expect("Expected timeout numeric value");
            // time::Duration::new(numeric, 0)
        })
    )
);    )
);

fails with

error: no rules expected the token `i_`
   --> src/lib.rs:169:1
    |
169 | / named!(duration(&str) -> time::Duration,
170 | |     do_parse!(
171 | |         call!(gap) >>
172 | |         numeric : map!(fold_many1!(take_while_s!(is_digit),
...   |
185 | |     )
186 | | );
    | |__^
error: no rules expected the token `i_`
   --> src/lib.rs:169:1
    |
169 | / named!(duration(&str) -> time::Duration,
170 | |     do_parse!(
171 | |         call!(gap) >>
172 | |         numeric : map!(fold_many1!(take_while_s!(is_digit),
...   |
185 | |     )
186 | | );
    | |__^
    |
    |
    = note: this error originates in a macro outside of the current crate
    = note: this error originates in a macro outside of the current crate

Not sure what exaclty I am doing wrong.

Removing the map causes the macros to be happy, but then other things pop up. So i am really curious what exactly Iam doing wrong?


#2

Nom is so finicky!

This particular error seems to be caused by 1) needing to wrap the Duration::new call in a call! macro like call!(time::Duration::new, numeric, 0) and also 2) a mismatched parenthesis after the map closure when it should be after the fold_many! closure (I think.).


#3
named!(duration(&str) -> time::Duration,
    do_parse!(
        call!(gap) >>
        numeric : map!(
                    fold_many1!(
                        take_while_s!(is_digit),
                        String::new(),
                        |mut acc: String, item| { acc.push_str(item); acc }
                    ),
                    |numeric_str| { numeric_str.parse::<u64>().expect("Expected timeout numeric value") }
        ) >>
        call!(gap) >>
        d : switch!(take_while!(is_alphabetic),
            "s" => call!(time::Duration::new, numeric, 0) |
            "min" => call!(time::Duration::new, numeric*60, 0) |
        ) >>
        ({
            d
        })
    )
);

you were right!

Unfortunately that is still not enough, there is still this one:

error: no rules expected the token `i_`
   --> src/lib.rs:169:1
    |
169 | / named!(duration(&str) -> time::Duration,
170 | |     do_parse!(
171 | |         call!(gap) >>
172 | |         numeric : map!(fold_many1!(take_while_s!(is_digit),
...   |
187 | |     )
188 | | );
    | |__^
    |
    = note: this error originates in a macro outside of the current crate

#4

The working solution:

named!(duration(&str) -> time::Duration,
    do_parse!(
        call!(gap) >>
        numeric : map!(
                    fold_many1!(
                        take_while_s!(is_digit),
                        String::new(),
                        |mut acc: String, item| { acc.push_str(item); acc }
                    ),
                    |numeric_str| { numeric_str.parse::<u64>().expect("Expected timeout numeric value") }
        ) >>
        call!(gap) >>
        f : switch!(take_while!(is_alphabetic),
             "s" => value!(1u64) |
             "min" => value!(60u64)
            ) >>
        ({
            let f = 1;
            time::Duration::new(numeric * f, 0)
        })
    )
);

though I am still curious as to why the above version did not work as expected.