first commit

This commit is contained in:
2025-11-09 01:06:25 +02:00
commit dbaa2feb48
28 changed files with 1551 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
use crate::ast::Block;
use super::inline::parse_inlines;
pub fn parse_blocks(input: &str) -> Vec<Block> {
let mut blocks = Vec::new();
let mut lines = input.lines().peekable();
while let Some(line) = lines.next() {
if line.starts_with("#") {
let level = line.chars().take_while(|&c| c == '#').count() as u8;
let text = line[level as usize..].trim();
blocks.push(Block::Heading {
level,
content: parse_inlines(text),
});
} else if let Some(quote_body) = line.strip_prefix(">") {
let quote_blocks = parse_blocks(quote_body);
blocks.push(Block::Quote(quote_blocks));
} else if line.starts_with("```") {
let lang_line = line.strip_prefix("```").unwrap().to_string();
let lang = if lang_line.is_empty() {
None
} else {
Some(lang_line)
};
let mut code = String::new();
while lines.peek().is_some() && !lines.peek().unwrap().starts_with("```") {
code.push_str(&format!("{}\n", lines.next().unwrap()));
}
lines.next();
blocks.push(Block::Code {
language: lang,
content: code,
});
} else if line.trim().is_empty() {
continue;
} else {
blocks.push(Block::Paragraph(parse_inlines(line)));
}
}
blocks
}

View File

@@ -0,0 +1,61 @@
use crate::ast::Inline;
pub fn parse_inlines(input: &str) -> Vec<Inline> {
let mut inlines = Vec::new();
let mut chars = input.chars().peekable();
while let Some(c) = chars.next() {
match c {
'*' => {
let inner = collect_until(&mut chars, '*');
inlines.push(Inline::Bold(parse_inlines(&inner)));
}
'_' => {
let inner = collect_until(&mut chars, '_');
inlines.push(Inline::Italic(parse_inlines(&inner)));
}
'`' => {
let code = collect_until(&mut chars, '`');
inlines.push(Inline::Code(code));
}
'[' => {
let text = collect_until(&mut chars, ']');
if chars.next() == Some('(') {
let href = collect_until(&mut chars, ')');
inlines.push(Inline::Link {
text: parse_inlines(&text),
href,
});
}
}
_ => {
let mut text = String::new();
text.push(c);
while let Some(&nc) = chars.peek() {
if matches!(nc, '*' | '_' | '`' | '[') {
break;
}
text.push(chars.next().unwrap());
}
inlines.push(Inline::Text(text));
}
}
}
inlines
}
fn collect_until<I: Iterator<Item = char>>(
chars: &mut std::iter::Peekable<I>,
end: char,
) -> String {
let mut s = String::new();
while let Some(&c) = chars.peek() {
if c == end {
chars.next();
break;
}
s.push(chars.next().unwrap());
}
s
}