2012-11-25 42 views
1

在下面的代碼,我可以每個令牌後使用正確解析空格秒差距:爲什麼不秒差距讓表中的運營商之前,空格爲buildExpressionParser

whitespace = skipMany (space <?> "") 

number :: Parser Integer 
number = result <?> "number" 
    where 
    result = do { 
    ds <- many1 digit; 
    whitespace; 
    return (read ds) 
    } 

table = result 
    where 
    result = [ 
    [Infix (genParser '*' (*)) AssocLeft, 
    Infix (genParser '/' div) AssocLeft], 
    [Infix (genParser '+' (+)) AssocLeft, 
    Infix (genParser '-' (-)) AssocLeft]] 
    genParser s f = char s >> whitespace >> return f 

factor = parenExpr <|> number <?> "parens or number" 
    where 
    parenExpr = do { 
    char '('; 
    x <- expr; 
    char ')'; 
    whitespace; 
    return x 
    } 

expr :: Parser Integer 
expr = buildExpressionParser table factor <?> "expression" 

不過,我得到一個解析錯誤時試圖解析空格前,後運營商:

whitespace = skipMany (space <?> "") 

number :: Parser Integer 
number = result <?> "number" 
    where 
    result = do { 
    ds <- many1 digit; 
    return (read ds) 
    } 

table = result 
    where 
    result = [ 
    [Infix (genParser '*' (*)) AssocLeft, 
    Infix (genParser '/' div) AssocLeft], 
    [Infix (genParser '+' (+)) AssocLeft, 
    Infix (genParser '-' (-)) AssocLeft]] 
    genParser s f = whitespace >> char s >> whitespace >> return f 

factor = parenExpr <|> number <?> "parens or number" 
    where 
    parenExpr = do { 
    char '('; 
    x <- expr; 
    char ')'; 
    return x 
    } 

expr :: Parser Integer 
expr = buildExpressionParser table factor <?> "expression" 

解析錯誤是:

$ ./parsec_example < <(echo "2 * 2 * 3") 
"(stdin)" (line 2, column 1): 
unexpected end of input 
expecting "*" 

爲什麼會發生這種情況?有沒有其他的方法來解析的空白區域的運營商?

回答

5

當我測試你的代碼時,2 * 2 * 3解析正確,但2 + 2沒有。解析失敗,因爲*的解析器消耗了一些輸入,並且在該位置未啓用回溯,所以不能嘗試其他解析器。

buildExpressionParser創建的表達式解析器會嘗試依次解析每個運算符,直到成功。當解析2 + 2,將發生以下情況:

  • 第一2number匹配。其餘的輸入是 + 2(注意開始處的空格)。
  • 解析器genParser '*' (*)應用於輸入。它消耗空間,但不匹配+字符。
  • 其他中綴運算符解析器自動失敗,因爲某些輸入被genParser '*' (*)消耗。

您可以通過在try包分析器的重要組成部分解決這個問題。這將保存輸入,直到char s成功。如果char s失敗,則buildExpressionParser可以回溯並嘗試另一箇中綴運算符。

genParser s f = try (whitespace >> char s) >> whitespace >> return f 

此解析器的缺點是,因爲它回溯到一個管道符前的前導空格之前,它將重複掃描空白。在成功匹配後解析空白通常會更好,如OP的第一個解析器示例。

+3

請注意,OP的示例在輸入結尾處有一個換行符('echo'除非用'-n'調用,否則會添加一個換行符)。 ''2 * 2 * 3「'起作用,但''2 * 2 * 3 \ n」'不起作用。 – hammar