0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Swift:開発中のMarkdown解析パッケージをもう少しテストしやすくする

Posted at

前回、SwiftでMarkdownを解析してオブジェクトツリーに変換するという記事を作成しましたが、今回はその続きです。

尚、前回記事で「レンダリングはしてくれるけどオブジェクトツリーにしてくれるパッケージあまりないな...」と言いましたが大抵のSwiftのMarkdownレンダリング系パッケージは

text-->node tree-->html

のような変換過程を辿っていることがわかりました。つまり今Tryしていることはいわゆる「車輪の再発明」ということです。勉強になるからいいかと割り切ってます

問題点

「とりあえず動くもの!」ということで前回はとりあえずif分岐をひたすらしていましたが流石にテストし難いです。

/// 以下はイメージです
func parse(text:String){
  while(全文字探査するまで){
    if(H1の場合){
    }
    else if(H2の場合){
    }
    // 中略。無限にelse if...
    else if(コードブロック("```")の場合){
    }

    1文字進めるまたは処理済みの所まで進める()
  }
}

変更

Markdownの要素(# H1**strong**,...)ごとにシンプルなテストができるようにしたいです

Markdown要素ごとの解析処理の分化

前回記事で述べているように、この開発中のパッケージではMarkdownの各種要素をそれぞれインライン要素、ブロック要素とみなしています。それぞれの要素ごとにParserを作成するイメージでクラス分離していってます。

(紫で塗りつぶされているのはprotocol)

Untitled Diagram.drawio.png

BlockParserDelegateInlineParserDelegateもそれぞれ、解析結果のオブジェクトと、まだ解析していないMarkdown文字列を返します。

BlockParserDelegate

import Foundation
public protocol BlockParserDelegate{
    func parse(_ text:String, closure:(String)->[MDInline]) -> (nextText:String, node:MDBlock?);
}

InlineParserDelegate

import Foundation
public protocol InlineParserDelegate{
    func parse(text:String) -> (nextText:String, node:MDInline?)
}

このProtocolの実装クラスStrongParser

import Foundation
public class StrongParser: InlineParserDelegate
{
    public func parse(text:String) -> (nextText:String, node:MDInline?){
        if(text.starts(with: "**")){
            // scan
            let subStartIndex = text.index(text.startIndex, offsetBy: 2)
            let subText = text[subStartIndex...]
            if let end = subText.range(of: "**"){
                // text between ** and **
                let content = subText[..<end.lowerBound]
                
                return (String(subText[end.upperBound...]), MDStrong(String(content)))
            }
        }
        
        return (text, nil)
    }
}

StrongParserでは以下のようなイメージです

let parser = StrongParser();

let result1 = parser.parse(text: "**bold**text")

// result1.nextText -> "text"
// result1.node -> MDStrong(text:"bold")

let result2 = parser.parse(text: "texttext")

// result2.nextText -> "texttext"
// result2.node -> nil

結果

この変更によってMarkdown要素ごとのテストが可能になりました。あとは呼び出し側のクラスがテスト済みのそれらをうまく使うだけです。


今回の記事の内容を適用したまだまだ改善の余地が大きいMarkdown解析Githubリポジトリ

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?