Parsing ANTLR解析文本和带引号的ID

Parsing ANTLR解析文本和带引号的ID,parsing,antlr,grammar,quotes,Parsing,Antlr,Grammar,Quotes,我正在ANTLR中开发一个SQL语法,它允许使用带引号的标识符(表名、字段名等)以及带引号的文本字符串 问题在于,这种语法似乎总是将引用的输入匹配为“quoted_LITERAL”,而不是作为用引号包装的ID 以下是我的结果: 输入:“blahblah”结果:字符串_文字如预期 输入:field1 restul:按预期列名称 输入:table.field1结果:列规格符合预期 输入:'table.'field1'结果:string_literal,MissingTokenException

我正在ANTLR中开发一个SQL语法,它允许使用带引号的标识符(表名、字段名等)以及带引号的文本字符串

问题在于,这种语法似乎总是将引用的输入匹配为“quoted_LITERAL”,而不是作为用引号包装的ID

以下是我的结果:

  • 输入:“blahblah”结果:字符串_文字如预期
  • 输入:field1 restul:按预期列名称
  • 输入:table.field1结果:列规格符合预期
  • 输入:'table.'field1'结果: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;} ;

这是您的经典转换/减少冲突。(除了ANTLR不会移动或减少,因为它不是堆栈自动机。)

您有以下问题:

当您处于simpleExpression状态时,您需要通过一个令牌前瞻来决定采用哪个分支。在ANTLR的情况下,由于lexer和parser之间没有区别,所以一个标记是一个字符。(您应该看到ANTLR发出的关于冲突的警告。)

更妙的是,“鲍勃·迪伦”和“表1”有什么区别?从解析器的角度来看,没有。那么,您希望如何在以下两者之间做出区别:

('\"'table_name '\"' '.')? '\"' column_name '\"'

我强烈建议将
simpleExpression
规则重写为:

simpleExpression: 
    IDENTIFIER |
    IDENTIFIER . IDENTIFIER |
    QUOTED_LITERAL |
    QUOTED_LITERAL . QUOTED_LITERAL |
    boolean_literal;
然后在simpleExpression的操作代码中决定要做什么。特别是因为我很确定您可以引用带有引号的名称的表;“用户”和“Bod Dillan”在语法上永远是平等的


这也取决于语法,你也可以在更高的层次上解决和蔼可亲的问题

这是您的经典转换/减少冲突。(除了ANTLR不会移动或减少,因为它不是堆栈自动机。)

您有以下问题:

当您处于simpleExpression状态时,您需要通过一个令牌前瞻来决定采用哪个分支。在ANTLR的情况下,由于lexer和parser之间没有区别,所以一个标记是一个字符。(您应该看到ANTLR发出的关于冲突的警告。)

更妙的是,“鲍勃·迪伦”和“表1”有什么区别?从解析器的角度来看,没有。那么,您希望如何在以下两者之间做出区别:

('\"'table_name '\"' '.')? '\"' column_name '\"'

我强烈建议将
simpleExpression
规则重写为:

simpleExpression: 
    IDENTIFIER |
    IDENTIFIER . IDENTIFIER |
    QUOTED_LITERAL |
    QUOTED_LITERAL . QUOTED_LITERAL |
    boolean_literal;
然后在simpleExpression的操作代码中决定要做什么。特别是因为我很确定您可以引用带有引号的名称的表;“用户”和“Bod Dillan”在语法上永远是平等的


这也取决于语法,你也可以在更高的层次上解决和蔼可亲的问题

antlr lexer是贪婪的,因为当有两个可能的令牌匹配时,它将匹配最长的一个

当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;
如果lexer看到的东西看起来像一个带引号的id,它就无法知道该使用哪个,所以它会将它分解成更小的标记。在您的解析器中,您在期望可能被引用的id的地方使用id,在期望被引用的文本的地方使用quoted_literal

QUOTED_LITERAL中的语法谓词阻止它在输入不明确时匹配完整quote

这样看来,它将无法正确解析以下行

'tag' text 'second'
as“text”将被解析为带引号的文本。如果这是一个有效的输入,那么您需要

fragment QUOTED_ID;
QUOTED_LITERAL: QUOTE ( ID {$type=QUOTED_ID} | .* ) QUOTE;
id: ID | QUOTED_ID;
quoted_literal: QUOTED_LITERAL | QUOTED_ID;

(我的示例并没有涵盖输入中的所有情况,但扩展它应该是显而易见的。您可能还需要一些操作来在AST中生成正确的标记或从文本中添加/删除引号,这取决于您在解析后所做的操作。)

antlr lexer是贪婪的,因为当存在两个可能的标记匹配时,它将匹配最长的一个

当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;
如果lexer看到的东西看起来像一个带引号的id,它就无法知道该使用哪个,所以它会将它分解成更小的标记。在您的解析器中,您在期望可能被引用的id的地方使用id,在期望被引用的文本的地方使用quoted_literal

QUOTED_LITERAL中的语法谓词阻止它在输入不明确时匹配完整quote

这样看来,它将无法正确解析以下行

'tag' text 'second'
as“text”将被解析为带引号的文本。如果这是一个有效的输入,那么您需要

fragment QUOTED_ID;
QUOTED_LITERAL: QUOTE ( ID {$type=QUOTED_ID} | .* ) QUOTE;
id: ID | QUOTED_ID;
quoted_literal: QUOTED_LITERAL | QUOTED_ID;

(我的示例并没有涵盖输入中的所有情况,但扩展它应该是显而易见的。您可能还需要一些操作来在AST中生成正确的标记,或者从文本中添加/删除引号,具体取决于您在解析后所做的操作。)

如果有人感兴趣,我从引用的文本字符串中去掉了一点灵活性。文字字符串只能用单引号引用,标识符可以选择用双引号引用。只要文字引号和标识符引号定义良好且不重叠,语法就很简单

这项政策使