This commit is contained in:
Noah Hellman 2022-11-29 00:33:43 +01:00
parent 2a2851178a
commit e84e7dd50b
5 changed files with 202 additions and 73 deletions

View file

@ -306,8 +306,8 @@ mod test {
test_parse!(
"para\n",
(Enter(Leaf(Paragraph)), ""),
(Element(Inline), "para\n"),
(Exit, ""),
(Element(Inline), "para"),
(Exit(Leaf(Paragraph)), ""),
);
}
@ -317,8 +317,8 @@ mod test {
"para0\npara1\n",
(Enter(Leaf(Paragraph)), ""),
(Element(Inline), "para0\n"),
(Element(Inline), "para1\n"),
(Exit, ""),
(Element(Inline), "para1"),
(Exit(Leaf(Paragraph)), ""),
);
}
@ -333,14 +333,14 @@ mod test {
"15\n", //
),
(Enter(Leaf(Heading { level: 1 })), "#"),
(Element(Inline), " 2\n"),
(Exit, "#"),
(Element(Inline), "2"),
(Exit(Leaf(Heading { level: 1 })), "#"),
(Element(Blankline), "\n"),
(Enter(Leaf(Heading { level: 1 })), "#"),
(Element(Inline), " 8\n"),
(Element(Inline), " 12\n"),
(Element(Inline), "15\n"),
(Exit, "#"),
(Element(Inline), "15"),
(Exit(Leaf(Heading { level: 1 })), "#"),
);
}
@ -356,17 +356,17 @@ mod test {
),
(Enter(Container(Blockquote)), ">"),
(Enter(Leaf(Paragraph)), ""),
(Element(Inline), " a\n"),
(Exit, ""),
(Element(Blankline), "\n"),
(Element(Inline), "a"),
(Exit(Leaf(Paragraph)), ""),
(Element(Blankline), ""),
(Enter(Leaf(Heading { level: 2 })), "##"),
(Element(Inline), " hl\n"),
(Exit, "##"),
(Element(Blankline), "\n"),
(Element(Inline), "hl"),
(Exit(Leaf(Heading { level: 2 })), "##"),
(Element(Blankline), ""),
(Enter(Leaf(Paragraph)), ""),
(Element(Inline), " para\n"),
(Exit, ""),
(Exit, ">"),
(Element(Inline), "para"),
(Exit(Leaf(Paragraph)), ""),
(Exit(Container(Blockquote)), ">"),
);
}
@ -379,9 +379,13 @@ mod test {
"```", //
),
(Enter(Leaf(CodeBlock { fence_length: 3 })), "```"),
(Element(Inline), "\n"),
(Element(Inline), "l0\n"),
(Exit, "```"),
(Element(Inline), ""),
(Element(Inline), "l0"),
(Exit(Leaf(CodeBlock { fence_length: 3 })), "```"),
(Element(Blankline), "\n"),
(Enter(Leaf(Paragraph)), ""),
(Element(Inline), "para"),
(Exit(Leaf(Paragraph)), ""),
);
test_parse!(
concat!(
@ -395,8 +399,8 @@ mod test {
(Element(Inline), "lang\n"),
(Element(Inline), "l0\n"),
(Element(Inline), "```\n"),
(Element(Inline), " l1\n"),
(Exit, "````"),
(Element(Inline), " l1"),
(Exit(Leaf(CodeBlock { fence_length: 4 })), "````"),
);
}

View file

@ -1,6 +1,6 @@
use crate::Event;
pub fn push_html<'s, I: Iterator<Item = Event>>(s: &mut String, events: I) {
pub fn push_html<'s, I: Iterator<Item = Event<'s>>>(s: &mut String, events: I) {
Writer::new(events).write()
}
@ -8,7 +8,7 @@ struct Writer<I> {
events: I,
}
impl<I: Iterator<Item = Event>> Writer<I> {
impl<'s, I: Iterator<Item = Event<'s>>> Writer<I> {
fn new(events: I) -> Self {
Self { events }
}

View file

@ -15,7 +15,7 @@ pub enum Atom {
Escape,
Nbsp,
OpenMarker, // ??
Ellipses,
Ellipsis,
ImageMarker, // ??
EmDash,
EnDash,

View file

@ -12,11 +12,11 @@ pub struct Block;
const EOF: char = '\0';
#[derive(Debug, PartialEq, Eq)]
pub enum Event2<'s> {
pub enum Event<'s> {
/// Start of a tag.
Start(TagKind<'s>, Attributes<'s>),
Start(Tag<'s>, Attributes<'s>),
/// End of a tag.
End(TagKind<'s>),
End(Tag<'s>),
/// A string object, text only.
Str(&'s str),
/// A verbatim string.
@ -38,16 +38,13 @@ pub enum Event2<'s> {
/// A newline that may or may not break a line in the output format.
Softbreak,
/// A newline that must break a line.
HardBreak,
Hardbreak,
/// An escape character, not visible in output.
Escape,
}
// Attributes are rare, better to pay 8 bytes always and sometimes an extra allocation instead of
// always 24 bytes.
#[derive(Debug, PartialEq, Eq)]
pub struct Attributes<'s>(Option<Box<Vec<(&'s str, &'s str)>>>);
#[derive(Debug, PartialEq, Eq)]
pub enum TagKind<'s> {
pub enum Tag<'s> {
/// A paragraph.
Paragraph,
/// A heading.
@ -82,6 +79,24 @@ pub enum TagKind<'s> {
DescriptionItem,
/// A footnote definition.
Footnote { tag: &'s str },
/// A subscripted element.
Subscript,
/// A superscripted element.
Superscript,
/// An inserted element.
Insert,
/// A deleted element.
Delete,
/// An element emphasized with a bold typeface.
Strong,
/// An emphasized element.
Emphasis,
/// A highlighted inline element.
Mark,
/// An quoted element, using single quotes.
SingleQuoted,
/// A quoted inline element, using double quotes.
DoubleQuoted,
}
#[derive(Debug, PartialEq, Eq)]
@ -128,22 +143,96 @@ pub enum OrderedListFormat {
ParenParen,
}
/*
impl<'s> Event<'s> {
fn from_inline(src: &'s str, inline: inline::Event) -> Self {
match inline {
Enter
let content = inline.span.of(src);
match inline.kind {
inline::EventKind::Enter(c) | inline::EventKind::Exit(c) => {
let t = match c {
inline::Container::Span => Tag::Span,
inline::Container::Subscript => Tag::Subscript,
inline::Container::Superscript => Tag::Superscript,
inline::Container::Insert => Tag::Insert,
inline::Container::Delete => Tag::Delete,
inline::Container::Emphasis => Tag::Emphasis,
inline::Container::Strong => Tag::Strong,
inline::Container::Mark => Tag::Mark,
inline::Container::SingleQuoted => Tag::SingleQuoted,
inline::Container::DoubleQuoted => Tag::DoubleQuoted,
_ => todo!(),
};
if matches!(inline.kind, inline::EventKind::Enter(_)) {
Self::Start(t, Attributes::none())
} else {
Self::End(t)
}
}
inline::EventKind::Atom(a) => match a {
inline::Atom::Ellipsis => Self::Ellipsis,
inline::Atom::EnDash => Self::EnDash,
inline::Atom::EmDash => Self::EmDash,
inline::Atom::Nbsp => Self::NonBreakingSpace,
inline::Atom::Softbreak => Self::Softbreak,
inline::Atom::Hardbreak => Self::Hardbreak,
inline::Atom::Escape => Self::Escape,
_ => todo!(),
},
inline::EventKind::Node(n) => match n {
inline::Node::Str => Self::Str(content),
inline::Node::Verbatim => Self::Verbatim(content),
inline::Node::InlineMath => Self::Math {
content,
display: false,
},
inline::Node::DisplayMath => Self::Math {
content,
display: true,
},
_ => todo!(),
},
}
}
}
*/
impl<'s> Tag<'s> {
fn from_block(src: &'s str, block: block::Block) -> Self {
match block {
block::Block::Leaf(l) => match l {
block::Leaf::Paragraph => Self::Paragraph,
block::Leaf::Heading { level } => Self::Heading { level },
block::Leaf::CodeBlock { .. } => Self::CodeBlock { language: None },
_ => todo!(),
},
block::Block::Container(c) => match c {
block::Container::Blockquote => Self::Blockquote,
block::Container::Div { .. } => Self::Div,
block::Container::Footnote { .. } => Self::Footnote { tag: todo!() },
_ => todo!(),
},
}
}
}
// Attributes are rare, better to pay 8 bytes always and sometimes an extra allocation instead of
// always 24 bytes.
#[derive(Debug, PartialEq, Eq)]
pub enum Event {
Start(block::Block),
End,
Inline(inline::Event),
Blankline,
pub struct Attributes<'s>(Option<Box<Vec<(&'s str, &'s str)>>>);
impl<'s> Attributes<'s> {
#[must_use]
pub fn none() -> Self {
Self(None)
}
#[must_use]
pub fn valid(src: &str) -> bool {
todo!()
}
#[must_use]
pub fn parse(src: &'s str) -> Self {
todo!()
}
}
pub struct Parser<'s> {
@ -166,14 +255,14 @@ impl<'s> Parser<'s> {
}
impl<'s> Iterator for Parser<'s> {
type Item = Event;
type Item = Event<'s>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(parser) = &mut self.parser {
// inside leaf block, with inline content
if let Some(mut inline) = parser.next() {
inline.span = inline.span.translate(self.inline_start);
return Some(Event::Inline(inline));
return Some(Event::from_inline(self.src, inline));
} else if let Some(ev) = self.tree.next() {
match ev.kind {
tree::EventKind::Element(atom) => {
@ -181,9 +270,9 @@ impl<'s> Iterator for Parser<'s> {
parser.parse(ev.span.of(self.src));
self.inline_start = ev.span.start();
}
tree::EventKind::Exit => {
tree::EventKind::Exit(block) => {
self.parser = None;
return Some(Event::End);
return Some(Event::End(Tag::from_block(self.src, block)));
}
tree::EventKind::Enter(..) => unreachable!(),
}
@ -199,29 +288,60 @@ impl<'s> Iterator for Parser<'s> {
if matches!(block, block::Block::Leaf(..)) {
self.parser = Some(inline::Parser::new());
}
Event::Start(block)
Event::Start(Tag::from_block(self.src, block), Attributes::none())
}
tree::EventKind::Exit => Event::End,
tree::EventKind::Exit(block) => Event::End(Tag::from_block(self.src, block)),
})
}
}
#[cfg(test)]
mod test {
use super::Attributes;
use super::Event::*;
use crate::block::Block::*;
use crate::block::Container::*;
use crate::block::Leaf::*;
use crate::inline::Atom::*;
use crate::inline::EventKind::*;
use crate::inline::Node::*;
use super::Tag::*;
macro_rules! test_parse {
($($st:ident,)? $src:expr $(,$($token:expr),* $(,)?)?) => {
#[allow(unused)]
let actual = super::Parser::new($src).collect::<Vec<_>>();
let expected = &[$($($token),*,)?];
assert_eq!(actual, expected, "\n\n{}\n\n", $src);
assert_eq!(
actual,
expected,
concat!(
"\n",
"\x1b[0;1m====================== INPUT =========================\x1b[0m\n",
"\x1b[2m{}",
"\x1b[0;1m================ ACTUAL vs EXPECTED ==================\x1b[0m\n",
"{}",
"\x1b[0;1m======================================================\x1b[0m\n",
),
$src,
{
let a = actual.iter().map(|n| format!("{:?}", n)).collect::<Vec<_>>();
let b = expected.iter().map(|n| format!("{:?}", n)).collect::<Vec<_>>();
let max = a.len().max(b.len());
let a_width = a.iter().map(|a| a.len()).max().unwrap_or(0);
a.iter()
.map(AsRef::as_ref)
.chain(std::iter::repeat(""))
.zip(b.iter().map(AsRef::as_ref).chain(std::iter::repeat("")))
.take(max)
.map(|(a, b)|
format!(
"\x1b[{}m{:a_width$}\x1b[0m {}= \x1b[{}m{}\x1b[0m\n",
if a == b { "2" } else { "31" },
a,
if a == b { '=' } else { '!' },
if a == b { "2" } else { "32" },
b,
a_width = a_width,
)
)
.collect::<String>()
},
);
};
}
@ -229,25 +349,25 @@ mod test {
fn para() {
test_parse!(
"para",
Start(Leaf(Paragraph)),
Inline(Node(Str).span(0, 4)),
End
Start(Paragraph, Attributes::none()),
Str("para"),
End(Paragraph),
);
test_parse!(
"pa ra",
Start(Leaf(Paragraph)),
Inline(Node(Str).span(0, 9)),
End
Start(Paragraph, Attributes::none()),
Str("pa ra"),
End(Paragraph),
);
test_parse!(
"para0\n\npara1",
Start(Leaf(Paragraph)),
Inline(Node(Str).span(0, 6)),
End,
Start(Paragraph, Attributes::none()),
Str("para0\n"),
End(Paragraph),
Blankline,
Start(Leaf(Paragraph)),
Inline(Node(Str).span(7, 12)),
End,
Start(Paragraph, Attributes::none()),
Str("para1"),
End(Paragraph),
);
}
}

View file

@ -4,7 +4,7 @@ use crate::Span;
pub enum EventKind<C, E> {
Enter(C),
Element(E),
Exit,
Exit(C),
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -53,10 +53,15 @@ impl<C: Copy, E: Copy> Iterator for Tree<C, E> {
};
Some(Event { kind, span: n.span })
} else if let Some(block_ni) = self.branch.pop() {
let Node { next, span, .. } = &self.nodes[block_ni.index()];
let Node { next, kind, span } = &self.nodes[block_ni.index()];
let cont = if let NodeKind::Container(c, _) = kind {
c
} else {
panic!();
};
self.head = *next;
Some(Event {
kind: EventKind::Exit,
kind: EventKind::Exit(*cont),
span: *span,
})
} else {
@ -192,7 +197,7 @@ impl<C: Copy + std::fmt::Display, E: Copy + std::fmt::Display> std::fmt::Display
write!(f, "{}{}", indent, container)?;
level += 1;
}
EventKind::Exit => {
EventKind::Exit(_) => {
level -= 1;
continue;
}