在Haskell中将字符插入到解析器组合字符流中

时间:2013-08-20 15:12:45

标签: parsing haskell parsec parser-combinators uu-parsinglib

此问题与Parsecuu-parsinglib都有关。当我们编写解析器组合器时,它们处理来自编译器的字符流。是否有可能解析一个字符并将其放回(或返回另一个字符)到输入流?

我希望例如解析输入“test + 5”,解析test以及识别test之后模式,例如将v字符放回到字符流中,因此在继续解析过程时,我们将匹配v + 5

我现在不想在任何特定情况下使用它 - 我想深入了解可能性。

3 个答案:

答案 0 :(得分:1)

我不确定这些解析器是否可以直接使用,但一般情况下,您可以通过将解析器与允许注入剩余部分的流式传输相结合来实现它。

例如,使用attoparsec-conduit,您可以使用

将解析器转换为管道
sinkParser :: (AttoparsecInput a, MonadThrow m)
           => Parser a b -> Consumer a m b

其中Consumer是一种不产生任何输出的特殊管道,只接收输入并返回最终值。

由于管道支持剩余部分,您可以创建一个帮助器方法,该方法可以转换一个解析器,该解析器可选地返回一个值以将其推送到流中:

import Data.Attoparsec.Types
import Data.Conduit
import Data.Conduit.Attoparsec
import Data.Functor

reinject :: (AttoparsecInput a, MonadThrow m)
    => Parser a (Maybe a, b) -> Consumer a m b
reinject p = do
    (lo, r) <- sinkParser p
    maybe (return ()) leftover lo
    return r

然后使用sinkParser将标准解析器转换为导管,使用reinject将这些特殊解析器转换为导管,然后组合导管而不是解析器。

答案 1 :(得分:1)

我认为存档的最简单方法是构建一个多层解析器。想想词法分析器+解析器组合。这是解决这个问题的一种干净方法。

你必须将两种解析分开。搜索和替换解析转到第一个解析器,build-the-AST解析到第二个解析器。或者您可以创建中间令牌表示。

import Text.Parsec
import Text.Parsec.String

parserLvl1 :: Parser String
parserLvl1 = many (try (string "test" >> return 'v') <|> anyChar)

parserLvl2 :: Parser Plus
parserLvl2 = do text1 <- many (noneOf "+")
                char '+'
                text2 <- many (noneOf "+")
                return $ Plus text1 text2

data Plus = Plus String String
  deriving Show

wholeParse :: String -> Either ParseError Plus
wholeParse source = do res1 <- parse parserLvl1 "lvl1" source
                       res2 <- parse parserLvl2 "lvl2" res1
                       return res2

现在你可以解析你的例子了。 wholeParse "test+5"会产生Right (Plus "v" "5")

可能的变化:

  • 创建一个用于组合包装解析器阶段的类和实例。 (可能携带解析器状态。)
  • 创建一个中间表示,一个令牌流

答案 2 :(得分:1)

使用pSwitch函数可以在uu-parsinglib中轻松完成。但问题是你为什么要这样做?因为输入中缺少v?在这种情况下,uu-parsinglib将自动执行纠错,因此您不需要这样的东西。否则你可以写

pSwitch :: (st1 -> (st2, st2 -> st1)) -> P st2 a -> P st1 a
pInsert_v = pSwitch (\st1 -> (prepend v st2, id) (pSucceed ())

这取决于您的实际状态类型v实际添加的方式,因此您必须自己定义函数

prepend
。我不知道,例如这样的插入将如何影响文件中的当前位置等。

Doaitse Swierstra