001// lexical analyzer for GSQLParser component java version 002 003/**************************************************** 004 Lexical analizer for GSQLParser Java component 005 Copyright (c) 2004-2024 by Gudu software 006****************************************************/ 007 008package gudusoft.gsqlparser; 009 010import java.util.HashMap; 011import java.io.InputStreamReader; 012 013import java.util.Locale; 014import java.io.BufferedReader; 015import java.io.IOException; 016 017 018public class TLexerSnowflake extends TCustomLexer{ 019 static int yynmarks = 0 ; 020 static int yynmatches ; 021 static int yyntrans ; 022 static int yynstates ; 023 static int[] yyk,yym ; // 1 based 024 static int[] yytint; // 1 based 025 static TYytRec[] yyt ; // 1 based 026 static int[] yykl,yykh,yyml,yymh,yytl,yyth ; // 0 based 027 private static String[] keywordlist; 028 static String table_file; 029 static HashMap<String, Integer> keywordValueList; 030 static HashMap<Integer, Integer> keywordTypeList; 031 static int[][] yystateTable; 032 033 static { 034 keywordValueList = new HashMap<String, Integer>(); 035 keywordTypeList = new HashMap<Integer, Integer>(); 036 table_file = "/gudusoft/gsqlparser/parser/snowflake/snowflake_lex_table.txt"; 037 if (TBaseType.enterprise_edition||TBaseType.snowflake_edition){ 038 inittable(); 039 } 040 } 041 042 public TLexerSnowflake(){ 043 super(); 044 dbvendor = EDbVendor.dbvsnowflake; 045 } 046 047 // Track non-SQL languages (JAVA, PYTHON, SCALA) so $$ body is not parsed as SQL 048 private boolean isNonSQLLanguageDetected = false; 049 private boolean isLanguagePending = false; 050 051public boolean canBeColumnName(int tokencode){ 052 //http://blog.csdn.net/superbeck/article/details/5387476 053 boolean ret = false; 054 int modifiers = keyword_type_identifier | keyword_type_column ; 055 Integer s = keywordTypeList.get(tokencode); 056 if (s != null){ 057 int modifier = s; 058 ret = (modifiers & modifier) == modifier; 059 } 060 061 return ret; 062} 063 064 public int iskeyword(String str){ 065 int ret = -1; 066 Integer s = keywordValueList.get(str.toUpperCase(Locale.ENGLISH)); 067 if( s != null){ 068 ret = s; 069 } 070 return ret;// -1 means not a keyword 071 } 072 073 public int getkeywordvalue(String keyword){ 074 int ret = 0; 075 Integer s = keywordValueList.get(keyword.toUpperCase(Locale.ENGLISH)); 076 if( s != null){ 077 ret = s; 078 } 079 return ret;// 0 means not a keyword 080 } 081 082 public static EKeywordType getKeywordType(String keyword){ 083 return TCustomLexer.getKeywordType(keyword,keywordValueList,keywordTypeList); 084 } 085 086 static void yystateLookupConfigure() { 087 int yystates = yytl.length; 088 yystateTable = new int[257][yystates]; 089 090 // initialize to empty 091 for(int i = 0; i < yystates; i++) { 092 for (int j = 0; j < 257; j++) 093 yystateTable[j][i] = -1; 094 } 095 096 for(int i = 0; i < yystates; i++) { 097 int low = yytl[i]; 098 int high = yyth[i]; 099 for (int j = low; j <= high; j++) { 100 for (char c: yyt[j].cc) { 101 yystateTable[c][i] = j; 102 } 103 } 104 } 105 } 106 107 int yylex(){ 108 int yyn; 109 while (true) { // top level while 110 yynew(); 111 while (true){ //scan 112 for(yyn = yykl[yystate]; yyn <= yykh[yystate]; yyn++){ 113 yymark(yyk[yyn]); 114 } 115 116 for(yyn=yymh[yystate]; yyn>= yyml[yystate]; yyn--){ 117 yymatch(yym[yyn]); 118 } 119 120 if(yytl[yystate] > yyth[yystate]){ 121 break; 122 } 123 124 yyscan(); 125// yyn = yytl[yystate]; 126 totablechar(); 127// while( (yyn <= yyth[yystate]) && (!(charinarray(yytablechar,yyt[yyn].cc))) ){ 128// yyn++; 129// } 130// if (yyn > yyth[yystate]){ 131// break; 132// } 133 134 yyn = yystateTable[yytablechar][yystate]; 135 if (yyn == -1) 136 break; 137 138 yystate = yyt[yyn].s; 139 } //scan 140 141 while (true){ //action 142 int yyrule; 143 if ( (yyrule = yyfind()) != -1 ){ 144 yyaction(yyrule); 145 if (yyreject){ 146 continue; 147 } 148 }else if( (!yydefault() ) && (yywrap()) ){ 149 yyclear(); 150 returni(0); 151 } 152 break; 153 } 154 155 if (!yydone) { 156 continue; 157 } 158 break; 159 } // top level while 160 161 return yyretval; 162 } 163 164 static void inittable(){ 165 166 //if (yynmarks > 0) return; //init table already 167 168 String line; 169 boolean inyyk=false,inyym=false,inyykl=false,inyykh=false,inyyml=false,inyymh=false,inyytl=false,inyyth=false,inyytint=false,inyyt=false,inkeyword=false; 170 int yyk_count=0,yym_count=0,yykl_count=0,yykh_count=0,yyml_count=0,yymh_count=0,yytl_count=0,yyth_count=0,yytint_count=0,yyt_count=0; 171 int c=0; 172 keywordValueList.clear(); 173 keywordTypeList.clear(); 174 175 BufferedReader br = new BufferedReader(new InputStreamReader(TLexerSnowflake.class.getResourceAsStream(table_file))); 176 177 try{ 178 while( (line = br.readLine()) != null){ 179 if (line.trim().startsWith("yynmarks=")){ 180 String[] ss = line.split("[=;]"); 181 yynmarks=Integer.parseInt(ss[1].trim()); 182 yyk = new int[yynmarks+1]; 183 }else if (line.trim().startsWith("yynmatches=")){ 184 String[] ss = line.split("[=;]"); 185 yynmatches=Integer.parseInt(ss[1].trim()); 186 yym = new int[yynmatches+1]; 187 }else if (line.trim().startsWith("yyntrans=")){ 188 String[] ss = line.split("[=;]"); 189 yyntrans=Integer.parseInt(ss[1].trim()); 190 yytint = new int[yyntrans+1]; 191 yyt = new TYytRec[yyntrans+1]; 192 }else if (line.trim().startsWith("yynstates=")){ 193 String[] ss = line.split("[=;]"); 194 yynstates=Integer.parseInt(ss[1].trim()); 195 yykl = new int[yynstates]; 196 yykh = new int[yynstates]; 197 yyml = new int[yynstates]; 198 yymh = new int[yynstates]; 199 yytl = new int[yynstates]; 200 yyth = new int[yynstates]; 201 }else if (line.trim().startsWith("<end>")){ 202 if (inyyk){ 203 inyyk = false; 204 if (yynmarks+1 != yyk_count ){ 205 System.out.println("required1:"+(yynmarks)+" actually:"+(yyk_count-1)); 206 } 207 } 208 else if(inyym){ 209 inyym = false; 210 if (yynmatches+1 != yym_count ){ 211 System.out.println("required2:"+(yynmatches)+" actually:"+(yym_count-1)); 212 } 213 } 214 else if(inyykl){ 215 inyykl = false; 216 if (yynstates != yykl_count ){ 217 System.out.println("required3:"+(yynstates)+" actually:"+(yykl_count)); 218 } 219 } 220 else if(inyykh){ 221 inyykh = false; 222 if (yynstates != yykh_count ){ 223 System.out.println("required4:"+(yynstates)+" actually:"+(yykh_count)); 224 } 225 } 226 else if(inyyml){ 227 inyyml = false; 228 if (yynstates != yyml_count ){ 229 System.out.println("required5:"+(yynstates)+" actually:"+(yyml_count)); 230 } 231 } 232 else if(inyymh){ 233 inyymh = false; 234 if (yynstates != yymh_count ){ 235 System.out.println("required:"+(yynstates)+" actually:"+(yymh_count)); 236 } 237 } 238 else if(inyytl){ 239 inyytl = false; 240 if (yynstates != yytl_count ){ 241 System.out.println("required6:"+(yynstates)+" actually:"+(yytl_count)); 242 } 243 } 244 else if(inyyth){ 245 inyyth = false; 246 if (yynstates != yyth_count ){ 247 System.out.println("required7:"+(yynstates)+" actually:"+(yyth_count)); 248 } 249 } 250 else if(inyytint){ 251 inyytint = false; 252 if (yyntrans + 1 != yytint_count ){ 253 System.out.println("required8:"+(yyntrans)+" actually:"+(yytint_count-1)); 254 } 255 } 256 else if(inyyt){ 257 inyyt = false; 258 if (yyntrans+1 != yyt_count ){ 259 System.out.println("required9:"+(yyntrans)+" actually:"+(yyt_count-1)); 260 } 261 } 262 else if(inkeyword){ 263 inkeyword = false; 264 } 265 }else if(line.trim().startsWith("yyk =")){ 266 inyyk = true; 267 }else if(line.trim().startsWith("yym =")){ 268 inyym = true; 269 }else if(line.trim().startsWith("yykl =")){ 270 inyykl = true; 271 }else if(line.trim().startsWith("yykh =")){ 272 inyykh = true; 273 }else if(line.trim().startsWith("yyml =")){ 274 inyyml = true; 275 }else if(line.trim().startsWith("yymh =")){ 276 inyymh = true; 277 }else if(line.trim().startsWith("yytl =")){ 278 inyytl = true; 279 }else if(line.trim().startsWith("yyth =")){ 280 inyyth = true; 281 }else if(line.trim().startsWith("yytint =")){ 282 inyytint = true; 283 }else if(line.trim().startsWith("yyt =")){ 284 inyyt = true; 285 }else if(line.trim().startsWith("keywordsvalue =")){ 286 inkeyword = true; 287 }else if(inyyk){ 288 String[] ss = line.split("[,]"); 289 for(int j=0;j<ss.length;j++){ 290 // System.out.println(ss[j].trim()); 291 yyk[yyk_count++] = Integer.parseInt(ss[j].trim()); 292 } 293 }else if(inyym){ 294 String[] ss = line.split("[,]"); 295 for(int j=0;j<ss.length;j++){ 296 // System.out.println(ss[j].trim()); 297 yym[yym_count++] = Integer.parseInt(ss[j].trim()); 298 } 299 }else if(inyykl){ 300 String[] ss = line.split("[,]"); 301 for(int j=0;j<ss.length;j++){ 302 // System.out.println(ss[j].trim()); 303 yykl[yykl_count++] = Integer.parseInt(ss[j].trim()); 304 } 305 }else if(inyykh){ 306 String[] ss = line.split("[,]"); 307 for(int j=0;j<ss.length;j++){ 308 // System.out.println(ss[j].trim()); 309 yykh[yykh_count++] = Integer.parseInt(ss[j].trim()); 310 } 311 }else if(inyyml){ 312 String[] ss = line.split("[,]"); 313 for(int j=0;j<ss.length;j++){ 314 // System.out.println(ss[j].trim()); 315 yyml[yyml_count++] = Integer.parseInt(ss[j].trim()); 316 } 317 }else if(inyymh){ 318 String[] ss = line.split("[,]"); 319 for(int j=0;j<ss.length;j++){ 320 // System.out.println(ss[j].trim()); 321 yymh[yymh_count++] = Integer.parseInt(ss[j].trim()); 322 } 323 }else if(inyytl){ 324 String[] ss = line.split("[,]"); 325 for(int j=0;j<ss.length;j++){ 326 // System.out.println(ss[j].trim()); 327 yytl[yytl_count++] = Integer.parseInt(ss[j].trim()); 328 } 329 }else if(inyyth){ 330 String[] ss = line.split("[,]"); 331 for(int j=0;j<ss.length;j++){ 332 // System.out.println(ss[j].trim()); 333 yyth[yyth_count++] = Integer.parseInt(ss[j].trim()); 334 } 335 }else if(inyytint){ 336 String[] ss = line.split("[,]"); 337 for(int j=0;j<ss.length;j++){ 338 // System.out.println(ss[j].trim()); 339 yytint[yytint_count++] = Integer.parseInt(ss[j].trim()); 340 } 341 }else if(inyyt){ 342 //System.out.println(line.trim()); 343 344 c = 0; 345 String[] st = line.trim().split(",,"); 346 char[] tmp = new char[st.length]; 347 for(int i=0;i<st.length;i++){ 348 349 if(st[i].startsWith("\'")) { 350 if(st[i].length() == 3){ // 'a' 351 tmp[c++] = st[i].charAt(1); 352 }else if(st[i].length() == 4) { // '\\' 353 tmp[c++] = st[i].charAt(2); 354 }else{ 355 System.out.println(" read yytstr error, error string is "+st[i]+ "line: "+ yyt_count); 356 } 357 }else{ 358 try{ 359 tmp[c++] = (char)Integer.parseInt(st[i]); // char in number like 32 that represent space 360 } catch (NumberFormatException nfe) { 361 System.out.println("NumberFormatException: " + nfe.getMessage()); 362 } 363 } 364 } //while hasmoreTokens 365 366 //yyt[lineno] = new YYTrec(tmp,yytint[lineno]); 367 yyt[yyt_count] = new TYytRec(tmp,yytint[yyt_count]); 368 yyt_count++; 369 370 }else if(inkeyword){ 371 String[] ss =line.split("[=]"); 372 373 int val1 = -1; 374 int val2 = -1; 375 try { 376 val1 = Integer.parseInt(ss[1]); 377 val2 = Integer.parseInt(ss[2]); 378 } 379 catch (NumberFormatException nfe) { 380 System.out.println("NumberFormatException: " + nfe.getMessage()); 381 } 382 keywordValueList.put(ss[0].toUpperCase(),val1); 383 keywordTypeList.put(val1,val2); 384 } 385 } 386 }catch(IOException e){ 387 System.out.println(e.toString()); 388 } 389 390 yystateLookupConfigure(); 391 392 } 393 394 395 void yyaction(int yyruleno){ 396 397 398 int ic; 399 char[] tmparray = {'=','+','-','*','/','>','<'}; 400 401 yylvalstr = getyytext(); 402 /* actions: */ 403 switch(yyruleno){ 404 case 1: 405 406 { 407 returni (filepath_sign); 408 break; 409 } 410 411 case 2: 412 413 { 414 if (yylvalstr.equalsIgnoreCase(dolqstart)) 415 { 416 //dolqstart = ""; 417 start(init); 418 addlit(yylvalstr,yytextlen); 419 yylvalstr = litbufdup(); 420 returni(sconst); 421 } 422 else 423 { 424 //nchars = yytextlen; 425 addlit(yylvalstr, yytextlen-1); 426 yyless(yytextlen-1); 427 return; 428 } 429 //System.out.println("<xdolq>{dolqdelim}: "+dolqstart); 430 break; 431 } 432 433 case 3: 434 435 { 436 if (isReadyForFunctionBody) { // meet the first $$ 437 //isInFunctionBody = true; 438 isReadyForFunctionBody = false; 439 functionBodyDelimiterIndex++; 440 functionBodyDelimiter.add(yylvalstr); 441 // System.out.println("start function body:"+functionBodyDelimiter.get(functionBodyDelimiterIndex)); 442 returni(snowflake_function_delimiter); 443 }else if ((functionBodyDelimiterIndex>=0)&&(functionBodyDelimiter.get(functionBodyDelimiterIndex).equalsIgnoreCase(yylvalstr))){ // meet the second $$ 444 // Lookahead to check if this $$ is ending function body or starting a nested string 445 // If next char is whitespace, ';', ')', or EOF, it's ending the function body 446 // If next char is alphanumeric, backslash, or other content, it's starting a nested string 447 char nextChar = get_char(); 448 boolean isEndingFunctionBody = (nextChar == ' ' || nextChar == '\t' || nextChar == '\n' || 449 nextChar == '\r' || nextChar == ';' || nextChar == ')' || 450 nextChar == '\0' || nextChar == 0); 451 if (nextChar != '\0' && nextChar != 0) { 452 unget_char(nextChar); // put the character back 453 } 454 455 if (isEndingFunctionBody) { 456 //isInFunctionBody = false; 457 //System.out.println("end function body:"+functionBodyDelimiter.get(functionBodyDelimiterIndex)); 458 functionBodyDelimiter.remove(functionBodyDelimiterIndex); 459 functionBodyDelimiterIndex--; 460 returni(snowflake_function_delimiter); 461 } else { 462 // Not ending function body, start a nested dollar-quoted string 463 start(xdolq); 464 startlit(); 465 dolqstart = yylvalstr; 466 addlit(yylvalstr,yytextlen); 467 } 468 }else { 469 if (getyysstate() == xq){ 470 nchars = yytextlen; 471 addlit(yylvalstr, yytextlen-1); 472 yyless(nchars-1); 473 return;//exit; 474 }else{ 475 start(xdolq); 476 startlit(); 477 dolqstart = yylvalstr; 478 addlit(yylvalstr,yytextlen); 479 } 480 } 481 482 break; 483 } 484 485 case 4: 486 487 { 488 addlit(yylvalstr, yytextlen); 489 //System.out.println("<xdolq>{dolqinside}: "+yylvalstr); 490 break; 491 } 492 493 case 5: 494 495 { 496 addlit(yylvalstr, yytextlen); 497 break; 498 } 499 500 case 6: 501 502 { 503 addlitchar(yylvalstr.charAt(0)); 504 break; 505 } 506 507 508 case 7: 509 510 { 511 if (getyysstate() == xq) 512 { 513 nchars = yytextlen; 514 addlit(yylvalstr, yytextlen-1); 515 yyless(nchars-1); 516 return;//exit; 517 } 518 519 start(xq); 520 startlit(); 521 addlit(yylvalstr,yytextlen); 522 break; 523 } 524 525 case 8: 526 527 { 528 if (getyysstate() == xq) 529 { 530 nchars = yytextlen; 531 addlit(yylvalstr, yytextlen-1); 532 yyless(nchars-1); 533 return;//exit; 534 } 535 536 start(xq); 537 startlit(); 538 addlit(yylvalstr,yytextlen); 539 break; 540 } 541 542 case 9: 543 544 { 545 if (getyysstate() == xq) 546 { 547 nchars = yytextlen; 548 addlit(yylvalstr, yytextlen-1); 549 yyless(nchars-1); 550 return;//exit; 551 } 552 553 start(xq); 554 startlit(); 555 addlit(yylvalstr,yytextlen); 556 break; 557 } 558 559 case 10: 560 561 { 562 addlit(yylvalstr,yytextlen); 563 if (xcdepth <= 0) 564 { 565 start(init); 566 yylvalstr = litbufdup(); 567 returni(cmtslashstar); 568 } 569 else 570 xcdepth--; 571 572 break; 573 } 574 575 576 case 11: 577 578 { 579 if (yylvalstr.equalsIgnoreCase("/*+")){ 580 xcdepth++; 581 yyless(2); 582 addlit(yylvalstr,yytextlen); 583 }else{ 584 yyless(1); 585 addlit(yylvalstr,1); 586 } 587 588 break; 589 590 } 591 592 case 12: 593 594 { 595 596 if (getyysstate() == xq) 597 { 598 nchars = yytextlen; 599 addlit(yylvalstr, yytextlen-1); 600 yyless(nchars-1); 601 return;//exit; 602 } 603 604 xcdepth = 0; 605 start(xc); 606 startlit(); 607 yyless(2); 608 addlit(yylvalstr,yytextlen); 609 610 break; 611 } 612 613 case 13: 614 615 { 616 addlit(yylvalstr,yytextlen); 617 618 break; 619 } 620 621 case 14: 622 623 { 624 addlitchar(yylvalstr.charAt(0)); 625 626 break; 627 } 628 629 case 15: 630 631 { 632 start(init); 633 addlit(yylvalstr, yytextlen); 634 yylvalstr = litbufdup(); 635 if( yylvalstr.startsWith("b")|| (yylvalstr.startsWith("B"))){ 636 returni(bconst); 637 }else if( yylvalstr.startsWith("x")|| (yylvalstr.startsWith("X"))){ 638 returni(xconst); 639 }else 640 { 641 returni(sconst); 642 } 643 break; 644 } 645 646 case 16: 647 648 { 649 if (insqlpluscmd){ 650 yyless(0); 651 yylvalstr = litbufdup(); 652 start(init); 653 returni(sconst); 654 }else{ 655 addlit(yylvalstr,yytextlen); 656 } 657 658 break; 659 } 660 case 17: 661 662 { 663 if (getyysstate() == xq) 664 { 665 nchars = yytextlen; 666 addlit(yylvalstr, yytextlen-1); 667 yyless(nchars-1); 668 return;//exit; 669 } 670 671 start(xq); 672 startlit(); 673 addlit(yylvalstr,yytextlen); 674 675 dummych1 = get_char(); 676 if (dummych1 == '\\') // recognize string like '\' 677 { 678 dummych2 = get_char(); 679 if (dummych2 == '\'') 680 { 681 // start(init); 682 addlit("\\", 1); 683 addlit("\'", 1); 684 //yylvalstr = litbufdup(); 685 //returni(sconst); 686 } 687 else 688 { 689 unget_char(dummych2); 690 unget_char(dummych1); 691 } 692 } 693 else 694 { unget_char(dummych1);} 695 696 break; 697 } 698 699 case 18: 700 701 { 702 start(xq); 703 startlit(); 704 addlit(yylvalstr, yytextlen); 705 dummych1 = get_char(); 706 if (dummych1 == '\\') // recognize string like '\' 707 { 708 dummych2 = get_char(); 709 if (dummych2 == '\'') 710 { 711 // start(init); 712 addlit("\\", 1); 713 addlit("\'", 1); 714 //yylvalstr = litbufdup(); 715 //returni(sconst); 716 } 717 else 718 { 719 unget_char(dummych2); 720 unget_char(dummych1); 721 } 722 } 723 else 724 { unget_char(dummych1);} 725 726 break; 727 } 728 729 case 19: 730 731 { 732 addlit(yylvalstr, yytextlen); 733 break; 734 } 735 736 case 20: 737 738 { 739 dummych1 = get_char(); 740 unget_char(dummych1); 741 if (dummych1 == (char)10) 742 { 743 if (insqlpluscmd){ 744 nchars = yytextlen; 745 if(yylvalstr.charAt(nchars-1) == (char)13){ 746 yyless(nchars - 1); 747 yylvalstr = yylvalstr.substring(0,nchars); 748 } 749 start(init); 750 addlit(yylvalstr, nchars-1); 751 yylvalstr = litbufdup(); 752 returni(sconst); //in sqlplus command, characters between ' and return is treated as a string 753 754 }else{ 755 dummych1 = get_char(); 756 addlit(yylvalstr+dummych1, yytextlen+1); 757 } 758 } else if (dummych1 == '\\') 759 { 760 // Handle backslash inside string literals 761 // Snowflake supports both \' (C-style escape) and '' (SQL standard escape) 762 // The pattern \'' is ambiguous: 763 // - Could be \' (escape) + ' (close) - e.g., 'text\'' means text' then close 764 // - Could be \ (literal) + '' (escape) - e.g., 'text\''more' means text\'more 765 // We distinguish by looking at what follows \'' : 766 // - \''' : \' (escape) + '' (leave for xqdouble) - two quote chars in value 767 // - \''<letter/digit>: \ + '' (backslash + escape, string continues) 768 // - \''<space><SQL keyword>: \' + close (string ends, keyword follows) 769 // - \''<space><other>: \ + '' (backslash + escape, string continues with space) 770 // - \''<EOF/control>: \' + close 771 // See mantisbt issue 4298 for details 772 dummych1 = get_char(); // Read the backslash 773 dummych2 = get_char(); // Read the next char 774 if (dummych2 == '\'') { 775 // Have \' - check if followed by another quote 776 char dummych3 = get_char(); 777 if (dummych3 == '\'') { 778 // \'' pattern - check what comes after 779 char dummych4 = get_char(); 780 if (dummych4 == '\'') { 781 // \''' : consume \' as escape, leave '' for xqdouble 782 unget_char(dummych4); 783 unget_char(dummych3); 784 addlit(yylvalstr+dummych1+dummych2, yytextlen+2); 785 } else if (Character.isLetterOrDigit(dummych4) || dummych4 == '_') { 786 // \''<letter/digit>: \ + '' (string continues) 787 unget_char(dummych4); 788 unget_char(dummych3); 789 unget_char(dummych2); 790 addlit(yylvalstr+dummych1, yytextlen+1); 791 } else if (dummych4 == ' ' || dummych4 == '\t') { 792 // \''<space> - check if followed by SQL keyword 793 char dummych5 = get_char(); 794 char dummych6 = get_char(); 795 // Check for SQL keywords that typically follow a column/value expression 796 // Only check keywords that wouldn't appear as regular words in strings 797 String twoChars = "" + Character.toLowerCase(dummych5) + Character.toLowerCase(dummych6); 798 if (twoChars.equals("as")) { // AS is the most reliable indicator of SQL context 799 // Likely SQL keyword follows - treat \'' as \' + close 800 unget_char(dummych6); 801 unget_char(dummych5); 802 unget_char(dummych4); 803 unget_char(dummych3); 804 addlit(yylvalstr+dummych1+dummych2, yytextlen+2); 805 } else { 806 // Not a SQL keyword - treat as \ + '' (string continues) 807 unget_char(dummych6); 808 unget_char(dummych5); 809 unget_char(dummych4); 810 unget_char(dummych3); 811 unget_char(dummych2); 812 addlit(yylvalstr+dummych1, yytextlen+1); 813 } 814 } else if (dummych4 == ';' || dummych4 == ',' || dummych4 == ')' 815 || dummych4 == '|' || dummych4 == '+' || dummych4 == '=' 816 || dummych4 == '<' || dummych4 == '>' || dummych4 == '/' 817 || dummych4 == '*') { 818 // \''<SQL-punctuation>: \' (escape) + ' (close string) 819 unget_char(dummych4); 820 unget_char(dummych3); 821 addlit(yylvalstr+dummych1+dummych2, yytextlen+2); 822 } else if (dummych4 >= ' ' && dummych4 != (char)0 && dummych4 != (char)-1) { 823 // Other printable: \ + '' (string continues) 824 unget_char(dummych4); 825 unget_char(dummych3); 826 unget_char(dummych2); 827 addlit(yylvalstr+dummych1, yytextlen+1); 828 } else { 829 // \'' at EOF or followed by control char: \' escape + ' closes string 830 unget_char(dummych4); 831 unget_char(dummych3); 832 addlit(yylvalstr+dummych1+dummych2, yytextlen+2); 833 } 834 } else { 835 // \' followed by non-quote: consume \' as C-style escape 836 unget_char(dummych3); 837 addlit(yylvalstr+dummych1+dummych2, yytextlen+2); 838 } 839 } else { 840 // \X where X is not quote: consume both 841 addlit(yylvalstr+dummych1+dummych2, yytextlen+2); 842 } 843 } 844 else 845 { addlit(yylvalstr, yytextlen);} 846 847 break; 848 } 849 850 case 21: 851 852 { 853 addlit(yylvalstr, yytextlen); 854 // When the matched literal ends in a backslash, decide 855 // whether that final backslash is the first half of a 856 // \' escape pair (so the next quote should be consumed 857 // as the escape's second half) or the second half of a 858 // \\ escape pair (so the next quote is the literal's 859 // closing delimiter). Resolve via the parity of the 860 // trailing backslash run: 861 // - odd : final backslash unpaired -> treat next 862 // quote as \' escape (mantisbt issue 4435, 863 // cases like '\\'\\'X', '\\'\\''') 864 // - even : all trailing backslashes form \\ pairs 865 // already -> let the next quote close 866 // (preserve literals like '\\\\'). 867 int trailingBackslashes = 0; 868 for (int bi = yytextlen - 1; bi >= 0 && yylvalstr.charAt(bi) == '\\'; bi--) 869 { 870 trailingBackslashes++; 871 } 872 if ((trailingBackslashes & 1) == 1) 873 { 874 dummych1 = get_char(); 875 if (dummych1 == '\'') 876 { 877 dummych2 = get_char(); 878 if (dummych2 == '\'') 879 { 880 char dummych3 = get_char(); 881 if (dummych3 == '\'') 882 { 883 // \''' : \' (escape) + '' (leave for xqdouble) 884 unget_char(dummych3); 885 unget_char(dummych2); 886 addlit("\'", 1); 887 } 888 else if (Character.isLetterOrDigit(dummych3) || dummych3 == '_') 889 { 890 // \''<letter/digit>: backslash literal + '' 891 // (string continues with xqdouble matching '') 892 unget_char(dummych3); 893 unget_char(dummych2); 894 unget_char(dummych1); 895 } 896 else if (dummych3 == ' ' || dummych3 == '\t') 897 { 898 char dummych4 = get_char(); 899 char dummych5 = get_char(); 900 String twoChars = "" + Character.toLowerCase(dummych4) + Character.toLowerCase(dummych5); 901 if (twoChars.equals("as")) 902 { 903 // \''<space>AS : \' (escape) + ' (close) + AS 904 unget_char(dummych5); 905 unget_char(dummych4); 906 unget_char(dummych3); 907 unget_char(dummych2); 908 addlit("\'", 1); 909 } 910 else 911 { 912 // \''<space><other> : backslash literal + '' 913 unget_char(dummych5); 914 unget_char(dummych4); 915 unget_char(dummych3); 916 unget_char(dummych2); 917 unget_char(dummych1); 918 } 919 } 920 else if (dummych3 == ';' || dummych3 == ',' || dummych3 == ')' 921 || dummych3 == '|' || dummych3 == '+' || dummych3 == '=' 922 || dummych3 == '<' || dummych3 == '>' || dummych3 == '/' 923 || dummych3 == '*') 924 { 925 // \''<SQL-punctuation>: \' (escape) + ' (close) 926 unget_char(dummych3); 927 unget_char(dummych2); 928 addlit("\'", 1); 929 } 930 else if (dummych3 >= ' ' && dummych3 != (char)0 && dummych3 != (char)-1) 931 { 932 // Other printable: backslash literal + '' 933 unget_char(dummych3); 934 unget_char(dummych2); 935 unget_char(dummych1); 936 } 937 else 938 { 939 // \'' at EOF or control char: \' (escape) + ' (close) 940 unget_char(dummych3); 941 unget_char(dummych2); 942 addlit("\'", 1); 943 } 944 } 945 else 946 { 947 // \' followed by non-quote: simple C-style escape 948 unget_char(dummych2); 949 addlit("\'", 1); 950 } 951 } 952 else 953 { 954 unget_char(dummych1); 955 } 956 } 957 break; 958 } 959 960 961 962 case 22: 963 964 { 965 start(init); 966 addlit(yylvalstr, yytextlen); 967 if ((literallen == 0) && (!insqlpluscmd)) 968 {returni (error);} 969 if (literallen >= namedatalen) 970 { 971 setlengthofliteralbuf(namedatalen); 972 literallen = namedatalen; 973 } 974 yylvalstr = litbufdup(); 975 returni (ident); 976 977 break; 978 } 979 980 981 case 23: 982 983 { 984 if (insqlpluscmd){ 985 yyless(0); 986 yylvalstr = litbufdup(); 987 start(init); 988 returni(sconst); 989 }else{ 990 addlit(yylvalstr, yytextlen); 991 } 992 993 break; 994 } 995 996 case 24: 997 998 { 999 addlit(yylvalstr, yytextlen); 1000 break; 1001 } 1002 1003 case 25: 1004 1005 { 1006 // Handle ""identifier"" pattern from Snowflake GET_DDL() output. 1007 // When we see " followed by another ", and then identifier content 1008 // ending with "", consume the whole thing as one identifier token. 1009 dummych1 = get_char(); 1010 if (dummych1 == '"') { 1011 // We have "" at the start. Peek to see if identifier content follows. 1012 dummych2 = get_char(); 1013 if (Character.isLetter(dummych2) || dummych2 == '_' || (dummych2 >= '\200' && dummych2 <= '\377')) { 1014 // This is ""identifier..."" pattern. Read until closing "". 1015 startlit(); 1016 addlit("\"\"", 2); 1017 addlit(String.valueOf(dummych2), 1); 1018 // Read chars until we find closing "" 1019 while (true) { 1020 char ch = get_char(); 1021 if (ch == '"') { 1022 char ch2 = get_char(); 1023 if (ch2 == '"') { 1024 // Found "" - this is the closing "" 1025 addlit("\"\"", 2); 1026 yylvalstr = litbufdup(); 1027 returni(ident); 1028 break; 1029 } 1030 // Single " not followed by " - treat as closing quote 1031 unget_char(ch2); 1032 addlit("\"", 1); 1033 yylvalstr = litbufdup(); 1034 returni(ident); 1035 break; 1036 } 1037 if (ch == '\n' || ch == (char)0 || ch == (char)-1) { 1038 // End of line or input - close the identifier 1039 unget_char(ch); 1040 addlit("\"", 1); 1041 yylvalstr = litbufdup(); 1042 returni(ident); 1043 break; 1044 } 1045 addlit(String.valueOf(ch), 1); 1046 } 1047 break; 1048 } 1049 // "" not followed by identifier content - push back and handle normally 1050 unget_char(dummych2); 1051 unget_char(dummych1); 1052 } else { 1053 unget_char(dummych1); 1054 } 1055 start(xd); 1056 startlit(); 1057 addlit(yylvalstr, yytextlen); 1058 break; 1059 } 1060 1061 case 26: 1062 1063 { 1064 dummych1 = get_char(); 1065 unget_char(dummych1); 1066 if (dummych1 == (char)10) 1067 { 1068 if (insqlpluscmd){ 1069 nchars = yytextlen; 1070 if(yylvalstr.charAt(nchars-1) == (char)13){ 1071 yyless(nchars - 1); 1072 yylvalstr = yylvalstr.substring(0,nchars); 1073 } 1074 start(init); 1075 addlit(yylvalstr, nchars-1); 1076 yylvalstr = litbufdup(); 1077 returni(sconst); //in sqlplus command, characters between ' and return is treated as a string 1078 1079 }else{ 1080 dummych1 = get_char(); 1081 addlit(yylvalstr+dummych1, yytextlen+1); 1082 } 1083 1084 } else 1085 addlit(yylvalstr, yytextlen); 1086 1087 break; 1088 } 1089 1090 case 27: 1091 1092 { 1093 returni(lexnewline); 1094 break; 1095 } 1096 1097 case 28: 1098 1099 { 1100 returni(lexspace); 1101 break; 1102 } 1103 1104 case 29: 1105 1106 { 1107 if ((getyysstate() == xq) 1108 || (getyysstate() == xd) 1109 || (getyysstate() == xc) 1110 ) 1111 { 1112 addlit(yylvalstr, 1); 1113 yyless(1); 1114 return;//exit; 1115 } 1116 1117 returni(cmtdoublehyphen); 1118 break; 1119 } 1120 1121 case 30: 1122 1123 { 1124 if (yylvalstr.charAt(0) == '{') { 1125 // Look ahead for {{variable}} template substitution syntax 1126 char ch1 = get_char(); 1127 if (ch1 == '{') { 1128 StringBuilder varBuf = new StringBuilder(); 1129 boolean closed = false; 1130 while (true) { 1131 char ch = get_char(); 1132 if (ch == 0) break; 1133 if (ch == '}') { 1134 char ch2 = get_char(); 1135 if (ch2 == '}') { 1136 closed = true; 1137 break; 1138 } else { 1139 varBuf.append(ch); 1140 if (ch2 != 0) varBuf.append(ch2); 1141 else break; 1142 } 1143 } else { 1144 varBuf.append(ch); 1145 } 1146 } 1147 if (closed) { 1148 yylvalstr = "{{" + varBuf.toString() + "}}"; 1149 yytextlen = yylvalstr.length(); 1150 // Consume trailing identifier chars (e.g., {{env}}_suffix) 1151 StringBuilder trailBuf2 = new StringBuilder(); 1152 while (true) { 1153 char tc2 = get_char(); 1154 if (tc2 == 0) break; 1155 if (Character.isLetterOrDigit(tc2) || tc2 == '_') { 1156 trailBuf2.append(tc2); 1157 } else { 1158 unget_char(tc2); 1159 break; 1160 } 1161 } 1162 if (trailBuf2.length() > 0) { 1163 yylvalstr = yylvalstr + trailBuf2.toString(); 1164 yytextlen = yylvalstr.length(); 1165 } 1166 returni(ident); 1167 } else { 1168 // Unclosed — put back and return '{' 1169 String s = varBuf.toString(); 1170 for (int i = s.length() - 1; i >= 0; i--) { 1171 unget_char(s.charAt(i)); 1172 } 1173 unget_char(ch1); 1174 returnc('{'); 1175 } 1176 } else { 1177 if (ch1 != 0) unget_char(ch1); 1178 returnc('{'); 1179 } 1180 } else { 1181 returnc(yylvalstr.charAt(0)); 1182 } 1183 break; 1184 } 1185 1186 case 31: 1187 1188 { 1189 returni(cmpop); 1190 break; 1191 } 1192 1193 case 32: 1194 1195 { 1196 1197 if (getyysstate() == xc) 1198 { 1199 slashstar = yylvalstr.indexOf("*/"); 1200 if (slashstar >= 0) 1201 { 1202 start(init); 1203 addlit(yylvalstr,slashstar+2); 1204 yylvalstr = litbufdup(); 1205 yyless(slashstar+2); 1206 returni(cmtslashstar); 1207 } 1208 else 1209 { 1210 addlit(yylvalstr,1); 1211 yyless(1); 1212 } 1213 } 1214 else if (getyysstate() == xq) 1215 { 1216 addlit(yylvalstr,1); 1217 yyless(1); 1218 } 1219 else 1220 { 1221 nchars = yytextlen; 1222 slashstar = yylvalstr.indexOf("/*"); 1223 dashdash = yylvalstr.indexOf("--"); 1224 if ((slashstar > 0) && (dashdash > 0)) 1225 { 1226 //if both appear, take the first one 1227 if (slashstar > dashdash) 1228 {slashstar = dashdash;} 1229 } 1230 else 1231 { 1232 // if slashstar=0 then slashstar := dashdash; 1233 // add (getyysstate <> xc) to avoid something like this */--,here */ should be handled instead of -- 1234 if ((slashstar > 0) && (getyysstate() != xc)) { 1235 nchars = slashstar; 1236 } 1237 } 1238 1239 while ((nchars > 1) 1240 && ( (yylvalstr.charAt(nchars-1) == '+' ) 1241 || (yylvalstr.charAt(nchars-1) =='-')) 1242 && (getyysstate() != xc)) 1243 { 1244 for (ic = nchars - 1; ic>=1; ic--) 1245 { 1246 if (isopchar(yylvalstr.charAt(ic-1))) break; 1247 } 1248 if (ic >= 1) break; 1249 nchars--; 1250 } 1251 1252 if (nchars < yytextlen) 1253 { 1254 //Strip the unwanted chars from the token 1255 yyless(nchars); 1256 yylvalstr = yylvalstr.substring(0,nchars); 1257 } 1258 1259 ///* 1260 // * If what we have left is only one char, and it's 1261 // * one of the characters matching "self", then 1262 // * return it as a character token the same way 1263 // * that the "self" rule would have. 1264 // * make sure @ return as self char, by james wang 1265 // */ 1266 if ((nchars == 1) && (isselfchar(yylvalstr.charAt(0)) || (yylvalstr.charAt(0) == '@'))) 1267 { 1268 returnc(yylvalstr.charAt(0)); 1269 } 1270 else if ( 1271 (nchars >= 2) 1272 &&( 1273 charinarray(yylvalstr.charAt(nchars-1-1), tmparray) 1274 && ((yylvalstr.charAt(nchars-1) == ':') 1275 ||(yylvalstr.charAt(nchars-1) == '.') 1276 ) 1277 ) 1278 ) 1279 { 1280 yyless(nchars-1); 1281 yylvalstr = yylvalstr.substring(0,nchars-1); 1282 if (nchars == 2) 1283 returnc(yylvalstr.charAt(0)); 1284 else 1285 returni(op); 1286 } 1287 else if ( 1288 (nchars >= 2) 1289 && ( 1290 charinarray(yylvalstr.charAt(nchars-1-1),tmparray) 1291 && (yylvalstr.charAt(nchars-1) == '&') 1292 ) 1293 ) 1294 { 1295 yyless(nchars-1); 1296 yylvalstr = yylvalstr.substring(0,nchars-1); 1297 if (nchars == 2) 1298 returnc(yylvalstr.charAt(0)); 1299 else 1300 returni(op); 1301 } 1302 else if ( (nchars > 1) && (yylvalstr.charAt(0) == '~')) 1303 { 1304 yyless(1); 1305 returnc(yylvalstr.charAt(0)); 1306 } 1307 else if ( (nchars == 2) && (yylvalstr.charAt(0) == '.') && (yylvalstr.charAt(1) == '*')) 1308 { 1309 yyless(1); 1310 returnc(yylvalstr.charAt(0)); 1311 } 1312 else if ((nchars == 2) && ((yylvalstr.charAt(0) == '=') && ( (yylvalstr.charAt(1) == '?')||(yylvalstr.charAt(1) == '@')) )) 1313 { 1314 yyless(1); 1315 returnc(yylvalstr.charAt(0)); 1316 } 1317 else if ( (nchars >= 2) && ((yylvalstr.charAt(0) == '@')&&(yylvalstr.charAt(1) != '>'))) 1318 { 1319 yyless(1); 1320 returnc(yylvalstr.charAt(0)); 1321 } 1322 else if ( (nchars >= 2) && ((yylvalstr.charAt(0) == '/'))) 1323 { 1324 yyless(1); 1325 returnc(yylvalstr.charAt(0)); 1326 } 1327 else if (((nchars > 2) && (yylvalstr.charAt(0) == '*')) 1328 && (yylvalstr.charAt(1) == '/') 1329 && (getyysstate() == xc) 1330 ) 1331 { //in comment, and find */ , then it must the end of comment 1332 yyless(2); 1333 addlit(yylvalstr,yytextlen); 1334 if (xcdepth <= 0) 1335 { 1336 start(init); 1337 yylvalstr = litbufdup(); 1338 returni(cmtslashstar); 1339 } 1340 else 1341 xcdepth--; 1342 } 1343 else 1344 returni(op); 1345 } 1346 1347 break; 1348 } 1349 1350 case 33: 1351 1352 { 1353 returni(iconst); 1354 break; 1355 } 1356 1357 case 34: 1358 1359 { 1360 ///* for i in 1..5 loop, we can't recognize 1. as a decimal,but 1 as decimal 1361 nchars = yytextlen; 1362 if (yylvalstr.charAt(nchars-1) == '.') 1363 { 1364 dummych1 = get_char(); 1365 unget_char(dummych1); 1366 if (dummych1 == '.') 1367 { 1368 yyless(nchars-1); 1369 yylvalstr = yylvalstr.substring(0,nchars - 1); 1370 returni (iconst); 1371 return;//exit; 1372 } 1373 } 1374 returni (fconst); 1375 break; 1376 } 1377 1378 case 35: 1379 1380 { 1381 returni (fconst); 1382 break; 1383 } 1384 1385 case 36: 1386 1387 { 1388 returni (sconst); 1389 break; 1390 } 1391 1392 case 37: 1393 1394 { 1395 boolean dollarConstant = false; 1396 if (getyysstate() == xdolq){ 1397 int p = yylvalstr.indexOf("$"); 1398 if (p > 0){ 1399 dollarConstant = true; 1400 addlit(yylvalstr, p); 1401 yyless(p); 1402 return; 1403 } 1404 } 1405 1406 // Handle identifiers ending with $$ inside function body (e.g., "b$$") 1407 // When inside a function body, split identifier at $$ boundary 1408 // Only split if the identifier contains $$ (the function body delimiter) 1409 // Do NOT split identifiers like SYSTEM$TASK_RUNTIME_INFO that contain single $ 1410 if (functionBodyDelimiterIndex >= 0){ // inside function body 1411 int p = yylvalstr.indexOf("$$"); 1412 if (p > 0){ 1413 yylvalstr = yylvalstr.substring(0,p); 1414 yyless(p); 1415 } 1416 } 1417 1418 if (!dollarConstant){ 1419 // Look ahead for {{variable}} template to merge with identifier 1420 // e.g., FOCG{{env_suffix}} -> single ident "FOCG{{env_suffix}}" 1421 while (true) { 1422 char lk1 = get_char(); 1423 if (lk1 == '{') { 1424 char lk2 = get_char(); 1425 if (lk2 == '{') { 1426 StringBuilder vBuf = new StringBuilder(); 1427 boolean vClosed = false; 1428 while (true) { 1429 char vc = get_char(); 1430 if (vc == 0) break; 1431 if (vc == '}') { 1432 char vc2 = get_char(); 1433 if (vc2 == '}') { vClosed = true; break; } 1434 else { vBuf.append(vc); if (vc2 != 0) vBuf.append(vc2); else break; } 1435 } else { vBuf.append(vc); } 1436 } 1437 if (vClosed) { 1438 yylvalstr = yylvalstr + "{{" + vBuf.toString() + "}}"; 1439 yytextlen = yylvalstr.length(); 1440 // Continue loop to check for more {{}} or trailing identifier chars 1441 // Consume any trailing identifier chars after }} 1442 StringBuilder trailing = new StringBuilder(); 1443 while (true) { 1444 char tc = get_char(); 1445 if (tc == 0) break; 1446 if (Character.isLetterOrDigit(tc) || tc == '_') { 1447 trailing.append(tc); 1448 } else { 1449 unget_char(tc); 1450 break; 1451 } 1452 } 1453 if (trailing.length() > 0) { 1454 yylvalstr = yylvalstr + trailing.toString(); 1455 yytextlen = yylvalstr.length(); 1456 } 1457 continue; // check for another {{ 1458 } else { 1459 String s = vBuf.toString(); 1460 for (int si = s.length() - 1; si >= 0; si--) unget_char(s.charAt(si)); 1461 unget_char(lk2); 1462 unget_char(lk1); 1463 break; 1464 } 1465 } else { 1466 unget_char(lk2); 1467 unget_char(lk1); 1468 break; 1469 } 1470 } else { 1471 if (lk1 != 0) unget_char(lk1); 1472 break; 1473 } 1474 } 1475 1476 int rw; 1477 if ( (rw = iskeyword(yylvalstr)) != -1) { 1478 if (rw == TBaseType.rrw_as){ 1479 isReadyForFunctionBody = true; 1480 // check whether language is javascript, if yes, don't set isReadyForFunctionBody to true 1481 if ((TOKEN_TABLE[TBaseType.rrw_snowflake_language][COLUMN0_COUNT] > 0) 1482 &&(TOKEN_TABLE[TBaseType.rrw_snowflake_javascript][COLUMN0_COUNT] > 0)){ 1483 if (TOKEN_TABLE[TBaseType.rrw_snowflake_javascript][COLUMN5_FIRST_POS] - TOKEN_TABLE[TBaseType.rrw_snowflake_language][COLUMN5_FIRST_POS] <= 2){ 1484 // RETURNS STRING LANGUAGE JAVASCRIPT 1485 // 如果是 RETURNS STRING LANGUAGE JAVASCRIPT,那么不需要设置isReadyForFunctionBody = true,即把整个$$...$$当作字符串处理 1486 isReadyForFunctionBody = false; 1487 } 1488 } 1489 // Also check for non-SQL languages (JAVA, PYTHON, SCALA) detected earlier 1490 if (isNonSQLLanguageDetected) { 1491 isReadyForFunctionBody = false; 1492 } 1493 1494 }else if (rw == TBaseType.rrw_snowflake_language){ 1495 isLanguagePending = true; 1496 isReadyForFunctionBody = false; 1497 }else{ 1498 isReadyForFunctionBody = false; 1499 } 1500 returni(rw); 1501 } 1502 else 1503 { 1504 if (isLanguagePending) { 1505 String langName = yylvalstr.toLowerCase(Locale.ROOT); 1506 if (langName.equals("java") || langName.equals("python") || langName.equals("scala")) { 1507 isNonSQLLanguageDetected = true; 1508 } 1509 isLanguagePending = false; 1510 } 1511 isReadyForFunctionBody = false; 1512 returni(ident); 1513 } 1514 } 1515 1516 break; 1517 } 1518 1519 case 38: 1520 1521 { 1522 if (getyysstate() == xdolq){ 1523 addlit(yylvalstr, yytextlen); 1524 return; 1525 }else{ 1526 returni (param); 1527 } 1528 1529 break; 1530 } 1531 1532 case 39: 1533 1534 { 1535 returni (outer_join); 1536 break; 1537 } 1538 1539 case 40: 1540 1541 { 1542 returni (typecast); 1543 break; 1544 } 1545 1546 case 41: 1547 1548 { 1549 returni (double_dot); 1550 break; 1551 } 1552 1553 case 42: 1554 1555 { 1556 returni (assign_sign); 1557 break; 1558 } 1559 1560 case 43: 1561 1562 { 1563 returni (variable); 1564 break; 1565 } 1566 1567 case 44: 1568 1569 { 1570 returni (bind_v); 1571 break; 1572 } 1573 1574 1575 case 45: 1576 1577 { 1578 // Backtick-quoted identifier (SnowSQL compatibility) 1579 StringBuilder buf = new StringBuilder(); 1580 buf.append('`'); 1581 while (true) { 1582 char ch = get_char(); 1583 if (ch == 0) break; 1584 buf.append(ch); 1585 if (ch == '`') break; 1586 } 1587 yylvalstr = buf.toString(); 1588 yytextlen = yylvalstr.length(); 1589 returni(ident); 1590 break; 1591 } 1592 1593 case 46: 1594 1595 { 1596 // Look ahead for ${variable} template substitution syntax 1597 char ch1 = get_char(); 1598 if (ch1 == '{') { 1599 StringBuilder varBuf = new StringBuilder(); 1600 boolean closed = false; 1601 while (true) { 1602 char ch = get_char(); 1603 if (ch == 0) break; 1604 if (ch == '}') { closed = true; break; } 1605 varBuf.append(ch); 1606 } 1607 if (closed) { 1608 // Simple ${name} — return as IDENT for template variable substitution 1609 yylvalstr = "${" + varBuf.toString() + "}"; 1610 yytextlen = yylvalstr.length(); 1611 returni(ident); 1612 } else { 1613 // Unclosed — put back and return error 1614 String s = varBuf.toString(); 1615 for (int i = s.length() - 1; i >= 0; i--) { 1616 unget_char(s.charAt(i)); 1617 } 1618 unget_char('{'); 1619 returni(error); 1620 } 1621 } else { 1622 if (ch1 != 0) unget_char(ch1); 1623 returni(error); 1624 } 1625 break; 1626 } 1627 1628 case 47: 1629 1630 { 1631 returni (error); 1632 break; 1633 } 1634 1635 default:{ 1636 System.out.println("fatal error in yyaction"); 1637 } 1638 }//switch 1639}/*yyaction*/; 1640 1641 1642 1643 }