#[macro_use]
mod state_machine;
mod lexer;
mod tag_scanner;
mod tree_builder_simulator;
use self::lexer::Lexer;
pub use self::lexer::{
AttributeBuffer, AttributeOutline, Lexeme, LexemeSink, NonTagContentLexeme,
NonTagContentTokenOutline, TagLexeme, TagTokenOutline,
};
use self::state_machine::{ActionError, ParsingTermination, StateMachine};
pub use self::tag_scanner::TagHintSink;
use self::tag_scanner::TagScanner;
pub use self::tree_builder_simulator::ParsingAmbiguityError;
use self::tree_builder_simulator::{TreeBuilderFeedback, TreeBuilderSimulator};
use crate::rewriter::RewritingError;
use cfg_if::cfg_if;
#[derive(Clone, Copy, Debug)]
pub enum ParserDirective {
WherePossibleScanForTagsOnly,
Lex,
}
pub struct ParserContext<S> {
output_sink: S,
tree_builder_simulator: TreeBuilderSimulator,
}
pub trait ParserOutputSink: LexemeSink + TagHintSink {}
pub struct Parser<S: ParserOutputSink> {
lexer: Lexer<S>,
tag_scanner: TagScanner<S>,
current_directive: ParserDirective,
context: ParserContext<S>,
}
impl<S: ParserOutputSink> Parser<S> {
pub fn new(output_sink: S, initial_directive: ParserDirective, strict: bool) -> Self {
let context = ParserContext {
output_sink,
tree_builder_simulator: TreeBuilderSimulator::new(strict),
};
Parser {
lexer: Lexer::new(),
tag_scanner: TagScanner::new(),
current_directive: initial_directive,
context,
}
}
pub fn parse(&mut self, input: &[u8], last: bool) -> Result<usize, RewritingError> {
use ActionError::*;
let mut parse_result = match self.current_directive {
ParserDirective::WherePossibleScanForTagsOnly => {
self.tag_scanner
.run_parsing_loop(&mut self.context, input, last)
}
ParserDirective::Lex => self.lexer.run_parsing_loop(&mut self.context, input, last),
};
loop {
match parse_result {
Err(ParsingTermination::EndOfInput {
consumed_byte_count,
}) => {
return Ok(consumed_byte_count);
}
Err(ParsingTermination::ActionError(ParserDirectiveChangeRequired(
new_directive,
sm_bookmark,
))) => {
self.current_directive = new_directive;
trace!(@continue_from_bookmark sm_bookmark, self.current_directive, input);
parse_result = match self.current_directive {
ParserDirective::WherePossibleScanForTagsOnly => self
.tag_scanner
.continue_from_bookmark(&mut self.context, input, last, sm_bookmark),
ParserDirective::Lex => self.lexer.continue_from_bookmark(
&mut self.context,
input,
last,
sm_bookmark,
),
};
}
Err(ParsingTermination::ActionError(RewritingError(err))) => return Err(err),
Ok(unreachable) => match unreachable {},
}
}
}
pub fn get_dispatcher(&mut self) -> &mut S {
&mut self.context.output_sink
}
}
cfg_if! {
if #[cfg(feature = "integration_test")] {
use crate::html::{LocalNameHash, TextType};
impl<S: ParserOutputSink> Parser<S> {
pub fn switch_text_type(&mut self, text_type: TextType) {
match self.current_directive {
ParserDirective::WherePossibleScanForTagsOnly => {
self.tag_scanner.switch_text_type(text_type)
}
ParserDirective::Lex => self.lexer.switch_text_type(text_type),
}
}
pub fn set_last_start_tag_name_hash(&mut self, name_hash: LocalNameHash) {
match self.current_directive {
ParserDirective::WherePossibleScanForTagsOnly => {
self.tag_scanner.set_last_start_tag_name_hash(name_hash)
}
ParserDirective::Lex => self.lexer.set_last_start_tag_name_hash(name_hash),
}
}
}
}
}