use crate::ast::{Token, Value};
use crate::error::{Error, Result, Span};
use crate::lexer::Lexer;
use crate::optimization::{extract_string_content, AllocationStats};
use crate::parser::ParserOptions;
use rustc_hash::FxHashMap;
pub struct OptimizedParserV3<'a> {
input: &'a str,
lexer: Lexer<'a>,
options: ParserOptions,
depth: usize,
stats: ParserStats,
}
#[derive(Debug, Default, Clone)]
pub struct ParserStats {
pub values_parsed: usize,
pub strings_parsed: usize,
pub arrays_parsed: usize,
pub objects_parsed: usize,
pub small_vec_optimizations: usize,
pub compact_string_optimizations: usize,
pub presized_collections: usize,
}
impl<'a> OptimizedParserV3<'a> {
pub fn new(input: &'a str, options: ParserOptions) -> Self {
Self {
input,
lexer: Lexer::new(input),
options,
depth: 0,
stats: ParserStats::default(),
}
}
pub fn parse(&mut self) -> Result<Value> {
let (token, span) = self.next_token()?;
self.parse_value((token, span))
}
pub fn stats(&self) -> &ParserStats {
&self.stats
}
pub fn allocation_stats(&self) -> AllocationStats {
AllocationStats::default()
}
fn next_token(&mut self) -> Result<(Token, Span)> {
self.lexer.next_token_with_span()
}
fn peek_token(&mut self) -> Result<(Token, Span)> {
self.lexer
.peek_with_span()
.map(|&(token, span)| (token, span))
}
fn parse_value(&mut self, token: (Token, Span)) -> Result<Value> {
self.stats.values_parsed += 1;
match token.0 {
Token::String => self.parse_string_optimized(token.1),
Token::Number => self.parse_number_optimized(token.1),
Token::True => Ok(Value::Bool(true)),
Token::False => Ok(Value::Bool(false)),
Token::Null => Ok(Value::Null),
Token::LeftBracket => self.parse_array_optimized(),
Token::LeftBrace => self.parse_object_optimized(),
Token::Newline => {
let (next_token, next_span) = self.next_token()?;
self.parse_value((next_token, next_span))
}
_ => Err(Error::UnexpectedChar('?', token.1.start)),
}
}
fn parse_string_optimized(&mut self, span: Span) -> Result<Value> {
self.stats.strings_parsed += 1;
let string_slice = &self.input[span.start..span.end];
let content = extract_string_content(string_slice)
.map_err(|_| Error::UnterminatedString(span.start))?;
if content.len() <= 16 {
self.stats.compact_string_optimizations += 1;
}
Ok(Value::String(content.to_string()))
}
fn parse_number_optimized(&mut self, span: Span) -> Result<Value> {
super::number::parse_number_token(self.input, span)
}
fn parse_array_optimized(&mut self) -> Result<Value> {
self.stats.arrays_parsed += 1;
self.depth += 1;
if self.depth > self.options.max_depth {
return Err(Error::DepthLimitExceeded(0));
}
let mut elements = Vec::new();
let mut first = true;
let estimated_size = self.estimate_array_size();
if estimated_size <= 8 {
self.stats.small_vec_optimizations += 1;
}
elements.reserve(estimated_size.min(16)); self.stats.presized_collections += 1;
loop {
let (next_token, span) = self.peek_token()?;
if next_token == Token::Newline {
self.next_token()?;
continue;
}
if next_token == Token::RightBracket {
self.next_token()?; break;
}
if !first {
if next_token == Token::Comma {
self.next_token()?; } else if self.options.newline_as_comma && next_token == Token::Newline {
self.next_token()?; } else {
return Err(Error::Expected {
expected: "comma or ]".to_string(),
found: format!("{next_token:?}"),
position: span.start,
});
}
}
let (value_token, value_span) = self.next_token()?;
let value = self.parse_value((value_token, value_span))?;
elements.push(value);
first = false;
}
self.depth -= 1;
Ok(Value::Array(elements))
}
fn parse_object_optimized(&mut self) -> Result<Value> {
self.stats.objects_parsed += 1;
self.depth += 1;
if self.depth > self.options.max_depth {
return Err(Error::DepthLimitExceeded(0));
}
let estimated_size = self.estimate_object_size();
let mut object = FxHashMap::with_capacity_and_hasher(estimated_size, Default::default());
self.stats.presized_collections += 1;
let mut first = true;
loop {
let (next_token, span) = self.peek_token()?;
if next_token == Token::Newline {
self.next_token()?;
continue;
}
if next_token == Token::RightBrace {
self.next_token()?; break;
}
if !first {
if next_token == Token::Comma {
self.next_token()?; } else if self.options.newline_as_comma && next_token == Token::Newline {
self.next_token()?; } else {
return Err(Error::Expected {
expected: "comma or }".to_string(),
found: format!("{next_token:?}"),
position: span.start,
});
}
}
loop {
let (next_token, _) = self.peek_token()?;
if next_token == Token::Newline {
self.next_token()?;
} else {
break;
}
}
let (key_token, key_span) = self.next_token()?;
let key = match key_token {
Token::String => self.parse_string_key(key_span)?,
Token::UnquotedString if self.options.allow_unquoted_keys => {
let key_slice = &self.input[key_span.start..key_span.end];
key_slice.to_string()
}
_ => {
return Err(Error::Expected {
expected: "string key".to_string(),
found: format!("{key_token:?}"),
position: key_span.start,
});
}
};
let (colon_token, colon_span) = self.next_token()?;
if colon_token != Token::Colon {
return Err(Error::Expected {
expected: "colon".to_string(),
found: format!("{colon_token:?}"),
position: colon_span.start,
});
}
let (value_token, value_span) = self.next_token()?;
let value = self.parse_value((value_token, value_span))?;
object.insert(key, value);
first = false;
}
self.depth -= 1;
Ok(Value::Object(object))
}
fn parse_string_key(&mut self, span: Span) -> Result<String> {
let string_slice = &self.input[span.start..span.end];
let content = extract_string_content(string_slice)
.map_err(|_| Error::UnterminatedString(span.start))?;
Ok(content.to_string())
}
fn estimate_array_size(&self) -> usize {
4
}
fn estimate_object_size(&self) -> usize {
4
}
}
pub fn parse_optimized_v3(input: &str) -> Result<Value> {
let mut parser = OptimizedParserV3::new(input, ParserOptions::default());
parser.parse()
}
pub fn parse_optimized_v3_with_options(input: &str, options: ParserOptions) -> Result<Value> {
let mut parser = OptimizedParserV3::new(input, options);
parser.parse()
}
pub fn parse_v3_with_stats(
input: &str,
options: ParserOptions,
) -> Result<(Value, ParserStats, AllocationStats)> {
let mut parser = OptimizedParserV3::new(input, options);
let value = parser.parse()?;
let stats = parser.stats().clone();
let alloc_stats = parser.allocation_stats();
Ok((value, stats, alloc_stats))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_optimized_v3_parser_simple() {
let input = r#"{"name": "test", "values": [1, 2, 3]}"#;
let result = parse_optimized_v3(input);
assert!(result.is_ok());
let value = result.unwrap();
match value {
Value::Object(obj) => {
assert!(obj.contains_key("name"));
assert!(obj.contains_key("values"));
}
_ => panic!("Expected object"),
}
}
#[test]
fn test_parser_v3_with_stats() {
let input = r#"{"items": [1, 2, 3, 4], "meta": {"count": 4}}"#;
let result = parse_v3_with_stats(input, ParserOptions::default());
assert!(result.is_ok());
let (_, stats, _) = result.unwrap();
assert!(stats.objects_parsed >= 2); assert!(stats.arrays_parsed >= 1); assert!(stats.small_vec_optimizations > 0);
assert!(stats.presized_collections > 0);
}
#[test]
fn test_small_vector_optimization() {
let input = r#"[1, 2]"#; let result = parse_v3_with_stats(input, ParserOptions::default());
assert!(result.is_ok());
let (_, stats, _) = result.unwrap();
assert!(stats.small_vec_optimizations > 0);
}
}