Open6
MarkDownパーサーを作ってみる

主な仕様
- パーサーコンビネータ的に実装したい
- MarkDownの基本的な機能をサポート
- 見出し(H1〜H6)
- 太字
- イタリック
- コード(インライン / ブロック)
- 数式(インライン / ブロック)
- 区切り線
- ASTの構成
- HTMLの生成
- 拡張記法にも対応したい

BNF
最終的にはこれを目指す
; ドキュメントはブロック要素の連続
document ::= block*
; ブロック要素 (改行で区切られる大きな単位)
block ::= thematic_break | atx_heading | fenced_code_block | html_block | paragraph | blank_line
; 水平線 (---, ***, ___)
thematic_break ::= ('*' ' '*){3,} '\n' | ('-' ' '*){3,} '\n' | ('_' ' '*){3,} '\n'
; ATX見出し (# ... ## ...)
atx_heading ::= ('#'{1,6}) ' '+ text_line '\n'
; フェンスされたコードブロック (``` ... ```)
; ※開始と終了は状態を持つため、純粋なBNFでの表現は困難
fenced_code_block ::= '```' | '~~~' <info_string> '\n' code_lines '```' | '~~~' '\n'
; HTMLブロック (<div>...</div>など)
; ※これも複雑な開始・終了条件を持つ
; 段落 (他のブロックに当てはまらない行の連続)
paragraph ::= (inline_content '\n')+
; 空行
blank_line ::= '\n'
; --- インライン要素 (ブロック内で解釈される小さな単位) ---
; インラインコンテンツ
inline_content ::= (text | emphasis | strong | code_span | link | image | raw_html)+
; 強調 (*)
emphasis ::= '*' inline_content '*' | '_' inline_content '_'
; 強い強調 (**)
strong ::= '**' inline_content '**' | '__' inline_content '__'
; コードスパン (`)
code_span ::= '`' <text_without_backticks> '`'
; リンク
link ::= '[' link_text ']' '(' link_destination ')'
; テキスト (特殊文字以外の連続)
text ::= <any_character_not_in_special_set>+
; 行のテキスト (改行まで)
text_line ::= <any_character_except_newline>*

木構造の作成
とりあえず構文木の雛形を作ってみる
fn main() {
let doc: DocumentNode = DocumentNode {
blocks: vec![
BlockNode::Heading(HeadingNode {
level: 1,
contents: "こんにちは".to_string(),
}),
BlockNode::Paragraph(ParagraphNode {
contents: vec![
InlineNode::PlainText(PlainTextNode {
contents: "こんにちは.これがMarkDownの構文木です.".to_string(),
}),
InlineNode::Link(LinkNode {
contents: "GitHubリポジトリ".to_string(),
href: "https://github.com/kentakom1213/md-parser".to_string(),
}),
],
}),
],
};
}