Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Parser.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2009 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "Parser.h"
21 
22 #if MSC_VERSION
23 #pragma warning(disable:4786)
24 #endif
25 
26 
27 //-------------------------------------------------
28 // Macros
29 //-------------------------------------------------
30 
31 #define REGULAR_MAX_LENGTH 10
32 #define START_DYNAMIC '<'
33 #define END_DYNAMIC '>'
34 #define START_OPTIONAL '['
35 #define END_OPTIONAL ']'
36 #define REGULAR_EXPRESSION '$'
37 
38 // use GetDouble and type-cast it to <<type>>
39 #define FUNC_IMPL_CAST_GETDOUBLE(func_name,type) \
40 bool CParserValue::func_name(type &ret) \
41 { \
42  double d; \
43  if (GetDouble(d)) \
44  return ret = (type)d, true; \
45  else \
46  return false; \
47 }
48 
49 // Function-implementation creator for GetArg%type% that will call
50 // Get%type% from the CParserValue
51 // func_name must belong to CParserFile
52 #define FUNC_IMPL_GETARG(func_name, get_name, type) \
53 bool CParserLine::func_name(size_t arg, type &ret) \
54 { \
55  if (GetArgCount() <= arg) \
56  return false; \
57  return m_Arguments[arg].get_name(ret); \
58 }
59 
60 //-------------------------------------------------
61 // Function definitions
62 //-------------------------------------------------
63 
64 static bool _IsStrictNameChar(const char& c);
65 static bool _IsValueChar(const char& c);
66 
67 
68 // Functions used for checking a character if it belongs to a value
69 // or not
70 
71 // Checks ident
72 static bool _IsStrictNameChar(const char& c)
73 {
74  return ((c >= 'a' && c <= 'z') ||
75  (c >= 'A' && c <= 'Z') ||
76  (c >= '0' && c <= '9'));
77 }
78 
79 // Checks value
80 static bool _IsValueChar(const char& c)
81 {
82  return ((c >= 'a' && c <= 'z') ||
83  (c >= 'A' && c <= 'Z') ||
84  (c >= '0' && c <= '9') ||
85  c=='.' || c=='_');
86 }
87 
88 // CParserValue
89 // ---------------------------------------------------------------------| Class
90 
92 {
93 }
94 
96 {
97 }
98 
99 // Parse the std::string in Value to different types
100 
101 // bool
102 bool CParserValue::GetBool(bool &ret)
103 {
104  // TODO Raj Add or remove some? I can make it all lowercase
105  // first so True and TRUE also works, or you could just
106  // add them too
107 
108  // true
109  if (m_String == "true" ||
110  m_String == "on" ||
111  m_String == "1" ||
112  m_String == "yes")
113  {
114  ret = true;
115  return true;
116  }
117  else
118  // false
119  if (m_String == "false" ||
120  m_String == "off" ||
121  m_String == "0" ||
122  m_String == "no")
123  {
124  ret = false;
125  return true;
126  }
127 
128  // point only erroneous runs reach
129  return false;
130 }
131 
132 // double
133 bool CParserValue::GetDouble(double &ret)
134 {
135  // locals
136  double TempRet = 0.0;
137  size_t Size = m_String.size();
138  size_t i;
139  bool AtLeastOne = false; // Checked if at least one of the loops
140  // run, otherwise "." would parse OK
141  size_t DecimalPos;
142  bool Negative = false; // "-" is found
143 
144  // Check if '-' is found
145  if (m_String[0]=='-')
146  {
147  Negative = true;
148  }
149 
150  // find decimal position
151  DecimalPos = m_String.find(".");
152  if (DecimalPos == std::string::npos)
153  DecimalPos = Size;
154 
155  // Iterate left of the decimal sign
156  //
157  for (i=(Negative?1:0); i < DecimalPos; ++i)
158  {
159  // Set AtLeastOne to true
160  AtLeastOne = true;
161 
162  // Check if a digit is found
163  if (m_String[i] >= '0' && m_String[i] <= '9')
164  {
165  double exp = (DecimalPos-i-1); // disambiguate pow() argument type
166  TempRet += (m_String[i]-'0')*pow(10.0, exp);
167  }
168  else
169  {
170  // parse error!
171  return false;
172  }
173  }
174 
175  // Iterate right of the decimal sign
176  //
177  for (i=DecimalPos+1; i < Size; ++i)
178  {
179  // Set AtLeastOne to true
180  AtLeastOne = true;
181 
182  // Check if a digit is found
183  if (m_String[i] >= '0' && m_String[i] <= '9')
184  {
185  double exp = (int)(DecimalPos-i); // disambiguate pow() argument type
186  TempRet += (m_String[i]-'0')*pow(10.0,exp);
187  }
188  // It will accept and ending f, like 1.0f
189  else if (!(i==Size-1 && m_String[i] == 'f'))
190  {
191  // parse error!
192  return false;
193  }
194  }
195 
196  if (!AtLeastOne)return false;
197 
198  // Set the reference to the temp value and return success
199  ret = (Negative?-TempRet:TempRet);
200  return true;
201 }
202 
203 // std::string - only return m_String, can't fail
204 bool CParserValue::GetString(std::string &ret)
205 {
206  ret = m_String;
207  return true;
208 }
209 
210 bool CParserValue::GetString( CStr& ret )
211 {
212  ret = m_String;
213  return true;
214 }
215 
216 // These macros include the IMPLEMENTATION of the
217 // the function in the macro argument for CParserValue
218 // They use GetDouble, and then type-cast it
219 FUNC_IMPL_CAST_GETDOUBLE(GetFloat, float)
220 FUNC_IMPL_CAST_GETDOUBLE(GetChar, char)
221 FUNC_IMPL_CAST_GETDOUBLE(GetShort, short)
222 FUNC_IMPL_CAST_GETDOUBLE(GetInt, int)
223 FUNC_IMPL_CAST_GETDOUBLE(GetLong, long)
224 FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedShort, unsigned short)
225 FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedInt, unsigned int)
226 FUNC_IMPL_CAST_GETDOUBLE(GetUnsignedLong, unsigned long)
227 
228 // CParserTaskTypeNode
229 // ---------------------------------------------------------------------| Class
230 
231 CParserTaskTypeNode::CParserTaskTypeNode() : m_ParentNode(NULL), m_NextNode(NULL), m_AltNode(NULL)
232 {
233 }
234 
236 {
237 }
238 
239 // Delete all children
241 {
242  // Delete nodes if applicable
243  if (m_NextNode)
244  {
246  delete m_NextNode;
247  m_NextNode = NULL;
248  }
249 
250  if (m_AltNode)
251  {
253  delete m_AltNode;
254  m_AltNode = NULL;
255  }
256 }
257 
258 // CParserTaskType
259 // ---------------------------------------------------------------------| Class
260 
262 {
263 }
264 
266 {
267 }
268 
269 // Delete m_BaseNode and all of its children
271 {
272  if (m_BaseNode)
273  {
275  delete m_BaseNode;
276  m_BaseNode = NULL;
277  }
278 }
279 
280 // CParserLine
281 // ---------------------------------------------------------------------| Class
282 
284 {
285 }
286 
288 {
289 
290  ClearArguments();
291 }
292 
293 // Clear arguments (deleting m_Memory
295 {
296  // Now we can actually clear it
297  m_Arguments.clear();
298  return true;
299 }
300 
301 // Implementation of CParserFile::GetArg*
302 // it just checks if argument isn't out of range, and
303 // then it uses the the respective function in CParserValue
304 FUNC_IMPL_GETARG(GetArgString, GetString, std::string)
305 FUNC_IMPL_GETARG(GetArgBool, GetBool, bool)
306 FUNC_IMPL_GETARG(GetArgChar, GetChar, char)
307 FUNC_IMPL_GETARG(GetArgShort, GetShort, short)
308 FUNC_IMPL_GETARG(GetArgInt, GetInt, int)
309 FUNC_IMPL_GETARG(GetArgLong, GetLong, long)
310 FUNC_IMPL_GETARG(GetArgUnsignedShort, GetUnsignedShort, unsigned short)
311 FUNC_IMPL_GETARG(GetArgUnsignedInt, GetUnsignedInt, unsigned int)
312 FUNC_IMPL_GETARG(GetArgUnsignedLong, GetUnsignedLong, unsigned long)
313 FUNC_IMPL_GETARG(GetArgFloat, GetFloat, float)
314 FUNC_IMPL_GETARG(GetArgDouble, GetDouble, double)
315 
316 // ParseString
317 // ------------------------------------------------------------------| Function
318 // Parses a line, dividing it into segments according to defined semantics
319 // each segment is called an argument and represents a value of some kind
320 // ex:
321 // variable = 5 => variable, =, 5
322 // CallFunc(4,2) => CallFunc, 4, 2
323 
324 // TODO Gee: Make Parser use CStr.
325 bool CParserLine::ParseString(const CParser& Parser, const std::string &strLine)
326 {
327  // Don't process empty std::string
328  if (strLine == std::string())
329  {
330  m_ParseOK = false; // Empty lines should never be inputted by CParserFile
331  return m_ParseOK;
332  }
333 
334  // Locals
335  bool Extract=false;
336  size_t ExtractPos=0;
337  char Buffer[256];
338  char Letter[] = {'\0','\0'}; // Letter as std::string
339  std::vector<std::string> Segments;
340  std::string strSub;
341  size_t i;
342 
343  // Set result to false, then if a match is found, turn it true
344  m_ParseOK = false;
345 
346  /*
347  TODO Gee Remove this comment!
348  // Remove C++-styled comments!
349  // * * * *
350  int pos = strLine.find("//");
351  if (pos != std::string::npos)
352  strLine = strLine.substr(0,pos);
353  */
354 
355  // Divide std::string into smaller std::vectors, separators are unusual signs
356  // * * * *
357 
358  for (i=0; i<strLine.size(); ++i)
359  {
360  // Check if we're trying to use some kind of type
361  if (!Extract)
362  {
363  // GET NAME, IDENT, FLOAT
364  if (_IsValueChar(strLine[i]))
365  {
366  Extract = true;
367  ExtractPos = i;
368  memset((void*)Buffer, '\0', sizeof(char)*256);
369  }
370  else
371  // GET STRING BETWEEN QUOTES
372  if (strLine[i] == '\"')
373  {
374  // Extract a std::string, search for another "
375  size_t pos = strLine.find("\"", i+1);
376 
377  // If matching can't be found,
378  // the parsing will fail!
379  if (pos == std::string::npos)
380  {
381  // TODO Gee - Output in logfile
382  m_ParseOK = false;
383  return m_ParseOK;
384  }
385 
386  // Get substring
387  // Add a " to indicate this is a "..." std::string
388  // and can't be used as name
389  strSub = "\"" + strLine.substr(i+1, pos-i-1);
390 
391  // Input substring!
392  Segments.push_back(strSub);
393 
394  // Now we can't skip everything that we
395  // we just read in, update i
396  i = pos;
397  }
398  // GET JUST ONE CHARACTER
399  else
400  {
401  // Input just the one char
402  Letter[0] = strLine[i];
403  Segments.push_back(Letter);
404  continue;
405  }
406  }
407  // Extract whatever
408  if (Extract)
409  {
410  // No type names are longer than 256 characters
411  if (i-ExtractPos >= 256)
412  {
413  Extract=false;
414  }
415  else
416  {
417  // Extract std::string after $ !
418  // break whenever we reach a sign that's not A-Z a-z
419  if (_IsValueChar(strLine[i]))
420  {
421  Buffer[i-ExtractPos] = strLine[i];
422  }
423  else
424  {
425  // Extraction is finished
426  Extract=false;
427 
428  // strLine[i] is now a non-regular character
429  // we'll jump back one step so that will
430  // be included next loop
431  --i;
432  }
433 
434  // Check if std::string is complete
435  if (i == strLine.size()-1)
436  Extract=false;
437  }
438 
439  // If extraction was finished! Input Buffer
440  if (Extract == false)
441  {
442  Segments.push_back( std::string(Buffer) );
443  }
444  }
445  }
446 
447  // Try to find an appropriate CParserTaskType in parser
448  // * * * *
449 
450  // Locals
451  size_t Progress; // progress in Segments index
452  size_t Lane=0; // Have many alternative routes we are in
453  bool Match; // If a task-type match has been found
454  // The std::vector of these three represents the different lanes
455  // LastValidProgress[1] takes you back to lane 1 and how
456  // the variables was set at that point
457  std::vector<size_t> LastValidProgress; // When diving into a dynamic argument store store
458  // the last valid so you can go back to it
459  std::vector<size_t> LastValidArgCount; // If an alternative route turns out to fail, we
460  // need to know the amount of arguments on the last
461  // valid position, so we can remove them.
462  std::vector<bool> LastValidMatch; // Match at that point
463  bool BlockAltNode = false; // If this turns true, the alternative route
464  // tested was not a success, and the settings
465  // should be set back in order to test the
466  // next node instead
467  bool LookNoFurther = false; // If this turns true, it means a definite match has been
468  // found and no further looking is required
469  CParserTaskTypeNode *CurNode=NULL; // Current node on task type
470  CParserTaskTypeNode *PrevNode=NULL; // Last node
471  UNUSED2(PrevNode);
472 
473  // Iterate all different TaskType, and all TaskTypeElements...
474  // start from left and go to the right (prog), comparing
475  // the similarities. If enough
476  // similarities are found, then we can declare progress as
477  // that type and exit loop
478  std::vector<CParserTaskType>::const_iterator cit_tt;
479  for (cit_tt = Parser.m_TaskTypes.begin();
480  cit_tt != Parser.m_TaskTypes.end();
481  ++cit_tt)
482  {
483  // Reset for this task-type
484  Match = true;
485  Progress = 0;
486  ClearArguments(); // Previous failed can have filled this
487  CurNode = cit_tt->m_BaseNode; // Start at base node
488  LookNoFurther = false;
489  BlockAltNode = false;
490 
491  // This loop will go through the whole tree until
492  // it reaches an empty node
493  while (!LookNoFurther)
494  {
495  // Check if node is valid
496  // otherwise try to jump back to parent
497  if (CurNode->m_NextNode == NULL &&
498  (CurNode->m_AltNode == NULL || BlockAltNode))
499  {
500  // Jump back to valid
501  //CurNode = PrevNode;
502 
503  // If the node has no children, it's the last, and we're
504  // on lane 0, i.e. with no
505  if (CurNode->m_NextNode == NULL &&
506  (CurNode->m_AltNode == NULL || BlockAltNode) &&
507  Lane == 0)
508  {
509  if (Progress != Segments.size())
510  Match = false;
511 
512  break;
513  }
514  else
515  {
516  CParserTaskTypeNode *OldNode = NULL;
517 
518  // Go back to regular route!
519  for(;;)
520  {
521  OldNode = CurNode;
522  CurNode = CurNode->m_ParentNode;
523 
524  if (CurNode->m_AltNode == OldNode)
525  {
526  break;
527  }
528  }
529 
530  // If the alternative route isn't repeatable, block alternative route for
531  // next loop cycle
532  if (!CurNode->m_AltNodeRepeatable)
533  BlockAltNode = true;
534 
535  // Decrease lane
536  --Lane;
537  }
538  }
539 
540  // Check alternative route
541  // * * * *
542 
543  // Check if alternative route is present
544  // note, if an alternative node has already failed
545  // we don't want to force usage of the next node
546  // therefore BlockAltNode has to be false
547  if (!BlockAltNode)
548  {
549  if (CurNode->m_AltNode)
550  {
551  // Alternative route found, we'll test this first!
552  CurNode = CurNode->m_AltNode;
553 
554  // --- New node is set!
555 
556  // Make sure they are large enough
557  if (LastValidProgress.size() < Lane+1)
558  {
559  LastValidProgress.resize(Lane+1);
560  LastValidMatch.resize(Lane+1);
561  LastValidArgCount.resize(Lane+1);
562  }
563 
564  // Store last valid progress
565  LastValidProgress[Lane] = Progress;
566  LastValidMatch[Lane] = Match;
567  LastValidArgCount[Lane] = (int)m_Arguments.size();
568 
569  ++Lane;
570 
571  continue;
572  }
573  }
574  else BlockAltNode = false;
575 
576  // Now check Regular Next Node
577  // * * * *
578 
579  if (CurNode->m_NextNode)
580  {
581  // Important!
582  // Change working node to the next node!
583  CurNode = CurNode->m_NextNode;
584 
585  // --- New node is set!
586 
587  // CHECK IF LETTER IS CORRECT
588  if (CurNode->m_Letter != '\0')
589  {
590  // OPTIONALLY SKIP BLANK SPACES
591  if (CurNode->m_Letter == '_')
592  {
593  // Find blank space if any!
594  // and jump to the next non-blankspace
595  if (Progress < Segments.size())
596  {
597  // Skip blankspaces AND tabs!
598  while (Segments[Progress].size()==1 &&
599  (Segments[Progress][0]==' ' ||
600  Segments[Progress][0]=='\t'))
601  {
602  ++Progress;
603 
604  // Check length
605  if (Progress >= Segments.size())
606  {
607  break;
608  }
609  }
610  }
611  }
612  else
613  // CHECK LETTER IF IT'S CORRECT
614  {
615  if (Progress < Segments.size())
616  {
617  // This should be 1-Letter long
618  if (Segments[Progress].size() != 1)
619  Match = false;
620 
621  // Check Letter
622  if (CurNode->m_Letter != Segments[Progress][0])
623  Match = false;
624 
625  // Update progress
626  ++Progress;
627  }
628  else Match = false;
629  }
630  }
631 
632  else if (CurNode->m_Type == typeNull)
633  {
634  // Match without doing anything (leaving Match==true)
635  }
636 
637  // CHECK NAME
638  else
639  {
640  // Do this first, because we wan't to
641  // avoid the Progress and Segments.size()
642  // check for this
643  if (CurNode->m_Type == typeAddArg)
644  {
645  // Input argument
646  CParserValue value;
647  value.m_String = CurNode->m_String;
648  m_Arguments.push_back(value);
649  }
650  else
651  {
652  // Alright! An ident or const has been acquired, if we
653  // can't find any or if the std::string has run out
654  // that invalidates the match
655 
656  // String end?
657  if (Progress >= Segments.size())
658  {
659  Match = false;
660  }
661  else
662  {
663  // Store argument in CParserValue!
664  CParserValue value;
665 
666  switch(CurNode->m_Type)
667  {
668  case typeIdent:
669  // Check if this really is a std::string
670  if (!_IsStrictNameChar(Segments[Progress][0]))
671  {
672  Match = false;
673  break;
674  }
675 
676  // Same as at typeValue, but this time
677  // we won't allow strings like "this", just
678  // like this
679  if (Segments[Progress][0] == '\"')
680  Match = false;
681  else
682  value.m_String = Segments[Progress];
683 
684  // Input argument!
685  m_Arguments.push_back(value);
686 
687  ++Progress;
688  break;
689  case typeValue:
690  // Check if this really is a std::string
691  if (!_IsValueChar(Segments[Progress][0]) &&
692  Segments[Progress][0] != '\"')
693  {
694  Match = false;
695  break;
696  }
697 
698  // Check if initial is -> " <-, because that means it was
699  // stored from a "String like these with quotes"
700  // We don't want to store that prefix
701  if (Segments[Progress][0] == '\"')
702  value.m_String = Segments[Progress].substr(1, Segments[Progress].size()-1);
703  else
704  value.m_String = Segments[Progress];
705 
706  // Input argument!
707  m_Arguments.push_back(value);
708 
709  ++Progress;
710  break;
711  case typeRest:
712  // This is a comment, don't store it.
713  // Now BREAK EVERYTHING !
714  // We're done, we found our match and let's get out
715  LookNoFurther = true;
716  //Match = true;
717  break;
718  default:
719  break;
720  }
721  }
722  }
723  }
724  }
725 
726  // Check if match is false! if it is, try returning to last valid state
727  if (!Match && Lane > 0)
728  {
729  // The alternative route failed
730  BlockAltNode = true;
731 
732  CParserTaskTypeNode *OldNode = NULL;
733 
734  // Go back to regular route!
735  for(;;)
736  {
737  OldNode = CurNode;
738  CurNode = CurNode->m_ParentNode;
739 
740  if (CurNode->m_AltNode == OldNode)
741  {
742  break;
743  }
744  }
745 
746  // Decrease lane
747  --Lane;
748 
749  // Restore values as before
750  Progress = LastValidProgress[Lane];
751  Match = LastValidMatch[Lane];
752  m_Arguments.resize(LastValidArgCount[Lane]);
753  }
754  }
755 
756  // Check if it was a match!
757  if (Match)
758  {
759  // Before we celebrate the match, let's check if whole
760  // of Segments has been used, and if so we have to
761  // nullify the match
762  //if (Progress == Segments.size())
763  {
764  // *** REPORT MATCH WAS FOUND ***
765  m_TaskTypeName = cit_tt->m_Name;
766  m_ParseOK = true;
767  break;
768  }
769  }
770  }
771 
772  // POST-PROCESSING OF ARGUMENTS!
773 
774  // if _minus is found as argument, remove it and add "-" to the one after that
775  // note, it's easier if std::iterator isn't used here
776 
777  for (i=1; i<GetArgCount(); ++i)
778  {
779  if (m_Arguments[i-1].m_String == "_minus")
780  {
781  // Add "-" to next, and remove "_minus"
782  m_Arguments[i].m_String = "-" + m_Arguments[i].m_String;
783  m_Arguments.erase(m_Arguments.begin() + (i-1));
784  }
785  }
786 
787  return m_ParseOK;
788 }
789 
790 // CParser
791 // ---------------------------------------------------------------------| Class
792 
793 // ctor
795 {
796 }
797 
798 // dtor
800 {
801  // Delete all task type trees
802  std::vector<CParserTaskType>::iterator itTT;
803  for (itTT = m_TaskTypes.begin();
804  itTT != m_TaskTypes.end();
805  ++itTT)
806  {
807  itTT->DeleteTree();
808  }
809 }
810 
811 // InputTaskType
812 // ------------------------------------------------------------------| Function
813 // A task-type is a std::string representing the acquired syntax when parsing
814 // This function converts that std::string into a binary tree, making it easier
815 // and faster to later parse.
816 bool CParser::InputTaskType(const std::string& strName, const std::string& strSyntax)
817 {
818  // Locals
819  CParserTaskType TaskType; // Object we acquire to create
820  char Buffer[REGULAR_MAX_LENGTH];
821  size_t ExtractPos = 0;
822  bool Extract = false;
823  bool Error = false;
824  size_t i;
825 
826  // Construct base node
827  TaskType.m_BaseNode = new CParserTaskTypeNode();
828 
829  // Working node
830  CParserTaskTypeNode *CurNode = TaskType.m_BaseNode;
831 
832  // Loop through the std::string and construct nodes in the binary tree
833  // when applicable
834  for (i=0; i<strSyntax.size(); ++i)
835  {
836  // Extract is a variable that is true when we want to extract
837  // parts that is longer than one character.
838  if (!Extract)
839  {
840  if (strSyntax[i] == REGULAR_EXPRESSION)
841  {
842  Extract = true;
843  ExtractPos = i+1; // Skip $
844  memset((void*)Buffer, '\0', sizeof(char)*REGULAR_MAX_LENGTH);
845 
846  // We don't want to extract '$' so we'll just continue to next loop run
847  continue;
848  }
849  else
850  if (strSyntax[i] == START_DYNAMIC || strSyntax[i] == START_OPTIONAL)
851  {
852 
853  // Slight hack: because things are stored in a binary tree,
854  // it can't handle "<[a][b]>" -- the <...> node has only
855  // one slot for an optional [...] node. To avoid this problem,
856  // typeNull nodes are used to indicate things that always
857  // succeed but can have altnodes attached:
858 /*
859  parent parent
860  \ ===> \
861  <...> ===> <...> <-- CurNode
862  / \ / \
863  / \ / \
864 next [a] Null [a] <-- added NewNode
865  / \
866  next [b]
867 */
868  if (CurNode->m_AltNode)
869  {
870 
871  // Rearrange the tree, as shown above:
872 
873  // Create NewNode
874  CParserTaskTypeNode* NewNode = new CParserTaskTypeNode();
875  NewNode->m_ParentNode = CurNode;
876  NewNode->m_Letter = '\0';
877  NewNode->m_Type = typeNull;
878 
879  // Copy 'next' into NewNode
880  NewNode->m_NextNode = CurNode->m_NextNode;
881  // Replace 'next' with NewNode inside CurNode
882  CurNode->m_NextNode = NewNode;
883 
884  // Update CurNode, so the following code inserts into [b]
885  CurNode = NewNode;
886  }
887 
888  // Dive into the alternative node
889  ENSURE(! CurNode->m_AltNode);
890  CurNode->m_AltNode = new CParserTaskTypeNode();
891  CurNode->m_AltNode->m_ParentNode = CurNode;
892 
893  // It's repeatable
894  CurNode->m_AltNodeRepeatable = bool(strSyntax[i]==START_DYNAMIC);
895 
896  // Set to current
897  CurNode = CurNode->m_AltNode;
898 
899  // We're done extracting for now
900  continue;
901  }
902  else
903  if (strSyntax[i] == END_DYNAMIC || strSyntax[i] == END_OPTIONAL)
904  {
905  CParserTaskTypeNode *OldNode = NULL;
906 
907  // Jump out of this alternative route
908  for(;;)
909  {
910  OldNode = CurNode;
911  CurNode = CurNode->m_ParentNode;
912 
913  if (CurNode == NULL)
914  {
915  // Syntax error
916  Error = true;
917  break;
918  }
919 
920  if (CurNode->m_AltNode == OldNode)
921  {
922  break;
923  }
924  }
925 
926  if (Error)break;
927  }
928  else
929  {
930  // Check if this is the first input
931  // CONSTRUCT A CHILD NODE
932  ENSURE(! CurNode->m_NextNode);
933  CurNode->m_NextNode = new CParserTaskTypeNode();
934  CurNode->m_NextNode->m_ParentNode = CurNode;
935 
936  // Jump into !
937  CurNode = CurNode->m_NextNode;
938 
939  // Set CurNode
940  CurNode->m_Letter = strSyntax[i];
941  }
942  }
943 
944  // Extact
945  if (Extract)
946  {
947  // No type names are longer than REGULAR_MAX_LENGTH characters
948  if (i-ExtractPos >= REGULAR_MAX_LENGTH)
949  {
950  Extract=false;
951  }
952  else
953  {
954  // Extract std::string after $ !
955  // break whenever we reach a sign that's not A-Z a-z
956  if (_IsStrictNameChar(strSyntax[i]))
957  {
958  Buffer[i-ExtractPos] = strSyntax[i];
959  }
960  else
961  {
962  // Extraction is finished
963  Extract=false;
964 
965  // strLine[i] is now a non-regular character
966  // we'll jump back one step so that will
967  // be included next loop
968  --i;
969  }
970 
971  // Check if std::string is complete
972  if (i == strSyntax.size()-1)
973  Extract=false;
974  }
975 
976  // If extraction was finished! Input Buffer
977  if (Extract == false)
978  {
979  // CONSTRUCT A CHILD NODE
980  ENSURE(! CurNode->m_NextNode);
981  CurNode->m_NextNode = new CParserTaskTypeNode();
982  CurNode->m_NextNode->m_ParentNode = CurNode;
983 
984  // Jump into !
985  CurNode = CurNode->m_NextNode;
986 
987  CurNode->m_Letter = '\0';
988 
989  std::string str = std::string(Buffer);
990 
991  // Check value and set up CurNode accordingly
992  if (str == "value") CurNode->m_Type = typeValue;
993  else
994  if (str == "ident") CurNode->m_Type = typeIdent;
995  else
996  if (str == "rest") CurNode->m_Type = typeRest;
997  else
998  if (str == "rbracket") CurNode->m_Letter = '>';
999  else
1000  if (str == "lbracket") CurNode->m_Letter = '<';
1001  else
1002  if (str == "rbrace") CurNode->m_Letter = ']';
1003  else
1004  if (str == "lbrace") CurNode->m_Letter = '[';
1005  else
1006  if (str == "dollar") CurNode->m_Letter = '$';
1007  else
1008  if (str == "arg")
1009  {
1010  // After $arg, you need a parenthesis, within that parenthesis is a std::string
1011  // that will be added as an argument when it's passed through
1012 
1013  CurNode->m_Type = typeAddArg;
1014 
1015  // Check length, it has to have place for at least a '(' and ')' after $arg
1016  if (ExtractPos+4 >= strSyntax.size())
1017  {
1018  Error = true;
1019  break;
1020  }
1021 
1022  // We want to extract what's inside the parenthesis after $arg
1023  // if it's not there at all, it's a syntactical error
1024  if (strSyntax[ExtractPos+3] != '(')
1025  {
1026  Error = true;
1027  break;
1028  }
1029 
1030  // Now try finding the second ')'
1031  size_t Pos = strSyntax.find(")", ExtractPos+5);
1032 
1033  // Check if ')' exists at all
1034  if (Pos == std::string::npos)
1035  {
1036  Error = true;
1037  break;
1038  }
1039 
1040  // Now extract std::string within ( and )
1041  CurNode->m_String = strSyntax.substr(ExtractPos+4, Pos-(ExtractPos+4));
1042 
1043  // Now update position
1044  i = (int)Pos;
1045  }
1046  else
1047  {
1048  // TODO Gee report in log too
1049  Error = true;
1050  }
1051  }
1052  }
1053  }
1054 
1055  // Input TaskType
1056  if (!Error)
1057  {
1058  // Set name and input
1059  TaskType.m_Name = strName;
1060  m_TaskTypes.push_back(TaskType);
1061  }
1062 
1063  return !Error;
1064 }
1065 
1066 
1068 
1069 CParser& CParserCache::Get(const char* str)
1070 {
1071  CacheType::iterator it = m_Cached.find(str);
1072  if (it == m_Cached.end())
1073  {
1074  CParser* parser = new CParser;
1075  parser->InputTaskType("", str);
1076  m_Cached[str] = parser;
1077  return *parser;
1078  }
1079  else
1080  {
1081  CParser* parser = it->second;
1082  return *parser;
1083  }
1084 }
std::string m_Name
Definition: Parser.h:181
std::deque< CParserValue > m_Arguments
Definition: Parser.h:193
#define FUNC_IMPL_GETARG(func_name, get_name, type)
Definition: Parser.cpp:52
std::string m_String
Definition: Parser.h:112
void DeleteTree()
Definition: Parser.cpp:270
bool GetString(std::string &ret)
Definition: Parser.cpp:204
bool GetBool(bool &ret)
Definition: Parser.cpp:102
#define FUNC_IMPL_CAST_GETDOUBLE(func_name, type)
Definition: Parser.cpp:39
CParserValue()
Definition: Parser.cpp:91
#define END_DYNAMIC
Definition: Parser.cpp:33
#define REGULAR_MAX_LENGTH
Definition: Parser.cpp:31
~CParser()
Definition: Parser.cpp:799
static CacheType m_Cached
Definition: Parser.h:263
#define REGULAR_EXPRESSION
Definition: Parser.cpp:36
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
bool GetDouble(double &ret)
Definition: Parser.cpp:133
static CStrInternInternals * GetString(const char *str, size_t len)
Definition: CStrIntern.cpp:95
#define START_OPTIONAL
Definition: Parser.cpp:34
bool ClearArguments()
Definition: Parser.cpp:294
std::map< T, P >::iterator iterator
Definition: Parser.h:254
CParserTaskTypeNode * m_AltNode
Definition: Parser.h:159
~CParserLine()
Definition: Parser.cpp:287
std::string m_String
Definition: Parser.h:142
CParserTaskTypeNode * m_NextNode
Definition: Parser.h:149
void DeleteChildren()
Definition: Parser.cpp:240
std::vector< CParserTaskType > m_TaskTypes
Definition: Parser.h:231
#define START_DYNAMIC
Definition: Parser.cpp:32
_ParserValueType m_Type
Definition: Parser.h:141
static CParser & Get(const char *str)
Definition: Parser.cpp:1069
CParser()
Definition: Parser.cpp:794
bool InputTaskType(const std::string &strName, const std::string &strSyntax)
Definition: Parser.cpp:816
CParserTaskTypeNode * m_BaseNode
Definition: Parser.h:178
static bool _IsValueChar(const char &c)
Definition: Parser.cpp:80
CParserTaskTypeNode * m_ParentNode
Definition: Parser.h:146
bool m_AltNodeRepeatable
Definition: Parser.h:153
~CParserValue()
Definition: Parser.cpp:95
static bool _IsStrictNameChar(const char &c)
Definition: Parser.cpp:72
#define END_OPTIONAL
Definition: Parser.cpp:35