use crate::error::Error;
use crate::lexer::Span;
use crate::prelude::*;
use crate::Json;
use crate::JsonConfig;
use crate::Result;
use crate::lexer::token::Token;
use crate::lexer::token::TokenKind;
struct Parser<'a> {
tokens: &'a [Token],
src: &'a str,
curr: usize,
conf: JsonConfig,
depth: u32,
}
impl Parser<'_> {
fn parse(&mut self) -> Result<Json> {
self.value()
}
fn is_finished(&self) -> bool {
self.curr >= self.tokens.len()
}
fn error<T>(&mut self, msg: impl Into<Cow<'static, str>>) -> Result<T> {
let fpos = self.previous()?.span().file_position(self.src);
let mut error = Error::new(msg);
error.set_file_pos(fpos);
Err(error)
}
fn value(&mut self) -> Result<Json> {
if self.depth > self.conf.max_depth {
return self.error("Max depth reached");
}
macro_rules! enter {
($c:expr) => {{
self.depth += 1;
let j = $c;
self.depth -= 1;
j
}};
}
if self.match_type(TokenKind::LSquareBracket) {
enter!(self.array())
} else if self.match_type(TokenKind::LeftBrace) {
enter!(self.object())
} else if self.match_type(TokenKind::Minus) {
self.consume(TokenKind::Number, "Expected number after '-'")?;
self.number(true)
} else if self.match_type(TokenKind::Plus) {
self.consume(TokenKind::Number, "Expected number after '+'")?;
self.number(false)
} else if self.match_type(TokenKind::Number) {
self.number(false)
} else if self.match_type(TokenKind::String) {
self.string()
} else if self.match_type(TokenKind::True) {
Ok(Json::True)
} else if self.match_type(TokenKind::False) {
Ok(Json::False)
} else if self.match_type(TokenKind::Null) {
Ok(Json::Null)
} else {
self.error("Unknown token")
}
}
fn array(&mut self) -> Result<Json> {
let mut elems = Vec::new();
while !self.check(TokenKind::RSquareBracket) {
if self.is_finished() {
break;
}
if !elems.is_empty() {
self.consume(TokenKind::Comma, "Expected comma after element")?;
}
if self.peek()?.get_type() == TokenKind::RSquareBracket {
if self.conf.allow_trailing_commas {
continue;
}
return self.error("Trailing comma on list");
}
let json = self.value()?;
elems.push(json);
}
self.consume(TokenKind::RSquareBracket, "Unclosed '['")?;
Ok(elems.into())
}
fn object(&mut self) -> Result<Json> {
let mut elems = Map::new();
while !self.check(TokenKind::RightBrace) {
if self.is_finished() {
break;
}
if !elems.is_empty() {
self.consume(TokenKind::Comma, "Expected comma after element")?;
}
if !self.check(TokenKind::String) {
let msg = match self.previous().unwrap().get_type() {
TokenKind::Comma => {
if self.conf.allow_trailing_commas {
continue;
}
"Trailing comma in object"
}
_ => "Expected STRING",
};
return self.error(msg);
}
let span = self.advance()?.span();
let key = self.owned_lexem_strip(span);
self.consume(TokenKind::Colon, "Expected ':'")?;
let json = self.value()?;
elems.insert(key, json);
}
self.consume(TokenKind::RightBrace, "Unclosed '{'")?;
Ok(Json::Object(elems))
}
fn owned_lexem_strip(&self, span: Span) -> Box<str> {
let slice = span.slice(self.src);
let slice = slice.strip_prefix("\"").unwrap_or(slice);
let slice = slice.strip_suffix("\"").unwrap_or(slice);
Box::from(slice)
}
fn number(&mut self, minus: bool) -> Result<Json> {
let mut n: f64 = self.previous()?.span().slice(self.src).parse()?;
if minus {
n *= -1.0;
}
Ok(Json::Number(n))
}
fn string(&mut self) -> Result<Json> {
let s = self.previous()?.span();
let s = self.owned_lexem_strip(s);
Ok(Json::String(s))
}
fn consume(&mut self, t: TokenKind, msg: &'static str) -> Result<&Token> {
if self.check(t) {
return self.advance();
}
self.error(msg)
}
fn match_type(&mut self, t: TokenKind) -> bool {
if self.check(t) {
self.advance().unwrap();
return true;
}
false
}
fn check(&mut self, t: TokenKind) -> bool {
if self.is_finished() {
return false;
}
self.peek().unwrap().get_type() == t
}
fn advance(&mut self) -> Result<&Token> {
if !self.is_finished() {
self.curr += 1;
}
self.previous()
}
fn peek(&mut self) -> Result<&Token> {
self.tokens
.get(self.curr)
.ok_or_else(|| "Index should be valid when calling peek".into())
}
fn previous(&mut self) -> Result<&Token> {
self.tokens
.get(self.curr - 1)
.ok_or_else(|| "Index should be valid when calling peek".into())
}
}
pub fn parse(src: &str, tokens: &[Token], conf: JsonConfig) -> Result<Json> {
Parser {
tokens,
src,
curr: 0,
depth: 0,
conf,
}
.parse()
}