HaskellとParsecでLisp REPL その5(Bool型の追加)
今回の変更点
ブール値の追加。データ型とパーサの拡張のみ。
データ型
data LispVal = Atom String | List [LispVal] | Number Integer | Bool Bool
ちゃんとshow
できるようにしておく:
showVal (Bool True) = "#t" showVal (Bool False) = "#f"
パーサ拡張
Bool
型はユーザ入力が"#t"ならBool True、"#f"ならBool Falseである。このパーサはちょっとだけややこしい。
というのは、今までの三つの種類のデータ型はそもそも先頭の文字をパースした時点でどの要素になるかがわかったのだが、Bool
型の場合Atom
型とかぶってしまっている。
まず、Atom
型のパーサのほうがBool
型よりも許容度が高いので("#t"はAtom
型としてもパース可能)、パースを試す順番はBool
のほうが先でなければいけない:
parseExpr = parseBool <|> parseAtom <|> parseList <|> parseNumber
これで"#t"がAtom
型として認識されてしまう問題は回避できる。
しかし、まだ気をつけないといけない。
今までのような実装だとたとえば"#define"のような文字列をパースする時に、parseBool
が"#"は処理し"d"を読んだ時点で諦めるので、"#"文字が次のparseAtom
に渡されない問題が発生する。
parseBool :: Parser LispVal parseBool = (try parseTrue) <|> (try parseFalse)
parseBool
がtry
を使うことによって解決する。try
はバックトラックしてくれるので、parseTrue
を試してみて失敗したら元の文字まで戻ってparseFalse
に回し、それも失敗したらまた元の文字まで戻って次のパーサに回す。
parseTrue
/parseFalse
の定義:
parseTrue :: Parser LispVal parseTrue = string "#t" >> return (Bool True) parseFalse :: Parser LispVal parseFalse = string "#f" >> return (Bool False)
ParsecのParserはモナドなので>>
とかreturn
がでてくる。"#t"や"#f"のパースが成功したら、マッチした文字列は使わず直接Bool True
/Bool False
をParserモナドに入れて返す。
評価器・REPL・Main
例によって変更なし
次回
ブール値が表現できるようになったので、ブール値を返す比較演算子を定義する。