/*************************************************************************** textfileparser.cpp - description ------------------- begin : Thu Jul 26 2001 copyright : (C) 2001 by Martin Bickel email : bickel@asc-hq.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include "ascstring.h" #include "basicmessages.h" #include "textfileparser.h" #include "stringtokenizer.h" #include "textfile_evaluation.h" const int TextFormatParser::operationsNum = 6; const char* TextFormatParser::operations[6] = { "=", "*=", "+=", "->", "->*", "-=" }; const char* TextFormatParser::whiteSpace = " \t"; ASCString TextPropertyGroup::Entry::toString() const { Operator o = op; if ( o == alias_all_resolved ) o = alias_all; ASCString s = propertyName + TextFormatParser::operations[o] + value ; return s; } void TextPropertyList::buildIDs() { for ( iterator i = begin(); i != end(); i++ ) { int id = (*i)->evalID(); if ( id > 0 ) { if ( identCache.find ( id ) != identCache.end() ) 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."); identCache[id] = *i; } } } TextPropertyGroup* TextPropertyList::get ( int id ) { IdentCache::iterator f = identCache.find ( id ); if ( f != identCache.end() ) return &(*(f->second)); else return NULL; } ///////////////////// TextPropertyGroup ////////////////////////// void TextPropertyGroup :: error ( const ASCString& msg ) { fatalError ( "Error evaluating file " + location + "\n" + msg + "\nThe inheritance is\n" + listInheritanceFilenames() ); } void TextPropertyGroup :: print( int indent ) { for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) { for ( int m = 0; m<= indent; m++ ) displayLogMessage(10, " " ); displayLogMessage(10, i->toString()); displayLogMessage(10, "\n"); } for ( Parents::iterator i = parents.begin(); i != parents.end(); i++ ) { for ( int n = 0; n< indent; n++ ) printf(" " ); displayLogMessage(10, " is inheriting from " + (*i)->location + "\n" ); (*i)->print(indent+1 ); } } void TextPropertyGroup :: buildInheritance(TextPropertyList& tpl ) { static list callStack; if ( !inheritanceBuild ) { if ( std::find ( callStack.begin(), callStack.end(), this ) != callStack.end() ) error ( "endless inheritance loop detected: type " + typeName + "; ID " + strrr ( id )); callStack.push_back( this ); PropertyReadingContainer prc ( typeName, this ); prc.addBool ( "abstract", abstract, false ); int iid; prc.addInteger("ID", iid, 0 ); if ( find ( typeName+".parent") != NULL ) { typedef vector ParentIDs; ParentIDs parentIDs; prc.addIntegerArray ( "parent", parentIDs ); for ( ParentIDs::iterator i = parentIDs.begin(); i != parentIDs.end(); i++ ) { TextPropertyGroup* p = tpl.get ( *i ); if ( p ) { parents.push_back ( p ); displayLogMessage( 10, ASCString(" entering parent with ID ") + strrr(*i) + " ("+p->location+")\n" ); p->buildInheritance( tpl ); displayLogMessage( 10, ASCString(" leaving parent with ID ") + strrr(*i) + "\n" ); } else error ( location + " : no parent with ID " + strrr(*i) + " of type " + typeName + " could be found !" ); } } //! building inheritance /* for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) { if ( i->op != Entry::eq && i->op != Entry::alias_all && i->op != Entry::alias) { Parents::iterator p = parents.begin(); while ( p != parents.end() ) { i->parent = (*p)->find( i->propertyName ); if ( i->parent ) break; p++; } if ( p == parents.end()) error ( "could not find a parent entry for " + typeName + " :: " + i->propertyName ); } } */ resolveAllAlias(); callStack.pop_back(); inheritanceBuild = true; } } ASCString TextPropertyGroup :: listInheritanceFilenames() { ASCString s; for ( Parents::iterator p = parents.begin(); p != parents.end(); p++ ) s += (*p)->listInheritanceFilenames(); s += location + "\n"; return s; } void TextPropertyGroup :: resolveAllAlias( ) { int loop = 0; typedef list Unresolved; Unresolved unresolved; do { Entries additionalEntries; int resolvedCounter = 0; EntryPointerList toResolve; if ( loop == 0 ) { for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) { bool resolved = processAlias ( *i , additionalEntries, toResolve ); if ( !resolved ) unresolved.push_back ( &(*i) ); else resolvedCounter++; } } else { Unresolved newunresolved; for ( Unresolved::iterator i = unresolved.begin(); i != unresolved.end(); i++ ) { bool resolved = processAlias ( **i , additionalEntries, toResolve ); if ( !resolved ) newunresolved.push_back ( *i ); else resolvedCounter++; } unresolved = newunresolved; } for ( Entries::iterator i = additionalEntries.begin(); i != additionalEntries.end(); i++ ) // if ( find ( i->propertyName ) == NULL ) addEntry ( *i ); for ( EntryPointerList::iterator i = toResolve.begin(); i != toResolve.end(); i++ ) (*i)->op = Entry::alias_all_resolved; if ( !resolvedCounter && !unresolved.empty() ) for ( Unresolved::iterator i = unresolved.begin(); i != unresolved.end(); i++ ) error ( "could not resolve the reference " + (*i)->value + " for " + typeName + " :: " + (*i)->propertyName ); loop++; } while ( !unresolved.empty() ); } bool reallyVerbose = false; int TextPropertyGroup::findGeneration ( Entry* e ) { for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) if ( &(*i) == e ) return 0; for ( Parents::iterator i = parents.begin(); i != parents.end(); i++ ) { int fg = (*i)->findGeneration( e ); if ( fg >= 0 ) return fg+1; } return -1; } bool TextPropertyGroup::processAlias( Entry& e, Entries& entriesToAdd, EntryPointerList& markAsResolved ) { if ( e.op == Entry::alias_all ) { ASCString ss = e.value; ss.toLower(); ASCString::size_type pos; do { pos = ss.find ( " " ); if ( pos != ss.npos ) ss.erase(pos,1); } while ( pos != ss.npos ); ASCString s_without_dot = ss; if ( ss.length() > 0 ) if ( ss[ss.length()-1] != '.' ) ss = ss + "."; if ( s_without_dot.length() > 0 ) if ( s_without_dot[s_without_dot.length()-1] == '.' ) s_without_dot.erase ( s_without_dot.length()-1 ); ASCString newName = e.propertyName; if ( newName.length() > 0 ) if ( newName[newName.length()-1] != '.' ) newName = newName + "."; int counter = 0; Matches matches; findMatches ( ss, s_without_dot, matches ); for ( Matches::iterator i = matches.begin(); i != matches.end(); i++ ) if ( (*i)->op == Entry::alias_all ) { displayLogMessage ( 9, " alias is pending " + ss + "* <- " + e.propertyName + " because " + (*i)->propertyName + " is unresolved\n" ); return false; } for ( Matches::iterator i = matches.begin(); i != matches.end(); i++ ) if ( (*i)->op != Entry::alias_all_resolved ) { Entry e = **i; e.propertyName.replace ( 0, ss.length(), newName ); TextPropertyGroup::Entry* existent = find ( e.propertyName ); // if ( existent == NULL || findGeneration(*i) <= findGeneration( existent ) ) { if ( existent == NULL || findGeneration( existent ) > 0 ) { /* if ( existent && findGeneration( existent ) == 0 ) { displayLogMessage(10, " overwriting entry " + existent->toString() + " because it belongs to an older generation\n" ); *existent = e; } else */ entriesToAdd.push_back ( e ); displayLogMessage(10, " aliasing entry " + (*i)->toString() + " to " + e.propertyName + " \n" ); } counter++; } else displayLogMessage(10, " skipping aliasing entry " + (*i)->toString() + " to " + e.propertyName + " because it is already resolved\n" ); if ( !counter ) { displayLogMessage ( 9, " could not successfully resolve alias " + ss + "* <- " + e.propertyName + "\n" ); return false; } else { markAsResolved.push_back ( &e ); displayLogMessage ( 9, " successfully resolved alias " + e.propertyName + " ->* " + ss + "\n" ); return true; } } if ( e.op == Entry::alias ) { ASCString s = e.value; s.toLower(); ASCString::size_type pos; do { pos = s.find ( " " ); if ( pos != s.npos ) s.erase(pos,1); } while ( pos != s.npos ); Entry* alias = find ( s ); if ( !alias ) return false; else { e.value = alias->value; e.op = alias->op; e.parent = alias->parent; return true; } } //! building inheritance if ( e.op != Entry::eq && e.op != Entry::alias_all && e.op != Entry::alias && e.op != Entry::alias_all_resolved ) { Parents::iterator p = parents.begin(); while ( p != parents.end() ) { e.parent = (*p)->find( e.propertyName ); if ( e.parent ) break; p++; } if ( p == parents.end()) return false; else return true; } return true; } int TextPropertyGroup :: evalID() { PropertyReadingContainer prc ( typeName, this ); prc.addInteger ( "ID", id, 0 ); return id; } TextPropertyGroup::Entry* TextPropertyGroup :: find( const ASCString& n ) { EntryCache::iterator i = entryCache.find ( n ); if ( i != entryCache.end() ) return i->second; else { for ( Parents::iterator p = parents.begin(); p != parents.end(); p++ ) { TextPropertyGroup::Entry* ent = (*p)->find ( n ); if ( ent ) return ent; } return NULL; } } void TextPropertyGroup::findMatches( const ASCString& name, const ASCString& name_without_dot, Matches& matches ) { for ( Entries::iterator i = entries.begin(); i != entries.end(); i++ ) if ( i->propertyName.find ( name ) == 0 || i->propertyName == name_without_dot ) matches.push_back ( &(*i) ); for ( Parents::iterator p = parents.begin(); p != parents.end(); p++ ) (*p)->findMatches ( name, name_without_dot, matches ); } void TextPropertyGroup::addEntry( const Entry& entry ) { entries.push_back ( entry ); entryCache[entry.propertyName] = &entries.back(); } ///////////////////// TextFormatParser ////////////////////////// ASCString TextFormatParser::readLine ( ) { ASCString s; int noCommentLines = 0; int bracketsOpen = 0; do { ASCString t = stream->readString(); ASCString::size_type pos = t.find_first_not_of ( whiteSpace ); if ( pos != ASCString::npos ) { if ( t[pos] != ';' ) { noCommentLines++; if ( !s.empty() ) s += " "; s += t.substr ( pos ); if ( t.find ( "[" ) != t.npos ) bracketsOpen++; if ( t.find ( "]" ) != t.npos ) bracketsOpen--; } } else { // empty line if ( bracketsOpen ) s += "\n"; } } while ( noCommentLines == 0 || bracketsOpen > 0 ); ASCString::size_type pos = s.find ( "[" ); if ( pos != s.npos ) s.erase ( pos, 1 ); pos = s.rfind ( "]" ); if ( pos != s.npos ) s.erase ( pos, 1 ); return s; } void TextFormatParser::parseLine ( const ASCString& line ) { StringTokenizer st ( line ); s1 = st.getNextToken(); s2 = st.getNextToken(); s3 = st.getRemaining(); int op = -1; for ( int i = 0; i < operationsNum; i++ ) if ( s2 == operations[i] ) op = i; if ( op != -1 ) { /* if ( s3.empty() ) error ( "missing data after operand"); */ ASCString s; for ( Level::iterator i = level.begin(); i != level.end(); i++ ) s += *i + "."; s += s1; textPropertyGroup->addEntry ( TextPropertyGroup::Entry (s, TextPropertyGroup::Entry::Operator(op), s3 ) ); return; } if ( !s1.empty() && s2 == "{" ) { s1.toLower(); startLevel ( s1 ); return; } if ( s1 == "}" ) { if ( level.empty() ) error ("closing unopened bracket"); s2.toLower(); if ( s2 != level.back() ) error ( "unmatching close brackets: " + s2 ); level.pop_back(); levelDepth--; return; } error ( "unknown operator at entry " + s1 ); } void TextFormatParser::startLevel ( const ASCString& levelName ) { if ( levelDepth == 0 ) { if ( !primaryName.empty() ) if ( levelName.compare_ci ( primaryName ) ) error ( "expecting group " + primaryName + " , found " + levelName ); textPropertyGroup->typeName = levelName; textPropertyGroup->typeName.toLower(); } int curlevel = ++levelDepth; level.push_back ( levelName ); do { parseLine ( readLine() ); } while ( levelDepth >= curlevel ); } TextPropertyGroup* TextFormatParser::run ( ) { textPropertyGroup = new TextPropertyGroup ; textPropertyGroup->fileName = stream->getDeviceName(); textPropertyGroup->location = stream->getLocation(); parseLine ( readLine() ); return textPropertyGroup; } void TextFormatParser::error ( const ASCString& errmsg ) { ASCString msg; if ( stream ) msg = stream->getLocation() + " : " + errmsg; else msg = " : " + errmsg ; displayLogMessage ( 0, msg + "\n" ); throw ParsingError ( msg ); }