Reducing boilerplate when wrapping LALRPOP parsers

⚓ Rust    📅 2025-07-10    👤 surdeus    👁️ 4      

surdeus

I'm trying to wrap parsers produced by LALRPOP which do not implement a parser trait but just provide a parse function.

The code below works fine but is there a way to reduce the amount of boilerplate?

For example, is there a way to avoid spelling out the types in the trait implementation here?

pub type DefsParserEx = ParserEx<grammar::DefsParser, Vec<ast::Definition>>;

impl Parser for DefsParserEx {
    type T = grammar::DefsParser;
    type U = Vec<ast::Definition>;
...

Also, is there a way to provide a blanket parse implementation rather than copy the same code for each trait implementation?

Thanks, Joel
pub struct ParserEx<T, U> {
    parser: T,
    marker: std::marker::PhantomData<U>,
}

pub trait Parser {
    type T;
    type U;

    fn new() -> Self;

    fn parse<'input>(
        &self,
        file_id: FileId,
        lexer: lexer::Lexer<'input>,
    ) -> Result<Self::U, error::LalrParseError<'input>>;
}

pub fn parse<'input, T>(
    parser: impl Parser<U = T>,
    s: &'input str,
) -> Result<T, error::LalrParseError<'input>> {
    let lexer = lexer::Lexer::new(s);
    parser.parse(FileId::empty(), lexer)
}

pub type DefsParserEx = ParserEx<grammar::DefsParser, Vec<ast::Definition>>;

impl Parser for DefsParserEx {
    type T = grammar::DefsParser;
    type U = Vec<ast::Definition>;

    fn new() -> Self {
        ParserEx {
            parser: Self::T::new(),
            marker: std::marker::PhantomData,
        }
    }

    fn parse<'input>(
        &self,
        file_id: FileId,
        lexer: lexer::Lexer<'input>,
    ) -> Result<Self::U, error::LalrParseError<'input>> {
        self.parser.parse(file_id, lexer)
    }
}

pub type CtrStartParserEx =
    ParserEx<grammar::ConstructorStartParser, (Loc<ast::Ident>, ast::Display, bool)>;

impl Parser for CtrStartParserEx {
    type T = grammar::ConstructorStartParser;
    type U = (Loc<ast::Ident>, ast::Display, bool);

    fn new() -> Self {
        ParserEx {
            parser: Self::T::new(),
            marker: std::marker::PhantomData,
        }
    }

    fn parse<'input>(
        &self,
        file_id: FileId,
        lexer: lexer::Lexer<'input>,
    ) -> Result<Self::U, error::LalrParseError<'input>> {
        self.parser.parse(file_id, lexer)
    }
}

1 post - 1 participant

Read full topic

🏷️ rust_feed