1

私は、引用された識別子 (テーブル名、フィールド名など) と引用されたリテラル文字列を許可する ANTLR の SQL 文法に取り組んでいます。

問題は、この文法が引用符で囲まれた入力を常に "QUOTED_LITERAL" として一致させ、ID を引用符で囲んだものとして一致させないように見えることです。

ここに私の結果があります:

  • 入力: 'blahblah' 結果: 期待どおりの string_literal。
  • 入力: フィールド 1 の結果: 期待どおりの column_name
  • 入力: table.field1 結果: 期待どおりの column_spec
  • 入力: '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;} ;
4

3 に答える 3

1

誰かが興味を持っている場合に備えて、引用符で囲まれたリテラル文字列から柔軟性を少し取り除きました。リテラル文字列は一重引用符でのみ引用でき、識別子はオプションで二重引用符で囲むことができます。リテラルの引用と識別子の引用が明確に定義されていて、それらが重複していない限り、文法は自明です。

このポリシーにより、文法がより明確になり、識別子を引用する機能が削除されません。JDBC メソッド getIdentifierQuote を使用して、識別子をラップするために使用できる引用符を報告します。

于 2013-02-01T14:38:50.137 に答える
0

antlr lexer は貪欲です。つまり、トークンの一致候補が 2 つある場合、可能な限り長いものに一致します。

lexer が「some_id」を検出すると、最初の引用を単なる引用または引用されたリテラルとして一致させることができます。リテラルの方が長いので一致します。

補足として、一般的には、(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 を使用します。

QUOTED_LITERAL の構文述語は、入力があいまいな場合に完全な引用符に一致することを防ぎます。

これを見ると、次のような行を正しく解析できません

'tag' text 'second'

as ' 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 で正しいトークンを生成するか、テキストから引用符を追加/削除するためのアクションが必要になるでしょう。あなたが解析します。)

于 2013-01-24T16:14:36.740 に答える
0

これは、従来の shift/reduce 競合です。(ただし、ANTLR はスタック オートマトンではないため、シフトまたは削減しません。)

次の問題があります。

simpleExpression 状態にあるときは、1 つのトークン先読みでどの分岐を取るかを決定する必要があります。ANTLRの場合、レクサーとパーサーで区別がつかないので、1つのトークンは1文字です。(競合に関する ANTLR からの警告が表示されるはずです。)

「ボブ・ディラン」と「table1」の違いは何ですか?パーサーの観点からは、何もありません。では、次の違いをどのように期待しますか。

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

(  '\"'
    ( ('\\' '\\') | ('\"' '\"') | ('\\' '\"') | ~('\"') )* 
'\"'  )

simpleExpressionルールを次のように書き直すことを強くお勧めします。

simpleExpression: 
    IDENTIFIER |
    IDENTIFIER . IDENTIFIER |
    QUOTED_LITERAL |
    QUOTED_LITERAL . QUOTED_LITERAL |
    boolean_literal;

そして、simpleExpression のアクション コードで何をするかを決定します。特に、引用符で囲まれた名前でテーブルを参照できると確信しているためです。「users」と「Bod Dillan」は構文的に同等です。

おろし文法にもよりますが、より高いレベルで愛嬌を解決できる場合もあります。

于 2013-01-24T16:06:59.610 に答える