use crate::{
text::{
ParsedExponent,
ParsedFinite,
ParsedSignificand,
StrTextBuf,
TextWriter,
},
ParseError,
};
use core::fmt::{
self,
Write,
};
#[derive(Debug)]
pub struct FiniteParser<B> {
buf: B,
error: Option<ParseError>,
significand: ParsedSignificand,
exponent: Option<ParsedExponent>,
has_sign: bool,
has_decimal: bool,
has_digits: bool,
}
impl<'a> FiniteParser<StrTextBuf<'a>> {
pub fn parse_str(input: &'a str) -> Result<ParsedFinite<StrTextBuf<'a>>, ParseError> {
let mut parser = FiniteParser::begin(StrTextBuf::new(input));
parser.parse_ascii(input.as_bytes())?;
parser.end()
}
}
impl<B: TextWriter> FiniteParser<B> {
pub fn begin(mut buf: B) -> Self {
FiniteParser {
significand: buf.begin_significand(),
exponent: None,
buf,
error: None,
has_sign: false,
has_decimal: false,
has_digits: false,
}
}
pub fn checked_push_significand_digit(&mut self, digit: u8) -> Result<(), ParseError> {
if self.buf.remaining_capacity() == Some(0) {
Err(ParseError::buffer_too_small())
} else {
self.push_significand_digit(digit);
Ok(())
}
}
pub(in crate::text) fn push_significand_digit(&mut self, digit: u8) {
self.has_digits = true;
self.buf
.push_significand_digit(&mut self.significand, digit)
}
pub fn checked_significand_is_negative(&mut self) -> Result<(), ParseError> {
if self.buf.remaining_capacity() == Some(0) {
Err(ParseError::buffer_too_small())
} else {
self.significand_is_negative();
Ok(())
}
}
pub(in crate::text) fn significand_is_negative(&mut self) {
self.has_sign = true;
self.buf.significand_is_negative(&mut self.significand)
}
pub(in crate::text) fn significand_is_positive(&mut self) {
self.has_sign = true;
self.buf.significand_is_positive(&mut self.significand)
}
pub(in crate::text) fn push_significand_decimal_point(&mut self) {
self.has_decimal = true;
self.has_digits = false;
self.buf
.push_significand_decimal_point(&mut self.significand)
}
pub fn checked_begin_exponent(&mut self) -> Result<(), ParseError> {
if self.buf.remaining_capacity() == Some(0) {
Err(ParseError::buffer_too_small())
} else {
self.begin_exponent();
Ok(())
}
}
pub(in crate::text) fn begin_exponent(&mut self) {
self.has_sign = false;
self.has_digits = false;
self.exponent = Some(self.buf.begin_exponent());
}
pub fn parse(buf: B, input: impl fmt::Display) -> Result<ParsedFinite<B>, ParseError> {
let mut parser = FiniteParser::begin(buf);
match write!(&mut parser, "{}", input) {
Ok(()) => parser.end(),
Err(err) => Err(parser.unwrap_context(err)),
}
}
pub fn parse_fmt(&mut self, f: impl fmt::Display) -> Result<(), ParseError> {
write!(self, "{}", f).map_err(|err| self.unwrap_context(err))
}
pub fn parse_ascii(&mut self, mut ascii: &[u8]) -> Result<(), ParseError> {
if let Some(remaining_capacity) = self.buf.remaining_capacity() {
if remaining_capacity < ascii.len() {
return Err(ParseError::buffer_too_small());
}
}
if self.exponent.is_none() {
while !ascii.is_empty() {
match ascii[0] {
b'0'..=b'9' => {
self.push_significand_digit(ascii[0]);
}
b'-' if !self.has_sign && !self.has_digits => {
self.significand_is_negative();
}
b'.' if !self.has_decimal => {
self.push_significand_decimal_point();
}
b'e' | b'E' if self.has_digits => {
self.begin_exponent();
ascii = &ascii[1..];
break;
}
b'+' if !self.has_sign && !self.has_digits => {
self.significand_is_positive();
}
c => return Err(ParseError::unexpected_char(c, "any digit")),
}
ascii = &ascii[1..];
}
}
if let Some(ref mut exponent) = self.exponent {
while !ascii.is_empty() {
match ascii[0] {
b'0'..=b'9' => {
self.has_digits = true;
self.buf.push_exponent_digit(exponent, ascii[0]);
}
b'-' if !self.has_sign && !self.has_digits => {
self.has_sign = true;
self.buf.exponent_is_negative(exponent);
}
b'+' if !self.has_sign && !self.has_digits => {
self.has_sign = true;
self.buf.exponent_is_positive(exponent);
}
c => return Err(ParseError::unexpected_char(c, "any digit")),
}
ascii = &ascii[1..];
}
}
Ok(())
}
pub fn end(self) -> Result<ParsedFinite<B>, ParseError> {
debug_assert!(
self.error.is_none(),
"attempt to complete a parser with an error context"
);
if !self.has_digits {
return Err(ParseError::unexpected_end(if !self.has_sign {
"a sign or digit"
} else {
"any digit"
}));
}
Ok(ParsedFinite {
finite_buf: self.buf,
finite_significand: self.significand,
finite_exponent: self.exponent,
})
}
pub fn context(&mut self, err: ParseError) -> fmt::Error {
self.error = Some(err);
fmt::Error
}
pub fn unwrap_context(&mut self, _: fmt::Error) -> ParseError {
self.error.take().unwrap_or_else(ParseError::source)
}
}
impl<B: TextWriter> Write for FiniteParser<B> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.parse_ascii(s.as_bytes())
.map_err(|err| self.context(err))
}
}