"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