FParsec标识符与关键字

时间:2012-03-19 01:27:23

标签: f# parser-combinators fparsec

对于带有关键字的语言,需要一些特殊的技巧来防止例如“if”被解释为标识符而“ifSomeVariableName”变成关键字“if”后跟标识符流中的标识符“SomeVariableName”。

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

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

2 个答案:

答案 0 :(得分:5)

我认为,这个问题非常简单。答案是你必须:

  1. 解析整个单词([a-z]+),仅限小写;
  2. 检查它是否属于字典;如果是这样,请返回keyword;否则,解析器将退回;
  3. 分别解析identifier;
  4. E.g。 (只是一个假设的代码,没有经过测试):

    let keyWordSet =
        System.Collections.Generic.HashSet<_>(
            [|"while"; "begin"; "end"; "do"; "if"; "then"; "else"; "print"|]
        )
    let pKeyword =
       (many1Satisfy isLower .>> nonAlphaNumeric) // [a-z]+
       >>= (fun s -> if keyWordSet.Contains(s) then (preturn x) else fail "not a keyword")
    
    let pContent =
        pLineComment <|> pOperator <|> pNumeral <|> pKeyword <|> pIdentifier
    

    上面的代码将解析关键字或标识符两次。或者,要修复它,您可以:

    1. 解析整个单词([a-z][A-Z]+[a-z][A-Z][0-9]+),例如一切字母数字;
    2. 检查它是关键字还是标识符(小写并且属于字典)和其中之一
      1. 返回关键字
      2. 返回标识符
    3. P.S。如果它不破坏逻辑,不要忘记首先订购“更便宜”的解析器。

答案 1 :(得分:0)

您可以为空格定义解析器,并检查是否后跟关键字或标识符。 例如,一些通用的空白解析器看起来像

let pWhiteSpace = pLineComment <|> pMultilineComment <|> pSpaces

这将需要至少一个空格

let ws1 = skipMany1 pWhiteSpace

然后如果看起来像

let pIf = pstring "if" .>> ws1