[英]How to parse this grammar in Parsec? (unusual case of left recursion)
我是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
为
closeBracket = (Parsec.char ']' >> return ()) <|> Parsec.eof
但是,尽管反复试验了很多,但我还没有找到一种解决方案,可以将不匹配的]
匹配到字符串的开头。 我知道Parsec中向左递归的通常解决方案是chainl1
函数,但这似乎对中缀运算符非常专业,我在这里看不到使用它的方法。
这是我对此的看法:
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 []]]
如您所见,它会根据需要为]][][[
和[[]][][[]]
返回完全相同的内容。
这是一个基于redneb对我的代码的改进的自我解答。 此版本涵盖[]]
,其中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 []]]]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.