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