1

如果我试图将以下内容解析为linesand fields。行由 分隔'\n',字段由 分隔'|'

abcd|efgh|ijkl
mnopq\|rst|uvwxy
za|bcd
efg|hijk|lmnop

我可以定义以下内容:

let displayCharacter = satisfy (fun c -> ' ' <= c && c <= '~')
let escapedDC = pchar '\\' >>. displayCharacter
let test1 = 
    run (manyChars (escapedDC <|> displayCharacter)) "asdf\|efgh|ijkl"
    // Success: "asdf|efgh|ijkl"

let fields = sepBy (manyChars (escapedDC <|> displayCharacter)) (pchar '|')无法将 排除在外'|'。这些分隔符是上下文相关的,因此我想避免将它们硬编码为显示字符,displayCharacter'|'在某些上下文中可能需要转义。

如果我尝试定义一个fieldwith manyCharsTill,那么我需要考虑一行中的最后一个元素 with anyOf "|\n",但这会将所有行读入一个line.

'|'除了在某些情况下支持的之外,我可能还有更多的子分隔符。出于这个原因,必须为每种情况定义 displayCharacter 和 escapedDC 的版本似乎很麻烦。相反,使用前瞻功能似乎更干净。或者也许是一个被调用的解析器both,它以某种方式需要同时在两个解析器上进行匹配。

manyCharsSepBy (escapedDC <|> displayCharacter) (pchar '|')

或者

let contextualDisplayCharacter1 = both displayCharacter (satisfy ((<>) '|'))

有没有更简单的方法来实现这一点?也许只是我隐含的 BNF 有缺陷——如果修复了,会很容易翻译吗?

=============

这是我能想到的最好的方法,但我想从专家那里知道这是否是最灵活的方法。

let displayCharacter (excludeDelimiters : string) = satisfy (fun c -> ' ' <= c && c <= '~' && not (Seq.exists ((=) c) excludeDelimiters))
let escapedDisplayCharacter = pchar '\\' >>. displayCharacter ""

let field = 
    manyChars (escapedDisplayCharacter <|> displayCharacter "|")
4

0 回答 0