1 // Copyright (C) 2007-2024 CEA, EDF
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.
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/ or email : webmaster.salome@opencascade.com
19 // Author : Anthony Geay (CEA/DEN)
21 #include "SPythonParser.hxx"
23 #include "InterpKernelException.hxx"
28 using namespace MEDCoupling;
30 const char SPythonPredParser::FIELD_TYPE_STR[]="MEDCalculatorDBField";
32 const char SPythonParser::NUMBERS[]="0123456789";
34 #if PY_VERSION_HEX < 0x03050000
36 Py_EncodeLocale(const wchar_t *text, size_t *error_pos)
38 return _Py_wchar2char(text, error_pos);
42 SPythonParser::SPythonParser():_type(EMPTY_TYPE)
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.
51 bool SPythonParser::parseWithoutEqual(const std::string& s, int parLev, PyObject *glob, PyObject *loc, std::vector<SPythonParser>& alreadyParsed)
53 keepSelectedLevOfPar(s,parLev,glob,loc);
54 TypeOfEntity ty=_pred.getType();
67 if(isParenthesisMatching(_content,tmp))
73 if(isAlreadyControledParenthesis(_content))
82 _type=analyzeType(glob,loc,alreadyParsed);
89 return _type==FIELDDB_TYPE || _type==INT_TYPE || _type==FLOAT_TYPE;
92 void SPythonParser::keepSelectedLevOfPar(const std::string& s, int parLev, PyObject *glob, PyObject *loc)
96 std::size_t len=s.length();
97 //bool found=false; // todo: unused
103 _pred.assign(pred,glob,loc);
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; // todo: unused
126 _content=_content.substr(1,_content.length()-1);
127 _content_py=_content;
131 _pred.assign(pred,glob,loc);
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);
142 * Size of returned vector is depth of parenthesis.
143 * Foreach level number of parenthesis groups.
145 std::vector<int> SPythonParser::levOfParenthesis(const std::string& s)
147 std::vector<int> ret(1);
149 std::size_t curLev=0;
150 std::size_t len=s.length();
151 for(std::size_t i=0;i<len;i++)
156 if(ret.size()<=++curLev)
170 * Analyzes regarding _content attribute the type of value.
172 TypeOfEntity SPythonParser::analyzeType(PyObject *glob, PyObject *loc, const std::vector<SPythonParser>& alreadyParsed)
174 static const int OPS_SEP_LGTH=5;
175 static const char OPS_SEP[OPS_SEP_LGTH+1]="+-*/^";
178 std::string content=_content;//.substr(1,_content.length()-2);
181 while(p!=std::string::npos)
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;
194 TypeOfEntity SPythonParser::getTypeOfElt(const std::string& elt, PyObject *glob, PyObject *loc, const std::vector<SPythonParser>& alreadyParsed) const
196 std::size_t sz=elt.length();
199 if(elt[0]=='@' && elt[sz-1]=='@')
201 std::string tmp=elt.substr(1,sz-2);
203 std::istringstream iss(tmp); iss >> id;
204 return alreadyParsed[id].getType();
207 return SPythonPredParser::getTypeOfVar(elt,glob,loc);
210 TypeOfEntity SPythonParser::combineType(TypeOfEntity t1, TypeOfEntity t2) const
216 if(t1==UNKNOWN_TYPE || t2==UNKNOWN_TYPE)
218 if((t1==INT_TYPE && t2==FLOAT_TYPE) || (t2==INT_TYPE && t1==FLOAT_TYPE))
220 if((t1==INT_TYPE && t2==FIELDDB_TYPE) || (t2==INT_TYPE && t1==FIELDDB_TYPE))
222 if((t1==FLOAT_TYPE && t2==FIELDDB_TYPE) || (t2==FLOAT_TYPE && t1==FIELDDB_TYPE))
228 * This method makes the assumption that s as a minimum length of size 2 with first char == '(' and the last ')'.
230 bool SPythonParser::isParenthesisMatching(const std::string& w, std::string& res)
232 std::ostringstream 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;
238 for(int i=0;i<nbOfParams;i++)
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);
245 if(!isElementInParenthesisMatching(w1,w1out,isNum))
254 pos=pos2!=std::string::npos?pos2+1:std::string::npos;
262 * This method checks that s match the following regexp : "@[0123456789]?@"
264 bool SPythonParser::isAlreadyControledParenthesis(const std::string& s)
266 std::size_t len=s.length();
269 if(s[0]!='@' || s[len-1]!='@')
271 std::string tmp=s.substr(1,len-2);
272 return tmp.find_first_not_of(NUMBERS,0,10)==std::string::npos;
276 * No assumption here check that the following regexp is followed [' ']*[0:9]*[' ']*:?[' ']*[0:9]*[' ']*
278 bool SPythonParser::isElementInParenthesisMatching(const std::string& s, std::string& result, bool& isNumber)
281 std::ostringstream ret;
282 std::size_t pos1=s.find_first_not_of(' ');
283 if(pos1==std::string::npos)
285 std::size_t pos2=s.find_first_not_of(NUMBERS,pos1,10);
286 std::string elt1=s.substr(pos1,pos2-pos1);
288 std::size_t pos3=s.find_first_not_of(' ',pos2);
289 if(pos3==std::string::npos)
299 std::size_t pos4=s.find_first_not_of(' ',pos3);
300 if(pos4==std::string::npos)
305 std::size_t pos5=s.find_first_not_of(NUMBERS,pos4,10);
306 if(pos5==pos4)//an another character found after : !
309 if(pos5==std::string::npos)
311 elt2=s.substr(pos4,std::string::npos);
316 elt2=s.substr(pos4,pos5-pos4);
319 std::size_t pos6=s.find_first_not_of(' ',pos5);
320 if(pos6==pos5)//an another character found after 2nd elt !
322 return pos6==std::string::npos;
325 std::string SPythonParser::substitute(const std::vector<SPythonParser>& v) const
327 std::string ret=_content_py;
328 replaceFromCompacted(ret,v);
332 std::string SPythonParser::replaceByCompacted(const std::string& s, int parLev, int id) const
337 std::size_t len=s.length();
338 std::size_t begin=0,end=0;
354 begin=s.find_last_of("(+-*/^",i-1,6);
355 begin=begin!=std::string::npos?begin+1:0;
359 end=s.find_first_of(')',i+1);
360 end=end!=std::string::npos?end-begin+1:std::string::npos;
364 std::ostringstream oss,oss1;
365 oss << '@' << id << '@';
366 return scpy.replace(begin,end,oss.str());
369 std::string SPythonParser::getRepr(const std::vector<SPythonParser>& v) const
371 std::string ret(_pred.getRepr());
375 replaceFromCompacted(ret,v);
379 void SPythonParser::replaceFromCompacted(std::string& ret,const std::vector<SPythonParser>& v)
381 std::size_t pos=ret.find_first_of('@',0);
383 while(pos!=std::string::npos)
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);
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);
399 SPythonPredParser::SPythonPredParser():_type(EMPTY_TYPE)
404 * This method analyses _pred attribute to deduce type of returned param.
406 void SPythonPredParser::assign(const std::string& s, PyObject *glob, PyObject *loc)
410 if(s.empty()) { _type=IDENTITY_TYPE; return ; }
411 std::size_t p=s.find_last_of('.');
412 if(p==std::string::npos)
417 _method=s.substr(p+1);
421 int type=getTypeOfVar(_var,glob,loc);
424 if(type==FIELDDB_TYPE)
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
444 bool SPythonPredParser::empty() const
446 return _var.empty() && _method.empty();
449 TypeOfEntity SPythonPredParser::getTypeOfVar(const std::string& var, PyObject *glob, PyObject *loc)
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);
456 if(PyDict_GetItemString(glob,TMPVAR)==Py_True)
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 #if PY_VERSION_HEX < 0x030c0000 // See PEP-623
463 const char *type=Py_EncodeLocale(PyUnicode_AS_UNICODE(p), NULL);
465 const char *type=Py_EncodeLocale(PyUnicode_AsWideCharString(p,NULL), NULL);
467 std::string typecpp=std::string(type);
468 if(typecpp=="function")
477 std::string SPythonPredParser::getRepr() const
483 std::string tmp(_var);
489 SPythonParserHL::SPythonParserHL(PyObject *glob, PyObject *loc):_glob(glob),_loc(loc)
493 bool SPythonParserHL::parse(const std::string& s, std::string& result)
495 std::vector<std::string> ps=splitBetweenEqualChar(s);
500 return parseWithoutEqual(ps[0],type,result);
502 if(!parseWithoutEqual(ps.back(),type,result))
504 for(int n=ps.size()-1;n!=0;n--)
507 TypeOfEntity typeLeft;
508 if(parseWithoutEqual(ps[n-1],typeLeft,leftRes))
510 if(typeLeft==FIELDDB_TYPE)
511 result=leftRes+".assign("+result+")";
513 result=leftRes+"="+result;
516 result=ps[n-1]+"="+result;
521 bool SPythonParserHL::parseWithoutEqual(const std::string& s, TypeOfEntity& type, std::string& result)
526 std::vector<int> v=SPythonParser::levOfParenthesis(sst);
527 std::size_t levOfPar=v.size();
528 std::vector<SPythonParser> allSubs;
530 for(std::size_t i=levOfPar;i!=0;i--)
531 for(int j=0;j<v[i-1];j++,k++)
534 if(!subs.parseWithoutEqual(sst,i-1,_glob,_loc,allSubs))
537 sst=subs.replaceByCompacted(sst,i-1,k);
538 allSubs.push_back(subs);
540 result=allSubs.back().substitute(allSubs);
541 type=allSubs.back().getType();
545 std::vector<std::string> SPythonParserHL::splitBetweenEqualChar(const std::string &s)
547 std::size_t p=0,p2,p3;
548 std::vector<std::string> ret;
549 while(p!=std::string::npos)
551 p2=s.find_first_of('=',p);
552 p3=p2!=std::string::npos?p2-p:p2;
553 std::string tmp=s.substr(p,p3);
555 p=p2!=std::string::npos?p2+1:p2;