use vize_l0::{Allocator, SourceRoot, Span, Vec};
use super::{Document, LexErrorCode, LexOptions, Lexer, ProfileKind, QuoteType};
use crate::markup::entity::DecodedEntity;
mod sink;
mod structure;
pub use structure::{
DocumentHtmlAttribute, DocumentHtmlAttributes, DocumentHtmlElement, DocumentHtmlRefusal,
DocumentHtmlStructure,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DocumentTokenKind {
Text,
TextEntity,
Interpolation,
OpenTagName,
OpenTagEnd,
SelfClosingTag,
CloseTagName,
AttributeName,
AttributeNameEnd,
AttributeData,
AttributeEntity,
AttributeEnd(QuoteType),
DirectiveName,
DirectiveArgument,
DirectiveModifier,
Comment,
Cdata,
ProcessingInstruction,
Declaration {
terminated: bool,
},
DeclarationRecovery {
terminated: bool,
},
}
#[derive(Debug)]
struct Event {
kind: RecordedKind,
span: Span,
}
#[derive(Debug)]
enum RecordedKind {
Token(DocumentTokenKind),
TextEntity(DecodedEntity),
AttributeEntity(DecodedEntity),
}
impl Event {
fn kind(&self) -> DocumentTokenKind {
match self.kind {
RecordedKind::Token(kind) => kind,
RecordedKind::TextEntity(_) => DocumentTokenKind::TextEntity,
RecordedKind::AttributeEntity(_) => DocumentTokenKind::AttributeEntity,
}
}
fn decoded_entity(&self) -> Option<DecodedEntity> {
match self.kind {
RecordedKind::TextEntity(value) | RecordedKind::AttributeEntity(value) => Some(value),
RecordedKind::Token(_) => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DocumentLexicalError {
pub code: LexErrorCode,
pub offset: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DocumentTreePolicy {
FoldNameCase,
HtmlSelfClosing,
ImpliedEndTags,
TableContentModel,
}
const UNFINISHED_TREE_POLICIES: [DocumentTreePolicy; 4] = [
DocumentTreePolicy::FoldNameCase,
DocumentTreePolicy::HtmlSelfClosing,
DocumentTreePolicy::ImpliedEndTags,
DocumentTreePolicy::TableContentModel,
];
pub struct NativeDocument<'a> {
allocator: &'a Allocator,
root: SourceRoot<'a>,
events: Vec<'a, Event>,
errors: Vec<'a, DocumentLexicalError>,
normal_end: bool,
declaration_refusal: Option<DocumentLexicalRefusal>,
}
impl core::fmt::Debug for NativeDocument<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("NativeDocument")
.field("root", &self.root)
.field("events", &self.events)
.field("errors", &self.errors)
.field("normal_end", &self.normal_end)
.field("declaration_refusal", &self.declaration_refusal)
.finish_non_exhaustive()
}
}
impl<'a> NativeDocument<'a> {
#[must_use]
pub fn lex_in(allocator: &'a Allocator, root: SourceRoot<'a>) -> Self {
let mut events = Vec::new_in(&allocator);
let mut errors = Vec::new_in(&allocator);
let recorder = sink::Recorder {
events: &mut events,
errors: &mut errors,
end_calls: 0,
declaration_refusal: None,
};
let mut lexer = Lexer::<Document, _>::new(root.source(), recorder, LexOptions::default());
lexer.run();
let normal_end = lexer.normal_end_state();
let recorder = lexer.into_sink();
assert_eq!(recorder.end_calls, 1);
let declaration_refusal = recorder.declaration_refusal;
Self {
allocator,
root,
events,
errors,
normal_end,
declaration_refusal,
}
}
#[must_use]
pub fn root(&self) -> SourceRoot<'a> {
self.root
}
#[must_use]
pub fn source(&self) -> &'a str {
self.root.source()
}
#[must_use]
pub fn tokens(&self) -> impl ExactSizeIterator<Item = DocumentToken<'_, 'a>> {
self.events
.iter()
.map(|event| DocumentToken { owner: self, event })
}
#[must_use]
pub fn errors(&self) -> &[DocumentLexicalError] {
&self.errors
}
pub fn html_structure(&self) -> Result<DocumentHtmlStructure<'_, 'a>, DocumentHtmlRefusal> {
structure::construct(self)
}
#[must_use]
pub fn unfinished_tree_policies(&self) -> &[DocumentTreePolicy; 4] {
&UNFINISHED_TREE_POLICIES
}
pub fn normal_completion(
&self,
) -> Result<DocumentLexicalCompletion<'_, 'a>, DocumentLexicalRefusal> {
if !self.errors.is_empty() {
return Err(DocumentLexicalRefusal::RecoveredSyntax);
}
if let Some(refusal) = self.declaration_refusal {
return Err(refusal);
}
if !self.normal_end {
return Err(DocumentLexicalRefusal::PendingSyntax);
}
Ok(DocumentLexicalCompletion { owner: self })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DocumentLexicalRefusal {
RecoveredSyntax,
UnterminatedDeclaration,
RecoveredDeclaration,
PendingSyntax,
}
#[derive(Debug)]
pub struct DocumentLexicalCompletion<'o, 'a> {
owner: &'o NativeDocument<'a>,
}
impl<'o, 'a> DocumentLexicalCompletion<'o, 'a> {
#[must_use]
pub fn owner(&self) -> &'o NativeDocument<'a> {
self.owner
}
#[must_use]
pub fn profile(&self) -> ProfileKind {
ProfileKind::Document
}
}
#[derive(Debug)]
pub struct DocumentToken<'o, 'a> {
owner: &'o NativeDocument<'a>,
event: &'o Event,
}
impl<'o, 'a> DocumentToken<'o, 'a> {
#[must_use]
pub fn owner(&self) -> &'o NativeDocument<'a> {
self.owner
}
#[must_use]
pub fn kind(&self) -> DocumentTokenKind {
self.event.kind()
}
#[must_use]
pub fn decoded_entity(&self) -> Option<DecodedEntity> {
self.event.decoded_entity()
}
#[must_use]
pub fn span(&self) -> Span {
self.event.span
}
#[must_use]
pub fn source(&self) -> Option<&'a str> {
self.owner
.source()
.get(self.event.span.start as usize..self.event.span.end as usize)
}
}
#[cfg(test)]
mod tests;
#[cfg(test)]
mod entity_tests;