这个问题与 和 都Parsec
有关uu-parsinglib
。当我们编写解析器组合器时,它们会处理来自编译器的字符流。是否有可能解析一个字符并将其放回(或返回另一个字符)到输入流?
例如,我想解析输入“test + 5”,解析t
, e
, s
,t
并在识别test
模式后,将例如v
字符放回字符流中,因此在继续解析过程时,我们正在匹配v + 5
我现在不想在任何特定情况下使用它——我想深入了解这些可能性。
这个问题与 和 都Parsec
有关uu-parsinglib
。当我们编写解析器组合器时,它们会处理来自编译器的字符流。是否有可能解析一个字符并将其放回(或返回另一个字符)到输入流?
例如,我想解析输入“test + 5”,解析t
, e
, s
,t
并在识别test
模式后,将例如v
字符放回字符流中,因此在继续解析过程时,我们正在匹配v + 5
我现在不想在任何特定情况下使用它——我想深入了解这些可能性。
这很容易在 uu-parsinglib 中使用 pSwitch 函数完成。但问题是你为什么要这样做?因为输入中缺少 v?在这种情况下,uu-parsinglib 会自动执行纠错,所以你不需要这样的东西。否则你可以写
pSwitch :: (st1 -> (st2, st2 -> st1)) -> P st2 a -> P st1 a
pInsert_v = pSwitch (\st1 -> (prepend v st2, id) (pSucceed ())
这取决于您实际添加 v 的实际状态类型,因此您必须定义函数
前置你自己。我不知道例如这样的插入将如何影响文件中的当前位置等。
多艾特斯维尔斯特拉
我认为最简单的存档方法是构建一个多层解析器。想想词法分析器+解析器的组合。这是解决此问题的一种干净方法。
您必须将这两种解析分开。搜索和替换解析转到第一个解析器,构建 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")
。
可能的变化:
我不确定这些解析器是否可以直接使用,但一般来说,您可以通过将解析器与一些允许注入剩余部分的流结合起来来完成它。
例如,使用attoparsec-conduit可以将解析器转换为管道,使用
sinkParser :: (AttoparsecInput a, MonadThrow m)
=> Parser a b -> Consumer a m b
whereConsumer
是一种特殊的管道,它不产生任何输出,只接收输入并返回最终值。
由于管道支持剩余部分,因此您可以创建一个辅助方法,该方法将解析器转换为可选地返回要推送到流中的值到管道中:
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
,然后组合管道而不是解析器。