使用Attoparsec解析简单的分子名称

时间:2014-10-25 08:09:32

标签: parsing haskell attoparsec

我发现学习如何使用Attoparsec非常困难,因为文档实际上只是一个API文档,并且基本上没有教程(除了来自FPComplete的教程)。如果你知道其他我可以学习Attoparsec的地方,那就太棒了。

我必须采用以下格式解析简单的分子名称:NaClCO2H2OHCNH2O2
元素名称是一个大写字母,可选地后跟一个小写字母(我不考虑那些符号长于2个字符的元素)。
元素后面可以跟一个数字(这将是公式中的下标)。

新版本(感谢Mark' s和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

其中emptyData.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)

1 个答案:

答案 0 :(得分:3)

字母解析部分有两个问题:

  • inClass不是解析器,它是一个传递给satisfy的函数。
  • <*>的类型为Parser (a -> b) -> Parser a -> Parser b,因此左侧的解析器应返回一个函数。通常,它使用如下:

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

    其中pf是带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")