我在ANTLR中处理SQL语法,它允许带引号的标识符(表名,字段名等),以及引用的文字字符串。
问题是这个语法似乎总是将引用的输入与" QUOTED_LITERAL"匹配,并且永远不会将ID包装在引号中。
以下是我的结果:
下面是我对SQL语法表达部分的简化语法,如果有人可以帮助确定匹配引用的规则以外的引号规则所需的内容,谢谢。
grammar test;
expression
:
simpleExpression EOF!
;
simpleExpression
:
column_spec
| literal_value
;
column_spec
:
(table_name '.')? column_name
| ('\''table_name '\'''.')? '\'' column_name '\''
| ('\"'table_name '\"' '.')? '\"' column_name '\"'
;
string_literal: QUOTED_LITERAL ;
boolean_literal: 'TRUE' | 'FALSE' ;
literal_value :
(
string_literal
| boolean_literal
)
;
table_name :ID;
column_name :ID;
QUOTED_LITERAL:
( '\''
( ('\\' '\\') | ('\'' '\'') | ('\\' '\'') | ~('\'') )*
'\'' )
|
( '\"'
( ('\\' '\\') | ('\"' '\"') | ('\\' '\"') | ~('\"') )*
'\"' )
;
ID
:
( 'A'..'Z' | 'a'..'z' ) ( 'A'..'Z' | 'a'..'z' | '_' | '0'..'9'| '::' )*
;
WHITE_SPACE : ( ' '|'\r'|'\t'|'\n' ) {$channel=HIDDEN;} ;
答案 0 :(得分:0)
这是你经典的转变/减少冲突。 (除非ANTLR不移位或减少;因为它不是堆栈自动机。)
您遇到以下问题:
当您处于simpleExpression状态时,您需要决定使用一个令牌前瞻的分支。在ANTLR的情况下,由于词法分析器和解析器之间没有区别,因此一个标记是单个字符。 (你应该看到ANTLR关于冲突的警告。)
它变得更好,“Bob Dillan”和“table1”有什么区别?从解析器的角度来看,没有。那么你如何期望在:
之间产生影响('\"'table_name '\"' '.')? '\"' column_name '\"'
和
( '\"'
( ('\\' '\\') | ('\"' '\"') | ('\\' '\"') | ~('\"') )*
'\"' )
我强烈建议将simpleExpression
规则重写为:
simpleExpression:
IDENTIFIER |
IDENTIFIER . IDENTIFIER |
QUOTED_LITERAL |
QUOTED_LITERAL . QUOTED_LITERAL |
boolean_literal;
然后在simpleExpression的动作代码中决定该怎么做。特别是因为我很确定你可以引用一个带引号的表格;从来没有那么“用户”和“Bod Dillan”在语法上是平等的。
它还取决于更改的语法,您也可以在更高的层次上解决可靠性问题。
答案 1 :(得分:0)
antlr lexer贪婪,因为当有两个可能的令牌匹配时,它将匹配最长的匹配。
当词法分析器看到'some_id'时,它可以将第一个引用匹配为引号或引用的文字。文字更长,所以匹配。
作为旁注,您通常不希望lexer规则可以匹配任何内容(如ID)或在解析器规则中使用字符串常量,但只需要引用令牌名称。
你想做的就是这样。
QUOTE: '\'';
ID: ('a'..'z' | 'A'..'Z')+; // Must have at least one character
QUOTED_LITERAL: QUOTE ( (ID QUOTE) => { $type=QUOTE; } ) | .* QUOTE;
id: ID | QUOTE ID QUOTE;
quoted_literal: QUOTED_LITERAL | QUOTE ID QUOTE;
如果词法分析器看到的内容看起来像带引号的id,则无法分辨使用哪个内容,因此会将其分解为较小的令牌。在您的解析器中,您使用可能引用ID的id,以及您期望QUOTED_LITERAL的quoted_lite。
当输入不明确时,QUOTED_LITERAL中的句法谓词会阻止它匹配完整的引用。
看起来这样,它将无法正确解析
之类的行'tag' text 'second'
因为'text'将被解析为QUOTED_LITERAL。如果这是一个有效的输入,那么你需要像
这样的东西fragment QUOTED_ID;
QUOTED_LITERAL: QUOTE ( ID {$type=QUOTED_ID} | .* ) QUOTE;
id: ID | QUOTED_ID;
quoted_literal: QUOTED_LITERAL | QUOTED_ID;
(我的示例并未涵盖您输入中的所有情况,但扩展它应该是显而易见的。您可能还需要一些操作来在AST中生成正确的令牌或添加/删除文本中的引号,具体取决于哪一个你在解析之后就做了。)
答案 2 :(得分:0)
如果有人感兴趣,我从引用的文字字符串中删除了一点灵活性。文字字符串只能用单引号引用,标识符可以选择用双引号引用。只要文字引号和标识符引用定义明确并且它们不重叠,语法就是微不足道的。
此政策使语法更清晰,并且不会删除引用标识符的功能。我使用JDBC方法getIdentifierQuote来报告可以用来包装标识符的引用。