]> git.sven.stormbind.net Git - sven/vym.git/blob - parser.cpp
Import Upstream version 2.6.11
[sven/vym.git] / parser.cpp
1 #include "parser.h"
2
3 #include <QDebug>
4 #include <QRegExp>
5 #include <iostream>
6
7 #include "command.h"
8 #include "treeitem.h"
9
10 extern QList <Command*> modelCommands;
11
12 Parser::Parser()
13 {
14     initParser();
15 }
16
17 void Parser::initParser()
18 {
19     initAtom();
20     current=-1;
21 }
22
23 void Parser::initAtom()
24 {
25     atom="";
26     com="";
27     paramList.clear();
28     errLevel=NoError;
29     errDescription="";
30     errMessage="";
31 }
32
33 void Parser::parseAtom (QString s)
34 {
35     initAtom();
36     QRegExp re;
37     int pos;
38
39     // Strip WS at beginning
40     while (s.length() > 0 && (
41                s.at(0) == '\n' ||
42                s.at(0) == '\r' ||
43                s.at(0) == ' ') )
44         s = s.mid(1);
45     if (s.length() == 0)
46         return;
47
48     // Get command
49     re.setPattern ("^(\\w*)");
50     pos=re.indexIn (s);
51     if (pos>=0)
52         com=re.cap(1);
53
54     // Get parameters
55     paramList.clear();
56     QString t;
57     int leftParenthesis;
58     int rightParenthesis;
59     if (!nextParenthesisContents(s, leftParenthesis, rightParenthesis, t)) return;
60
61     paramList = findParameters(t);
62     atom = s;
63 }
64
65 QString Parser::getAtom()
66 {
67     return atom;
68 }
69
70 QString Parser::getCommand()
71 {
72     return com;
73 }
74
75 QStringList Parser::getParameters()
76 {
77     return paramList;
78 }
79
80 int Parser::parCount()
81 {
82     return paramList.count();
83 }
84
85 QString Parser::errorMessage()
86 {
87     QString l;
88     switch (errLevel)
89     {
90         case NoError: l="No Error"; break;
91         case Warning: l="Warning"; break;
92         case Aborted: l="Aborted"; break;
93         default: l="";
94     }
95     return QString ("Error Level: '%1'  Command: '%2' Description: '%3'")
96         .arg(l).arg(com).arg(errDescription);
97 }
98
99 QString Parser::errorDescription()
100 {
101     return errDescription;
102 }
103
104 ErrorLevel Parser::errorLevel()
105 {
106     return errLevel;
107 }
108
109 void Parser::setError(ErrorLevel level, const QString &description)
110 {
111     errDescription=description;
112     errLevel=level;
113 }
114
115 bool Parser::checkParameters(TreeItem *selti)
116 {
117     foreach (Command *c, modelCommands)
118     {
119         if (c->getName() == com)
120         {
121             // Check type of selection
122             if (selti)
123             {
124                 bool ok;
125                 ok=false;
126                 TreeItem::Type st=selti->getType();
127                 Command::SelectionType ct=c->getSelectionType();
128                 if (ct==Command::TreeItem || ct==Command::BranchOrImage)
129                 {
130                     if (st==TreeItem::MapCenter ||
131                         st==TreeItem::Branch ||
132                         st==TreeItem::XLink ||
133                         st==TreeItem::Image ) 
134                         ok=true;
135                 } else if ( ct==Command::BranchOrImage )
136                 {
137                     if (st==TreeItem::MapCenter ||
138                         st==TreeItem::Branch ||
139                         st==TreeItem::Image ) 
140                         ok=true;
141                 } else if ( ct==Command::Branch || ct==Command::BranchLike)
142                 {
143
144                     if (st == TreeItem::MapCenter ||
145                         st == TreeItem::Branch )
146                         ok=true;
147                 } else if ( ct==Command::Image )            
148                 {
149                     if (st==TreeItem::Image )
150                         ok=true;
151                 } else if ( ct==Command::Any)       
152                 {
153                     ok=true;
154                 } else if ( ct==Command::XLink)     
155                 {
156                     if (st==TreeItem::XLink)
157                         ok=true;
158                 } else
159                     qWarning()<<"Parser::checkParameters  Unknown selection type";
160                 if (!ok)
161                 {
162                     setError (Aborted, "Selection does not match command");
163                     return false;
164                 }
165             }
166
167             // Check for number of parameters
168             int optPars=0;
169             for (int i=0; i < c->parCount(); i++ )
170                 if (c->isParOptional(i) ) optPars++;
171             if (paramList.count() < (c->parCount() - optPars) ||
172                 paramList.count() > c->parCount() )
173             {
174                 QString expected;
175                 if (optPars>0)
176                     expected=QString("%1..%2").arg(c->parCount()-optPars).arg(c->parCount() );
177                 else 
178                     expected=QString().setNum(c->parCount());
179                 setError (
180                     Aborted,
181                     QString("Wrong number of parameters: Expected %1, but found %2").arg(expected).arg(paramList.count()));
182                 return false;
183             }
184
185             // Check types of parameters
186             bool ok;
187             for (int i=0; i < paramList.count(); i++ )
188             {   
189                 switch (c->getParType(i) )
190                 {
191                     case Command::String:
192                         parString (ok,i);
193                         if (!ok) 
194                         {
195                             // Convert to string implicitly
196                             paramList[i]='"' + paramList[i] + '"';
197                             ok=true;
198                         }
199                         break;
200                     case Command::Int:  
201                         parInt (ok,i);
202                         break;
203                     case Command::Double:       
204                         parDouble (ok,i);
205                         break;
206                     case Command::Color:        
207                         parColor (ok,i);
208                         break;
209                     case Command::Bool: 
210                         parBool (ok,i);
211                         break;
212                     default: ok=false;  
213                 }
214                 if (!ok)
215                 {
216                     setError (
217                         Aborted, 
218                         QString("Parameter %1 has wrong type").arg(i));
219                     return false;
220                 }
221             }
222             resetError();
223             return true;
224         }    
225     } 
226     setError (Aborted,"Unknown command");
227     return false;
228 }
229
230 void Parser::resetError ()
231 {
232     errMessage="";
233     errDescription="";
234     errLevel=NoError;
235 }
236
237 bool Parser::checkParCount (const int &expected)
238 {
239     if (paramList.count()!=expected)
240     {
241         setError (
242             Aborted,
243             QString("Wrong number of parameters: Expected %1, but found %2").arg(expected).arg(paramList.count()));
244         return false;
245     } 
246     return true;    
247 }
248
249 bool Parser::checkParIsInt(const int &index)
250 {
251     bool ok;
252     if (index > paramList.count())
253     {
254         setError (
255             Aborted,
256             QString("Parameter index %1 is outside of parameter list").arg(index));
257         return false;
258     } else
259     {
260         paramList[index].toInt (&ok, 10);
261         if (!ok)
262         {
263             setError (
264                 Aborted,
265                 QString("Parameter %1 is not an integer").arg(index));
266             return false;
267         } 
268     }   
269     return true;
270 }
271
272 bool Parser::checkParIsDouble(const int &index)
273 {
274     bool ok;
275     if (index > paramList.count())
276     {
277         setError (
278             Aborted,
279             QString("Parameter index %1 is outside of parameter list").arg(index));
280         return false;
281     } else
282     {
283         paramList[index].toDouble (&ok);
284         if (!ok)
285         {
286             setError (
287                 Aborted,
288                 QString("Parameter %1 is not double").arg(index));
289             return false;
290         } 
291     }   
292     return true;
293 }
294
295 int Parser::parInt (bool &ok,const uint &index)
296 {
297     if (checkParIsInt (index))
298         return paramList[index].toInt (&ok, 10);
299     ok=false;
300     return 0;
301 }
302
303 QString Parser::parString (bool &ok, const int &index)
304 {
305     // return the string at index, this could be also stored in
306     // a variable later
307     
308     // Try to find out if string boundaries are "" or ''
309     QRegExp rx;
310     int pos = paramList[index].indexOf("\"");
311     int n   = paramList[index].indexOf("'");
312
313     if ( n < 0 && pos < 0 )
314     {
315         // Neither " nor ' found
316         ok = false;
317         return "";
318     } else if ( pos >= 0 && n < 0) 
319         // Found ", but no ' 
320         rx.setPattern("\"(.*)\"");
321     else if ( n >= 0 && pos < 0) 
322         // Found ', but no "
323         rx.setPattern("'(.*)'");
324     else if ( pos > n )
325         // "" is within ''
326         rx.setPattern("'(.*)'");
327     else
328         // '' is within ""
329         rx.setPattern("\"(.*)\"");
330
331
332
333     QString r;
334     pos=rx.indexIn (paramList[index]);
335     if (pos>=0)
336     {
337         r = rx.cap (1);
338         ok = true;
339     } else    
340     {
341         r = "";
342         ok = false;
343     }
344     return r;
345 }
346
347 bool Parser::parBool (bool &ok,const int &index)
348 {
349     // return the bool at index, this could be also stored in
350     // a variable later
351     QString r;
352     ok=true;
353     QString p=paramList[index];
354     if (p=="true" || p=="1")
355         return true;
356     else if (p=="false" || p=="0")
357         return false;
358     ok=false;
359     return ok;
360 }
361
362 QColor Parser::parColor(bool &ok,const int &index)
363 {
364     // return the QColor at index
365     ok = false;
366     QString r;
367     QColor c;
368
369     // testscipts use single quotes, convert them first
370     paramList[index].replace ("'", "\"");
371     QRegExp re("\"(.*)\"");
372     int pos = re.indexIn (paramList[index]);
373     if (pos >= 0)
374     {
375         r = re.cap (1);
376         c.setNamedColor(r);
377         ok = c.isValid();
378     }   
379     return c;
380 }
381
382 double Parser::parDouble (bool &ok,const int &index)
383 {
384     if (checkParIsDouble (index))
385         return paramList[index].toDouble (&ok);
386     ok=false;
387     return 0;
388 }
389
390 void Parser::setScript(const QString &s)
391 {
392     script=s;
393 }   
394
395 QString Parser::getScript()
396 {
397     return script;
398 }   
399
400 void Parser::execute()
401 {
402     current=0;
403 }   
404
405 bool Parser::next() //FIXME-3 parser does not detect missing closing " or '("foo" ()'
406 {
407     int start=current;
408     if (current<0) execute();
409     if (current+1>=script.length()) return false;
410
411     bool inQuote=false;
412     QChar bnd;
413
414     while (true)
415     {
416         // Check if we are inside a string
417         if (script.at(current) == '\"')
418         {
419             if (inQuote)
420             {
421                 if (script.at(current) == bnd)
422                     inQuote=false;
423             } else
424             {
425                 inQuote = true;
426                 bnd = '\"';
427             }
428         } else if (script.at(current) == '\'' )
429         {
430             if (inQuote)
431             {
432                 if (script.at(current) == bnd)
433                     inQuote=false;
434             } else
435             {
436                 inQuote = true;
437                 bnd = '\'';
438             }
439         }
440
441         // Check for EOL
442         if (script.at(current) == '\n')
443         {
444             if (current+1 < script.length())
445             {
446                 current++;
447                 if (script.at(current) == '\r')
448                     current++;
449             }
450         }
451
452         // Check if we are in a comment
453         if (!inQuote && script.at(current)=='#')
454         {
455             while (script.at(current)!='\n')
456             {
457                 current++;
458                 if (script.at(current) == '\r')
459                     current++;
460                 if (current+1>=script.length())
461                     return false;
462             }
463             start=current;
464         }
465
466         // Check for end of atom
467         if (!inQuote && script.at(current)==';')
468         {
469             parseAtom(script.mid(start,current-start) );
470             current++;
471             return true;
472         }
473
474         // Check for end of script
475         if (current+1>=script.length() )
476         {
477             if (inQuote)
478             {
479                 setError (Aborted,"Runaway string");
480                 return false;
481             } else
482             {
483                 atom=script.mid(start);
484                 return true;
485             }
486         }
487         current++;
488     }
489 }   
490
491 QStringList Parser::getCommands() 
492 {
493     QStringList list;
494     foreach (Command *c, modelCommands)
495         list.append (c->getName() );
496     return list;        
497 }
498
499 QStringList Parser::findParameters(const QString &s)
500 {
501     QStringList ret;
502     int left = 0;
503     bool inquote = false;
504
505     // Try to find out if string boundaries are "" or ''
506     QString bnd;
507     int pos = s.indexOf("\"");
508     int n   = s.indexOf("'");
509
510     if ( n < 0 && pos < 0 )
511     {
512         // Neither " nor ' found, ignore later
513         bnd = "\"";
514     } else if ( pos >= 0 && n < 0) 
515         // Found ", but no ' 
516         bnd = "\"";
517     else if ( n >= 0 && pos < 0) 
518         // Found ', but no "
519         bnd = "'";
520     else if ( pos > n )
521         // "" is within ''
522         bnd = "'";
523     else
524         // '' is within ""
525         bnd = "\"";
526
527     pos = 0;
528     while (pos < s.length())
529     {
530         if (s.at(pos) == bnd ) 
531         {
532             if (inquote)
533                 inquote = false;
534             else    
535                 inquote = true;
536         }
537         if (s.at(pos) == ',' && !inquote)
538         {
539             ret << s.mid(left, pos - left );
540             left = pos + 1;
541         }
542         pos++;
543     }
544     if (left > 0) 
545         ret << s.mid(left, pos - left );
546     else
547         if (!s.isEmpty()) ret << s;
548     return ret;
549 }
550
551 bool Parser::nextParenthesisContents(
552         const QString &s, 
553         int &leftParenthesis, 
554         int &rightParenthesis, 
555         QString &contents)
556 {
557     int pos = 0;
558     int leftP  = -1;
559     int rightP = -1;
560     int openParenthesis = 0;
561     bool inQuote = false;
562     QChar bnd;
563     while (pos < s.length())
564     {
565         // Check if we are inside a string
566         if (s.at(current) == '\"')
567         {
568             if (inQuote)
569             {
570                 if (s.at(current) == bnd)
571                     inQuote=false;
572             } else
573             {
574                 inQuote = true;
575                 bnd = '\"';
576             }
577         } else if (s.at(current) == '\'' )
578         {
579             if (inQuote)
580             {
581                 if (s.at(current) == bnd)
582                     inQuote=false;
583             } else
584             {
585                 inQuote = true;
586                 bnd = '\'';
587             }
588         }
589
590         if (s.at(pos) == '(' && !inQuote)
591         {
592             openParenthesis++;
593             if (openParenthesis == 1) leftP=pos;
594         }
595
596         if (s.at(pos) == ')' && !inQuote)
597         {
598             openParenthesis--;
599             if (openParenthesis == 0) rightP=pos;
600         }
601
602         if (openParenthesis < 0) 
603         {
604             setError(Aborted, "Error, too many closing parenthesis!");
605             return false;
606         }
607
608         pos++;
609     }
610
611     if (leftP < 0)
612     {
613         setError(Aborted, "Error: No left parenthesis found");
614         return false;
615     }
616
617     if (rightP < 0) 
618     {
619         setError(Aborted, "Error: No right parenthesis found");
620         return false;
621     }
622
623     contents = s.mid(leftP+1, rightP - leftP - 1);
624     pos = leftParenthesis;
625     leftParenthesis  = leftP;
626     rightParenthesis = rightP;
627
628     return true;
629 }
630