use hermes_atom_table::AtomTable;
use hermes_support::diag::Subsystem;
use hermes_support::location::{SMRange, SourceId};
use hermes_support::manager::SourceErrorManager;
use crate::lexer::{GrammarContext, JSLexer};
use crate::token::Token;
use crate::token_kinds::TokenKind;
use super::factory::Prop;
use super::{JSONFactory, JSONValue};
const CTX: GrammarContext = GrammarContext::AllowDiv;
const MAX_RECURSION_DEPTH: u32 = if cfg!(debug_assertions) { 512 } else { 4096 };
pub struct JSONParser<'a> {
factory: &'a JSONFactory<'a>,
lexer: JSLexer<'a>,
recursion_depth: u32,
}
impl<'a> JSONParser<'a> {
pub fn new(
factory: &'a JSONFactory<'a>,
buf_id: SourceId,
sm: &'a mut SourceErrorManager,
atoms: &'a AtomTable,
convert_surrogates: bool,
) -> JSONParser<'a> {
let lexer =
JSLexer::new_with_convert_surrogates(buf_id, sm, atoms, CTX, convert_surrogates);
JSONParser {
factory,
lexer,
recursion_depth: 0,
}
}
pub fn error_count(&self) -> u32 {
self.lexer.get_source_mgr().error_count()
}
fn error(&mut self, msg: impl Into<String>) {
let range: SMRange = self.lexer.token().source_range();
self.lexer.get_source_mgr_mut().error_at(
range.start,
Some(range),
msg.into(),
Subsystem::Parser,
);
}
fn cur(&self) -> &Token {
self.lexer.token()
}
fn advance(&mut self) -> &Token {
self.lexer.advance(CTX)
}
pub fn parse(&mut self) -> Option<&'a JSONValue<'a>> {
self.advance();
let res = self.parse_value()?;
if self.lexer.get_source_mgr().error_count() != 0 {
return None;
}
Some(res)
}
fn parse_value(&mut self) -> Option<&'a JSONValue<'a>> {
if self.recursion_depth >= MAX_RECURSION_DEPTH {
self.error("Too many nested JSON values");
return None;
}
self.recursion_depth += 1;
let res = self.parse_value_impl();
self.recursion_depth -= 1;
res
}
fn parse_value_impl(&mut self) -> Option<&'a JSONValue<'a>> {
let mut needs_negation = false;
match self.cur().kind() {
TokenKind::string_literal => {
let lit = self.cur().get_string_literal();
self.advance();
Some(self.factory.get_string(lit))
}
TokenKind::minus => {
needs_negation = true;
self.advance();
if self.cur().kind() != TokenKind::numeric_literal {
self.error("No numeric literal following minus (-) token in value");
return None;
}
self.parse_number(needs_negation)
}
TokenKind::numeric_literal => self.parse_number(needs_negation),
TokenKind::l_brace => {
self.advance();
self.parse_object()
}
TokenKind::l_square => {
self.advance();
self.parse_array()
}
TokenKind::rw_true => {
self.advance();
Some(self.factory.get_boolean(true))
}
TokenKind::rw_false => {
self.advance();
Some(self.factory.get_boolean(false))
}
TokenKind::rw_null => {
self.advance();
Some(self.factory.get_null())
}
_ => {
self.error("JSON object or array expected");
None
}
}
}
fn parse_number(&mut self, needs_negation: bool) -> Option<&'a JSONValue<'a>> {
let v = self.cur().get_numeric_literal();
let res = self.factory.get_number(if needs_negation { -v } else { v });
self.advance();
Some(res)
}
fn parse_array(&mut self) -> Option<&'a JSONValue<'a>> {
let mut storage: Vec<&'a JSONValue<'a>> = Vec::new();
if self.cur().kind() != TokenKind::r_square {
loop {
let val = self.parse_value()?;
storage.push(val);
if self.cur().kind() == TokenKind::comma {
self.advance();
if self.cur().kind() == TokenKind::r_square {
break;
}
} else {
break;
}
}
if self.cur().kind() != TokenKind::r_square {
self.error("expected ']'");
return None;
}
}
self.advance(); Some(self.factory.new_array(&storage))
}
fn parse_object(&mut self) -> Option<&'a JSONValue<'a>> {
let mut pairs: Vec<Prop<'a>> = Vec::new();
if self.cur().kind() != TokenKind::r_brace {
loop {
if self.cur().kind() != TokenKind::string_literal {
self.error("expected a string");
return None;
}
let key = self.factory.get_string(self.cur().get_string_literal());
if self.advance().kind() != TokenKind::colon {
self.error("expected ':'");
return None;
}
self.advance();
let val = self.parse_value()?;
pairs.push((key, val));
if self.cur().kind() == TokenKind::comma {
self.advance();
if self.cur().kind() == TokenKind::r_brace {
break;
}
} else {
break;
}
}
if self.cur().kind() != TokenKind::r_brace {
self.error("expected '}'");
return None;
}
}
self.advance();
if let Some(dup) = self.factory.sort_props(&mut pairs) {
let name = String::from_utf8_lossy(self.factory.atoms().bytes(dup)).into_owned();
self.error(format!("key '{name}' is already present"));
return None;
}
self.factory.new_object_sorted(&pairs)
}
}
#[cfg(test)]
mod parser_tests {
use super::super::*;
use bumpalo::Bump;
use hermes_atom_table::AtomTable;
use hermes_support::manager::SourceErrorManager;
fn parse_ok<'a>(
arena: &'a Bump,
atoms: &'a AtomTable,
sm: &'a mut SourceErrorManager,
src: &str,
) -> Option<&'a JSONValue<'a>> {
let f = arena.alloc(JSONFactory::new(arena, atoms));
let id = sm.add_buffer("json", src);
let mut p = JSONParser::new(f, id, sm, atoms, false);
p.parse()
}
#[test]
fn scalars() {
let arena = Bump::new();
let atoms = AtomTable::new();
let mut sm = SourceErrorManager::new();
assert_eq!(
parse_ok(&arena, &atoms, &mut sm, "true").and_then(|v| v.as_boolean()),
Some(true)
);
assert_eq!(
parse_ok(&arena, &atoms, &mut sm, "false").and_then(|v| v.as_boolean()),
Some(false)
);
assert_eq!(
parse_ok(&arena, &atoms, &mut sm, "null").map(|v| v.kind()),
Some(JSONKind::Null)
);
assert_eq!(
parse_ok(&arena, &atoms, &mut sm, "42").and_then(|v| v.as_number()),
Some(42.0)
);
assert_eq!(
parse_ok(&arena, &atoms, &mut sm, "-1.5").and_then(|v| v.as_number()),
Some(-1.5)
);
let s = parse_ok(&arena, &atoms, &mut sm, "'hi'")
.unwrap()
.as_string()
.unwrap();
assert_eq!(atoms.bytes(s), b"hi");
}
fn with_parse<R>(src: &str, f: impl FnOnce(Option<&JSONValue<'_>>, &AtomTable) -> R) -> R {
let arena = Bump::new();
let atoms = AtomTable::new();
let factory = arena.alloc(JSONFactory::new(&arena, &atoms));
let mut sm = SourceErrorManager::new();
let id = sm.add_buffer("json", src);
let mut parser = JSONParser::new(factory, id, &mut sm, &atoms, false);
let result = parser.parse();
f(result, &atoms)
}
#[test]
fn arrays() {
with_parse("[-1.0, -1, -0]", |r, _| {
let v = r.unwrap().as_array().unwrap();
assert_eq!(v.len(), 3);
assert_eq!(v.at(0).as_number(), Some(-1.0));
assert_eq!(v.at(2).as_number(), Some(-0.0));
});
with_parse("[]", |r, _| assert!(r.unwrap().as_array().unwrap().is_empty()));
with_parse("[1,2,3,]", |r, _| assert!(r.is_some()));
with_parse("[1,2", |r, _| assert!(r.is_none()));
}
#[test]
fn lone_minus_errors() {
let arena = Bump::new();
let atoms = AtomTable::new();
let f = arena.alloc(JSONFactory::new(&arena, &atoms));
let mut sm = SourceErrorManager::new();
let id = sm.add_buffer("json", "-");
let mut p = JSONParser::new(f, id, &mut sm, &atoms, false);
assert!(p.parse().is_none());
assert_eq!(p.error_count(), 1);
}
}