Haskell技術の勉強のため、技術評論社の「Haskell入門」をつまみ食いしながら読んでいます。
文章の構造を解読する構文解析、またそれを解読するためのパーサーについては以前から少し気になっていたのですが、Haskellは構文解析にもってこいの言語だということを知ったので、今回は上記の本を参考に勉強をしました。Haskellはコンパイル型の言語ということもあり、コードを書けば書くほどファイルが増えてしまって面倒なのでQiitaに覚書としてまとめることにしました。
間違いなどあればご指摘いただければ幸いです。
モジュールData.List.SplitのsplitOn
まずはパッケージsplit-0.2.4のモジュールData.List.Splitの中にあるsplitOn関数を使って簡単なパーサーを作っていきます。
このsplitOn関数は与えられた文字列に対して、与えられた記号("/"や"."など)で分割を行ってくれる関数です。以下のように定義されています。第一引数が分割したい記号、二つ目が分割したい文字列になります。
ghci> :t splitOn
splitOn :: Eq a => [a] -> [a] -> [[a]]
例えば"abc/d"という文字列に対して、"/"で分割を行うならば以下のように実装されます。
ghci> splitOn "/" "abc/d"
["abc","d"]
この関数を使って2023/11/16のように与えられる文字列をYMDと呼ばれるデータ型を使って、(YMD 2023 11 16)というように分解していきます。コードは以下の通り。
import Data.List.Split
data YMD =YMD Int Int Int deriving Show
parseYMD :: String -> Maybe YMD
parseYMD = listToymd . splitOn "/"
where
listToymd :: [String] -> Maybe YMD
listToymd (y:m:d:_) = Just $ YMD (read y) (read m) (read d)
listToymd _ = Nothing
main = print $ parseYMD "2023/11/16"
splitOn関数で["2023","11","16"]と分割した上で、listToymdでYMD型に変換してきます。read関数を使っていますが、これは文字列として認識されている2023などをInt型に変換するために使っています。
これを以下で実行
stack runghc par.hs --package split
なお、stackを除いて以下のように実行するとうまくいかないので注意
runghc par.hs --package split
この場合、Data.List.Splitというモジュールはないと返事されてしまう。
実行結果は以下の通り。
Just (YMD 2023 11 16)
これで簡単なパーサーを実装できたが、例えば入力にどのような型でも入れることができるなど、問題点は多い。2023/11/16を分解するという単純なパーサーでもちゃんと実装するとかなり大変であることがここからもわかる。