2016-01-10 48 views
2

我想在使用Parsec解析時獲得最終令牌位置。用Parsec獲取最終位置

舉例來說,如果我用下面的方式identifier組合子:

test = do 
    start <- getPosition 
    result <- identifier 
    end <- getPosition 

end不會指向標識結束。它會指向下一個標記(跳過空格)。

我可以編寫我自己的identifier combinator以在跳過空格之前獲得最終位置,但是我不知道Parsec中是否已經有這樣做。

是否有一種方式來獲得與秒差距一組合子的結束位置?

+0

不只是'start'加上'result'的長度? –

+0

那麼,在這種情況下,是的。但是在更復雜的情況下(多行字符串或函數定義),計算結束位置將更加困難。 – antoyo

回答

1

如果您的意思是Text.Parsec.Tokenidentifier,我不認爲這會很容易。 identifier使用lexeme定義:

identifier = 
    lexeme $ try $ 
    do{ name <- ident 
     ; if (isReservedName name) 
     then unexpected ("reserved word " ++ show name) 
     else return name 
     } 

lexeme消耗空白:

lexeme p 
    = do{ x <- p; whiteSpace; return x } 

這意味着由你從identifier結果的時間,識別符的結束位置都將丟失。

我不認爲有一個優雅的解決方案。難看是的makeTokenParser定義從Text.Parsec.Token複製和更改其lexeme定義,以便它不跳過空白。然後你就可以有這樣的:

myMakeTokenParser :: (Stream s m Char) 
        => GenLanguageDef s u m -> GenTokenParser s u m 
myMakeTokenParser languageDef 
    = TokenParser{ identifier = identifier 
       , reserved = reserved 
    ... 
    lexeme p = p 
    ... 
    } 

lexer = myMakeTokenParser haskellDef 
identifier' = identifier lexer 
test = do 
    start <- getPosition 
    result <- identifier' 
    end <- getPosition 
    return (result, (start, end)) 

main = parseTest test "abc def " 

> :main 
("abc",((line 1, column 1),(line 1, column 4))) 

當然,現在你必須要小心,因爲解析器不會空白跳過了。另一種方法是,使lexeme記錄內解析器(例如identifier)作爲「用戶狀態」的部分的端部的位置,然後跳過空白如常。然後您可以從用戶狀態中檢索結束位置。