此問題與Parsec
和uu-parsinglib
都有關。當我們編寫解析器組合器時,它們處理來自編譯器的字符流。是否有可能解析一個字符並將其放回(或返回另一個字符)到輸入流?在Haskell中將字符插入解析器組合字符流中
我想例如解析輸入「測試+ 5」,解析t
,e
,s
,t
和識別test
圖案,把例如v
字符回到字符流之後,因此而continuating解析過程我們與v + 5
匹配我現在不想在任何特定情況下使用此功能 - 我想深入瞭解可能性。
此問題與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可以做到這一點,但它肯定會增加解析器的複雜性,並使調試更加困難。它的一個常見用途是實現編程語言的預處理器。 –