2013-04-24 63 views
0

我使用GHC在Windows上進行編譯。這裏是我的代碼(also available here):Haskell parsec前綴運算符問題

module GMC.GMLParser (parseGML) where 

import Control.Applicative ((<$>), (<*>)) 
import Text.ParserCombinators.Parsec 
import Text.ParserCombinators.Parsec.Expr 
import Text.ParserCombinators.Parsec.Language 
import qualified Text.ParserCombinators.Parsec.Token as P 

type VarIdent = String 
type FunIdent = String 

data Expr = 
     Var VarIdent 
    | IntLit Integer 
    | StringLit String 
    | BiLit Bool 
    | Op String Expr Expr 
    | UnOp String Expr 
    | Call FunIdent [Expr] 
    deriving (Eq, Show) 

data Stat = 
     Seq [Stat] 
    | Skip 
    | Assign (Maybe VarIdent) Expr 
    | If Expr Stat (Maybe Stat) 
    deriving (Eq, Show) 

lexer = P.makeTokenParser gmlDef 

parens   = P.parens lexer  
braces   = P.braces lexer  
semi   = P.semi lexer 
semiSep   = P.semiSep lexer 
semiSep1  = P.semiSep1 lexer  
commaSep  = P.commaSep lexer 
commaSep1  = P.commaSep1 lexer 
brackets  = P.brackets lexer 
whiteSpace  = P.whiteSpace lexer  
symbol   = P.symbol lexer  
identifier  = P.identifier lexer  
reserved  = P.reserved lexer  
reservedOp  = P.reservedOp lexer 
integer   = P.integer lexer  
charLiteral  = P.charLiteral lexer  
stringLiteral = P.stringLiteral lexer 


operators = 
    [ [ prefix "-" ] 
    , [ op "*" AssocLeft, op "/" AssocLeft ] 
    , [ op "+" AssocLeft, op "-" AssocLeft ] 
    , [ op "=" AssocNone, op "<>" AssocNone, op "<=" AssocNone 
     , op "<" AssocNone, op ">=" AssocNone, op ">" AssocNone ] 
    , [ op "&" AssocRight, op "&&" AssocRight ] -- Right for shortcircuiting 
    , [ op "|" AssocRight, op "||" AssocRight ] -- Right for shortcircuiting 
    , [ op ":=" AssocRight ] 
    ] 
    where 
     op name assoc = Infix (do{ reservedOp name 
            ; return (\x y -> Op name x y) 
            }) assoc 
     prefix name  = Prefix (do{ reservedOp name 
            ; return (\x -> UnOp name x) 
            }) 


gmlDef :: LanguageDef st 
gmlDef = emptyDef 
    { commentStart = "/*" 
    , commentEnd  = "*/" 
    , commentLine  = "//" 
    , nestedComments = True 
    , identStart  = letter 
    , identLetter  = alphaNum <|> oneOf "_" 
    , reservedNames = [] 
    , reservedOpNames = [] 
    , caseSensitive = True 
    } 

parseGML :: String -> Either ParseError [Stat] 
parseGML input = parse (whiteSpace >> many stat) "" input 

intLit :: Parser Expr 
intLit = IntLit <$> integer 

strLit :: Parser Expr 
strLit = StringLit <$> stringLiteral 

variable :: Parser Expr 
variable = do ident <- identifier 
       memb <- optional $ symbol "." -- ignored for now, only parse its existance 
       vname <- optional identifier -- ignored for now, only parse its existance 
       indx <- optional $ brackets expr -- ignored for now, only parse its existance 
       return (Var ident) 

expr :: Parser Expr 
expr = buildExpressionParser operators genExpr 

genExpr :: Parser Expr 
genExpr = choice [ intLit 
       , strLit 
       , try callExpr 
       , variable 
       , parens expr 
       ] 

callExpr :: Parser Expr 
callExpr = Call <$> identifier <*> parens (commaSep expr) 

stat :: Parser Stat 
stat = do optional $ skipMany1 semi 
      choice [ ifStat 
        , assignStat 
        , seqStat 
        ] 

seqStat :: Parser Stat 
seqStat = do stmts <- braces $ many stat 
      return $ if null stmts then Skip else Seq stmts 

