Salome HOME
Copyright update 2020
[modules/med.git] / src / MEDCalculator / Swig / SPythonInterpreter.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 "SPythonInterpreter.hxx"
22 #include "SPythonParser.hxx"
23
24 #include <vector>
25 #include <sstream>
26 #include <algorithm>
27
28 using namespace MEDCoupling;
29
30 const char *SPythonInterpreter::INDENT_TOKEN[]={"def","class","for","if","while","try","except"};
31
32 const char SPythonInterpreter::NUMBERS[]="0123456789";
33
34 SPythonInterpreter::SPythonInterpreter(PyObject *globals, PyObject *locals):_indent_must_change(false),_glob(globals),_loc(locals)
35 {
36   _indent_pos.insert(0);
37 }
38
39 void SPythonInterpreter::initialize()
40 {
41   _indent_pos.clear();
42   _indent_pos.insert(0);
43   _indent_must_change=false;
44   _cmd.clear();
45 }
46
47 bool SPythonInterpreter::run(const char *str, bool& isSPython)
48 {
49   isSPython=false;
50   std::string s(str);
51   if(s.empty())
52     {
53       finishSession();
54       _indent_must_change=false;
55       return true;
56     }
57   std::size_t pos=s.find_first_not_of(' ');
58   if(pos==std::string::npos)
59     return false;
60   if(s[pos]=='#')
61     return false;
62   if(!checkIndentCoherency(s,pos))
63     return true;
64   if(!isIndenter(s,pos))
65     {
66       _indent_must_change=false;
67       if(pos==0)
68         {
69           if(isSPythonExpression(s))
70             {
71               isSPython=true;
72               return true;
73             }
74           else
75             {
76               _cmd+=s;
77               _cmd+="\n";
78               return finishSession();
79             }
80         }
81       _cmd+=s;
82       _cmd+="\n";
83       return false;
84     }
85   else
86     {
87       _indent_must_change=true;
88       _cmd+=s;
89       _cmd+="\n";
90       return false;
91     }
92 }
93
94 bool SPythonInterpreter::finishSession()
95 {
96   PyObject *res=0;
97   if(_cmd.empty())
98     return false;
99   res=PyRun_String(_cmd.c_str(),Py_file_input,_glob,_loc);
100   _cmd.clear();
101   checkPythonInterp(res);
102   //_indent_pos.clear();
103   //_indent_pos.insert(0);
104   return true;
105 }
106
107 void SPythonInterpreter::checkPythonInterp(PyObject *r)
108 {
109   if(!r)
110     PyErr_Print();
111 }
112
113 bool SPythonInterpreter::checkIndentCoherency(const std::string& s, std::size_t p)
114 {
115   if(!_indent_must_change)
116     {
117       if(_indent_pos.find(p)!=_indent_pos.end())
118         {
119           std::set<int>::iterator it=_indent_pos.begin();
120           bool found=false;
121           for(;it!=_indent_pos.end() && !found;it++)
122             if(*it==(int)p)
123               found=true;
124           if(found)
125             _indent_pos.erase(it,_indent_pos.end());
126           return true;
127         }
128       else
129         {//let python doing the job of error msg !
130           _cmd+=s;
131           finishSession();
132           _indent_pos.clear();
133           return true;
134         }
135     }
136   else
137     {
138       if((int)p>*_indent_pos.rbegin())
139         {
140           _indent_pos.insert(p);
141           return true;
142         }
143       else
144         {//let python doing the job of error msg !
145           _cmd+=s;
146           finishSession();
147           _indent_pos.clear();
148           return true;
149         }
150     }
151 }
152
153 /*!
154  * looks that s contains at the begin of non empty char a python keyword that forces indentation of next line.
155  */
156 bool SPythonInterpreter::isIndenter(const std::string& s, std::size_t p)
157 {
158   std::string s1=s.substr(p);
159   for(int i=0;i<NB_OF_INDENT;i++)
160     {
161       std::string elt(INDENT_TOKEN[i]);
162       std::size_t sz=elt.length();
163       if(s1.length()>=sz)
164         if(s1.substr(0,sz)==elt)
165           return true;
166     }
167   return false;
168 }
169
170 std::string SPythonInterpreter::strip(const std::string& s)
171 {
172   std::size_t sz=s.length();
173   std::size_t n1=std::count(s.c_str(),s.c_str()+sz,' ');
174   std::size_t n2=std::count(s.c_str(),s.c_str()+sz,'\n');
175   std::size_t n3=std::count(s.c_str(),s.c_str()+sz,'\t');
176   std::string ret(sz-n1-n2-n3,'$');
177   std::size_t i=0,j=0;
178   for(;i!=std::string::npos;)
179     {
180       i=s.find_first_not_of(" \n\t",i);
181       if(i!=std::string::npos)
182         ret[j++]=s[i++];
183     }
184   return ret;
185 }
186
187 bool SPythonInterpreter::isSPythonExpression(const std::string& s)
188 {
189   std::string w(s);
190   if(w.find("import ")!=std::string::npos)
191     return false;
192   if(w.find_first_of('@')!=std::string::npos)
193     return false;
194   if(w.find("del ")!=std::string::npos)
195     return false;
196   const char PRINT[]="print(";
197   const char ENDPRINT[]=")";
198   bool isPrint=w.find(PRINT)!=std::string::npos;
199   isPrint &= w.find(ENDPRINT)!=std::string::npos;
200   if(isPrint)
201     {
202       std::size_t p=w.find(PRINT);
203       w=w.substr(p+sizeof(PRINT)-sizeof(ENDPRINT)-1);
204     }
205   std::string result;
206   if(!isSPythonExpressionLev1(w,result))
207     return false;
208   if(isPrint)
209     result=std::string(PRINT)+result;
210   _cmd+=result+"\n";
211   finishSession();
212   return true;
213 }
214
215 bool SPythonInterpreter::isSPythonExpressionLev1(const std::string& s, std::string& result)
216 {
217   std::string sst=strip(s);
218   SPythonParserHL p(_glob,_loc);
219   if(!p.parse(sst,result))
220     return false;
221   return true;
222 }
223
224 bool SPythonInterpreter::isCandidateParenthesis(const std::string& s, std::size_t p1, std::size_t& n)
225 {
226   std::size_t p2=s.find_first_of(')',p1);
227   std::size_t p3=s.find_first_of('(',p1+1);
228   if(p2!=std::string::npos && (p3==std::string::npos || (p3!=std::string::npos && p2<p3) ))
229     {
230       n=p2-p1;
231       return true;
232     }
233   return false;
234 }