【问题标题】:How to match EOF condition using antlr 4如何使用 antlr 4 匹配 EOF 条件
【发布时间】:2020-09-26 04:39:10
【问题描述】:

我是 ANTLR 新手,目前正在为 ANTLR 4 中的酷语言编写词法分析器。 更多酷语言请参考http://theory.stanford.edu/~aiken/software/cool/cool-manual.pdf

我试图实现的一个很酷的语言规则是检测注释(可能是嵌套的)或字符串常量中的 EOF 并报告为错误。

这是我写的规则:

ERROR :  '(*' (COMMENT|~['(*'|'*)'])*? (~['*)']) EOF {reportError("EOF in comment");} 
        |'"' (~[\n"])* EOF {reportError("EOF in string");};
fragment COMMENT     : '(*' (COMMENT|~['(*'|'*)'])*? '*)'

这里的片段 COMMENT 是我使用的递归规则。

上面使用的函数reportError报告错误如下:

public void reportError(String errorString){
        setText(errorString);
        setType(ERROR);
}

但是当我在下面给出的测试文件上运行它时:

"Test String

它给出以下输出:

line 1:0 token recognition error at: '"Test String\n'
#name "helloworld.cl"

显然没有识别出带有EOF的字符串,也没有检测到错误。

有人可以帮我指出我哪里出错了,因为 EOF(因此,错误规则)在某种程度上没有被词法分析器检测到。

如果有不清楚的地方,请务必提及。

【问题讨论】:

    标签: comments antlr4 eof lexer string-constant


    【解决方案1】:

    您不需要专用的ERROR 规则。您可以直接在错误侦听器中使用未完成的字符串处理该特定情况。但是,您的评论规则不应该是片段,因为它必须自己识别必须处理的词素(片段规则是仅在其他词法分析器规则中使用的规则)。

    当词法分析器到达一个字符串但由于输入结束而无法完成它时,您可以从错误侦听器中的当前词法分析器状态获取有问题的输入。然后,您可以检查以查看未完成的确切内容,就像我在这里对 MySQL 中的 3 种引用文本类型所做的那样:

    void LexerErrorListener::syntaxError(Recognizer *recognizer, Token *, size_t line,
                                         size_t charPositionInLine, const std::string &, std::exception_ptr ep) {
      // The passed in string is the ANTLR generated error message which we want to improve here.
      // The token reference is always null in a lexer error.
      std::string message;
      try {
        std::rethrow_exception(ep);
      } catch (LexerNoViableAltException &) {
        Lexer *lexer = dynamic_cast<Lexer *>(recognizer);
        CharStream *input = lexer->getInputStream();
        std::string text = lexer->getErrorDisplay(input->getText(misc::Interval(lexer->tokenStartCharIndex, input->index())));
        if (text.empty())
          text = " "; // Should never happen.
    
        switch (text[0]) {
          case '/':
            message = "Unfinished multiline comment";
            break;
          case '"':
            message = "Unfinished double quoted string literal";
            break;
          case '\'':
            message = "Unfinished single quoted string literal";
            break;
          case '`':
            message = "Unfinished back tick quoted string literal";
            break;
    
          default:
            // Hex or bin string?
            if (text.size() > 1 && text[1] == '\'' && (text[0] == 'x' || text[0] == 'b')) {
              message = std::string("Unfinished ") + (text[0] == 'x' ? "hex" : "binary") + " string literal";
              break;
            }
    
            // Something else the lexer couldn't make sense of (likely there is no rule that accepts this input).
            message = "\"" + text + "\" is no valid input at all";
            break;
        }
        owner->addError(message, 0, lexer->tokenStartCharIndex, line, charPositionInLine,
                        input->index() - lexer->tokenStartCharIndex);
      }
    }
    

    此代码取自parser module in MySQL Workbench

    【讨论】:

      【解决方案2】:
      '"' (~[\n"])* EOF
      

      这里~[\n"]* 部分将停在第一个\n" 或文件末尾。

      如果它在 " 处停止,则规则不匹配,因为 EOF 不匹配,这就是我们想要的,因为字符串文字已正确终止。

      如果它在文件末尾停止,那么随后的 EOF 将匹配,您将获得一个 ERROR 令牌。所以这也是你想要的。

      但如果它停在\nEOF 将不匹配,即使在这种情况下您想要一个错误标记,您也不会得到错误标记。由于您的输入以\n 结尾,这正是您在这里遇到的情况。所以除了EOF,你还应该允许错误的字符串文字以\n结尾:

      '"' (~[\n"])* ('\n' | EOF)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-28
        • 1970-01-01
        相关资源
        最近更新 更多