IN句を伴うコメントを特別扱いする様にしてみた。
2WaySQLの中でもかなり怪しい仕様、と言ってもこれが非常に便利なのだけど、
IN句の後ろにあるコメント及び、その後のテキストノードが括弧で括られている時、
括弧で括られている範囲を、実行時にゴッソリ落して、再構築してくれるんですなぁ。
例えば、こんな風に元のSQLがなっている。
SELECT * FROM HOGE WHERE HOGEID IN /*piro*/ (3,2,1)
実行時に、piroとして、
{"cc","dd","ff","gg"}
みたいなのが渡されると、最終的なSQLは、
SELECT * FROM HOGE WHERE HOGEID IN (?,?,?,?)
となり、出来た?に、左から、cc,dd,ff,ggがバインドされるんですなぁ。
結構大変かと思ったけど、意外とサックリできた感じ。
grammar TwoWaySQL; options { language = Java; output = AST; ASTLabelType = CommonTree; } tokens { BEGINNODE; IFNODE; EXPRESSIONNODE; ELSENODE; ELSEIFNODE; INBINDNODE; COMMENTNODE; } @header { package twowaysql.grammar; } @lexer::header { package twowaysql.grammar; } @lexer::members { boolean inComment = false; boolean inLineComment = false; } twowaySQL : txt EOF; txt : (comment | inbind | charactors)+ ; charactors : (IDENT| SYMBOLS | QUOTED | SYM_C | SYM_LP | SYM_RP)+ ; // $<comment comment : begincomment | ifcomment | blockcomment | linecomment ; blockcomment : C_ST charactors C_ED -> ^(COMMENTNODE charactors); linecomment : C_LN_ST charactors C_LN_ED; ifcomment : (C_ST IF expression C_ED txt elseifnode* elsenode? endcomment) -> ^(IFNODE expression txt elseifnode* elsenode? ) ; elseifnode : elseifcomment txt -> ^(ELSEIFNODE elseifcomment txt); elsenode : elsecomment txt -> ^(ELSENODE txt); elseifcomment : (C_ST ELSEIF expression C_ED | C_LN_ST ELSEIF expression C_LN_ED) -> expression; elsecomment : (C_ST ELSE C_ED | C_LN_ST ELSE C_LN_ED) ; expression : charactors -> ^(EXPRESSIONNODE charactors); begincomment : ((C_ST BEGIN C_ED | C_LN_ST BEGIN C_LN_ED) txt endcomment) -> ^(BEGINNODE txt); endcomment : C_ST END C_ED | C_LN_ST END C_LN_ED; inbind : IN blockcomment SYM_LP inbindchars (SYM_C inbindchars)* SYM_RP -> ^(INBINDNODE IN blockcomment) ; inbindchars : (IDENT| SYMBOLS | SYM_C | QUOTED)+; // $> SYMBOLS : '*' | '/' | '-'; SYM_LP : '('; SYM_RP : ')'; SYM_C : ','; QUOTED : SYM_Q ~(SYM_Q)+ SYM_Q; fragment SYM_Q : '\u0027' | '"'; C_ST : {!inComment}? '/*' { inComment = true; }; C_ED : {inComment}? '*/' { inComment = false; }; C_LN_ST : {!inComment}? '--' { inLineComment = true; inComment = true; }; C_LN_ED : {inLineComment}? ( LN_R LN_N | LN_R | LN_N | EOF ) { inLineComment = false; inComment = false; }; // $<Keywords BEGIN : ('b'|'B')('e'|'E')('g'|'G')('i'|'I')('n'|'N'); IF : ('i'|'I')('f'|'F'); ELSE : ('e'|'E')('l'|'L')('s'|'S')('e'|'E'); ELSEIF : ELSE IF; END : ('e'|'E')('n'|'N')('d'|'D'); IN : {!inComment}? ('i'|'I')('n'|'N'); // $> IDENT : CHAR+; fragment WS : '\t' | '\v' | '\f' | ' ' | '\u00A0'; fragment LN_R : '\r'; fragment LN_N : '\n'; fragment CHAR : ~(SYMBOLS | SYM_Q | SYM_LP | SYM_RP | SYM_C | LN_R | LN_N | WS); LT : {!inLineComment}? (LN_R | LN_N)+ { $channel = HIDDEN; }; WHITE_SPACES : (WS)+ { $channel = HIDDEN; };
こんなテキストを食わせて実行してみているですだよ。
ada.cec のしのし / 3 d/*aa hoge,( piro*/ ぎゃぼー -- BeGiN /* If aa - bb dd*/moge piro /*eLsEIF ccc + 44 '*/' */cccd -- elSEif fuga < '0' dfdze -- ElSe zzz "-- fugafuga" iN/* hoge In(aa,bbb)*/('hoge',40) -- eNd -- moge うぼぁ zz /* EnD*/