我如何使用与另一个 Parsec 解析器具有不同流类型的 Parsec 解析器?
How can I use a Parsec parser which has a different stream type than another Parsec parser?
我有一个用 Text
作为流类型编写的解析器,而默认情况下 Text.Parsec.String
模块使用 String
否则。
如何在 Parsec String b c
的上下文中使用自定义编写的解析器 (Parsec Text b c
)?
基本上我似乎需要这样一个功能:
f :: Parsec Text b c -> Parsec String b c
f = undefined
虽然听起来可行,但做起来似乎相当复杂。
这很可怕,但相对简单。思路是利用low-level函数runParsecT
和mkPT
对解析器进行解构和重构,用适配器括起来修改传入和传出状态的流类型:
import Text.Parsec
import Data.Text (Text)
import qualified Data.Text as Text
stringParser :: (Monad m) => ParsecT Text u m a -> ParsecT String u m a
stringParser p = mkPT $ \st -> (fmap . fmap . fmap) outReply $ runParsecT p (inState st)
where inState :: State String u -> State Text u
inState (State i pos u) = State (Text.pack i) pos u
outReply :: Reply Text u a -> Reply String u a
outReply (Ok a (State i pos u) e) = Ok a (State (Text.unpack i) pos u) e
outReply (Error e) = Error e
它似乎工作正常:
myTextParser :: Parsec Text () String
myTextParser = (:) <$> oneOf "abc" <*> many letter
myStringParser :: Parsec String () (String, String)
myStringParser = (,) <$> p <* spaces <*> p
where p = stringParser myTextParser
main = do
print =<< parseTest myStringParser "avocado butter"
print =<< parseTest myStringParser "apple error"
给予:
λ> main
("avocado","butter")
()
parse error at (line 1, column 7):
unexpected "e"
expecting space
()
但是,这里可能存在一些严重的性能问题,除非它被用于小型玩具解析器。 pack
调用将获取整个传入流并将其转换为 Text
值。如果您从惰性 String
解析(例如,从惰性 I/O 调用),第一次使用转换后的解析器会将整个字符串作为 Text
读入内存并将其抽取作为 String
退出;对同一个解析器的进一步调用将 re-pack 剩余的流每次都为 Text
。切换到惰性 Text
并没有多大帮助,因为 pack
仍然会将整个输入打包到“惰性”Text
值中。
您需要 运行 一些 tests/benchmarks 来查看您的应用程序是否可以接受这种性能损失。一般来说,重写 Text
解析器(或者看看它是否会用抽象流类型编译)将是一个更好的方法。
完整代码示例:
{-# OPTIONS_GHC -Wall #-}
import Text.Parsec
import Data.Text (Text)
import qualified Data.Text as Text
stringParser :: (Monad m) => ParsecT Text u m a -> ParsecT String u m a
stringParser p = mkPT $ \st -> (fmap . fmap . fmap) outReply $ runParsecT p (inState st)
where inState :: State String u -> State Text u
inState (State i pos u) = State (Text.pack i) pos u
outReply :: Reply Text u a -> Reply String u a
outReply (Ok a (State i pos u) e) = Ok a (State (Text.unpack i) pos u) e
outReply (Error e) = Error e
myTextParser :: Parsec Text () String
myTextParser = (:) <$> oneOf "abc" <*> many letter
myStringParser :: Parsec String () (String, String)
myStringParser = (,) <$> p <* spaces <*> p
where p = stringParser myTextParser
main = do
print =<< parseTest myStringParser "avocado butter"
print =<< parseTest myStringParser "apple error"
我有一个用 Text
作为流类型编写的解析器,而默认情况下 Text.Parsec.String
模块使用 String
否则。
如何在 Parsec String b c
的上下文中使用自定义编写的解析器 (Parsec Text b c
)?
基本上我似乎需要这样一个功能:
f :: Parsec Text b c -> Parsec String b c
f = undefined
虽然听起来可行,但做起来似乎相当复杂。
这很可怕,但相对简单。思路是利用low-level函数runParsecT
和mkPT
对解析器进行解构和重构,用适配器括起来修改传入和传出状态的流类型:
import Text.Parsec
import Data.Text (Text)
import qualified Data.Text as Text
stringParser :: (Monad m) => ParsecT Text u m a -> ParsecT String u m a
stringParser p = mkPT $ \st -> (fmap . fmap . fmap) outReply $ runParsecT p (inState st)
where inState :: State String u -> State Text u
inState (State i pos u) = State (Text.pack i) pos u
outReply :: Reply Text u a -> Reply String u a
outReply (Ok a (State i pos u) e) = Ok a (State (Text.unpack i) pos u) e
outReply (Error e) = Error e
它似乎工作正常:
myTextParser :: Parsec Text () String
myTextParser = (:) <$> oneOf "abc" <*> many letter
myStringParser :: Parsec String () (String, String)
myStringParser = (,) <$> p <* spaces <*> p
where p = stringParser myTextParser
main = do
print =<< parseTest myStringParser "avocado butter"
print =<< parseTest myStringParser "apple error"
给予:
λ> main
("avocado","butter")
()
parse error at (line 1, column 7):
unexpected "e"
expecting space
()
但是,这里可能存在一些严重的性能问题,除非它被用于小型玩具解析器。 pack
调用将获取整个传入流并将其转换为 Text
值。如果您从惰性 String
解析(例如,从惰性 I/O 调用),第一次使用转换后的解析器会将整个字符串作为 Text
读入内存并将其抽取作为 String
退出;对同一个解析器的进一步调用将 re-pack 剩余的流每次都为 Text
。切换到惰性 Text
并没有多大帮助,因为 pack
仍然会将整个输入打包到“惰性”Text
值中。
您需要 运行 一些 tests/benchmarks 来查看您的应用程序是否可以接受这种性能损失。一般来说,重写 Text
解析器(或者看看它是否会用抽象流类型编译)将是一个更好的方法。
完整代码示例:
{-# OPTIONS_GHC -Wall #-}
import Text.Parsec
import Data.Text (Text)
import qualified Data.Text as Text
stringParser :: (Monad m) => ParsecT Text u m a -> ParsecT String u m a
stringParser p = mkPT $ \st -> (fmap . fmap . fmap) outReply $ runParsecT p (inState st)
where inState :: State String u -> State Text u
inState (State i pos u) = State (Text.pack i) pos u
outReply :: Reply Text u a -> Reply String u a
outReply (Ok a (State i pos u) e) = Ok a (State (Text.unpack i) pos u) e
outReply (Error e) = Error e
myTextParser :: Parsec Text () String
myTextParser = (:) <$> oneOf "abc" <*> many letter
myStringParser :: Parsec String () (String, String)
myStringParser = (,) <$> p <* spaces <*> p
where p = stringParser myTextParser
main = do
print =<< parseTest myStringParser "avocado butter"
print =<< parseTest myStringParser "apple error"