001package gudusoft.gsqlparser;
002
003
004import gudusoft.gsqlparser.nodes.TDummy;
005import gudusoft.gsqlparser.nodes.TParseTreeNode;
006import gudusoft.gsqlparser.nodes.TParseTreeNodeList;
007import gudusoft.gsqlparser.nodes.TNodeFactory;
008
009
010import java.util.ArrayList;
011import java.util.Map;
012
013
014/**
015 * Base parser of all databases.
016 */
017public class TCustomParser {
018
019    // Make the map static and initialize it directly
020//    static final Map<Integer, int[]> postgresRollbackTokens = new HashMap<Integer, int[]>() {{
021//        // TBaseType.ident must be the last element in each array
022//        put(TBaseType.rrw_postgresql_insert_values, new int[]{TBaseType.rrw_values,TBaseType.ident});
023//    }};
024
025
026    /**
027     * Count of entries into yyparse error handling (keyword rollback, error
028     * shift/discard, abort) since this parser instance was created. Read by
029     * {@link DynamicSqlProofHarness}: the dynamic-SQL publication proof must
030     * fail when a parse needed ANY recovery, including silently-successful
031     * keyword rollback. Never reset — use a fresh parser per proof.
032     */
033    int proofRecoveryEvents = 0;
034
035    /**
036     * Subset of {@link #proofRecoveryEvents}: entries that attempted the
037     * deterministic keyword-to-identifier rollback (e.g. {@code select a.wait
038     * from b}). Typed separately so a future audited-rewrite refinement can
039     * treat successful keyword rollback as a bounded rewrite instead of a
040     * disqualifying recovery — today the proof still refuses on it (sound,
041     * conservative; costs recall on the ~72 rollback-only keyword-column
042     * shapes, tracked in the design doc).
043     */
044    int proofKeywordRollbackEvents = 0;
045
046    /** @see #proofRecoveryEvents */
047    public int getProofRecoveryEvents() {
048        return proofRecoveryEvents;
049    }
050
051    /** @see #proofKeywordRollbackEvents */
052    public int getProofKeywordRollbackEvents() {
053        return proofKeywordRollbackEvents;
054    }
055
056    /* ------------------------------------------------------------------
057     * Proof-mode LR configuration observation (design R4 — the commitment
058     * check for dynamic-SQL edge provenance).
059     *
060     * A dynamic-SQL site is literal text with holes whose runtime value is
061     * unknown. To decide whether a construct is COMMITTED — i.e. no hole
062     * instantiation can still extend it and rebind a name — we need the LR
063     * automaton's own configuration at the point where the literal prefix
064     * ends, because the grammar, not a hand-written table of clause kinds,
065     * is what knows which productions are still open.
066     *
067     * This block only OBSERVES. It never changes an action, a reduction or
068     * a token. When {@link #proofLrCutoffOffset} is negative (the default)
069     * every field here is untouched and the cost is one int comparison per
070     * lookahead fetch.
071     * ------------------------------------------------------------------ */
072
073    /** Returned by {@link #do_yyparse} when it stopped at the proof cutoff. */
074    public static final int PROOF_LR_STOPPED_AT_CUTOFF = 10099;
075
076    /** One reduction the automaton performed, recorded before the stack is popped. */
077    public static final class ProofReduction {
078        /** yacc rule number (positive). */
079        public final int rule;
080        /** Number of stack entries the rule pops. */
081        public final int length;
082        /** LHS symbol the rule reduces to, as stored in the generated tables. */
083        public final int lhsSymbol;
084        /** Stack depth BEFORE the pop — the rule spans depths [depth-length+1, depth]. */
085        public final int stackDepthBeforePop;
086        /**
087         * Character offset of the lookahead token in hand when the reduction
088         * ran, or -1 when the reduction needed no lookahead (a default
089         * reduction). Only reductions taken with a STABLE lookahead — one that
090         * lies before the first hole — may be used as positive evidence.
091         */
092        public final long lookaheadOffset;
093
094        ProofReduction(int rule, int length, int lhsSymbol, int stackDepthBeforePop,
095                long lookaheadOffset) {
096            this.rule = rule;
097            this.length = length;
098            this.lhsSymbol = lhsSymbol;
099            this.stackDepthBeforePop = stackDepthBeforePop;
100            this.lookaheadOffset = lookaheadOffset;
101        }
102
103        @Override
104        public String toString() {
105            return "rule=" + rule + " len=" + length + " lhs=" + lhsSymbol
106                    + " depth=" + stackDepthBeforePop + " lookahead@" + lookaheadOffset;
107        }
108    }
109
110    /**
111     * The automaton's configuration at the proof cutoff: the state stack as it
112     * stood when the parser first needed a token at or beyond the cutoff, plus
113     * every reduction performed before that point.
114     *
115     * <p>The snapshot is taken while the cutoff token has been READ but not yet
116     * shifted, and before any action is looked up for it. So the stack is
117     * exactly "what the literal prefix committed to", with no influence from
118     * the unstable token — which is the whole point.
119     */
120    public static final class ProofLrConfiguration {
121        /** The state stack, bottom first — a copy of {@code yys[1..yysp]}. */
122        public final int[] stateStack;
123        /** Character offset of the token the parser stopped before. */
124        public final long stoppedBeforeOffset;
125        /** Token code of that token. */
126        public final int stoppedBeforeTokenCode;
127        /** Reductions performed before the cutoff, in order. */
128        public final java.util.List<ProofReduction> reductions;
129
130        ProofLrConfiguration(int[] stateStack, long stoppedBeforeOffset,
131                int stoppedBeforeTokenCode, java.util.List<ProofReduction> reductions) {
132            this.stateStack = stateStack;
133            this.stoppedBeforeOffset = stoppedBeforeOffset;
134            this.stoppedBeforeTokenCode = stoppedBeforeTokenCode;
135            this.reductions = java.util.Collections.unmodifiableList(reductions);
136        }
137
138        /** Depth of the state stack. */
139        public int depth() {
140            return stateStack.length;
141        }
142
143        /** The state on top of the stack — the automaton's current state. */
144        public int topState() {
145            return stateStack.length == 0 ? 0 : stateStack[stateStack.length - 1];
146        }
147
148        @Override
149        public String toString() {
150            return "ProofLrConfiguration{depth=" + depth() + " top=" + topState()
151                    + " stoppedBefore@" + stoppedBeforeOffset
152                    + " token=" + stoppedBeforeTokenCode
153                    + " reductions=" + reductions.size() + "}";
154        }
155    }
156
157    /**
158     * Stop the parse before consuming any token at or beyond this character
159     * offset, and record the configuration. Negative (default) disables all
160     * observation.
161     */
162    long proofLrCutoffOffset = -1;
163
164    /** Populated only when the cutoff was actually reached; null otherwise. */
165    ProofLrConfiguration proofLrConfiguration;
166
167    /** Reductions seen so far this parse; allocated only when observing. */
168    private java.util.List<ProofReduction> proofLrReductions;
169
170    /**
171     * Arm the observer. Must be called before {@code yyparse()}. Observation is
172     * read-only: the parse proceeds exactly as it otherwise would, up to the
173     * cutoff, at which point it stops WITHOUT entering error recovery (recovery
174     * unwinds the very stack we are trying to read).
175     */
176    public void setProofLrCutoffOffset(long offset) {
177        this.proofLrCutoffOffset = offset;
178        this.proofLrConfiguration = null;
179        this.proofLrReductions = offset < 0 ? null : new java.util.ArrayList<ProofReduction>();
180    }
181
182    /** The configuration captured at the cutoff, or null if it was never reached. */
183    public ProofLrConfiguration getProofLrConfiguration() {
184        return proofLrConfiguration;
185    }
186
187    EDbVendor dbvendor;
188    TDatabaseYYSType yylval;
189    TDatabaseYYSType[] yyv; // 1 based array
190    int[] yys; // parser state stack, reused across parse calls for performance
191    TDatabaseYYSType yyval = null;
192
193    // the terminal or non-terminal numbers in the right side of a BNF rule, start from 1.
194    // Take this rule for example:  
195    //      sqlstmts:  stmt ';'
196    // stmt is non-terminal, its number is 1, ';' is terminal, its number is 2.
197    // when sqlstmts rule is reduced, the yysp is 2 which means it points to the ';' token.
198    // and if we need to access the stmt node, we can use yyv[yysp-1] to get it.
199    int yysp; 
200    
201    boolean isbeginofbatch; //used by mssql
202    TSourceTokenList tmp_sourcetokenlist;
203    int tmp_curtokenpos;
204
205    public TSourceTokenList sourcetokenlist;
206    public TNodeFactory nf;
207    public TParseTreeNode rootNode;
208    final int yymaxdepth = 1048;
209    final int _error = 256;
210    final int yyfnone = 0;
211    final int  yyfaccept = 1;
212    final int yyfabort = 2;
213    final int  yyferror = 3;
214    final int aopAbort = 0;
215    final int aopContinue = 1;
216    final int tsOriginal = 0;
217    final int tsDeleted = 1;
218    final int tsIgnoreByYacc = 2;
219    final int tsMarkDeletedInPPDoWhiteSpace = 3;
220    final int tsAddedInPP = 4;
221    final int tsDeletedInPP = 5;
222    final int tsAlreadlyAddedToList = 6;
223    final int tsSynataxError = 7;
224    final int tsSynataxErrorProcessed = 8;
225    final int tsAddbyHand = 9;
226    final int tsAddedInTokensInScript = 10;
227    //final int tsNotStartTokenOfSQL,
228    final int tsIgnoredByGetRawStatement = 11;
229    final int tsDummyStatus = 12;
230
231    // Vendor-specific SQL command resolver (injected by TGSqlParser)
232    public gudusoft.gsqlparser.sqlcmds.ISqlCmds sqlcmds;
233    public TCustomLexer lexer;
234
235    TParseTreeNode tmpnode,tmpnode1,tmpnode2,tmpnode3,tmpnode4;
236    
237    int yyflag;//    yyfnone,yyfaccept,yyfabort   ,yyferror
238    int nextstmtstartpos , curtokenpos,stmtendpos;
239    TSourceToken acceptedtoken,currentsourcetoken,recovertoken,errorstmtstarttoken;
240    int currentyystate,currentyysp,currentyyn;
241    int[] retvalue = {0};   // used when need to pass VAR variable to a function, return value by this variable
242    public TCustomSqlStatement sql;
243
244    public TNodeFactory getNf() {
245        return nf;
246    }
247
248    private TCustomParser(){
249        curtokenpos = 0;
250        yylval = new TDatabaseYYSType();
251        yyv =    new TDatabaseYYSType[yymaxdepth + 1]; //1 based
252        yys =    new int[yymaxdepth + 1]; // 1 based, reused across parse calls
253        yyval = null;
254        // sqlcmds will be injected by TGSqlParser based on vendor
255    }
256
257    TCustomParser(EDbVendor pDbVendor){
258        this();
259        this.dbvendor = pDbVendor;
260        nf = new TNodeFactory(this.dbvendor);
261    }
262
263    TParseTreeNodeList yacclcons(Object obj, TParseTreeNodeList parseTreeNodeList,boolean IsParse){
264        return null;
265    }
266
267    TParseTreeNodeList yaccmakeList1(Object x1,boolean IsParse){
268      return null;
269    }
270    
271    TParseTreeNodeList yaccmakeList2(Object x1,Object x2,boolean IsParse){
272      return null;
273    }
274    
275    TParseTreeNodeList yaccnconc(TParseTreeNodeList l1, TParseTreeNodeList l2){
276        return null;
277    }
278    TParseTreeNodeList yacclappend(TParseTreeNodeList parseTreeNodeList,Object obj,boolean IsParse){
279        return null;
280    }
281    int yylexwrap(boolean isignore){
282        int ret = 0;
283        if (sourcetokenlist == null)  return ret;
284        TSourceToken ast = getasourcetoken(isignore);
285        if (ast == null ) return ret;
286        if (ast.tokencode == 0) return ret;
287        yylval.yyTSourceToken = ast;
288
289        if ((yylval.yyTSourceToken.tokencode == TBaseType.bind_v)
290        && (dbvendor == EDbVendor.dbvmysql))
291        {
292            yylval.yyTSourceToken.tokencode = TBaseType.ident;
293            //yylval.yyTSourceToken.nodetype := T_BindV;
294        }
295        if (dbvendor == EDbVendor.dbvoracle){
296            yylval.yyTSourceToken.tag = 0;
297        }
298        //System.out.println(yylval.yyTSourceToken.toString());
299        return yylval.yyTSourceToken.tokencode;
300    }
301
302    TSourceToken read_to_next_parentheses(boolean isIncluding){
303        return read_to_next_parentheses(isIncluding, new TDummy());
304    }
305
306    TSourceToken read_to_next_parentheses(boolean isIncluding, TParseTreeNode ownerNode){
307        int nested = 0;
308        int yychar = -1;
309
310        TSourceToken ret = null,prevSt = null;
311
312
313        while (true){
314            yychar = yylexwrap(false);//yyLexer.yylexwrap;
315            if (yychar<0) {yychar = 0;}
316            if (yychar == 0) { return ret;}
317
318            if (yylval.yyTSourceToken.tokentype == ETokenType.ttleftparenthesis)
319            {nested++;}
320
321            if (yylval.yyTSourceToken.tokentype == ETokenType.ttrightparenthesis)
322            {nested--;}
323
324            if (prevSt == null){
325                ownerNode.setStartToken(yylval.yyTSourceToken);
326            }
327
328            if (nested < 0)
329            {
330                //curtokenpos--; //rollback ')'
331                if (isIncluding) {
332                    stmtendpos = curtokenpos;// - 1;
333                    ret = yylval.yyTSourceToken;
334                    ownerNode.setEndToken(ret);
335                }else{
336                    curtokenpos--;
337                    stmtendpos = curtokenpos;// - 1;
338                    ret = prevSt;
339                    ownerNode.setEndToken(ret);
340                }
341                break; // end of this node
342            }
343
344            ret = yylval.yyTSourceToken;
345
346            if (yylval.yyTSourceToken.tokentype == ETokenType.ttsemicolon)
347            {
348                ownerNode.setEndToken(ret);
349                break;
350            }
351
352            prevSt =  yylval.yyTSourceToken;
353        } // while
354
355        return ret;
356
357    }
358
359    TSourceToken read_before_subquery(){
360        int yychar = -1;
361        TSourceToken ret = null;
362
363        while (true){
364            yychar = yylexwrap(false);//yyLexer.yylexwrap;
365            if (yychar<0) {yychar = 0;}
366            if (yychar == 0) { return ret;}
367
368            if (yylval.yyTSourceToken.tokencode == TBaseType.rrw_as)
369            {
370                TSourceToken next = yylval.yyTSourceToken.nextSolidToken();
371                if (next != null){
372                    if ((next.tokencode == TBaseType.rrw_select)||(next.tokencode == TBaseType.rrw_with)){
373                        curtokenpos--;
374                        break;
375                    }
376                }
377            }
378
379            ret = yylval.yyTSourceToken;
380            if (yylval.yyTSourceToken.tokentype == ETokenType.ttsemicolon)
381            {
382                break;
383            }
384        } // while
385
386        return ret;
387
388    }
389
390    TokenAndText read_consume_valid_filename_token(boolean removeTokenBeforeWhitespace) {
391        int yychar = -1;
392        String ret = "";
393        TSourceToken lastToken = null;
394
395        while (true) {
396            yychar = yylexwrap(false); // yyLexer.yylexwrap;
397            if (yychar < 0) {
398                yychar = 0;
399            }
400            if (yychar == 0) {
401                return new TokenAndText(lastToken, ret);
402            }
403
404            if ((yylval.yyTSourceToken.tokentype == ETokenType.ttwhitespace)
405                    || (yylval.yyTSourceToken.tokentype == ETokenType.ttreturn)
406                    || (yylval.yyTSourceToken.tokentype == ETokenType.ttsemicolon)
407                    || (yylval.yyTSourceToken.tokencode == ')')
408                    || (yylval.yyTSourceToken.tokencode == '(')
409            ) {
410                curtokenpos--; // rollback to ensure the token is not consumed
411                break;
412            }
413
414
415            ret = ret + yylval.yyTSourceToken.toString();
416            lastToken = yylval.yyTSourceToken;
417            if (removeTokenBeforeWhitespace) {
418                yylval.yyTSourceToken.tokenstatus = ETokenStatus.tsdeleted;
419            }
420        }
421        return new TokenAndText(lastToken, ret);
422    }
423
424    TSourceToken read_before_this_token(int[] pTokenCodes){
425        int yychar = -1;
426        TSourceToken ret = null;
427        boolean found = false;
428
429        while (true){
430            yychar = yylexwrap(false);//yyLexer.yylexwrap;
431            if (yychar<0) {yychar = 0;}
432            if (yychar == 0) { return ret;}
433
434            for(int k=0;k<pTokenCodes.length;k++){
435                if (yylval.yyTSourceToken.tokencode == pTokenCodes[k])
436                {
437                    found = true;
438                    curtokenpos--;
439                    break;
440                }
441            }
442            if (found) break;
443            ret = yylval.yyTSourceToken;
444            if (yylval.yyTSourceToken.tokentype == ETokenType.ttsemicolon)
445            {
446                break;
447            }
448        } // while
449
450        return ret;
451    }
452
453    TSourceToken read_before_this_token(int pTokenCode){
454        return read_before_this_token(new int[] {pTokenCode});
455    }
456
457    TSourceToken read_to_semicolon(){
458        return  read_to_semicolon(new TDummy(),true);
459    }
460
461    TSourceToken read_to_semicolon(boolean includeSemicolon){
462        return  read_to_semicolon(new TDummy(),includeSemicolon);
463    }
464
465    TSourceToken read_to_semicolon(TParseTreeNode ownerNode){
466        return  read_to_semicolon(ownerNode,true);
467    }
468
469    TSourceToken read_to_semicolon(TParseTreeNode ownerNode,boolean includeSemicolon){
470
471        int yychar = -1;
472
473        TSourceToken ret = null;
474
475        while (true){
476            yychar = yylexwrap(false);//yyLexer.yylexwrap;
477            if (yychar<0) {yychar = 0;}
478            if (yychar == 0) { return ret;}
479
480            if ((ret == null)&&(ownerNode.getStartToken() == null)){
481                ownerNode.setStartToken(yylval.yyTSourceToken);
482            }
483            ret = yylval.yyTSourceToken;
484
485            if (yylval.yyTSourceToken.tokentype == ETokenType.ttsemicolon)
486            {
487                break;
488            }
489        } // while
490        if (ret.tokentype == ETokenType.ttsemicolon){
491            ownerNode.setEndToken(ret.getPrevTokenInChain());
492            if (!includeSemicolon){
493                curtokenpos -- ;
494            }
495        }else{
496            ownerNode.setEndToken(ret);
497        }
498
499        return ret;
500
501    }
502
503    TSourceToken read_a_token( ){
504
505        int yychar = -1;
506
507        TSourceToken ret = null;
508        yychar = yylexwrap(false);
509        ret = yylval.yyTSourceToken;
510
511        return ret;
512
513    }
514
515
516    TSourceToken read_to_this_token( int tokenCode){
517
518        int yychar = -1;
519
520        TSourceToken ret = null;
521
522        while (true){
523            yychar = yylexwrap(false);//yyLexer.yylexwrap;
524            if (yychar<0) {yychar = 0;}
525            if (yychar == 0) { return ret;}
526
527            ret = yylval.yyTSourceToken;
528
529            if (yylval.yyTSourceToken.tokencode == tokenCode)
530            {
531                break;
532            }
533        } // while
534
535        return ret;
536
537    }
538
539    /**
540     * Read tokens until we encounter double closing braces }}
541     * Used for MLE JavaScript code blocks: AS MLE LANGUAGE JAVASCRIPT {{ ... }}
542     */
543    TSourceToken read_to_double_close_brace(){
544        int yychar = -1;
545        TSourceToken ret = null;
546        TSourceToken prevToken = null;
547
548        while (true){
549            yychar = yylexwrap(false);
550            if (yychar < 0) {yychar = 0;}
551            if (yychar == 0) { return ret;}
552
553            ret = yylval.yyTSourceToken;
554
555            // Check if we have }} (two consecutive closing braces)
556            if (prevToken != null &&
557                prevToken.tokencode == '}' &&
558                ret.tokencode == '}') {
559                break;
560            }
561
562            prevToken = ret;
563        }
564
565        return ret;
566    }
567
568    TSourceToken getasourcetoken(boolean isignore){
569      TSourceToken newst = null;
570      int j;
571      if (sourcetokenlist == null) return null;
572
573        if (curtokenpos > sourcetokenlist.size() - 1) return null;
574        for (j = curtokenpos; j < sourcetokenlist.size();j++)
575        {
576            newst = sourcetokenlist.get(j);
577            if ((newst.tokencode == TBaseType.lexspace) && isignore)   continue;
578            if ((newst.tokencode == TBaseType.lexnewline) && isignore)  continue;
579            if ((newst.tokencode == TBaseType.cmtdoublehyphen) && isignore)  continue;
580            if ((newst.tokencode == TBaseType.cmtslashstar) && isignore)  continue;
581            if ((newst.tokenstatus == ETokenStatus.tsignorebyyacc))  continue;
582
583            if (newst.tokenstatus != ETokenStatus.tsdeleted)  break;
584        }
585        if (j == sourcetokenlist.size()) return null;
586        else
587          curtokenpos = curtokenpos + (j-curtokenpos+1);
588
589        // System.out.println("curtokenpos:"+curtokenpos+" "+newst);
590
591//        pstr := '';
592//        asqlstatement := TCustomSqlStatement(sqlstatement);
593//
594//        //check bind variable
595//        // if (newst.TokenType = ttBindVar) and (dbvendor <> dbvmssql) and (newst.TokenStatus <> tsDeleted) then
596//        if (newst.TokenType = ttBindVar) and  (newst.TokenStatus <> tsDeleted) then
597//          begin
598//            if assigned(onBindVar) then
599//              begin
600//                onBindVar(self,copy(newst.SourceCode,2,length(newst.SourceCode)-1),pstr);
601//              end;
602//            // newst.TokenType := ttIdentifier;
603//            if assigned(asqlstatement) then
604//              begin
605//                lcValue := TLzValue.create(asqlstatement);
606//                lcValue.ValueName := copy(newst.SourceCode,2,length(newst.SourceCode)-1);
607//                lcValue.ValueStr := pstr;
608//                lcValue.SourceToken := newst;
609//                asqlstatement.params.add(lcValue);
610//              end;
611//            if length(pstr) <> 0 then
612//              newst.SourceCode := pstr;
613//          end;
614
615//        //check  variable
616//        // if (newst.TokenType = ttSqlVar) and (dbvendor <> dbvmssql) and (newst.TokenStatus <> tsDeleted) then
617//        if (newst.TokenType = ttSqlVar)  and (newst.TokenStatus <> tsDeleted) then
618//          begin
619//            if assigned(onSqlVar) then
620//              begin
621//                onSqlVar(self,copy(newst.SourceCode,2,length(newst.SourceCode)-1),pstr);
622//              end;
623//            if dbvendor <> dbvmssql then
624//              newst.TokenType := ttIdentifier;
625//            if assigned(asqlstatement) then
626//              begin
627//                lcValue := TLzValue.create(asqlstatement);
628//                lcValue.ValueName := copy(newst.SourceCode,2,length(newst.SourceCode)-1);
629//                lcValue.ValueStr := pstr;
630//                lcValue.SourceToken := newst;
631//                asqlstatement.sqlvars.add(lcValue);
632//              end;
633//            if length(pstr) <> 0 then
634//              newst.SourceCode := pstr;
635//          end;
636//
637//         pstr := '';
638//
639//        try
640//          if assigned(OnParserToken)
641//            and (newst.CodeSource = csOriginal)
642//            and (newst.TokenStatus <> tsDeleted)
643//            and (newst.TokenType <> ttBindVar)
644//          then OnParserToken(self,newst,Integer(DbVendor));
645//        except
646//         // paot := aotNone;
647//        end;
648
649//        if (newst.tokenstatus = tsDeleted) then
650//          begin
651//            if assigned(RecoverToken) then
652//              begin
653//                newst := RecoverToken;
654//                RecoverToken := nil;
655//              end
656//            else
657//              newst := GetASourceToken(IsIgnore);
658//          end;
659
660        return newst;
661    }
662
663    int dobefore_yyparse(){
664        int ret = 10000;
665        for (int i=0; i< sourcetokenlist.size();i++)
666        {      // if there are no solid tokentext in list, return -1, don't parse anymore
667            if( (sourcetokenlist.get(i).tokentype == ETokenType.ttwhitespace)
668            || (sourcetokenlist.get(i).tokentype == ETokenType.ttreturn)
669            || (sourcetokenlist.get(i).tokencode == TBaseType.cmtdoublehyphen)
670            || (sourcetokenlist.get(i).tokencode == TBaseType.cmtslashstar) )
671            {continue;}
672            else
673            {
674                ret = 0;
675                break;
676            }
677        }
678
679        return ret;
680
681    }
682
683    //spFatalError yyparse stack overflow error 1010 ,CurrentSourceToken.lines,CurrentSourceToken.columns
684    void onparseerrorhandle(EErrorType errortype,String pmsg,String token,long xposition,long yposition, int errorno){
685        if (sql != null){
686           sql.parseerrormessagehandle( new TSyntaxError(token,xposition,yposition,pmsg,errortype,errorno,null,-1));
687        }else{
688            System.out.println(pmsg);
689        }
690    }
691
692    boolean yyact(int state,int sym,int[] act){
693     return false;
694    }
695
696    boolean yygoto(int state, int sym,int[] nstate){
697       return false;
698    }
699
700    /**
701     * according to the input state and sym, get the new state from the goto table
702     * return false if no state found (this will cause a parse error), otherwise return true, and the new state is stored in nstate[0]
703     *
704     * if method is called when a reduce action is performed.
705     *
706     * @param state
707     * @param sym
708     * @param nstate
709     * @param p_yygl
710     * @param p_yygh
711     * @param p_yyg_sym
712     * @param p_yyg_act
713     * @return
714     */
715    boolean yygoto(int state, int sym,int[] nstate, int[] p_yygl, int[] p_yygh, int[] p_yyg_sym,  int[] p_yyg_act){
716        boolean r;
717        int k = p_yygl[state];
718        while ((k<=p_yygh[state]) && (p_yyg_sym[k] != sym)) {k++;}
719        if (k>p_yygh[state])
720            r = false;
721        else
722        {
723            nstate[0] = p_yyg_act[k];
724            r = true;
725        }
726        return r;
727    }
728
729    /**
730     * according to the input state and sym, get the action from the action table
731     * return false if no action found (this will cause a parse error), otherwise return true, and the action is stored in act[0]
732     * if act[0] > 0, it means shift, the value is the new state, can be used in yyact() method
733     * if act[0] < 0, it means reduce, the value is the rule number, used in yyaction(-value) method
734     * if act[0] = 0, it means parse completed successfully.
735     *
736     * @param state
737     * @param sym
738     * @param act
739     * @param p_yyal
740     * @param p_yyah
741     * @param p_yya_sym
742     * @param p_yya_act
743     * @return
744     */
745    boolean yyact(int state,int sym,int[] act, int[] p_yyal, int[] p_yyah, int[] p_yya_sym, int[] p_yya_act){
746        boolean r;
747        int k = p_yyal[state];
748        while ((k <= p_yyah[state]) && (p_yya_sym[k] != sym) ) {k++;}
749        if (k>p_yyah[state])
750            r = false;
751        else {
752            act[0]  = p_yya_act[k];
753            r = true;
754        }
755        return r;
756    }
757
758    public int do_yyparse(int [] p_yyd,
759                          int [] p_yyal, int [] p_yyah, int [] p_yya_sym, int[] p_yya_act,
760                          int [] p_yygl, int [] p_yygh, int [] p_yyg_sym, int [] p_yyg_act,
761                          int [] p_yyr_len,int [] p_yyr_sym,
762                          Map<Integer, int[]> p_rollbackTokens
763                          ){
764        int lcprevyysp,lcyystate,lcyyn,yyn=0;
765        // yys array is now an instance field, reused across parse calls for performance
766        TDatabaseYYSType rollback_token = null;
767        int lcAction;
768        boolean lcrollbackstate ;
769        int lcrollbacktokens;
770
771        curtokenpos = 0;
772        int lcRetries = dobefore_yyparse();//super.yyparse();
773        if (lcRetries == -1)
774        { // no solid tokentext in tokentext list
775            return 0;
776        }
777
778        int rollback_mode = 0;
779        int yystate = 0, yychar = -1, yynerrs = 0, yyerrflag = 0;
780        yysp = 0;
781        boolean lcIsError = false;
782        boolean lcIsParse = false;
783        boolean lcIsReduce= false;
784        boolean lcIsShift = false;
785
786        int rollback_state = yystate;
787        int rollback_sp = yysp;
788
789        boolean isBeginOfBatch = true; //used in mssql
790        boolean lccanreduce; // used in mssql
791
792        isbeginofbatch = true;
793
794        while (true){ // the main loop for parsing
795
796            // this is the point where parse started:
797
798            yysp++;
799            if (yysp > yymaxdepth)
800            {
801                // yyerror('yyparse stack overflow');
802                onparseerrorhandle(EErrorType.spfatalerror, "yyparse stack overflow error 1010" , currentsourcetoken.getAstext(),currentsourcetoken.lineNo,currentsourcetoken.columnNo,10010);
803                //goto abort;
804//                if (dbvendor == EDbVendor.dbvmssql){
805//                    onparseerrorhandle(EErrorType.spfatalabort,"abort !!! error 1001",currentsourcetoken.astext ,currentsourcetoken.lineNo,currentsourcetoken.columnNo,10011);
806//                }
807
808                return 10001;
809            }
810
811            yys[yysp] = yystate;
812            yyv[yysp] = yyval;
813
814            while (true){
815                // this is the point where NEXT is started:
816                // get next symbol
817
818                if( (p_yyd[yystate] == 0) && (yychar == -1))
819                //(* get next symbol *)
820                {
821                    do{
822                        yychar = yylexwrap(true);//yyLexer.yylexwrap;
823                        if (yychar<0) { yychar = 0;}
824                        // ignore comments and blanks [ \n\t]
825                        //if not( (yychar=_COMMENT) or (yychar=_BLANK) or
826                        //        (yychar=_TAB) or (yychar=_NEWLINE) ) then break;
827                        if (!( (yychar== TBaseType.cmtdoublehyphen)|| (yychar== TBaseType.cmtslashstar) ||
828                                (yychar== TBaseType.lexspace) || (yychar== TBaseType.lexnewline) ||  (yychar== TBaseType.sqlpluscmd) ))
829                        {
830                            if (yychar == 0)
831                            {
832                                //System.out.println("error tokentext:"+yylval.yyTSourceToken+"c "+currentsourcetoken);
833                                if ( ( sourcetokenlist.get(sourcetokenlist.size() - 1)).tokencode != 0)
834                                {
835                                    //System.out.println("yychar is 0, but tokencode is:"+ ( sourcetokenlist.get(sourcetokenlist.size() - 1)).tokencode);
836                                    yylval.yyTSourceToken =  new TSourceToken(" ");
837                                    //                                      NewSourceToken(yylval.yyTSourceToken,'',ttUnknown,wtNotAWord,eNotAComment,-1,-1);
838                                    yylval.yyTSourceToken.container = sourcetokenlist;
839                                    yylval.yyTSourceToken.tokencode = 0;
840                                    //sourcetokenlist.add(yylval.yyTSourceToken);
841                                    yylval.yyTSourceToken.posinlist = sourcetokenlist.size() - 1;
842                                    curtokenpos = sourcetokenlist.size();
843                                    //showmessage('end of input');
844                                }
845                                else
846                                {
847                                    yylval.yyTSourceToken =  sourcetokenlist.get(sourcetokenlist.size() - 1);
848                                    // showmessage('end of input again');
849                                }
850                            }
851
852                            currentsourcetoken = yylval.yyTSourceToken;
853                            break;
854                        }
855                        else
856                        {
857                            //ignore non solid tokentext
858                        }
859
860                    } while (true);
861
862                    // Proof-mode cutoff (design R4 commitment check). The token
863                    // has been READ but not shifted, and no action has been
864                    // looked up for it yet, so the stack here is exactly what
865                    // the literal prefix committed to — uncontaminated by the
866                    // unstable token. Stop by RETURNING rather than by falling
867                    // into error handling: MSSQL recovery pops the stack looking
868                    // for a state that shifts `error`, destroying the snapshot.
869                    if (proofLrCutoffOffset >= 0 && proofLrConfiguration == null
870                            && currentsourcetoken != null
871                            && currentsourcetoken.offset >= proofLrCutoffOffset)
872                    {
873                        int[] stackCopy = new int[yysp];
874                        for (int i = 1; i <= yysp; i++)
875                        {
876                            stackCopy[i - 1] = yys[i];
877                        }
878                        proofLrConfiguration = new ProofLrConfiguration(stackCopy,
879                                currentsourcetoken.offset, yychar, proofLrReductions);
880                        return PROOF_LR_STOPPED_AT_CUTOFF;
881                    }
882                } //(* get next symbol *)
883
884                if  (!lcIsError)
885                {
886                    yyn = p_yyd[yystate];
887                    if (yyn != 0)
888                    {
889                        //goto reduce; (* simple state *)
890                        lcIsReduce = true;
891                        break;
892                    }
893
894                    // (* no default action; search parse table *)
895                    boolean foundAction = yyact(yystate, yychar, retvalue,p_yyal,p_yyah,p_yya_sym,p_yya_act);
896                    if(foundAction) {yyn = retvalue[0];}
897                    if (! foundAction)
898                    { // no action found, here we do error handling
899                        // Proof-mode observability (DynamicSqlProofHarness): every
900                        // entry into error handling — keyword rollback included —
901                        // is a recovery event. A parse that needed ANY recovery is
902                        // not evidence for the dynamic-SQL publication proof, even
903                        // when the rollback silently succeeds.
904                        proofRecoveryEvents++;
905                        // lcRestartPos :=   curtokenpos;
906
907                        lcAction = aopAbort;
908                        lcrollbackstate = false;
909                        lcrollbacktokens = 0;
910
911                        //if parse error occurs due to a keyword, then change this keyword to identifier
912                        // and retry again,
913                        // for example
914
915                        // select a.wait from b
916                        // wait is a keyword, can not be field name due to .y file,
917                        // but it's a legal in mssql, use this procedure while during parsing,
918                        // we can fix such problem which is very common
919                        // we don't recover from FROM keyword
920
921                        if ((yylval.yyTSourceToken.tokencode >= TBaseType.rrw_select )
922                                && (yylval.yyTSourceToken.tokencode != TBaseType.rrw_from )
923                                && (dbvendor != EDbVendor.dbvmssql?true:(yylval.yyTSourceToken.tokencode != TBaseType.rrw_where ))
924                        )
925                        {
926                            proofKeywordRollbackEvents++;
927                            // 如果一个token有多个侯备的其他token可以尝试,我们逐一尝试,如果没有成功的,将token类型改为ident进行最后尝试,还不成功,则报错。
928
929                            int[] rollbackTokens = new int[1];
930                            rollbackTokens[0] = TBaseType.ident;
931                            if ((p_rollbackTokens!=null) && (p_rollbackTokens.containsKey(yylval.yyTSourceToken.tokencode))) {
932                                rollbackTokens = p_rollbackTokens.get(yylval.yyTSourceToken.tokencode);
933                            }
934
935                            for(int i=0;i<rollbackTokens.length;i++)
936                            {
937                                yylval.yyTSourceToken.tokencode = rollbackTokens[i];
938                                if (yyact(yystate, yylval.yyTSourceToken.tokencode, retvalue, p_yyal,p_yyah,p_yya_sym,p_yya_act))
939                                {
940                                    yyn = retvalue[0];
941                                    if (yyn>0)
942                                    {
943                                        //goto shift;
944                                        lcIsShift = true;
945                                        break;
946                                    }
947                                    else if( yyn<0 )
948                                    {
949                                        // goto reduce
950                                        lcIsReduce = true;
951                                        break;
952                                    }
953                                    else
954                                    {
955                                        // goto accept;
956                                        return 0;
957                                    }
958                                }
959                            }
960
961                            if (lcIsShift||lcIsReduce)
962                            {
963                                if (yylval.yyTSourceToken.tokencode == TBaseType.ident){
964                                    yylval.yyTSourceToken.tokentype = ETokenType.ttidentifier;
965                                }
966                                yychar = yylval.yyTSourceToken.tokencode;
967                                break;
968                            }
969                        }
970
971                        // end of error recover
972
973                        if (lcAction == aopAbort )
974                        {
975                            // goto error;
976                            lcIsError = true;
977                        }
978                    }
979                    else if (yyn>0)
980                    {
981                        //goto shift;
982                        lcIsShift = true;
983                        break;
984                    }
985                    else if( yyn<0 )
986                    {
987                        // goto reduce
988                        lcIsReduce = true;
989                        break;
990                    }
991                    else
992                    {
993                        // goto accept;
994                        return 0;
995                    }
996
997                } // if not lcIsError
998
999                if (lcIsError)
1000                {
1001                    lcIsError = false;
1002
1003                    if ((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase)||(dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq)){
1004                        if (yyerrflag<=2)                //  (* incomplete recovery; try again *)
1005                        {
1006                            yyerrflag = 3;
1007                            //(* uncover a state with shift action on error tokentext *)
1008                            //(* error; start error recovery: *)
1009
1010
1011                            lcprevyysp = yysp;
1012                            while ((yysp>0)
1013                                    &&  !( yyact(yys[yysp], _error, retvalue,p_yyal,p_yyah,p_yya_sym,p_yya_act)
1014                                    &&( (yyn = retvalue[0]) >0 )
1015                            )
1016                            )
1017                            {
1018                                yysp--;
1019                            }
1020
1021                            if (lcprevyysp != yysp) // if error stmt is just the next state need, then it's a fake error, don't report
1022                                geterrormsg(yystate, yychar,10101);
1023
1024                            if (yysp==0)
1025                            {
1026                                //assert(1=2,'Should be an error handle rule in y file, never goes to here!!!');
1027                                onparseerrorhandle(EErrorType.spfatalerror,"error recover failed error", currentsourcetoken.getAstext(),currentsourcetoken.lineNo,currentsourcetoken.columnNo,10012);
1028                                //goto abort; (* parser has fallen from stack; abort *)
1029
1030                                onparseerrorhandle(EErrorType.spfatalabort,"abort !!! error 1001", currentsourcetoken.getAstext(),currentsourcetoken.lineNo,currentsourcetoken.columnNo,10013);
1031
1032                                return 10003;
1033                            }
1034
1035
1036                            errorstmtstarttoken = null;
1037
1038
1039                            yystate = yyn; //           (* simulate shift on error *)
1040                            yychar = -1;
1041                            stmtendpos = curtokenpos-1;
1042
1043                            //goto parse;
1044                            lcIsParse = true;
1045                            break;
1046                        }
1047                        else   //(yyerrflag<=2)                               (* no shift yet; discard symbol *)
1048                        {
1049                            if (yychar==0)
1050                            {
1051
1052                                // goto abort; (* end of input; abort *)
1053                                onparseerrorhandle(EErrorType.spfatalabort, "abort !!! error 1001", currentsourcetoken.getAstext(),currentsourcetoken.lineNo,currentsourcetoken.columnNo,10013);
1054
1055                                return  10004;
1056                            }
1057
1058                            yychar = -1;
1059                            stmtendpos = curtokenpos-1;
1060                            sourcetokenlist.get(curtokenpos-1).tokenstatus = ETokenStatus.tssynataxerror;
1061                            //  showmessage('lookahead:'+inttostr(curtokenpos-1)+'->'+sourcetokenlist[curtokenpos-1].astext+'->yyd[yystate]:'+inttostr(yystate));
1062                            // goto next;     (* clear lookahead char and try again *)
1063                            continue;
1064                        }
1065
1066                    }else{
1067
1068                        // Enhanced parser to handle SQL statements that are missing a semicolon at the end. 
1069                        // When a statement without a semicolon is encountered, the parser will attempt to add a virtual semicolon and continue parsing rather than throwing an error. This improves parsing robustness for incomplete statements.
1070
1071                        // 如果当前token是0 (end of input),并且可以shift一个分号,则shift一个分号,否则报错
1072                        if (yychar == 0 && yyact(yystate, ';', retvalue, p_yyal,p_yyah,p_yya_sym,p_yya_act) && retvalue[0] > 0) {
1073                            yyn = retvalue[0];
1074                            lcIsShift = true;
1075                            break;
1076                        }
1077                        geterrormsg(yystate, yychar,10102);
1078                        return 10102;
1079                    }
1080
1081                }// if lcIsError
1082
1083            }  //next
1084
1085            if (lcIsParse)
1086            {
1087                lcIsParse = false;
1088                continue;
1089            }
1090
1091            if (lcIsShift)
1092            {
1093                // this is the point where SHIFT is started:
1094
1095                lcIsShift = false;
1096                //(* go to new state, clear lookahead character: *)
1097                rollback_state = yystate;
1098                rollback_mode = 1;
1099                rollback_sp = yysp;
1100                rollback_token = yyval;
1101                if ((yyerrflag>0) && (yychar != _error ) )
1102                {
1103                    if ((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase)||(dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq)){
1104                        nextstmtstartpos = curtokenpos - 1;
1105                    }
1106                    yyerrflag--;
1107                }
1108                yystate = yyn;
1109                yychar = -1;
1110                yyval = new TDatabaseYYSType();
1111                yyval.copy( yylval);
1112
1113                if (lcRetries > 0) lcRetries--;
1114
1115                if ((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase)||(dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq)){
1116                    stmtendpos = curtokenpos - 1; // tokentext became part of stmt only after shift
1117                    acceptedtoken = currentsourcetoken;
1118                }
1119
1120                // goto parse;
1121                continue;
1122
1123            }//if lcIsShift
1124
1125            if (lcIsReduce)
1126            {
1127                // this is the point where REDUCE is started:
1128
1129                lcIsReduce = false;
1130
1131                //(* execute action, pop rule from stack, and go to next state: *)
1132                // yyval.yyTLz_Parse_Tree_Node := nil;
1133                yyflag = yyfnone;
1134                yyval = new TDatabaseYYSType();
1135                yyaction(-yyn);
1136
1137                // Proof-mode observation: record the reduction BEFORE the pop,
1138                // so the rule's extent on the stack is still visible. yychar is
1139                // -1 when this is a default reduction, i.e. one the prefix
1140                // determined without any lookahead.
1141                if (proofLrReductions != null && proofLrConfiguration == null)
1142                {
1143                    proofLrReductions.add(new ProofReduction(-yyn, p_yyr_len[-yyn],
1144                            p_yyr_sym[-yyn], yysp,
1145                            (yychar == -1 || currentsourcetoken == null)
1146                                    ? -1L : currentsourcetoken.offset));
1147                }
1148
1149                rollback_mode = 2;
1150                yysp = yysp - p_yyr_len[-yyn];
1151
1152                if (yygoto(yys[yysp], p_yyr_sym[-yyn], retvalue,p_yygl,p_yygh,p_yyg_sym,p_yyg_act))
1153                {
1154                    yyn = retvalue[0];
1155                    yystate = yyn;
1156                }
1157
1158                //(* handle action calls to yyaccept, yyabort and yyerror: *)
1159                // goto parse;
1160                continue;
1161
1162            } //if lcIsReduce
1163
1164        }   // parse
1165
1166    }
1167
1168
1169    void  geterrormsg(int state, int sym, int errorno){
1170        if (sql != null){
1171            String s;
1172           if (sym == 0){
1173               s = "end of input, state:"+state;
1174           }else{
1175               s = "syntax error, state:"+state;
1176           }
1177          //System.out.println(currentsourcetoken);
1178//          if (currentsourcetoken != null){
1179//              s = s+"toke code:"+currentsourcetoken.tokencode+" ,token type:"+currentsourcetoken.tokentype;
1180//          }
1181
1182            sql.parseerrormessagehandle(new TSyntaxError(currentsourcetoken,s, EErrorType.spfatalerror,errorno,null));
1183
1184
1185
1186        }else
1187          System.out.println("syntax error 10033,state:"+state);
1188
1189    }
1190    
1191
1192
1193    void yyaction (int yyruleno){
1194
1195    }
1196    
1197public int yyparse(){
1198    return -1;
1199}
1200
1201}
1202
1203class TDatabaseYYSType {
1204    String yylzString;
1205    TSourceToken yyTSourceToken;
1206    TParseTreeNodeList yyTParseTreeNodeList;
1207    TParseTreeNode yyTParseTreeNode;
1208    TSourceTokenList yyTSourceTokenList;
1209    ArrayList yyArrayList;
1210   // TStatementList yyTStatementList;
1211    
1212    public void copy(TDatabaseYYSType p){
1213       yyTSourceToken = p.yyTSourceToken;
1214       yyTParseTreeNodeList = p.yyTParseTreeNodeList;
1215       yyTParseTreeNode = p.yyTParseTreeNode;
1216        yyTSourceTokenList = p.yyTSourceTokenList;
1217        yyArrayList = p.yyArrayList;
1218    }
1219}
1220
1221class TokenAndText{
1222    public TSourceToken lastToken;
1223    public String text;
1224
1225    public TokenAndText(TSourceToken lastToken, String text) {
1226        this.lastToken = lastToken;
1227        this.text = text;
1228    }
1229}
1230