squiid_parser/
lib.rs

1//! Squiid Parser is the algebraic expression parser for [Squiid Calculator](https://gitlab.com/ImaginaryInfinity/squiid-calculator/squiid), designed
2//! to parse infix notation into postifix (RPN) notation, which can then be evaluated by [squiid-engine](https://crates.io/crates/squiid-engine).
3//!
4//! This module provides functionality for lexing, parsing, and handling algebraic expressions,
5//! supporting implicit multiplication and correct operator precedence using the Shunting Yard algorithm.
6//!
7//! ## Modules
8//!
9//! - [`error`]: Defines error types encountered during parsing.
10//! - [`lexer`]: Handles lexical analysis, converting input strings into tokens.
11//! - [`parser`]: Implements parsing logic, including handling implicit multiplication and operator precedence.
12//! - [`tokens`]: Defines token structures used throughout the parsing process.
13//!
14//! ## Functionality
15//!
16//! The primary function exposed is [`parse`], which converts an infix algebraic string
17//! into a vector of tokens in RPN format. It ensures proper handling of operators,
18//! parentheses, and implicit multiplication.
19//!
20//! ## Features
21//!
22//! - **FFI Support** (optional): Enables C-compatible parsing via the `ffi` module.
23//! - **Strict Safety Guidelines**: Uses `#![deny(clippy::unwrap_used)]` and related lints
24//!   to enforce error handling best practices.
25//!
26//! ## Usage
27//!
28//! ```
29//! use squiid_parser::parse;
30//! use squiid_parser::error::ParserError;
31//!
32//! fn main() -> Result<(), ParserError> {
33//!     let expected = vec!["3", "6", "4", "6", "*", "+", "*", "5", "/"];
34//!     let input = "3(6+4*6)/5";
35//!     assert_eq!(expected, parse(input)?);
36//!
37//!     Ok(())
38//! }
39//! ```
40//!
41//! ## Error Handling
42//!
43//! If parsing fails, an appropriate [`ParserError`] is returned, such as
44//! `MismatchedParenthesis` when parentheses are unbalanced.
45
46#![deny(clippy::unwrap_used)]
47#![deny(clippy::expect_used)]
48#![deny(clippy::panic)]
49#![deny(clippy::missing_panics_doc)]
50
51pub mod error;
52pub mod lexer;
53pub mod parser;
54pub mod tokens;
55
56#[cfg(feature = "ffi")]
57mod ffi;
58
59use crate::lexer::lex;
60use error::ParserError;
61use parser::{parse_implicit_multiplication, parse_subtract_sign, shunting_yard_parser};
62
63/// Parse an algebraic string into a vec of tokens in RPN format.
64///
65/// # Arguments
66///
67/// * `input` - The string to parse
68///
69/// # Errors
70///
71/// If any errors occur while parsing, a [`ParserError`] will be returned
72///
73/// # Examples
74///
75/// ```
76/// use squiid_parser::parse;
77/// use squiid_parser::error::ParserError;
78///
79/// fn main() -> Result<(), ParserError> {
80///     let expected = vec!["3", "6", "4", "6", "*", "+", "*", "5", "/"];
81///     let input = "3(6+4*6)/5";
82///     assert_eq!(expected, parse(input)?);
83///
84///     Ok(())
85/// }
86/// ```
87pub fn parse(input: &str) -> Result<Vec<&str>, ParserError> {
88    // check for unmatched parenthesis
89    if input.matches('(').count() != input.matches(')').count() {
90        return Err(ParserError::MismatchedParenthesis);
91    }
92
93    let mut tokens = lex(input)?;
94    parse_subtract_sign(&mut tokens);
95    parse_implicit_multiplication(&mut tokens);
96    shunting_yard_parser(tokens)
97}