【问题标题】:FParsec identifiers vs keywordsFParsec 标识符与关键字
【发布时间】:2012-03-19 01:27:22
【问题描述】:

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

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

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

【问题讨论】:

    标签: f# parser-combinators fparsec


    【解决方案1】:

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

    1. 解析整个单词 ([a-z]+),仅小写;
    2. 检查它是否属于字典;如果是,返回一个keyword;否则,解析器将退回;
    3. 单独解析identifier

    例如(只是一个假设的代码,未经测试):

    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. 返回一个标识符

    附:不要忘记先订购“更便宜”的解析器,如果它不破坏逻辑的话。

    【讨论】:

    • 我喜欢你的第二个过程。它基本上与词法分析器后处理器技巧相同,但只是内联。事后看来,这是最明显的解决方案:)。谢谢
    • 上述答案中pKeyword 的定义让我很困惑。我的类型推断表明它是 Parser&lt;bool&gt;,这不是(恕我直言)你想要的 - 你想返回 Parser&lt;string&gt; 或包装在 Reply 类型中的失败,我看不出如何用|&gt;&gt; 运算符?
    • @Sam,感谢您指出这一点。我已经更新了守卫规则。根据特定领域的具体情况,可能还需要将解析器包装到attempt 中。希望这会有所帮助。
    【解决方案2】:

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

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

    这将需要至少一个空格

    let ws1 = skipMany1 pWhiteSpace
    

    那么 if 会是这样的

    let pIf = pstring "if" .>> ws1
    

    【讨论】:

    • "if(" 不会这样匹配。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多