F# FParsec标识符与关键字

F# FParsec标识符与关键字,f#,parser-combinators,fparsec,F#,Parser Combinators,Fparsec,对于具有关键字的语言,需要进行一些特殊的欺骗,以防止例如“if”被解释为标识符,“ifSomeVariableName”在令牌流中变成关键字“if”,后跟标识符“SomeVariableName” 对于递归下降和Lex/Yacc,我只是采用了在lexer和解析器之间转换令牌流的方法(根据有用的指令) 然而,FParsec似乎并没有真正做一个单独的lexer步骤,所以我想知道处理这个问题的最佳方法是什么。说到这里,Haskell的Parsec似乎支持lexer层,但FParsec不支持?您可以为空

对于具有关键字的语言,需要进行一些特殊的欺骗,以防止例如“if”被解释为标识符,“ifSomeVariableName”在令牌流中变成关键字“if”,后跟标识符“SomeVariableName”

对于递归下降和Lex/Yacc,我只是采用了在lexer和解析器之间转换令牌流的方法(根据有用的指令)


然而,FParsec似乎并没有真正做一个单独的lexer步骤,所以我想知道处理这个问题的最佳方法是什么。说到这里,Haskell的Parsec似乎支持lexer层,但FParsec不支持?

您可以为空白定义一个解析器,并检查它后面是否跟有关键字或标识符。 例如,一些通用的空白解析器如下所示

let pWhiteSpace = pLineComment <|> pMultilineComment <|> pSpaces
let pIf = pstring "if" .>> ws1
那么如果你看起来像

let pWhiteSpace = pLineComment <|> pMultilineComment <|> pSpaces
let pIf = pstring "if" .>> ws1

我觉得这个问题很简单。答案是你必须:

  • 解析出整个单词(
    [a-z]+
    ),仅小写
  • 检查它是否属于字典;如果是,则返回一个
    关键字
    ;否则,解析器将后退
  • 分别解析
    标识符
    例如(只是一个假设代码,未经测试):

    let关键字集=
    System.Collections.Generic.HashSet(
    [|“while”;“begin”;“end”;“do”;“if”;“then”;“else”;“print”|]
    )
    让我来说说=
    (多数情况下为非对数)/[a-z]+
    >>=(乐趣->如果关键字集.Contains,则(预返回x)否则失败“不是关键字”)
    让P内容=
    pLineComment pOperator PNUMAL pKeyword pIdentifier
    
    上面的代码将解析关键字或标识符两次。要解决此问题,您也可以:

  • 解析出一个完整的单词(
    [a-z][a-z]+[a-z][a-z][0-9]+
    ),例如所有字母数字
  • 检查它是关键字还是标识符(小写,属于字典),以及
  • 返回关键字
  • 返回一个标识符

  • 另外,如果不会破坏逻辑,请不要忘记先订购“更便宜”的解析器。

    “如果”(“不会以这种方式匹配。我喜欢你的第二个进程。它基本上与lexer后处理器技巧相同,但只是内联。20/20后见之明,这是最明显的解决方案:)。谢谢上面答案中的
    pKeyword
    的定义让我困惑。我的类型推断表明它是一个
    解析器
    ,它不是(IMHO)你想要的-你想要返回一个
    解析器
    ,或者一个包含在
    回复
    类型中的失败,我看不出如何使用
    |>
    操作符来实现这一点,谢谢你指出这一点。我已经更新了守卫规则。根据域的具体情况,可能还需要将解析器包装到
    trunt
    中。希望这有帮助。