如果我试图将以下内容解析为lines
and 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
但'|'
在某些上下文中可能需要转义。
如果我尝试定义一个field
with 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 "|")