如果我正在尝试将以下内容解析为lines
和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
定义单个manyCharsTill
,那么我需要考虑anyOf "|\n"
行上的最后一个元素,但这会读入所有行一个line
。
在某些情况下,我可能还有'|'
以外的其他subdelimiters。因此,为每种情况定义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 "|")