我试图解析一个简单的表达式,但它有一个无限循环

时间:2016-05-06 12:49:14

标签: parsing haskell text-parsing parsec

我想解析像这样的语言

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"

有人可以解释为什么它不起作用以及如何解决它?

1 个答案:

答案 0 :(得分:3)

事情是,在您的代码中,exprList会在尝试时循环播放 解析一个标识符,即parse exprList "" "foo"进入 无限循环。这是因为它试图将其解析为列表 标签或表达式,表达式可以是列表。一旦 它无法成为exprLabel它试图查看它是否可以是expr等等 它再次调用exprList

要解决此问题,您需要确保expr检查是否存在 在尝试exprLabel之前identifierexprList。请注意,如果 以上所有失败它仍将进入循环。这是因为它不知道这只是列表的开头(或列表列表列表......)。

要解决此问题,您可以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")])