diff --git a/src/iter/all_ast_node_iter.rs b/src/iter/all_ast_node_iter.rs new file mode 100644 index 0000000..1598990 --- /dev/null +++ b/src/iter/all_ast_node_iter.rs @@ -0,0 +1,102 @@ +use std::collections::VecDeque; + +use super::ast_node::AstNode; +use super::ast_node_iter::AstNodeIter; + +pub struct AllAstNodeIter<'r, 's> { + root: Option>, + queue: VecDeque>, +} + +impl<'r, 's> Iterator for AllAstNodeIter<'r, 's> { + type Item = AstNode<'r, 's>; + + fn next(&mut self) -> Option { + if let Some(root) = self.root.take() { + self.queue.push_back(AstNodeIter::from_ast_node(&root)); + return Some(root); + } + while let Some(child) = self.queue.front_mut() { + let next_elem_this_iter = match child { + AstNodeIter::Document(ref mut i) => i.next(), + AstNodeIter::Heading(ref mut i) => i.next(), + AstNodeIter::Section(ref mut i) => i.next(), + AstNodeIter::Paragraph(ref mut i) => i.next(), + AstNodeIter::PlainList(ref mut i) => i.next(), + AstNodeIter::PlainListItem(ref mut i) => i.next(), + AstNodeIter::GreaterBlock(ref mut i) => i.next(), + AstNodeIter::DynamicBlock(ref mut i) => i.next(), + AstNodeIter::FootnoteDefinition(ref mut i) => i.next(), + AstNodeIter::Comment(ref mut i) => i.next(), + AstNodeIter::Drawer(ref mut i) => i.next(), + AstNodeIter::PropertyDrawer(ref mut i) => i.next(), + AstNodeIter::NodeProperty(ref mut i) => i.next(), + AstNodeIter::Table(ref mut i) => i.next(), + AstNodeIter::TableRow(ref mut i) => i.next(), + AstNodeIter::VerseBlock(ref mut i) => i.next(), + AstNodeIter::CommentBlock(ref mut i) => i.next(), + AstNodeIter::ExampleBlock(ref mut i) => i.next(), + AstNodeIter::ExportBlock(ref mut i) => i.next(), + AstNodeIter::SrcBlock(ref mut i) => i.next(), + AstNodeIter::Clock(ref mut i) => i.next(), + AstNodeIter::DiarySexp(ref mut i) => i.next(), + AstNodeIter::Planning(ref mut i) => i.next(), + AstNodeIter::FixedWidthArea(ref mut i) => i.next(), + AstNodeIter::HorizontalRule(ref mut i) => i.next(), + AstNodeIter::Keyword(ref mut i) => i.next(), + AstNodeIter::BabelCall(ref mut i) => i.next(), + AstNodeIter::LatexEnvironment(ref mut i) => i.next(), + AstNodeIter::Bold(ref mut i) => i.next(), + AstNodeIter::Italic(ref mut i) => i.next(), + AstNodeIter::Underline(ref mut i) => i.next(), + AstNodeIter::StrikeThrough(ref mut i) => i.next(), + AstNodeIter::Code(ref mut i) => i.next(), + AstNodeIter::Verbatim(ref mut i) => i.next(), + AstNodeIter::PlainText(ref mut i) => i.next(), + AstNodeIter::RegularLink(ref mut i) => i.next(), + AstNodeIter::RadioLink(ref mut i) => i.next(), + AstNodeIter::RadioTarget(ref mut i) => i.next(), + AstNodeIter::PlainLink(ref mut i) => i.next(), + AstNodeIter::AngleLink(ref mut i) => i.next(), + AstNodeIter::OrgMacro(ref mut i) => i.next(), + AstNodeIter::Entity(ref mut i) => i.next(), + AstNodeIter::LatexFragment(ref mut i) => i.next(), + AstNodeIter::ExportSnippet(ref mut i) => i.next(), + AstNodeIter::FootnoteReference(ref mut i) => i.next(), + AstNodeIter::Citation(ref mut i) => i.next(), + AstNodeIter::CitationReference(ref mut i) => i.next(), + AstNodeIter::InlineBabelCall(ref mut i) => i.next(), + AstNodeIter::InlineSourceBlock(ref mut i) => i.next(), + AstNodeIter::LineBreak(ref mut i) => i.next(), + AstNodeIter::Target(ref mut i) => i.next(), + AstNodeIter::StatisticsCookie(ref mut i) => i.next(), + AstNodeIter::Subscript(ref mut i) => i.next(), + AstNodeIter::Superscript(ref mut i) => i.next(), + AstNodeIter::TableCell(ref mut i) => i.next(), + AstNodeIter::Timestamp(ref mut i) => i.next(), + }; + if let Some(next_elem_this_iter) = next_elem_this_iter { + self.queue + .push_back(AstNodeIter::from_ast_node(&next_elem_this_iter)); + return Some(next_elem_this_iter); + } else { + self.queue.pop_front(); + } + } + + None + } +} + +impl<'r, 's> IntoIterator for AstNode<'r, 's> { + type Item = AstNode<'r, 's>; + + type IntoIter = AllAstNodeIter<'r, 's>; + + fn into_iter(self) -> Self::IntoIter { + AllAstNodeIter { + root: Some(self), + queue: VecDeque::new(), + } + } +} diff --git a/src/iter/ast_node.rs b/src/iter/ast_node.rs new file mode 100644 index 0000000..0efae5f --- /dev/null +++ b/src/iter/ast_node.rs @@ -0,0 +1,251 @@ +use super::macros::to_ast_node; +use crate::types::AngleLink; +use crate::types::BabelCall; +use crate::types::Bold; +use crate::types::Citation; +use crate::types::CitationReference; +use crate::types::Clock; +use crate::types::Code; +use crate::types::Comment; +use crate::types::CommentBlock; +use crate::types::DiarySexp; +use crate::types::Document; +use crate::types::DocumentElement; +use crate::types::Drawer; +use crate::types::DynamicBlock; +use crate::types::Element; +use crate::types::Entity; +use crate::types::ExampleBlock; +use crate::types::ExportBlock; +use crate::types::ExportSnippet; +use crate::types::FixedWidthArea; +use crate::types::FootnoteDefinition; +use crate::types::FootnoteReference; +use crate::types::GreaterBlock; +use crate::types::Heading; +use crate::types::HorizontalRule; +use crate::types::InlineBabelCall; +use crate::types::InlineSourceBlock; +use crate::types::Italic; +use crate::types::Keyword; +use crate::types::LatexEnvironment; +use crate::types::LatexFragment; +use crate::types::LineBreak; +use crate::types::NodeProperty; +use crate::types::Object; +use crate::types::OrgMacro; +use crate::types::Paragraph; +use crate::types::PlainLink; +use crate::types::PlainList; +use crate::types::PlainListItem; +use crate::types::PlainText; +use crate::types::Planning; +use crate::types::PropertyDrawer; +use crate::types::RadioLink; +use crate::types::RadioTarget; +use crate::types::RegularLink; +use crate::types::Section; +use crate::types::SrcBlock; +use crate::types::StatisticsCookie; +use crate::types::StrikeThrough; +use crate::types::Subscript; +use crate::types::Superscript; +use crate::types::Table; +use crate::types::TableCell; +use crate::types::TableRow; +use crate::types::Target; +use crate::types::Timestamp; +use crate::types::Underline; +use crate::types::Verbatim; +use crate::types::VerseBlock; + +pub enum AstNode<'r, 's> { + // Document Nodes + Document(&'r Document<'s>), + Heading(&'r Heading<'s>), + Section(&'r Section<'s>), + // Elements + Paragraph(&'r Paragraph<'s>), + PlainList(&'r PlainList<'s>), + PlainListItem(&'r PlainListItem<'s>), + GreaterBlock(&'r GreaterBlock<'s>), + DynamicBlock(&'r DynamicBlock<'s>), + FootnoteDefinition(&'r FootnoteDefinition<'s>), + Comment(&'r Comment<'s>), + Drawer(&'r Drawer<'s>), + PropertyDrawer(&'r PropertyDrawer<'s>), + NodeProperty(&'r NodeProperty<'s>), + Table(&'r Table<'s>), + TableRow(&'r TableRow<'s>), + VerseBlock(&'r VerseBlock<'s>), + CommentBlock(&'r CommentBlock<'s>), + ExampleBlock(&'r ExampleBlock<'s>), + ExportBlock(&'r ExportBlock<'s>), + SrcBlock(&'r SrcBlock<'s>), + Clock(&'r Clock<'s>), + DiarySexp(&'r DiarySexp<'s>), + Planning(&'r Planning<'s>), + FixedWidthArea(&'r FixedWidthArea<'s>), + HorizontalRule(&'r HorizontalRule<'s>), + Keyword(&'r Keyword<'s>), + BabelCall(&'r BabelCall<'s>), + LatexEnvironment(&'r LatexEnvironment<'s>), + // Objects + Bold(&'r Bold<'s>), + Italic(&'r Italic<'s>), + Underline(&'r Underline<'s>), + StrikeThrough(&'r StrikeThrough<'s>), + Code(&'r Code<'s>), + Verbatim(&'r Verbatim<'s>), + PlainText(&'r PlainText<'s>), + RegularLink(&'r RegularLink<'s>), + RadioLink(&'r RadioLink<'s>), + RadioTarget(&'r RadioTarget<'s>), + PlainLink(&'r PlainLink<'s>), + AngleLink(&'r AngleLink<'s>), + OrgMacro(&'r OrgMacro<'s>), + Entity(&'r Entity<'s>), + LatexFragment(&'r LatexFragment<'s>), + ExportSnippet(&'r ExportSnippet<'s>), + FootnoteReference(&'r FootnoteReference<'s>), + Citation(&'r Citation<'s>), + CitationReference(&'r CitationReference<'s>), + InlineBabelCall(&'r InlineBabelCall<'s>), + InlineSourceBlock(&'r InlineSourceBlock<'s>), + LineBreak(&'r LineBreak<'s>), + Target(&'r Target<'s>), + StatisticsCookie(&'r StatisticsCookie<'s>), + Subscript(&'r Subscript<'s>), + Superscript(&'r Superscript<'s>), + TableCell(&'r TableCell<'s>), + Timestamp(&'r Timestamp<'s>), +} + +impl<'r, 's> From<&'r DocumentElement<'s>> for AstNode<'r, 's> { + fn from(value: &'r DocumentElement<'s>) -> Self { + match value { + DocumentElement::Heading(inner) => inner.into(), + DocumentElement::Section(inner) => inner.into(), + } + } +} + +impl<'r, 's> From<&'r Element<'s>> for AstNode<'r, 's> { + fn from(value: &'r Element<'s>) -> Self { + match value { + Element::Paragraph(inner) => inner.into(), + Element::PlainList(inner) => inner.into(), + Element::GreaterBlock(inner) => inner.into(), + Element::DynamicBlock(inner) => inner.into(), + Element::FootnoteDefinition(inner) => inner.into(), + Element::Comment(inner) => inner.into(), + Element::Drawer(inner) => inner.into(), + Element::PropertyDrawer(inner) => inner.into(), + Element::Table(inner) => inner.into(), + Element::VerseBlock(inner) => inner.into(), + Element::CommentBlock(inner) => inner.into(), + Element::ExampleBlock(inner) => inner.into(), + Element::ExportBlock(inner) => inner.into(), + Element::SrcBlock(inner) => inner.into(), + Element::Clock(inner) => inner.into(), + Element::DiarySexp(inner) => inner.into(), + Element::Planning(inner) => inner.into(), + Element::FixedWidthArea(inner) => inner.into(), + Element::HorizontalRule(inner) => inner.into(), + Element::Keyword(inner) => inner.into(), + Element::BabelCall(inner) => inner.into(), + Element::LatexEnvironment(inner) => inner.into(), + } + } +} + +impl<'r, 's> From<&'r Object<'s>> for AstNode<'r, 's> { + fn from(value: &'r Object<'s>) -> Self { + match value { + Object::Bold(inner) => inner.into(), + Object::Italic(inner) => inner.into(), + Object::Underline(inner) => inner.into(), + Object::StrikeThrough(inner) => inner.into(), + Object::Code(inner) => inner.into(), + Object::Verbatim(inner) => inner.into(), + Object::PlainText(inner) => inner.into(), + Object::RegularLink(inner) => inner.into(), + Object::RadioLink(inner) => inner.into(), + Object::RadioTarget(inner) => inner.into(), + Object::PlainLink(inner) => inner.into(), + Object::AngleLink(inner) => inner.into(), + Object::OrgMacro(inner) => inner.into(), + Object::Entity(inner) => inner.into(), + Object::LatexFragment(inner) => inner.into(), + Object::ExportSnippet(inner) => inner.into(), + Object::FootnoteReference(inner) => inner.into(), + Object::Citation(inner) => inner.into(), + Object::CitationReference(inner) => inner.into(), + Object::InlineBabelCall(inner) => inner.into(), + Object::InlineSourceBlock(inner) => inner.into(), + Object::LineBreak(inner) => inner.into(), + Object::Target(inner) => inner.into(), + Object::StatisticsCookie(inner) => inner.into(), + Object::Subscript(inner) => inner.into(), + Object::Superscript(inner) => inner.into(), + Object::Timestamp(inner) => inner.into(), + } + } +} + +to_ast_node!(&'r Document<'s>, AstNode::Document); +to_ast_node!(&'r Heading<'s>, AstNode::Heading); +to_ast_node!(&'r Section<'s>, AstNode::Section); +to_ast_node!(&'r Paragraph<'s>, AstNode::Paragraph); +to_ast_node!(&'r PlainList<'s>, AstNode::PlainList); +to_ast_node!(&'r PlainListItem<'s>, AstNode::PlainListItem); +to_ast_node!(&'r GreaterBlock<'s>, AstNode::GreaterBlock); +to_ast_node!(&'r DynamicBlock<'s>, AstNode::DynamicBlock); +to_ast_node!(&'r FootnoteDefinition<'s>, AstNode::FootnoteDefinition); +to_ast_node!(&'r Comment<'s>, AstNode::Comment); +to_ast_node!(&'r Drawer<'s>, AstNode::Drawer); +to_ast_node!(&'r PropertyDrawer<'s>, AstNode::PropertyDrawer); +to_ast_node!(&'r NodeProperty<'s>, AstNode::NodeProperty); +to_ast_node!(&'r Table<'s>, AstNode::Table); +to_ast_node!(&'r TableRow<'s>, AstNode::TableRow); +to_ast_node!(&'r VerseBlock<'s>, AstNode::VerseBlock); +to_ast_node!(&'r CommentBlock<'s>, AstNode::CommentBlock); +to_ast_node!(&'r ExampleBlock<'s>, AstNode::ExampleBlock); +to_ast_node!(&'r ExportBlock<'s>, AstNode::ExportBlock); +to_ast_node!(&'r SrcBlock<'s>, AstNode::SrcBlock); +to_ast_node!(&'r Clock<'s>, AstNode::Clock); +to_ast_node!(&'r DiarySexp<'s>, AstNode::DiarySexp); +to_ast_node!(&'r Planning<'s>, AstNode::Planning); +to_ast_node!(&'r FixedWidthArea<'s>, AstNode::FixedWidthArea); +to_ast_node!(&'r HorizontalRule<'s>, AstNode::HorizontalRule); +to_ast_node!(&'r Keyword<'s>, AstNode::Keyword); +to_ast_node!(&'r BabelCall<'s>, AstNode::BabelCall); +to_ast_node!(&'r LatexEnvironment<'s>, AstNode::LatexEnvironment); +to_ast_node!(&'r Bold<'s>, AstNode::Bold); +to_ast_node!(&'r Italic<'s>, AstNode::Italic); +to_ast_node!(&'r Underline<'s>, AstNode::Underline); +to_ast_node!(&'r StrikeThrough<'s>, AstNode::StrikeThrough); +to_ast_node!(&'r Code<'s>, AstNode::Code); +to_ast_node!(&'r Verbatim<'s>, AstNode::Verbatim); +to_ast_node!(&'r PlainText<'s>, AstNode::PlainText); +to_ast_node!(&'r RegularLink<'s>, AstNode::RegularLink); +to_ast_node!(&'r RadioLink<'s>, AstNode::RadioLink); +to_ast_node!(&'r RadioTarget<'s>, AstNode::RadioTarget); +to_ast_node!(&'r PlainLink<'s>, AstNode::PlainLink); +to_ast_node!(&'r AngleLink<'s>, AstNode::AngleLink); +to_ast_node!(&'r OrgMacro<'s>, AstNode::OrgMacro); +to_ast_node!(&'r Entity<'s>, AstNode::Entity); +to_ast_node!(&'r LatexFragment<'s>, AstNode::LatexFragment); +to_ast_node!(&'r ExportSnippet<'s>, AstNode::ExportSnippet); +to_ast_node!(&'r FootnoteReference<'s>, AstNode::FootnoteReference); +to_ast_node!(&'r Citation<'s>, AstNode::Citation); +to_ast_node!(&'r CitationReference<'s>, AstNode::CitationReference); +to_ast_node!(&'r InlineBabelCall<'s>, AstNode::InlineBabelCall); +to_ast_node!(&'r InlineSourceBlock<'s>, AstNode::InlineSourceBlock); +to_ast_node!(&'r LineBreak<'s>, AstNode::LineBreak); +to_ast_node!(&'r Target<'s>, AstNode::Target); +to_ast_node!(&'r StatisticsCookie<'s>, AstNode::StatisticsCookie); +to_ast_node!(&'r Subscript<'s>, AstNode::Subscript); +to_ast_node!(&'r Superscript<'s>, AstNode::Superscript); +to_ast_node!(&'r TableCell<'s>, AstNode::TableCell); +to_ast_node!(&'r Timestamp<'s>, AstNode::Timestamp); diff --git a/src/iter/ast_node_iter.rs b/src/iter/ast_node_iter.rs new file mode 100644 index 0000000..6c51c11 --- /dev/null +++ b/src/iter/ast_node_iter.rs @@ -0,0 +1,332 @@ +use std::marker::PhantomData; + +use super::ast_node::AstNode; +use super::macros::children_iter; +use super::macros::empty_iter; +use super::macros::multi_field_iter; +use crate::types::AngleLink; +use crate::types::BabelCall; +use crate::types::Bold; +use crate::types::Citation; +use crate::types::CitationReference; +use crate::types::Clock; +use crate::types::Code; +use crate::types::Comment; +use crate::types::CommentBlock; +use crate::types::DiarySexp; +use crate::types::Document; +use crate::types::DocumentElement; +use crate::types::Drawer; +use crate::types::DynamicBlock; +use crate::types::Element; +use crate::types::Entity; +use crate::types::ExampleBlock; +use crate::types::ExportBlock; +use crate::types::ExportSnippet; +use crate::types::FixedWidthArea; +use crate::types::FootnoteDefinition; +use crate::types::FootnoteReference; +use crate::types::GreaterBlock; +use crate::types::Heading; +use crate::types::HorizontalRule; +use crate::types::InlineBabelCall; +use crate::types::InlineSourceBlock; +use crate::types::Italic; +use crate::types::Keyword; +use crate::types::LatexEnvironment; +use crate::types::LatexFragment; +use crate::types::LineBreak; +use crate::types::NodeProperty; +use crate::types::Object; +use crate::types::OrgMacro; +use crate::types::Paragraph; +use crate::types::PlainLink; +use crate::types::PlainList; +use crate::types::PlainListItem; +use crate::types::PlainText; +use crate::types::Planning; +use crate::types::PropertyDrawer; +use crate::types::RadioLink; +use crate::types::RadioTarget; +use crate::types::RegularLink; +use crate::types::Section; +use crate::types::SrcBlock; +use crate::types::StatisticsCookie; +use crate::types::StrikeThrough; +use crate::types::Subscript; +use crate::types::Superscript; +use crate::types::Table; +use crate::types::TableCell; +use crate::types::TableRow; +use crate::types::Target; +use crate::types::Timestamp; +use crate::types::Underline; +use crate::types::Verbatim; +use crate::types::VerseBlock; + +/// Iterator over the AST nodes contained within the starting node. +/// +/// This only iterates over the children, not the starting node itself. So an AstNodeIter::PlainList would only return PlainListItems, not the PlainList. +/// +/// This only iterates over AST nodes, so an AstNodeIter::Heading would iterate over both the title and section contents, but it would not iterate over simple strings like the TODO keyword or priority. +pub(crate) enum AstNodeIter<'r, 's> { + // Document Nodes + Document(DocumentIter<'r, 's>), + Heading(HeadingIter<'r, 's>), + Section(SectionIter<'r, 's>), + // Elements + Paragraph(ParagraphIter<'r, 's>), + PlainList(PlainListIter<'r, 's>), + PlainListItem(PlainListItemIter<'r, 's>), + GreaterBlock(GreaterBlockIter<'r, 's>), + DynamicBlock(DynamicBlockIter<'r, 's>), + FootnoteDefinition(FootnoteDefinitionIter<'r, 's>), + Comment(CommentIter<'r, 's>), + Drawer(DrawerIter<'r, 's>), + PropertyDrawer(PropertyDrawerIter<'r, 's>), + NodeProperty(NodePropertyIter<'r, 's>), + Table(TableIter<'r, 's>), + TableRow(TableRowIter<'r, 's>), + VerseBlock(VerseBlockIter<'r, 's>), + CommentBlock(CommentBlockIter<'r, 's>), + ExampleBlock(ExampleBlockIter<'r, 's>), + ExportBlock(ExportBlockIter<'r, 's>), + SrcBlock(SrcBlockIter<'r, 's>), + Clock(ClockIter<'r, 's>), + DiarySexp(DiarySexpIter<'r, 's>), + Planning(PlanningIter<'r, 's>), + FixedWidthArea(FixedWidthAreaIter<'r, 's>), + HorizontalRule(HorizontalRuleIter<'r, 's>), + Keyword(KeywordIter<'r, 's>), + BabelCall(BabelCallIter<'r, 's>), + LatexEnvironment(LatexEnvironmentIter<'r, 's>), + // Objects + Bold(BoldIter<'r, 's>), + Italic(ItalicIter<'r, 's>), + Underline(UnderlineIter<'r, 's>), + StrikeThrough(StrikeThroughIter<'r, 's>), + Code(CodeIter<'r, 's>), + Verbatim(VerbatimIter<'r, 's>), + PlainText(PlainTextIter<'r, 's>), + RegularLink(RegularLinkIter<'r, 's>), + RadioLink(RadioLinkIter<'r, 's>), + RadioTarget(RadioTargetIter<'r, 's>), + PlainLink(PlainLinkIter<'r, 's>), + AngleLink(AngleLinkIter<'r, 's>), + OrgMacro(OrgMacroIter<'r, 's>), + Entity(EntityIter<'r, 's>), + LatexFragment(LatexFragmentIter<'r, 's>), + ExportSnippet(ExportSnippetIter<'r, 's>), + FootnoteReference(FootnoteReferenceIter<'r, 's>), + Citation(CitationIter<'r, 's>), + CitationReference(CitationReferenceIter<'r, 's>), + InlineBabelCall(InlineBabelCallIter<'r, 's>), + InlineSourceBlock(InlineSourceBlockIter<'r, 's>), + LineBreak(LineBreakIter<'r, 's>), + Target(TargetIter<'r, 's>), + StatisticsCookie(StatisticsCookieIter<'r, 's>), + Subscript(SubscriptIter<'r, 's>), + Superscript(SuperscriptIter<'r, 's>), + TableCell(TableCellIter<'r, 's>), + Timestamp(TimestampIter<'r, 's>), +} + +impl<'r, 's> AstNodeIter<'r, 's> { + pub(crate) fn from_ast_node(node: &AstNode<'r, 's>) -> AstNodeIter<'r, 's> { + match node { + AstNode::Document(inner) => AstNodeIter::Document(inner.into_iter()), + AstNode::Heading(inner) => AstNodeIter::Heading(inner.into_iter()), + AstNode::Section(inner) => AstNodeIter::Section(inner.into_iter()), + AstNode::Paragraph(inner) => AstNodeIter::Paragraph(inner.into_iter()), + AstNode::PlainList(inner) => AstNodeIter::PlainList(inner.into_iter()), + AstNode::PlainListItem(inner) => AstNodeIter::PlainListItem(inner.into_iter()), + AstNode::GreaterBlock(inner) => AstNodeIter::GreaterBlock(inner.into_iter()), + AstNode::DynamicBlock(inner) => AstNodeIter::DynamicBlock(inner.into_iter()), + AstNode::FootnoteDefinition(inner) => { + AstNodeIter::FootnoteDefinition(inner.into_iter()) + } + AstNode::Comment(inner) => AstNodeIter::Comment(inner.into_iter()), + AstNode::Drawer(inner) => AstNodeIter::Drawer(inner.into_iter()), + AstNode::PropertyDrawer(inner) => AstNodeIter::PropertyDrawer(inner.into_iter()), + AstNode::NodeProperty(inner) => AstNodeIter::NodeProperty(inner.into_iter()), + AstNode::Table(inner) => AstNodeIter::Table(inner.into_iter()), + AstNode::TableRow(inner) => AstNodeIter::TableRow(inner.into_iter()), + AstNode::VerseBlock(inner) => AstNodeIter::VerseBlock(inner.into_iter()), + AstNode::CommentBlock(inner) => AstNodeIter::CommentBlock(inner.into_iter()), + AstNode::ExampleBlock(inner) => AstNodeIter::ExampleBlock(inner.into_iter()), + AstNode::ExportBlock(inner) => AstNodeIter::ExportBlock(inner.into_iter()), + AstNode::SrcBlock(inner) => AstNodeIter::SrcBlock(inner.into_iter()), + AstNode::Clock(inner) => AstNodeIter::Clock(inner.into_iter()), + AstNode::DiarySexp(inner) => AstNodeIter::DiarySexp(inner.into_iter()), + AstNode::Planning(inner) => AstNodeIter::Planning(inner.into_iter()), + AstNode::FixedWidthArea(inner) => AstNodeIter::FixedWidthArea(inner.into_iter()), + AstNode::HorizontalRule(inner) => AstNodeIter::HorizontalRule(inner.into_iter()), + AstNode::Keyword(inner) => AstNodeIter::Keyword(inner.into_iter()), + AstNode::BabelCall(inner) => AstNodeIter::BabelCall(inner.into_iter()), + AstNode::LatexEnvironment(inner) => AstNodeIter::LatexEnvironment(inner.into_iter()), + AstNode::Bold(inner) => AstNodeIter::Bold(inner.into_iter()), + AstNode::Italic(inner) => AstNodeIter::Italic(inner.into_iter()), + AstNode::Underline(inner) => AstNodeIter::Underline(inner.into_iter()), + AstNode::StrikeThrough(inner) => AstNodeIter::StrikeThrough(inner.into_iter()), + AstNode::Code(inner) => AstNodeIter::Code(inner.into_iter()), + AstNode::Verbatim(inner) => AstNodeIter::Verbatim(inner.into_iter()), + AstNode::PlainText(inner) => AstNodeIter::PlainText(inner.into_iter()), + AstNode::RegularLink(inner) => AstNodeIter::RegularLink(inner.into_iter()), + AstNode::RadioLink(inner) => AstNodeIter::RadioLink(inner.into_iter()), + AstNode::RadioTarget(inner) => AstNodeIter::RadioTarget(inner.into_iter()), + AstNode::PlainLink(inner) => AstNodeIter::PlainLink(inner.into_iter()), + AstNode::AngleLink(inner) => AstNodeIter::AngleLink(inner.into_iter()), + AstNode::OrgMacro(inner) => AstNodeIter::OrgMacro(inner.into_iter()), + AstNode::Entity(inner) => AstNodeIter::Entity(inner.into_iter()), + AstNode::LatexFragment(inner) => AstNodeIter::LatexFragment(inner.into_iter()), + AstNode::ExportSnippet(inner) => AstNodeIter::ExportSnippet(inner.into_iter()), + AstNode::FootnoteReference(inner) => AstNodeIter::FootnoteReference(inner.into_iter()), + AstNode::Citation(inner) => AstNodeIter::Citation(inner.into_iter()), + AstNode::CitationReference(inner) => AstNodeIter::CitationReference(inner.into_iter()), + AstNode::InlineBabelCall(inner) => AstNodeIter::InlineBabelCall(inner.into_iter()), + AstNode::InlineSourceBlock(inner) => AstNodeIter::InlineSourceBlock(inner.into_iter()), + AstNode::LineBreak(inner) => AstNodeIter::LineBreak(inner.into_iter()), + AstNode::Target(inner) => AstNodeIter::Target(inner.into_iter()), + AstNode::StatisticsCookie(inner) => AstNodeIter::StatisticsCookie(inner.into_iter()), + AstNode::Subscript(inner) => AstNodeIter::Subscript(inner.into_iter()), + AstNode::Superscript(inner) => AstNodeIter::Superscript(inner.into_iter()), + AstNode::TableCell(inner) => AstNodeIter::TableCell(inner.into_iter()), + AstNode::Timestamp(inner) => AstNodeIter::Timestamp(inner.into_iter()), + } + } +} + +multi_field_iter!( + Document<'s>, + DocumentIter, + zeroth_section, + std::option::Iter<'r, Section<'s>>, + children, + std::slice::Iter<'r, Heading<'s>> +); +multi_field_iter!( + Heading<'s>, + HeadingIter, + title, + std::slice::Iter<'r, Object<'s>>, + children, + std::slice::Iter<'r, DocumentElement<'s>> +); +children_iter!(Section<'s>, SectionIter, std::slice::Iter<'r, Element<'s>>); +children_iter!( + Paragraph<'s>, + ParagraphIter, + std::slice::Iter<'r, Object<'s>> +); +children_iter!( + PlainList<'s>, + PlainListIter, + std::slice::Iter<'r, PlainListItem<'s>> +); +multi_field_iter!( + PlainListItem<'s>, + PlainListItemIter, + tag, + std::slice::Iter<'r, Object<'s>>, + children, + std::slice::Iter<'r, Element<'s>> +); +children_iter!( + GreaterBlock<'s>, + GreaterBlockIter, + std::slice::Iter<'r, Element<'s>> +); +children_iter!( + DynamicBlock<'s>, + DynamicBlockIter, + std::slice::Iter<'r, Element<'s>> +); +children_iter!( + FootnoteDefinition<'s>, + FootnoteDefinitionIter, + std::slice::Iter<'r, Element<'s>> +); +empty_iter!(Comment<'s>, CommentIter); +children_iter!(Drawer<'s>, DrawerIter, std::slice::Iter<'r, Element<'s>>); +children_iter!( + PropertyDrawer<'s>, + PropertyDrawerIter, + std::slice::Iter<'r, NodeProperty<'s>> +); +empty_iter!(NodeProperty<'s>, NodePropertyIter); +children_iter!(Table<'s>, TableIter, std::slice::Iter<'r, TableRow<'s>>); +children_iter!( + TableRow<'s>, + TableRowIter, + std::slice::Iter<'r, TableCell<'s>> +); +children_iter!( + VerseBlock<'s>, + VerseBlockIter, + std::slice::Iter<'r, Object<'s>> +); +empty_iter!(CommentBlock<'s>, CommentBlockIter); +empty_iter!(ExampleBlock<'s>, ExampleBlockIter); +empty_iter!(ExportBlock<'s>, ExportBlockIter); +empty_iter!(SrcBlock<'s>, SrcBlockIter); +empty_iter!(Clock<'s>, ClockIter); +empty_iter!(DiarySexp<'s>, DiarySexpIter); +empty_iter!(Planning<'s>, PlanningIter); +empty_iter!(FixedWidthArea<'s>, FixedWidthAreaIter); +empty_iter!(HorizontalRule<'s>, HorizontalRuleIter); +empty_iter!(Keyword<'s>, KeywordIter); +empty_iter!(BabelCall<'s>, BabelCallIter); +empty_iter!(LatexEnvironment<'s>, LatexEnvironmentIter); +children_iter!(Bold<'s>, BoldIter, std::slice::Iter<'r, Object<'s>>); +children_iter!(Italic<'s>, ItalicIter, std::slice::Iter<'r, Object<'s>>); +children_iter!( + Underline<'s>, + UnderlineIter, + std::slice::Iter<'r, Object<'s>> +); +children_iter!( + StrikeThrough<'s>, + StrikeThroughIter, + std::slice::Iter<'r, Object<'s>> +); +empty_iter!(Code<'s>, CodeIter); +empty_iter!(Verbatim<'s>, VerbatimIter); +empty_iter!(PlainText<'s>, PlainTextIter); +empty_iter!(RegularLink<'s>, RegularLinkIter); +children_iter!( + RadioLink<'s>, + RadioLinkIter, + std::slice::Iter<'r, Object<'s>> +); +children_iter!( + RadioTarget<'s>, + RadioTargetIter, + std::slice::Iter<'r, Object<'s>> +); +empty_iter!(PlainLink<'s>, PlainLinkIter); +empty_iter!(AngleLink<'s>, AngleLinkIter); +empty_iter!(OrgMacro<'s>, OrgMacroIter); +empty_iter!(Entity<'s>, EntityIter); +empty_iter!(LatexFragment<'s>, LatexFragmentIter); +empty_iter!(ExportSnippet<'s>, ExportSnippetIter); +multi_field_iter!( + FootnoteReference<'s>, + FootnoteReferenceIter, + definition, + std::slice::Iter<'r, Object<'s>>, +); +empty_iter!(Citation<'s>, CitationIter); +empty_iter!(CitationReference<'s>, CitationReferenceIter); +empty_iter!(InlineBabelCall<'s>, InlineBabelCallIter); +empty_iter!(InlineSourceBlock<'s>, InlineSourceBlockIter); +empty_iter!(LineBreak<'s>, LineBreakIter); +empty_iter!(Target<'s>, TargetIter); +empty_iter!(StatisticsCookie<'s>, StatisticsCookieIter); +empty_iter!(Subscript<'s>, SubscriptIter); +empty_iter!(Superscript<'s>, SuperscriptIter); +children_iter!( + TableCell<'s>, + TableCellIter, + std::slice::Iter<'r, Object<'s>> +); +empty_iter!(Timestamp<'s>, TimestampIter); diff --git a/src/iter/macros.rs b/src/iter/macros.rs new file mode 100644 index 0000000..3834d1b --- /dev/null +++ b/src/iter/macros.rs @@ -0,0 +1,114 @@ +/// Write the implementation of From<> to convert a borrow of the type to an AstNode +macro_rules! to_ast_node { + ($inp:ty, $enum:expr) => { + impl<'r, 's> From<$inp> for AstNode<'r, 's> { + fn from(value: $inp) -> Self { + $enum(value) + } + } + }; +} + +pub(crate) use to_ast_node; + +/// Create iterators for ast nodes where it only has to iterate over children +macro_rules! children_iter { + ($astnodetype:ty, $itertype:ident, $innertype:ty) => { + pub struct $itertype<'r, 's> { + next: $innertype, + } + + impl<'r, 's> Iterator for $itertype<'r, 's> { + type Item = AstNode<'r, 's>; + + fn next(&mut self) -> Option { + self.next.next().map(Into::::into) + } + } + + impl<'r, 's> IntoIterator for &'r $astnodetype { + type Item = AstNode<'r, 's>; + + type IntoIter = $itertype<'r, 's>; + + fn into_iter(self) -> Self::IntoIter { + $itertype { + next: self.children.iter(), + } + } + } + }; +} + +pub(crate) use children_iter; + +/// Create iterators for ast nodes that do not contain any ast node children. +macro_rules! empty_iter { + ($astnodetype:ty, $itertype:ident) => { + pub struct $itertype<'r, 's> { + phantom: PhantomData<&'r $astnodetype>, + } + + impl<'r, 's> Iterator for $itertype<'r, 's> { + type Item = AstNode<'r, 's>; + + fn next(&mut self) -> Option { + None + } + } + + impl<'r, 's> IntoIterator for &'r $astnodetype { + type Item = AstNode<'r, 's>; + + type IntoIter = $itertype<'r, 's>; + + fn into_iter(self) -> Self::IntoIter { + $itertype { + phantom: PhantomData, + } + } + } + }; +} + +pub(crate) use empty_iter; + +/// Create iterators for ast nodes where it has to iterate over multiple child lists. +macro_rules! multi_field_iter { + ($astnodetype:ty, $itertype:ident, $firstfieldname: ident, $firstinnertype:ty, $($fieldname: ident, $innertype:ty),*) => { + pub struct $itertype<'r, 's> { + $firstfieldname: $firstinnertype, + $( +$fieldname: $innertype, +),* + } + + impl<'r, 's> Iterator for $itertype<'r, 's> { + type Item = AstNode<'r, 's>; + + fn next(&mut self) -> Option { + self.$firstfieldname.next().map(Into::::into) + $( +.or_else(|| self.$fieldname.next().map(Into::::into)) +),* + } + } + + impl<'r, 's> IntoIterator for &'r $astnodetype { + type Item = AstNode<'r, 's>; + + type IntoIter = $itertype<'r, 's>; + + fn into_iter(self) -> Self::IntoIter { + $itertype { + $firstfieldname: self.$firstfieldname.iter(), + $( +$fieldname: self.$fieldname.iter(), +),* + } + } + } + }; +} + +pub(crate) use multi_field_iter; diff --git a/src/iter/mod.rs b/src/iter/mod.rs new file mode 100644 index 0000000..d820847 --- /dev/null +++ b/src/iter/mod.rs @@ -0,0 +1,5 @@ +mod all_ast_node_iter; +mod ast_node; +mod ast_node_iter; +mod macros; +pub(crate) use ast_node::AstNode; diff --git a/src/lib.rs b/src/lib.rs index bbcaf1f..c522cf6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod compare; mod context; mod error; +mod iter; pub mod parser; pub mod types; diff --git a/src/parser/document.rs b/src/parser/document.rs index 9c26a41..4d6a64b 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -7,8 +7,6 @@ use super::in_buffer_settings::apply_in_buffer_settings; use super::in_buffer_settings::scan_for_in_buffer_settings; use super::org_source::OrgSource; use super::section::zeroth_section; -use super::token::AllTokensIterator; -use super::token::Token; use super::util::get_consumed; use crate::context::parser_with_context; use crate::context::Context; @@ -19,6 +17,7 @@ use crate::context::RefContext; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; +use crate::iter::AstNode; use crate::parser::org_source::convert_error; use crate::parser::util::blank_line; use crate::types::Document; @@ -111,15 +110,14 @@ fn document_org_source<'b, 'g, 'r, 's>( _document(context, input).map(|(rem, out)| (Into::<&str>::into(rem), out))?; { // If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets. - let all_radio_targets: Vec<&Vec>> = document - .iter_tokens() - .filter_map(|tkn| match tkn { - Token::Object(obj) => Some(obj), - _ => None, - }) - .filter_map(|obj| match obj { - Object::RadioTarget(rt) => Some(rt), - _ => None, + let all_radio_targets: Vec<&Vec>> = Into::::into(&document) + .into_iter() + .filter_map(|ast_node| { + if let AstNode::RadioTarget(ast_node) = ast_node { + Some(ast_node) + } else { + None + } }) .map(|rt| &rt.children) .collect(); @@ -155,9 +153,3 @@ fn _document<'b, 'g, 'r, 's>( }, )) } - -impl<'s> Document<'s> { - fn iter_tokens<'r>(&'r self) -> impl Iterator> { - AllTokensIterator::new(Token::Document(self)) - } -} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b19f9ca..8729294 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -43,7 +43,6 @@ mod table; mod target; mod text_markup; mod timestamp; -mod token; mod util; pub use document::parse; pub use document::parse_with_settings; diff --git a/src/parser/token.rs b/src/parser/token.rs deleted file mode 100644 index 8479273..0000000 --- a/src/parser/token.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::collections::VecDeque; - -use crate::types::Document; -use crate::types::DocumentElement; -use crate::types::Element; -use crate::types::Heading; -use crate::types::NodeProperty; -use crate::types::Object; -use crate::types::PlainListItem; -use crate::types::Section; -use crate::types::TableCell; -use crate::types::TableRow; - -pub(crate) enum Token<'r, 's> { - Document(&'r Document<'s>), - Heading(&'r Heading<'s>), - Section(&'r Section<'s>), - Object(&'r Object<'s>), - Element(&'r Element<'s>), - PlainListItem(&'r PlainListItem<'s>), - TableRow(&'r TableRow<'s>), - TableCell(&'r TableCell<'s>), - NodeProperty(&'r NodeProperty<'s>), -} - -impl<'r, 's> Token<'r, 's> { - fn iter_tokens(&self) -> Box> + '_> { - match self { - Token::Document(document) => Box::new( - document - .zeroth_section - .iter() - .map(Token::Section) - .chain(document.children.iter().map(Token::Heading)), - ), - Token::Heading(heading) => Box::new(heading.title.iter().map(Token::Object).chain( - heading.children.iter().map(|de| match de { - DocumentElement::Heading(ref obj) => Token::Heading(obj), - DocumentElement::Section(ref obj) => Token::Section(obj), - }), - )), - Token::Section(section) => Box::new(section.children.iter().map(Token::Element)), - Token::Object(obj) => match obj { - Object::Bold(inner) => Box::new(inner.children.iter().map(Token::Object)), - Object::Italic(inner) => Box::new(inner.children.iter().map(Token::Object)), - Object::Underline(inner) => Box::new(inner.children.iter().map(Token::Object)), - Object::StrikeThrough(inner) => Box::new(inner.children.iter().map(Token::Object)), - Object::Code(_) => Box::new(std::iter::empty()), - Object::Verbatim(_) => Box::new(std::iter::empty()), - Object::PlainText(_) => Box::new(std::iter::empty()), - Object::RegularLink(_) => Box::new(std::iter::empty()), - Object::RadioLink(inner) => Box::new(inner.children.iter().map(Token::Object)), - Object::RadioTarget(inner) => Box::new(inner.children.iter().map(Token::Object)), - Object::PlainLink(_) => Box::new(std::iter::empty()), - Object::AngleLink(_) => Box::new(std::iter::empty()), - Object::OrgMacro(_) => Box::new(std::iter::empty()), - Object::Entity(_) => Box::new(std::iter::empty()), - Object::LatexFragment(_) => Box::new(std::iter::empty()), - Object::ExportSnippet(_) => Box::new(std::iter::empty()), - Object::FootnoteReference(inner) => { - Box::new(inner.definition.iter().map(Token::Object)) - } - Object::Citation(_) => Box::new(std::iter::empty()), // TODO: Iterate over children - Object::CitationReference(_) => Box::new(std::iter::empty()), // TODO: Iterate over children - Object::InlineBabelCall(_) => Box::new(std::iter::empty()), - Object::InlineSourceBlock(_) => Box::new(std::iter::empty()), - Object::LineBreak(_) => Box::new(std::iter::empty()), - Object::Target(_) => Box::new(std::iter::empty()), - Object::StatisticsCookie(_) => Box::new(std::iter::empty()), - Object::Subscript(_) => Box::new(std::iter::empty()), // TODO: Iterate over children - Object::Superscript(_) => Box::new(std::iter::empty()), // TODO: Iterate over children - Object::Timestamp(_) => Box::new(std::iter::empty()), - }, - Token::Element(elem) => match elem { - Element::Paragraph(inner) => Box::new(inner.children.iter().map(Token::Object)), - Element::PlainList(inner) => { - Box::new(inner.children.iter().map(Token::PlainListItem)) - } - Element::GreaterBlock(inner) => Box::new(inner.children.iter().map(Token::Element)), - Element::DynamicBlock(inner) => Box::new(inner.children.iter().map(Token::Element)), - Element::FootnoteDefinition(inner) => { - Box::new(inner.children.iter().map(Token::Element)) - } - Element::Comment(_) => Box::new(std::iter::empty()), - Element::Drawer(inner) => Box::new(inner.children.iter().map(Token::Element)), - Element::PropertyDrawer(inner) => { - Box::new(inner.children.iter().map(Token::NodeProperty)) - } - Element::Table(inner) => Box::new(inner.children.iter().map(Token::TableRow)), - Element::VerseBlock(inner) => Box::new(inner.children.iter().map(Token::Object)), - Element::CommentBlock(_) => Box::new(std::iter::empty()), - Element::ExampleBlock(_) => Box::new(std::iter::empty()), - Element::ExportBlock(_) => Box::new(std::iter::empty()), - Element::SrcBlock(_) => Box::new(std::iter::empty()), - Element::Clock(_) => Box::new(std::iter::empty()), - Element::DiarySexp(_) => Box::new(std::iter::empty()), - Element::Planning(_) => Box::new(std::iter::empty()), - Element::FixedWidthArea(_) => Box::new(std::iter::empty()), - Element::HorizontalRule(_) => Box::new(std::iter::empty()), - Element::Keyword(_) => Box::new(std::iter::empty()), - Element::BabelCall(_) => Box::new(std::iter::empty()), - Element::LatexEnvironment(_) => Box::new(std::iter::empty()), - }, - Token::PlainListItem(elem) => Box::new(elem.children.iter().map(Token::Element)), - Token::TableRow(elem) => Box::new(elem.children.iter().map(Token::TableCell)), - Token::TableCell(elem) => Box::new(elem.children.iter().map(Token::Object)), - Token::NodeProperty(_) => Box::new(std::iter::empty()), - } - } -} - -pub(crate) struct AllTokensIterator<'r, 's> { - queued_tokens: VecDeque>, -} - -impl<'r, 's> AllTokensIterator<'r, 's> { - pub(crate) fn new(tkn: Token<'r, 's>) -> Self { - let mut queued_tokens = VecDeque::new(); - queued_tokens.push_back(tkn); - AllTokensIterator { queued_tokens } - } -} - -impl<'r, 's> Iterator for AllTokensIterator<'r, 's> { - type Item = Token<'r, 's>; - - fn next(&mut self) -> Option { - let next_token = match self.queued_tokens.pop_front() { - Some(tkn) => tkn, - None => return None, - }; - self.queued_tokens.extend(next_token.iter_tokens()); - Some(next_token) - } -}