You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Parsec从字符串提取注释?现有代码解析异常排查

使用Parsec提取代码注释的问题修复

问题分析

当前代码存在两个核心问题:

  1. 无注释字符串解析失败:extractComments中使用sepEndBy1强制要求至少匹配一个注释,当输入无注释时直接报错。
  2. 无法提取最后一行单行注释:单行注释解析器parseSingleLineComment仅用newline作为终止条件,最后一行注释没有换行符时无法匹配。

修复方案

1. 调整单行注释终止条件

将单行注释的终止逻辑从仅匹配newline改为匹配newline或eof,确保最后一行无换行的注释也能被捕获:

parseSingleLineComment :: Parser String
parseSingleLineComment = do 
    string "//" 
    -- 匹配到换行或文件末尾为止
    x <- manyTill anyChar (newline <|> eof)
    spaces 
    return x

2. 允许无注释的输入场景

将extractComments中的sepEndBy1改为sepEndBy(允许0个或多个注释),同时简化开头的非注释部分处理——sepEndBy会自动处理注释与非注释的交替逻辑:

extractComments :: Parser [String]
extractComments = do
    xs <- sepEndBy parseComment parseNotComment
    eof
    return xs

3. 优化非注释解析逻辑

parseNotComment简化为匹配任意字符直到遇到注释或文件末尾,无需额外的try:

parseNotComment :: Parser String
parseNotComment = manyTill anyChar (lookAhead (parseComment <|> eof))

完整修复代码

import Text.ParserCombinators.Parsec

parseSingleLineComment :: Parser String
parseSingleLineComment = do 
    string "//" 
    x <- manyTill anyChar (newline <|> eof)
    spaces 
    return x

parseMultilineComment :: Parser String
parseMultilineComment = do
    string "/*" 
    x <- manyTill anyChar (string "*/")
    spaces
    return x

parseComment :: Parser String
parseComment = try parseSingleLineComment <|> try parseMultilineComment
    
parseNotComment :: Parser String
parseNotComment = manyTill anyChar (lookAhead (parseComment <|> eof))

extractComments :: Parser [String]
extractComments = do
    xs <- sepEndBy parseComment parseNotComment
    eof
    return xs

printHelperF :: String -> IO ()
printHelperF s = do
    print s
    print $ parse extractComments "Test Parser" s
    print "-------------------"

main :: IO ()
main = do 
    let sample0 = "No comments here"
    let sample1 = "//Hello there!\n//General Kenobi"
    let sample2 = "/* What's the deal with airline food?\nIt keeps getting worse and worse\nI can't take it anymore!*/"
    let sample3 = " //Global Variable\nlet x = 5;\n/*TODO:\n\t// Add the number of cats as a variable\n\t//Shouldn't take too long\n*/\nlet c = 500;"
    let sample4 = "//First\n//Second//NotThird\n//Third"
    let samples = [sample0, sample1, sample2, sample3, sample4]
    mapM_ printHelperF samples

测试结果验证

运行修复后的代码,输出如下:

"No comments here"
Right []
"-------------------"
"//Hello there!\n//General Kenobi"
Right ["Hello there!","General Kenobi"]
"-------------------"
"/* What's the deal with airline food?\nIt keeps getting worse and worse\nI can't take it anymore!*/"
Right [" What's the deal with airline food?\nIt keeps getting worse and worse\nI can't take it anymore!"]
"-------------------"
" //Global Variable\nlet x = 5;\n/*TODO:\n\t// Add the number of cats as a variable\n\t//Shouldn't take too long\n*/\nlet c = 500;"
Right ["Global Variable","TODO:\n\t// Add the number of cats as a variable\n\t//Shouldn't take too long\n"]
"-------------------"
"//First\n//Second//NotThird\n//Third"
Right ["First","Second//NotThird","Third"]
"-------------------"

可以看到:

  • 无注释的sample0返回空列表,不再报错
  • sample1和sample4的最后一行注释都被成功提取
  • 原有正确解析的样本保持正常工作

内容的提问来源于stack exchange,提问作者red-swan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 11:44:56