【问题标题】:FParsec - how to parse strings separated by pipes?FParsec - 如何解析由管道分隔的字符串?
【发布时间】:2018-07-24 13:51:53
【问题描述】:

为了好玩,我正在使用 FParsec 编写一个小型 org-mode 解析器,但在将表格行解析为字符串列表时遇到了一些麻烦。我当前的代码如下所示:

let parseRowEntries :Parser<RowEntries, unit> =
    let skipInitialPipe = skipChar '|'
    let notaPipe  = function
        | '|' -> false
        | _ -> true
    let pipeSep = pchar '|'

    skipInitialPipe >>. sepEndBy (many1Satisfy notaPipe) pipeSep
    |>> RowEntries

这工作正常,直到您解析字符串 |blah\n|blah\n|blah|,这应该因为换行符而失败。不幸的是,在notaPipe 条件中简单地将\n 设置为false 会导致解析器在第一个'blah' 之后停止并说它已成功解析。我想要 manySatisfy 做的是解析(几乎)任何字符,在管道处停止,无法解析换行符(可能是 eof 字符)。

我尝试过使用charsTillString,但这也只是在第一个管道处停止解析,没有错误。

【问题讨论】:

  • 所以规则是一行必须以管道字符开始结束,对吧?即|foo|\n|bar|\n 有效,但|foo\n|bar\n 无效,因为没有终止管道?在那种情况下,我认为您想要的是使用 between 组合器的东西。我会做一些测试,然后根据我的发现写一个答案。

标签: f# fparsec


【解决方案1】:

如果我正确理解了您的规范,这应该可以:

let parseOneRow :Parser<_, unit> =
    let notaPipe  = function
        | '|' -> false
        | '\n' -> false
        | _ -> true
    let pipe = pchar '|'

    pipe >>. manyTill (many1Satisfy notaPipe .>> pipe) (skipNewline <|> eof)

let parseRowEntries :Parser<_, unit> =
    many parseOneRow

run parseRowEntries "|row|with|four|columns|\n|second|row|"
// Success: [["row"; "with"; "four"; "columns"]; ["second"; "row"]]

结构是每一行都以管道开头,然后一行内的段在概念上是row|with|,等等。 .&gt;&gt; 组合器丢弃管道。该行的“直到”部分使用skipNewline 而不是newline 的原因是因为eof 解析器返回unit,所以我们需要一个解析器,它需要换行符并返回unit。这就是skipNewline 解析器。

我尝试在它们不属于的地方(例如,在管道之前)抛出换行符,这会导致这个解析器完全失败。如果一列为空(即两个竖线字符并排出现,如||),它也会失败,我认为这也是您想要的。如果您想允许空行,只需使用manySatisfy 而不是many1Satisfy

【讨论】:

  • P.S.我尝试了between 组合器,但分隔符与结束字符相同的事实使得between 解析器使用起来有点棘手。有可能它可以工作,但我最终采取了不同的方法,而不是弄清楚如何让between 工作。
  • 我也尝试过使用 between 解析器,但还是绕了一大圈。开头和结尾的| 将 i 抛出一个循环。
  • 我所做的小改动是将 many1Satisfy 变成 manySatisfy(因为从技术上讲,|| 是一个有效的条目。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
相关资源
最近更新 更多