summaryrefslogtreecommitdiff
path: root/src/ast
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-04-23 18:58:41 +0800
committer魏曹先生 <1992414357@qq.com>2026-04-23 18:58:41 +0800
commit7d9f9be43469748148da5cdf516cd8b32238e1f5 (patch)
treee3904be9901294e0193419cb30e8f6fa1d33fae3 /src/ast
parent7525fe0834e47bef425135e8cda1d576c44060a5 (diff)
重构AST抽象rewrite
Diffstat (limited to 'src/ast')
-rw-r--r--src/ast/parser.rs52
-rw-r--r--src/ast/parser/emphasis.rs137
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;
+}