在parsec中使用lexeme

Hsi*_*Dai 3 haskell parsec

我不明白如何使用lexeme函数

我看过上面的问题,但我还是不明白.

例如,文档中的示例也不起作用.

mainParser  = do{ whiteSpace
                ; ds <- many (lexeme digit)
                ; eof
                ; return (sum ds)
                }
Run Code Online (Sandbox Code Playgroud)

Zet*_*eta 5

免责声明:我还没有用过Parsec.话虽这么说,lexeme是一个领域GenTokenParser s u m.如果你在GHCi中检查它的类型,你最终会得到

lexeme :: GenTokenParser s u m -> ParsecT s u m a -> ParsecT s u m a
Run Code Online (Sandbox Code Playgroud)

因此,您已经需要一个可以创建的通用令牌解析器makeTokenParser.后者的类型如下:

makeTokenParser
  :: Stream s m Char =>
     Text.Parsec.Token.GenLanguageDef s u m
     -> Text.Parsec.Token.GenTokenParser s u m
Run Code Online (Sandbox Code Playgroud)

它需要一个语言定义并返回一个令牌解析器.由于您没有任何特定语言,您可以使用emptyDeffrom Text.Parsec.Language.注意,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"
Run Code Online (Sandbox Code Playgroud)

示例输出:

*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


kee*_*ing 5

免责声明:我不是 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
Run Code Online (Sandbox Code Playgroud)

让我们运行上面的代码。

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"
Run Code Online (Sandbox Code Playgroud)

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

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

哎呀!我们的解析器出了点问题。你能发现输入的差异吗?现在,如果您发现了差异,您可以看到在第二种情况下末尾有空格,但是为什么这个解析器能够处理数字文字之前的空格?记住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
Run Code Online (Sandbox Code Playgroud)

并在运行此代码后

*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"
Run Code Online (Sandbox Code Playgroud)

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