parser-state 0.1.0

A small Rust library for building hand-rolled parsers
Documentation
use anyhow::anyhow;
use std::cmp::PartialEq;
use std::fmt::Display;

pub trait Token {
    type Tag: Display + PartialEq;
    fn tag(&self) -> Self::Tag;
    fn lexeme(&self) -> &str;
}

pub trait Lexer {
    type Token: Token;
    fn next_token(&mut self) -> anyhow::Result<Self::Token>;
}

pub struct ParserState<L: Lexer> {
    lexer: L,
    pub current: L::Token,
}

impl<L: Lexer> ParserState<L> {
    pub fn new(mut lexer: L) -> anyhow::Result<Self> {
        let current = lexer.next_token()?;
        Ok(Self { lexer, current })
    }

    pub fn advance(&mut self) -> anyhow::Result<()> {
        self.current = self.lexer.next_token()?;
        Ok(())
    }

    pub fn advance_keep_current(&mut self) -> anyhow::Result<L::Token> {
        let kept = std::mem::replace(&mut self.current, self.lexer.next_token()?);
        Ok(kept)
    }

    pub fn matches(&mut self, expected_tag: <L::Token as Token>::Tag) -> anyhow::Result<bool> {
        let is_match = self.current.tag() == expected_tag;
        if is_match {
            self.advance()?;
        }
        Ok(is_match)
    }

    pub fn expect(&mut self, expected_tag: <L::Token as Token>::Tag) -> anyhow::Result<()> {
        if self.current.tag() == expected_tag {
            self.advance()?;
            Ok(())
        } else {
            Err(anyhow!(
                "expected `{}` but got `{}`",
                expected_tag,
                self.current.lexeme()
            ))
        }
    }

    pub fn expect_lexeme(&mut self, tag: <L::Token as Token>::Tag) -> anyhow::Result<String> {
        if self.current.tag() == tag {
            let token = self.advance_keep_current()?;
            Ok(token.lexeme().to_string())
        } else {
            Err(anyhow!(
                "expected `{}` but found `{}`",
                tag,
                self.current.lexeme()
            ))
        }
    }
}