use std::io; use std::str; use super::error::{Error, ErrorCode, Result}; pub struct StringReader> { iter: Iter, line: usize, col: usize, ch: Vec, } impl StringReader where Iter: Iterator, { #[inline] pub fn new(iter: Iter) -> Self { StringReader { iter, line: 1, col: 0, ch: Vec::new(), } } fn next(&mut self) -> Option> { match self.iter.next() { None => None, Some(b'\n') => { self.line += 1; self.col = 0; Some(Ok(b'\n')) } Some(c) => { self.col += 1; Some(Ok(c)) } } } pub fn pos(&mut self) -> (usize, usize) { (self.line, self.col) } pub fn eof(&mut self) -> Result { Ok(self.peek()?.is_none()) } pub fn peek_next(&mut self, idx: usize) -> Result> { while self.ch.len() <= idx { match self.next() { Some(Err(err)) => return Err(Error::Io(err)), Some(Ok(ch)) => self.ch.push(ch), None => return Ok(None), } } Ok(Some(self.ch[idx])) } // pub fn peek_next_or_null(&mut self, idx: usize) -> Result { // Ok(try!(self.peek_next(idx)).unwrap_or(b'\x00')) // } pub fn peek(&mut self) -> Result> { self.peek_next(0) } pub fn peek_or_null(&mut self) -> Result { Ok(self.peek()?.unwrap_or(b'\x00')) } pub fn eat_char(&mut self) -> u8 { self.ch.remove(0) } pub fn uneat_char(&mut self, ch: u8) { self.ch.insert(0, ch); } pub fn next_char(&mut self) -> Result> { match self.ch.first() { Some(&ch) => { self.eat_char(); Ok(Some(ch)) } None => match self.next() { Some(Err(err)) => Err(Error::Io(err)), Some(Ok(ch)) => Ok(Some(ch)), None => Ok(None), }, } } pub fn next_char_or_null(&mut self) -> Result { Ok(self.next_char()?.unwrap_or(b'\x00')) } fn eat_line(&mut self) -> Result<()> { loop { match self.peek()? { Some(b'\n') | None => return Ok(()), _ => {} } self.eat_char(); } } pub fn parse_whitespace(&mut self) -> Result<()> { loop { match self.peek_or_null()? { b' ' | b'\n' | b'\t' | b'\r' => { self.eat_char(); } b'#' => self.eat_line()?, b'/' => { match self.peek_next(1)? { Some(b'/') => self.eat_line()?, Some(b'*') => { self.eat_char(); self.eat_char(); while !(self.peek()?.unwrap_or(b'*') == b'*' && self.peek_next(1)?.unwrap_or(b'/') == b'/') { self.eat_char(); } self.eat_char(); self.eat_char(); } Some(_) => { self.eat_char(); } None => return Err(self.error(ErrorCode::TrailingCharacters)), //todo } } _ => { return Ok(()); } } } } pub fn error(&mut self, reason: ErrorCode) -> Error { Error::Syntax(reason, self.line, self.col) } } pub enum Number { I64(i64), U64(u64), F64(f64), } pub struct ParseNumber> { rdr: StringReader, result: Vec, } // macro_rules! try_or_invalid { // ($e:expr) => { // match $e { // Some(v) => v, // None => { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } // } // } // } impl> ParseNumber { #[inline] pub fn new(iter: Iter) -> Self { ParseNumber { rdr: StringReader::new(iter), result: Vec::new(), } } pub fn parse(&mut self, stop_at_next: bool) -> Result { match self.try_parse() { Ok(()) => { self.rdr.parse_whitespace()?; let mut ch = self.rdr.next_char_or_null()?; if stop_at_next { let ch2 = self.rdr.peek_or_null()?; // end scan if we find a punctuator character like ,}] or a comment if ch == b',' || ch == b'}' || ch == b']' || ch == b'#' || ch == b'/' && (ch2 == b'/' || ch2 == b'*') { ch = b'\x00'; } } match ch { b'\x00' => { let res = str::from_utf8(&self.result).expect("Internal error: json parsing"); let mut is_float = false; for ch in res.chars() { if ch == '.' || ch == 'e' || ch == 'E' { is_float = true; break; } } if is_float { Ok(Number::F64( res.parse::().expect("Internal error: json parsing"), )) } else if res.starts_with('-') { Ok(Number::I64( res.parse::().expect("Internal error: json parsing"), )) } else { Ok(Number::U64( res.parse::().expect("Internal error: json parsing"), )) } } _ => Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)), } } Err(e) => Err(e), } } fn try_parse(&mut self) -> Result<()> { if self.rdr.peek_or_null()? == b'-' { self.result.push(self.rdr.eat_char()); } let mut has_value = false; if self.rdr.peek_or_null()? == b'0' { self.result.push(self.rdr.eat_char()); has_value = true; // There can be only one leading '0'. if let b'0'..=b'9' = self.rdr.peek_or_null()? { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } } loop { match self.rdr.peek_or_null()? { b'0'..=b'9' => { self.result.push(self.rdr.eat_char()); has_value = true; } b'.' => { if !has_value { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } self.rdr.eat_char(); return self.try_decimal(); } b'e' | b'E' => { if !has_value { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } self.rdr.eat_char(); return self.try_exponent(); } _ => { if !has_value { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } return Ok(()); } } } } fn try_decimal(&mut self) -> Result<()> { self.result.push(b'.'); // Make sure a digit follows the decimal place. match self.rdr.next_char_or_null()? { c @ b'0'..=b'9' => { self.result.push(c); } _ => { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } }; while let b'0'..=b'9' = self.rdr.peek_or_null()? { self.result.push(self.rdr.eat_char()); } match self.rdr.peek_or_null()? { b'e' | b'E' => { self.rdr.eat_char(); self.try_exponent() } _ => Ok(()), } } fn try_exponent(&mut self) -> Result<()> { self.result.push(b'e'); match self.rdr.peek_or_null()? { b'+' => { self.result.push(self.rdr.eat_char()); } b'-' => { self.result.push(self.rdr.eat_char()); } _ => {} }; // Make sure a digit follows the exponent place. match self.rdr.next_char_or_null()? { c @ b'0'..=b'9' => { self.result.push(c); } _ => { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); } }; while let b'0'..=b'9' = self.rdr.peek_or_null()? { self.result.push(self.rdr.eat_char()); } Ok(()) } }