我必须为遗留编程语言编写一个解析器,将其转换为另一种语言。 SQL语句可以直接嵌入到赋值中。
由于我不需要实际解析SQL,只是将其作为字符串传递给目标环境的库函数,我想使用以下规则将SQL语句识别为词法分析器级别的标记。
SqlStatement : SELECT .+ ';' ;
不幸的是,sql语句可以 以分号或关键字EXECUTING(它引入一个命令块,但这不相关)终止
。我无法简单地将另一个令牌定义为:
SqlAndExecute : SELECT .+ EXECUTING ;
由于两者重叠,这导致ANTLR(令人惊讶地?)发出虚假的“ELECT”令牌。
即使它奏效了,我甚至都写不出像
这样的东西 SqlStatement : SELECT .+ ';' | EXECUTING;
因为我需要区分这两种形式。
我可以得到这个结果吗?我试图写句法谓词,但我可能仍然遗漏了一些东西。
如果可能的话,我宁愿避免解析SQL查询。
注意:SELECT定义为S E L E C T
fragment S: 's'|'S'
,依此类推标识符中的其他字母;类似于EXECUTING
答案 0 :(得分:2)
在这种情况下不要使用.+ ';'
:因此,您无法区分';'
作为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
或者是字符串文字,或者当前面没有EXECUTING
时,除了单引号之外的任何字符('\''
)或半结肠(';'
)。 “当前面没有EXECUTING
”时 - 必须通过词法分析器和semantic predicate中的一些手动额外预测来处理。
快速演示:
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 : ;
如果您现在解析输入:
Select bar from EXECUTINGIT EXECUTING
x
Select foo from EXECUTING
y
SELECT a FROM b WHERE c=';' and More;
以下内容将打印到控制台:
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
。