解析完整输入两次

时间:2013-05-14 20:22:52

标签: f# fparsec

为了使用OperatorPrecedenceParser实现不区分大小写的中缀运算符,我正在预处理输入,将其解析为由字符串文字分隔的文本。然后在文本部分中搜索需要大写的中缀运算符(以符合OPP已知的运算符)。然后进行实际的解析。

我的问题是,两个阶段都可以组合成一个解析器吗?我试过了

// preprocess: Parser<string,_>
// scalarExpr: Parser<ScalarExpr,_>
let filter = (preprocess .>> eof) >>. (scalarExpr .>> eof)

但它在输入结束时失败,似乎期待scalarExpr。输入可以由preprocessscalarExpr独立解析,所以我猜这是eof的问题,但我似乎无法做到正确。这可能吗?

以下是其他解析器供参考。

let stringLiteral = 
  let subString = manySatisfy ((<>) '"')
  let escapedQuote = stringReturn "\"\"" "\""
  (between (pstring "\"") (pstring "\"") (stringsSepBy subString escapedQuote)) 

let canonicalizeKeywords =
  let keywords = 
    [
      "OR"
      "AND"
      "CONTAINS"
      "STARTSWITH"
      "ENDSWITH"
    ]
  let caseInsensitiveKeywords = HashSet(keywords, StringComparer.InvariantCultureIgnoreCase)
  fun text ->
    let re = Regex(@"([\w][\w']*\w)")
    re.Replace(text, MatchEvaluator(fun m ->
      if caseInsensitiveKeywords.Contains(m.Value) then m.Value.ToUpperInvariant()
      else m.Value))

let preprocess = 
  stringsSepBy 
    ((manySatisfy ((<>) '"')) |>> canonicalizeKeywords) 
    (stringLiteral |>> (fun s -> "\"" + s + "\"")) 

1 个答案:

答案 0 :(得分:1)

使用FParsec的OperatorPrecedenceParser解析不区分大小写的运算符的最简单方法是为要支持的每个大小写添加运算符定义。如果您只需要支持短操作员名称,例如“和”或“或”,则可以简单地添加所有可能的案例组合。如果您想使用对于此方法来说太长的运算符名称,您可能会考虑仅支持理智的外壳,即小写,大写,camelCase和PascalCase。当您想要支持多个外壳时,通常可以方便地编写一个辅助函数,从标准函数中自动生成所有需要的外壳。

如果你有很长的运营商名称并且你真的想支持所有套管,OperatorPrecedenceParser的动态可配置性也允许采用以下方法,这比转换输入更容易,更有效:

  1. 在输入中搜索所支持的运算符的所有不区分大小写的事件。这种搜索不应该错过任何事件,但是如果发现误报,则没有问题。运算符名称在函数名称内或字符串文字内使用。
  2. 将您在步骤1中找到的所有独特外壳添加到OperatorPrecedenceParser。 (通常不会有很多相同运营商的外壳。)
  3. 使用配置的OperatorPrecedenceParser解析输入。
  4. 解析多个输入时,您可以保留OperatorPrecedenceParser实例,并根据需要懒洋洋地添加新的运算符框。