diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-04-23 18:58:41 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-04-23 18:58:41 +0800 |
| commit | 7d9f9be43469748148da5cdf516cd8b32238e1f5 (patch) | |
| tree | e3904be9901294e0193419cb30e8f6fa1d33fae3 /src/ast | |
| parent | 7525fe0834e47bef425135e8cda1d576c44060a5 (diff) | |
重构AST抽象rewrite
Diffstat (limited to 'src/ast')
| -rw-r--r-- | src/ast/parser.rs | 52 | ||||
| -rw-r--r-- | src/ast/parser/emphasis.rs | 137 |
2 files changed, 174 insertions, 15 deletions
diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 9add926..bfcf271 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -1,25 +1,26 @@ use std::{any::Any, collections::HashMap, str::Chars}; -use crate::ast::{Layer, Line, MarkdownAST, Token}; +use crate::ast::{Fragment, Line, MarkdownAST, Token}; +pub mod emphasis; pub mod headings; type ProcessFn = fn(&char, &mut ParserInternalStatus) -> ParserMatchResult; fn match_fn_list() -> Vec<ProcessFn> { - // 要求以 预处理、词、行、层、后处理 的顺序编写列表 - // 因为 词处理器 将会写入 records_tokens 由 行处理器 消费 - // 接着 行处理器 将会写入 records_lines 由 层处理器 消费 - // 最后 层处理器 将所有行写入当前层中 + // 要求以 预处理、词、行、块、后处理 的顺序编写列表 + // 然后 词处理器 将会写入 records_tokens 由 行处理器 消费 + // 接着 行处理器 将会写入 records_lines 由 块处理器 消费 + // 块处理器则将自身加入块列表中 vec![ // 预处理器 // ... // 词处理器 - // ... + emphasis::proc, // 行处理器 - headings::proc, - // 层处理器 // ... + // 块处理器 + headings::proc, // 后处理器 post, ] @@ -76,6 +77,9 @@ pub(crate) struct ParserInternalStatus<'a> { /// 记录的 Token,用于暂存无归属的 Token records_tokens: Vec<Token>, + /// 记录的 Fragment,用于暂存无归属的 Fragment + records_fragment: Fragment, + /// 临时类型表 tmp: HashMap<&'a str, Box<dyn Any>>, } @@ -120,17 +124,19 @@ pub(crate) enum ParserMatchResult { } pub fn markdown_parser( - content: &str, + content: String, cfg: MarkdownParserConfig, ) -> Result<MarkdownAST, MarkdownASTParseError> { // 创建空 AST,无任何内容 - let ast = MarkdownAST { - root: Layer { - range_row_begin: 0, - range_row_end: 0, - lines: Vec::new(), - }, + let ast = MarkdownAST { blocks: vec![] }; + + // 在末尾添加换行符,以确保末尾行一定能被执行 + let mut content = content; + let ending = match cfg.ending_rule { + LineEndingRule::CRLF => "\r\n", + LineEndingRule::LF => "\n", }; + content.push_str(ending); // 初始化内部状态 let mut inr = ParserInternalStatus { @@ -142,6 +148,7 @@ pub fn markdown_parser( col: 0, records_lines: Vec::new(), records_tokens: Vec::new(), + records_fragment: Fragment::default(), tmp: HashMap::new(), }; @@ -165,6 +172,9 @@ pub fn markdown_parser( // 当前处理函数的索引值 let mut idx: u8 = 0; + // 该字符是否已完成处理 + let mut done = false; + for v in &match_vec { // 当前处理函数在放弃列表中 if aborted.contains(&idx) { @@ -180,8 +190,13 @@ pub fn markdown_parser( // // 在 `post` 中,必须处理该字符的换行逻辑,否则会产生字符位置指针异常 if !matches!(c, '\r' | '\n') { + // 为字符标记为已完成处理 + done = true; + // 跳过当前步骤前,提前将列指针右移 inr.col += 1; + + // 所有处理器跳过当前步骤 break; } } @@ -213,9 +228,16 @@ pub fn markdown_parser( )); } } + + // 下一个处理器继续处理 idx += 1; } + if !done { + // 如果字符未完成处理,说明是普通字符,需要加入 records_fragment + inr.records_fragment.str.push(c); + } + // 将列指针右移 inr.col += 1; } diff --git a/src/ast/parser/emphasis.rs b/src/ast/parser/emphasis.rs new file mode 100644 index 0000000..c8e6b9b --- /dev/null +++ b/src/ast/parser/emphasis.rs @@ -0,0 +1,137 @@ +use crate::ast::parser::{ParserInternalStatus, ParserMatchResult}; + +#[derive(Default)] +struct EmphasisTmp { + /// 强调开始的列 + emphasis_begin_col: u16, + + /// 前缀,用于后缀匹配 + prefix_count: String, + + /// 是否正在输入强调前缀 + typing_emphasis_prefix: bool, + + /// 是否正在输入强调内容 + typing_emphasis_content: bool, +} + +#[derive(Default, PartialEq, Eq)] +#[repr(u8)] +enum Style { + /// 无样式 + #[default] + None, + + /// 星号 + Star, + + /// 下划线 + Underline, +} + +impl Style { + /// 反转样式 + pub fn invert(&self) -> Style { + match self { + Style::Underline => Style::Star, + Style::Star => Style::Underline, + Style::None => Style::None, + } + } +} + +impl std::fmt::Display for Style { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Style::None => write!(f, ""), + Style::Star => write!(f, "*"), + Style::Underline => write!(f, "_"), + } + } +} + +impl From<Style> for char { + fn from(style: Style) -> char { + match style { + Style::None => ' ', + Style::Star => '*', + Style::Underline => '_', + } + } +} + +fn get_tmp<'a>(inr: &'a mut ParserInternalStatus) -> &'a mut EmphasisTmp { + inr.get_tmp_or_init::<EmphasisTmp>("emphasis_tmp") +} + +pub(crate) fn proc(c: &char, inr: &mut ParserInternalStatus) -> ParserMatchResult { + match c { + // 输入能被识别的符号时 + + // 星星 + '*' => typed_emphasis_char(Style::Star, inr), + + // 下划线 + '_' => typed_emphasis_char(Style::Underline, inr), + + // 输入其他字符时 + _ => typed_other_char(c, inr), + } +} + +fn typed_emphasis_char(s: Style, inr: &mut ParserInternalStatus) -> ParserMatchResult { + let col = inr.col; + let tmp = get_tmp(inr); + + // 如果没设置样式(没初始化) + if tmp.prefix_style == Style::None { + // 设置样式 + tmp.prefix_style = s; + // 设置前缀长度 + tmp.prefix_count = 1; + // 设置开始位置 + tmp.emphasis_begin_col = col; + + tmp.typing_emphasis_prefix = true; + tmp.typing_emphasis_content = false; + } else + // 如果设置了样式,则判断其是否匹配 + if tmp.prefix_style == s { + // 如果匹配,增加前缀长度 + tmp.prefix_count += 1; + + // 增加长度后,如果前缀长度大于 3(最长),将报语法错误 + if tmp.prefix_count > 3 { + return ParserMatchResult::SyntaxError { + begin_col: tmp.emphasis_begin_col, + begin_row: inr.row, + end_col: inr.col, + end_row: inr.row, + msg: "Emphasis characters can be at most 3".to_string(), + }; + } + } else { + // 如果不匹配,将报语法错误 + return ParserMatchResult::SyntaxError { + begin_col: inr.col, + begin_row: inr.row, + end_col: inr.col, + end_row: inr.row, + msg: format!( + "Emphasis statement (style: \"{}\") cannot use another emphasis statement (style: \"{}\") before closing", + s.invert(), + s + ), + }; + } + + ParserMatchResult::Done +} + +fn typed_other_char(c: &char, inr: &mut ParserInternalStatus) -> ParserMatchResult { + let tmp = get_tmp(inr); + + // 修改当前状态 + tmp.typing_emphasis_prefix = false; + tmp.typing_emphasis_content = true; +} |
