如何在Antlr中的不同词法分析器模式下使用相同的令牌?

问题描述

我有一个基于sql Antlr4的词法分析器,它也可以从sql注释中解析特定的标签

OPEN_COMMENT: '/*' -> mode(COMMENT);
mode COMMENT;
NAME_TAG  :  '@name';
CLOSE_COMMENT: '*/' -> mode(DEFAULT_MODE);

此词法分析器能够读取以下内容

/* @name GetAllUsers */

但是,我也希望能够阅读行注释:

-- @name @GetAllUsers

我不能使用相同的模式,因为CLOSE_COMMENT的工作方式有所不同:块注释应为*/,行注释应为\n。但是,无论是行注释还是块注释,我都希望解析器获得相同的标记

我该如何实现?还是使用词法分析器模式根本不是正确的方法

(出于这个问题的目的,我将代码仅修整为必要的部分,因此更易于阅读和推理。您可以找到有问题的here中的整个代码。)

解决方法

您必须将某些内容复制到2种不同的模式中。您可以定义通用令牌,例如语法NAME块内的tokens { ... }令牌,以便可以在展位模式下共享它。

快速演示:

lexer grammar TestLexer;

tokens {
  NAME
}

ID
 : [a-zA-Z_] [a-zA-Z_0-9]*
 ;

LINE_COMMENT_START
 : '--' -> skip,mode(LINE_COMMENT_MODE)
 ;

BLOCK_COMMENT_START
 : '/*' -> skip,mode(BLOCK_COMMENT_MODE)
 ;

mode LINE_COMMENT_MODE;

  LINE_COMMENT_MODE_ID
   : ID -> type(ID)
   ;

  LINE_COMMENT_NAME
   : '@name' -> type(NAME)
   ;

  LINE_COMMENT_END
   : [\r\n]+ -> skip,mode(DEFAULT_MODE)
   ;

  LINE_COMMENT_OTHER
   : . -> skip
   ;

mode BLOCK_COMMENT_MODE;

  BLOCK_COMMENT_MODE_ID
   : ID -> type(ID)
   ;

  BLOCK_COMMENT_NAME
   : '@name' -> type(NAME)
   ;

  BLOCK_COMMENT_END
   : '*/' -> skip,mode(DEFAULT_MODE)
   ;

  BLOCK_COMMENT_OTHER
   : . -> skip
   ;

如果将输入标记化:

-- @name GetAllUsers
/* @name GetAllUsers */

您将获得以下令牌:

NAME                      `@name`
ID                        `GetAllUsers`
NAME                      `@name`
ID                        `GetAllUsers`