2015-08-31 61 views
3

我想分析與同$(如$a=$b)開頭的變量表達式開頭的變量,使用秒差距的令牌和Expr的模塊。這裏是我的代碼的簡化版本:

module Main where 

import Control.Monad.Identity 
import Control.Applicative 

import Text.Parsec 
import Text.Parsec.String 

import qualified Text.Parsec.Token as Tok 
import qualified Text.Parsec.Language as Tok 
import qualified Text.Parsec.Expr  as Ex 

data Expr 
    = BinaryOp String Expr Expr 
    | Var String 
    deriving (Show) 

lexer :: Tok.TokenParser() 
lexer = Tok.makeTokenParser style 
    where 
    style = Tok.emptyDef 
     { Tok.reservedOpNames = ["="] 
     , Tok.reservedNames = [] 
     , Tok.identStart  = letter 
     , Tok.identLetter  = alphaNum 
     } 

reservedOp = Tok.reservedOp lexer 
identifier = Tok.identifier lexer 
whiteSpace = Tok.whiteSpace lexer 

parseExpr :: String -> Either ParseError Expr 
parseExpr = parse (whiteSpace *> expr <* eof) "" 

expr :: Parser Expr 
expr = Ex.buildExpressionParser opTable terms <?> "expression" 
    where 
    opTable = 
     [ [ Ex.Infix (reservedOp "=" >> return (BinaryOp "=")) Ex.AssocLeft ] ] 
    terms = 
     try var 

var :: Parser Expr 
var = Var <$> (char '$' >> identifier) 

-- 

main :: IO() 
main = case parseExpr "$a=$b" of 
    Left err -> print err 
    Right expr -> print expr 

這工作得很好用空白圍繞運營商的表達式(如$a = $b),但沒有空格($a=$b)我得到的錯誤:

(line 1, column 5): 
unexpected '$' 
expecting operator 

而且,如果我修改解析器以解析不以$開頭的變量,則解析器可以使用和不使用空格。因此,$與運算符之間沒有空白的組合似乎存在問題。

回答

3

的問題是在默認的令牌解析器opStartopLetter的定義:

emptyDef :: LanguageDef st 
emptyDef = LanguageDef 
       { commentStart = "" 
       ... 
       , opStart  = opLetter emptyDef 
       , opLetter  = oneOf ":!#$%&*+./<=>[email protected]\\^|-~" 
       ... 
       } 

令牌解析器貪婪地匹配使用opStartopLetter運營商的名字,所以$a=$b分析一樣$a =$ b。並且由於=$不是運算符,所以會出現語法錯誤。

如果從opLetter東西刪除$應該工作,例如:

lexer :: Tok.TokenParser() 
lexer = Tok.makeTokenParser style 
    where 
    style = Tok.emptyDef 
     { Tok.reservedOpNames = ["="] 
     , Tok.reservedNames = [] 
     , Tok.identStart  = letter 
     , Tok.identLetter  = alphaNum 
     , Tok.opLetter  = oneOf ":!#%" -- add this line 
     } 
+0

真棒,謝謝! – dermoritz