如何在Parsec中解析这个语法? (左递归的异常情况)

时间:2016-09-30 08:56:50

标签: parsing haskell parsec

我是Haskell,Parsec的初学者,一般都是编写解析器。我正在尝试解析一种简单的语言,它(为了这个问题进一步简化它)只包含嵌套括号的字符串,例如: [[][]][]

我有下面的Haskell代码,工作正常。但是,我想扩展它,以便不匹配的括号将匹配字符串的结尾。例如,]][][[应该等同于[[]][][[]],而[]]应该等同于[[]]。对匹配字符串结尾的开括号执行此操作很容易,但是对于匹配字符串开头的闭括号执行此操作会导致左递归和无限循环,而我还没有找到解决该问题的方法。我不确定原因是否与我正在考虑语法的方式或我使用Parsec库的方式有关,但不管怎样我都很欣赏被展示出前进的方向。

这是我的工作代码:

{-# LANGUAGE NoMonomorphismRestriction #-}

import qualified Text.Parsec as Parsec

import Control.Applicative

-- for testing
parse rule text = Parsec.parse rule "(source)" text

data Expr = Brackets [Expr]
        deriving(Show)

openBracket = Parsec.char '['
closeBracket = Parsec.char ']'

parseBrackets = do
        expr <- Parsec.between openBracket closeBracket parseExpr
        return $ Brackets expr

parseExpr = Parsec.many parseBrackets

如果我希望封闭的括号与字符串的末尾匹配,我可以将closeBracket的定义更改为

closeBracket = (Parsec.char ']' >> return ()) <|> Parsec.eof

但是,尽管有相当多的试验和错误,我还没有找到一个解决方案来匹配不匹配的] s与字符串的开头。我知道在Parsec中左递归的常用解决方案是chainl1函数,但这对于中缀运算符来说似乎非常专业,我在这里看不到使用它的方法。

2 个答案:

答案 0 :(得分:2)

以下是我对此的看法:

import qualified Text.Parsec as Parsec
import Text.Parsec.String (Parser)
import Control.Monad (void)
import Control.Applicative

data Expr = Brackets [Expr]
        deriving(Show)

parseTopLevel :: Parser [Expr]
parseTopLevel =
    ((:) <$> parseStart <*> parseExpr) <|> parseExpr

parseStart :: Parser Expr
parseStart = do
    closeBracket
    go (Brackets [])
  where
    go r = (closeBracket *> go (Brackets [r])) <|> return r

parseBrackets :: Parser Expr
parseBrackets = do
        expr <- Parsec.between openBracket closeBracket parseExpr
        return $ Brackets expr

parseExpr :: Parser [Expr]
parseExpr = Parsec.many parseBrackets

openBracket :: Parser ()
openBracket = void $ Parsec.char '['

closeBracket :: Parser ()
closeBracket = (void $ Parsec.char ']') <|> Parsec.eof

正如您所看到的,为了解析字符串开头的不平衡括号,我无法使用parsec附带的任何组合器,我只是编写了自己的parseStart。剩下的就是你的代码。

以下是您的示例所返回的内容:

λ> Parsec.parse parseTopLevel "" "]][][["
Right [Brackets [Brackets []],Brackets [],Brackets [Brackets []]]
λ> Parsec.parse parseTopLevel "" "[[]][][[]]"
Right [Brackets [Brackets []],Brackets [],Brackets [Brackets []]]

如您所见,它会为]][][[[[]][][[]]返回完全相同的内容。

答案 1 :(得分:1)

以下是基于redneb's improvements to my code的自我回答。此版本涵盖[]]之类的案例,其中redneb的代码忽略了不匹配的],而不是将它们与字符串的开头匹配。

此代码的工作原理是将整个表达式解析为] - 分隔的平衡表达式列表,然后从该列表中显式构建解析树。这种感觉有点像承认失败,因为解析树的构造发生在与实际解析分开的步骤中。那么似乎chainl1似乎是如何运作的,所以也许它毕竟是“正确的方式”。如果其他人有更好的解决方案,我不会接受我自己的答案。

import qualified Text.Parsec as Parsec
import Text.Parsec.String (Parser)
import Control.Monad (void)
import Control.Applicative

-- for testing
parse rule text = Parsec.parse rule "(source)" text

data Expr = Brackets [Expr]
        deriving(Show)

parseTopLevel :: Parser [Expr]
parseTopLevel = do
        exprList <- parseExprAsList
        return $ composeExpr exprList

composeExpr :: [[Expr]] -> [Expr]
composeExpr [exprList] = exprList
composeExpr (exprList:next:tail) = composeExpr $ (Brackets exprList:next) : tail

parseExprAsList :: Parser [[Expr]]
parseExprAsList = Parsec.sepBy parseBalancedExpr (Parsec.char ']')

parseBalancedExpr :: Parser [Expr]
parseBalancedExpr = Parsec.many parseBrackets

parseBrackets :: Parser Expr
parseBrackets = do
        expr <- Parsec.between openBracket closeBracket parseBalancedExpr
        return $ Brackets expr

openBracket :: Parser ()
openBracket = void $ Parsec.char '['

closeBracket :: Parser ()
closeBracket = (void $ Parsec.char ']') <|> Parsec.eof

一些测试用例:

*Main> parse parseTopLevel "[]]"
Right [Brackets [Brackets []]]
*Main> parse parseTopLevel "[[]]"
Right [Brackets [Brackets []]]

*Main> parse parseTopLevel "]["
Right [Brackets [],Brackets []]
*Main> parse parseTopLevel "[][]"
Right [Brackets [],Brackets []]

*Main> parse parseTopLevel "[]][]]]"
Right [Brackets [Brackets [Brackets [Brackets []],Brackets []]]]
*Main> parse parseTopLevel "[[[[]][]]"
Right [Brackets [Brackets [Brackets [Brackets []],Brackets []]]]