]> SALOME platform Git repositories - modules/med.git/blob - src/MEDCalculator/Swig/SPythonParser.cxx
Salome HOME
Merge from V6_main_20120808 08Aug12
[modules/med.git] / src / MEDCalculator / Swig / SPythonParser.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF 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/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SPythonParser.hxx"
21
22 #include "InterpKernelException.hxx"
23
24 #include <algorithm>
25 #include <sstream>
26
27 using namespace ParaMEDMEM;
28
29 const char SPythonPredParser::FIELD_TYPE_STR[]="MEDCalculatorDBField";
30
31 const char SPythonParser::NUMBERS[]="0123456789";
32
33 SPythonParser::SPythonParser():_type(EMPTY_TYPE)
34 {
35 }
36
37 /*!
38  * This method checks that the input 's' follows the following pattern :
39  * "{number || var[.attribute]?{@number@}? || @number@ }*{+ - * / }*"
40  * \b All of {var} should be of type int,float funct or MEDCalculatorDBField.
41  */
42 bool SPythonParser::parseWithoutEqual(const std::string& s, int parLev, PyObject *glob, PyObject *loc, std::vector<SPythonParser>& alreadyParsed)
43 {
44   keepSelectedLevOfPar(s,parLev,glob,loc);
45   TypeOfEntity ty=_pred.getType();
46   switch(ty)
47     {
48     case FLOAT_TYPE:
49     case INT_TYPE:
50     case UNKNOWN_TYPE:
51       {
52         _type=ty;
53         return false;
54       }
55     case FIELDDB_TYPE:
56       {
57         std::string tmp;
58         if(isParenthesisMatching(_content,tmp))
59           {
60             _type=FIELDDB_TYPE;
61             _content_py=tmp;
62             return true;
63           }
64         if(isAlreadyControledParenthesis(_content))
65           {
66             _type=FIELDDB_TYPE;
67             return true;
68           }
69         return false;
70       }
71     case IDENTITY_TYPE:
72       {
73         _type=analyzeType(glob,loc,alreadyParsed);
74         break;
75       }
76     case FUNC_TYPE:
77     case EMPTY_TYPE:
78       return false;
79     }
80   return _type==FIELDDB_TYPE || _type==INT_TYPE || _type==FLOAT_TYPE;
81 }
82
83 void SPythonParser::keepSelectedLevOfPar(const std::string& s, int parLev, PyObject *glob, PyObject *loc)
84 {
85   int curLev=0;
86   std::size_t i=0;
87   std::size_t len=s.length();
88   bool found=false;
89   if(parLev==0)
90     {
91       _content=s;
92       _content_py=s;
93       std::string pred;
94       _pred.assign(pred,glob,loc);
95       return ;
96     }
97   for(i=0;i<len;i++)
98     {
99       switch(s[i])
100         {
101         case '(':
102           ++curLev;
103           break;
104         case ')':
105           curLev--;
106           break;
107         }
108       if(curLev==parLev)
109         {
110           std::size_t end=s.find_first_of(')',i);
111           end=end!=std::string::npos?end-i:std::string::npos;
112           _content=s.substr(i,end);
113           found=true;
114           break;
115         }
116     }
117   _content=_content.substr(1,_content.length()-1);
118   _content_py=_content;
119   std::string pred;
120   if(i==0)
121     {
122       _pred.assign(pred,glob,loc);
123       return ;
124     }
125   std::size_t begin=s.find_last_of("(+-*/^",i-1,6);
126   begin=begin!=std::string::npos?begin+1:0;
127   pred=s.substr(begin,i-begin);
128   _pred.assign(pred,glob,loc);
129 }
130
131
132 /*!
133  * Size of returned vector is depth of parenthesis.
134  * Foreach level number of parenthesis groups.
135  */
136 std::vector<int> SPythonParser::levOfParenthesis(const std::string& s)
137 {
138   std::vector<int> ret(1);
139   ret[0]=1;
140   std::size_t curLev=0;
141   std::size_t len=s.length();
142   for(std::size_t i=0;i<len;i++)
143     {
144       switch(s[i])
145         {
146         case '(':
147           if(ret.size()<=++curLev)
148             ret.push_back(1);
149           else
150             ret[curLev]++;
151           break;
152         case ')':
153           curLev--;
154           break;
155         }
156     }
157   return ret;
158 }
159
160 /*!
161  * Analyzes regarding _content attribute the type of value.
162  */
163 TypeOfEntity SPythonParser::analyzeType(PyObject *glob, PyObject *loc, const std::vector<SPythonParser>& alreadyParsed)
164 {
165   static const int OPS_SEP_LGTH=5;
166   static const char OPS_SEP[OPS_SEP_LGTH+1]="+-*/^";
167   _type=EMPTY_TYPE;
168   TypeOfEntity type;
169   std::string content=_content;//.substr(1,_content.length()-2);
170   std::size_t p=0;
171   int parId=0;
172   while(p!=std::string::npos)
173     {
174       std::size_t p2=content.find_first_of(OPS_SEP,p,OPS_SEP_LGTH);
175       std::size_t p3=p2!=std::string::npos?p2-p:p2;
176       std::string elt=content.substr(p,p3);
177       type=getTypeOfElt(elt,glob,loc,alreadyParsed);
178       _type=combineType(_type,type);
179       p=p2!=std::string::npos?p2+1:p2;
180       parId++;
181     }
182   return _type;
183 }
184
185 TypeOfEntity SPythonParser::getTypeOfElt(const std::string& elt, PyObject *glob, PyObject *loc, const std::vector<SPythonParser>& alreadyParsed) const
186 {
187   std::size_t sz=elt.length();
188   if(sz>=3)
189     {
190       if(elt[0]=='@' && elt[sz-1]=='@')
191         {
192           std::string tmp=elt.substr(1,sz-2);
193           int id;
194           std::istringstream iss(tmp); iss >> id;
195           return alreadyParsed[id].getType();
196         }
197     }
198   return SPythonPredParser::getTypeOfVar(elt,glob,loc);
199 }
200
201 TypeOfEntity SPythonParser::combineType(TypeOfEntity t1, TypeOfEntity t2) const
202 {
203   if(t1==EMPTY_TYPE)
204     return t2;
205   if(t1==t2)
206     return t1;
207   if(t1==UNKNOWN_TYPE || t2==UNKNOWN_TYPE)
208     return UNKNOWN_TYPE;
209   if((t1==INT_TYPE && t2==FLOAT_TYPE) || (t2==INT_TYPE && t1==FLOAT_TYPE))
210     return FLOAT_TYPE;
211   if((t1==INT_TYPE && t2==FIELDDB_TYPE) || (t2==INT_TYPE && t1==FIELDDB_TYPE))
212     return FIELDDB_TYPE;
213   if((t1==FLOAT_TYPE && t2==FIELDDB_TYPE) || (t2==FLOAT_TYPE && t1==FIELDDB_TYPE))
214     return FIELDDB_TYPE;
215   return UNKNOWN_TYPE;
216 }
217
218 /*!
219  * This method makes the assumption that s as a minimum length of size 2 with first char == '(' and the last ')'.
220  */
221 bool SPythonParser::isParenthesisMatching(const std::string& w, std::string& res)
222 {
223   std::ostringstream result;
224   //result << '(';
225   std::size_t sLgth=w.length();
226   //std::string w(s,1,sLgth-2);
227   int nbOfParams=std::count(w.c_str(),w.c_str()+sLgth,',')+1;
228   std::size_t pos=0;
229   for(int i=0;i<nbOfParams;i++)
230     {
231       std::size_t pos2=w.find(',',pos);
232       std::size_t pos3=pos2!=std::string::npos?pos2-pos:std::string::npos;
233       std::string w1=w.substr(pos,pos3);
234       std::string w1out;
235       bool isNum;
236       if(!isElementInParenthesisMatching(w1,w1out,isNum))
237         return false;
238       if(!isNum)
239         result << '\"';
240       result << w1out;
241       if(!isNum)
242         result << '\"';
243       if(i!=nbOfParams-1)
244         result << ',';
245       pos=pos2!=std::string::npos?pos2+1:std::string::npos;
246     }
247   //result << ')';
248   res=result.str();
249   return true;
250 }
251
252 /*!
253  * This method checks that s match the following regexp : "@[0123456789]?@"
254  */
255 bool SPythonParser::isAlreadyControledParenthesis(const std::string& s)
256 {
257   std::size_t len=s.length();
258   if(len<3)
259     return false;
260   if(s[0]!='@' || s[len-1]!='@')
261     return false;
262   std::string tmp=s.substr(1,len-2);
263   return tmp.find_first_not_of(NUMBERS,0,10)==std::string::npos;
264 }
265
266 /*!
267  * No assumption here check that the following regexp is followed [' ']*[0:9]*[' ']*:?[' ']*[0:9]*[' ']*
268  */
269 bool SPythonParser::isElementInParenthesisMatching(const std::string& s, std::string& result, bool& isNumber)
270 {
271   isNumber=false;
272   std::ostringstream ret;
273   std::size_t pos1=s.find_first_not_of(' ');
274   if(pos1==std::string::npos)
275     return false;
276   std::size_t pos2=s.find_first_not_of(NUMBERS,pos1,10);
277   std::string elt1=s.substr(pos1,pos2-pos1);
278   ret << elt1;
279   std::size_t pos3=s.find_first_not_of(' ',pos2);
280   if(pos3==std::string::npos)
281     {//'56'
282       isNumber=true;
283       result=ret.str();
284       return true;
285     }
286   if(s[pos3]!=':')
287     return false;
288   ret << ':';
289   pos3++;
290   std::size_t pos4=s.find_first_not_of(' ',pos3);
291   if(pos4==std::string::npos)
292     {// '56:'
293       result=ret.str();
294       return true;
295     }
296   std::size_t pos5=s.find_first_not_of(NUMBERS,pos4,10);
297   if(pos5==pos4)//an another caracter found after : !
298     return false;
299   std::string elt2;
300   if(pos5==std::string::npos)
301     {
302       elt2=s.substr(pos4,std::string::npos);
303       ret << elt2;
304       result=ret.str();
305       return true;
306     }
307   elt2=s.substr(pos4,pos5-pos4);
308   ret << elt2;
309   result=ret.str();
310   std::size_t pos6=s.find_first_not_of(' ',pos5);
311   if(pos6==pos5)//an another caracter found after 2nd elt !
312     return false;
313   return pos6==std::string::npos;
314 }
315
316 std::string SPythonParser::substitute(const std::vector<SPythonParser>& v) const
317 {
318   std::string ret=_content_py;
319   replaceFromCompacted(ret,v);
320   return ret;
321 }
322
323 std::string SPythonParser::replaceByCompacted(const std::string& s, int parLev, int id) const
324 {
325   std::string scpy(s);
326   int curLev=0;
327   std::size_t i=0;
328   std::size_t len=s.length();
329   std::size_t begin=0,end=0;
330   for(i=0;i<len;i++)
331     {
332       switch(s[i])
333         {
334         case '(':
335           ++curLev;
336           break;
337         case ')':
338           curLev--;
339           break;
340         }
341       if(curLev==parLev)
342         {
343           if(i!=0)
344             {
345               begin=s.find_last_of("(+-*/^",i-1,6);
346               begin=begin!=std::string::npos?begin+1:0;
347             }
348           else
349             begin=0;
350           end=s.find_first_of(')',i+1);
351           end=end!=std::string::npos?end-begin+1:std::string::npos;
352           break;
353         }
354     }
355   std::ostringstream oss,oss1;
356   oss << '@' << id << '@';
357   return scpy.replace(begin,end,oss.str());
358 }
359
360 std::string SPythonParser::getRepr(const std::vector<SPythonParser>& v) const
361 {
362   std::string ret(_pred.getRepr());
363   ret+='(';
364   ret+=_content_py;
365   ret+=')';
366   replaceFromCompacted(ret,v);
367   return ret;
368 }
369
370 void SPythonParser::replaceFromCompacted(std::string& ret,const std::vector<SPythonParser>& v)
371 {
372   std::size_t pos=ret.find_first_of('@',0);
373   std::size_t pos2;
374   while(pos!=std::string::npos)
375     {
376       pos2=ret.find_first_of('@',pos+1);
377       if(pos2==std::string::npos)
378         throw INTERP_KERNEL::Exception("Internal Error occurs !");
379       std::string tmp=ret.substr(pos+1,pos2-pos-1);
380       std::istringstream iss(tmp);
381       int id;
382       iss >> id;
383       std::string tmp2=v[id].getRepr(v);
384       ret.replace(pos,pos2-pos+1,tmp2);
385       pos=ret.find_first_of('@',pos2+1+tmp2.size()-tmp.size()-2);
386     }
387 }
388
389
390 SPythonPredParser::SPythonPredParser():_type(EMPTY_TYPE)
391 {
392 }
393
394 /*!
395  * This method analyses _pred attribute to deduce type of returned param.
396  */
397 void SPythonPredParser::assign(const std::string& s, PyObject *glob, PyObject *loc)
398 {
399   _var.clear();
400   _method.clear();
401   if(s.empty()) { _type=IDENTITY_TYPE; return ; }
402   std::size_t p=s.find_last_of('.');
403   if(p==std::string::npos)
404     _var=s;
405   else
406     {
407       _var=s.substr(0,p);
408       _method=s.substr(p+1);
409     }
410   if(_method.empty())
411     {
412       int type=getTypeOfVar(_var,glob,loc);
413       if(type!=FUNC_TYPE)
414         {
415           if(type==FIELDDB_TYPE)
416             _type=FIELDDB_TYPE;
417           else
418             _type=UNKNOWN_TYPE;
419         }
420       else
421         _type=IDENTITY_TYPE;
422     }
423   else
424     {
425       int type=getTypeOfVar(_var,glob,loc);
426       if(type==FIELDDB_TYPE)
427         {//To improve in case that some FieldDB swigged method return a different type than FieldDB
428           _type=FIELDDB_TYPE;
429         }
430       else
431         _type=UNKNOWN_TYPE;
432     }
433 }
434
435 bool SPythonPredParser::empty() const
436 {
437   return _var.empty() && _method.empty();
438 }
439
440 TypeOfEntity SPythonPredParser::getTypeOfVar(const std::string& var, PyObject *glob, PyObject *loc)
441 {
442   static const char TMPVAR[]="tmpvvr37911022";
443   std::ostringstream oss; oss << TMPVAR << "=isinstance(" << var << "," << FIELD_TYPE_STR << ")";
444   PyObject *res=PyRun_String(oss.str().c_str(),Py_single_input,glob,loc);
445   if(res==0)
446     return UNKNOWN_TYPE;
447   if(PyDict_GetItemString(glob,TMPVAR)==Py_True)
448     return FIELDDB_TYPE;
449   oss.str(std::string(TMPVAR));
450   oss << TMPVAR << "=type(" << var << ").__name__";
451   PyRun_String(oss.str().c_str(),Py_single_input,glob,loc);
452   PyObject *p=PyDict_GetItemString(glob,TMPVAR);
453   const char *type=PyString_AS_STRING(p);
454   std::string typecpp=std::string(type);
455   if(typecpp=="function")
456     return FUNC_TYPE;
457   if(typecpp=="int")
458     return INT_TYPE;
459   if(typecpp=="float")
460     return FLOAT_TYPE;
461   return UNKNOWN_TYPE;
462 }
463
464 std::string SPythonPredParser::getRepr() const
465 {
466   if(_method.empty())
467       return _var;
468   else
469     {
470       std::string tmp(_var);
471       tmp+='.';
472       return tmp+_method;
473     }
474 }
475
476 SPythonParserHL::SPythonParserHL(PyObject *glob, PyObject *loc):_glob(glob),_loc(loc)
477 {
478 }
479
480 bool SPythonParserHL::parse(const std::string& s, std::string& result)
481 {
482   std::vector<std::string> ps=splitBetweenEqualChar(s);
483   TypeOfEntity type;
484   if(ps.empty())
485     return false;
486   if(ps.size()==1)
487     return parseWithoutEqual(ps[0],type,result);
488   result.clear();
489   if(!parseWithoutEqual(ps.back(),type,result))
490     return false;
491   for(int n=ps.size()-1;n!=0;n--)
492     {
493       std::string leftRes;
494       TypeOfEntity typeLeft;
495       if(parseWithoutEqual(ps[n-1],typeLeft,leftRes))
496           {
497             if(typeLeft==FIELDDB_TYPE)
498               result=leftRes+".assign("+result+")";
499             else
500               result=leftRes+"="+result;
501           }
502       else
503         result=ps[n-1]+"="+result;
504     }
505   return true;
506 }
507
508 bool SPythonParserHL::parseWithoutEqual(const std::string& s, TypeOfEntity& type, std::string& result)
509 {
510   if(s.empty())
511     return false;
512   std::string sst(s);
513   std::vector<int> v=SPythonParser::levOfParenthesis(sst);
514   std::size_t levOfPar=v.size();
515   std::vector<SPythonParser> allSubs;
516   int k=0;
517   for(std::size_t i=levOfPar;i!=0;i--)
518     for(int j=0;j<v[i-1];j++,k++)
519       {
520         SPythonParser subs;
521         if(!subs.parseWithoutEqual(sst,i-1,_glob,_loc,allSubs))
522           return false;
523         if(i!=1)
524           sst=subs.replaceByCompacted(sst,i-1,k);
525         allSubs.push_back(subs);
526       }
527   result=allSubs.back().substitute(allSubs);
528   type=allSubs.back().getType();
529   return true;
530 }
531
532 std::vector<std::string> SPythonParserHL::splitBetweenEqualChar(const std::string &s)
533 {
534   std::size_t p=0,p2,p3;
535   std::vector<std::string> ret;
536   while(p!=std::string::npos)
537     {
538       p2=s.find_first_of('=',p);
539       p3=p2!=std::string::npos?p2-p:p2;
540       std::string tmp=s.substr(p,p3);
541       ret.push_back(tmp);
542       p=p2!=std::string::npos?p2+1:p2;
543     }
544   return ret;
545 }