我想解析像这样的语言
foo = (bar, bar1 = (bar2 = bar4), bar5)
我写了一个简单的解析器
module SimpleParser where
import Text.Parsec.String (Parser)
import Text.Parsec.Language (emptyDef)
import Text.Parsec
import qualified Text.Parsec.Token as Tok
import Text.Parsec.Char
import Prelude
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = emptyDef {
Tok.identLetter = alphaNum
}
parens :: Parser a -> Parser a
parens = Tok.parens lexer
commaSep :: Parser a -> Parser [a]
commaSep = Tok.commaSep lexer
identifier :: Parser String
identifier = Tok.identifier lexer
reservedOp :: String -> Parser ()
reservedOp = Tok.reservedOp lexer
data Expr = Ident String | Label String Expr | ExprList [Expr] deriving (Eq, Ord, Show)
parseExpr :: String -> Either ParseError Expr
parseExpr s = parse expr "" s
expr :: Parser Expr
expr = parens expr
<|> try exprList
<|> ident
ident :: Parser Expr
ident = do
var <- identifier
return $ Ident var
exprLabel :: Parser Expr
exprLabel = do
var <- identifier
reservedOp "="
body <- expr
return $ Label var body
exprList :: Parser Expr
exprList = do
list <- commaSep (try exprLabel <|> expr)
return $ ExprList list
但即使使用以下简单输入,它也有一个无限循环:
test = parseExpr "foo = bar"
有人可以解释为什么它不起作用以及如何解决它?
答案 0 :(得分:3)
事情是,在您的代码中,exprList
会在尝试时循环播放
解析一个标识符,即parse exprList "" "foo"
进入
无限循环。这是因为它试图将其解析为列表
标签或表达式,表达式可以是列表。一旦
它无法成为exprLabel
它试图查看它是否可以是expr
等等
它再次调用exprList
。
要解决此问题,您需要确保expr
检查是否存在
在尝试exprLabel
之前identifier
或exprList
。请注意,如果
以上所有失败它仍将进入循环。这是因为它不知道这只是列表的开头(或列表列表列表......)。
要解决此问题,您可以expr
仅与exprList
匹配parens
,并使用exprList
作为起始Parser
。
expr :: Parser Expr
expr = parens (exprList)
<|> try exprLabel
<|> ident
exprList :: Parser Expr
exprList = do
list <- commaSep expr
return $ ExprList list
它的工作原理如下:
>parse exprList "" "(foo=bar),foo=bar"
Right (ExprList [ExprList [Label "foo" (Ident "bar")],Label "foo" (Ident "bar")])