如何用 megaparsec 解析多行?许多 。许多人遇到 space 泄漏
How to parse multiple lines with megaparsec? many . many runs into space leak
我想解析一些非常简单的文本,例如,
"abcxyzzzz\nhello\n"
变成 ["abcxyzzz", "hello"] :: String
.
不寻找更简单的函数来执行此操作(如 words
),因为我需要解析更复杂的东西,我只是在这里奠定基础。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module RgParse where
import Data.Text (Text)
import Text.Megaparsec
import Text.Megaparsec.Char
data SimpleData = SimpleData String deriving (Eq, Show, Ord)
data SimpleData' = SimpleData' [String] deriving (Eq, Show, Ord)
instance ShowErrorComponent SimpleData where
showErrorComponent = show
instance ShowErrorComponent String where
showErrorComponent = show
simple :: Parsec String Text SimpleData
simple = do
x <- many (noneOf (Just '\n'))
pure $ SimpleData x
simple' :: Parsec String Text SimpleData'
simple' = do
x <- many (many (noneOf (Just '\n')))
pure $ SimpleData' x
example2 :: Text
example2 = "abcxyzzzz\nhello\n"
main :: IO ()
main = do
print "Simple:"
case parse simple "<stdin>" example2 of
Left bundle -> putStr (errorBundlePretty bundle)
Right result -> print result
print "Simple':"
case parse simple' "<stdin>" example2 of
Left bundle -> putStr (errorBundlePretty bundle)
Right result -> print result
print "done.."
不幸的是,上面的代码在进入 simple'
时遇到了无限循环/space 泄漏,因为它输出以下内容:
Hello, Haskell!
[]
"Simple:"
SimpleData "abcxyzzzz"
"Simple':"
使用 megaparsec-7.0.5(不是最新的 9.x.x)。
是否有更简单的方法来获取多行?
仅将 many
应用于至少使用一个标记(此处为一个 Char
)或失败的解析器。那是因为 many
通过 运行 它的参数工作,直到它失败。 many x
可能消耗零个令牌,因此 many (many x)
打破了这个要求。
请注意,一行至少应包含一个终止换行符。这样就可以满足该要求。
oneline :: Parsec String Text String
oneline = many (noneOf (Just '\n')) <* single '\n'
manylines :: Parsec String Text [String]
manylines = many oneline
simple :: Parsec String Text SimpleData
simple = do
x <- oneline
pure $ SimpleData x
simple' :: Parsec String Text SimpleData'
simple' = do
x <- manylines
pure $ SimpleData' x
many p
的宽松要求是 p
的任何重复都必须在有限次数的迭代后失败(这里 p = many x
永远不会失败),因此 p
可能在某些步骤中不消耗任何东西,但它必须是有状态的,以便在一些重复之后它最终消耗一些东西或失败。但是上面的近似值在实践中是一个很好的经验法则。
我想解析一些非常简单的文本,例如,
"abcxyzzzz\nhello\n"
变成 ["abcxyzzz", "hello"] :: String
.
不寻找更简单的函数来执行此操作(如 words
),因为我需要解析更复杂的东西,我只是在这里奠定基础。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module RgParse where
import Data.Text (Text)
import Text.Megaparsec
import Text.Megaparsec.Char
data SimpleData = SimpleData String deriving (Eq, Show, Ord)
data SimpleData' = SimpleData' [String] deriving (Eq, Show, Ord)
instance ShowErrorComponent SimpleData where
showErrorComponent = show
instance ShowErrorComponent String where
showErrorComponent = show
simple :: Parsec String Text SimpleData
simple = do
x <- many (noneOf (Just '\n'))
pure $ SimpleData x
simple' :: Parsec String Text SimpleData'
simple' = do
x <- many (many (noneOf (Just '\n')))
pure $ SimpleData' x
example2 :: Text
example2 = "abcxyzzzz\nhello\n"
main :: IO ()
main = do
print "Simple:"
case parse simple "<stdin>" example2 of
Left bundle -> putStr (errorBundlePretty bundle)
Right result -> print result
print "Simple':"
case parse simple' "<stdin>" example2 of
Left bundle -> putStr (errorBundlePretty bundle)
Right result -> print result
print "done.."
不幸的是,上面的代码在进入 simple'
时遇到了无限循环/space 泄漏,因为它输出以下内容:
Hello, Haskell!
[]
"Simple:"
SimpleData "abcxyzzzz"
"Simple':"
使用 megaparsec-7.0.5(不是最新的 9.x.x)。
是否有更简单的方法来获取多行?
仅将 many
应用于至少使用一个标记(此处为一个 Char
)或失败的解析器。那是因为 many
通过 运行 它的参数工作,直到它失败。 many x
可能消耗零个令牌,因此 many (many x)
打破了这个要求。
请注意,一行至少应包含一个终止换行符。这样就可以满足该要求。
oneline :: Parsec String Text String
oneline = many (noneOf (Just '\n')) <* single '\n'
manylines :: Parsec String Text [String]
manylines = many oneline
simple :: Parsec String Text SimpleData
simple = do
x <- oneline
pure $ SimpleData x
simple' :: Parsec String Text SimpleData'
simple' = do
x <- manylines
pure $ SimpleData' x
many p
的宽松要求是 p
的任何重复都必须在有限次数的迭代后失败(这里 p = many x
永远不会失败),因此 p
可能在某些步骤中不消耗任何东西,但它必须是有状态的,以便在一些重复之后它最终消耗一些东西或失败。但是上面的近似值在实践中是一个很好的经验法则。