parser.cpp

Go to the documentation of this file.
00001 /*
00002  * parser.cpp
00003  *
00004  * Copyright (C) 2007,2009   Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of the primitive vector graphics parser.  See vgfx.h.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "vgfx.h"               // always include our own header first!
00036 #include "drawer.h"
00037 
00038 #include <istream>
00039 #include <sstream>
00040 
00041 #include "common/wave_ex.h"
00042 #include "objtree/objtree.h"    // property set
00043 #include "perf/perf.h"
00044 #include "util/parsing.h"
00045 #include "util/token_stream.h"
00046 
00047 
00048 // typedefs and constants ------------------------------------------------------
00049 
00050 namespace vgfx {
00051 
00052 static const eParseBehavior s_line_behavior = (eParseBehavior) (
00053     eParse_StripComments |
00054     eParse_StripBogus |
00055     eParse_RespectQuotes
00056     );
00057 
00058 static const eParseBehavior s_token_behavior = eParse_RespectQuotes;
00059 
00060 
00061 
00062 ////////////////////////////////////////////////////////////////////////////////
00063 //
00064 //      static helper methods
00065 //
00066 ////////////////////////////////////////////////////////////////////////////////
00067 
00068 static bool
00069 isMultilineType
00070 (
00071 IN const char * type
00072 )
00073 throw()
00074 {
00075         ASSERT(type, "null");
00076 
00077         if (!strcmp("group", type) ||
00078             !strcmp("function", type))
00079                 return true;
00080         return false;
00081 }
00082 
00083 
00084 
00085 static void
00086 breakPath
00087 (
00088 IN const char * in_path,
00089 OUT VecString& elements,
00090 OUT VecString& relpath
00091 )
00092 {
00093         ASSERT(in_path, "null");
00094 
00095         elements.clear();
00096 
00097         std::string current;
00098         for (const char * p = in_path; *p; ++p) {
00099                 if ('.' == *p) {
00100                         elements.push_back(current);
00101                         current = "";
00102                         continue;
00103                 }
00104 
00105                 current += *p;
00106         }
00107         elements.push_back(current);
00108 
00109         relpath.clear();
00110         for (int i = 1; i < (int) elements.size(); ++i) {
00111                 relpath.push_back(elements[i]);
00112         }
00113 }
00114 
00115 
00116 
00117 void
00118 processAdd
00119 (
00120 IN ObjectMap * map,
00121 IN const char * action
00122 )
00123 {
00124         perf::Timer timer("processAdd");
00125 
00126         ASSERT(map, "null");
00127         ASSERT(action, "null");
00128 
00129         // DPRINTF("In processAdd()...");
00130 
00131         // convert to stream
00132         std::istringstream stream(action);
00133         std::string line, token;
00134 
00135         // keep reading lines
00136         while (!stream.eof()) {
00137                 line = getNextLineFromStream(stream, s_line_behavior);
00138 
00139                 // get the leading token
00140                 const char * cursor =
00141                     getNextTokenFromString(line.c_str(), token, eParse_None);
00142 
00143                 // empty?
00144                 if ("" == token) {
00145                         continue;       // skip this line
00146                 }
00147 
00148                 // special case: group?
00149                 if ("group" == token) {
00150                         getNextTokenFromString(cursor, token, eParse_None);
00151                         if ("{" != token) {
00152                                 WAVE_EX(wex);
00153                                 wex << "Expected an opening bracket after 'group' key.";
00154                                 wex << "  Read '" << token << "' instead?";
00155                         }
00156                         smart_ptr<Primitive> g = parseGroup(stream, map);
00157                         ASSERT(g, "Failed to parse group?");
00158                         ASSERT(!strcmp("group", g->getType()),
00159                             "New object isn't a group?");
00160                         map->addObject(g);
00161                         // DPRINTF("Added a group...");
00162 
00163                         // skip to next input--don't fall into primitive parsing
00164                         continue;
00165                 }
00166 
00167                 // special case: function?
00168                 if ("function" == token) {
00169                         getNextTokenFromString(cursor, token, eParse_None);
00170                         if ("{" != token) {
00171                                 WAVE_EX(wex);
00172                                 wex << "Expected an opening bracket after 'function' key.";
00173                                 wex << "  Read '" << token << "' instead?";
00174                         }
00175                         smart_ptr<Primitive> fn = parseFunction(stream, map);
00176                         ASSERT(fn, "failed to parse function?");
00177                         ASSERT(!strcmp("function", fn->getType()),
00178                             "New object isn't a function?");
00179                         map->addObject(fn);
00180                         continue;
00181                 }
00182 
00183                 // construct dictionary
00184                 dictionary_t dict;
00185                 getDictionaryFromString(cursor, token.c_str(), dict);
00186 
00187                 // create primitive
00188                 try {
00189                         // DPRINTF("About to create an object of type '%s'", token);
00190                         smart_ptr<Primitive> p =
00191                             Primitive::create(token.c_str(), dict);
00192                         ASSERT(p, "failed to create object of type '%s'",
00193                             token.c_str());
00194                         // DPRINTF("  Adding to map...");
00195                         map->addObject(p);
00196                 } catch (std::exception& e) {
00197                         // add some more messaging, and re-throw
00198                         WAVE_EX(wex);
00199                         wex << "Failed to create object of type '" << token;
00200                         wex << "': " << e.what();
00201                 }
00202         }
00203 }
00204 
00205 
00206 
00207 static void
00208 processSet
00209 (
00210 IN ObjectMap * map,
00211 IN const char * action
00212 )
00213 {
00214         perf::Timer timer("processSet");
00215 
00216         ASSERT(map, "null");
00217         ASSERT(action, "null");
00218 
00219         // extract path
00220         std::string path;
00221         const char * cursor = getNextTokenFromString(action, path, eParse_None);
00222         VecString elements, relpath;
00223         breakPath(path.c_str(), elements, relpath);
00224         // DPRINTF("path = '%s'", path);
00225         // DPRINTF("path size = %d", elements.size());
00226         // DPRINTF("relative path size = %d", relpath.size());
00227 
00228         // extract dictionary
00229         dictionary_t data;
00230         getDictionaryFromString(cursor, "set", data);
00231 
00232         // get object
00233         Primitive * p = map->findObject(elements[0].c_str());
00234         if (!p) {
00235                 WAVE_EX(wex);
00236                 wex << "Set failed: object with id '" << elements[0] << "' ";
00237                 wex << "does not exist";
00238         }
00239 
00240         // tell object the good news
00241         p->setContainerDictionary(relpath, data);
00242 }
00243 
00244 
00245 
00246 static void
00247 processRemove
00248 (
00249 IN ObjectMap * map,
00250 IN const char * action
00251 )
00252 {
00253         perf::Timer timer("processRemove");
00254 
00255         ASSERT(map, "null");
00256         ASSERT(action, "null");
00257 
00258         // get path
00259         std::string path;
00260         getNextTokenFromString(action, path, eParse_None);
00261         VecString elements, relpath;
00262         breakPath(path.c_str(), elements, relpath);
00263 
00264         if (1 == elements.size()) {
00265                 // remove object!
00266                 map->removeObject(elements[0].c_str());
00267                 return;
00268         }
00269 
00270         // look up this object
00271         const char * id = elements[0].c_str();
00272         Primitive * p = map->findObject(id);
00273         if (!p) {
00274                 WAVE_EX(wex);
00275                 wex << "Error processing remove action: object does not exist:";
00276                 wex << " '" << id << "'";
00277         }
00278 
00279         // ask object to delete container
00280         p->removeContainer(relpath);
00281 }
00282 
00283 
00284 
00285 static void
00286 getActionString
00287 (
00288 IN const char * verb,
00289 IO std::istream& stream,
00290 OUT std::string& action
00291 )
00292 {
00293         // perf::Timer timer("getActionString");
00294 
00295         ASSERT(verb, "null action verb");
00296 
00297         action.clear();
00298 
00299         // extract first line no matter what
00300         std::string line = getNextLineFromStream(stream, s_line_behavior);
00301         action += line;
00302 
00303         // just one line, or multi?
00304         std::string token;
00305         getNextTokenFromString(line.c_str(), token, eParse_None);
00306         if (strcmp("add", verb) || !isMultilineType(token.c_str())) {
00307                 return; // not adding multiline
00308         }
00309 
00310         // multi-line action!  keep parsing lines
00311         int depth = 1;
00312         while (depth > 0) {
00313                 action += "\n";
00314                 line = getNextLineFromStream(stream, s_line_behavior);
00315                 action += line;
00316 
00317                 // crap--won't this be destroyed by brackets in quotes???
00318                 if (strstr(line.c_str(), "{"))
00319                         ++depth;
00320                 if (strstr(line.c_str(), "}"))
00321                         --depth;
00322         }
00323 }
00324 
00325 
00326 
00327 static void
00328 getUndoForAction
00329 (
00330 IN ObjectMap * map,
00331 IN const char * verb,
00332 IO const char * action,
00333 OUT std::string& undo
00334 )
00335 {
00336         perf::Timer timer("getUndoForAction");
00337 
00338         ASSERT(map, "null map");
00339         ASSERT(verb, "null action verb");
00340         ASSERT(action, "null action");
00341 
00342         undo.clear();
00343         std::string line, path, token;
00344 
00345         if (!strcmp("add", verb)) {
00346                 // adding an object.  Undo is a remove with that ID
00347                 undo = "remove ";
00348                 const char * cursor =
00349                     getNextTokenFromString(action, token, eParse_None);
00350                 if (!isMultilineType(token.c_str())) {
00351                         // it's a single-line type (not a group)
00352                         dictionary_t data;
00353                         getDictionaryFromString(cursor, token.c_str(), data);
00354                         undo += getRequiredValue(data, "id");
00355                 } else {
00356                         // it's a multi-line type (a group or function)
00357                         // walk through lines until we get the id
00358                         std::istringstream iss(action);
00359                         const char * cursor = NULL;
00360                         while ("id" != token) {
00361                                 line =
00362                                     getNextLineFromStream(iss, s_line_behavior);
00363                                 cursor = getNextTokenFromString(line.c_str(),
00364                                     token, eParse_None);
00365                         }
00366                         getNextTokenFromString(cursor, token, eParse_None);
00367                         undo += token;
00368                 }
00369         } else if (!strcmp("remove", verb)) {
00370                 // removing an object.  Undo is either an add or a set
00371 
00372                 // get path
00373                 getNextTokenFromString(action, path, eParse_None);
00374                 VecString elements, relpath;
00375                 breakPath(path.c_str(), elements, relpath);
00376 
00377                 // get the ID, then the primitive
00378                 Primitive * p = map->findObject(elements[0].c_str());
00379                 if (!p) {
00380                         WAVE_EX(wex);
00381                         wex << "Asking to remove an object that does not ";
00382                         wex << "exist: '" << elements[0] << "'";
00383                 }
00384 
00385                 if (1 == elements.size()) {
00386                         // an entire object
00387                         // get persisted object
00388                         undo = "add ";
00389                         std::ostringstream persist;
00390                         p->persist(persist);
00391                         undo += persist.str();
00392                 } else {
00393                         // a container
00394                         undo = "set ";
00395                         undo += path;
00396                         undo += " ";
00397 
00398                         // more
00399                         dictionary_t data;
00400                         p->getContainerDictionary(relpath, data);
00401                         for (dictionary_t::iterator i = data.begin();
00402                              i != data.end(); ++i) {
00403                                 undo += i->first;
00404                                 undo += " \"";
00405                                 undo += i->second;
00406                                 undo += "\" ";
00407                         }
00408                 }
00409         } else if (!strcmp("set", verb)) {
00410                 // changing an attribute value.
00411                 //   Undo is a set with the old value, or a remove if this is a
00412                 //   create
00413 
00414                 // get path
00415                 const char * cursor =
00416                     getNextTokenFromString(action, path, eParse_None);
00417                 VecString elements, relpath;
00418                 breakPath(path.c_str(), elements, relpath);
00419                 if (elements.size() < 1) {
00420                         WAVE_EX(wex);
00421                         wex << "Invalid path: '" << path << "'";
00422                 }
00423 
00424                 // get the object
00425                 const char * id = elements[0].c_str();
00426                 Primitive * p = map->findObject(id);
00427                 if (!p) {
00428                         WAVE_EX(wex);
00429                         wex << "Attempting to set attribute value for object ";
00430                         wex << "that does not exist, id='" << id << "'";
00431                 }
00432 
00433                 // does this path already exist?
00434                 if (p->doesContainerExist(relpath)) {
00435                         // container exists!  Undo is a set with old values
00436                         undo = "set ";
00437                         undo += path;
00438 
00439                         // get all the old values for everything in the input
00440                         // dictionary
00441                         dictionary_t new_values;
00442                         getDictionaryFromString(cursor, "set", new_values);
00443 
00444                         dictionary_t old_values;
00445                         p->getContainerDictionary(relpath, old_values);
00446 
00447                         for (dictionary_t::iterator i = new_values.begin();
00448                              i != new_values.end(); ++i) {
00449 
00450                                 const char * name = i->first.c_str();
00451 
00452                                 dictionary_t::iterator d = old_values.find(name);
00453                                 if (old_values.end() == d) {
00454                                         WAVE_EX(wex);
00455                                         wex << "Trying to set an attribute '";
00456                                         wex << name << "' that does not exist";
00457                                         wex << "in its container '" << path;
00458                                         wex << "'";
00459                                 }
00460 
00461                                 undo += " ";
00462                                 undo += name;
00463                                 undo += " \"";
00464                                 undo += d->second;
00465                                 undo += "\"";
00466                         }
00467                 } else {
00468                         // container does not exist
00469                         if (!p->canCreateContainer(relpath)) {
00470                                 WAVE_EX(wex);
00471                                 wex << "Asking to set values in a container ";
00472                                 wex << "that does not exist: '" << relpath[0];
00473                                 wex << "'";
00474                         }
00475 
00476                         // container can be created!  Undo is a remove
00477                         undo = "remove ";
00478                         undo += path;
00479                 }
00480         } else {
00481                 WAVE_EX(wex);
00482                 wex << "Invalid action verb: '" << verb << "', cannot undo.";
00483         }
00484 }
00485 
00486 
00487 
00488 static void
00489 processAction
00490 (
00491 IN ObjectMap * map,
00492 IN const char * verb,
00493 IN const char * action
00494 )
00495 {
00496         perf::Timer timer("processAction");
00497 
00498         ASSERT(map, "null");
00499         ASSERT(verb, "null");
00500         ASSERT(action, "null");
00501 
00502         // what sort of verb is it?
00503         if (!strcmp("add", verb)) {
00504                 // DPRINTF("Add request:\n%s\n--------", action);
00505                 processAdd(map, action);
00506         } else if (!strcmp("remove", verb)) {
00507                 // DPRINTF("Remove request (id = '%s')", action);
00508                 processRemove(map, action);
00509         } else if (!strcmp("set", verb)) {
00510                 // DPRINTF("Set request:\n%s\n-------", action);
00511                 processSet(map, action);
00512         } else {
00513                 WAVE_EX(wex);
00514                 wex << "Cannot process this action verb: '" << verb << "'";
00515         }
00516 }
00517 
00518 
00519 
00520 static void
00521 processRequestInternal
00522 (
00523 IN ObjectMap * map,
00524 IN std::istream& stream,
00525 IN bool single_transaction,
00526 OUT VecString& undo_actions
00527 )
00528 {
00529         perf::Timer timer("processRequestInternal");
00530         // This function is used internally, called by processRequest()
00531         // That function handles opening/closing the undo request, and
00532         //   dealing with failures transactionally.  This function can
00533         //   throw at any time.  It should add undo actions for every
00534         //   action it successfully processes.
00535         ASSERT(map, "null");
00536         ASSERT(stream.good(), "bad request stream");
00537 
00538         undo_actions.clear();   // haven't done anything yet
00539 
00540         // verify the request begins with "request {"
00541         expectToken(stream, "request");
00542         expectToken(stream, "{");
00543 
00544         // keep parsing until we find the closing bracket
00545         std::string token;
00546         while (true) {
00547                 getNextToken(stream, token);
00548                 if (token == "}") {
00549                         break;          // end of request!
00550                 }
00551                 const char * verb = token.c_str();
00552                 // DPRINTF("Parsing action of verb '%s'...", verb);
00553 
00554                 // is this a valid action verb?
00555                 if (token != "add" &&
00556                     token != "remove" &&
00557                     token != "set") {
00558                         WAVE_EX(wex);
00559                         wex << "Invalid action verb: '" << verb << "'";
00560                 }
00561 
00562                 // first, extract atomic action into a single string so we can
00563                 //   parse it multiple times if necessary
00564                 std::string action_str;
00565                 getActionString(verb, stream, action_str);
00566                 const char * action = action_str.c_str();
00567                 // DPRINTF("Extracted action string:\n%s %s\n--------",
00568                 //      verb, action);
00569 
00570                 // get undo string for this action
00571                 std::string undo;
00572                 if (single_transaction) {
00573                         try {
00574                                 getUndoForAction(map, verb, action, undo);
00575                         } catch (std::exception& e) {
00576                                 WAVE_EX(wex);
00577                                 wex << "Error calculating undo action for this";
00578                                 wex << " action:\n" << verb << " " << action;
00579                                 wex << "\nError: " << e.what();
00580                         }
00581                         // DPRINTF("Undo string:\n%s\n--------", undo.c_str());
00582                 }
00583 
00584                 // process action
00585                 // DPRINTF("processing action '%s'", verb);
00586                 try {
00587                         processAction(map, verb, action);
00588                 } catch (std::exception& e) {
00589                         WAVE_EX(wex);
00590                         wex << "Error processing this action:\n";
00591                         wex << verb << " " << action << "\n";
00592                         wex << "Error: " << e.what();
00593                 }
00594 
00595                 // success!  Add undo to list
00596                 if (single_transaction) {
00597                         undo_actions.push_back(undo);
00598                 }
00599         }
00600 }
00601 
00602 
00603 ////////////////////////////////////////////////////////////////////////////////
00604 //
00605 //      public API
00606 //
00607 ////////////////////////////////////////////////////////////////////////////////
00608 
00609 
00610 bool
00611 processRequest
00612 (
00613 IN ObjectMap * map,
00614 IN std::istream& stream,
00615 IN bool single_transaction,
00616 OUT std::string& undo_request,
00617 OUT std::string& diagnostic
00618 )
00619 {
00620         perf::Timer timer("processRequest");
00621 
00622         ASSERT(map, "null map in processRequest()");
00623         ASSERT(stream.good(), "bad stream in processRequest()");
00624 
00625         // clear input strings
00626         diagnostic.clear();
00627         undo_request.clear();
00628 
00629         VecString undo_actions; // list of undo actions
00630 
00631         // process the request
00632         bool success = false;
00633         try {
00634                 processRequestInternal(map, stream, single_transaction,
00635                     undo_actions);
00636                 success = true;
00637         } catch (std::exception& e) {
00638                 diagnostic = e.what();
00639         } catch (...) {
00640                 diagnostic = "Unknown exception!  Hard failure.";
00641                 WAVE_EX(wex);
00642                 wex << diagnostic;
00643         }
00644 
00645         // turn the undo actions into a request string
00646         if (single_transaction) {
00647                 // walk through in reverse order
00648                 undo_request = "request {\n";
00649                 for (VecString::reverse_iterator iter = undo_actions.rbegin();
00650                      iter != undo_actions.rend(); ++iter) {
00651                         undo_request += *iter;
00652                         undo_request += "\n";
00653                 }
00654                 undo_request += "}";
00655         }
00656         // DPRINTF("Undo request:\n--------\n%s\n--------", undo_request.c_str());
00657 
00658         // success?
00659         if (success) {
00660                 diagnostic = "Success.";
00661         } else if (single_transaction) {
00662                 // failed, and in single transaction: attempt to roll back.
00663                 //   (process the undo request we have)
00664                 std::string local_undo = undo_request;
00665                 undo_request = "";      // don't return anything to caller
00666                 try {
00667                         VecString unused_undo;
00668                         std::istringstream undo_stream(local_undo);
00669                         processRequestInternal(map, undo_stream, false,
00670                             unused_undo);
00671                 } catch (std::exception& e) {
00672                         diagnostic += "\nEncountered exception while trying to";
00673                         diagnostic += " roll back: ";
00674                         diagnostic += e.what();
00675                         diagnostic += "\nHard failure.";
00676                         WAVE_EX(wex);
00677                         wex << diagnostic;
00678                 } catch (...) {
00679                         diagnostic += "\nUnknown exception while attempting to";
00680                         diagnostic += " roll back!\nHard failure.";
00681                         WAVE_EX(wex);
00682                         wex << diagnostic;
00683                 }
00684         }
00685 
00686         // all done
00687         return success;
00688 }
00689 
00690 
00691 
00692 };      // vgfx namespace
00693