use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec::Vec;
use crate::error::{Error, ErrorKind};
use crate::lexer::{Lexer, Spanned, Token};
use crate::options::{Allow, Options, Repairs};
use crate::value::{Number, Value, write_escaped};
pub const MAX_NESTING_DEPTH: usize = 256;
const FRAME_PREALLOC: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tag {
OpenBrace,
CloseBrace,
OpenBracket,
CloseBracket,
Colon,
Comma,
Semicolon,
Plus,
Ellipsis,
OpenParen,
CloseParen,
Str { truncated: bool },
Num { truncated: bool },
Bool,
Null,
Undefined,
Word { truncated: bool },
}
impl Tag {
fn of(token: &Token<'_>) -> Self {
match token {
Token::OpenBrace => Tag::OpenBrace,
Token::CloseBrace => Tag::CloseBrace,
Token::OpenBracket => Tag::OpenBracket,
Token::CloseBracket => Tag::CloseBracket,
Token::Colon => Tag::Colon,
Token::Comma => Tag::Comma,
Token::Semicolon => Tag::Semicolon,
Token::Plus => Tag::Plus,
Token::Ellipsis => Tag::Ellipsis,
Token::OpenParen => Tag::OpenParen,
Token::CloseParen => Tag::CloseParen,
Token::Str { truncated, .. } => Tag::Str {
truncated: *truncated,
},
Token::Num { truncated, .. } => Tag::Num {
truncated: *truncated,
},
Token::Bool(_) => Tag::Bool,
Token::Null => Tag::Null,
Token::Undefined => Tag::Undefined,
Token::Word { truncated, .. } => Tag::Word {
truncated: *truncated,
},
}
}
fn starts_value(self) -> bool {
matches!(
self,
Tag::OpenBrace
| Tag::OpenBracket
| Tag::Str { .. }
| Tag::Num { .. }
| Tag::Bool
| Tag::Null
| Tag::Undefined
| Tag::Word { .. }
| Tag::OpenParen
)
}
fn punct_char(self) -> char {
match self {
Tag::OpenBrace => '{',
Tag::CloseBrace => '}',
Tag::OpenBracket => '[',
Tag::CloseBracket => ']',
Tag::Colon => ':',
Tag::Comma => ',',
Tag::Semicolon => ';',
Tag::Plus => '+',
Tag::Ellipsis => '.',
Tag::OpenParen => '(',
Tag::CloseParen => ')',
_ => '\0',
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ContainerKind {
Object,
Array,
Group,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Frame {
kind: ContainerKind,
first: bool,
comma_pending: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct TopState {
first_pos: usize,
prose_first: bool,
count: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CpPhase {
InContainer,
ContinueParent,
TopContinue,
}
#[derive(Debug, Clone)]
pub(crate) struct ResumeCp {
pub(crate) valid: bool,
pub(crate) input_pos: usize,
pub(crate) output_len: usize,
pub(crate) phase: CpPhase,
pub(crate) frames: Vec<Frame>,
pub(crate) top: TopState,
}
impl ResumeCp {
pub(crate) fn new() -> Self {
Self {
valid: false,
input_pos: 0,
output_len: 0,
phase: CpPhase::TopContinue,
frames: Vec::new(),
top: TopState::default(),
}
}
}
pub(crate) struct Parser<'a, 'b> {
lexer: Lexer<'a>,
peeked: Option<(Spanned<'a>, Tag)>,
opts: Options,
frames: Vec<Frame>,
max_depth: usize,
top: TopState,
out: Option<&'b mut String>,
cp_slot: Option<&'b mut ResumeCp>,
resume_phase: Option<CpPhase>,
eof_dependent: bool,
shifted_at: Option<usize>,
values: Vec<Value>,
}
impl<'a, 'b> Parser<'a, 'b> {
pub(crate) fn new(input: &'a str, opts: Options) -> Self {
Self::with_sink(input, opts, None, None)
}
pub(crate) fn new_stream(
input: &'a str,
opts: Options,
out: &'b mut String,
cp: Option<&'b mut ResumeCp>,
) -> Self {
Self::with_sink(input, opts, Some(out), cp)
}
fn with_sink(
input: &'a str,
opts: Options,
out: Option<&'b mut String>,
cp_slot: Option<&'b mut ResumeCp>,
) -> Self {
Self {
lexer: Lexer::new(input, opts),
peeked: None,
opts,
frames: Vec::with_capacity(FRAME_PREALLOC),
max_depth: 0,
top: TopState::default(),
out,
cp_slot,
resume_phase: None,
eof_dependent: false,
shifted_at: None,
values: Vec::new(),
}
}
pub(crate) fn new_stream_resumed(
input: &'a str,
opts: Options,
out: &'b mut String,
cp: &'b mut ResumeCp,
) -> Self {
let (phase, input_pos, top) = (cp.phase, cp.input_pos, cp.top);
let mut parser = Self::with_sink(input, opts, Some(out), Some(cp));
if let Some(cp) = parser.cp_slot.as_deref() {
parser.frames.extend_from_slice(&cp.frames);
}
parser.lexer.set_pos(input_pos);
parser.max_depth = parser.frames.len();
parser.top = top;
parser.resume_phase = Some(phase);
parser
}
pub(crate) fn max_depth(&self) -> usize {
self.max_depth
}
pub(crate) fn shifted_at(&self) -> Option<usize> {
self.shifted_at
}
fn streaming(&self) -> bool {
self.out.is_some()
}
fn emit(&mut self) -> Option<&mut String> {
self.out.as_deref_mut()
}
fn invalidate_cp_from(&mut self, first_pos: usize) {
let Some(slot) = self.cp_slot.as_deref_mut() else {
return;
};
if slot.valid && slot.output_len >= first_pos {
slot.valid = false;
}
}
fn save_cp(&mut self, phase: CpPhase, stable: bool) {
if !stable || self.eof_dependent || self.cp_slot.is_none() {
return;
}
let Some(out_len) = self.out.as_ref().map(|out| out.len()) else {
return;
};
let frames_len = match phase {
CpPhase::ContinueParent => self.frames.len().saturating_sub(1),
_ => self.frames.len(),
};
let frames = &self.frames[..frames_len];
if frames.iter().any(|f| f.kind == ContainerKind::Group) {
return;
}
let Some(slot) = self.cp_slot.as_deref_mut() else {
return;
};
slot.valid = true;
slot.input_pos = self.lexer.pos();
slot.output_len = out_len;
slot.phase = phase;
slot.frames.clear();
slot.frames.extend_from_slice(frames);
slot.top = self.top;
}
fn strict(&self) -> bool {
self.opts.repairs == Repairs::NONE
}
fn enter(&mut self, kind: ContainerKind) -> Result<(), Error> {
let wrap_level = usize::from(self.top.count >= 1);
if self.frames.len() + wrap_level >= MAX_NESTING_DEPTH {
return Err(Error::new(ErrorKind::DepthLimitExceeded, self.position()));
}
self.frames.push(Frame {
kind,
first: true,
comma_pending: false,
});
self.max_depth = self.max_depth.max(self.frames.len());
Ok(())
}
fn leave(&mut self) {
self.frames.pop();
}
fn peek_newline(&mut self) -> Result<bool, Error> {
self.fill(false)?;
Ok(self
.peeked
.as_ref()
.is_some_and(|(spanned, _)| spanned.newline_before))
}
pub(crate) fn parse_document(&mut self) -> Result<Value, Error> {
self.parse_top()?;
if self.top.count == 1 {
return Ok(self.pop_value());
}
if self.values.iter().map(value_depth).max().unwrap_or(0) >= MAX_NESTING_DEPTH {
return Err(Error::new(ErrorKind::DepthLimitExceeded, self.lexer.pos()));
}
Ok(Value::Array(core::mem::take(&mut self.values)))
}
pub(crate) fn parse_document_stream(&mut self) -> Result<(), Error> {
self.parse_top()
}
pub(crate) fn parse_resume(&mut self) -> Result<(), Error> {
let mut need_tail = match self.resume_phase.unwrap_or(CpPhase::TopContinue) {
CpPhase::TopContinue => return self.top_continue(),
CpPhase::InContainer => false,
CpPhase::ContinueParent => true,
};
while let Some(frame) = self.frames.last_mut() {
if need_tail {
frame.first = false;
frame.comma_pending = false;
}
need_tail = true;
let kind = frame.kind;
match kind {
ContainerKind::Object => self.run_object_loop()?,
ContainerKind::Array => self.run_array_loop()?,
ContainerKind::Group => {
return Err(Error::new(ErrorKind::UnexpectedEnd, self.position()));
}
};
self.leave();
}
self.top.count += 1;
self.top_continue()
}
fn parse_top(&mut self) -> Result<(), Error> {
let position = self.position();
self.peek_tag(false)?;
let prose_first = matches!(
self.peeked.as_ref(),
Some((spanned, _))
if matches!(
&spanned.token,
Token::Word { text, .. } if text.chars().any(|c| c.is_whitespace())
)
);
let first_pos = self.out.as_ref().map_or(0, |out| out.len());
self.top = TopState {
first_pos,
prose_first,
count: 0,
};
if !self.parse_value()? {
return Err(Error::new(ErrorKind::NoValueFound, position));
}
self.top.count = 1;
self.top_continue()
}
fn pop_value(&mut self) -> Value {
self.values
.pop()
.expect("tree mode pushes one value per parsed value")
}
fn top_continue(&mut self) -> Result<(), Error> {
let first_pos = self.top.first_pos;
let prose_first = self.top.prose_first;
loop {
let mut pending_comma = false;
loop {
match self.peek_tag(false)? {
Some(Tag::Comma) => {
self.take();
if pending_comma && self.strict() {
return Err(Error::new(ErrorKind::TrailingComma, self.position()));
}
pending_comma = true;
}
Some(
tag @ (Tag::Semicolon
| Tag::Ellipsis
| Tag::Plus
| Tag::Colon
| Tag::CloseParen
| Tag::CloseBrace
| Tag::CloseBracket),
) => {
if self.strict() {
return Err(self.unexpected(tag));
}
self.take();
}
_ => break,
}
}
let next_is_value = matches!(self.peek_tag(false)?, Some(tag) if tag.starts_value());
if !next_is_value {
if pending_comma && self.strict() {
return Err(Error::new(ErrorKind::TrailingComma, self.position()));
}
break;
}
let separated = pending_comma || self.peek_newline()?;
if !separated || prose_first || !self.opts.repairs(Repairs::NDJSON) {
return Err(Error::new(ErrorKind::TrailingValue, self.position()));
}
let base = self.emit().map(|out| out.len());
let wraps_first = self.top.count == 1;
let inserted_bracket = self.streaming() && wraps_first;
if wraps_first {
let first_depth = match self.out.as_deref() {
Some(out) => structural_depth(&out[first_pos..]),
None => value_depth(&self.values[0]),
};
if first_depth >= MAX_NESTING_DEPTH {
return Err(Error::new(ErrorKind::DepthLimitExceeded, self.position()));
}
}
if let Some(out) = self.emit() {
if inserted_bracket {
out.insert(first_pos, '[');
}
out.push_str(", ");
}
if inserted_bracket {
self.shifted_at = Some(first_pos);
self.invalidate_cp_from(first_pos);
}
match self.parse_value()? {
true => self.top.count += 1,
false => {
if let (Some(out), Some(base)) = (self.emit(), base) {
out.truncate(base + usize::from(inserted_bracket));
if inserted_bracket {
out.remove(first_pos);
}
}
if inserted_bracket {
self.shifted_at = None;
self.invalidate_cp_from(first_pos);
}
break;
}
}
}
if self.top.count > 1 {
if let Some(out) = self.emit() {
out.push(']');
}
}
Ok(())
}
fn position(&self) -> usize {
match &self.peeked {
Some((spanned, _)) => spanned.start,
None => self.lexer.pos(),
}
}
fn fill(&mut self, key_position: bool) -> Result<(), Error> {
if self.peeked.is_none() {
self.lexer.set_in_container(!self.frames.is_empty());
if let Some(spanned) = self.lexer.next_token(key_position)? {
let tag = Tag::of(&spanned.token);
self.peeked.get_or_insert((spanned, tag));
}
}
Ok(())
}
fn peek_tag(&mut self, key_position: bool) -> Result<Option<Tag>, Error> {
self.fill(key_position)?;
Ok(self.peeked.as_ref().map(|(_, tag)| *tag))
}
fn take(&mut self) -> Spanned<'a> {
self.peeked
.take()
.map(|(spanned, _)| spanned)
.expect("peek was called before take")
}
}
impl Parser<'_, '_> {
fn parse_value(&mut self) -> Result<bool, Error> {
while let Some(tag @ (Tag::Ellipsis | Tag::Semicolon | Tag::Plus)) = self.peek_tag(false)? {
if self.strict() {
return Err(self.unexpected(tag));
}
self.take();
}
let Some(tag) = self.peek_tag(false)? else {
return Ok(false);
};
match tag {
Tag::OpenBrace => self.parse_object(),
Tag::OpenBracket => self.parse_array(),
Tag::OpenParen => self.parse_group(),
Tag::Str { truncated } => self.parse_string_value(truncated),
Tag::Num { truncated } => {
let Token::Num { text, .. } = self.take().token else {
return Ok(false);
};
if truncated && !self.opts.allows(Allow::NUM) {
return Ok(false);
}
self.eof_dependent |= truncated;
match self.out.as_deref_mut() {
Some(out) => out.push_str(&text),
None => self
.values
.push(Value::Number(Number::from_normalized(text.into_owned()))),
}
Ok(true)
}
Tag::Bool => {
let Token::Bool(value) = self.take().token else {
return Ok(false);
};
Ok(self.produce(if value { "true" } else { "false" }, Value::Bool(value)))
}
Tag::Null | Tag::Undefined => {
self.take();
Ok(self.produce("null", Value::Null))
}
Tag::Word { .. } => self.parse_word_value(),
Tag::CloseBrace
| Tag::CloseBracket
| Tag::CloseParen
| Tag::Colon
| Tag::Comma
| Tag::Semicolon
| Tag::Plus
| Tag::Ellipsis => Ok(false),
}
}
fn produce(&mut self, json: &str, value: Value) -> bool {
match self.out.as_deref_mut() {
Some(out) => out.push_str(json),
None => self.values.push(value),
}
true
}
fn produce_str(&mut self, text: Cow<'_, str>) -> bool {
match self.out.as_deref_mut() {
Some(out) => write_escaped(out, &text),
None => self.values.push(Value::String(text.into_owned())),
}
true
}
fn parse_group(&mut self) -> Result<bool, Error> {
self.enter(ContainerKind::Group)?;
let result = self.parse_group_inner();
self.leave();
result
}
fn parse_group_inner(&mut self) -> Result<bool, Error> {
if self.strict() {
return Err(Error::new(
ErrorKind::UnexpectedCharacter('('),
self.position(),
));
}
self.take();
let mut inner = self.parse_value()?;
loop {
match self.peek_tag(false)? {
Some(Tag::CloseParen) => {
self.take();
break;
}
Some(Tag::Semicolon) => {
self.take();
}
Some(tag) if tag.starts_value() && !inner => {
inner = self.parse_value()?;
}
_ => break,
}
}
Ok(inner)
}
fn parse_string_value(&mut self, truncated: bool) -> Result<bool, Error> {
let spanned = self.take();
let Token::Str { text, .. } = spanned.token else {
return Ok(false);
};
if !self.accept_truncated(truncated, spanned.start)? {
return Ok(false);
}
let mut text = text;
loop {
if self.peeked.is_none() && self.lexer.peek_significant_char() != Some('+') {
break;
}
if !matches!(self.peek_tag(false)?, Some(Tag::Plus)) {
break;
}
if !self.opts.repairs(Repairs::CONCATENATION) {
return Err(Error::new(
ErrorKind::UnexpectedCharacter('+'),
self.position(),
));
}
match self.lexer.peek_significant_char() {
Some(c) if crate::chars::is_quote(c) || c == '&' || c == '\\' => {}
_ => break,
}
self.take();
match self.peek_tag(false)? {
Some(Tag::Str {
truncated: segment_truncated,
}) => {
let spanned = self.take();
let segment_start = spanned.start;
if let Token::Str { text: next, .. } = spanned.token {
if !self.accept_truncated(segment_truncated, segment_start)? {
return Ok(false);
}
text.to_mut().push_str(&next);
}
}
_ => break,
}
}
Ok(self.produce_str(text))
}
fn accept_truncated(&mut self, truncated: bool, start: usize) -> Result<bool, Error> {
if !truncated {
return Ok(true);
}
if !self.opts.repairs(Repairs::TRUNCATION) {
return Err(Error::new(ErrorKind::UnexpectedEnd, start));
}
if !self.opts.allows(Allow::STR) {
return Ok(false);
}
self.eof_dependent = true;
Ok(true)
}
fn parse_word_value(&mut self) -> Result<bool, Error> {
let spanned = self.take();
let start = spanned.start;
let Token::Word { text, truncated } = spanned.token else {
return Ok(false);
};
let next_is_call =
if self.peeked.is_none() && self.lexer.peek_significant_char() != Some('(') {
false
} else {
matches!(self.peek_tag(false)?, Some(Tag::OpenParen))
};
if next_is_call {
if !self.opts.repairs(Repairs::CALLS) {
return Err(Error::new(ErrorKind::UnexpectedCharacter('('), start));
}
return self.parse_group();
}
if !self.opts.repairs(Repairs::UNQUOTED) {
return Err(Error::new(ErrorKind::UnquotedValue, start));
}
if !self.accept_truncated(truncated, start)? {
return Ok(false);
}
Ok(self.produce_str(Cow::Borrowed(text)))
}
}
enum KeyStep<'a> {
Key(Cow<'a, str>, bool),
Skip,
Stop,
}
impl<'a> Parser<'a, '_> {
fn parse_key(&mut self) -> Result<KeyStep<'a>, Error> {
let Some(tag) = self.peek_tag(true)? else {
return Ok(KeyStep::Stop);
};
if !matches!(
tag,
Tag::Str { .. }
| Tag::Word { .. }
| Tag::Num { .. }
| Tag::Bool
| Tag::Null
| Tag::Undefined
) {
if self.strict() {
return Err(Error::new(ErrorKind::ExpectedObjectKey, self.position()));
}
self.take();
return Ok(KeyStep::Skip);
}
let spanned = self.take();
Ok(match spanned.token {
Token::Str { text, truncated } | Token::Num { text, truncated } => {
KeyStep::Key(text, truncated)
}
Token::Word { text, truncated } => {
if !self.opts.repairs(Repairs::UNQUOTED) {
return Err(Error::new(ErrorKind::UnquotedValue, spanned.start));
}
KeyStep::Key(Cow::Borrowed(text), truncated)
}
Token::Bool(value) => {
KeyStep::Key(Cow::Borrowed(if value { "true" } else { "false" }), false)
}
_ => KeyStep::Key(Cow::Borrowed("null"), false),
})
}
fn emit_null_member(
&mut self,
members: &mut Vec<(String, Value)>,
key: Cow<'_, str>,
stable_tail: bool,
) {
let idx = self.frames.len() - 1;
let first = self.frames[idx].first;
if let Some(out) = self.emit() {
if !first {
out.push_str(", ");
}
write_escaped(out, &key);
out.push_str(": null");
} else {
members.push((key.into_owned(), Value::Null));
}
self.frames[idx].first = false;
self.frames[idx].comma_pending = false;
let stable = stable_tail || !self.lexer.at_effective_end();
self.save_cp(CpPhase::InContainer, stable);
}
fn parse_object(&mut self) -> Result<bool, Error> {
self.open_container(ContainerKind::Object, '{')?;
let result = self.run_object_loop();
self.leave();
result
}
fn parse_array(&mut self) -> Result<bool, Error> {
self.open_container(ContainerKind::Array, '[')?;
let result = self.run_array_loop();
self.leave();
result
}
fn open_container(&mut self, kind: ContainerKind, opener: char) -> Result<(), Error> {
self.enter(kind)?;
self.take();
if let Some(out) = self.emit() {
out.push(opener);
}
Ok(())
}
fn separator_step(&mut self, idx: usize, key_position: bool, own: Tag) -> Result<Step, Error> {
let Frame {
first,
comma_pending,
..
} = self.frames[idx];
match self.peek_tag(key_position)? {
None => return Ok(Step::End),
Some(tag @ (Tag::CloseBrace | Tag::CloseBracket)) => {
if tag != own && self.strict() {
return Err(self.unexpected(tag));
}
if comma_pending && self.strict() {
return Err(Error::new(ErrorKind::TrailingComma, self.position()));
}
self.take();
return Ok(Step::Closed);
}
Some(Tag::Comma) => {
self.take();
if (first || comma_pending) && self.strict() {
return Err(Error::new(ErrorKind::TrailingComma, self.position()));
}
self.frames[idx].comma_pending = true;
self.save_cp(CpPhase::InContainer, true);
return Ok(Step::Again);
}
Some(tag @ (Tag::Semicolon | Tag::Ellipsis | Tag::Colon | Tag::Plus)) => {
if self.strict() {
return Err(self.unexpected(tag));
}
self.take();
return Ok(Step::Again);
}
_ => {}
}
if !first && !comma_pending && self.strict() {
return Err(Error::new(ErrorKind::ExpectedComma, self.position()));
}
Ok(Step::Member)
}
fn unexpected(&self, tag: Tag) -> Error {
Error::new(
ErrorKind::UnexpectedCharacter(tag.punct_char()),
self.position(),
)
}
fn separator_mark(&mut self, idx: usize) -> Option<usize> {
let first = self.frames[idx].first;
let out = self.out.as_deref_mut()?;
let mark = out.len();
if !first {
out.push_str(", ");
}
Some(mark)
}
fn rollback(&mut self, mark: Option<usize>) {
if let (Some(out), Some(mark)) = (self.out.as_deref_mut(), mark) {
out.truncate(mark);
}
}
fn member_done(&mut self, idx: usize) {
self.frames[idx].first = false;
self.frames[idx].comma_pending = false;
}
fn run_object_loop(&mut self) -> Result<bool, Error> {
let idx = self.frames.len() - 1;
let mut members: Vec<(String, Value)> = Vec::new();
let closed = loop {
match self.separator_step(idx, true, Tag::CloseBrace)? {
Step::Again => continue,
Step::Closed => break true,
Step::End => break false,
Step::Member => {}
}
if matches!(
self.peek_tag(true)?,
Some(Tag::OpenBrace | Tag::OpenBracket)
) {
break false;
}
let (key, key_truncated) = match self.parse_key()? {
KeyStep::Key(key, truncated) => (key, truncated),
KeyStep::Skip => continue,
KeyStep::Stop => break false,
};
if key_truncated {
self.eof_dependent = true;
if !self.opts.repairs(Repairs::TRUNCATION) {
return Err(Error::new(ErrorKind::UnexpectedEnd, self.position()));
}
if !self.opts.allows(Allow::KEY) {
break false;
}
}
match self.peek_tag(false)? {
Some(Tag::Colon) => {
self.take();
}
Some(Tag::Comma | Tag::Semicolon) => {
self.emit_null_member(&mut members, key, true);
continue;
}
None => {
if self.opts.repairs(Repairs::TRUNCATION) && self.opts.allows(Allow::KEY) {
self.emit_null_member(&mut members, key, false);
}
break false;
}
Some(other) if other.starts_value() => {
if self.strict() {
return Err(Error::new(ErrorKind::ExpectedColon, self.position()));
}
}
Some(_) => {
self.take();
self.emit_null_member(&mut members, key, false);
continue;
}
}
match self.peek_tag(false)? {
None => {
if self.opts.repairs(Repairs::TRUNCATION) {
self.emit_null_member(&mut members, key, false);
}
break false;
}
Some(Tag::CloseBrace | Tag::CloseBracket | Tag::Comma | Tag::Semicolon)
if !self.strict() =>
{
self.emit_null_member(&mut members, key, false);
continue;
}
_ => {}
}
let mark = self.separator_mark(idx);
if let Some(out) = self.emit() {
write_escaped(out, &key);
out.push_str(": ");
}
if !self.parse_value()? {
self.rollback(mark);
break false;
}
if mark.is_none() {
let value = self.pop_value();
members.push((key.into_owned(), value));
}
self.member_done(idx);
};
self.close_container(closed, Allow::OBJ, '}', || Value::Object(members))
}
fn run_array_loop(&mut self) -> Result<bool, Error> {
let idx = self.frames.len() - 1;
let mut items: Vec<Value> = Vec::new();
let closed = loop {
match self.separator_step(idx, false, Tag::CloseBracket)? {
Step::Again => continue,
Step::Closed => break true,
Step::End => break false,
Step::Member => {}
}
let mark = self.separator_mark(idx);
if !self.parse_value()? {
self.rollback(mark);
break false;
}
if mark.is_none() {
let value = self.pop_value();
items.push(value);
}
self.member_done(idx);
};
self.close_container(closed, Allow::ARR, ']', || Value::Array(items))
}
fn close_container(
&mut self,
closed: bool,
flag: Allow,
closer: char,
node: impl FnOnce() -> Value,
) -> Result<bool, Error> {
let keep = closed || {
if !self.opts.repairs(Repairs::TRUNCATION) {
return Err(Error::new(ErrorKind::UnexpectedEnd, self.position()));
}
self.opts.allows(flag)
};
match self.out.as_deref_mut() {
Some(out) => {
debug_assert!(keep);
out.push(closer);
if closed {
self.save_cp(CpPhase::ContinueParent, true);
}
}
None if keep => self.values.push(node()),
None => {}
}
Ok(keep)
}
}
enum Step {
Member,
Again,
Closed,
End,
}
pub(crate) fn structural_depth(text: &str) -> usize {
let mut depth = 0usize;
let mut max = 0usize;
let mut in_str = false;
let mut esc = false;
for &b in text.as_bytes() {
if in_str {
if esc {
esc = false;
} else if b == b'\\' {
esc = true;
} else if b == b'"' {
in_str = false;
}
continue;
}
match b {
b'"' => in_str = true,
b'[' | b'{' => {
depth += 1;
max = max.max(depth);
}
b']' | b'}' => depth = depth.saturating_sub(1),
_ => {}
}
}
max
}
fn value_depth(value: &crate::value::Value) -> usize {
use crate::value::Value;
match value {
Value::Array(items) => 1 + items.iter().map(value_depth).max().unwrap_or(0),
Value::Object(members) => {
1 + members
.iter()
.map(|(_, v)| value_depth(v))
.max()
.unwrap_or(0)
}
_ => 0,
}
}
#[cfg(test)]
mod truncated_peek_tests {
use super::*;
#[test]
fn two_pass_string_is_truncated() {
let opts = Options::all();
let mut lexer = Lexer::new("\"a#\n", opts);
let tok = lexer.next_token(false).expect("token").expect("some");
match tok.token {
Token::Str { truncated, .. } => assert!(truncated, "expected truncated Str"),
other => panic!("expected Str, got {:?}", other),
}
}
}