wip
This commit is contained in:
parent
2a2851178a
commit
e84e7dd50b
5 changed files with 202 additions and 73 deletions
48
src/block.rs
48
src/block.rs
|
@ -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 })), "````"),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub enum Atom {
|
|||
Escape,
|
||||
Nbsp,
|
||||
OpenMarker, // ??
|
||||
Ellipses,
|
||||
Ellipsis,
|
||||
ImageMarker, // ??
|
||||
EmDash,
|
||||
EnDash,
|
||||
|
|
208
src/lib.rs
208
src/lib.rs
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
13
src/tree.rs
13
src/tree.rs
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue