2013-04-24 52 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,或者您是否应该以其他方式解决问题。