ifStat :: Parser Stat 
ifStat = If <$> (reserved "if" >> expr) 
      <*> (optional (reserved "then") >> stat) 
      <*> (optionMaybe $ reserved "else" >> stat) 

assignStat :: Parser Stat 
assignStat = do ident <- (optionMaybe $ try $ variable >> symbol "=") 
       stmt <- case ident of 
        Just x -> expr 
        Nothing -> callExpr 
       return (Assign ident stmt) 

討論的問題是,解析前綴的實數和變量給出奇怪的結果。

x=-3給出[Assign (Just "=") (UnOp "-" (IntLit 3))]這是正確的。然而,像x=5+-3x = (arr[4]>-1 && 1)這樣的更復雜的表達式似乎會給出不正確的結果。

x = arr[4]>-1給然而[Assign (Just '=') (Var "arr")]應該[Assign (Just "x") (Op ">" (Var "arr") (UnOp "-" (IntLit 1)))]

x=5+-3奇怪給[Assign (Just "=" (IntLit 5))當它應該是[Assign (Just "x") (Op "+" (IntLit 5) (UnOp "-" (IntLit 3)))]

我認爲其原因是與我的運算符優先級,或者,在一般我實現的前綴-運營商似乎是不可靠的。我將不勝感激指導。

謝謝!

回答

3

的幾個問題:

ident <- (optionMaybe $ try $ variable >> symbol "=") 

這是解析和忽略variable,然後返回的symbol "="結果。此外,variable在這裏將是一個類型錯誤。我會用identifier代替這裏的測試,但你可能想要更有趣的東西。

parse (whiteSpace >> many stat) "" input 

您的測試輸入表明您打算整個事情被解析。您最後應該吃空白,然後使用eof以確保它消耗整個輸入。

最後,在輸入"x = arr[4]>-1"我很確定詞法分析器認爲>-是一個單一的標記,就像Haskell自己的語法一樣。所以在這種情況下,解析器是正確的(如果你添加我建議的eof,將會出錯)。請注意,這是而不是與賦值語句發生,因爲它不由Parsec的表達式分析器分析。

這裏的輸出進行這些更改(請原諒我的古怪GHCI提示)後,我得到:

∀x. x ⊢ parseGML "x=-3" 
Right [Assign (Just "x") (UnOp "-" (IntLit 3))] 
∀x. x ⊢ parseGML "x = arr[4]>-1" 
Left (line 1, column 11): 
unexpected '>' 
expecting ";", "if", identifier, "{" or end of input 
∀x. x ⊢ parseGML "x = arr[4]> -1" 
Right [Assign (Just "x") (Op ">" (Var "arr") (UnOp "-" (IntLit 1)))] 
∀x. x ⊢ parseGML "x = 5+-3" 
Left (line 1, column 6): 
unexpected '+' 
expecting ";", "if", identifier, "{" or end of input 
∀x. x ⊢ parseGML "x = 5+ -3" 
Right [Assign (Just "x") (Op "+" (IntLit 5) (UnOp "-" (IntLit 3)))] 
∀x. x ⊢ 
+0

是否有可能說服表達式解析器> - 是不是運營商? – kvanberendonck 2013-04-24 01:11:44

+0

@ kvanberendonck:可能。不過,我並不十分熟悉它。 – 2013-04-24 01:33:46

+0

這有點偏離主題,但我喜歡提示!我在哪裏可以找出它的含義?我猜這跟邏輯有關係? – ocharles 2013-04-24 06:47:27

3

你行128:

assignStat = do ident <- (optionMaybe $ try $ variable >> symbol "=") 

我要專注於該位:

variable >> symbol "=" 

這裏做的事情:

  • 解析一個變量,扔掉的解析結果
  • 解析=標記
  • 返回解析=作爲整體的解析結果的結果

你想,而不是發生什麼:

  • 解析變量
  • 解析=令牌,扔掉解析結果
  • 返回解析變量的結果作爲整體解析的結果

什麼改變的代碼片段來:

variable <* symbol "=" 

(您可能需要從Control.Applicative進口(<*)

除非它不是簡單:詞彙具有以下類型:

variable >> symbol "=" :: Parser String 
variable <* symbol "=" :: Parser Expr 

你必須制定出自己variable是否真的合適解析器在這一點上打電話,或者是否Align利弊的第一場結構工廠應該是Maybe Expr,或者您是否應該以其他方式解決問題。