use core::range::Range;
use crate::{
error::{Error, ErrorKind, Result},
tokens::{Token, TokenStream, TokenWithContext},
traverse::{Visitor, parse_tokens_at_depth, validate_start_of_value},
};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ArrayState {
Open,
ValueOrEnd {
open_ctx: TokenWithContext,
},
Value {
open_ctx: TokenWithContext,
expect_ctx: TokenWithContext,
},
CommaOrEnd {
open_ctx: TokenWithContext,
last_value_range: Range<usize>,
},
End(Range<usize>),
}
impl ArrayState {
pub fn process<'a>(
self,
tokens: &mut TokenStream<'a>,
text: &'a str,
visitor: &mut impl Visitor<'a>,
depth: usize,
) -> Result<Self> {
let next_state = match self {
ArrayState::Open => match tokens.next_token()? {
Some(
open_ctx @ TokenWithContext {
token: Token::OpenSquareBracket,
..
},
) => {
visitor.on_array_open(open_ctx.range);
ArrayState::ValueOrEnd { open_ctx }
}
maybe_token => {
return Err(Error::from_maybe_token_with_context(
|tok| ErrorKind::ExpectedOpenBrace {
expected: '['.into(),
context: None,
found: tok,
},
maybe_token,
text,
));
}
},
ArrayState::ValueOrEnd { open_ctx } => match tokens.peek_token()?.copied() {
Some(TokenWithContext {
token: Token::ClosedSquareBracket,
range: closed_range,
}) => {
tokens.next_token()?;
visitor.on_array_close(closed_range);
ArrayState::End(open_ctx.range.start..closed_range.end)
}
Some(token_ctx) if token_ctx.token.is_start_of_value() => ArrayState::Value {
open_ctx,
expect_ctx: open_ctx,
},
Some(_) => {
return Err(Error::from_maybe_token_with_context(
|tok| {
ErrorKind::expected_entry_or_closed_delimiter(open_ctx, tok)
.expect("array should open with a square bracket")
},
tokens.next_token()?,
text,
));
}
None => {
return Err(Error::from_maybe_token_with_context(
|tok| {
ErrorKind::expected_entry_or_closed_delimiter(open_ctx, tok)
.expect("array should open with a square bracket")
},
None,
text,
));
}
},
ArrayState::Value {
open_ctx,
expect_ctx,
} => {
validate_start_of_value(text, expect_ctx, tokens.peek_token()?.copied())?;
let value_range = parse_tokens_at_depth(tokens, text, false, visitor, depth + 1)?;
ArrayState::CommaOrEnd {
open_ctx,
last_value_range: value_range,
}
}
ArrayState::CommaOrEnd { open_ctx, .. } => match tokens.peek_token()?.copied() {
Some(TokenWithContext {
token: Token::ClosedSquareBracket,
range: closed_range,
}) => {
tokens.next_token()?;
visitor.on_array_close(closed_range);
ArrayState::End(open_ctx.range.start..closed_range.end)
}
Some(
comma_ctx @ TokenWithContext {
token: Token::Comma,
..
},
) => {
tokens.next_token()?;
visitor.on_item_delim();
ArrayState::Value {
open_ctx,
expect_ctx: comma_ctx,
}
}
_ => {
return Err(Error::from_maybe_token_with_context(
|tok| {
ErrorKind::expected_entry_or_closed_delimiter(open_ctx, tok)
.expect("array should open with a square bracket")
},
tokens.next_token()?,
text,
));
}
},
ArrayState::End(_) => {
return Ok(self);
}
};
Ok(next_state)
}
}
pub fn parse_array<'a>(
tokens: &mut TokenStream<'a>,
text: &'a str,
depth: usize,
visitor: &mut impl Visitor<'a>,
) -> Result<Range<usize>> {
let mut state = ArrayState::Open;
loop {
state = state.process(tokens, text, visitor, depth)?;
if let ArrayState::End(result) = state {
break Ok(result);
}
}
}