Text.Prasec.Error
ParseError 例外
Text.Parsec.Error モジュールには Parsec でパターンマッチが失敗したときの ParseError 例外の関連のプログラムが記述されている。parseTest でパターンマッチが失敗すると例外が発生し、エラーの場所が表示される。Prelude> :m + Text.Parsec Text.Parsec.Error Text.Parsec.Pos Prelude Text.Parsec Text.Parsec.Error Text.Parsec.Pos> :set prompt "Parsec> " Parsec>
Parsec> parseTest letter "123" parse error at (line 1, column 1): unexpected "1" expecting letter
このとき、これらの情報を保持した ParseError 例外が発生している。
ParseError の取り出し
そこで、ParseError 例外を取り出してみた。これは runParser 関数を利用すると取り出すことができる。runParser 関数の型は次のようになるので、(Left pe) のパターンで ParseError 型のデータを取り出せる。
Parsec> :t runParser runParser :: Stream s Data.Functor.Identity.Identity t => Parsec s u a -> u -> SourceName -> s -> Either ParseError a Parsec> (Left pe) = runParser letter () "SourceName" "123" Parsec> :t pe pe :: ParseError
ParseError は Show クラスのインスタンスなので、上で得られた pe は show 関数で内容を見ることができる。したがって、ghci では pe と入力するだけで表示できる。
Parsec> pe "SourceName" (line 1, column 1): unexpected "1" expecting letter
ParseError の実際のデータ型は :i コマンドで検索できる。
Parsec> :i ParseError data ParseError = Text.Parsec.Error.ParseError !SourcePos [Message] -- Defined in ‘Text.Parsec.Error’ instance [safe] Eq ParseError -- Defined in ‘Text.Parsec.Error’ instance [safe] Show ParseError -- Defined in ‘Text.Parsec.Error’
Text.Parsec.Error の概要
ここで、Text.Parsec.Error モジュールの概要を :browse コマンドで見てみる。
Parsec> :browse Text.Parsec.Error data Message = SysUnExpect !String | UnExpect !String | Expect !String | Message !String data ParseError = Text.Parsec.Error.ParseError !SourcePos [Message] addErrorMessage :: Message -> ParseError -> ParseError errorIsUnknown :: ParseError -> Bool errorMessages :: ParseError -> [Message] errorPos :: ParseError -> SourcePos mergeError :: ParseError -> ParseError -> ParseError messageString :: Message -> String newErrorMessage :: Message -> SourcePos -> ParseError newErrorUnknown :: SourcePos -> ParseError setErrorMessage :: Message -> ParseError -> ParseError setErrorPos :: SourcePos -> ParseError -> ParseError showErrorMessages :: String -> String -> String -> String -> String -> [Message] -> String
ParseError 型
これを見ると ParseError のデータ構造が、
ParseError !SourcePos [Message]
なので、ParseError のフィールドにはパースエラーが発生したソースの位置 SourcePos とエラーメッセージのリスト [Message] が入っているのが分かる。
ParseError データコンストラクタはエクスポートされていないので、新しい ParseError を作るときは 、newErrorUnknoun 関数を使う。
Parsec> pe = newErrorUnknown (initialPos "foo") Parsec> pe "foo" (line 1, column 1):unknown parse error
Message 型
Message データ型はエラーメッセージであるが、エラーの種類によってデータコンストラクタが異なっている。このデータコンストラクタの違いによって、エラーメッセージを端末に表示するときにエラーの種類によって表現を変えることができる。
エラーメッセージはデータコンストラクタで作成できるが、Show クラスのインスタンスではないので ghci で表示できない。ParseError 関係の関数をテストするときにエラーメッセージが必要になるが、表示されなくても変数には束縛できるし、変数の型チェックをすることで、それがエラーメッセージであることが分かる。
Parsec> msg = Message "foo" Parsec> msg
<interactive>:5:1: error: • No instance for (Show Message) arising from a use of ‘print’ • In a stmt of an interactive GHCi command: print it Parsec> :t msg msg :: Message
addErrorMessage 関数
sddErrorMessage 関数は ParseError にメッセージを追加する。
Parsec> addErrorMessage (Message "bar") pe "foo" (line 1, column 1): bar
errorIsUnknown 関数
errorIsUnknown 関数は ParseError が Unknown エラーかどうかをチェックする。
Parsec> errorIsUnknown pe True
errorMessages 関数
errorMessages 関数は ParseError からメッセージのリストを取り出す。Message 型は Show クラスのインスタンスではないので、ghci では表示できない。:t コマンドでタイプを表示する。
Parsec> :t errorMessages pem errorMessages pem :: [Message]
errorPos 関数
errorPos 関数は ParseError から SourcePos ソース位置を取り出す。
Parsec> errorPos pe "foo" (line 1, column 1)
mergeError 関数
mergeError 関数は2つの ParseError をマージする。
Parsec> mergeError pe pem "foo" (line 1, column 1): bar
messageString 関数
messageString 関数はメッセージを文字列に変換する。
Parsec> messageString (Message "bar") "bar"
newErrorMessage 関数 はメッセージと、SourcePos から新しい ParseError を作る。
setErrorMessage 関数は SourcePos のメッセージを引数のメッセージと置き換える。
setErrorPos 関数は Parse Error のパース位置を引数の SourcePos と置き換える。
showErrorMessages はメッセージのリストを文字列に変換する。メッセージリスト意外に沢山の文字列の引数を必要としているが、メッセージの種類に応じて prefix になる。
newErrorMessage 関数
Parsec> newErrorMessage (Message "bar") (initialPos "foo")
"foo" (line 1, column 1):
bar
setErrorMessages 関数
Parsec> setErrorMessage (Message "baz") pem
"foo" (line 1, column 1):
baz
setErrorPos 関数
Parsec> setErrorPos (newPos "baz" 3 4) pe
"baz" (line 3, column 4):unknown parse error
showErrorMessagews 関数
Parsec> showErrorMessages "foo" "bar" "baz" "foobar" "foobaz" [Expect "qux"]
"\nbaz qux"
これらの実験で印象的だったのは showErrorMessages でメッセージのコンストラクタの違いによって表現を変えているところだ。showErrorMessages のソースがどうなっているかは興味深いが今は調べない。