parsey/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
//! # Parsey
//!
//! `parsey` is a lightweight framework for creating custom parsers and abstract syntax trees (ASTs).
//! It provides two key traits: [`Parser`] and [`Ast`], which together form the foundation
//! for building parsers and representing the structure of parsed data.
//!
//! ## Key Features
//! - **Generic Parsing Framework:** Abstracts the process of parsing tokens into structured data.
//! - **Customizable AST Nodes:** Easily define nodes of your AST by implementing the [`Ast`] trait.
//!
//! ## Getting Started
//!
//! Let's implement a simple parser that parses a stream of zero and one tokens into groups of two
//! bits!
//!
//! ### Step 1: Implement the `Parser` Trait
//! Define a struct that will serve as your parser. This struct must implement the [`Parser`] trait,
//! which iterates over tokens and produces an AST.
//!
//! ```rust,ignore
//! use parsey::{Ast, Parser, TokenStream};
//!
//! #[derive(Debug, PartialEq)]
//! pub enum MyToken {
//! Zero,
//! One,
//! }
//!
//! #[derive(Debug, PartialEq)]
//! pub struct MyError;
//!
//! pub struct MyParser {
//! tokens: Vec<MyToken>,
//! }
//!
//! impl Parser<MyToken, MyError> for MyParser {
//! type Root = Root;
//!
//! fn expect(
//! token_stream: &mut TokenStream<Self, MyToken, MyError>,
//! token: MyToken,
//! ) -> Result<(), MyError> {
//! if token_stream.peek() == Some(&token) {
//! token_stream.next();
//! Ok(())
//! } else {
//! Err(MyError)
//! }
//! }
//! }
//!
//! impl Iterator for MyParser {
//! type Item = MyToken;
//!
//! fn next(&mut self) -> Option<Self::Item> {
//! self.tokens.pop()
//! }
//! }
//!
//! impl From<Vec<MyToken>> for MyParser {
//! fn from(mut value: Vec<MyToken>) -> Self {
//! value.reverse();
//! Self { tokens: value }
//! }
//! }
//! ```
//!
//! ### Step 2: Define the AST Nodes
//! Create the structure for your AST by implementing the [`Ast`] trait for each node.
//! The root node must match the type defined in `Parser::Root`.
//!
//! ```rust,ignore
//! #[derive(Debug, PartialEq)]
//! pub struct Root(Vec<TwoBit>);
//!
//! #[derive(Debug, PartialEq)]
//! pub enum TwoBit {
//! ZeroZero,
//! ZeroOne,
//! OneZero,
//! OneOne,
//! }
//!
//! impl Ast<MyToken, MyError> for Root {
//! fn parse<P>(token_stream: &mut TokenStream<P, MyToken, MyError>) -> Result<Self, MyError>
//! where
//! P: Parser<MyToken, MyError>,
//! {
//! let mut two_bits = vec![];
//! while !token_stream.is_empty() {
//! two_bits.push(TwoBit::parse(token_stream)?);
//! }
//! Ok(Self(two_bits))
//! }
//! }
//!
//! impl Ast<MyToken, MyError> for TwoBit {
//! fn parse<P>(token_stream: &mut TokenStream<P, MyToken, MyError>) -> Result<Self, MyError>
//! where
//! P: parsey::Parser<MyToken, MyError>,
//! {
//! use MyToken::*;
//! use TwoBit::*;
//!
//! match next_n!(token_stream, 2, MyError) {
//! [Zero, Zero] => Ok(ZeroZero),
//! [Zero, One] => Ok(ZeroOne),
//! [One, Zero] => Ok(OneZero),
//! [One, One] => Ok(OneOne),
//! }
//! }
//! }
//! ```
//!
//! ### Step 3: Parse Tokens
//!
//! Use your parser to parse a sequence of tokens into an AST.
//!
//! ```rust,ignore
//! use MyToken::{One, Zero};
//! use TwoBit::{OneOne, OneZero, ZeroOne, ZeroZero};
//!
//! let tokens = vec![Zero, Zero, Zero, One, One, Zero, One, One];
//! let ast = parse::<MyParser, MyToken, MyError>(tokens);
//! assert_eq!(ast, Ok(Root(vec![ZeroZero, ZeroOne, OneZero, OneOne])));
//! ```
pub use ast::Ast;
pub use parser::Parser;
pub use token_stream::TokenStream;
mod ast;
mod parser;
mod token_stream;
/// Parse a vec of tokens into the provided root AST node.
///
/// # Type Parameters
/// - `P`: The parser used to parse the tokens.
/// - `Token`: The type of token being parsed.
/// - `Error`: The error type that can be returned from parsing.
pub fn parse<P, Token, Error>(tokens: Vec<Token>) -> Result<P::Root, Error>
where
P: Parser<Token, Error>,
{
P::from(tokens).parse()
}
/// Get the next `n` tokens from `token_stream`.
#[macro_export]
macro_rules! next_n {
($token_stream:expr, $n:expr) => {{
let tokens: [_; $n] = $token_stream.next_n($n).try_into().unwrap();
tokens
}};
}
/// Peek at the next `n` tokens from `token_stream` without consuming them.
#[macro_export]
macro_rules! peek_n {
($token_stream:expr, $n:expr) => {{
let tokens: [_; $n] = $token_stream.peek_n($n).try_into().unwrap();
tokens
}};
}
/// Get the next `n` tokens from `token_stream` or return the provided
/// error if the token stream ends before the required amount of tokens
/// are consumed.
#[macro_export]
macro_rules! require_next_n {
($token_stream:expr, $n:expr, $error:expr) => {
match $token_stream.require_next_n($n) {
Some(tokens) => {
// Unwrapping here is safe
let tokens: [_; $n] = tokens.try_into().unwrap();
tokens
}
None => return Err($error),
};
};
}
/// Peek at the next `n` tokens from `token_stream` or return the provided
/// error if the token stream ends before the required amount of tokens
/// are peeked.
#[macro_export]
macro_rules! require_peek_n {
($token_stream:expr, $n:expr, $error:expr) => {
match $token_stream.require_peek_n($n) {
Some(tokens) => {
// Unwrapping here is safe
let tokens: [_; $n] = tokens.try_into().unwrap();
tokens
}
None => return Err($error),
};
};
}