使用ANTLR分隔包含通配符和备用结尾的标记

使用ANTLR分隔包含通配符和备用结尾的标记,antlr,lexer,Antlr,Lexer,我必须为一种遗留编程语言编写一个解析器,以便将其转换为另一种编程语言。SQL语句可以直接嵌入到分配中 由于我不需要实际解析SQL,只需将其作为字符串传递给目标环境的库函数,因此我希望使用以下规则在lexer级别将SQL语句识别为标记 SqlStatement : SELECT .+ ';' ; 不幸的是,sql语句可以用分号终止,也可以用关键字EXECUTING终止(这会引入一个命令块,但这并不相关) 我不能简单地将另一个令牌定义为: SqlAndExecute : SELECT .+ EX

我必须为一种遗留编程语言编写一个解析器,以便将其转换为另一种编程语言。SQL语句可以直接嵌入到分配中

由于我不需要实际解析SQL,只需将其作为字符串传递给目标环境的库函数,因此我希望使用以下规则在lexer级别将SQL语句识别为标记

 SqlStatement : SELECT .+ ';' ;
不幸的是,sql语句可以用分号终止,也可以用关键字EXECUTING终止(这会引入一个命令块,但这并不相关)

我不能简单地将另一个令牌定义为:

SqlAndExecute : SELECT .+ EXECUTING ;
由于两者重叠,这会导致ANTLR(令人惊讶地?)发出虚假的“选择”标记

即使成功了,我也写不出这样的东西

 SqlStatement : SELECT .+ ';' | EXECUTING;
因为我需要区分这两种形式

我能得到这个结果吗?我试着写语法谓词,但可能还是遗漏了一些东西

如果可能的话,我宁愿避免解析SQL查询


注意:SELECT被定义为带有
片段S:'S'|'S'
S E L E C T
,依此类推标识符中的其他字母;同样,对于执行

也不要使用
+';'在这种情况下:这样,您就无法区分
';'作为SQL语句的结尾,在字符串文本中

因此,要区分
SqlAndExecute
SqlStatement
,只需匹配两个令牌的共同点,然后在最后更改令牌的类型,如下所示:

Sql
 : SELECT Space SqlAtom+ ( ';'       {$type=SqlStatement;}
                         | EXECUTING {$type=SqlAndExecute;}
                         )
 ;

fragment SqlStatement  : /* empty, used only for the token-type */ ;
fragment SqlAndExecute : /* empty, used only for the token-type */ ;
现在,
SqlAtom
要么是字符串文字,要么在前面没有执行
时,是除单引号(
“\”
)或分号(
;“
)以外的任何字符。“当前方没有执行<代码>时”-部分必须由lexer和a中的一些手动额外前方处理

快速演示:

grammar T;  

@lexer::members {

  private boolean aheadIgnoreCase(String text) {
    int i;

    for(i = 0; i < text.length(); i++) {

      String charAhead = String.valueOf((char)input.LA(i + 1));

      if(!charAhead.equalsIgnoreCase(String.valueOf(text.charAt(i)))) {
        return false;
      }
    }

    // there  can't be a letter after 'text', otherwise it would be an identifier
    return !Character.isLetter((char)input.LA(i + 1));
  }
}

parse
 : (t=. {System.out.printf("\%-15s'\%s'\n", tokenNames[$t.type], $t.text);})* EOF
 ;

Sql
 : SELECT SP SqlAtom+ ( ';'       {$type=SqlStatement;}
                      | EXECUTING {$type=SqlAndExecute;}
                      )
 ;

Space
 : SP+ {skip();}
 ;

Id
 : ('a'..'z' | 'A'..'Z')+
 ;

fragment SqlAtom
 : {!aheadIgnoreCase("executing")}?=> ~('\'' | ';')
 | Str
 ;

fragment Str : '\'' ('\'\'' | ~('\'' | '\r' | '\n'))* '\'';

fragment SELECT    : S E L E C T;
fragment EXECUTING : E X E C U T I N G;
fragment SP        : ' ' | '\t' | '\r' | '\n';

fragment C : 'c' | 'C';
fragment E : 'e' | 'E';
fragment G : 'g' | 'G';
fragment I : 'i' | 'I';
fragment L : 'l' | 'L';
fragment N : 'n' | 'N';
fragment S : 's' | 'S';
fragment T : 't' | 'T';
fragment U : 'u' | 'U';
fragment X : 'x' | 'X';

fragment SqlStatement  : ;
fragment SqlAndExecute : ;
以下内容将打印到控制台:

SqlAndExecute  'Select bar from EXECUTINGIT EXECUTING'
Id             'x'
SqlAndExecute  'Select foo from EXECUTING'
Id             'y'
SqlStatement   'SELECT a FROM b WHERE c=';' and More;'
编辑 请注意,
Sql
规则现在总是生成一个
SqlStatement
SqlAndExecute
标记。换句话说:永远不会有
Sql
令牌。如果要匹配
SqlStatement
SqlAndExecute
,请创建与其中一个匹配的解析器规则:

sql
 : SqlStatement
 | SqlAndExecute
 ;

并在解析器规则中使用
sql
,而不是
sql

非常好的答案,非常感谢。我还有一个问题。如何在语法规则中匹配SqlAndExecute或SqlStatement?e、 g.我尝试了以下两种方法:
rule:SET Id TO(SqlAndExecute | SqlStatement)
rule:SET Id TO Sql
,除非我遗漏了什么,否则它们不会work@NoWhereMan,请参阅我的编辑。但是
规则:将Id设置为(SqlAndExecute | SqlStatement)肯定会起作用。如果没有,输入根本不符合规则。我不知道我做错了什么,但现在它终于起作用了:)非常感谢,你救了我的命
sql
 : SqlStatement
 | SqlAndExecute
 ;