use vize_l0::{Allocator, SourceRoot, Span, Vec};
use super::{Document, LexErrorCode, LexOptions, Lexer, ProfileKind, QuoteType};
mod sink;
#[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: DocumentTokenKind,
span: Span,
}
#[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,
];
#[derive(Debug)]
pub struct NativeDocument<'a> {
root: SourceRoot<'a>,
events: Vec<'a, Event>,
errors: Vec<'a, DocumentLexicalError>,
normal_end: bool,
declaration_refusal: Option<DocumentLexicalRefusal>,
}
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 {
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
}
#[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 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;