"Write Yourself a Scheme in 48 Hours/Parsing" http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Parsing を読む

Parsingの練習問題の続き。

Excercise 2 support escaping of internal quotes (続き)

many (noneOf "\"")

の部分は、このままだと、「"」が来たら止まって、それまで読んだところを文字列で返すような感じになっている。
で、「abc"def"g」 だったら、(「abc」 , 「"def"g」)だけど、
「abc\"def"g」 だったら(「abc"def」 , 「"g」) という感じにしたいと。つまりバックスラッシュでのエスケープに対応したい。
(noneOf "\"")のところだけ変えてもどうにもうまくいきそうにない感じなので、manyの部分を展開することに。で、前回はmanyと
同じように動かすにはどうすればいいか考えた、と。で今回はそれをもとに、many (noneOf "\"")とほぼ同じように動くだろうと思われるものを、manyを使わずに書いてみた。

pstr = do {
 x <- noneOf "\"";
 do {
    xs <- pstr;
    return (x:xs)
   }
  <|>
  return [x]
 } <|> return []

どうやったらもっときれいに書けるのか?今は期待通り動けばよしとして先に行く。
で、これにエスケープの仕組みを加えるために、とりあえず、「\"」ときたら「"」と読むパーサを作る。

escaped = do {
  _ <- char '\\';
  x <- char '\"';
  return [x]
}

もっときれいに書けないだろうか。

escaped2 = liftM (:[]) $char '\\' >> char '\"' 

最初のdo記法のままで十分な気がした。試しに動かしてみる。

Main> let p (Right x) = putStr (x++"\n");p x = print x
Main> p$parse escaped "" ""
Left (line 1, column 1):
unexpected end of input
expecting "\\"
Main> p$parse escaped "" "\\"
Left (line 1, column 2):
unexpected end of input
expecting "\""
Main> p$parse escaped "" "\\\""
"
Main> p$parse escaped "" "\\\"a"
"

OKでしょう。ではこれでさっきのpstr にescapedを加えてみる。

pstr = do {
    x <- noneOf "\"";
    do {
        xs <- pstr;
        return (x:xs)
    }
    <|> return [x]
    } 
    <|> do {
        xs  <- escaped;
        ys <- pstr;
        return (xs ++ ys)
    }
    <|> return []

動きを確かめてみると、

Main> p$parse pstrold "" "abc\""
abc
Main> p$parse pstrold "" "abc\\\""
abc\
Main> p$parse pstrold "" "abc\\\"e"
abc\

だめでした。

pstr = do {
    xs  <- escaped;
    ys <- pstr;
    return (xs ++ ys)
    }
    <|> do {
        x <- noneOf "\"";
        do {
           xs <- pstr;
           return (x:xs)
           }
        <|> return [x]
    }
    <|>  return []

順番を変えてみると、

Main> p$parse pstr "" "abc\\\"e"
abc"e

うまくいったもよう。