如何使用haskell parsec库使用应用程序函子解析Intel十六进制记录?

如何使用haskell parsec库使用应用程序函子解析Intel十六进制记录?,haskell,parsec,Haskell,Parsec,我想用parsec使用applicative functor样式解析Intel十六进制记录。典型记录如下所示: :100100001461360121470136007EFE09D2190140 第一个字符始终是“:”,接下来的两个字符是十六进制字符串,表示记录中的字节数。接下来的四个字符是十六进制字符串,用于标识数据的起始地址。我有如下代码,但我不知道如何应用程序地将字节计数传递给解析数据字节的解析器。我的非工作代码如下所示 line = startOfRecord . byteCount .

我想用parsec使用applicative functor样式解析Intel十六进制记录。典型记录如下所示:

:100100001461360121470136007EFE09D2190140

第一个字符始终是“:”,接下来的两个字符是十六进制字符串,表示记录中的字节数。接下来的四个字符是十六进制字符串,用于标识数据的起始地址。我有如下代码,但我不知道如何应用程序地将字节计数传递给解析数据字节的解析器。我的非工作代码如下所示

line = startOfRecord . byteCount . address . recordType . recordData . checksum
startOfRecord = char ':'
byteCount = toHexValue <$> count 2 hexDigit
address = toHexValue <$> count 4 hexDigit
recordType = toHexValue <$> count 2 hexDigit
recordData c = toHexValue <$> count c hexDigit
recordData c CharParser = count c hexDigit
checksum = toHexValue <$> count 2 hexDigit

toHexValue :: String -> Int
toHexValue = fst . head . readHex
line=startOfRecord。字节数。地址。记录类型。记录数据。校验和
startOfRecord=char':'
字节计数=到十六进制值计数2个十六进制数字
地址=toHexValue计数4个六位数
recordType=toHexValue计数2个hexDigit
recordData c=toHexValue计数c hexDigit
recordData c CharParser=计数c十六位数
校验和=toHexValue计数2个hexDigit
toHexValue::String->Int
toHexValue=fst。头。readHex

有人能帮我吗?谢谢。

为了使用parsec,您需要在问题中包含许多未包含的内容。要定义像
startOfRecord
这样的东西,我们需要禁用可怕的单态限制。如果我们想为任何类似
startOfRecord
的东西编写类型签名,我们还需要启用
FlexibleContexts
。我们还需要导入parsec、
Control.Applicative
Numeric(readHex)

我还将使用
Data.Word中的
Word8
Word16
,因为它们与英特尔十六进制记录中使用的类型完全匹配

import Data.Word
忽略某个时刻的
recordData
,我们可以定义如何读取字节(
Word8
)和16位整数地址(
Word16
)的十六进制值

撇开
recordData
,我们现在可以用
Applicative
风格编写类似于您的
行的内容。
Applicative
样式中的应用程序编写为(
为函数组合或)

如果我们有一个具有该类型的函数,我们可以在这里使用它并生成一个
ParserT
,它读取的内容类似于记录,但仍然缺少
recordData
。我们将创建一个数据类型来保存除实际数据之外的所有intel十六进制记录

data IntelHexRecord = IntelHexRecord Word8 Word16 Word8 {- [Word8] -} Word8
如果我们将其放入
(使用
常量
放弃
开始记录

这是我们所能采用的
Applicative
风格。假设我们已经知道
字节数
,那么让我们定义如何读取
记录数据

recordData :: (Stream s m Char) => Word8 -> ParsecT s u m [Word8]
recordData c = count (fromIntegral c) hexWord8
我们还将修改
IntelHexRecord
,以便有一个存放数据的位置

data IntelHexRecord = IntelHexRecord Word8 Word16 Word8 [Word8] Word8
如果您有一个
应用程序f
,通常无法根据内容选择结构。这就是
Applicative
Monad
之间的巨大区别;a
Monad
的绑定,
(>=)::对于所有a b。MA->(a->MB)->MB
,允许您根据内容选择结构。这正是我们需要做的,以根据我们先前通过读取
字节计数
获得的结果来确定如何读取
记录数据

recordData :: (Stream s m Char) => Word8 -> ParsecT s u m [Word8]
recordData c = count (fromIntegral c) hexWord8
的定义中使用一个bind
>=
最简单的方法是完全切换到
Monad
ic样式和
do
-符号

line = do
    startOfRecord
    bc   <- byteCount
    addr <- address
    rt   <- recordType
    rd   <- recordData bc
    cs   <- checksum
    return $ IntelHexRecord bc addr rt rd cs
line=do
开始记录

bc就我的理解而言,应用程序解析器(与一元解析器相比)的局限性在于您仅限于解析上下文无关的表达式

我的意思是,关于如何在某一点进行解析的决定不能依赖于之前解析的值,而只能依赖于结构(即,解析器失败,因此我们尝试应用不同的解析器)

我发现这可以从运营商本身得到解释:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b

感谢您的快速回复,并提供了一个很好的解释。稍微相关:是否有现成的haskell库用于此目的?
line = const IntelHexRecord <$> startOfRecord <*> byteCount <*> address <*> recordType <*> checksum
*> :t line
line :: Stream s m Char => ParsecT s u m IntelHexRecord
recordData :: (Stream s m Char) => Word8 -> ParsecT s u m [Word8]
recordData c = count (fromIntegral c) hexWord8
data IntelHexRecord = IntelHexRecord Word8 Word16 Word8 [Word8] Word8
line = do
    startOfRecord
    bc   <- byteCount
    addr <- address
    rt   <- recordType
    rd   <- recordData bc
    cs   <- checksum
    return $ IntelHexRecord bc addr rt rd cs
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
parseRecord = do
  count <- byteCount
  ...
  rData <- recordData count
  ...
  return (count,rData,...)