【问题标题】:Parsing data with Parsec and omitting comments使用 Parsec 解析数据并省略注释
【发布时间】:2012-10-20 17:20:33
【问题描述】:

我正在尝试编写一个 Haksell Parsec Parser,它将文件中的输入数据解析为 LogLine 数据类型,如下所示:

--Final parser that holds the indvidual parsers.
final :: Parser [LogLine]
final = do{ logLines <- sepBy1 logLine eol
        ; return logLines
        }


--The logline token declaration
logLine :: Parser LogLine
logLine = do
name <-  plainValue -- parse the name (identifier)
many1 space -- parse and throw away a space
args1 <- bracketedValue -- parse the first arguments
many1 space -- throw away the second sapce
args2 <- bracketedValue -- parse the second list of arguments
many1 space -- 
constant <- plainValue -- parse the constant identifier
space
weighting <- plainValue --parse the weighting double
space
return $ LogLine name args1 args2 constant weighting

它可以很好地解析所有内容,但是现在我需要将 cmets 添加到文件中,并且我必须修改解析器以使其忽略它们。 它应该支持仅以“--”开头并以 '\n' 结尾的单行 cmets 我尝试如下定义评论标记:

comments :: Parser String
comments = do 
    string "--"
    comment <- (manyTill anyChar newline)
    return ""

然后将其插入final 解析器,如下所示:

final :: Parser [LogLine]
final = do 
        optional comments
        logLines <- sepBy1 logLine (comments<|>newline)
        optional comments
        return logLines

它编译得很好,但它不解析。我尝试了一些小的修改,但最好的结果是将所有内容解析到第一个评论,所以我开始认为这不是这样做的方法。 PS: 我见过这个Similar Question,但它与我想要实现的目标略有不同。

【问题讨论】:

  • 我怀疑您的问题至少有一部分是logLineweighting 之前和之后都需要一个空格。根据您的格式,让它们占用任意数量的空格(仅' ' 或所有空格)可能是可行的方法。然后分隔符可以是optional commentscomments &lt;|&gt; fmap return newline(注意:comments &lt;|&gt; newline 不应该编译,因为comments 返回[Char]newline Char。)
  • 好的,输入文件的格式是这样的:在每一行你有一个 logLine 或一个注释,你不能在同一行有多个。像这样的东西:name arg1 arg2 c1 weight --comment goes here. name2 arg1 arg2 c2 weight2 我知道在权重之后需要额外的空间是不合逻辑的,但是如果我删除它,无论出于何种原因它都不会解析。我想问你,当你说 separator 时,你的意思是我应该定义一个新的分隔符还是只做类似的事情:logLines &lt;- sepBy1 (logLine) (comments &lt;|&gt; fmap return newline)
  • 抱歉换行符没有出现。示例应该是这样的:name arg1 arg2 c1 weight \n --comment 在这里。\n name2 arg1 arg2 c2 weight2

标签: parsing haskell parsec


【解决方案1】:

如果我正确理解您在评论中对格式的描述,您的格式示例将是

name arg1 arg2 c1 weight
-- comment goes here

可选地后跟更多日志行和/或 cmets。

那么你的问题是日志行和注释行之间有一个换行符,这意味着分隔符解析器的 comments 部分失败 - comments 必须以 "--" 开头 - 不消耗输入,所以 newline 被尝试并成功。然后下一行以"--" 开头,这使得plainValue 在不消耗输入的情况下失败,从而结束sepBy1

解决的办法是让分隔符先消耗一个换行符,然后再多条注释行如下:

final = do
    skipMany comments
    sepEndBy1 logLine (newline >> skipMany comments)

通过允许序列以分隔符结束(sepEndBy1 而不是 sepBy1),最后 LogLine 之后的任何注释行都会自动跳过。

【讨论】:

    【解决方案2】:

    按照我理解您的问题的方式,每一行都是注释或日志行。如果是这样,final 应该如下所示:

    final :: Parser [LogLine]
    final = do 
            logLines <- sepBy1 (comment<|>logLine) newline
            return logLines
    

    【讨论】:

    • 是的,我想过,但问题是sepBy1 只会采用相同类型的数据类型来解析。所以这是行不通的,因为评论是 Char 而 logLine 是 logLine
    • 我只需将 cmets 的值添​​加到 LogLine 并将其过滤掉。但是,如果您不想这样做,也可以使用它:lines &lt;- endBy logLine (many1 $ comments&lt;|&gt;string "\n")
    猜你喜欢
    • 1970-01-01
    • 2013-01-18
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多