我想解析以下文字:
keyword some more values
funcKeyw funcName1
funcKeyw funcName2
funcKeyw funcName3
keyword some more values
funcKeyw funcName2
keyword some more values
funcKeyw funcName4
缩进由标签完成。每个块由keyword
启动,并在同一行中添加一些其他值。缩进的所有内容都属于同一个块。 在所有函数调用(以funcKeyw
关键字开头)后,可能会有子keyword
块(由“空”行分隔;“空”表示没有任何内容它或空白字符)。
type IndentLevel = Int
data Block = Block { blockFuncCalls :: [String]
, blockBlocks :: [Block]
}
block :: GenParser Char st Block
block = parseBlock 0
where
parseBlock lvl = do
count lvl tab
string "keyword"
-- [...] Parse other stuff in that line.
newline
-- Parse 'function calls'.
fs <- sepBy1 (blockFunc (lvl + 1)) emptyLines
-- Parse optional child blocks.
emptyLines
bs <- sepBy (parseBlock (lvl + 1)) emptyLines
return Block { blockFuncCalls=fs
, blockBlocks=bs
}
blockFunc :: IndentLevel -> GenParser Char st String
blockFunc lvl = do
count lvl tab
string "funcKeyw"
-- [...] Parse function name etc..
newline
return funcName -- Parsed func name.
emptyLine :: GenParser Char st ()
emptyLine = many (oneOf "\t ") >> newline >> return ()
emptyLines :: GenParser Char st ()
emptyLines = many emptyLine >> return ()
问题是blockFunc
解析器在子块启动时不会停止解析,但会返回错误unexpected 'keyword'
。
我该如何避免?我想我可以使用try
或choice
为每一行选择正确的解析器,但我想要求函数调用在子块之前。
答案 0 :(得分:2)
我注意到的一件事是sepBy
组合器有一些意想不到的行为,即如果分隔符开始被解析,那么失败,整个sepBy
失败,而不是简单返回到目前为止解析的内容。您可以使用以下变体,这些变体因try
内的其他sepBy1Try
而异:
sepBy1Try :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]
sepBy1Try p sep = do
x <- p
xs <- many (try $ sep *> p)
return (x:xs)
sepByTry p sep = sepBy1Try p sep <|> return []
使用这些代替sepBy
:
block :: GenParser Char st Block
block = parseBlock 0
where
parseBlock lvl = do
count lvl tab
string "keyword"
otherStuff <- many (noneOf "\r\n")
newline
-- Parse 'function calls'.
fs <- sepBy1Try (blockFunc (lvl + 1)) emptyLines
-- Parse optional child blocks.
emptyLines
bs <- sepByTry (try $ parseBlock (lvl + 1)) emptyLines
return Block { blockFuncCalls=fs
, blockBlocks=bs
, blockValues=words otherStuff
}
我还修改了您的数据类型以捕获更多信息(仅用于演示目的)。另外,请注意递归try
前面的另一个parseBlock
- 这是因为这个解析必须在不消耗输入的情况下失败,当它看到例如一个选项卡但是期望两个时,{{1}允许它回溯到“下一级”。
最后,更改以下内容:
try
与sepBy相同的推理......
为清晰起见,使用简单漂亮的打印机进行测试:
emptyLines :: GenParser Char st ()
emptyLines = many (try emptyLine) >> return ()
和
data Block = Block { blockValues :: [String]
, blockFuncCalls :: [String]
, blockBlocks :: [Block]
} deriving (Show, Eq)
pprBlock :: Block -> String
pprBlock = unlines . go id where
go ii (Block vals funcs subblocks) =
let ii' = ii . ('\t':) in
(ii $ unwords $ "keyword":vals) :
map (\f -> ii' $ "function " ++ f) funcs ++
concatMap (go ii') subblocks
test0_run = either (error.show) (putStrLn.pprBlock) $ parse block "" $ test0
test0 = unlines $
[ "keyword some more values"
, "\tfuncKeyw funcName1"
, "\tfuncKeyw funcName2"
, "\t"
, "\tfuncKeyw funcName3"
, "\t"
, "\tkeyword some more values"
, "\t\tfuncKeyw funcName2"
, ""
, "\tkeyword some more values"
, "\t\tfuncKeyw funcName4"
]