rust – Parsing “my lhs = my rhs”

Just below is a nom parser which can parse one-line expressions like my lhs = my rhs:

Cargo.toml

(package)
name = "basic-test"
version = "0.0.0"
edition = "2018"

(dependencies)
nom = "6.1.2"

src/main.rs

/// Nom shortcuts.
mod nom_prelude {
    pub use nom::{
        IResult,
        combinator::{ map, recognize },
        multi::{ many0, many1 },
        sequence::{ terminated, separated_pair },
    };

    pub mod complete {
        pub use nom::{
            bytes::complete::tag,
            character::complete::{ anychar, none_of, space0 },
        };
    }
}

/// Parsing stuff.
mod parse {
    use super::nom_prelude::{*, complete::*};

    // TYPES/TRAITS DEFINITIONS AROUND NOM (treating inputs as &str):

    /// The nom input type.
    type ParseInput<'a> = &'a str;

    /// The nom result type, considering the defined input type.
    type ParseResult<'a, R> = IResult<ParseInput<'a>, R>;

    /// The type of a nom parser, considering the defined input type.
    trait Parser<'a, R>: FnMut(ParseInput<'a>) -> ParseResult<'a, R> {}
    impl<'a, R, F> Parser<'a, R> for F
        where F: FnMut(ParseInput<'a>) -> ParseResult<'a, R>
    {}

    // PARSE FUNCTIONS (HOW TO IMPROVE THE FOLLOWING CODE?):

    /// Parses an equal statement, like "FOO = BAR".
    pub fn equal_statement<'a>(input: ParseInput<'a>) -> ParseResult<(&'a str, &'a str)> {
        separated_pair(
            equal_statement_lhs,
            terminated(tag("="), space0),
            equal_statement_rhs,
        )
        (input)
    }

    /// Parses the left-hand side of an equal statement.
    fn equal_statement_lhs<'a>(input: ParseInput<'a>) -> ParseResult<&'a str> {
        map(
            recognize(
                many1(none_of("="))
            ),
            |s: &'a str| s.trim()
        )
        (input)
    }

    /// Parses the right-hand side of an equal statement (the remaining characters of the input).
    fn equal_statement_rhs<'a>(input: ParseInput<'a>) -> ParseResult<&'a str> {
        recognize(many0(anychar))
        (input)
    }
}

fn main() {
    let input = "my shortcut  =   /some/path";
    let parsed = parse::equal_statement(input);
    println!("{:?}", parsed);
}

Output

Ok(("", ("my shortcut", "/some/path")))

With the given code, the parsing is working like this:

  1. Take all characters until you meet an equal sign “=”;
  2. Right trim the taken chars and store the result as the LHS;
  3. Skip the equal sign “=” and the following spaces (pattern =(SPACES*));
  4. Take all the remaining characters;
  5. Store the taken chars as the RHS.

I want the code to be simplified if possible, and to behave like this instead:

  1. Take all characters until you meet the pattern (SPACES*)=(SPACES*);
  2. Store the taken chars as the LHS;
  3. Skip the already parsed equal sign pattern (SPACES*)=(SPACES*);
  4. Take all the remaining characters;
  5. Store the taken chars as the RHS.

How can I improve/simplify the code ?

  • especially the part commented with // PARSE FUNCTIONS (HOW TO IMPROVE THE FOLLOWING CODE?);
  • I don’t like the use of trim(), which looks like a doubloon (parsing something which has already been parsed).