在Haskell中将字符插入解析器组合字符流中
此问题与Parsec
和uu-parsinglib
都有关。当我们编写解析器组合器时,它们处理来自编译器的字符流。是否有可能解析一个字符并将其放回(或返回另一个字符)到输入流?在Haskell中将字符插入解析器组合字符流中
我想例如解析输入“测试+ 5”,解析t
,e
,s
,t
和识别test
图案,把例如v
字符回到字符流之后,因此而continuating解析过程我们与v + 5
匹配我现在不想在任何特定情况下使用此功能 - 我想深入了解可能性。
我不确定这些解析器是否可以直接使用,但通常情况下,您可以通过将解析器与允许注入剩余物的一些流相结合来实现它。
例如,使用attoparsec-conduit则可以使用
sinkParser :: (AttoparsecInput a, MonadThrow m)
=> Parser a b -> Consumer a m b
转解析器到导管,其中Consumer
是一种特殊的不产生任何输出导管的,只接收的输入,并返回一个最终值。
由于管道支持剩饭,您可以创建转换一个解析器,可选择返回一个值被推到流进的管道中的helper方法:
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
,然后结合导管而不是解析器。
我认为最简单的方法来存档这是建立一个多层次的分析器。想想一个词法分析器+解析器组合。这是解决这个问题的一个干净方法。
你必须分开这两种解析。搜索和替换解析转到第一个解析器,而构建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")
。
可能的变型:
- 创建类和实例用于组合包裹解析器阶段。 (可能携带解析器的状态。)
- 创建的中间表示,令牌
流这很容易在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是如何实际增加,所以你必须给自己定义函数
prepend。我不知道这样的插入会如何影响该文件中的当前位置等
Doaitse Swierstra
这当然是可能的秒差距,你可以使用'getInput'和'setInput'功能。 –
是的,Parsec可以做到这一点,但它肯定会增加解析器的复杂性,并使调试更加困难。它的一个常见用途是实现编程语言的预处理器。 –