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