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
//! # About
//!
//! Welcome to the documentation for `math-calc`! This is a simple arithmatic
//! library that gets an inputted [String], [u8] slice or a plain [&str](str)
//! and outputs the finished calculation.
//!
//! This essentially means you can input multiple calculations seperated by a
//! `,` (similar to how JSON can be used) and get a [Vec]<[i32]> containing the
//! finished calculations.
//!
//! # Examples
//!
//! A basic parsing of a `&str` directly:
//!
//! ```rust
//! use math_calc::{ErrorKind, parse_str};
//!
//! fn main() {
//!     // Expected returns: 1st: 3, 2nd: 110, 3rd: 123
//!     let inputted_calculations = "1 + 2, 550 / 5, 8 * 16 + (-25 / 5)";
//!     
//!     // Generate results
//!     let output_vec: Vec<i32> = parse_str(inputted_calculations).unwrap();
//!
//!     // Output results of calculation
//!     println!("Calculation results (in vector):\n\n{:?}", output_vec);
//! }
//! ```

#[macro_use]
extern crate lalrpop_util;

use std::str;

lalrpop_mod!(grammar);

/// Primary error enum that is used on frontend functions to enable proper
/// error parsing downstream.
///
/// - [ErrorKind::ParseError]: The result when the parser has trouble
/// understanding the inputted string.
/// - [ErrorKind::InvalidInputEncoding]: When a given input is encoded poorly.
/// **Make sure `input` in [parse_str] is UTF-8!**
#[derive(Debug)]
pub enum ErrorKind<'a> {
    ParseError(lalrpop_util::ParseError<usize, grammar::Token<'a>, &'a str>),
    InvalidInputEncoding(std::str::Utf8Error),
}

/// Parses a given [u8] slice into the expected [Vec]<[i32]> result.
///
/// # Examples
///
/// Basic valid [u8] slice parsing:
///
/// ```rust
/// use math_calc::{ErrorKind, parse_u8_slice};
///
/// fn main() {
///     // Says "(90 + 5) / 5". Should result in `19`.
///     let my_slice = &[40, 53, 32, 43, 32, 57, 48, 41, 32, 47, 32, 53];
///
///     // Parse `my_slice` into the resulting i32 vector
///     let output_vec: Vec<i32> = parse_u8_slice(my_slice).unwrap();
///
///     // Outputs the final i32 vector
///     println!("Calculation results (in vector):\n\n{:?}", output_vec);
/// }
/// ```
///
/// Error handling for an invalid u8 formatting (needs to be valid `UTF-8`):
///
/// ```rust
/// use math_calc::{ErrorKind, parse_u8_slice};
///
/// fn main() {
///     // Invalid due to bad formatting (`255` is not a valid character).
///     let bad_formatting = &[255, 255, 255, 255, 255, 255, 255, 255, 255];
///
///     // A test assert to prove threoy
///     assert!(parse_u8_slice(bad_formatting).is_err())
/// }
/// ```
pub fn parse_u8_slice(input: &[u8]) -> Result<Vec<i32>, ErrorKind> {
    let input_str = match str::from_utf8(input) {
        Ok(x) => x,
        Err(e) => return Err(ErrorKind::InvalidInputEncoding(e)),
    };

    parse_str(input_str)
}

/// Parses a given [str] into the expected [Vec]<[i32]> result.
///
/// ***NOTE: This is the primary function that other `parse_x()` functions hook onto***.
///
/// # Examples
///
/// ```rust
/// use math_calc::{ErrorKind, parse_str};
///
/// fn main() {
///     let test_operation = "12 + 24, 5 + (4 / (2 + 2))"; // 1: 36, 2: 6
///
///     println!(
///         "Result of operation: {:?}",
///         parse_str(&test_operation).unwrap()
///     );
/// }
/// ```
pub fn parse_str(arithmatic: &str) -> Result<Vec<i32>, ErrorKind> {
    match grammar::GrammarParser::new().parse(arithmatic) {
        Ok(x) => Ok(x),
        Err(e) => Err(ErrorKind::ParseError(e)),
    }
}