use crate::ast::Token;
use crate::error::{Result, Span};
pub trait JsonLexer {
fn position(&self) -> usize;
fn next_token(&mut self) -> Result<(Token, Span)>;
fn peek_token(&mut self) -> Result<&(Token, Span)>;
fn span_text(&self, span: &Span) -> &str;
fn line_col(&self) -> (usize, usize);
fn is_eof(&self) -> bool;
fn stats(&self) -> LexerStats {
LexerStats::default()
}
}
#[derive(Debug, Default, Clone)]
pub struct LexerStats {
pub tokens_count: usize,
pub bytes_processed: usize,
pub time_ns: u64,
pub errors_count: usize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LexerMode {
Standard,
Strict,
Forgiving,
Streaming,
}
#[derive(Debug, Clone)]
pub struct LexerConfig {
pub mode: LexerMode,
pub max_depth: usize,
pub track_positions: bool,
pub collect_stats: bool,
pub buffer_size: usize,
}
impl Default for LexerConfig {
fn default() -> Self {
LexerConfig {
mode: LexerMode::Forgiving,
max_depth: 128,
track_positions: true,
collect_stats: false,
buffer_size: 8192,
}
}
}
pub mod debug_lexer;
pub mod fast_lexer;
pub mod logos_lexer;
pub use debug_lexer::DebugLexer;
pub use fast_lexer::FastLexer;
pub use logos_lexer::{Lexer, LogosLexer};
pub fn create_lexer<'a>(input: &'a str, config: LexerConfig) -> Box<dyn JsonLexer + 'a> {
match config.mode {
LexerMode::Standard | LexerMode::Forgiving => {
if config.collect_stats || config.track_positions {
Box::new(DebugLexer::new(input, config))
} else {
Box::new(FastLexer::new(input, config))
}
}
LexerMode::Strict => Box::new(LogosLexer::new(input)),
LexerMode::Streaming => Box::new(FastLexer::new(input, config)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lexer_creation() {
let input = r#"{"key": "value"}"#;
let config = LexerConfig::default();
let lexer = create_lexer(input, config);
assert_eq!(lexer.position(), 0);
let strict_config = LexerConfig {
mode: LexerMode::Strict,
..Default::default()
};
let strict_lexer = create_lexer(input, strict_config);
assert!(!strict_lexer.is_eof());
}
}