use crate::ast::{Number, Token, Value};
use crate::error::{Error, Result, Span};
use crate::lexer::{JsonLexer, Lexer};
use crate::parser::ParserOptions;
use rustc_hash::FxHashMap;
pub struct RecursiveDescentParser<'a> {
lexer: Lexer<'a>,
current_token: Option<(Token, Span)>,
options: ParserOptions,
depth: usize,
}
impl<'a> RecursiveDescentParser<'a> {
pub fn new(input: &'a str, options: ParserOptions) -> Self {
Self {
lexer: Lexer::new(input),
current_token: None,
options,
depth: 0,
}
}
pub fn parse(&mut self) -> Result<Value> {
self.advance()?;
let value = self.parse_value()?;
self.expect_eof()?;
Ok(value)
}
fn advance(&mut self) -> Result<()> {
loop {
let (token, span) = self.lexer.next_token_with_span()?;
self.current_token = Some((token, span));
match &self.current_token.as_ref().unwrap().0 {
Token::SingleLineComment | Token::MultiLineComment => {
if self.options.allow_comments {
continue; } else {
return Err(Error::Custom("Comments are not allowed".to_string()));
}
}
_ => break,
}
}
Ok(())
}
fn peek(&self) -> Option<&Token> {
self.current_token.as_ref().map(|(token, _)| token)
}
fn current_span(&self) -> Span {
self.current_token
.as_ref()
.map(|(_, span)| *span)
.unwrap_or(Span { start: 0, end: 0 })
}
fn expect(&mut self, expected: Token) -> Result<()> {
if let Some((token, _)) = &self.current_token {
if std::mem::discriminant(token) == std::mem::discriminant(&expected) {
self.advance()?;
Ok(())
} else {
Err(Error::Expected {
expected: format!("{expected:?}"),
found: format!("{token:?}"),
position: self.current_span().start,
})
}
} else {
Err(Error::Expected {
expected: format!("{expected:?}"),
found: "EOF".to_string(),
position: self.current_span().start,
})
}
}
fn expect_eof(&self) -> Result<()> {
if let Some((Token::Eof, _)) = &self.current_token {
Ok(())
} else {
Err(Error::Expected {
expected: "end of input".to_string(),
found: format!("{:?}", self.current_token.as_ref().map(|(t, _)| t)),
position: self.current_span().start,
})
}
}
fn check_depth(&self) -> Result<()> {
if self.depth >= self.options.max_depth {
Err(Error::DepthLimitExceeded(self.current_span().start))
} else {
Ok(())
}
}
fn parse_value(&mut self) -> Result<Value> {
match self.peek() {
Some(Token::LeftBrace) => self.parse_object(),
Some(Token::LeftBracket) => self.parse_array(),
Some(Token::String) => self.parse_string(),
Some(Token::Number) => self.parse_number(),
Some(Token::True) => self.parse_true(),
Some(Token::False) => self.parse_false(),
Some(Token::Null) => self.parse_null(),
Some(Token::UnquotedString) if self.options.allow_unquoted_keys => {
self.parse_unquoted_string()
}
Some(token) => Err(Error::Expected {
expected: "value".to_string(),
found: format!("{token:?}"),
position: self.current_span().start,
}),
None => Err(Error::Expected {
expected: "value".to_string(),
found: "EOF".to_string(),
position: self.current_span().start,
}),
}
}
fn parse_object(&mut self) -> Result<Value> {
self.check_depth()?;
self.depth += 1;
self.expect(Token::LeftBrace)?;
let mut object = FxHashMap::default();
if let Some(Token::RightBrace) = self.peek() {
self.advance()?;
self.depth -= 1;
return Ok(Value::Object(object));
}
loop {
let key = self.parse_object_key()?;
self.expect(Token::Colon)?;
let value = self.parse_value()?;
object.insert(key, value);
match self.peek() {
Some(Token::Comma) => {
self.advance()?;
if let Some(Token::RightBrace) = self.peek() {
if self.options.allow_trailing_commas {
break;
} else {
return Err(Error::Custom("Trailing comma not allowed".to_string()));
}
}
}
Some(Token::RightBrace) => break,
Some(Token::Newline) if self.options.newline_as_comma => {
self.advance()?;
if let Some(Token::RightBrace) = self.peek() {
break;
}
}
Some(token) => {
return Err(Error::Expected {
expected: "comma or closing brace".to_string(),
found: format!("{token:?}"),
position: self.current_span().start,
});
}
None => {
return Err(Error::Expected {
expected: "comma or closing brace".to_string(),
found: "EOF".to_string(),
position: self.current_span().start,
});
}
}
}
self.expect(Token::RightBrace)?;
self.depth -= 1;
Ok(Value::Object(object))
}
fn parse_object_key(&mut self) -> Result<String> {
match self.peek() {
Some(Token::String) => {
if let Some((Token::String, span)) = self.current_token {
let key = self.parse_string_from_span(span)?;
self.advance()?;
if let Value::String(s) = key {
Ok(s)
} else {
unreachable!("parse_string_from_span should return a String")
}
} else {
unreachable!("Token::String should be present")
}
}
Some(Token::UnquotedString) if self.options.allow_unquoted_keys => {
if let Some((Token::UnquotedString, span)) = self.current_token {
let key = self.lexer.span_text(&span).to_string();
self.advance()?;
Ok(key)
} else {
unreachable!("Token::UnquotedString should be present")
}
}
Some(token) => Err(Error::Expected {
expected: "string key".to_string(),
found: format!("{token:?}"),
position: self.current_span().start,
}),
None => Err(Error::Expected {
expected: "string key".to_string(),
found: "EOF".to_string(),
position: self.current_span().start,
}),
}
}
fn parse_array(&mut self) -> Result<Value> {
self.check_depth()?;
self.depth += 1;
self.expect(Token::LeftBracket)?;
let mut array = Vec::new();
if let Some(Token::RightBracket) = self.peek() {
self.advance()?;
self.depth -= 1;
return Ok(Value::Array(array));
}
loop {
array.push(self.parse_value()?);
match self.peek() {
Some(Token::Comma) => {
self.advance()?;
if let Some(Token::RightBracket) = self.peek() {
if self.options.allow_trailing_commas {
break;
} else {
return Err(Error::Custom("Trailing comma not allowed".to_string()));
}
}
}
Some(Token::RightBracket) => break,
Some(Token::Newline) if self.options.newline_as_comma => {
self.advance()?;
if let Some(Token::RightBracket) = self.peek() {
break;
}
}
Some(token) => {
return Err(Error::Expected {
expected: "comma or closing bracket".to_string(),
found: format!("{token:?}"),
position: self.current_span().start,
});
}
None => {
return Err(Error::Expected {
expected: "comma or closing bracket".to_string(),
found: "EOF".to_string(),
position: self.current_span().start,
});
}
}
}
self.expect(Token::RightBracket)?;
self.depth -= 1;
Ok(Value::Array(array))
}
fn parse_string(&mut self) -> Result<Value> {
if let Some((Token::String, span)) = self.current_token {
let value = self.parse_string_from_span(span)?;
self.advance()?;
Ok(value)
} else {
Err(Error::Expected {
expected: "string".to_string(),
found: format!("{:?}", self.current_token.as_ref().map(|(t, _)| t)),
position: self.current_span().start,
})
}
}
fn parse_unquoted_string(&mut self) -> Result<Value> {
if let Some((Token::UnquotedString, span)) = self.current_token {
let value = self.lexer.span_text(&span).to_string();
self.advance()?;
Ok(Value::String(value))
} else {
Err(Error::Expected {
expected: "unquoted string".to_string(),
found: format!("{:?}", self.current_token.as_ref().map(|(t, _)| t)),
position: self.current_span().start,
})
}
}
fn parse_string_from_span(&self, span: Span) -> Result<Value> {
let text = self.lexer.span_text(&span);
let content = if (text.starts_with('"') && text.ends_with('"'))
|| (text.starts_with('\'') && text.ends_with('\'') && self.options.allow_single_quotes)
{
&text[1..text.len() - 1]
} else {
return Err(Error::Custom("Invalid string format".to_string()));
};
let mut result = String::new();
let mut chars = content.chars();
while let Some(ch) = chars.next() {
if ch == '\\' {
match chars.next() {
Some('"') => result.push('"'),
Some('\\') => result.push('\\'),
Some('/') => result.push('/'),
Some('b') => result.push('\u{0008}'),
Some('f') => result.push('\u{000C}'),
Some('n') => result.push('\n'),
Some('r') => result.push('\r'),
Some('t') => result.push('\t'),
Some('u') => {
let hex: String = chars.by_ref().take(4).collect();
if hex.len() != 4 {
return Err(Error::Custom("Invalid unicode escape".to_string()));
}
match u32::from_str_radix(&hex, 16) {
Ok(code) => {
if let Some(unicode_char) = char::from_u32(code) {
result.push(unicode_char);
} else {
return Err(Error::Custom(
"Invalid unicode code point".to_string(),
));
}
}
Err(_) => {
return Err(Error::Custom("Invalid unicode escape".to_string()))
}
}
}
Some(ch) => {
return Err(Error::Custom(format!("Invalid escape sequence: \\{ch}")))
}
None => return Err(Error::Custom("Incomplete escape sequence".to_string())),
}
} else {
result.push(ch);
}
}
Ok(Value::String(result))
}
fn parse_number(&mut self) -> Result<Value> {
if let Some((Token::Number, span)) = self.current_token {
let text = self.lexer.span_text(&span);
if let Ok(int_val) = text.parse::<i64>() {
self.advance()?;
return Ok(Value::Number(Number::Integer(int_val)));
}
if let Ok(float_val) = text.parse::<f64>() {
self.advance()?;
return Ok(Value::Number(Number::Float(float_val)));
}
Err(Error::InvalidNumber(span.start))
} else {
Err(Error::Expected {
expected: "number".to_string(),
found: format!("{:?}", self.current_token.as_ref().map(|(t, _)| t)),
position: self.current_span().start,
})
}
}
fn parse_true(&mut self) -> Result<Value> {
self.expect(Token::True)?;
Ok(Value::Bool(true))
}
fn parse_false(&mut self) -> Result<Value> {
self.expect(Token::False)?;
Ok(Value::Bool(false))
}
fn parse_null(&mut self) -> Result<Value> {
self.expect(Token::Null)?;
Ok(Value::Null)
}
}
pub fn parse_recursive(input: &str, options: ParserOptions) -> Result<Value> {
let mut parser = RecursiveDescentParser::new(input, options);
parser.parse()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_null() {
let result = parse_recursive("null", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Null);
}
#[test]
fn test_parse_boolean() {
let result = parse_recursive("true", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Bool(true));
let result = parse_recursive("false", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Bool(false));
}
#[test]
fn test_parse_number() {
let result = parse_recursive("42", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Number(Number::Integer(42)));
let result = parse_recursive("3.14", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Number(Number::Float(3.14)));
}
#[test]
fn test_parse_string() {
let result = parse_recursive(r#""hello""#, ParserOptions::default()).unwrap();
assert_eq!(result, Value::String("hello".to_string()));
}
#[test]
fn test_parse_array() {
let result = parse_recursive("[1, 2, 3]", ParserOptions::default()).unwrap();
assert_eq!(
result,
Value::Array(vec![
Value::Number(Number::Integer(1)),
Value::Number(Number::Integer(2)),
Value::Number(Number::Integer(3)),
])
);
}
#[test]
fn test_parse_object() {
let result = parse_recursive(r#"{"key": "value"}"#, ParserOptions::default()).unwrap();
let mut expected = FxHashMap::default();
expected.insert("key".to_string(), Value::String("value".to_string()));
assert_eq!(result, Value::Object(expected));
}
#[test]
fn test_parse_nested() {
let json = r#"{"array": [1, 2, {"nested": true}]}"#;
let result = parse_recursive(json, ParserOptions::default()).unwrap();
let mut nested_obj = FxHashMap::default();
nested_obj.insert("nested".to_string(), Value::Bool(true));
let mut expected = FxHashMap::default();
expected.insert(
"array".to_string(),
Value::Array(vec![
Value::Number(Number::Integer(1)),
Value::Number(Number::Integer(2)),
Value::Object(nested_obj),
]),
);
assert_eq!(result, Value::Object(expected));
}
#[test]
fn test_parse_with_comments() {
let json = r#"{"key": "value", /* comment */ "number": 42}"#;
let result = parse_recursive(json, ParserOptions::default()).unwrap();
let mut expected = FxHashMap::default();
expected.insert("key".to_string(), Value::String("value".to_string()));
expected.insert("number".to_string(), Value::Number(Number::Integer(42)));
assert_eq!(result, Value::Object(expected));
}
#[test]
fn test_parse_with_trailing_comma() {
let json = r#"{"key": "value", "number": 42,}"#;
let result = parse_recursive(json, ParserOptions::default()).unwrap();
let mut expected = FxHashMap::default();
expected.insert("key".to_string(), Value::String("value".to_string()));
expected.insert("number".to_string(), Value::Number(Number::Integer(42)));
assert_eq!(result, Value::Object(expected));
}
#[test]
fn test_parse_with_unquoted_keys() {
let json = r#"{key: "value", number: 42}"#;
let result = parse_recursive(json, ParserOptions::default()).unwrap();
let mut expected = FxHashMap::default();
expected.insert("key".to_string(), Value::String("value".to_string()));
expected.insert("number".to_string(), Value::Number(Number::Integer(42)));
assert_eq!(result, Value::Object(expected));
}
#[test]
fn test_depth_limit() {
let mut options = ParserOptions::default();
options.max_depth = 2;
let json = r#"{"a": {"b": {"c": "too deep"}}}"#;
let result = parse_recursive(json, options);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::DepthLimitExceeded(_)));
}
#[test]
fn test_escape_sequences() {
let json = r#""hello\nworld\t\"quote\"""#;
let result = parse_recursive(json, ParserOptions::default()).unwrap();
assert_eq!(result, Value::String("hello\nworld\t\"quote\"".to_string()));
}
#[test]
fn test_unicode_escape() {
let json = r#""\u0041\u0042\u0043""#;
let result = parse_recursive(json, ParserOptions::default()).unwrap();
assert_eq!(result, Value::String("ABC".to_string()));
}
#[test]
fn test_empty_containers() {
let result = parse_recursive("{}", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Object(FxHashMap::default()));
let result = parse_recursive("[]", ParserOptions::default()).unwrap();
assert_eq!(result, Value::Array(vec![]));
}
}