F# 分析完整输入两次

F# 分析完整输入两次,f#,fparsec,F#,Fparsec,为了使用operatorReceidenceParser实现不区分大小写的中缀运算符,我正在预处理输入,将其解析为由字符串文本分隔的文本。然后在文本部分搜索需要大写的中缀运算符(以符合OPP中已知的运算符)。然后进行实际的解析 我的问题是,这两个阶段可以组合成一个解析器吗?我试过了 //预处理:解析器 //scalarExpr:解析器 让过滤器=(预处理.>>eof)>>。(scalarExpr.>>eof) 但它在输入结束时失败,似乎期待一个scalarExpr。输入可以由preproces

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

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

//预处理:解析器
//scalarExpr:解析器
让过滤器=(预处理.>>eof)>>。(scalarExpr.>>eof)
但它在输入结束时失败,似乎期待一个
scalarExpr
。输入可以由
preprocess
scalarExpr
独立解析,因此我猜这是
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 + "\"")) 
让stringLiteral=
让subString=many满足(()“”)
让escapedQuote=stringReturn“\”\“”
(在(pstring“\”)(pstring“\”)(stringsSepBy子字符串转义引号)之间)
let规范化关键字=
let关键字=
[
“或”
“和”
“包含”
“开始使用”
“ENDSWITH”
]
让caseInsensitiveKeywords=HashSet(关键字,StringComparer.InvariantCultureInogoreCase)
趣味文本->
让re=Regex(@“([\w][\w']*\w)”)
关于替换(文本,匹配评估器)(乐趣m->
如果caseInsensitiveKeywords.Contains(m.Value),那么m.Value.toUpper不变量()
除此之外(m.价值)
让预处理=
斯特林塞比
((许多满足(()“”)|>>规范化关键字)
(stringLiteral |>>(趣味s->“\”“+s+”\))

使用FParsec解析不区分大小写的运算符的最简单方法是为要支持的每个大小写添加运算符定义。如果只需要支持短运算符名称,如“and”或“or”“,您可以简单地添加所有可能的案例组合。如果您想使用这个方法太长的运算符名称,您可能只考虑支持SCAN外壳,即小写、大写、CAMELSE和PascalCase。当您想要支持多个外壳时,通常编写一个辅助函数来自动从标准外壳生成所有需要的外壳是很方便的

如果您有很长的操作员名称,并且确实希望支持所有外壳,则
OperatorReceidenceParser
的动态配置功能还允许使用以下方法,这应该比转换输入更简单、更高效:

  • 在输入中搜索所有不区分大小写的受支持运算符。此搜索不应遗漏任何事件,但如果在函数名或字符串文本中使用运算符名,则如果发现误报,则不会出现问题
  • 将在步骤1中找到的所有唯一外壳添加到
    OperatorReceidenceParser
    。(通常情况下,同一操作员不会有多个外壳。)
  • 使用配置的
    运算符或ReceidenceParser
    解析输入
    解析多个输入时,您可以保留
    operatorreceidenceparser
    实例,并根据需要懒洋洋地添加新的运算符外壳。

    您的运算符是否太长,以至于您无法将所有感兴趣的大小写组合都添加到OPP(例如,使用一个小助手函数)?如何在预处理过程中使用大写运算符,是否直接改变输入缓冲区?如果你想两次解析输入,你必须回溯到开始或者创建一个新的字符流。有五个这样的操作符(将来可能会更多),最长的是10个字符<代码>预处理返回一个带有大写运算符的新字符串。我已经用其他解析器更新了我的问题。我刚刚意识到我可能需要
    >=
    而不是
    >>。
    但是类型不起作用,所以我现在被难住了。@StephanTolksdorf:可以安全地说我需要调用
    parse
    两次,每个解析器一次吗?没有办法将它们结合起来吗?如果我读得正确,
    preprocess
    是一个解析器,它返回预处理的字符串。如果这是正确的,您需要为此字符串创建一个新的CharStream,并在此CharStream上运行第二个解析器,例如,使用runParserOnString。然而,整个方法相当丑陋。您是否可以只支持小写、大写、camelCase和PascalCase等合理的大小写组合?