2017-04-20 75 views
0

我真的很難分析Haskell,但它最有意義。Parsec:處理重疊解析器

我正在建立一個模板化程序主要是爲了學習解析更好;模板可以通過{{ value }}表示法插值。

這是我目前的解析器,

data Template a = Template [Either String a] 
data Directive = Directive String 

templateFromFile :: FilePath -> IO (Either ParseError (Template Directive)) 
templateFromFile = parseFromFile templateParser 

templateParser :: Parser (Template Directive) 
templateParser = do 
    tmp <- template 
    eof 
    return tmp 

template :: Parser (Template Directive) 
template = Template <$> many (dir <|> txt) 
    where 
     dir = Right <$> directive 
     txt = Left <$> many1 (anyChar <* notFollowedBy directive) 

directive :: Parser Directive 
directive = do 
    _ <- string "{{" 
    txt <- manyTill anyChar (string "}}") 
    return $ Directive txt 

然後我在文件上是這樣運行:

{{ value }} 

This is normal Text 

{{ expression }} 

當我運行使用templateFromFile "./template.txt"這個我得到的錯誤:

Left "./template.txt" (line 5, column 17): 
unexpected Directive " expression " 

爲什麼會發生這種情況,我該如何解決?

我的基本理解是,many1 (anyChar <* notFollowedBy directive) 應抓住所有的字符,直到下一個指令的開始,然後應該失敗並返回字符列表直到該點;那麼 它應該回落到以前的many,並應嘗試再次解析dir並應成功;顯然還有其他事情正在發生。我是 在解析器大部分重疊時無法解析如何解析之間其他事情。

我很想知道如何更加地道地構建這些技巧,請讓我知道我是否以愚蠢的方式做事。乾杯!謝謝你的時間!

回答

2

你有幾個問題。首先,在Parsec中,如果解析器消耗任何輸入然後失敗,那就是錯誤。所以,當分析器:

anyChar <* notFollowedBy directive 

失敗(因爲性格後跟一個指令),它失敗anyChar已經消耗輸入,並立即生成一個錯誤。因此,解析器:

let p1 = many1 (anyChar <* notFollowedBy directive) 

如果運行到指令中將永遠不會成功。例如:

parse p1 "" "okay" -- works 
parse p1 "" "oops {{}}" -- will fail after consuming "oops " 

可以通過插入一個try子句解決這個問題:

let p2 = many1 (try (anyChar <* notFollowedBy directive)) 
parse p2 "" "okay {{}}" 

其產生Right "okay"和揭示了第二個問題。解析器p2僅消耗指令後面沒有的字符,因此排除緊接在指令之前的空間,並且在解析器中無法消耗後跟指令的字符,因此它會卡住。

你真正想要的東西,如:

let p3 = many1 (notFollowedBy directive *> anyChar) 

它首先檢查,在當前位置,我們是不是抓住一個字符之前尋找一個指令。否try子句是必需的,因爲如果失敗,它將失敗而不消耗輸入。 (notFollowedBy從來沒有消耗輸入,按文檔。)

parse p3 "" "okay" -- returns Right "okay" 
parse p3 "" "okay {{}}" -- return Right "okay " 
parse p3 "" "{{fails}}" -- correctly fails w/o consuming input 

所以,把你原來的例子有:

txt = Left <$> many1 (notFollowedBy directive *> anyChar) 

應該正常工作。

0
many1 (anyChar <* notFollowedBy directive) 

這隻解析沒有跟着指令的字符。

{{ value }} 

This is normal Text 

{{ expression }} 

當解析中間的文本,它會停在最後t,該指令未消費之前離開換行(因爲它的,好了,一個字符後跟一個指令),所以下一次迭代,你嘗試解析一個指令,你失敗了。然後你在那個新行上重試txt,解析器期望它不被一個指令跟着,但是它找到了一個,因此就是錯誤。

+0

有道理;我怎樣才能最好地解決它做我期望的? –

+0

'txt = many1(notFollowedBy directive *> anyChar)'是最簡單的修復方法,儘管它每個指令運行兩次'directive'。 –