2017-06-11 35 views
0

在遵循教程Write yourself a Scheme in 48 hours的過程中,我試圖增強解析代碼以創建對十六進制,八進制,二進制和十進制文字的支持。使用Parsec設計解析代碼

import Text.ParserCombinators.Parsec hiding (spaces) 
import Control.Monad 
import Numeric 

hexChar :: Parser Char 
hexChar = ... 

octChar :: Parser Char 
octChar = ... 

hexNumber :: Parser Integer 
hexNumber = do 
    char '#' 
    char 'x' 
    s <- many1 hexChar 
    return $ (fst . head) readHex s 


octNumber :: Parser Integer 
octNumber = do 
    char '#' 
    char 'o' 
    s <- many1 hexChar 
    return $ (fst . head) readOct s 

如果我們在這個討論中忘記了十進制和二進制數:

parseNumber :: Parse Integer 
parseNumber = hexNumber <|> octNumber 

那麼這個解析器將無法識別八進制數。這似乎與需要從十六進制數字中區分出十六進制數和八進制數的前瞻字符的數量有關(如果我們在語法中刪除前導'#',那麼解析器將工作)。因此,我們似乎不得不重新審視的代碼和「分比化」龍頭「#」可以這麼說,通過降低在個別解析器的char '#'和定義:

parseNumber = char '#' >> (hexNumber <|> octNumber) 

這是不錯,但我發現的代碼少愉快。不知何故,如果我有一個叫hexNumber的函數,我希望它識別#xffff(這是合適的Scheme語法),而不是xffff。這是我必須忍受的事情,還是有辦法繞過主角#的強制分解?

回答

1

如果(<|>)的第一個參數在消耗了一些輸入後失敗,那麼它會立即失敗,而不會嘗試第二個選擇。如果第一個參數失敗應該導致使用第二個參數重試,則可以使用try來避免消耗輸入。在hexNumber中,只有在以下字符匹配'x'時,您才必須使用'#'

hexNumber :: Parser Integer 
hexNumber = do 
    try $ char '#' >> char 'x' 
    s <- many1 hexChar 
    return $ (fst . head) readHex s 


octNumber :: Parser Integer 
octNumber = do 
    try $ char '#' >> char 'o' 
    s <- many1 hexChar 
    return $ (fst . head) readOct s 

注意,因爲你解析'#'兩次,且作爲共用前綴變得更長,更復雜的事情變得更糟糕,這是有點低效。

+0

非常感謝你! –