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