1 // Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
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.
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.
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
17 // See http://www.salome-platform.org/
20 #include "QtxParser.h"
21 #include "QtxOperations.h"
26 QtxParser::QtxParser( QtxOperations* operations, const QString& expr )
27 : myOperations( operations )
35 setLastError( OperationsNull );
41 QtxParser::~QtxParser()
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
54 int QtxParser::search( const QStringList& list, const QString& str, int offset,
55 int& matchLen, int& listind )
57 QStringList::const_iterator anIt = list.begin(), aLast = list.end();
59 for( int ind = 0; anIt!=aLast; anIt++, ind++ )
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() ) ) )
68 matchLen = (*anIt).length();
79 \param pos - start position of substring
80 \param len - length of substring
82 QString QtxParser::note( const QString& str, int pos, int len )
84 return str.mid( pos, len ).stripWhiteSpace();
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
92 bool QtxParser::prepare( const QString& expr, Postfix& post )
94 int pos = 0, len = expr.length();
95 QValueStack< int > aBracketStack;
96 QStringList anOpers, anOpenBr, aCloseBr;
99 myOperations->bracketsList( anOpenBr, true );
100 myOperations->bracketsList( aCloseBr, false );
101 myOperations->opersList( anOpers );
105 setLastError( OperationsNull );
109 while( pos < len && lastError()==OK )
112 while( expr[ pos ].isSpace() && pos<len ) pos++;
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 );
121 if( expr[ pos ]=="'" )
124 while ( vpos< (int)expr.length() && expr[ vpos ]!="'" )
129 int res = myOperations->createValue( note( expr, pos, mLen ), item.myValue );
130 item.myType = res ? Value : Param;
138 aBracketStack.push( br_ind );
139 item.myValue = note( expr, pos, mBrLen );
146 if( aBracketStack.count()==0 )
148 setLastError( ExcessClose );
151 if( br_ind!=aBracketStack.top() )
153 setLastError( BracketsNotMatch );
159 item.myValue = note( expr, pos, mBrLen );
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
176 post.insert( post.at( post.count()-1 ), item );
183 if( oPos!=pos && cPos!=pos )
186 for( i=pos+1; i<(int)expr.length(); i++ )
187 if( expr[ i ].isSpace() )
191 if( oPos>=0 && oPos<vpos )
193 if( cPos>=0 && cPos<vpos )
195 if( opPos>=0 && opPos<vpos )
198 while( vpos<(int)expr.length() &&
199 ( expr[vpos].isLetter() || expr[vpos].isDigit() || expr[vpos]=='_' ) )
203 bool res = myOperations->createValue( note( expr, pos, mLen ), item.myValue );
204 item.myType = res ? Value : Param;
213 PostfixIterator anIt = post.begin(),
216 for( ; anIt!=aLast; anIt++ )
217 if( (*anIt).myType==Open )
219 else if( (*anIt).myType==Close )
224 setLastError( ExcessClose );
228 setLastError( CloseExpected );
230 return lastError()==OK;
234 Second step of parsing: determining types of operations
235 \param post - unsorted postfix
237 bool QtxParser::setOperationTypes( Postfix& post )
239 Postfix::iterator aStart = post.begin(),
241 anIt = aStart, aPrev, aNext;
242 QStringList anOpen, aClose;
245 myOperations->bracketsList( anOpen, true );
246 myOperations->bracketsList( aClose, false );
251 for( ; anIt!=aLast; anIt++ )
253 aPrev = anIt; aPrev--;
254 aNext = anIt; aNext++;
255 if( (*anIt).myType != Binary )
258 if( ( anIt==aStart || (*aPrev).myType == Open ||
259 (*aPrev).myType == Pre ||
260 (*aPrev).myType == Binary )
262 aNext!=aLast && ( (*aNext).myType == Value ||
263 (*aNext).myType == Param ||
264 (*aNext).myType == Open ||
265 (*aNext).myType == Binary ) )
266 (*anIt).myType = Pre;
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 )
275 ( aNext==aLast || (*aNext).myType == Close ) )
276 (*anIt).myType = Post;
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;
284 return lastError()==OK;
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
293 int QtxParser::globalBrackets( const QtxParser::Postfix& post, int f, int l )
300 min_br_num = (l-f+1)*5;
302 for( i=f; i<=l; i++ )
303 if( post[ i ].myType==QtxParser::Open )
307 for( i=l; i>=f; i-- )
308 if( post[ i ].myType==QtxParser::Close )
313 br = start_br<fin_br ? start_br : fin_br;
314 for( i=f+br; i<=l-br; i++ )
316 if( post[i].myType==QtxParser::Open )
318 else if( post[i].myType==QtxParser::Close )
320 if( br_num<min_br_num )
324 return br+min_br_num;
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
336 bool QtxParser::sort( const Postfix& post, Postfix& res,
337 const QStringList& anOpen,
338 const QStringList& aClose,
349 int br = globalBrackets( post, f, l );
353 res.append( post[ f ] );
361 QValueList< PostfixItemType > min_types;
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++ )
368 const PostfixItem& item = post[ j ];
369 PostfixItemType tt = item.myType;
370 if( tt==Binary || tt==Pre || tt==Post )
372 int cur_pr = myOperations->prior( item.myValue.toString(), tt==Binary );
375 if( min<0 || min>=cur_pr )
379 argmin.append( f+i );
380 min_types.append( tt );
385 argmin.clear(); argmin.append( f+i );
386 min_types.clear(); min_types.append( tt );
392 setLastError( InvalidOperation );
398 QString opBr = item.myValue.toString();
399 int ind = anOpen.findIndex( opBr ), brValue = 0;
402 const PostfixItem& anItem = post[ j ];
403 if( anItem.myType==Open )
406 if( anItem.myType==Close )
409 QString clBr = anItem.myValue.toString();
410 if( aClose.findIndex( clBr )==ind && brValue==0 )
417 setLastError( CloseExpected );
423 if( lastError()==OK )
426 QValueList< Postfix > parts;
427 QIntList::const_iterator anIt = argmin.begin(),
428 aLast = argmin.end();
430 bool ok = sort( post, one, anOpen, aClose, f, *anIt - 1 );
433 for( ; anIt!=aLast && ok; anIt++ )
435 QIntList::const_iterator aNext = anIt; aNext++;
436 ok = sort( post, one, anOpen, aClose, *anIt + 1, aNext==aLast ? l : *aNext - 1 );
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++ )
451 if( anOpen.contains( post[ *anIt ].myValue.toString() )==0 )
454 aStack.push( post[ *anIt ] );
458 res.append( post[ *anIt ] );
464 while( !aStack.isEmpty() )
466 res.append( aStack.top() );
469 res.append( post[ *anIt ] );
472 while( !aStack.isEmpty() )
474 res.append( aStack.top() );
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 )
488 setLastError( OperationsNull );
490 return lastError()==OK;
494 Build posfix by expression
495 \param expr - string expression
497 bool QtxParser::parse( const QString& expr )
502 QStringList opens, closes;
507 myOperations->bracketsList( opens, true );
508 myOperations->bracketsList( closes, false );
512 setLastError( OperationsNull );
516 //return prepare( expr, myPost ) && setOperationTypes( myPost );
517 return prepare( expr, p ) && setOperationTypes( p ) &&
518 sort( p, myPost, opens, closes );
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)
527 bool QtxParser::calculate( const QString& op, QtxValue& v1, QtxValue& v2 )
529 Error err = myOperations->isValid( op, v1.type(), v2.type() );
531 setLastError( myOperations->calculate( op, v1, v2 ) );
535 return lastError()==OK;
539 Calculates expression without postfix rebuilding
540 \return QtxValue as result (it is invalid if there were errors during calculation)
542 QtxValue QtxParser::calculate()
546 QStringList anOpen, aClose;
549 myOperations->bracketsList( anOpen, true );
550 myOperations->bracketsList( aClose, false );
554 setLastError( OperationsNull );
558 QtxValueStack aStack;
559 PostfixIterator anIt = myPost.begin(),
560 aLast = myPost.end();
561 for( ; anIt!=aLast && lastError()==OK; anIt++ )
563 QString nn = (*anIt).myValue.toString();
564 if( (*anIt).myType==Param )
568 QVariant& v = myParameters[ nn ];
572 setLastError( InvalidToken );
575 setLastError( InvalidToken );
578 else if( (*anIt).myType==Value )
579 aStack.push( (*anIt).myValue );
581 else if( (*anIt).myType==Pre || (*anIt).myType==Post )
583 if( anOpen.contains( nn )>0 )
586 if( calculate( nn, inv, inv ) )
587 aStack.push( QtxValue() );
589 else if( aClose.contains( nn )>0 )
591 QValueList< QtxValue > set;
594 if( aStack.count()==0 )
596 setLastError( StackUnderflow );
599 if( aStack.top().isValid() )
601 set.append( aStack.top() );
611 QtxValue qSet = set, inv;
612 if( calculate( nn, qSet, inv ) )
615 else if( aStack.count()>=1 )
618 QtxValue* v1 = &aStack.top(), *v2 = &inv; //"post-" case
619 if( (*anIt).myType==Pre )
621 v2 = &aStack.top(); v1 = &inv;
624 calculate( nn, *v1, *v2 );
627 setLastError( StackUnderflow );
630 else if( (*anIt).myType==Binary )
632 if( aStack.count()>=2 )
634 QVariant v2 = aStack.top(); aStack.pop();
635 calculate( nn, aStack.top(), v2 );
638 setLastError( StackUnderflow );
643 if( lastError()==OK )
645 int count = aStack.count();
647 setLastError( StackUnderflow );
651 setLastError( ExcessData );
657 Change expression, rebuild postfix and calculate it
658 \return QtxValue as result (it is invalid if there were errors during calculation)
660 QtxValue QtxParser::calculate( const QString& expr )
667 Change expression and rebuild postfix
669 bool QtxParser::setExpr( const QString& expr )
671 return parse( expr );
675 \return true, if parser contain parameter
676 \param name - name of parameter
678 bool QtxParser::has( const QString& name ) const
680 return myParameters.contains( name.stripWhiteSpace() );
685 \param name - name of parameter
686 \param value - value of parameter
688 void QtxParser::set( const QString& name, const QtxValue& value )
690 myParameters[ name.stripWhiteSpace() ] = value;
695 \param name - name of parameter
697 bool QtxParser::remove( const QString& name )
699 QString sname = name.stripWhiteSpace();
700 bool res = has( sname );
702 myParameters.remove( sname );
707 \return value of parameter (result is invalid if there is no such parameter)
708 \param name - name of parameter
710 QtxValue QtxParser::value( const QString& name ) const
712 QString sname = name.stripWhiteSpace();
714 return myParameters[ sname ].toString();
720 Searches first parameter with assigned invalid QtxValue
721 \return true if it is found
722 \param name - variable to return name of parameter
724 bool QtxParser::firstInvalid( QString& name ) const
726 QMap< QString, QtxValue >::const_iterator anIt = myParameters.begin(),
727 aLast = myParameters.end();
728 for( ; anIt!=aLast; anIt++ )
729 if( !anIt.data().isValid() )
738 Removes all parameters with assigned invalid QtxValues
740 void QtxParser::removeInvalids()
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() );
749 QStringList::const_iterator aLIt = toDelete.begin(),
750 aLLast = toDelete.end();
751 for( ; aLIt!=aLLast; aLIt++ )
752 myParameters.remove( *aLIt );
756 \return last error occured during parsing
758 QtxParser::Error QtxParser::lastError() const
764 Sets last error occured during parsing (for internal using only)
766 void QtxParser::setLastError( QtxParser::Error err )
772 \return string dump of internal parser postfix
774 QString QtxParser::dump() const
776 return dump( myPost );
780 \return string dump of postfix
781 \param post - postfix to be dumped
783 QString QtxParser::dump( const Postfix& post ) const
789 PostfixIterator anIt = post.begin(),
791 for( ; anIt!=aLast; anIt++ )
793 if( (*anIt).myType == Value &&
794 ( ( *anIt ).myValue.type()==QVariant::String ||
795 ( *anIt ).myValue.type()==QVariant::CString ) )
796 res += "'" + ( *anIt ).myValue.toString() + "'";
798 res += ( *anIt ).myValue.toString();
799 if( (*anIt).myType == Pre )
801 else if( (*anIt).myType == Post )
803 else if( (*anIt).myType == Binary )
813 Fills list with names of parameters
814 \param list - list to be filled
816 void QtxParser::paramsList( QStringList& list )
818 PostfixIterator anIt = myPost.begin(),
819 aLast = myPost.end();
820 for( ; anIt!=aLast; anIt++ )
821 if( (*anIt).myType==Param )
823 QString name = (*anIt).myValue.toString();
824 if( list.contains( name )==0 )
830 Removes all parameters
832 void QtxParser::clear()
834 myParameters.clear();
838 \return string representation for list of QtxValues
839 \param list - list to be converted
841 QString QtxParser::toString( const QValueList< QtxValue >& list )
843 QValueList< QtxValue >::const_iterator anIt = list.begin(),
845 QString res = "set : [ ";
846 for( ; anIt!=aLast; anIt++ )
847 res+=(*anIt).toString()+" ";