3

我一直在编写一个 attoparsec 解析器来解析度量单位的统一代码所称的<ATOM-SYMBOL>. 它被定义为某个类(该类包括所有数字 0-9)中不以数字结尾的最长字符序列。

所以给定我想要消费和返回的输入foo27foo因为237bar26我想要消费和返回237bar,因为19我想失败而不消费任何东西。

我不知道如何用takeWhile1or来构建它takeTillscan但我可能遗漏了一些明显的东西。

更新:到目前为止,我最好的尝试是我设法排除了完全是数字的序列

atomSymbol :: Parser Text
atomSymbol = do
               r <- core
               if (P.all (inClass "0-9") . T.unpack $ r)
                 then fail "Expected an atom symbol but all characters were digits."
                 else return r
  where
    core = A.takeWhile1 $ inClass "!#-'*,0-<>-Z\\^-z|~"

我尝试更改它以测试最后一个字符是否是数字而不是它们是否都是数字,但它似乎不会一次回溯一个字符。

更新 2:

整个文件位于https://github.com/dmcclean/dimensional-attoparsec/blob/master/src/Numeric/Units/Dimensional/Parsing/Attoparsec.hs。这仅针对来自https://github.com/dmcclean/Dimensionprefixes的分支构建。

4

1 回答 1

1

您应该重新表述问题并分别处理数字范围 ( 0-9) 和非数字字符范围 ( !#-'*,:-<>-Z\\^-z|~)。然后可以将感兴趣的句法元素描述为

  • 一个可选的数字范围,后跟
  • 非数字跨度,后跟
  • 零个或多个{数字跨度后跟非数字跨度}。
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Applicative ((<|>), many)
import Data.Char (isDigit)

import Data.Attoparsec.Combinator (option)
import Data.Attoparsec.Text (Parser)
import qualified Data.Attoparsec.Text as A
import Data.Text (Text)
import qualified Data.Text as T

atomSymbol :: Parser Text
atomSymbol = f <$> (option "" digitSpan)
               <*> (nonDigitSpan <|> fail errorMsg)
               <*> many (g <$> digitSpan <*> nonDigitSpan)
  where
    nonDigitSpan = A.takeWhile1 $ A.inClass "!#-'*,:-<>-Z\\^-z|~"
    digitSpan    = A.takeWhile1 isDigit
    f x y xss    = T.concat $ x : y : concat xss
    g x y        = [x,y]
    errorMsg     = "Expected an atom symbol but all characters (if any) were digits."

测试

[...] 给定foo27我想要消费并返回 foo 的输入,因为237bar26我想要消费并返回237bar,因为19我想失败而不消费任何东西。

λ> A.parseOnly atomSymbol "foo26"
Right "foo"

λ> A.parseOnly atomSymbol "237bar26"
Right "237bar"

λ> A.parseOnly atomSymbol "19"
Left "Failed reading: Expected an atom symbol but all characters (if any) were digits."
于 2015-12-05T09:39:24.193 回答