2014-02-08 126 views

回答

5

声明:我还没有使用过Parsec呢。也就是说,lexemeGenTokenParser s u m的字段。如果你检查它的类型在ghci中,你会

lexeme :: GenTokenParser s u m -> ParsecT s u m a -> ParsecT s u m a 

因此结束了,你已经需要一个通用的令牌解析器,您可以用makeTokenParser创建。后者的类型为:

makeTokenParser 
    :: Stream s m Char => 
    Text.Parsec.Token.GenLanguageDef s u m 
    -> Text.Parsec.Token.GenTokenParser s u m 

它需要一个语言定义并返回一个令牌解析器。由于您没有任何特定的语言,您可以使用Text.Parsec.Language中的emptyDef。请注意,whiteSpace也需要GenTokenParser。而在去年,在此设置你最终会与ds :: [Char],因此,你需要使用digitToIntData.Char之前,你其实可以总结你的数字:

import Text.Parsec 
import Text.Parsec.Token (lexeme, makeTokenParser, whiteSpace) 
import Text.Parsec.Language (emptyDef) 
import Data.Char (digitToInt) 

lexer = makeTokenParser emptyDef 

mainParser = do{ whiteSpace lexer 
    ; ds <- many (lexeme lexer digit) 
    ; eof 
    ; return (sum . map digitToInt $ ds) 
    } 

main = do 
    putStrLn "Please give some digits (whitespaces are ignored)" 
    line <- getLine 
    case parse mainParser "" line of 
    Right n -> putStrLn $ "Sum of digits is " ++ show n 
    Left _ -> putStrLn $ "Couldn't parse your line" 

输出示例:

*Main> :main 
Please give some digits 
7 8 91 72 3945 01 92 
Sum of digits is 67 

*Main> :main 
Please give some digits 
abc 1 
Couldn't parse your line
+0

Thakns,我看过GHCi的签名,但是hackage上的签名不匹配。他们应该更新文档和示例,它们甚至不是很接近。 –

+0

@HhyuDai:这个例子实际上很糟糕,因为'spaces >> many(oneOf ['0','9'])'已经是一个非常好的全面的数字解析器,就我的5分钟 - Parsec知识去。 – Zeta

4

免责声明:我不是Haskell或解析专家。我修改了上面的代码

import Text.Parsec 
import qualified Text.Parsec.Token as T 
import Text.Parsec.String (Parser) 
import Text.Parsec.Language (haskellDef) 

lexer = T.makeTokenParser haskellDef 

whiteSpace :: Parser() 
whiteSpace = T.whiteSpace lexer 


lexeme = T.lexeme lexer 

mainParser = do whiteSpace 
       ds <- many digit 
       eof 
       return ds 

让我们运行上面的代码。

Mukeshs-MacBook-Pro:Compilers mukeshtiwari$ ghci stmp.hs 
GHCi, version 7.6.1: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
[1 of 1] Compiling Main    (stmp.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> parse mainParser "" "1" 
Loading package array-0.4.0.1 ... linking ... done. 
Loading package deepseq-1.3.0.1 ... linking ... done. 
Loading package bytestring-0.10.0.0 ... linking ... done. 
Loading package transformers-0.3.0.0 ... linking ... done. 
Loading package mtl-2.1.2 ... linking ... done. 
Loading package text-0.11.2.3 ... linking ... done. 
Loading package parsec-3.1.3 ... linking ... done. 
Right "1" 
*Main> parse mainParser "" "12" 
Right "12" 
*Main> parse mainParser "" "123" 
Right "123" 
*Main> parse mainParser "" "  123" 
Right "123" 
*Main> parse mainParser "" " 123" 
Right "123" 
*Main> parse mainParser "" "  123" 
Right "123" 

到目前为止,每件东西都看起来不错。现在我们应该尝试一些更多的输入。

*Main> parse mainParser "" "123 " 
Left (line 1, column 4): 
unexpected ' ' 
expecting digit or end of input 

哎呀!我们的解析器出现了一些问题。你能发现输入的差异吗?现在,如果你发现了差异,你可以看到在第二种情况下末尾有空格,但是这个解析器如何在数字文字之前处理空格?记住whiteSpace函数,它会在数字文字之前吃掉所有空格,并将余下的输入提供给其余的代码(许多数字),在遇到非数字之前,它会继续消耗尽可能多的数字文字。同样,其余的输入(在我们的情况下剩余的空间)被传递给eof,所以我们的解析器抱怨空间。我们可以在阅读数字文字时忽略这些空间吗?我们知道,whiteSpace吃零或更多的空间,所以添加它的代码(忽略< *片刻)。

import Text.Parsec 
import qualified Text.Parsec.Token as T 
import Text.Parsec.String (Parser) 
import Text.Parsec.Language (haskellDef) 
import Control.Applicative ((<*)) 
lexer = T.makeTokenParser haskellDef 

whiteSpace :: Parser() 
whiteSpace = T.whiteSpace lexer 

lexeme = T.lexeme lexer 

mainParser = do whiteSpace 
       ds <- many (digit <* whiteSpace) 
       eof 
       return ds 

,并运行此代码

*Main> parse mainParser "" " 31312 " 
Right "31312" 
*Main> parse mainParser "" " 3131 2 " 
Right "31312" 
*Main> parse mainParser "" " 313 1 2 " 
Right "31312" 
*Main> parse mainParser "" " 3 1 3 1 2 " 
Right "31312" 
*Main> parse mainParser "" " 31 3 1 2 " 
Right "31312" 

后,现在它看起来不错。让我们试着看看这段代码如何处理这个空间。所有的初始空格都由whiteSpace拍摄,剩余的输入被传递给下一个函数(很多(数字< * whiteSpace))。这里数字消耗一个数字文字,而且WhiteSpace消耗零或更多的空间,并且这个计算的结果是数字的结果。查看文档lexeme,lexeme p首先应用解析器p和比whiteSpace解析器所以lexeme数字将首先消耗一个数字,然后零或更多的空间。

+0

这个答案的重要之处在于,示例中的'whiteSpace'和'lexeme'已经绑定到特定的'GenTokenParser',在这种情况下,示例更有意义。 +1。 (其实,我只是想编辑我的答案,但你的答案已经包含我想补充的内容:D)。 – Zeta