我使用GHC在Windows上编译。这是我的代码(also available here):
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)))]
我认为这是因为与我的运算符优先级有关,或者,通常我的前缀-
运算符的实现似乎是不可靠的。我非常感谢指导。
谢谢!
答案 0 :(得分:4)
一些问题:
ident <- (optionMaybe $ try $ variable >> symbol "=")
这是解析并忽略variable
,然后返回symbol "="
的结果。此外,variable
在这里会出现类型错误。我会在这里使用identifier
进行测试,但你可能想要更高级的东西。
parse (whiteSpace >> many stat) "" input
您的测试输入表明您打算解析整个问题。你可能应该在最后吃空格,然后使用eof
来确保它消耗整个输入。
最后,在输入"x = arr[4]>-1"
上我很确定词法分析器将>-
视为单个标记,Haskell自己的语法也是如此。所以在这种情况下解析器是正确的(如果你添加我建议的eof
,将会出错)。请注意,这与赋值语句一起发生 not ,因为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 ⊢
答案 1 :(得分: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
构造函数的第一个字段是否应该是{{1}或者你是否应该以其他方式修复它。