The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
sourceparser.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2016 by Sytyi Nick <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 /**
15  * @file
16  * This file contains implementation of sourceparser.cpp.
17  */
18 
20 
21 #include <boost/regex.hpp>
22 
23 #include <stack>
24 
25 namespace schema_validation{
26 /** Little parts of regex templates used to parse Wml annoations.
27  *For details, look http://wiki.wesnoth.org/WML_Annotation_Format , please
28  */
29 /** line is valid*/
30 const std::string valid = "^\\s*\\*\\s*";
31 /** begining of wiki block*/
32 const std::string wiki_begin ="^\\s*/\\*(?:WIKI|SCHEMA)";
33 /** whitespace is possible*/
34 const std::string space ="\\s*";
35 /** sigh "="*/
36 const std::string equals ="=";
37 /** non-mandatory sign "*/
39 /** begining of the block.*/
40 const std::string block_begin="@begin" ;
41 /** end of block*/
42 const std::string block_end ="@end";
43 /** allow directive*/
44 const std::string allow="@allow";
45 /** remove directive*/
46 const std::string remove="@remove";
47 /** sign "{" - curly bracket*/
49 /** sign "}" - another curly bracket*/
51 /** type of possible name identificator*/
52 const std::string name_type= "[a-z][a-zA-Z0-9_-]*" ;
53 /** type of possible parent indentificator*/
54 const std::string parent_type= "/|(?:[a-z][a-zA-Z0-9_-]*/)+";
55 /** type of possible link indentificator*/
56 const std::string link_type="(?:[a-z][a-zA-Z0-9_-]*/)*(?:[a-z][a-zA-Z0-9_-]*)";
57 /** template to number regex*/
58 const std::string number="\\d*";
59 /** sign "-" - hyphen-minus used to set sign of signed integer*/
60 const std::string sign="-?";
61 
62 /**
63  * end of line + possible various character before.
64  * Used to close template. ".*" is used cause I dont want to get error
65  * every misprint whitespace
66  * after annotation element
67  */
68 const std::string eol=".*$";
69 
70 /** Private function to surround an argument with brackets.
71  * This allows substitutions :-)
72  */
73 static std::string sub (const std::string & s){
74  return "(" + s + ")";
75 }
76 
77 /** Private function to surround argument with not mandatory quotes.
78  * Is used when creating properties
79  */
80 static std::string quote(const std::string & s){
81  return quote_symbol + s + quote_symbol ;
82 }
83 
84 /**
85  * Creates a property template
86  * @param name Name of property
87  * @param value Type of property value
88  * If value is empty creates simple property like {table}
89  * Else creates a named property like {name="[name_type_template]"}
90  */
92  const std::string & value = ""){
93  if (value.empty()){
94  return property_open + name + property_close;
95  }
96  return property_open + name + equals + quote(value) + property_close;
97 }
98 
99 
101  return valid;
102 }
103 
105  static std::string wiki = wiki_begin + eol;
106  return wiki;
107 }
108 
110  static std::string parent_begin = valid + block_begin
111  + property("parent")
112  + property("name",sub(parent_type))
113  + eol;
114  return parent_begin;
115 }
116 
118  static std::string parent_end = valid + block_end + property("parent")
119  + property("name",sub(parent_type))+ eol;
120  return parent_end;
121 }
122 
124  static std::string tag_begin = valid + block_begin + property("tag")
125  + property("name",sub(name_type))
126  + property("min",sub(number))
127  + property("max",sub(sign + number))
128  + sub(property("super",sub(link_type)))
129  +"?" + eol;
130  /* sub(property("super"),sub(link_type))+"?"
131  * property super is not mandatory
132  */
133  return tag_begin;
134 }
135 
136 
138  static std::string tag_end = valid + block_end + property("tag")
139  + property("name",sub(name_type)) + eol;
140  return tag_end;
141 }
142 
143 
145  static std::string allow_link = valid + allow + property("link")
146  +property("name",sub(link_type)) + eol;
147  return allow_link;
148 }
149 
151  static std::string global_link = valid + allow + property("global")
152  +property("name",sub(name_type)) + eol;
153  return global_link;
154 }
155 
156 static const std::string & get_allow_type(){
157  static std::string allow_type = valid + allow + property("type")
158  +property("name",sub(name_type))
159  +property("value",sub("\\^.+\\$"))
160  + eol;
161  return allow_type;
162 }
163 static const std::string & get_remove_type(){
164  static std::string remove_type = valid + remove + property("type")
165  +property("name",sub(name_type))
166  + eol;
167  return remove_type;
168 }
169 static const std::string & get_remove_key(){
170  static std::string remove_key = valid + remove + property("key")
171  +property("name",sub(name_type))
172  + eol;
173  return remove_key;
174 }
175 
177  static std::string keys_begin = valid + block_begin + property("table")
178  + property("config")+ eol;
179  return keys_begin;
180 }
181 
183  static std::string table_end = valid + block_end + property("table")
184  + eol;
185  return table_end;
186 }
187 
188 
190  static std::string key_value = valid + sub("[a-zA-z][a-zA-Z\\d_+-]*")
191  + "\\s*&\\s*"+sub(name_type)
192  +"\\s*&\\s?"+ sub(quote("[a-zA-Z._0-9+-]*"))
193  +"\\s&"+ eol;
194  return key_value;
195 }
196 
197 void test_regex( std::ostream & f ){
198  f << get_valid() << "\n"
199  << get_wiki() << "\n"
200  << get_parent_begin() << "\n"
201  << get_parent_end() << "\n"
202  << get_tag_begin() << "\n"
203  << get_tag_end() << "\n"
204  << get_allow_link() << "\n"
205  << get_allow_global() << "\n"
206  << get_table_key_begin() << "\n"
207  << get_table_end() << "\n"
208  << get_key_value() << "\n"
209  << get_allow_type() << "\n"
210  << std::endl;
211 }
212 
214 
215  std::fstream out;
216  if (output_.empty()){
217  return false;
218  }
219  out.open(output_.c_str(),std::ios::out|std::ios::trunc);
220  if (out.fail()){
221  errors_.add_simple_error("Cannot open file "+output_+
222  "\n Output would not be stored\n");
223  return false;
224  }
225  // remove all forbidden keys
226  for (std::vector<std::string>::const_iterator i= forbidden_.begin ();
227  i != forbidden_.end (); ++i){
229  types_.erase (*i);
230  }
231  out << "[wml_schema]\n";
233  i!= types_.end();++i){
234  out << " [type]\n"
235  << " name=" << i->first << "\n"
236  << " value=\""<< i->second << "\"\n"
237  <<" [/type]\n";
238  }
239  root_.print(out);
240  out << "[/wml_schema]\n";
241  out.close();
242  return true; // @TODO add error support
243 }
244 
245 
247  if (f_.fail()){
249  return false;
250  }
251  std::getline(f_,s);
252  line_ ++;
253  if (! f_.eof()){
254  if (f_.fail()){
256  return false;
257  }
258  }
259  return true;
260 }
261 // call without arg when you want to closethem all
264  for (it = current_.begin(); it != current_.end() && i > 0; ++it){
266  --i;
267  }
268 }
269 // call without arg when you want to closethem all
271  if (current_.empty()){
272  return;
273  }
274  std::stack<std::string> error_cache ;
275  while (current_.size() > 1){
276  if (i==0){
277  break;
278  }
279  class_tag tag (current_.back());
280  current_.pop_back();
281  current_.back().add_tag(tag);
282  error_cache.push(tag.get_name());
283  i--;
284  }
285  if (i!=0){
286  //adding to parent
287  if (parent_name_.empty()) {
288  orphan_tags_.push_back(current_.back());
289  errors_.add_orphan_error(input_,line_,current_.back().get_name());
290  }else{
291  error_cache.push(current_.back().get_name());
293  }
294  current_.pop_back();
295  }
296  std::string name_to_remove_from_cache = parent_name_;
297  for (std::vector<class_tag>::const_iterator ii = current_.begin();
298  ii!= current_.end();++ii){
299  name_to_remove_from_cache += ii->get_name() + "/";
300  }
301  while (! error_cache.empty()){
302  name_to_remove_from_cache += error_cache.top();
303  errors_.remove_link_errors(name_to_remove_from_cache);
304  error_cache.pop();
305  name_to_remove_from_cache += "/";
306  }
307 
308 }
309 
310 
312  if (input_.empty()){
313  errors_.add_simple_error("File was not defined\n");
314  // Use to hack sorting errors.
315  // Item with will be at the end of error list.
316  return false;
317  }
318 
319  f_.open(input_.c_str(),std::ios::in);
320  if (f_.fail()){
321  errors_.add_simple_error("File "+input_ + " cannot be opened\n");
322  return false;
323  }
324  line_ = 0;
325  bool result = true;
326  while (!f_.eof()){
327  std::string line;
328  if (! getline(line) ) {
329  f_.close();
330  f_.clear();
331 
333  parent_name_.clear();
334  return false;
335  } // is used to avoid exceptions.
336 
337  if (check_wiki(line)) {
338  result = parse_block();
339  if (! result) {
340  break;
341  }
342  }
343  }
344 
345  f_.close();
346 
347  // Clear all flags ( eg the eof flag ) after closing the file.
348  // This will let us reuse the same fstream variable for different files.
349  f_.clear();
350 
352  parent_name_.clear();
353  return result;
354 }
355 
356 
358  while (!f_.eof()){
359  std::string line;
360  if (! getline(line) ) { return false; }
361  if ( check_valid(line)) {
362  if (check_allow_type(line)) continue;
363  if (check_remove_type(line)) continue;
364  if (check_parent_begin(line)) continue;
365 
366  if (check_tag_begin(line)){
367  parse_tag();
368  continue;
369  }
370  check_parent_end(line);
371  }
372  else{
373  // wiki-block is closed. checking stack of opened tags
374  if (!current_.empty()){
377  // continue working
378  }
379  // block is closed and all tags are closed.
380  return true;
381 
382  }
383  }// end while
384  return false;
385 }
386 
388  while (!f_.eof()){
389  std::string line;
390  if (! getline(line) ) { return false; }
391  if (check_valid(line)) {
392  if (check_tag_begin(line)){
393  parse_tag();
394  }
395  if (check_tag_end(line)){
396  return true;
397  }
398  if (check_keys_begin(line)){
399  parse_keys();
400  }
401  if (check_allow_link(line)){
402  continue;
403  }
404  if (check_allow_global(line)){
405  continue;
406  }
407  if (check_remove_key(line)){
408  }
409  }else{
410  if (!current_.empty()){
411  // adding error messages for each unclosed entity
414  }
415  return true;
416  }
417  }
418  return true;
419 }
420 
421 
423  std::string line;
424  do{
425  if (! getline(line) ) { return false; }
426  if (! check_valid(line)) {
430  return false;
431  }
432  static const boost::regex value (get_key_value() );
433  boost::smatch sub;
434  bool res = boost::regex_match(line,sub,value);
435  if (res){
436  std::string type = sub[2];
437  class_key key (sub[1],type,sub[3]);
438  current_.back().add_key(key);
439  if (types_.find(type) == types_.end()){
441  }
442  }
443  }while (! check_keys_end(line));
444  return true;
445 }
446 
447 
449  // s must be like " *" or "*"
450  static const boost::regex valid (get_valid());
451  return boost::regex_search(s,valid);
452 }
453 
455  boost::regex wiki (get_wiki());
456  return boost::regex_match(s,wiki);
457 }
458 
460  // read tag;
461  static boost::regex tag (get_tag_begin());
462  boost::smatch sub;
463  bool res = boost::regex_match(s,sub,tag);
464  if (res){
465  std::string link = sub[5];
466  class_tag new_tag;
467  new_tag.set_name(sub[1]);
468  new_tag.set_min(sub[2]);
469  new_tag.set_max(sub[3]);
470  new_tag.set_super(link);
471  current_.push_back(new_tag);
472  if (! link.empty() &&
473  ! static_cast<const class_tag>(root_).find_tag(link,root_)){
475  }
476  return true;
477  }
478  return false;
479 }
480 
482  static const boost::regex endtag (get_tag_end());
483  boost::smatch sub;
484  bool res = boost::regex_match(s,sub,endtag);
485  if (res){
486  std::string name = sub[1];
487  if (current_.empty()){
489  return false;
490  }
492  int count_opened = 0;
493  do{
494  --ii;
495  if (ii->get_name() == name){
496  add_open_tag_error(count_opened);
497  close_opened_tags(++count_opened);
498  return true;
499  }else{
500  count_opened ++;
501  }
502  }while (ii != current_.begin()) ;
503  }
504  return false;
505 }
506 
508  static const boost::regex allow_link (get_allow_link());
509  boost::smatch sub;
510  bool res = boost::regex_match(s,sub,allow_link);
511  if (res){
512  if (!current_.empty()){
513  std::string link = sub[1];
514  current_.back().add_link(link);
515  if (static_cast<const class_tag>(root_).find_tag(link,root_) == nullptr){
517  }
518  }
519  }
520  return res;
521 }
522 
524  static const boost::regex allow_global (get_allow_global());
525  boost::smatch sub;
526  bool res = boost::regex_match(s,sub,allow_global);
527  if (res){
528  if (!current_.empty()){
529  current_.back().add_link("global/"+sub[1]);
530  }
531  }
532  return res;
533 }
534 
536  static const boost::regex parent (get_parent_begin());
537  boost::smatch sub;
538  bool res = boost::regex_match(s,sub,parent);
539  if (res){
540  std::string name = sub[1];
541  if (!parent_name_.empty()) {
543  }
544  parent_name_ = name;
545  }
546  return res;
547 }
548 
550  static const boost::regex parent (get_parent_end());
551  boost::smatch sub;
552  bool res = boost::regex_match(s,sub,parent);
553  if (res){
554  std::string name = sub[1];
555  if (parent_name_ == name) {
556  parent_name_.clear();
557  }else{
559  }
560  }
561  return true;
562 }
563 
565  static const boost::regex keys (get_table_key_begin());
566  return boost::regex_match(s,keys);
567 
568 }
569 
571  static const boost::regex endkeys (get_table_end());
572  bool res = boost::regex_match(s,endkeys);
573  return res;
574 }
575 
577  static const boost::regex allow_type (get_allow_type());
578  boost::smatch sub;
579  bool res = boost::regex_match(s,sub,allow_type);
580  if (res){
581  std::string name = sub[1];
582  std::string value = sub[2];
583  try{
584  boost::regex tmp (value);
585  }catch(std::exception ){
586  errors_.wrong_type_error(input_,line_,name,value);
587  return true;
588  }
589  if(types_.find(name)!=types_.end()){
591  }
592  types_[name]=value;
594  }
595  return res;
596 }
598  static const boost::regex remove_type (get_remove_type());
599  boost::smatch sub;
600  bool res = boost::regex_match(s,sub,remove_type);
601  if (res){
602  std::string name = sub[1];
603  types_[name]="";
604  forbidden_.push_back (name);
606  }
607  return res;
608 }
610  static const boost::regex remove_key (get_remove_key());
611  boost::smatch sub;
612  bool res = boost::regex_match(s,sub,remove_key);
613  if (res){
614  if (! current_.empty ()){
615  current_.back ().remove_key_by_name(sub[1]);
616  }
617  }
618  return res;
619 }
620 } // namespace schema_generator
const std::string & get_parent_end()
Template to check closing of parent block.
void test_regex(std::ostream &f)
Writes to the file regex templates list.
void remove_keys_by_type(const std::string &type)
Removes all keys with this type.
Definition: tag.cpp:158
const std::string parent_type
type of possible parent indentificator
const std::string & get_allow_global()
Template to check allow{global} block.
bool parse_source()
Parses file line-by-line, checking every line to open WIKI block Please, notice that main input work ...
static const std::string & get_allow_type()
bool check_valid(const std::string &s)
check the input line with a template check if the line is valid (still in block)
const std::string & get_table_key_begin()
Template to check begining of table{config} storing key values.
static const std::string & get_remove_type()
void remove_link_errors(const std::string &link)
Clears link cache.
std::vector< class_tag > current_
Stack of opened tags.
bool check_remove_key(const std::string &s)
Checks removed keys.
void add_orphan_error(const std::string &file, int line, const std::string &name)
Generate and put GCC-style error message about tag without parent.
bool check_wiki(const std::string &s)
Read tag form the line and add it to stack.
bool getline(std::string &s)
Gets a line from file and returns it.
const std::string block_end
end of block
const std::string & get_tag_begin()
Template to check if line contains opening of tag block.
const std::string & get_table_end()
Template to check if table is closed.
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
bool check_keys_begin(const std::string &s)
Checks beginning of keys.
This file contains sourceparser object, collecting annotations and building a tag tree...
const std::string & get_valid()
A few regex templates.
bool parse_tag()
Parses lines inside tag block.
std::map< std::string, std::string > types_
Allowed types.
bool parse_keys()
Read key table and add keys to tag on the top of the stack.
bool check_tag_begin(const std::string &s)
Checks line for tag annotation.
void add_second_parent_error(const std::string &file, int line, const std::string &first, const std::string &second)
Generate and put GCC-style error message about opening parent block before before closing previous bl...
const std::string number
template to number regex
void set_name(const std::string &name)
Definition: tag.hpp:205
const std::string & get_tag_end()
Template to check end of tag block.
class_key is used to save the information about one key.
Definition: tag.hpp:37
std::string output_
name of output file to print schema
bool check_parent_begin(const std::string &s)
Opens parrent block.
bool save_schema()
Saves tag tree to schema file.
GLuint in
Definition: glew.h:9261
void close_opened_tags(int i)
Сhecks stack of opened tags.
const std::string & get_key_value()
Template to get key value.
GLuint64EXT * result
Definition: glew.h:10727
const std::string sign
sign "-" - hyphen-minus used to set sign of signed integer
static std::string sub(const std::string &s)
Private function to surround an argument with brackets.
int line_
number of current read line.
std::vector< class_tag > orphan_tags_
List of tags without parents.
const std::string name_type
type of possible name identificator
std::vector< std::string > forbidden_
Types to remove.
bool check_tag_end(const std::string &s)
Puts closed tag to child list of previous tag.
static const std::string & get_remove_key()
GLsizei const GLfloat * value
Definition: glew.h:1817
void add_link_error(const std::string &file, int line, const std::string &link)
Generate and put GCC-style error message about failed link to link cache.
void add_opened_entity_error(const std::string &file, int line, const std::string &name)
Generate and put GCC-style error message about annotation block somebody missed to close...
void set_super(std::string const &s)
Definition: tag.hpp:226
std::string parent_name_
Name of current parent.
const std::string block_begin
begining of the block.
bool check_allow_type(const std::string &s)
Checks allowed types.
bool check_keys_end(const std::string &s)
Checks end of keys.
const std::string eol
end of line + possible various character before.
const std::string equals
sigh "="
bool check_allow_link(const std::string &s)
Checks links.
void add_type_error(const std::string &file, int line, const std::string &type)
Generate and put GCC-style error message about unknown type to type cache.
void overriding_type_error(const std::string &file, int line, const std::string &type)
Generate and put GCC-style error message about overriding type Overriding means that that type was de...
GLuint res
Definition: glew.h:9258
void add_simple_error(const std::string &message)
Puts simple error message in container.
static std::string property(const std::string &name, const std::string &value="")
Creates a property template.
void remove_type_errors(const std::string &type)
Clears type cache.
const std::string & get_allow_link()
Template to check allow{link} block.
const std::string space
whitespace is possible
bool parse_block()
Parses WIKI block line-by-line, checking every line to open annotation block.
class_tag root_
Root of the schema tree.
size_t i
Definition: function.cpp:1057
const std::string quote_symbol
non-mandatory sign "
GLuint const GLchar * name
Definition: glew.h:1782
const std::string property_close
sign "}" - another curly bracket
void add_tag(const class_tag &new_tag)
Definition: tag.hpp:232
static std::string quote(const std::string &s)
Private function to surround argument with not mandatory quotes.
void add_open_tag_error(int i)
Generates errors for each opened tag.
const std::string & get_wiki()
Template to check line is beginnnig of Wiki block.
const std::string allow
allow directive
void wrong_type_error(const std::string &file, int line, const std::string &name, const std::string &value)
Generate and put GCC-style error message about wrong type value.
const std::string link_type
type of possible link indentificator
const std::string & get_parent_begin()
Template to check begining of parent block.
const std::string wiki_begin
begining of wiki block
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
std::string input_
name of input file to be parsed
GLdouble s
Definition: glew.h:1358
bool check_parent_end(const std::string &s)
Closes parent block.
void add_unopened_entity_error(const std::string &file, int line, const std::string &name)
Generate and put GCC-style error message about closing block that wasn't opened.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
Stores information about tag.
Definition: tag.hpp:116
void print(std::ostream &os)
Prints information about tag to outputstream, recursively is used to print tag info the format is nex...
Definition: tag.cpp:97
bool check_remove_type(const std::string &s)
Checks removed types.
const std::string valid
Little parts of regex templates used to parse Wml annoations.
const std::string property_open
sign "{" - curly bracket
GLclampf f
Definition: glew.h:3024
class_error_container errors_
used to store errors
void add_read_error(const std::string &file, int line)
Generate and put GCC-style error message about error read file error.
bool check_allow_global(const std::string &s)
Checks allowed global tags.