]> SALOME platform Git repositories - modules/gui.git/blob - src/Qtx/QtxParser.cxx
Salome HOME
Bug fix for SSPP10924
[modules/gui.git] / src / Qtx / QtxParser.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/
18 //
19
20 #include "QtxParser.h"
21 #include "QtxOperations.h"
22
23 //================================================================
24 // Function : 
25 // Purpose  : 
26 //================================================================
27 QtxParser::QtxParser( QtxOperations* operations,
28                       const QString& expr )
29 : myOperations( operations )
30 {
31     if( myOperations )
32     {
33         setLastError( OK );
34         setExpr( expr );
35     }
36     else
37         setLastError( OperationsNull );
38 }
39
40 //================================================================
41 // Function : 
42 // Purpose  : 
43 //================================================================
44 QtxParser::~QtxParser()
45 {
46 }
47
48 //================================================================
49 // Function : 
50 // Purpose  : 
51 //================================================================
52 int QtxParser::search( const QStringList& list, const QString& str, int offset,
53                        int& matchLen, int& listind )
54 {
55     QStringList::const_iterator anIt = list.begin(), aLast = list.end();
56     int min = -1;
57     for( int ind = 0; anIt!=aLast; anIt++, ind++ )
58     {
59       //const char* where = str.latin1(), *what = (*anIt).latin1();
60         int pos = str.find( *anIt, offset );
61         if( pos>=0 && ( min < 0 || min > pos ||
62                         ( min==pos && matchLen< (int)(*anIt).length() ) ) )
63         {
64             min = pos;
65             listind = ind;
66             matchLen = (*anIt).length();
67         }
68     }
69     if( min<0 )
70         matchLen = 0;
71     return min;
72 }
73
74 //================================================================
75 // Function : 
76 // Purpose  : 
77 //================================================================
78 QString QtxParser::note( const QString& str, int pos, int len )
79 {
80     return str.mid( pos, len ).stripWhiteSpace();
81 }
82
83 //================================================================
84 // Function : 
85 // Purpose  : 
86 //================================================================
87 bool QtxParser::prepare( const QString& expr, Postfix& post )
88 {
89     int pos = 0, len = expr.length();
90     QValueStack< int > aBracketStack;
91     QStringList anOpers, anOpenBr, aCloseBr;
92     if( myOperations )
93     {
94         myOperations->bracketsList( anOpenBr, true );
95         myOperations->bracketsList( aCloseBr, false );
96         myOperations->opersList( anOpers );
97     }
98     else
99     {
100         setLastError( OperationsNull );
101         return false;
102     }
103
104     while( pos < len && lastError()==OK )
105     {
106         PostfixItem item;
107         while( expr[ pos ].isSpace() && pos<len ) pos++;
108         if( pos>=len )
109             break;
110
111         int mBrLen = 0, mLen = 0, br_ind = -1, op_ind = -1;
112         int oPos = search( anOpenBr, expr, pos, mBrLen, br_ind ),
113             cPos = oPos==pos ? -1 : search( aCloseBr, expr, pos, mBrLen, br_ind ),
114             opPos = search( anOpers, expr, pos, mLen, op_ind );
115
116         if( expr[ pos ]=="'" )
117         {
118             int vpos = pos+1;
119             while ( vpos< (int)expr.length() && expr[ vpos ]!="'" )
120                 vpos++;
121
122             mLen = vpos-pos+1;
123
124             int res = myOperations->createValue( note( expr, pos, mLen ), item.myValue );
125             item.myType = res ? Value : Param;
126             post.append( item );
127             pos = vpos+1;
128             continue;
129         }
130
131         if( oPos==pos )
132         {
133             aBracketStack.push( br_ind );
134             item.myValue = note( expr, pos, mBrLen );
135             item.myType = Open;
136             post.append( item );
137         }
138
139         else if( cPos==pos )
140         {
141             if( aBracketStack.count()==0 )
142             {
143                 setLastError( ExcessClose );
144                 break;
145             }
146             if( br_ind!=aBracketStack.top() )
147             {
148                 setLastError( BracketsNotMatch );
149                 break;
150             }
151             else
152             {
153                 aBracketStack.pop();
154                 item.myValue = note( expr, pos, mBrLen );
155                 item.myType = Close;
156                 post.append( item );
157             }
158         }
159         else
160             mBrLen = 0;
161
162         if( opPos==pos )
163         {
164             mBrLen = 0;
165             item.myValue = note( expr, pos, mLen );
166             item.myType = Binary;
167             //the type is set by default;
168             //the method setOperationTypes will set correct types
169
170             if( oPos==pos )
171                 post.insert( post.at( post.count()-1 ), item );
172             else
173                 post.append( item );
174         }
175         else
176         {
177             mLen = 0;
178             if( oPos!=pos && cPos!=pos )
179             {
180                 int i; 
181                 for( i=pos+1; i<(int)expr.length(); i++ )
182                     if( expr[ i ].isSpace() )
183                         break;
184
185                 int vpos = i;
186                 if( oPos>=0 && oPos<vpos )
187                     vpos = oPos;
188                 if( cPos>=0 && cPos<vpos )
189                     vpos = cPos;
190                 if( opPos>=0 && opPos<vpos )
191                     vpos = opPos;
192
193                 while( vpos<(int)expr.length() && 
194                        ( expr[vpos].isLetter() || expr[vpos].isDigit() || expr[vpos]=='_' ) )
195                     vpos++;
196
197                 mLen = vpos-pos;
198                 bool res = myOperations->createValue( note( expr, pos, mLen ), item.myValue );
199                 item.myType = res ? Value : Param;
200                 post.append( item );
201             }
202         }
203
204         pos+=mBrLen+mLen;
205     }
206
207     //Bracket checking
208     PostfixIterator anIt = post.begin(),
209                     aLast = post.end();
210     int brValue = 0;
211     for( ; anIt!=aLast; anIt++ )
212         if( (*anIt).myType==Open )
213             brValue++;
214         else if( (*anIt).myType==Close )
215             if( brValue>0 )
216                 brValue--;
217             else
218             {
219                 setLastError( ExcessClose );
220                 break;
221             }
222     if( brValue>0 )
223         setLastError( CloseExpected );
224
225     return lastError()==OK;
226 }
227
228 //================================================================
229 // Function : 
230 // Purpose  : 
231 //================================================================
232 bool QtxParser::setOperationTypes( Postfix& post )
233 {
234     Postfix::iterator aStart = post.begin(),
235                       aLast = post.end(),
236                       anIt = aStart, aPrev, aNext;
237     QStringList anOpen, aClose;
238     if( myOperations )
239     {
240         myOperations->bracketsList( anOpen, true );
241         myOperations->bracketsList( aClose, false );
242     }
243     else
244         return false;
245
246     for( ; anIt!=aLast; anIt++ )
247     {
248         aPrev = anIt; aPrev--;
249         aNext = anIt; aNext++;
250         if( (*anIt).myType != Binary )
251             continue;
252
253         if( ( anIt==aStart || (*aPrev).myType == Open ||
254                               (*aPrev).myType == Pre ||
255                               (*aPrev).myType == Binary )
256             && 
257             aNext!=aLast && ( (*aNext).myType == Value ||
258                               (*aNext).myType == Param ||
259                               (*aNext).myType == Open  ||
260                               (*aNext).myType == Binary ) )
261             (*anIt).myType = Pre;
262
263         else if( anIt!=aStart && ( (*aPrev).myType == Close ||
264                                    (*aPrev).myType == Param ||
265                                    (*aPrev).myType == Value ||
266                                    (*aPrev).myType == Pre ||
267                                    (*aPrev).myType == Post ||
268                                    (*aPrev).myType == Binary )
269                  &&
270                  ( aNext==aLast || (*aNext).myType == Close ) )
271             (*anIt).myType = Post;
272
273         if( anOpen.contains( ( *anIt ).myValue.toString() )>0 )
274             (*anIt).myType = Pre;
275         else if( aClose.contains( ( *anIt ).myValue.toString() )>0 )
276             (*anIt).myType = Post;
277     }
278
279     return lastError()==OK;
280 }
281
282 //================================================================
283 // Function : 
284 // Purpose  : 
285 //================================================================
286 int QtxParser::globalBrackets( const QtxParser::Postfix& post, int f, int l )
287 {
288     int i,
289         start_br = 0,
290         fin_br = 0,
291         br = 0,
292         br_num = 0,
293         min_br_num = (l-f+1)*5;
294     
295     for( i=f; i<=l; i++ )
296         if( post[ i ].myType==QtxParser::Open )
297             start_br++;
298         else
299             break;
300     for( i=l; i>=f; i-- )
301         if( post[ i ].myType==QtxParser::Close )
302             fin_br++;
303         else
304             break;
305
306     br = start_br<fin_br ? start_br : fin_br;
307     for( i=f+br; i<=l-br; i++ )
308     {
309         if( post[i].myType==QtxParser::Open )
310             br_num++;
311         else if( post[i].myType==QtxParser::Close )
312             br_num--;
313         if( br_num<min_br_num )
314             min_br_num = br_num;
315     }
316
317     return br+min_br_num;
318 }
319
320 //================================================================
321 // Function : 
322 // Purpose  : 
323 //================================================================
324 bool QtxParser::sort( const Postfix& post, Postfix& res,
325                       const QStringList& anOpen,
326                       const QStringList& aClose,
327                       int f, int l )
328 {
329     if( l<f )
330         return true;
331
332     if( f<0 )
333         f = 0;
334     if( l<0 )
335         l = post.count()-1;
336
337     int br = globalBrackets( post, f, l );
338     f+=br; l-=br;
339
340     if( f==l && f>=0 )
341         res.append( post[ f ] );
342     if( l<=f )
343         return true;
344
345     if( myOperations )
346     {
347         int min = -1;
348         QIntList argmin;
349         QValueList< PostfixItemType > min_types;
350
351         //Find operation with minimal priority
352         //PostfixIterator anIt = post.at( f ),
353         //                aLast = post.at( l+1 );
354         for( int i=0, j=f; j<=l; i++, j++ )
355         {
356             const PostfixItem& item = post[ j ];
357             PostfixItemType tt = item.myType;
358             if( tt==Binary || tt==Pre || tt==Post )
359             {
360                 int cur_pr = myOperations->prior( item.myValue.toString(), tt==Binary );
361                 if( cur_pr>0 )
362                 {
363                     if( min<0 || min>=cur_pr )
364                     {
365                         if( min==cur_pr )
366                         {
367                             argmin.append( f+i );
368                             min_types.append( tt );
369                         }
370                         else
371                         {
372                             min = cur_pr;
373                             argmin.clear(); argmin.append( f+i );
374                             min_types.clear(); min_types.append( tt );
375                         }
376                     }
377                 }
378                 else
379                 {
380                     setLastError( InvalidOperation );
381                     break;
382                 }
383             }
384             else if( tt==Open )
385             {
386                 QString opBr = item.myValue.toString();
387                 int ind = anOpen.findIndex( opBr ), brValue = 0;
388                 while( j<=l )
389                 {
390                     const PostfixItem& anItem = post[ j ];
391                     if( anItem.myType==Open )
392                         brValue++;
393
394                     if( anItem.myType==Close )
395                     {
396                         brValue--;
397                         QString clBr = anItem.myValue.toString();
398                         if( aClose.findIndex( clBr )==ind && brValue==0 )
399                             break;
400                     }
401                     i++; j++;
402                 }
403                 if( brValue>0 )
404                 {
405                     setLastError( CloseExpected );
406                     break;
407                 }
408             }
409         }
410
411         if( lastError()==OK )
412             if( min>=0 )
413             {
414                 QValueList< Postfix > parts;
415                 QIntList::const_iterator anIt = argmin.begin(),
416                                          aLast = argmin.end();
417                 Postfix one;
418                 bool ok = sort( post, one, anOpen, aClose, f, *anIt - 1 );
419                 parts.append( one );
420                 one.clear();
421                 for( ; anIt!=aLast && ok; anIt++ )
422                 {
423                     QIntList::const_iterator aNext = anIt; aNext++;
424                     ok = sort( post, one, anOpen, aClose, *anIt + 1, aNext==aLast ? l : *aNext - 1 );
425                     parts.append( one );
426                     one.clear();
427                 }
428                 if( !ok )
429                     return false;
430
431                 QValueList< Postfix >::const_iterator aPIt = parts.begin();
432                 QValueList< PostfixItemType >::const_iterator aTIt = min_types.begin();
433                 QValueStack< PostfixItem > aStack;
434                 res += (*aPIt); aPIt++;
435                 anIt = argmin.begin();
436                 for( ; anIt!=aLast; anIt++, aPIt++, aTIt++ )
437                 {
438                     if( *aTIt==Pre )
439                         if( anOpen.contains( post[ *anIt ].myValue.toString() )==0 )
440                         {
441                             res+=(*aPIt);
442                             aStack.push( post[ *anIt ] );
443                         }
444                         else
445                         {
446                             res.append( post[ *anIt ] );
447                             res+=(*aPIt);
448                         }
449                     else
450                     {
451                         res+=(*aPIt);
452                         while( !aStack.isEmpty() )
453                         {
454                             res.append( aStack.top() );
455                             aStack.pop();
456                         }
457                         res.append( post[ *anIt ] );
458                     }
459                 }
460                 while( !aStack.isEmpty() )
461                 {
462                     res.append( aStack.top() );
463                     aStack.pop();
464                 }
465             }
466             else
467             { //there are no operations
468                 PostfixIterator anIt = post.at( f ),
469                                 aLast = post.at( l+1 );
470                 for( ; anIt!=aLast; anIt++ )
471                     if( (*anIt).myType==Value || (*anIt).myType==Param )
472                         res.append( *anIt );
473             }
474     }
475     else 
476         setLastError( OperationsNull );
477
478     return lastError()==OK;
479 }
480
481 //================================================================
482 // Function : 
483 // Purpose  : 
484 //================================================================
485 bool QtxParser::parse( const QString& expr )
486 {
487     myPost.clear();
488
489     Postfix p;
490     QStringList opens, closes;
491
492     if( myOperations )
493     {
494         setLastError( OK );
495         myOperations->bracketsList( opens, true );
496         myOperations->bracketsList( closes, false );
497     }
498     else
499     {
500         setLastError( OperationsNull );
501         return false;
502     }
503
504     //return prepare( expr, myPost ) && setOperationTypes( myPost );
505     return prepare( expr, p ) && setOperationTypes( p ) && 
506            sort( p, myPost, opens, closes );
507 }
508
509 //================================================================
510 // Function : 
511 // Purpose  : 
512 //================================================================
513 bool QtxParser::calculate( const QString& op, QtxValue& v1, QtxValue& v2 )
514 {
515     Error err = myOperations->isValid( op, v1.type(), v2.type() );
516     if( err==OK )
517         setLastError( myOperations->calculate( op, v1, v2 ) );
518     else
519         setLastError( err );
520
521     return lastError()==OK;
522 }
523
524 //================================================================
525 // Function : 
526 // Purpose  : 
527 //================================================================
528 QtxValue QtxParser::calculate()
529 {
530     setLastError( OK );
531
532     QStringList anOpen, aClose;
533     if( myOperations )
534     {
535         myOperations->bracketsList( anOpen, true );
536         myOperations->bracketsList( aClose, false );
537     }
538     else
539     {
540         setLastError( OperationsNull );
541         return QtxValue();
542     }
543
544     QtxValueStack aStack;
545     PostfixIterator anIt = myPost.begin(),
546                     aLast = myPost.end();
547     for( ; anIt!=aLast && lastError()==OK; anIt++ )
548     {
549         QString nn = (*anIt).myValue.toString();
550         if( (*anIt).myType==Param )
551         {
552             if( has( nn ) )
553             {
554                 QVariant& v = myParameters[ nn ];
555                 if( v.isValid() )
556                     aStack.push( v );
557                 else
558                     setLastError( InvalidToken );
559             }
560             else
561                 setLastError( InvalidToken );
562         }
563
564         else if( (*anIt).myType==Value )
565             aStack.push( (*anIt).myValue );
566
567         else if( (*anIt).myType==Pre || (*anIt).myType==Post )
568         {
569             if( anOpen.contains( nn )>0 )
570             {
571                 QtxValue inv;
572                 if( calculate( nn, inv, inv ) )
573                     aStack.push( QtxValue() );
574             }
575             else if( aClose.contains( nn )>0 )
576             {
577                 QValueList< QtxValue > set;
578                 while( true )
579                 {
580                     if( aStack.count()==0 )
581                     {
582                         setLastError( StackUnderflow );
583                         break;
584                     }
585                     if( aStack.top().isValid() )
586                     {
587                         set.append( aStack.top() );
588                         aStack.pop();
589                     }
590                     else
591                     {
592                         aStack.pop();
593                         break;
594                     }
595                 }
596
597                 QtxValue qSet = set, inv;
598                 if( calculate( nn, qSet, inv ) )
599                     aStack.push( set );
600             }
601             else if( aStack.count()>=1 )
602             {
603                 QtxValue inv;
604                 QtxValue* v1 = &aStack.top(), *v2 = &inv; //"post-" case
605                 if( (*anIt).myType==Pre )
606                 {
607                     v2 = &aStack.top(); v1 = &inv;
608                 }
609
610                 calculate( nn, *v1, *v2 );
611             }
612             else
613                 setLastError( StackUnderflow );
614         }
615
616         else if( (*anIt).myType==Binary )
617         {
618             if( aStack.count()>=2 )
619             {
620                 QVariant v2 = aStack.top(); aStack.pop();
621                 calculate( nn, aStack.top(), v2 );
622             }
623             else
624                 setLastError( StackUnderflow );
625         }
626     }
627
628     QtxValue res;
629     if( lastError()==OK )
630     {
631         int count = aStack.count();
632         if( count==0 )
633             setLastError( StackUnderflow );
634         else if( count==1 )
635             res = aStack.top();
636         else
637             setLastError( ExcessData );
638     }
639     return res;
640 }
641
642 //================================================================
643 // Function : 
644 // Purpose  : 
645 //================================================================
646 QtxValue QtxParser::calculate( const QString& expr )
647 {
648     setExpr( expr );
649     return calculate();
650 }
651
652 //================================================================
653 // Function : 
654 // Purpose  : 
655 //================================================================
656 bool QtxParser::setExpr( const QString& expr )
657 {
658     return parse( expr );
659 }
660
661 //================================================================
662 // Function : 
663 // Purpose  : 
664 //================================================================
665 bool QtxParser::has( const QString& name ) const
666 {
667     return myParameters.contains( name.stripWhiteSpace() );
668 }
669
670 //================================================================
671 // Function : 
672 // Purpose  : 
673 //================================================================
674 void QtxParser::set( const QString& name, const QtxValue& value )
675 {
676     myParameters[ name.stripWhiteSpace() ] = value;
677 }
678
679 //================================================================
680 // Function : 
681 // Purpose  : 
682 //================================================================
683 bool QtxParser::remove( const QString& name )
684 {
685     QString sname = name.stripWhiteSpace();
686     bool res = has( sname );
687     if( res )
688         myParameters.remove( sname );
689     return res;
690 }
691
692 //================================================================
693 // Function : 
694 // Purpose  : 
695 //================================================================
696 QtxValue QtxParser::value( const QString& name ) const
697 {
698     QString sname = name.stripWhiteSpace();
699     if( has( sname ) )
700         return myParameters[ sname ].toString();
701     else
702         return QtxValue();
703 }
704
705 //================================================================
706 // Function : 
707 // Purpose  : 
708 //================================================================
709 bool QtxParser::firstInvalid( QString& name ) const
710 {
711     QMap< QString, QtxValue >::const_iterator anIt = myParameters.begin(),
712                                               aLast = myParameters.end();
713     for( ; anIt!=aLast; anIt++ )
714         if( !anIt.data().isValid() )
715         {
716             name = anIt.key();
717             return true;
718         }
719     return false;
720 }
721
722 //================================================================
723 // Function : 
724 // Purpose  : 
725 //================================================================
726 void QtxParser::removeInvalids()
727 {
728     QStringList toDelete;
729     QMap< QString, QtxValue >::const_iterator anIt = myParameters.begin(),
730                                               aLast = myParameters.end();
731     for( ; anIt!=aLast; anIt++ )
732         if( !anIt.data().isValid() )
733             toDelete.append( anIt.key() );
734
735     QStringList::const_iterator aLIt = toDelete.begin(),
736                                 aLLast = toDelete.end();
737     for( ; aLIt!=aLLast; aLIt++ )
738         myParameters.remove( *aLIt );
739 }
740
741 //================================================================
742 // Function : 
743 // Purpose  : 
744 //================================================================
745 QtxParser::Error QtxParser::lastError() const
746 {
747     return myLastError;
748 }
749
750 //================================================================
751 // Function : 
752 // Purpose  : 
753 //================================================================
754 void QtxParser::setLastError( QtxParser::Error err )
755 {
756     myLastError = err;
757 }
758
759 //================================================================
760 // Function : 
761 // Purpose  : 
762 //================================================================
763 QString QtxParser::dump() const
764 {
765     return dump( myPost );
766 }
767
768 //================================================================
769 // Function : 
770 // Purpose  : 
771 //================================================================
772 QString QtxParser::dump( const Postfix& post ) const
773 {
774     QString res;
775
776     if( myOperations )
777     {
778         PostfixIterator anIt = post.begin(),
779                         aLast = post.end();
780         for( ; anIt!=aLast; anIt++ )
781         {
782             if( (*anIt).myType == Value && 
783                     ( ( *anIt ).myValue.type()==QVariant::String ||
784                     ( *anIt ).myValue.type()==QVariant::CString ) )
785                 res += "'" + ( *anIt ).myValue.toString() + "'";
786             else
787                 res += ( *anIt ).myValue.toString();
788             if( (*anIt).myType == Pre )
789                 res += "(pre)";
790             else if( (*anIt).myType == Post )
791                 res += "(post)";
792             else if( (*anIt).myType == Binary )
793                 res += "(bin)";
794
795             res += "_";
796         }
797     }
798     return res;
799 }
800
801 //================================================================
802 // Function : 
803 // Purpose  : 
804 //================================================================
805 void QtxParser::paramsList( QStringList& list )
806 {
807     PostfixIterator anIt = myPost.begin(),
808                     aLast = myPost.end();
809     for( ; anIt!=aLast; anIt++ )
810         if( (*anIt).myType==Param )
811         {
812             QString name = (*anIt).myValue.toString();
813             if( list.contains( name )==0 )
814                 list.append( name );
815         }
816 }
817
818 //================================================================
819 // Function : 
820 // Purpose  : 
821 //================================================================
822 void QtxParser::clear()
823 {
824     myParameters.clear();
825 }
826
827 //================================================================
828 // Function : 
829 // Purpose  : 
830 //================================================================
831 QString QtxParser::toString( const QValueList< QtxValue >& list )
832 {
833     QValueList< QtxValue >::const_iterator anIt = list.begin(),
834                                            aLast = list.end();
835     QString res = "set : [ ";
836     for( ; anIt!=aLast; anIt++ )
837         res+=(*anIt).toString()+" ";
838     res+="]";
839     return res;
840 }