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