"Write Yourself a Scheme in 48 Hours/Parsing" http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Parsing を読む
これは面白い。
Excercise 2 support escaping of internal quotes
ということで、Stringのパースのとこで
"ab\"c" -> ['a','b','"','c']のようなエスケープをサポートしろという問題。
最初は以下のようになっている。
parseString :: Parser LispVal parseString = do char '"' x <- many (noneOf "\"") char '"' return $ String x
x <- many (noneOf "\"")
のあたりを変えると。
2文字続けて読む必要があるので、manyを使わない方法が必要なのではないかとあたりをつけてみた。
ということで、まずmanyと同じように動く関数を作ってみたいが、どうすればよいのか。
http://www.lab2.kuis.kyoto-u.ac.jp/~hanatani/tmp/Parsec.html
のmany1の説明のところに、
word :: Parser String word = do{ c <- letter ; do{ cs <- word ; return (c:cs) } <|> return [c] }
こういうパターンの抽象化がmany1だと書いてあった。
ということで、letterの代わりを引数でとるようにする。
mymany1 p = do { x <- p; do { ys <- mymany1 p; return (x:ys) } <|> return [x] }
ここで、Haskellの構文で引っかかった。"{"や";"をとりのぞいて、インデンテーションだけをつかって
すっきり書きなおそうと思ったが、return [x]のところのxがscopeにない、とか言われたりしてどうにもうまくいかなかった。
まぁ、"{"や";"を使って書けば動くので悩まず進む。
ためしに、parsecのソースのmanyやmany1のところを見てみたが、複雑でちょっとまだ手に負えない感じだった。
きっとエラーメッセージがきちんとしていて、効率的な実装になっているのだろう、と妄想してみる。
Main> parse (mymany1 $oneOf "ab") "" "aabbac" Right "aabba" Main> parse (mymany1 $oneOf "ab") "" "" Left (line 1, column 1): unexpected end of input Main> parse (mymany1 $oneOf "ab") "" "c" Left (line 1, column 1): unexpected "c"
とりあえず期待通りに動いているっぽい。
ということで、many1ができたのでmanyを。
mymany p = do { x <- p; do { ys <- mymany1 p; return (x:ys) } <|> return [x] } <|> return []
これも動いているもよう。
Main> parse (mymany $oneOf "ab") "" "abab" Right "abab" Main> parse (mymany $oneOf "ab") "" "c" Right "" Main> parse (mymany $oneOf "ab") "" "ac" Right "a"
だめだ、もう眠すぎる。。。zzz