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