此问题与Parsec
和uu-parsinglib
都有关。当我们编写解析器组合器时,它们处理来自编译器的字符流。是否有可能解析一个字符并将其放回(或返回另一个字符)到输入流?
我希望例如解析输入“test + 5”,解析t
,e
,s
,t
以及识别test
之后模式,例如将v
字符放回到字符流中,因此在继续解析过程时,我们将匹配v + 5
我现在不想在任何特定情况下使用它 - 我想深入了解可能性。
答案 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