如果我想将包含多个带括号的组的字符串解析为包含每个组的字符串列表,例如
"((a b c) a b c)"
到
["((a b c) a b c)","( a b c)"]
我如何使用parsec做到这一点? between
的使用看起来不错,但似乎无法用开始值和结束值分开。
答案 0 :(得分:11)
我使用递归解析器:
data Expr = List [Expr] | Term String
expr :: Parsec String () Expr
expr = recurse <|> terminal
其中terminal
是您的原语,在这种情况下,这些似乎是字符串,所以
where terminal = Term <$> many1 letter
和recurse
是
recurse = List <$>
(between `on` char) '(' ')' (expr `sepBy1` char ' ')
现在我们有一个很好的Expr
s树,我们可以用
collect r@(List ts) = r : concatMap collect ts
collect _ = []
答案 1 :(得分:9)
虽然jozefg的解决方案几乎与我提出的解决方案相同(我完全同意他的所有建议),但是有一些小的差异让我认为我应该发布第二个答案:
所以这是我的版本。正如jozefg已经建议的那样,将任务分成几个子任务。那些是:
关于1,我们首先需要树数据类型
import Text.Parsec
import Text.Parsec.String
import Control.Applicative ((<$>))
data Tree = Leaf String | Node [Tree]
然后是一个可以将字符串解析为此类值的函数。
parseTree :: Parser Tree
parseTree = node <|> leaf
where
node = Node <$> between (char '(') (char ')') (many parseTree)
leaf = Leaf <$> many1 (noneOf "()")
在我的版本中,我确实将括号内的孔字符串视为Leaf
节点(即,我不在白色空格处分割)。
现在我们需要收集我们感兴趣的树的子树:
nodes :: Tree -> [Tree]
nodes (Leaf _) = []
nodes t@(Node ts) = t : concatMap nodes ts
最后,Show
- Tree
的实例允许我们将它们转换为字符串。
instance Show Tree where
showsPrec d (Leaf x) = showString x
showsPrec d (Node xs) = showString "(" . showList xs . showString ")"
where
showList [] = id
showList (x:xs) = shows x . showList xs
然后可以解决原始任务,例如:
parseGroups :: Parser [String]
parseGroups = map show . nodes <$> parseTree
> parseTest parseGroups "((a b c) a b c)"
["((a b c) a b c)","(a b c)"]