Advanced Strategic Command
textfileparser.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  textfileparser.cpp - description
3  -------------------
4  begin : Thu Jul 26 2001
5  copyright : (C) 2001 by Martin Bickel
6  email : bickel@asc-hq.org
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <vector>
19 #include <SDL_image.h>
20 #include <algorithm>
21 #include "ascstring.h"
22 #include "textfileparser.h"
23 #include "stringtokenizer.h"
24 #include "textfile_evaluation.h"
25 
26 
28 const char* TextFormatParser::operations[6] = { "=", "*=", "+=", "->", "->*", "-=" };
29 const char* TextFormatParser::whiteSpace = " \t";
30 
31 
32 
33 
35 {
36  Operator o = op;
37  if ( o == alias_all_resolved )
38  o = alias_all;
39 
41  return s;
42 }
43 
45 {
46  for ( iterator i = begin(); i != end(); i++ ) {
47  int id = (*i)->evalID();
48  if ( id > 0 ) {
49 
50  if ( identCache.find ( id ) != identCache.end() )
51  fatalError("Duplicate ID in text file group " + (*i)->typeName + ": " + (*i)->location + " and " + identCache.find ( id )->second->location + " both have ID " + strrr(id ) + "\n\nThis is NOT a bug in ASC, this is a conflict between two data files.");
52 
53  identCache[id] = *i;
54  }
55  }
56 }
57 
59 {
60  IdentCache::iterator f = identCache.find ( id );
61  if ( f != identCache.end() )
62  return &(*(f->second));
63  else
64  return NULL;
65 }
66 
67 
69 
70 
71 void TextPropertyGroup :: error ( const ASCString& msg, bool printInheritance )
72 {
73  ASCString message = "Error evaluating file " + location + "\n" + msg;
74  if ( printInheritance )
75  message += "\nThe inheritance is\n" + listInheritanceFilenames() ;
76 
77  fatalError ( message );
78 }
79 
80 void TextPropertyGroup :: print( int indent )
81 {
82  for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) {
83  for ( int m = 0; m<= indent; m++ )
84  displayLogMessage(10, " " );
85 
86  displayLogMessage(10, i->toString());
87  displayLogMessage(10, "\n");
88  }
89 
90  for ( Parents::iterator i = parents.begin(); i != parents.end(); i++ ) {
91  for ( int n = 0; n< indent; n++ )
92  printf(" " );
93  displayLogMessage(10, " is inheriting from " + (*i)->location + "\n" );
94  (*i)->print(indent+1 );
95  }
96 }
97 
98 
100 {
101  static list<TextPropertyGroup*> callStack;
102 
103  if ( !inheritanceBuild ) {
104  if ( std::find ( callStack.begin(), callStack.end(), this ) != callStack.end() )
105  error ( "endless inheritance loop detected: type " + typeName + "; ID " + strrr ( id ), false);
106 
107  callStack.push_back( this );
108 
109  PropertyReadingContainer prc ( typeName, this );
110  prc.addBool ( "abstract", abstract, false );
111  int iid;
112  prc.addInteger("ID", iid, 0 );
113 
114  if ( find ( typeName+".parent") != NULL ) {
115  typedef vector<int> ParentIDs;
116  ParentIDs parentIDs;
117  prc.addIntegerArray ( "parent", parentIDs );
118  for ( ParentIDs::iterator i = parentIDs.begin(); i != parentIDs.end(); i++ ) {
119  TextPropertyGroup* p = tpl.get ( *i );
120  if ( p ) {
121  parents.push_back ( p );
122  displayLogMessage( 10, ASCString(" entering parent with ID ") + strrr(*i) + " ("+p->location+")\n" );
123  p->buildInheritance( tpl );
124  displayLogMessage( 10, ASCString(" leaving parent with ID ") + strrr(*i) + "\n" );
125  } else
126  error ( location + " : no parent with ID " + strrr(*i) + " of type " + typeName + " could be found !" );
127  }
128  }
129 
131  /*
132  for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) {
133  if ( i->op != Entry::eq && i->op != Entry::alias_all && i->op != Entry::alias) {
134  Parents::iterator p = parents.begin();
135  while ( p != parents.end() ) {
136  i->parent = (*p)->find( i->propertyName );
137  if ( i->parent )
138  break;
139  p++;
140  }
141  if ( p == parents.end())
142  error ( "could not find a parent entry for " + typeName + " :: " + i->propertyName );
143  }
144  }
145  */
146 
147  resolveAllAlias();
148 
149  callStack.pop_back();
150  inheritanceBuild = true;
151  }
152 }
153 
155 {
156  ASCString s;
157  for ( Parents::iterator p = parents.begin(); p != parents.end(); p++ )
158  s += (*p)->listInheritanceFilenames();
159  s += location + "\n";
160  return s;
161 }
162 
164 {
165  int loop = 0;
166  typedef list<Entry*> Unresolved;
167  Unresolved unresolved;
168  do {
169  Entries additionalEntries;
170  int resolvedCounter = 0;
171  EntryPointerList toResolve;
172 
173  if ( loop == 0 ) {
174  for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) {
175  bool resolved = processAlias ( *i , additionalEntries, toResolve );
176  if ( !resolved )
177  unresolved.push_back ( &(*i) );
178  else
179  resolvedCounter++;
180  }
181  } else {
182  Unresolved newunresolved;
183  for ( Unresolved::iterator i = unresolved.begin(); i != unresolved.end(); i++ ) {
184  bool resolved = processAlias ( **i , additionalEntries, toResolve );
185  if ( !resolved )
186  newunresolved.push_back ( *i );
187  else
188  resolvedCounter++;
189  }
190  unresolved = newunresolved;
191  }
192 
193  for ( Entries::iterator i = additionalEntries.begin(); i != additionalEntries.end(); i++ )
194  // if ( find ( i->propertyName ) == NULL )
195  addEntry ( *i );
196 
197  for ( EntryPointerList::iterator i = toResolve.begin(); i != toResolve.end(); i++ )
198  (*i)->op = Entry::alias_all_resolved;
199 
200  if ( !resolvedCounter && !unresolved.empty() )
201  for ( Unresolved::iterator i = unresolved.begin(); i != unresolved.end(); i++ ) {
202  error ( "could not resolve the reference for " + typeName + " :: " + (*i)->propertyName + " with operand " + (*i)->value + "\nSee STDOUT for a dump of current item" );
203  print();
204  }
205 
206  loop++;
207  } while ( !unresolved.empty() );
208 }
209 
210 bool reallyVerbose = false;
211 
213 {
214  for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ )
215  if ( &(*i) == e )
216  return 0;
217 
218  for ( Parents::iterator i = parents.begin(); i != parents.end(); i++ ) {
219  int fg = (*i)->findGeneration( e );
220  if ( fg >= 0 )
221  return fg+1;
222  }
223  return -1;
224 }
225 
226 
227 bool TextPropertyGroup::processAlias( Entry& e, Entries& entriesToAdd, EntryPointerList& markAsResolved )
228 {
229  if ( e.op == Entry::alias_all ) {
230  ASCString ss = e.value;
231  ss.toLower();
232  ASCString::size_type pos;
233  do {
234  pos = ss.find ( " " );
235  if ( pos != ss.npos )
236  ss.erase(pos,1);
237  } while ( pos != ss.npos );
238 
239  ASCString s_without_dot = ss;
240  if ( ss.length() > 0 )
241  if ( ss[ss.length()-1] != '.' )
242  ss = ss + ".";
243 
244  if ( s_without_dot.length() > 0 )
245  if ( s_without_dot[s_without_dot.length()-1] == '.' )
246  s_without_dot.erase ( s_without_dot.length()-1 );
247 
248  ASCString newName = e.propertyName;
249  if ( newName.length() > 0 )
250  if ( newName[newName.length()-1] != '.' )
251  newName = newName + ".";
252 
253  int counter = 0;
254  Matches matches;
255 
256 
257  findMatches ( ss, s_without_dot, matches );
258 
259  for ( Matches::iterator i = matches.begin(); i != matches.end(); i++ )
260  if ( (*i)->op == Entry::alias_all ) {
261  displayLogMessage ( 9, " alias is pending " + ss + "* <- " + e.propertyName + " because " + (*i)->propertyName + " is unresolved\n" );
262  return false;
263  }
264 
265  for ( Matches::iterator i = matches.begin(); i != matches.end(); i++ )
266  if ( (*i)->op != Entry::alias_all_resolved ) {
267  Entry e = **i;
268  e.propertyName.replace ( 0, ss.length(), newName );
269  TextPropertyGroup::Entry* existent = find ( e.propertyName );
270  // if ( existent == NULL || findGeneration(*i) <= findGeneration( existent ) ) {
271  if ( existent == NULL || findGeneration( existent ) > 0 ) {
272  /* if ( existent && findGeneration( existent ) == 0 ) {
273  displayLogMessage(10, " overwriting entry " + existent->toString() + " because it belongs to an older generation\n" );
274  *existent = e;
275  } else */
276  entriesToAdd.push_back ( e );
277  displayLogMessage(10, " aliasing entry " + (*i)->toString() + " to " + e.propertyName + " \n" );
278  }
279  counter++;
280  } else
281  displayLogMessage(10, " skipping aliasing entry " + (*i)->toString() + " to " + e.propertyName + " because it is already resolved\n" );
282 
283  if ( !counter ) {
284  displayLogMessage ( 9, " could not successfully resolve alias " + ss + "* <- " + e.propertyName + "\n" );
285  return false;
286  } else {
287  markAsResolved.push_back ( &e );
288  displayLogMessage ( 9, " successfully resolved alias " + e.propertyName + " ->* " + ss + "\n" );
289  return true;
290  }
291  }
292 
293  if ( e.op == Entry::alias ) {
294  ASCString s = e.value;
295  s.toLower();
296  ASCString::size_type pos;
297  do {
298  pos = s.find ( " " );
299  if ( pos != s.npos )
300  s.erase(pos,1);
301  } while ( pos != s.npos );
302  Entry* alias = find ( s );
303  if ( !alias )
304  return false;
305  else {
306  e.value = alias->value;
307  e.op = alias->op;
308  e.parent = alias->parent;
309  return true;
310  }
311  }
312 
314  if ( e.op != Entry::eq && e.op != Entry::alias_all && e.op != Entry::alias && e.op != Entry::alias_all_resolved ) {
315  Parents::iterator p = parents.begin();
316  while ( p != parents.end() ) {
317  e.parent = (*p)->find( e.propertyName );
318  if ( e.parent )
319  break;
320  p++;
321  }
322  if ( p == parents.end())
323  return false;
324  else
325  return true;
326  }
327 
328 
329  return true;
330 }
331 
332 
334 {
335  PropertyReadingContainer prc ( typeName, this );
336  prc.addInteger ( "ID", id, 0 );
337  return id;
338 }
339 
340 
341 
342 
344 {
345  EntryCache::iterator i = entryCache.find ( n );
346  if ( i != entryCache.end() )
347  return i->second;
348  else {
349  for ( Parents::iterator p = parents.begin(); p != parents.end(); p++ ) {
350  TextPropertyGroup::Entry* ent = (*p)->find ( n );
351  if ( ent )
352  return ent;
353  }
354  return NULL;
355  }
356 }
357 
358 void TextPropertyGroup::findMatches( const ASCString& name, const ASCString& name_without_dot, Matches& matches )
359 {
360  for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ )
361  if ( i->propertyName.find ( name ) == 0 || i->propertyName == name_without_dot )
362  matches.push_back ( &(*i) );
363 
364  for ( Parents::iterator p = parents.begin(); p != parents.end(); p++ )
365  (*p)->findMatches ( name, name_without_dot, matches );
366 }
367 
368 
369 void TextPropertyGroup::addEntry( const Entry& entry )
370 {
371  entries.push_back ( entry );
372  entryCache[entry.propertyName] = &entries.back();
373 }
374 
375 
376 
378 
380 {
381  ASCString s;
382  int noCommentLines = 0;
383  int bracketsOpen = 0;
384  do {
385  ASCString t = stream->readString();
386  ASCString::size_type pos = t.find_first_not_of ( whiteSpace );
387  if ( pos != ASCString::npos ) {
388  if ( t[pos] != ';' ) {
389  noCommentLines++;
390  if ( !s.empty() )
391  s += " ";
392 
393  s += t.substr ( pos );
394 
395  if ( t.find ( "[" ) != t.npos )
396  bracketsOpen++;
397 
398  if ( t.find ( "]" ) != t.npos )
399  bracketsOpen--;
400  }
401  } else {
402  // empty line
403  if ( bracketsOpen )
404  s += "\n";
405  }
406 
407  } while ( noCommentLines == 0 || bracketsOpen > 0 );
408 
409  ASCString::size_type pos = s.find ( "[" );
410  if ( pos != s.npos )
411  s.erase ( pos, 1 );
412 
413  pos = s.rfind ( "]" );
414  if ( pos != s.npos )
415  s.erase ( pos, 1 );
416 
417  return s;
418 }
419 
420 
421 
423 {
424 
425  StringTokenizer st ( line );
426  s1 = st.getNextToken();
427  s2 = st.getNextToken();
428  s3 = st.getRemaining();
429 
430  int op = -1;
431  for ( int i = 0; i < operationsNum; i++ )
432  if ( s2 == operations[i] )
433  op = i;
434 
435  if ( op != -1 ) {
436  /*
437  if ( s3.empty() )
438  error ( "missing data after operand");
439  */
440 
441  ASCString s;
442  for ( Level::iterator i = level.begin(); i != level.end(); i++ )
443  s += *i + ".";
444  s += s1;
445  textPropertyGroup->addEntry ( TextPropertyGroup::Entry (s, TextPropertyGroup::Entry::Operator(op), s3 ) );
446  return;
447  }
448 
449  if ( !s1.empty() && s2 == "{" ) {
450  s1.toLower();
451  startLevel ( s1 );
452  return;
453  }
454 
455  if ( s1 == "}" ) {
456  if ( level.empty() )
457  error ("closing unopened bracket");
458 
459  s2.toLower();
460  if ( s2 != level.back() )
461  error ( "unmatching close brackets: " + s2 );
462 
463  level.pop_back();
464  levelDepth--;
465  return;
466  }
467  error ( "unknown operator in entry " + line + "\n token1: " + s1 + " ; token2: " + s2 + " ; token3: " + s3 );
468 }
469 
470 
471 void TextFormatParser::startLevel ( const ASCString& levelName )
472 {
473 
474  if ( levelDepth == 0 ) {
475  if ( !primaryName.empty() )
476  if ( levelName.compare_ci ( primaryName ) )
477  error ( "expecting group " + primaryName + " , found " + levelName );
478  textPropertyGroup->typeName = levelName;
479  textPropertyGroup->typeName.toLower();
480  }
481 
482  int curlevel = ++levelDepth;
483  level.push_back ( levelName );
484 
485  do {
486  parseLine ( readLine() );
487  } while ( levelDepth >= curlevel );
488 }
489 
490 
492 {
493  textPropertyGroup = new TextPropertyGroup ;
494  textPropertyGroup->fileName = stream->getDeviceName();
495  textPropertyGroup->location = stream->getLocation();
496  textPropertyGroup->archive = stream->getArchive();
497  parseLine ( readLine() );
498  return textPropertyGroup;
499 }
500 
501 void TextFormatParser::error ( const ASCString& errmsg )
502 {
503  ASCString msg;
504  if ( stream )
505  msg = stream->getLocation() + " : " + errmsg;
506  else
507  msg = " : " + errmsg ;
508 
509  displayLogMessage ( 0, msg + "\n" );
510 
511  throw ParsingError ( msg );
512 }
513 
514 
515 
516 
517 
518 
void findMatches(const ASCString &name, const ASCString &name_without_dot, Matches &matches)
void addBool(const ASCString &name, bool &property)
ASCString & toLower()
Definition: ascstring.cpp:36
TextPropertyGroup * run()
enum TextPropertyGroup::Entry::Operator op
void addIntegerArray(const ASCString &name, vector< int > &property, bool required=true)
Functions to evaluate the parsed *.asctxt files.
static const char * whiteSpace
ASCString toString() const
static const int operationsNum
if(!yyg->yy_init)
Definition: scanner.cpp:695
The ASCString class provides an abstract way to manipulate strings.
Definition: ascstring.h:14
TextPropertyGroup * get(int id)
Operator
ASCString readLine()
Entry * find(const ASCString &n)
bool reallyVerbose
bool processAlias(Entry &e, Entries &entriesToAdd, EntryPointerList &markAsResolved)
void parseLine(const ASCString &line)
char * strrr(int a)
converts a to a string.
Definition: misc.cpp:66
A simple string tokenizer.
void displayLogMessage(int msgVerbosity, const char *message,...)
void buildInheritance(TextPropertyList &tpl)
ASCString value
int findGeneration(Entry *e)
Entry * parent
ASCString getNextToken()
void print(int indent=0)
void error(const ASCString &errmsg)
ASCString listInheritanceFilenames()
Class that stores all the (preparsed) entries of an .ASCTXT file.
void addInteger(const ASCString &name, int &property)
This String Tokenizer is NOT intended to be a general purpose tool. It is exclusively used by the Tex...
static const char * operations[]
vector< Entry * > Matches
void error(const ASCString &msg, bool printInheritance=true)
ASCString propertyName
ASCString typeName
the name of the structure. For example "VehicleType"
void fatalError(const ASCString &string)
void addEntry(const Entry &entry)
int compare_ci(const ASCCharTString &s) const
Definition: ascstring.h:296
Functions to parse the *.asctxt files.
void line(int x1, int y1, int x2, int y2, Uint8 actcol)
draws a simple line on the screen. Not very fast...
Definition: basegfx.cpp:181
ASCString getRemaining()
void startLevel(const ASCString &levelName)