tools/schema/sourceparser.cpp

Go to the documentation of this file.
00001 /* $Id: sourceparser.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003  Copyright (C) 2011 - 2012 by Sytyi Nick <nsytyi@gmail.com>
00004  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006  This program is free software; you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation; either version 2 of the License, or
00009  (at your option) any later version.
00010  This program is distributed in the hope that it will be useful,
00011  but WITHOUT ANY WARRANTY.
00012 
00013  See the COPYING file for more details.
00014 */
00015 /**
00016  * @file
00017  * This file contains implementation of sourceparser.cpp.
00018  */
00019 
00020 #include "tools/schema/sourceparser.hpp"
00021 
00022 #include "boost/regex.hpp"
00023 
00024 #include <stack>
00025 
00026 namespace schema_validation{
00027 /** Little parts of regex templates used to parse Wml annoations.
00028  *For details, look http://wiki.wesnoth.org/WML_Annotation_Format , please
00029  */
00030 /** line is valid*/
00031 const std::string valid = "^\\s*\\*\\s*";
00032 /** begining of wiki block*/
00033 const std::string wiki_begin ="^\\s*/\\*(?:WIKI|SCHEMA)";
00034 /** whitespace is possible*/
00035 const std::string space ="\\s*";
00036 /** sigh "="*/
00037 const std::string equals ="=";
00038 /** non-mandatory sign "*/
00039 const std::string quote_symbol ="\"?";
00040 /** begining of the block.*/
00041 const std::string block_begin="@begin" ;
00042 /** end of block*/
00043 const std::string block_end ="@end";
00044 /** allow directive*/
00045 const std::string allow="@allow";
00046 /** remove directive*/
00047 const std::string remove="@remove";
00048 /** sign "{" - curly bracket*/
00049 const std::string property_open="\\{";
00050 /** sign "}" - another curly bracket*/
00051 const std::string property_close= "\\}";
00052 /** type of possible name identificator*/
00053 const std::string name_type= "[a-z][a-zA-Z0-9_-]*" ;
00054 /** type of possible parent indentificator*/
00055 const std::string parent_type= "/|(?:[a-z][a-zA-Z0-9_-]*/)+";
00056 /** type of possible link indentificator*/
00057 const std::string link_type="(?:[a-z][a-zA-Z0-9_-]*/)*(?:[a-z][a-zA-Z0-9_-]*)";
00058 /** template to number regex*/
00059 const std::string number="\\d*";
00060 /** sign "-" - hyphen-minus used to set sign of signed integer*/
00061 const std::string sign="-?";
00062 
00063 /**
00064  * end of line + possible various character before.
00065  * Used to close template. ".*" is used cause I dont want to get error
00066  * every misprint whitespace
00067  * after annotation element
00068  */
00069 const std::string eol=".*$";
00070 
00071 /** Private function to surround an argument with brackets.
00072  * This allows substitutions :-)
00073  */
00074 static std::string sub (const std::string & s){
00075     return "(" + s + ")";
00076 }
00077 
00078 /** Private function to surround argument with not mandatory quotes.
00079  * Is used when creating properties
00080  */
00081 static std::string quote(const std::string & s){
00082     return quote_symbol + s + quote_symbol ;
00083 }
00084 
00085 /**
00086  * Creates a property template
00087  * @param name Name of property
00088  * @param value Type of property value
00089  * If value is empty creates simple property like {table}
00090  * Else creates a named property like {name="[name_type_template]"}
00091  */
00092 static std::string property(const std::string & name,
00093                             const std::string & value = ""){
00094     if (value.empty()){
00095         return property_open + name + property_close;
00096     }
00097     return property_open + name + equals + quote(value) + property_close;
00098 }
00099 
00100 
00101 const std::string & get_valid() {
00102     return valid;
00103 }
00104 
00105 const std::string & get_wiki() {
00106     static std::string wiki = wiki_begin + eol;
00107     return wiki;
00108 }
00109 
00110 const std::string & get_parent_begin() {
00111     static std::string parent_begin = valid + block_begin
00112                                       + property("parent")
00113                                       + property("name",sub(parent_type))
00114                                       + eol;
00115     return parent_begin;
00116 }
00117 
00118 const std::string & get_parent_end() {
00119     static std::string parent_end = valid + block_end + property("parent")
00120                                     + property("name",sub(parent_type))+ eol;
00121     return parent_end;
00122 }
00123 
00124 const std::string & get_tag_begin() {
00125     static std::string tag_begin = valid + block_begin + property("tag")
00126                                    + property("name",sub(name_type))
00127                                    + property("min",sub(number))
00128                                    + property("max",sub(sign + number))
00129                                    + sub(property("super",sub(link_type)))
00130                                    +"?" + eol;
00131     /* sub(property("super"),sub(link_type))+"?"
00132      * property super is not mandatory
00133      */
00134     return tag_begin;
00135 }
00136 
00137 
00138 const std::string & get_tag_end() {
00139     static std::string tag_end = valid + block_end + property("tag")
00140                                  + property("name",sub(name_type)) + eol;
00141     return tag_end;
00142 }
00143 
00144 
00145 const std::string & get_allow_link(){
00146     static std::string allow_link = valid + allow + property("link")
00147                                     +property("name",sub(link_type)) + eol;
00148     return allow_link;
00149 }
00150 
00151 const std::string & get_allow_global(){
00152     static std::string global_link = valid + allow + property("global")
00153                                      +property("name",sub(name_type)) + eol;
00154     return global_link;
00155 }
00156 
00157 static const std::string & get_allow_type(){
00158     static std::string allow_type = valid + allow + property("type")
00159                                     +property("name",sub(name_type))
00160                                     +property("value",sub("\\^.+\\$"))
00161                                     + eol;
00162     return allow_type;
00163 }
00164 static const std::string & get_remove_type(){
00165     static std::string remove_type = valid + remove + property("type")
00166                                     +property("name",sub(name_type))
00167                                     + eol;
00168     return remove_type;
00169 }
00170 static const std::string & get_remove_key(){
00171     static std::string remove_key = valid + remove + property("key")
00172                                     +property("name",sub(name_type))
00173                                     + eol;
00174     return remove_key;
00175 }
00176 
00177 const std::string & get_table_key_begin() {
00178     static std::string keys_begin = valid + block_begin + property("table")
00179                                     + property("config")+ eol;
00180     return keys_begin;
00181 }
00182 
00183 const std::string & get_table_end() {
00184     static std::string table_end = valid + block_end + property("table")
00185                                    + eol;
00186     return table_end;
00187 }
00188 
00189 
00190 const std::string & get_key_value(){
00191     static std::string key_value = valid + sub("[a-zA-z][a-zA-Z\\d_+-]*")
00192                                    + "\\s*&\\s*"+sub(name_type)
00193                                    +"\\s*&\\s?"+ sub(quote("[a-zA-Z._0-9+-]*"))
00194                                    +"\\s&"+ eol;
00195     return key_value;
00196 }
00197 
00198 void test_regex( std::ostream & f ){
00199     f << get_valid() << "\n"
00200             << get_wiki() << "\n"
00201             << get_parent_begin() << "\n"
00202             << get_parent_end() << "\n"
00203             << get_tag_begin() << "\n"
00204             << get_tag_end() << "\n"
00205             << get_allow_link() << "\n"
00206             << get_allow_global() << "\n"
00207             << get_table_key_begin() << "\n"
00208             << get_table_end() << "\n"
00209             << get_key_value() << "\n"
00210             << get_allow_type() << "\n"
00211             << std::endl;
00212 }
00213 
00214 bool class_source_parser::save_schema(){
00215 
00216     std::fstream out;
00217     if (output_.empty()){
00218         return false;
00219     }
00220     out.open(output_.c_str(),std::ios::out|std::ios::trunc);
00221     if (out.fail()){
00222         errors_.add_simple_error("Can not open file "+output_+
00223                                  "\n Output woulfd not be stored\n");
00224         return false;
00225     }
00226     // remove all forbidden keys
00227     for (std::vector<std::string>::const_iterator i= forbidden_.begin ();
00228     i != forbidden_.end (); ++i){
00229         root_.remove_keys_by_type (*i);
00230         types_.erase (*i);
00231     }
00232     out << "[wml_schema]\n";
00233     for (std::map<std::string,std::string>::iterator i=types_.begin();
00234     i!= types_.end();++i){
00235         out << "    [type]\n"
00236             << "        name=" << i->first << "\n"
00237             << "        value=\""<< i->second << "\"\n"
00238             <<"    [/type]\n";
00239     }
00240     root_.print(out);
00241     out << "[/wml_schema]\n";
00242     out.close();
00243     return true; // @TODO add error support
00244 }
00245 
00246 
00247 bool class_source_parser::getline(std::string &s){
00248     if (f_.fail()){
00249         errors_.add_read_error(input_,line_);
00250         return false;
00251     }
00252     std::getline(f_,s);
00253     line_ ++;
00254     if (! f_.eof()){
00255         if (f_.fail()){
00256             errors_.add_read_error(input_,line_);
00257             return false;
00258         }
00259     }
00260     return true;
00261 }
00262 // call without arg when you want to closethem all
00263 void class_source_parser::add_open_tag_error(int i = INT_MAX){
00264     std::vector<class_tag>::iterator it;
00265     for (it = current_.begin(); it != current_.end() && i > 0; ++it){
00266         errors_.add_opened_entity_error(input_,line_,it->get_name());
00267         --i;
00268     }
00269 }
00270 // call without arg when you want to closethem all
00271 void class_source_parser::close_opened_tags(int i = INT_MAX){
00272     if (current_.empty()){
00273         return;
00274     }
00275     std::stack<std::string> error_cache ;
00276     while (current_.size() > 1){
00277         if (i==0){
00278             break;
00279         }
00280         class_tag tag (current_.back());
00281         current_.pop_back();
00282         current_.back().add_tag(tag);
00283         error_cache.push(tag.get_name());
00284         i--;
00285     }
00286     if (i!=0){
00287         //adding to parent
00288         if (parent_name_.empty()) {
00289             orphan_tags_.push_back(current_.back());
00290             errors_.add_orphan_error(input_,line_,current_.back().get_name());
00291         }else{
00292             error_cache.push(current_.back().get_name());
00293             root_.add_tag(parent_name_,current_.back(),root_);
00294         }
00295         current_.pop_back();
00296     }
00297     std::string name_to_remove_from_cache = parent_name_;
00298     for (std::vector<class_tag>::const_iterator ii = current_.begin();
00299     ii!= current_.end();++ii){
00300         name_to_remove_from_cache +=  ii->get_name() + "/";
00301     }
00302     while (! error_cache.empty()){
00303         name_to_remove_from_cache +=  error_cache.top();
00304         errors_.remove_link_errors(name_to_remove_from_cache);
00305         error_cache.pop();
00306     name_to_remove_from_cache += "/";
00307     }
00308 
00309 }
00310 
00311 
00312 bool class_source_parser::parse_source(){
00313     if (input_.empty()){
00314         errors_.add_simple_error("File was not defined\n");
00315         // Use  to hack sorting errors.
00316         // Item with  will be at the end of error list.
00317         return false;
00318     }
00319 
00320     f_.open(input_.c_str(),std::ios::in);
00321     if (f_.fail()){
00322         errors_.add_simple_error("File "+input_ + " can not be opened\n");
00323         return false;
00324     }
00325     line_ = 0;
00326     bool result = true;
00327     while (!f_.eof()){
00328         std::string line;
00329         if (! getline(line) ) {
00330             f_.close();
00331             f_.clear();
00332 
00333             close_opened_tags();
00334             parent_name_.clear();
00335             return false;
00336         } // is used to avoid exceptions.
00337 
00338         if (check_wiki(line)) {
00339             result = parse_block();
00340             if (! result) {
00341                 break;
00342             }
00343         }
00344     }
00345 
00346     f_.close();
00347 
00348     // Clear all flags ( eg the eof flag ) after closing the file.
00349     // This will let us reuse the same fstream variable for different files.
00350     f_.clear();
00351 
00352     close_opened_tags();
00353     parent_name_.clear();
00354     return result;
00355 }
00356 
00357 
00358 bool class_source_parser::parse_block(){
00359     while (!f_.eof()){
00360         std::string line;
00361         if (! getline(line) ) { return false; }
00362         if ( check_valid(line)) {
00363             if (check_allow_type(line)) continue;
00364             if (check_remove_type(line)) continue;
00365             if (check_parent_begin(line)) continue;
00366 
00367             if (check_tag_begin(line)){
00368                 parse_tag();
00369                 continue;
00370             }
00371             check_parent_end(line);
00372         }
00373         else{
00374             // wiki-block is closed. checking stack of opened tags
00375             if (!current_.empty()){
00376                 add_open_tag_error();
00377                 close_opened_tags();
00378                 // continue working
00379             }
00380             // block is closed and all tags are closed.
00381             return true;
00382 
00383         }
00384     }// end while
00385     return false;
00386 }
00387 
00388 bool class_source_parser::parse_tag(){
00389     while (!f_.eof()){
00390         std::string line;
00391         if (! getline(line) ) { return false; }
00392         if (check_valid(line)) {
00393             if (check_tag_begin(line)){
00394                 parse_tag();
00395             }
00396             if (check_tag_end(line)){
00397                 return true;
00398             }
00399             if (check_keys_begin(line)){
00400                 parse_keys();
00401             }
00402             if (check_allow_link(line)){
00403                 continue;
00404             }
00405             if (check_allow_global(line)){
00406                 continue;
00407             }
00408             if (check_remove_key(line)){
00409             }
00410         }else{
00411             if (!current_.empty()){
00412                 // adding error messages for each unclosed entity
00413                 add_open_tag_error();
00414                 close_opened_tags();
00415             }
00416             return true;
00417         }
00418     }
00419     return true;
00420 }
00421 
00422 
00423 bool class_source_parser::parse_keys(){
00424     std::string line;
00425     do{
00426         if (! getline(line) ) { return false; }
00427         if (! check_valid(line)) {
00428             errors_.add_opened_entity_error(input_,line_,"Table config");
00429             add_open_tag_error();
00430             close_opened_tags();
00431             return false;
00432         }
00433         static const boost::regex value (get_key_value() );
00434         boost::smatch sub;
00435         bool res = boost::regex_match(line,sub,value);
00436         if (res){
00437             std::string type = sub[2];
00438             class_key key (sub[1],type,sub[3]);
00439             current_.back().add_key(key);
00440             if (types_.find(type) == types_.end()){
00441                 errors_.add_type_error(input_,line_,type);
00442             }
00443         }
00444     }while (! check_keys_end(line));
00445     return true;
00446 }
00447 
00448 
00449 bool class_source_parser::check_valid(const std::string &s){
00450     // s must be like " *" or "*"
00451     static const boost::regex valid (get_valid());
00452     return boost::regex_search(s,valid);
00453 }
00454 
00455 bool class_source_parser::check_wiki(const std::string& s){
00456     boost::regex wiki (get_wiki());
00457     return boost::regex_match(s,wiki);
00458 }
00459 
00460 bool class_source_parser::check_tag_begin(const std::string &s){
00461     // read tag;
00462     static boost::regex tag (get_tag_begin());
00463     boost::smatch sub;
00464     bool res = boost::regex_match(s,sub,tag);
00465     if (res){
00466         std::string link = sub[5];
00467         class_tag new_tag;
00468         new_tag.set_name(sub[1]);
00469         new_tag.set_min(sub[2]);
00470         new_tag.set_max(sub[3]);
00471         new_tag.set_super(link);
00472         current_.push_back(new_tag);
00473         if (! link.empty() &&
00474                 ! static_cast<const class_tag>(root_).find_tag(link,root_)){
00475             errors_.add_link_error(input_,line_,link);
00476         }
00477         return true;
00478     }
00479     return false;
00480 }
00481 
00482 bool class_source_parser::check_tag_end(const std::string &s){
00483     static const boost::regex endtag (get_tag_end());
00484     boost::smatch sub;
00485     bool res = boost::regex_match(s,sub,endtag);
00486     if (res){
00487         std::string name = sub[1];
00488         if (current_.empty()){
00489             errors_.add_unopened_entity_error(input_,line_,name);
00490             return false;
00491         }
00492         std::vector<class_tag>::iterator ii = current_.end();
00493         int count_opened = 0;
00494         do{
00495             --ii;
00496             if (ii->get_name() == name){
00497                 add_open_tag_error(count_opened);
00498                 close_opened_tags(++count_opened);
00499                 return true;
00500             }else{
00501                 count_opened ++;
00502             }
00503         }while (ii != current_.begin()) ;
00504     }
00505     return false;
00506 }
00507 
00508 bool class_source_parser::check_allow_link(const std::string &s){
00509     static const boost::regex allow_link (get_allow_link());
00510     boost::smatch sub;
00511     bool res = boost::regex_match(s,sub,allow_link);
00512     if (res){
00513         if (!current_.empty()){
00514             std::string link = sub[1];
00515             current_.back().add_link(link);
00516             if (static_cast<const class_tag>(root_).find_tag(link,root_) == NULL){
00517                 errors_.add_link_error(input_,line_,link);
00518             }
00519         }
00520     }
00521     return res;
00522 }
00523 
00524 bool class_source_parser::check_allow_global(const std::string &s){
00525     static const boost::regex allow_global (get_allow_global());
00526     boost::smatch sub;
00527     bool res = boost::regex_match(s,sub,allow_global);
00528     if (res){
00529         if (!current_.empty()){
00530             current_.back().add_link("global/"+sub[1]);
00531         }
00532     }
00533     return res;
00534 }
00535 
00536 bool class_source_parser::check_parent_begin(const std::string &s){
00537     static const boost::regex parent (get_parent_begin());
00538     boost::smatch sub;
00539     bool res = boost::regex_match(s,sub,parent);
00540     if (res){
00541         std::string name = sub[1];
00542         if (!parent_name_.empty()) {
00543             errors_.add_second_parent_error(input_,line_,parent_name_,name);
00544         }
00545         parent_name_ = name;
00546     }
00547     return res;
00548 }
00549 
00550 bool class_source_parser::check_parent_end(const std::string &s){
00551     static const boost::regex parent (get_parent_end());
00552     boost::smatch sub;
00553     bool res = boost::regex_match(s,sub,parent);
00554     if (res){
00555         std::string name = sub[1];
00556         if (parent_name_ == name) {
00557             parent_name_.clear();
00558         }else{
00559             errors_.add_unopened_entity_error(input_,line_,name);
00560         }
00561     }
00562     return true;
00563 }
00564 
00565 bool class_source_parser::check_keys_begin(const std::string &s){
00566     static const boost::regex keys (get_table_key_begin());
00567     return boost::regex_match(s,keys);
00568 
00569 }
00570 
00571 bool class_source_parser::check_keys_end(const std::string &s){
00572     static const boost::regex endkeys (get_table_end());
00573     bool res = boost::regex_match(s,endkeys);
00574     return res;
00575 }
00576 
00577 bool class_source_parser::check_allow_type(const std::string &s){
00578     static const boost::regex allow_type (get_allow_type());
00579     boost::smatch sub;
00580     bool res = boost::regex_match(s,sub,allow_type);
00581     if (res){
00582         std::string name = sub[1];
00583         std::string value = sub[2];
00584         try{
00585             boost::regex tmp (value);
00586         }catch(std::exception ){
00587             errors_.wrong_type_error(input_,line_,name,value);
00588             return true;
00589         }
00590         if(types_.find(name)!=types_.end()){
00591             errors_.overriding_type_error(input_,line_,name);
00592         }
00593         types_[name]=value;
00594         errors_.remove_type_errors(name);
00595     }
00596     return res;
00597 }
00598 bool class_source_parser::check_remove_type(const std::string &s){
00599     static const boost::regex remove_type (get_remove_type());
00600     boost::smatch sub;
00601     bool res = boost::regex_match(s,sub,remove_type);
00602     if (res){
00603         std::string name = sub[1];
00604         types_[name]="";
00605         forbidden_.push_back (name);
00606         errors_.remove_type_errors (name);
00607     }
00608     return res;
00609 }
00610 bool class_source_parser::check_remove_key(const std::string &s){
00611     static const boost::regex remove_key (get_remove_key());
00612     boost::smatch sub;
00613     bool res = boost::regex_match(s,sub,remove_key);
00614     if (res){
00615         if (! current_.empty ()){
00616             current_.back ().remove_key_by_name(sub[1]);
00617         }
00618     }
00619     return res;
00620 }
00621 } // namespace schema_generator
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:12 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs