2013-04-16 62 views
6

我是新來秒差距(和一般解析器),我有這個分析器我寫了一些麻煩:很難得到一個秒差距解析器跳過空格正確

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 

的想法是分析列出了此格式(我工作到s表達式):

(firstElement secondElement thirdElement and so on) 

我寫了這個代碼來測試它:

import Control.Applicative 
import Text.ParserCombinators.Parsec hiding (many) 

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 

test s = do 
    putStrLn $ "Testing " ++ show s ++ ":" 
    parseTest list s 
    putStrLn "" 

main = do 
    test "()" 
    test "(hello)" 
    test "(hello world)" 
    test "(hello world)" 
    test "(hello world)" 
    test "()" 

ŧ他是輸出I得到:

Testing "()": 
[] 

Testing "(hello)": 
["hello"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
parse error at (line 1, column 14): 
unexpected ")" 
expecting space or letter 

Testing "()": 
parse error at (line 1, column 3): 
unexpected ")" 
expecting space or letter 

正如你可以看到,當有列表的最後一個元素,並關閉)之間的空白失敗。我不明白爲什麼我在<* char ')'之前放入的spaces沒有消耗空白空間。我犯了什麼愚蠢的錯誤?

回答

12

的問題是,最終的空間由在參數中spaces消耗many

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 
        -- ^^^^^^ that one 

然後解析器預計some letter但發現一個右括號和因而失敗。

爲了解決這個問題,佔用空間只有令牌,

list = char '(' *> spaces *> many (some letter <* spaces) <* char ')' 

,它作爲期望:

$ runghc lisplists.hs 
Testing "()": 
[] 

Testing "(hello)": 
["hello"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "()": 
[] 
0

這是一個有點棘手。默認情況下解析器是貪婪的。你的情況意味着什麼?當你試圖解析(hello world)你從解析(開始,那麼你試圖匹配一些空格和標識符。所以我們這樣做。沒有空格,但有標識符。我們完了。我們再次嘗試與世界。現在我們剩下了_)。您嘗試解析器(spaces *> some letter)。它使它變得貪婪:所以你匹配空間,現在你期望得到一些字母,但是你會得到)。此時解析器失敗了,但它已經佔用了空間,所以你註定要失敗。你可以讓這個解析器不使用try組合子回溯:try (many (spaces *> some letter))

3

的問題是,一旦解析器many (spaces *> some letter)看到它致力於在默認情況下解析另一個項目,因爲秒差距的空間只有向前看一個字符,不走回頭路。

大錘解決方案是使用try使回溯,但像這樣的問題最好由後簡單地解析可選空白避免每個令牌代替,如Daniel's answer看到。