aboutsummaryrefslogtreecommitdiff
path: root/src/tokenizer.rs
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2023-08-28 19:04:58 +0200
committerMartin Fischer <martin@push-f.com>2023-09-03 23:00:05 +0200
commitf31bffb8426f04aaadea911e7c42b130a9ee80a5 (patch)
tree9e6e999f7a23baa797e2e7c99d0c6445866b06db /src/tokenizer.rs
parent5600dd2fd373879bede0949544cde71c232eb4f4 (diff)
fix!: remove adjusted_current_node_present_and_not_in_html_namespace
Conceptually the tokenizer emits tokens, which are then handled in the tree construction stage (which this crate doesn't yet implement). While the tokenizer can operate almost entirely based on its state (which may be changed via Tokenizer::set_state) and its internal state, there is the exception of the 'Markup declaration open state'[1], the third condition of which depends on the "adjusted current node", which in turn depends on the "stack of open elements" only known to the tree constructor. In 82898967320f90116bbc686ab7ffc2f61ff456c4 I tried to address this by adding the adjusted_current_node_present_and_not_in_html_namespace method to the Emitter trait. What I missed was that adding this method to the Emitter trait effectively crippled the composability of the API. You should be able to do the following: struct TreeConstructor<R, O> { tokenizer: Tokenizer<R, O, SomeEmitter<O>>, stack_of_open_elements: Vec<NodeId>, // ... } However this doesn't work if the implementation of SomeEmitter depends on the stack_of_open_elements field. This commits remedies this oversight by removing this method and instead making the Tokenizer yield values of a new Event enum: enum Event<T> { Token(T), CdataOpen } Event::CdataOpen signals that the new Tokenizer::handle_cdata_open method has to be called, which accepts a CdataAction: enum CdataAction { Cdata, BogusComment } the variants of which correspond exactly to the possible outcomes of the third condition of the 'Markup declaration open state'. Removing this method also has the added benefit that the DefaultEmitter is now again spec-compliant, which lets us expose it again in the next commit in good conscience (previously it just hard-coded the method implementation to return false, which is why I had removed the DefaultEmitter from the public API in the last release). [1]: https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
Diffstat (limited to 'src/tokenizer.rs')
-rw-r--r--src/tokenizer.rs37
1 files changed, 35 insertions, 2 deletions
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index 96d1c34..5b11db0 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -79,6 +79,38 @@ impl<R: Reader + Position<O>, O: Offset, E: Emitter<O>> Tokenizer<R, O, E> {
naively_switch_state: false,
}
}
+
+ /// To be called when the tokenizer iterator implementation yields [`Event::CdataOpen`].
+ ///
+ /// For spec-compliant parsing *action* must be [`CdataAction::Cdata`],
+ /// if there is an _adjusted current node_ and it is not an element in
+ /// the HTML namespace, or [`CdataAction::BogusComment`] otherwise
+ /// (as per the third condition under [Markup declaration open state]).
+ ///
+ /// [Markup declaration open state]: https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
+ pub fn handle_cdata_open(&mut self, action: CdataAction) {
+ machine::handle_cdata_open(self, action);
+ }
+}
+
+/// Used by [`Tokenizer::handle_cdata_open`] to determine how to process `<![CDATA[`
+///
+/// (Since as per the spec this depends on the _adjusted current node_).
+pub enum CdataAction {
+ /// Process it as CDATA.
+ Cdata,
+ /// Process it as a bogus comment.
+ BogusComment,
+}
+
+/// An event yielded by the [`Iterator`] implementation for the [`Tokenizer`].
+#[derive(Debug)]
+pub enum Event<T> {
+ /// A token emitted by the [`Emitter`].
+ Token(T),
+ /// The state machine encountered `<![CDATA[`. You must call [`Tokenizer::handle_cdata_open`],
+ /// before advancing the tokenizer iterator again.
+ CdataOpen,
}
/// The states you can set the tokenizer to.
@@ -307,12 +339,12 @@ where
R: Reader + Position<O>,
E: Emitter<O>,
{
- type Item = Result<E::Token, R::Error>;
+ type Item = Result<Event<E::Token>, R::Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(token) = self.emitter.pop_token() {
- return Some(Ok(token));
+ return Some(Ok(Event::Token(token)));
}
if self.eof {
@@ -326,6 +358,7 @@ where
self.eof = true;
self.emitter.emit_eof();
}
+ Ok(ControlToken::CdataOpen) => return Some(Ok(Event::CdataOpen)),
}
}
}