Antlr 从lexer跳过WS时编写对空格敏感的解析器规则

Antlr 从lexer跳过WS时编写对空格敏感的解析器规则,antlr,grammar,antlr4,Antlr,Grammar,Antlr4,我在处理空白方面遇到了一些问题。在以下语法摘录中,我设置了lexer,以便解析器跳过空白: ENTITY_VAR : 'user' | 'resource' ; INT : DIGIT+ | '-' DIGIT+ ; ID : LETTER (LETTER | DIGIT | SPECIAL)* ; ENTITY_ID : '__' ENTITY_VAR ('_w_' ID)?; NEWLINE : '\r'? '\n'; WS : [ \t\r\n]+ ->

我在处理空白方面遇到了一些问题。在以下语法摘录中,我设置了lexer,以便解析器跳过空白:

ENTITY_VAR
    : 'user'
    | 'resource'
    ;

INT : DIGIT+ | '-' DIGIT+ ;
ID : LETTER (LETTER | DIGIT | SPECIAL)* ;
ENTITY_ID : '__' ENTITY_VAR ('_w_' ID)?;

NEWLINE : '\r'? '\n';

WS : [ \t\r\n]+ -> skip; // skip spaces, tabs, newlines

fragment LETTER : [a-zA-Z];
fragment DIGIT : [0-9];
fragment SPECIAL : ('_' | '#' );
问题是,我想匹配形式为
ENTITY\u ID
的变量名称,这样匹配的字符串就不会有任何空格。正如我在这里所做的那样,将其作为一个lexer规则编写就足够了,但问题是我想用一个解析器规则来代替它,因为我想从我的代码中直接访问这两个标记
ENTITY\u VAR
ID
,而不是将它们压缩成一个完整的标记
ENTITY\u ID

有什么想法吗?
基本上,任何允许我直接访问
ENTITY\u VAR
ID
的解决方案都适合我,不管是将
ENTITY\u ID
作为lexer规则,还是将其移动到解析器。

就我通过浏览文档了解到的情况来看,这样的解决方案似乎不可行

解析器规则似乎只在默认通道上工作,因此我不能将
WS
发送到
channel(HIDDEN)
,然后仅为单个解析器规则恢复它

另一方面,antlr的一位作者指出,自版本4以来,不可能分解任何令牌

尽管我一点也不喜欢它,但似乎最快的方法是从lexer解析它(如问题中的代码),然后从Java重新解析整个字符串


尽管如此,对我的结论的任何其他更好的选择或更正都是受欢迎的。

正如您自己的答案所暗示的,在某种管道中连接两个解析器是一种合理而简单的设计/解决方案,我非常确信ANTLR能够帮助实现这一点

我不知道ANTLR人员在流/源解析方面做了多少工作。但是,采用两次传递策略应该足够有效,因为第一次传递仅仅是对一种常规语言进行词法分析,即在输入的大小上使用非常小的
c
,即
O(c*N)


如果你想要一个单程,代价是<代码> o(k*n)(用一个大k),你可以考虑,其中有(我没有尝试过)。

有几种方法我可以想到(不是按一个特殊的顺序):

  • 从规则
    实体\u ID
    发出多个令牌。寻找灵感
  • 在解析器中允许空白,然后检查
  • 使用单个令牌并在代码中拆分
  • 使用单个令牌并在将其传递给解析器之前修改令牌流。例如,lex,修改
    实体\u ID
    标记,并将它们拆分为几个其他标记,然后将此流传递给解析器
  • 不要跳过空格,在处理这些“额外标记”时,请检查它们是否在
    实体ID
    部分(=>是错误)内(=>忽略错误)
  • 不要跳过空白,在语法中允许空白的地方添加“WS*”(如果语法不是太大,可以)
  • 在解析器规则中插入谓词,以检查之间是否有空格
  • 创建如下“陷阱”规则:

    INVALID_ENTITY_ID : '__' WS+ ENTITY_VAR WS? ('_w_' WS? ID)?
                      | '__' WS? ENTITY_VAR WS+ ('_w_' WS? ID)?
                      | '__' WS? ENTITY_VAR WS? ('_w_' WS+ ID)
                      ;
    
    这将捕获无效的
    实体\u ID
    s,因为它比将成为单个令牌的部分长


  • 如果在“无错误”的情况下,2不会改变解析,也就是说,没有代码会因为允许空白而有不同的解释。

    可能会有帮助吗?一旦你无意中发现了
    “\uuuu”
    ,你就可以切换不跳过空格的模式了?谢谢你的建议。因此,如果我理解正确,我会编写一个解析器规则
    entityVar
    ,这样当匹配
    “\uuuuu”
    时,它会切换到禁用
    WS
    lexer规则的模式?Ops,我的意思是
    entityId
    ,而不是
    Var
    lexer模式不依赖于解析器规则。每当lexer匹配
    “\uuuuu”
    ,它都会切换模式,不管是否有解析器规则实际使用
    “\uuuuuu”
    标记。您能给出一些示例输入代码和您想要的吗?谢谢,但是我将用这个解决方案解析的字符串非常小,使用任何类型的库都肯定是一种过度使用。我对如何再次解析
    实体\u ID
    不感兴趣,而是通过使用Antlr:)的单次传递来完全避免它。最后,我选择了选项3。其他的每一个选项都涉及到对语法进行太多复杂的修改,在这种情况下,这些修改根本不值得。通过从java代码中通过正则表达式再次解析它,我能够保持语法简单明了。