ANTLR解析文字和带引号的ID

时间:2013-01-24 15:19:29

标签: parsing antlr grammar quotes

我在ANTLR中处理SQL语法,它允许带引号的标识符(表名,字段名等),以及引用的文字字符串。

问题是这个语法似乎总是将引用的输入与" QUOTED_LITERAL"匹配,并且永远不会将ID包装在引号中。

以下是我的结果:

  • 输入:' blahblah'结果:string_literal符合预期。
  • 输入:field1 restul:column_name按预期
  • 输入:table.field1结果:column_spec按预期
  • 输入:'表'。' field1' result:string_literal,MissingTokenException

下面是我对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;} ;

3 个答案:

答案 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来报告可以用来包装标识符的引用。