diff options
author | metamuffin <metamuffin@disroot.org> | 2022-09-25 18:29:22 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2022-09-25 18:29:22 +0200 |
commit | e3edf18503b3975ccec3b33c0cb9e7f0888bd031 (patch) | |
tree | 9b8d795bf5e0ff6e0f5cdd882cef07d495f73a72 /code/src/markdown/parser.rs | |
parent | a80b5c677417cdbc17df3109ef9d12afe79973cc (diff) | |
download | metamuffin-blog-e3edf18503b3975ccec3b33c0cb9e7f0888bd031.tar metamuffin-blog-e3edf18503b3975ccec3b33c0cb9e7f0888bd031.tar.bz2 metamuffin-blog-e3edf18503b3975ccec3b33c0cb9e7f0888bd031.tar.zst |
extend parser
Diffstat (limited to 'code/src/markdown/parser.rs')
-rw-r--r-- | code/src/markdown/parser.rs | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/code/src/markdown/parser.rs b/code/src/markdown/parser.rs index e69de29..cb025a6 100644 --- a/code/src/markdown/parser.rs +++ b/code/src/markdown/parser.rs @@ -0,0 +1,188 @@ +#[derive(Debug, Clone)] +pub enum Block { + Header(usize, Vec<Span>), + Paragraph(Vec<Span>), + Blockquote(Vec<Block>), + CodeBlock(Option<String>, String), + LatexBlock(String), + OrderedList(Vec<Vec<Block>>), + UnorderedList(Vec<Vec<Block>>), + Raw(String), + Hr, +} +#[derive(Debug, Clone)] +pub enum Span { + Break, + Text(String), + Code(String), + Link(String, String), + Image(String, String), + Emphasis(Vec<Span>), + Strong(Vec<Span>), + Latex(String), +} + +pub fn parse(mut s: &str) -> Vec<Block> { + let mut blocks = Vec::new(); + while s.len() != 0 { + if s.starts_with("\n") { + s = &s[1..]; + continue; + } + // TODO bad code here + if let Some((block, rest)) = try_header(s) { + s = rest; + blocks.push(block); + continue; + } + if let Some((block, rest)) = try_latex_block(s) { + s = rest; + blocks.push(block); + continue; + } + if let Some((block, rest)) = try_code_block(s) { + s = rest; + blocks.push(block); + continue; + } + if let Some((block, rest)) = try_list(s) { + s = rest; + blocks.push(block); + continue; + } + let lf = [s.find("\n\n"), s.find("\n-")] + .iter() + .filter_map(|e| *e) + .min() + .unwrap_or(s.len()); + let span = Span::parse(&s[..lf]); + blocks.push(Block::Paragraph(span)); + if lf >= s.len() { + break; + } + s = &s[lf + 1..]; + } + blocks +} + +fn try_code_block(mut s: &str) -> Option<(Block, &str)> { + if !s.starts_with("```") { + return None; + } + s = &s[3..]; + let lf = s.find('\n')?; + let syntax = if lf != 0 { + Some(String::from(&s[0..lf])) + } else { + None + }; + s = &s[lf..]; + let end = s.find("\n```\n")?; + Some(( + Block::CodeBlock(syntax, String::from(&s[..end])), + &s[end + 4..], + )) +} +fn try_latex_block(mut s: &str) -> Option<(Block, &str)> { + if !s.starts_with("$$") { + return None; + } + s = &s[2..]; + let end = s.find("$$")?; + Some((Block::LatexBlock(String::from(&s[..end])), &s[end + 2..])) +} +fn try_list(mut s: &str) -> Option<(Block, &str)> { + if !s.starts_with("-") { + return None; + }; + let mut blocks = vec![]; + loop { + if !s.starts_with("-") || s.len() == 0 { + break Some((Block::UnorderedList(blocks), s)); + } + s = &s[1..]; + let mut lf = s.find("\n").unwrap(); + while s[lf + 1..].starts_with(" -") { + lf += 2 + s[lf + 2..].find("\n").unwrap(); + } + eprintln!("{:?}", &s[..lf]); + let mut k = s[..lf] + .split("\n") + .map(|l| if l.starts_with(" ") { &l[2..] } else { &l }) + .collect::<Vec<_>>() + .join("\n"); + k.push('\n'); + blocks.push(parse(&k)); + s = &s[lf + 1..]; + } +} +fn try_header(s: &str) -> Option<(Block, &str)> { + if s.starts_with("#") { + let mut u = 0; + while s.chars().nth(u)? == '#' { + u += 1; + } + let lf = s.find('\n')?; + Some((Block::Header(u, Span::parse(&s[u..lf])), &s[lf + 1..])) + } else { + None + } +} + +impl Span { + pub fn parse(mut s: &str) -> Vec<Span> { + let mut spans = Vec::new(); + while s.len() != 0 { + let nt = s.find(&['*', '_', '`', '[', '$']); + + if let Some(nt) = nt { + spans.push(Span::Text(String::from(&s[..nt]))); + s = &s[nt..]; + if s.starts_with("**") { + s = &s[2..]; + let end = s.find("**").expect("** not ended"); + spans.push(Span::Strong(Span::parse(&s[..end]))); + s = &s[end + 2..]; + continue; + } + if s.starts_with("_") { + s = &s[1..]; + let end = s.find("_").expect("_ not ended"); + spans.push(Span::Emphasis(Span::parse(&s[..end]))); + s = &s[end + 1..]; + continue; + } + if s.starts_with("`") { + s = &s[1..]; + let end = s.find("`").expect("` not ended"); + spans.push(Span::Code(String::from(&s[..end]))); + s = &s[end + 1..]; + continue; + } + if s.starts_with("$") { + s = &s[1..]; + let end = s.find("$").expect("$ not ended"); + spans.push(Span::Latex(String::from(&s[..end]))); + s = &s[end + 1..]; + continue; + } + if s.starts_with("[") { + s = &s[1..]; + let del = s.find("](").expect("]( expected"); + let end = del + s[del..].find(")").expect(") expected"); + spans.push(Span::Link( + String::from(&s[..del]), + String::from(&s[del + 2..end]), + )); + s = &s[end + 1..]; + continue; + } + panic!("{s:?}") + } else { + spans.push(Span::Text(String::from(s))); + break; + } + } + spans + } +} |