haskell 如何通过Megaparsec解析带逗号的Number

inn6fuwd  于 2022-12-13  发布在  其他
关注(0)|答案(2)|浏览(129)

目前我有一个解析器:

pScientific :: Parser Scientific
pScientific = lexeme L.scientific

它可以轻松地解析4087.00之类的内容
但是失败,当数字4,087.00有没有办法让megaparsec用逗号解析数字?
PS:我对 haskell 非常陌生,所以如果这是一个愚蠢的问题,请道歉

kognpnkq

kognpnkq1#

如果解析器的其余部分没有逗号,一个便宜而令人愉快的解决方案是在解析之前将它们全部删除。
如果确实需要在解析过程中保留逗号,那么最好的办法是查找scientific的源代码,然后进行复制、粘贴和调整--我不知道有什么预先制作的解析器可以接受逗号。

cygmwpex

cygmwpex2#

不解析的原因是scientific类型主要是为JSON解析定义的,而JSON不允许这样做,并且使用逗号分隔数组和对象中的元素。
我们可以看看scientific [src]的实现:

-- | Parse a JSON number.
scientific :: Parser Scientific
scientific = do
  sign <- A.peekWord8'
  let !positive = not (sign == W8_MINUS)
  when (sign == W8_PLUS || sign == W8_MINUS) $
    void A.anyWord8

  n <- decimal0

  let f fracDigits = SP (B.foldl' step n fracDigits)
                        (negate $ B.length fracDigits)
      step a w = a * 10 + fromIntegral (w - W8_0)

  dotty <- A.peekWord8
  SP c e <- case dotty of
              Just W8_DOT -> A.anyWord8 *> (f <$> A.takeWhile1 isDigit_w8)
              _           -> pure (SP n 0)

  let !signedCoeff | positive  =  c
                   | otherwise = -c

  (A.satisfy (\ex -> case ex of W8_e -> True; W8_E -> True; _ -> False) *>
      fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
    return (Sci.scientific signedCoeff    e)
{-# INLINE scientific #-}

主要要修改的是decimal0部分,它捕获一个零或多个十进制数的序列。

import qualified Data.ByteString as B

decimal0' :: Parser Integer
decimal0' = do
  digits <- B.filter (\x -> x /= 44) <$> A.takeWhile1 (\x -> isDigit_w8 x || x == 44)
  if B.length digits > 1 && B.unsafeHead digits == 48
    then fail "leading zero"
    else return (bsToInteger digits)

然后使用具有以下内容的一个:

import qualified Data.Attoparsec.ByteString as A
import qualified Data.Scientific as Sci
import Data.Attoparsec.ByteString.Char8 (isDigit_w8)

-- | Parse a JSON number.
scientific :: Parser Scientific
scientific = do
  sign <- A.peekWord8'
  let !positive = not (sign == 45)
  when (sign == 43 || sign == 45) $
    void A.anyWord8

  n <- decimal0'

  let f fracDigits = SP (B.foldl' step n fracDigits)
                        (negate $ B.length fracDigits)
      step a w = a * 10 + fromIntegral (w - W8_0)

  dotty <- A.peekWord8
  SP c e <- case dotty of
              Just 46 -> A.anyWord8 *> (f <$> A.takeWhile1 isDigit_w8)
              _           -> pure (SP n 0)

  let !signedCoeff | positive  =  c
                   | otherwise = -c

  (A.satisfy (\ex -> case ex of W8_e -> True; W8_E -> True; _ -> False) *>
      fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
    return (Sci.scientific signedCoeff    e)
{-# INLINE scientific' #-}

这没有考虑到逗号被放置在每三个数字之后,因此这将需要额外的逻辑,但是这是在Scientific的整数部分中工作接受逗号的基本实现。

相关问题