2

Attoparsec の使い方を学ぶのは非常に難しいと思います。なぜなら、ドキュメントは実際には単なる API ドキュメントであり、基本的にチュートリアルがないからです (FPComplete のものを除く)。Attoparsec を学ぶことができる他の場所を知っていれば、それは素晴らしいことです。

次の形式で単純な分子名を解析する必要があります: NaCl, CO2, H2O, . 要素名は、オプションで小文字が続く大文字です (記号が 2 文字を超える要素は考慮していません)。 要素の後には数字を続けることができます(数式の添字になります)。HCNH2O2

新しいバージョン (Mark と Tarmil の提案のおかげで) はコンパイルされますが、解析されません:

module Chem
    where

import Data.Text (Text, pack)
import Control.Applicative ((<*>), (<$>))
import Data.Attoparsec.Text

data Element = Element String Int deriving (Eq, Ord, Show)
type Molecule = [Element]

parseString :: String -> Result Molecule
parseString = parse (many' parseElement) . pack

parseElement :: Parser Element
parseElement = do
    el <- (++) <$> pClass "A-Z" <*> option "" (pClass "a-z")
    n  <- option 1 decimal
    return $ Element el n

pClass :: String -> Parser String
pClass cls = (\c -> [c]) <$> satisfy (inClass cls)

どんな提案でも大歓迎です。

編集:私はそれを実行することができました。基本的に、Partial継続が返されました。解析を完了するには、空のバイト文字列をパーサーに供給する必要があります。したがって、正しいのparseStringは次のとおりです。

parseString = flip feed empty . parse (many' parseElement) . pack

はどこemptyですかData.Text.empty。ただし、インクリメンタル解析は必要ないため、便利な関数 がありますparseOnly。これは、追加の入力を待機せず、 を返しますEither

それを念頭に置いて、次のようにコードを書き直しました(現在は動作しています):

module Chem
    where

import Data.Text (Text, pack)
import Control.Applicative ((<*>), (<$>))
import Data.Attoparsec.Text

data Element = Element String Int deriving (Eq, Ord, Show)
type Molecule = [Element]

parseString :: String -> Either String Molecule
parseString = parseOnly (many' parseElement) . pack

parseElement :: Parser Element
parseElement = do
    el <- (++) <$> pClass "A-Z" <*> option "" (pClass "a-z")
    n  <- option 1 decimal
    return $ Element el n

pClass :: String -> Parser String
pClass cls = (\c -> [c]) <$> satisfy (inClass cls)
4

1 に答える 1

3

文字の解析部分に 2 つの問題があります。

  • inClassはパーサーではなく、 に渡されることを意図した関数ですsatisfy
  • <*>は typeParser (a -> b) -> Parser a -> Parser bであるため、左側のパーサーは関数を返す必要があります。通常、次のように使用されます。

    pf <$> p1 <*> p2 <*> ... <*> pn
    

    wherepfは引数を持つ関数nです。

したがって、ここではおそらく次のようなものが必要です。

-- parse a character in the given class, and transform it to a single-char string
pClass cls = (\c -> [c]) <$> satisfy (inClass cls)

-- ...
    el <- ((++) <$> pClass "A-Z" <*> pClass "a-z") <|> pClass "A-Z"
-- ...

これは、パーサーoptionを複製する代わりに、を使用することで強化されると思います。A-Z

    el <- (++) <$> pClass "A-Z" <*> option "" (pClass "a-z")
于 2014-10-25T08:55:28.847 に答える