0

我使用 GHC 在 Windows 上编译。这是我的代码(也可在此处获得):

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+-3然而,像and这样的更复杂的表达式x = (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)))]

我认为这是因为与我的运算符优先级有关,或者一般来说我对前缀-运算符的实现似乎不可靠。我将不胜感激指导。

谢谢!

4

2 回答 2

4

几个问题:

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 ⊢
于 2013-04-24T00:31:51.777 回答
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构造函数的第一个字段是否应该改为 a Maybe Expr,或者您是否应该以其他方式修复它。

于 2013-04-24T00:28:11.993 回答