json_syntax/parse/
number.rs

1use super::{Context, Error, Parse, Parser};
2use crate::{NumberBuf, SMALL_STRING_CAPACITY};
3use decoded_char::DecodedChar;
4use locspan::Meta;
5use smallvec::SmallVec;
6
7impl Parse for NumberBuf {
8	fn parse_in<C, E>(
9		parser: &mut Parser<C, E>,
10		context: Context,
11	) -> Result<Meta<Self, usize>, Error<E>>
12	where
13		C: Iterator<Item = Result<DecodedChar, E>>,
14	{
15		let i = parser.begin_fragment();
16		let mut buffer: SmallVec<[u8; SMALL_STRING_CAPACITY]> = SmallVec::new();
17
18		enum State {
19			Init,
20			FirstDigit,
21			Zero,
22			NonZero,
23			FractionalFirst,
24			FractionalRest,
25			ExponentSign,
26			ExponentFirst,
27			ExponentRest,
28		}
29
30		let mut state = State::Init;
31
32		while let Some(c) = parser.peek_char()? {
33			match state {
34				State::Init => match c {
35					'-' => state = State::FirstDigit,
36					'0' => state = State::Zero,
37					'1'..='9' => state = State::NonZero,
38					_ => return Err(Error::unexpected(parser.position, Some(c))),
39				},
40				State::FirstDigit => match c {
41					'0' => state = State::Zero,
42					'1'..='9' => state = State::NonZero,
43					_ => return Err(Error::unexpected(parser.position, Some(c))),
44				},
45				State::Zero => match c {
46					'.' => state = State::FractionalFirst,
47					'e' | 'E' => state = State::ExponentSign,
48					_ => {
49						if context.follows(c) {
50							break;
51						} else {
52							return Err(Error::unexpected(parser.position, Some(c)));
53						}
54					}
55				},
56				State::NonZero => match c {
57					'0'..='9' => state = State::NonZero,
58					'.' => state = State::FractionalFirst,
59					'e' | 'E' => state = State::ExponentSign,
60					_ => {
61						if context.follows(c) {
62							break;
63						} else {
64							return Err(Error::unexpected(parser.position, Some(c)));
65						}
66					}
67				},
68				State::FractionalFirst => match c {
69					'0'..='9' => state = State::FractionalRest,
70					_ => return Err(Error::unexpected(parser.position, Some(c))),
71				},
72				State::FractionalRest => match c {
73					'0'..='9' => state = State::FractionalRest,
74					'e' | 'E' => state = State::ExponentSign,
75					_ => {
76						if context.follows(c) {
77							break;
78						} else {
79							return Err(Error::unexpected(parser.position, Some(c)));
80						}
81					}
82				},
83				State::ExponentSign => match c {
84					'+' | '-' => state = State::ExponentFirst,
85					'0'..='9' => state = State::ExponentRest,
86					_ => return Err(Error::unexpected(parser.position, Some(c))),
87				},
88				State::ExponentFirst => match c {
89					'0'..='9' => state = State::ExponentRest,
90					_ => return Err(Error::unexpected(parser.position, Some(c))),
91				},
92				State::ExponentRest => match c {
93					'0'..='9' => state = State::ExponentRest,
94					_ => {
95						if context.follows(c) {
96							break;
97						} else {
98							return Err(Error::unexpected(parser.position, Some(c)));
99						}
100					}
101				},
102			}
103
104			// u8 conversion is safe since a number is composed of ASCII chars.
105			buffer.push(c as u8);
106			parser.next_char()?;
107		}
108
109		if matches!(
110			state,
111			State::Zero | State::NonZero | State::FractionalRest | State::ExponentRest
112		) {
113			parser.end_fragment(i);
114			Ok(Meta(unsafe { NumberBuf::new_unchecked(buffer) }, i))
115		} else {
116			Err(Error::unexpected(parser.position, None))
117		}
118	}
119}