我使用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+-3
和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)))]
我認爲其原因是與我的運算符優先級,或者,在一般我實現的前綴-
運營商似乎是不可靠的。我將不勝感激指導。
謝謝!
是否有可能說服表達式解析器> - 是不是運營商? – kvanberendonck 2013-04-24 01:11:44
@ kvanberendonck:可能。不過,我並不十分熟悉它。 – 2013-04-24 01:33:46
這有點偏離主題,但我喜歡提示!我在哪裏可以找出它的含義?我猜這跟邏輯有關係? – ocharles 2013-04-24 06:47:27