Regex 在F中标记新行和跨新行的字符串文字#

Regex 在F中标记新行和跨新行的字符串文字#,regex,parsing,f#,Regex,Parsing,F#,我正在使用F.开发一个解析器。在我当前的解决方案中,我使用以下方法拆分线: let regex s = new Regex(s, RegexOptions.Compiled) let linesRe = regex @"\r\n|\r|\n" 和lex代币,带有: let tokenRe = regex @"((?(\d+|\w+|(""\w+"")|\[|\]|.|=))\s*)*" let tokenizeLine (s: string) = [for x in tokenRe.M

我正在使用F.开发一个解析器。在我当前的解决方案中,我使用以下方法拆分线:


let regex s = new Regex(s, RegexOptions.Compiled)
let linesRe = regex @"\r\n|\r|\n"
和lex代币,带有:


let tokenRe = regex @"((?(\d+|\w+|(""\w+"")|\[|\]|.|=))\s*)*"
let tokenizeLine (s: string) =
  [for x in tokenRe.Match(s).Groups.["token"].Captures do
    let token =
      match x.Value with
      | "[" -> OPENBR
      // omissis...
      | s when isStringLiteral s -> STR (s.Substring(1, s.Length-2))
      | s -> ID s
    yield token]
这样,我将无法处理定义为以下内容的多行字符串:


lines = '''
The first newline is
trimmed in raw strings.
   All other whitespace
   is preserved.
'''
现在,我在每一行添加一个
换行符
标记,但我想:

  • 捕捉我匹配的每一行新词
  • 将多行字符串捕获为字符串文字

  • 我发现了类似的问题,但我甚至无法捕捉到这条新线。我尝试过使用各种设置的
    RegexOptions
    (包括
    Singleline
    Multiline
    )将第一个模式添加到第二个模式(之前没有拆分行),但我没有匹配新行就结束了。拆分行似乎是TOML文件的第一步或预处理,使用MarkSeemann建议的解析器库可能更容易做到这一点

    另一种选择是使用简单的状态机来分割行,例如

    let split separator (s:string) =
        let values = ResizeArray<_>()
        let rec gather start i qs =
            let add () = s.Substring(start,i-start) |> values.Add
            if i = s.Length then add()
            elif s.[i] = '"' && qs = 2 then inTripleQuotes start (i+1) 0
            elif s.[i] = '"' then gather start (i+1) (qs+1)
            elif s.[i] = separator then add(); gather (i+1) (i+1) 0
            else gather start (i+1) 0
        and inTripleQuotes start i qs =
            if s.[i] = '"' && qs = 2 then gather start (i+1) 0
            elif s.[i] = '"' then inTripleQuotes start (i+1) (qs+1)
            else inTripleQuotes start (i+1) 0
        gather 0 0 0
        values.ToArray()
    
    split '\n' text
    
    let拆分分隔符(s:string)=
    let values=ResizeArray()
    让rec收集开始i qs=
    让add()=s.Substring(start,i-start)|>values.add
    如果i=s.长度,则添加()
    如果s.[i]=''“&&qs=2,则整数引号开始(i+1)0
    elif s[i]=“”,然后开始聚集(i+1)(qs+1)
    elif s[i]=分隔符,然后添加();聚集(i+1)(i+1)0
    else聚集开始(i+1)0
    和inTripleQuotes start i qs=
    如果s.[i]=''“&&qs=2,则聚集开始(i+1)0
    如果s[i]=“”,则整数引号开始(i+1)(qs+1)
    else inTripleQuotes开始(i+1)0
    收集0 0 0
    values.ToArray()
    拆分'\n'文本
    

    在上面的
    split
    函数中,我使用了两个相互递归的函数,
    collect
    扫描直到到达分隔符,
    inTripleQuotes
    跳过三引号块中的分隔符。

    如果编写解析器,也许比正则表达式更合适…在我考虑之前,但我不喜欢太多的代码生成工具(我有失控的感觉)。。。我会重新考虑使用parser combinator库,看看你的FParsec建议。虽然我非常喜欢FParsec,但FsLex/FsYacc的优点是它的运行时可以封装在单个程序集中。使用FParsec,您需要FParsec F#组件+FParsec C#组件(约1兆欧)。此外,对于可重用组件,可见依赖项总是有问题。仅供参考;我为TOXML编写了一个EBNF语法(尚未完全完成),如果您选择FParsec,这可能会对您有所帮助:不,我认为TOML中不存在空值,无论如何,有人一直在研究ABNF语法,它可能比我的尝试更完整:我已经用以下数据测试了函数
    let text=sprintf“this\nis\”带3个引号的\n更多\n行\“\”上的文本
    fsharpi
    上,它完全符合我的实际解决方案。无论如何,我要加深对FParsec的了解。我想补充一点,FParsec很有趣,因为有了解析器组合器,我可以用声明的方式描述我的语法,库的内部设计似乎也有利于性能。无论如何,使用此函数并继续使用递归下降解析器(我正在编写)的好处是,我可以避免在代码中添加依赖项。使用FParsec,您还可以“免费”获得人类可读的错误消息@gsscoder在更仔细地研究了TOML的规范之后,我认为手动递归下降解析器对于这个场景是一个有效的选择。正如您所说,它消除了获取外部依赖的需要。以防万一,您可能会发现以下使用活动模式的JSON解析器片段®ex helpfull:@Philip Trelford,再次+1,非常感谢!在这种情况下,我认为这两条路都是可行的。递归下降解析器是我的第一选择(我起草了一些代码w/F#interactive),但我也在评估FParsec。是的,这是一个外部依赖,但我真的很喜欢代码的形成方式。不管怎样,我也会研究你的片段。