我有这样的字符串:***
,**(*)*
,****(**(**)*)**
我想在数据结构中解析它:
{p>data Tree = Node [S] Tree Tree | Empty
其中S
为*
(*
并不意味着任何字符只是星号()
我尝试构建解析器(我使用megaparsec
但它与习惯parsec
非常相似):
data Tree = Node [Char] Tree Tree | Empty deriving (Show)
chain :: Parser Tree
chain = do
line <- many $ char '*'
branch <- between (char '(') (char ')') chain
cont <- (eof >> return Empty) <|> chain
return $ Node line branch cont
test = parseTest chain "****(*(*)***)*(*)**"
但它没有用。我尝试了很多方法,但不能挣扎。
答案 0 :(得分:1)
让我们从一个更简单的测试用例开始:
> parseTest chain "*"
parse error at (line 1, column 2):
unexpected end of input
expecting "*" or "("
请注意,读取第一颗星后会出现解析错误。输入结束,但解析器期望读取另一个星形或左括号。
看着你的解析器,它很清楚:
line <- many $ char '*'
通过阅读第一串星星成功,但下一行:
branch <- between (char '(') (char ')') chain
在输入中需要一个左括号,并且不会以任何方式使其成为可选项。
我们可以通过写:
来解决这个问题branch <- option Empty $ between (char '(') (char ')') chain
现在,解析器在"***"
上工作正常,但它挂在"**(*)*"
上。问题是这一行:
cont <- (eof >> return Empty) <|> chain
这会尝试根据检测到输入结束来决定何时停止解析,但这仅适用于顶级chain
调用,其中当前树的末尾对应于输入的结尾 - 在一个递归调用,树可以在输入之前结束,所以这不会起作用。
具体来说,在测试用例"**(*)*"
中,当解析括号内的树,即*
时,我们将line
设置为*
,branch
设置为Empty
,然后cont
行看到我们不在输入的末尾(因为仍然要读取输入的")*"
的其余部分)并且递归调用chain
。在此递归调用中,line
设置为空字符串,branch
设置为Empty
,cont
行再次导致对chain
的递归调用,我们有一个无限循环。
相反,让我们编写一个解析树的tree
的解析器line
:
tree = do
line <- many $ char '*'
现在可选括号中的tree
(左侧):
mleft <- optionMaybe $ between (char '(') (char ')') tree
如果没有左手边,那么也不能是右手边(让自己相信这是真的! - 尝试写一个在括号中没有左手边的树,但仍然有一个非空的右侧,你会发现它无法完成),所以我们已经完成了:
case mleft of
Nothing -> return $ Node line Empty Empty
如果有左侧,则读取右侧树(可能是空的,但没关系)并返回节点:
Just left -> do
right <- tree
return $ Node line left right
整个解析器看起来像:
tree :: Parser Tree
tree = do
line <- many $ char '*'
mleft <- optionMaybe $ between (char '(') (char ')') tree
case mleft of
Nothing -> return $ Node line Empty Empty
Just left -> do
right <- tree
return $ Node line left right
并希望能达到您的期望:
> parseTest tree "*"
Node "*" Empty Empty
> parseTest tree "***"
Node "***" Empty Empty
> parseTest tree "**(*)*"
Node "**" (Node "*" Empty Empty) (Node "*" Empty Empty)
> parseTest tree "****(**(**)*)**"
Node "****" (Node "**" (Node "**" Empty Empty)
(Node "*" Empty Empty)) (Node "**" Empty Empty)
此解析器只是忽略尾随输入:
> parseTest tree "*hello*"
Node "*" Empty Empty
但你可以编写一个包装器来要求最外面的树的末尾对应于输入的结尾:
treeOnly :: Parser Tree
treeOnly = tree <* eof