use crate::number;
use crate::{Error, Options};
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Node {
Null,
Bool(bool),
Number(String),
Str(String),
Array(Vec<Node>),
Object(Vec<(String, Node)>),
}
pub(crate) struct Parser<'a> {
bytes: &'a [u8],
at: usize,
opts: &'a Options,
}
impl<'a> Parser<'a> {
pub(crate) fn new(bytes: &'a [u8], opts: &'a Options) -> Self {
Self { bytes, at: 0, opts }
}
pub(crate) fn document(&mut self) -> Result<Node, Error> {
let node = self.value(0)?;
self.skip_ws();
if self.at != self.bytes.len() {
return Err(Error::TrailingContent { offset: self.at });
}
Ok(node)
}
fn skip_ws(&mut self) {
while let Some(&c) = self.bytes.get(self.at) {
if matches!(c, b' ' | b'\t' | b'\n' | b'\r') {
self.at += 1;
} else {
break;
}
}
}
fn peek(&self) -> Option<u8> {
self.bytes.get(self.at).copied()
}
fn syntax(&self, reason: &'static str) -> Error {
Error::Syntax {
offset: self.at,
reason,
}
}
fn not_scalar(&self, offset: usize, reason: &'static str) -> Error {
Error::StringNotScalar { offset, reason }
}
fn enter(&self, depth: usize) -> Result<(), Error> {
if depth >= self.opts.depth_limit() {
return Err(Error::TooDeep {
limit: self.opts.depth_limit(),
offset: self.at,
});
}
Ok(())
}
fn value(&mut self, depth: usize) -> Result<Node, Error> {
self.skip_ws();
let Some(c) = self.peek() else {
return Err(self.syntax("expected a JSON value, found end of input"));
};
match c {
b'{' => self.object(depth),
b'[' => self.array(depth),
b'"' => Ok(Node::Str(self.string()?)),
b't' => self.literal(b"true", Node::Bool(true)),
b'f' => self.literal(b"false", Node::Bool(false)),
b'n' => self.literal(b"null", Node::Null),
b'-' | b'0'..=b'9' => self.number(),
_ => Err(self.syntax("expected a JSON value")),
}
}
fn literal(&mut self, word: &[u8], node: Node) -> Result<Node, Error> {
if self.bytes[self.at..].starts_with(word) {
self.at += word.len();
Ok(node)
} else {
Err(self.syntax("expected one of null, true, false"))
}
}
fn array(&mut self, depth: usize) -> Result<Node, Error> {
self.enter(depth)?;
self.at += 1;
let mut items = Vec::new();
self.skip_ws();
if self.peek() == Some(b']') {
self.at += 1;
return Ok(Node::Array(items));
}
loop {
items.push(self.value(depth + 1)?);
self.skip_ws();
match self.peek() {
Some(b',') => self.at += 1,
Some(b']') => {
self.at += 1;
return Ok(Node::Array(items));
}
_ => return Err(self.syntax("expected ',' or ']' in array")),
}
}
}
fn object(&mut self, depth: usize) -> Result<Node, Error> {
self.enter(depth)?;
self.at += 1;
let mut members: Vec<(String, Node)> = Vec::new();
let mut seen: HashSet<String> = HashSet::new();
self.skip_ws();
if self.peek() == Some(b'}') {
self.at += 1;
return Ok(Node::Object(members));
}
loop {
self.skip_ws();
if self.peek() != Some(b'"') {
return Err(self.syntax("object member name must be a string"));
}
let name_at = self.at;
let name = self.string()?;
if !seen.insert(name.clone()) {
return Err(Error::DuplicateMember {
name,
offset: name_at,
});
}
self.skip_ws();
if self.peek() != Some(b':') {
return Err(self.syntax("expected ':' after an object member name"));
}
self.at += 1;
let value = self.value(depth + 1)?;
members.push((name, value));
self.skip_ws();
match self.peek() {
Some(b',') => self.at += 1,
Some(b'}') => {
self.at += 1;
return Ok(Node::Object(members));
}
_ => return Err(self.syntax("expected ',' or '}' in object")),
}
}
}
fn string(&mut self) -> Result<String, Error> {
let opened_at = self.at;
self.at += 1;
let mut out = String::new();
loop {
let run_start = self.at;
while let Some(c) = self.peek() {
if c == b'"' || c == b'\\' || c < 0x20 {
break;
}
self.at += 1;
}
if run_start != self.at {
let chunk = core::str::from_utf8(&self.bytes[run_start..self.at])
.map_err(|_| self.not_scalar(run_start, "invalid UTF-8"))?;
if self.opts.wants_noncharacter_check()
&& chunk.chars().any(|c| is_noncharacter(c as u32))
{
return Err(self.not_scalar(run_start, "Unicode noncharacter in string"));
}
out.push_str(chunk);
}
match self.peek() {
None => return Err(self.not_scalar(opened_at, "unterminated string literal")),
Some(b'"') => {
self.at += 1;
return Ok(out);
}
Some(c) if c < 0x20 => {
return Err(self.not_scalar(self.at, "raw control character in string"))
}
Some(_) => self.escape(&mut out)?,
}
}
}
fn escape(&mut self, out: &mut String) -> Result<(), Error> {
let start = self.at;
self.at += 1;
let Some(kind) = self.peek() else {
return Err(self.not_scalar(start, "unterminated escape sequence"));
};
self.at += 1;
let simple = match kind {
b'"' => Some('"'),
b'\\' => Some('\\'),
b'/' => Some('/'),
b'b' => Some('\u{8}'),
b'f' => Some('\u{c}'),
b'n' => Some('\u{a}'),
b'r' => Some('\u{d}'),
b't' => Some('\u{9}'),
b'u' => None,
_ => return Err(self.syntax("unknown escape sequence")),
};
if let Some(c) = simple {
out.push(c);
return Ok(());
}
let unit = self.hex4(start)?;
let code = match unit {
0xD800..=0xDBFF => {
if self.peek() != Some(b'\\') || self.bytes.get(self.at + 1) != Some(&b'u') {
return Err(self.not_scalar(start, "unpaired high surrogate escape"));
}
self.at += 2;
let low = self.hex4(start)?;
if !(0xDC00..=0xDFFF).contains(&low) {
return Err(self.not_scalar(
start,
"high surrogate escape not followed by a low surrogate escape",
));
}
0x1_0000 + ((unit - 0xD800) << 10) + (low - 0xDC00)
}
0xDC00..=0xDFFF => return Err(self.not_scalar(start, "unpaired low surrogate escape")),
other => other,
};
if self.opts.wants_noncharacter_check() && is_noncharacter(code) {
return Err(self.not_scalar(start, "Unicode noncharacter in string"));
}
let ch = char::from_u32(code)
.ok_or_else(|| self.not_scalar(start, "escape does not name a Unicode scalar value"))?;
out.push(ch);
Ok(())
}
fn hex4(&mut self, start: usize) -> Result<u32, Error> {
let digits = self
.bytes
.get(self.at..self.at + 4)
.ok_or_else(|| self.not_scalar(start, "truncated \\u escape"))?;
let mut value = 0_u32;
for &d in digits {
let nibble = match d {
b'0'..=b'9' => u32::from(d - b'0'),
b'a'..=b'f' => u32::from(d - b'a' + 10),
b'A'..=b'F' => u32::from(d - b'A' + 10),
_ => return Err(self.not_scalar(start, "malformed \\u escape")),
};
value = value << 4 | nibble;
}
self.at += 4;
Ok(value)
}
fn number(&mut self) -> Result<Node, Error> {
let start = self.at;
if self.peek() == Some(b'-') {
self.at += 1;
}
match self.peek() {
Some(b'0') => self.at += 1,
Some(b'1'..=b'9') => self.digits(),
_ => return Err(self.syntax("expected a digit in a number")),
}
if self.peek() == Some(b'.') {
self.at += 1;
if !matches!(self.peek(), Some(b'0'..=b'9')) {
return Err(self.syntax("expected a digit after the decimal point"));
}
self.digits();
}
if matches!(self.peek(), Some(b'e' | b'E')) {
self.at += 1;
if matches!(self.peek(), Some(b'+' | b'-')) {
self.at += 1;
}
if !matches!(self.peek(), Some(b'0'..=b'9')) {
return Err(self.syntax("expected a digit in the exponent"));
}
self.digits();
}
let token = core::str::from_utf8(&self.bytes[start..self.at])
.map_err(|_| self.syntax("malformed number token"))?
.to_owned();
number::check(&token, self.opts)?;
Ok(Node::Number(token))
}
fn digits(&mut self) {
while matches!(self.peek(), Some(b'0'..=b'9')) {
self.at += 1;
}
}
}
pub(crate) fn is_noncharacter(code: u32) -> bool {
code & 0xFFFE == 0xFFFE || (0xFDD0..=0xFDEF).contains(&code)
}