2

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

我必须解析简单的分子名称,格式如下:NaCl, CO2, H2O, HCN, H2O2. 元素
名称是 一个大写字母,可选地后跟一个小写字母(我不考虑符号长度超过 2 个字符的那些元素)。 一个元素后面可以跟一个数字(这将是公式中的下标)。

新版本(感谢 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

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

  • inClass不是解析器,它是一个旨在传递给satisfy.
  • <*>有 type Parser (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 回答