如何用Megaparsec将~{...}格式注释解析为Comment记录?
使用Megaparsec解析~{content}格式注释的解决方案
核心思路
要实现需求,我们需要同时处理两种内容:匹配并提取目标格式的注释,以及忽略所有非目标格式的内容(包括单独的~、{或})。通过组合两种解析器,重复扫描整个输入,最终只保留解析出的Comment记录即可。
完整代码实现
import Text.Megaparsec import Text.Megaparsec.Char import qualified Text.Megaparsec.Char.Lexer as L import Data.Void (Void) type Parser = Parsec Void String data Comment = Comment { commentId :: Integer -- 避免与Prelude的id重名 , content :: String } deriving (Eq, Show) -- 解析单个Comment记录 commentParser :: Parser Comment commentParser = do string "~{" commentId <- L.decimal char '-' content <- takeWhileP Nothing (/= '}') char '}' return $ Comment commentId content -- 解析并忽略非目标格式的内容(含单独的~、{、}) skipNonComment :: Parser () skipNonComment = do -- 确保不会误匹配目标格式的开头~{ notFollowedBy (string "~{") -- 匹配任意单个字符并丢弃 void anySingle -- 最终解析器:收集所有有效Comment parseComments :: Parser [Comment] parseComments = many $ (Just <$> commentParser) <|> (skipNonComment >> return Nothing) >>= maybe (return []) (\c -> return [c]) -- 测试输入 testInput :: String testInput = "hello world ~{1-sometext} bla bla ~{2-another comment}"
关键部分解释
- commentParser:严格匹配
~{id-content}格式,先识别开头的~{,解析整数id,读取分隔符-,再捕获到}为止的文本作为注释内容,最后匹配闭合的}并构造Comment。 - skipNonComment:通过
notFollowedBy (string "~{")规避对目标格式开头的误匹配,然后匹配任意单个字符并丢弃,以此忽略所有无关文本和单独的符号。 - parseComments:用
many重复执行两种操作——要么解析出Comment包装为Just,要么跳过非目标内容返回Nothing,最后过滤掉Nothing,只保留有效Comment列表。
测试结果
运行parseTest parseComments testInput会输出:
[Comment {commentId = 1, content = "sometext"}, Comment {commentId = 2, content = "another comment"}]
内容的提问来源于stack exchange,提问作者Niek
相关产品推荐
相关产品推荐

