Open6

MarkDownパーサーを作ってみる

powellpowell

主な仕様

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

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>*
powellpowell

木構造の作成

とりあえず構文木の雛形を作ってみる

https://github.com/kentakom1213/md-parser/commit/b3c843b85d01a7fd2fe5889e1b905b2852bc3196

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(),
                    }),
                ],
            }),
        ],
    };
}