wesnoth-cvs-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Wesnoth-cvs-commits] wesnoth/src/serialization preprocessor.cpp prep...


From: Guillaume Melquiond
Subject: [Wesnoth-cvs-commits] wesnoth/src/serialization preprocessor.cpp prep...
Date: Sat, 07 May 2005 12:11:23 -0400

CVSROOT:        /cvsroot/wesnoth
Module name:    wesnoth
Branch:         
Changes by:     Guillaume Melquiond <address@hidden>    05/05/07 16:11:22

Modified files:
        src/serialization: preprocessor.cpp preprocessor.hpp 

Log message:
        A first try at the new WML preprocessor. A bit slower (due to the huge 
amount of inlined comments). It allows for recursive macro argument 
preprocessing (bug #10995), nested conditional directives, full error location 
tracking, smart textdomain tracking. Moreover it is stream-friendly: no huge 
memory buffer.

CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/wesnoth/wesnoth/src/serialization/preprocessor.cpp.diff?tr1=1.11&tr2=1.12&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/wesnoth/wesnoth/src/serialization/preprocessor.hpp.diff?tr1=1.6&tr2=1.7&r1=text&r2=text

Patches:
Index: wesnoth/src/serialization/preprocessor.cpp
diff -u wesnoth/src/serialization/preprocessor.cpp:1.11 
wesnoth/src/serialization/preprocessor.cpp:1.12
--- wesnoth/src/serialization/preprocessor.cpp:1.11     Wed Apr 27 22:17:37 2005
+++ wesnoth/src/serialization/preprocessor.cpp  Sat May  7 16:11:22 2005
@@ -1,4 +1,4 @@
-/* $Id: preprocessor.cpp,v 1.11 2005/04/27 22:17:37 silene Exp $ */
+/* $Id: preprocessor.cpp,v 1.12 2005/05/07 16:11:22 silene Exp $ */
 /*
    Copyright (C) 2003 by David White <address@hidden>
    Copyright (C) 2005 by Guillaume Melquiond <address@hidden>
@@ -21,6 +21,7 @@
 
 #include "filesystem.hpp"
 #include "log.hpp"
+#include "wassert.hpp"
 #include "wesconfig.h"
 #include "serialization/preprocessor.hpp"
 #include "serialization/string_utils.hpp"
@@ -40,153 +41,563 @@
        };
 };
 
-static int const max_recursion_levels = 100;
+class preprocessor;
+class preprocessor_file;
+class preprocessor_data;
+class preprocessor_streambuf;
+struct preprocessor_deleter;
 
-//this function takes a macro and parses it into the macro followed by its
-//arguments. Arguments are seperated by spaces, but an argument appearing 
inside
-//braces is treated as a single argument.
-static std::vector<std::string> parse_macro_arguments(const std::string& macro)
-{
-       const std::vector<std::string> args = utils::split(macro, ' ');
-       std::vector<std::string> res;
-       if(args.empty()) {
-               res.push_back("");
-               return res;
-       }
-
-       res.push_back(args.front());
-
-       bool in_braces = false;
-       for(std::vector<std::string>::const_iterator i = args.begin()+1; i != 
args.end(); ++i) {
-               size_t begin = 0, end = i->size();
-               if((*i)[0] == '(') {
-                       ++begin;
-               }
+class preprocessor
+{
+       preprocessor *const old_preprocessor_;
+       std::string old_textdomain_;
+       std::string old_location_;
+       int old_linenum_;
+protected:
+       preprocessor_streambuf &target_;
+       preprocessor(preprocessor_streambuf &);
+public:
+       virtual bool get_chunk() = 0;
+       virtual ~preprocessor();
+};
 
-               if(!in_braces) {
-                       res.push_back("");
-               }
+class preprocessor_streambuf: public std::streambuf
+{
+       std::string out_buffer_;
+       virtual int underflow();
+       std::ostringstream buffer_;
+       preprocessor *current_;
+       preproc_map *defines_;
+       std::string textdomain_;
+       std::string location_;
+       int linenum_;
+       int depth_;
+       bool quoted_;
+       friend class preprocessor;
+       friend class preprocessor_file;
+       friend class preprocessor_data;
+       friend struct preprocessor_deleter;
+       preprocessor_streambuf(preprocessor_streambuf const &);
+public:
+       preprocessor_streambuf(preproc_map *);
+};
 
-               if((*i)[i->size()-1] == ')') {
-                       in_braces = false;
-                       --end;
-               }
+preprocessor_streambuf::preprocessor_streambuf(preproc_map *def)
+       : current_(NULL), defines_(def), textdomain_(PACKAGE),
+         depth_(0), quoted_(false)
+{
+}
 
-               res.back() += " " + i->substr(begin,end-begin);
-               utils::strip(res.back());
+preprocessor_streambuf::preprocessor_streambuf(preprocessor_streambuf const &t)
+       : std::streambuf(), current_(NULL), defines_(t.defines_),
+         textdomain_(PACKAGE), depth_(t.depth_), quoted_(t.quoted_)
+{
+}
 
-               if(begin == 1 && end == i->size()) {
-                       in_braces = true;
+int preprocessor_streambuf::underflow()
+{
+       if (gptr() < egptr())
+               return *gptr();
+       // the buffer has been completely read; fill it again
+       // keep part of the previous buffer, to ensure putback capabilities
+       unsigned sz = out_buffer_.size();
+       if (sz > 3) {
+               out_buffer_ = out_buffer_.substr(sz - 3);
+               sz = 3;
+       }
+       buffer_.str(std::string());
+       buffer_ << out_buffer_;
+       while (current_) {
+               if (current_->get_chunk()) {
+                       if (buffer_.str().size() >= 2000)
+                               break;
+               } else {
+                        // automatically restore the previous preprocessor
+                       delete current_;
                }
        }
+       out_buffer_ = buffer_.str();
+       char *begin = &*out_buffer_.begin();
+       unsigned bs = out_buffer_.size();
+       setg(begin, begin + sz, begin + bs);
+       if (sz >= bs)
+               return EOF;
+       return (unsigned char)*(begin + sz);
+}
 
-       return res;
+preprocessor::preprocessor(preprocessor_streambuf &t)
+       : old_preprocessor_(t.current_), target_(t)
+{
+       old_location_ = target_.location_;
+       old_linenum_ = target_.linenum_;
+       old_textdomain_ = target_.textdomain_;
+       ++target_.depth_;
+       target_.current_ = this;
+}
+
+preprocessor::~preprocessor()
+{
+       wassert(target_.current_ == this);
+       target_.current_ = old_preprocessor_;
+       target_.location_ = old_location_;
+       target_.linenum_ = old_linenum_;
+       target_.textdomain_ = old_textdomain_;
+       if (!old_location_.empty())
+               target_.buffer_ << "\376line " << old_linenum_
+                               << ' ' << old_location_ << '\n';
+       if (!old_textdomain_.empty())
+               target_.buffer_ << "\376textdomain " << old_textdomain_ << '\n';
+       --target_.depth_;
 }
 
-static void internal_preprocess_file(const std::string& fname,
-                                     preproc_map& defines_map,
-                                     int depth, std::ostream &out,
-                                     std::string const &included_from,
-                                     bool previous_was_newline);
-
-static void internal_preprocess_data(std::istream &data_in,
-                                     preproc_map& defines_map,
-                                     int depth, std::ostream &out,
-                                     std::string const &included_from,
-                                     bool need_linenum,
-                                     std::string const &current_directory)
+class preprocessor_file: preprocessor
 {
-       std::string data_str;
+       std::vector< std::string > files_;
+       std::vector< std::string >::const_iterator pos_, end_;
+public:
+       preprocessor_file(preprocessor_streambuf &, std::string const &);
+       virtual bool get_chunk();
+};
+
+class preprocessor_data: preprocessor
+{
+       struct token_desc
        {
-               //temporary, only here to accomodate the old preprocessor
-               std::stringstream tmp_in;
-               tmp_in << data_in.rdbuf();
-               data_str = tmp_in.str();
-       }
-       std::string const &data = data_str;
-
-       bool in_quotes = false;
-       int current_line = 1;
-       bool previous_was_newline = !need_linenum;
-
-       for(std::string::const_iterator i = data.begin(); i != data.end(); ++i) 
{
-               const char c = *i;
-               if(c == '"') {
-                       in_quotes = !in_quotes;
+               char type;
+               int stack_pos;
+               int linenum;
+       };
+       scoped_istream in_;
+       std::string directory_;
+       std::vector< std::string > strings_;
+       std::vector< token_desc > tokens_;
+       int slowpath_, skipping_, linenum_;
+
+       std::string read_word();
+       std::string read_line();
+       void skip_spaces();
+       void skip_eol();
+       void push_token(char);
+       void pop_token();
+       void put(char);
+       void put(std::string const &);
+public:
+       preprocessor_data(preprocessor_streambuf &, std::istream *,
+                         std::string const &name, int line,
+                         std::string const &dir, std::string const &domain);
+       virtual bool get_chunk();
+};
+
+preprocessor_file::preprocessor_file(preprocessor_streambuf &t, std::string 
const &name)
+       : preprocessor(t)
+{
+       if (is_directory(name))
+               get_files_in_dir(name, &files_, NULL, ENTIRE_FILE_PATH);
+       else
+               new preprocessor_data(t, istream_file(name), name, 1, 
directory_name(name), t.textdomain_);
+       pos_ = files_.begin();
+       end_ = files_.end();
+}
+
+bool preprocessor_file::get_chunk()
+{
+       while (pos_ != end_) {
+               std::string const &name = *(pos_++);
+               unsigned sz = name.size();
+               if (sz < 5 || !std::equal(name.begin() + sz - 4, name.end(), 
".cfg"))
+                       continue;
+               new preprocessor_file(target_, name);
+               return true;
+       }
+       return false;
+}
+
+preprocessor_data::preprocessor_data(preprocessor_streambuf &t, std::istream 
*i,
+                                     std::string const &name, int linenum,
+                                     std::string const &directory, std::string 
const &domain)
+       : preprocessor(t), in_(i), directory_(directory), slowpath_(0), 
skipping_(0), linenum_(linenum)
+{
+       std::ostringstream s;
+       s << name;
+       if (!t.location_.empty())
+               s << ' ' << t.linenum_ << ' ' << t.location_;
+       t.location_ = s.str();
+       t.linenum_ = linenum;
+       t.textdomain_ = domain;
+       t.buffer_ << "\376line " << linenum << ' ' << t.location_
+                 << "\n\376textdomain " << domain << '\n';
+       push_token('*');
+}
+
+void preprocessor_data::push_token(char t)
+{
+       token_desc token = { t, strings_.size(), linenum_ };
+       tokens_.push_back(token);
+       std::ostringstream s;
+       if (!skipping_) {
+               s << "\376line " << linenum_ << ' ' << target_.location_
+                 << "\n\376textdomain " << target_.textdomain_ << '\n';
+       }
+       strings_.push_back(s.str());
+}
+
+void preprocessor_data::pop_token()
+{
+       strings_.erase(strings_.begin() + tokens_.back().stack_pos, 
strings_.end());
+       tokens_.pop_back();
+}
+
+void preprocessor_data::skip_spaces()
+{
+       for(;;) {
+               int c = in_->peek();
+               if (!in_->good() || (c != ' ' && c != '\t'))
+                       return;
+               in_->get();
+       }
+}
+
+void preprocessor_data::skip_eol()
+{
+       for(;;) {
+               int c = in_->get();
+               if (c == '\n') {
+                       ++linenum_;
+                       return;
                }
+               if (!in_->good())
+                       return;
+       }
+}
 
-               if(c == '{') {
-                       int bracket_depth = 1;
-                       std::stringstream newfile;
-                       for(++i; i != data.end(); ++i) {
-                               if(*i == '{') {
-                                       bracket_depth++;
-                               } else if(*i == '}') {
-                                       bracket_depth--;
-                                       if(bracket_depth == 0) {
-                                               break;
-                                       }
-                               }
+std::string preprocessor_data::read_word()
+{
+       std::string res;
+       for(;;) {
+               int c = in_->peek();
+               if (!in_->good() || utils::portable_isspace(c))
+                       return res;
+               in_->get();
+               res += (char)c;
+       }
+}
 
-                               newfile << *i;
-                       }
+std::string preprocessor_data::read_line()
+{
+       std::string res;
+       for(;;) {
+               int c = in_->get();
+               if (c == '\n') {
+                       ++linenum_;
+                       return res;
+               }
+               if (!in_->good())
+                       return res;
+               if (c != '\r')
+                       res += (char)c;
+       }
+}
 
-                       if(i == data.end())
-                               break;
+void preprocessor_data::put(char c)
+{
+       if (skipping_)
+               return;
+       if (slowpath_) {
+               strings_.back() += c;
+               return;
+       }
+       if (linenum_ != target_.linenum_ && c != '\n' ||
+           linenum_ != target_.linenum_ + 1 && c == '\n') {
+               target_.linenum_ = linenum_;
+               if (c == '\n')
+                       --target_.linenum_;
+               target_.buffer_ << "\376line " << target_.linenum_
+                               << ' ' << target_.location_ << '\n';
+       }
+       if (c == '\n')
+               ++target_.linenum_;
+       target_.buffer_ << c;
+}
+
+void preprocessor_data::put(std::string const &s)
+{
+       if (skipping_)
+               return;
+       if (slowpath_) {
+               strings_.back() += s;
+               return;
+       }
+       target_.buffer_ << s;
+       target_.linenum_ += std::count(s.begin(), s.end(), '\n');
+       target_.linenum_ -= std::count(s.begin(), s.end(), '\376');
+}
+
+bool preprocessor_data::get_chunk()
+{
+       char c = in_->get();
+       if (c == '\n')
+               ++linenum_;
+       token_desc &token = tokens_.back();
+       if (!in_->good()) {
+               char const *s;
+               switch (token.type) {
+               case '*': return false; // everything is fine
+               case 'i':
+               case 'I':
+               case 'j':
+               case 'J': s = "#ifdef"; break;
+               case '"': s = "quoted string"; break;
+               case '[':
+               case '{': s = "macro substitution"; break;
+               case '(': s = "macro argument"; break;
+               default: s = "???";
+               }
+               std::ostringstream error;
+               error << s << " not terminated, started at "
+                     << token.linenum << ' ' << target_.location_;
+               ERR_CF << error.str() << '\n';
+               throw config::error(error.str());
+       }
 
-                       const std::string newfilename = newfile.str();
-                       std::vector<std::string> items = 
parse_macro_arguments(newfilename);
-                       const std::string symbol = items.front();
+       if (c == '\376') {
+               std::string buffer(1, c);
+               for(;;) {
+                       char d = in_->get();
+                       if (!in_->good() || d == '\n')
+                               break;
+                       buffer += d;
+               }
+               buffer += '\n';
+               put(buffer);
+       } else if (c == '"') {
+               if (token.type == '"') {
+                       target_.quoted_ = false;
+                       std::string tmp = strings_.back();
+                       pop_token();
+                       put(tmp);
+                       char &t = tokens_.back().type;
+                       if (t == '{')
+                               t = '[';
+               } else if (!target_.quoted_) {
+                       target_.quoted_ = true;
+                       push_token('"');
+               } else {
+                       std::ostringstream error;
+                       error << "nested quoted string started at "
+                             << linenum_ << ' ' << target_.location_;
+                       ERR_CF << error.str() << '\n';
+                       throw config::error(error.str());
+               }
+               put(c);
+       } else if (c == '{') {
+               push_token('{');
+               ++slowpath_;
+       } else if (c == ')' && token.type == '(') {
+               tokens_.pop_back();
+               strings_.push_back(std::string());
+       } else if (c == '#' && !target_.quoted_) {
+               std::string command = read_word();
+               if (command == "define") {
+                       skip_spaces();
+                       int linenum = linenum_;
+                       std::string s = read_line();
+                       put('\n');
+                       std::vector< std::string > items = utils::split(s, ' ');
+                       if (items.empty()) {
+                               std::ostringstream error;
+                               error << "no macro name found after #define 
directive at "
+                                     << linenum_ << ' ' << target_.location_;
+                               ERR_CF << error.str() << '\n';
+                               throw config::error(error.str());
+                       }
+                       std::string symbol = items.front();
+                       items.erase(items.begin());
+                       int found_enddef = 0;
+                       std::string buffer;
+                       for(;;) {
+                               char d = in_->get();
+                               if (!in_->good())
+                                       break;
+                               if (d == '\n')
+                                       ++linenum_;
+                               buffer += d;
+                               if (d == '#')
+                                       found_enddef = 1;
+                               else if (found_enddef > 0)
+                                       if (++found_enddef == 7)
+                                               if (std::equal(buffer.end() - 
6, buffer.end(), "enddef"))
+                                                       break;
+                                               else
+                                                       found_enddef = 0;
+                       }
+                       if (found_enddef != 7) {
+                               std::ostringstream error;
+                               error << "unterminated preprocessor definition 
at "
+                                     << linenum << ' ' << target_.location_;
+                               ERR_CF << error.str() << '\n';
+                               throw config::error(error.str());
+                       }
+                       if (!skipping_) {
+                               buffer.erase(buffer.end() - 7, buffer.end());
+                               target_.defines_->insert(std::make_pair(
+                                       symbol, preproc_define(buffer, items, 
target_.textdomain_,
+                                                              linenum + 1, 
target_.location_)));
+                               LOG_CF << "defining macro " << symbol << '\n';
+                       }
+               } else if (command == "ifdef") {
+                       skip_spaces();
+                       std::string symbol = read_word();
+                       bool skip = target_.defines_->count(symbol) == 0;
+                       LOG_CF << "testing for macro " << symbol << ": " << 
(skip ? "not defined" : "defined") << '\n';
+                       if (skip)
+                               ++skipping_;
+                       push_token(skip ? 'J' : 'i');
+               } else if (command == "else") {
+                       if (token.type == 'J') {
+                               pop_token();
+                               --skipping_;
+                               push_token('j');
+                       } else if (token.type == 'i') {
+                               pop_token();
+                               ++skipping_;
+                               push_token('I');
+                       } else {
+                               std::ostringstream error;
+                               error << "unexpected #else at "
+                                     << linenum_ << ' ' << target_.location_;
+                               ERR_CF << error.str() << '\n';
+                               throw config::error(error.str());
+                       }
+               } else if (command == "endif") {
+                       switch (token.type) {
+                       case 'I':
+                       case 'J': --skipping_;
+                       case 'i':
+                       case 'j': break;
+                       default:
+                               std::ostringstream error;
+                               error << "unexpected #endif at "
+                                     << linenum_ << ' ' << target_.location_;
+                               ERR_CF << error.str() << '\n';
+                               throw config::error(error.str());
+                       }
+                       pop_token();
+               } else if (command == "textdomain") {
+                       skip_spaces();
+                       std::string s = read_word();
+                       put("#textdomain ");
+                       put(s);
+                       target_.textdomain_ = s;
+               } else if (command == "enddef") {
+                       std::ostringstream error;
+                       error << "unexpected #enddef at "
+                             << linenum_ << ' ' << target_.location_;
+                       ERR_CF << error.str() << '\n';
+                       throw config::error(error.str());
+               }
+               skip_eol();
+       } else if (token.type == '{' || token.type == '[') {
+               if (c == '(') {
+                       if (token.type == '[')
+                               token.type = '{';
+                       else
+                               strings_.pop_back();
+                       push_token('(');
+               } else if (utils::portable_isspace(c)) {
+                       if (token.type == '[') {
+                               strings_.push_back(std::string());
+                               token.type = '{';
+                       }
+               } else if (c == '}') {
+                       --slowpath_;
+                       if (skipping_) {
+                               pop_token();
+                               return true;
+                       }
+                       if (token.type == '{') {
+                               wassert(strings_.back().empty());
+                               strings_.pop_back();
+                       }
 
+                       std::string symbol = strings_[token.stack_pos];
+                       std::string::size_type pos = symbol.find('\376');
+                       while (pos != std::string::npos) {
+                               std::string::iterator b = symbol.begin();
+                               symbol.erase(b + pos, b + symbol.find('\n', pos 
+ 1) + 1);
+                               pos = symbol.find('\376', pos);
+                       }
                        //if this is a known pre-processing symbol, then we 
insert
                        //it, otherwise we assume it's a file name to load
-                       if(defines_map.count(symbol) != 0) {
-                               items.erase(items.begin());
-
-                               const preproc_define& val = defines_map[symbol];
-                               if(val.arguments.size() != items.size()) {
-                                       ERR_CF << "preprocessor symbol '" << 
symbol << "' has "
-                                              << items.size() << " arguments, "
-                                              << val.arguments.size() << " 
expected: '" << newfilename << "'\n";
+                       preproc_map::const_iterator macro = 
target_.defines_->find(symbol),
+                                                   unknown_macro = 
target_.defines_->end();
+                       if (macro != unknown_macro) {
+                               preproc_define const &val = macro->second;
+                               size_t nb_arg = strings_.size() - 
token.stack_pos - 1;
+                               if (nb_arg != val.arguments.size()) {
+                                       std::ostringstream error;
+                                       error << "preprocessor symbol '" << 
symbol << "' expects "
+                                             << val.arguments.size() << " 
arguments, but has "
+                                             << nb_arg << " arguments at " << 
linenum_
+                                             << ' ' << target_.location_;
+                                       ERR_CF << error.str() << '\n';
+                                       throw config::error(error.str());
                                }
 
                                std::string str = val.value;
 
                                //substitute in given arguments
-                               for(size_t n = 0; n != val.arguments.size(); 
++n) {
-                                       const std::string& replace_with = (n < 
items.size()) ? items[n] : "";
-
-                                       int subs = 0;
-
-                                       const std::string item = "{" + 
val.arguments[n] + "}";
-                                       std::string::size_type pos = 
str.find(item);
-                                       while(pos != std::string::npos) {
-                                               ++subs;
-                                               
str.replace(pos,item.size(),replace_with);
-                                               const std::string::size_type 
new_pos = str.find(item);
-                                               if(new_pos < 
pos+replace_with.size()) {
+                               for(size_t n = 0; n < nb_arg; ++n) {
+                                       std::string const &replace_with = 
strings_[token.stack_pos + n + 1];
+                                       std::string item = '{' + 
val.arguments[n] + '}';
+                                       std::string::size_type pos = 
str.find(item), old_pos = 0;
+                                       int num = 0;
+                                       while (pos != std::string::npos) {
+                                               num += std::count(str.begin() + 
old_pos, str.begin() + pos, '\n');
+                                               num -= std::count(str.begin() + 
old_pos, str.begin() + pos, '\376');
+                                               std::ostringstream s;
+                                               s << "\376line " << val.linenum 
+ num << ' ' << val.location
+                                                 << "\n\376textdomain " << 
val.textdomain << '\n';
+                                               std::string replace = 
replace_with + s.str();
+                                               str.replace(pos, item.size(), 
replace);
+                                               old_pos = pos + replace.size();
+                                               pos = str.find(item);
+                                               if (pos < old_pos) {
                                                        ERR_CF << "macro 
substitution in symbol '" << symbol
-                                                              << "' could lead 
to infinite recursion. Aborting.\n";
+                                                              << "' could lead 
to infinite recursion at "
+                                                              << linenum_ << ' 
' << target_.location_
+                                                              << ". 
Aborting.\n";
                                                        break;
                                                }
-
-                                               pos = new_pos;
                                        }
                                }
 
-                               std::ostringstream from;
-                               if (!in_quotes && !included_from.empty()) {
-                                       from << " {" << symbol << "} " << 
current_line << included_from;
-                                       if (previous_was_newline)
-                                               out << "#line 0" << from.str();
+                               pop_token();
+                               std::string dir;
+                               std::string::size_type pos = 
val.location.find(' ');
+                               if (pos != std::string::npos)
+                                       dir = val.location.substr(0, pos);
+                               std::istream *buffer = new 
std::istringstream(str);
+                               if (!slowpath_) {
+                                       LOG_CF << "substituting macro " << 
symbol << '\n';
+                                       new preprocessor_data(target_, buffer, 
val.location,
+                                                             val.linenum, dir, 
val.textdomain);
+                               } else {
+                                       LOG_CF << "substituting (slow) macro " 
<< symbol << '\n';
+                                       std::ostringstream res;
+                                       preprocessor_streambuf *buf =
+                                               new 
preprocessor_streambuf(target_);
+                                       {       std::istream in(buf);
+                                               new preprocessor_data(*buf, 
buffer, val.location,
+                                                                     
val.linenum, dir, val.textdomain);
+                                               res << in.rdbuf(); }
+                                       delete buf;
+                                       strings_.back() += res.str();
                                }
-                               std::istringstream stream(str);
-                               internal_preprocess_data(stream, defines_map, 
depth, out,
-                                                        from.str(), 
!previous_was_newline, "");
-                       } else if(depth < 20) {
+                       } else if (target_.depth_ < 40) {
+                               pop_token();
                                std::string prefix;
                                std::string nfname;
-
+                               std::string const &newfilename = symbol;
 #ifdef USE_ZIPIOS
                                if(newfilename != "" && newfilename[0] == '~') {
                                        // I do not know of any valid use of 
{~xxx} when {xxx} is
@@ -220,7 +631,7 @@
                                                //being preprocessed
                                                nfname = newfilename;
                                                
nfname.erase(nfname.begin(),nfname.begin()+2);
-                                               nfname = current_directory + 
nfname;
+                                               nfname = directory_ + nfname;
                                        
                                        } else {
 #ifdef USE_ZIPIOS
@@ -234,206 +645,49 @@
                                                        nfname = "data/" + 
newfilename;
                                        }
 
-                                       std::ostringstream from;
-                                       if (!in_quotes && 
!included_from.empty())
-                                               from << ' ' << current_line << 
included_from;
-                                       internal_preprocess_file(nfname, 
defines_map, depth + 1, out,
-                                                                from.str(), 
previous_was_newline);
-                               }
-                       } else {
-                               scoped_istream stream = 
istream_file(newfilename);
-                               out << stream->rdbuf();
-                       }
-
-                       previous_was_newline = false;
-                       need_linenum = true;
-               } else if(c == '#' && !in_quotes) {
-                       //we are about to skip some things, so keep track of
-                       //the start of where we're skipping, so we can count
-                       //the number of newlines, so we can track the line 
number
-                       //in the source file
-                       const std::string::const_iterator begin = i;
-
-                       //if this is the beginning of a pre-processing 
definition
-                       static const std::string hash_define("#define");
-                       if(size_t(data.end() - i) > hash_define.size() &&
-                          std::equal(hash_define.begin(),hash_define.end(),i)) 
{
-
-                               i += hash_define.size();
-
-                               i = std::find_if(i,data.end(),isgraph);
-
-                               const std::string::const_iterator end = 
std::find_if(i, data.end(), utils::isnewline);
-
-                               if(end == data.end())
-                                       break;
-
-                               const std::string items(i,end);
-                               std::vector<std::string> args = 
utils::split(items, ' ');
-                               const std::string symbol = args.front();
-                               args.erase(args.begin());
-
-                               std::stringstream value;
-                               static const std::string hash_enddef("#enddef");
-                               for(i = end+1; i <= data.end() - 
hash_enddef.size(); ++i) {
-                                       
if(std::equal(hash_enddef.begin(),hash_enddef.end(),i)) {
-                                               break;
+                                       if (!slowpath_)
+                                               new preprocessor_file(target_, 
nfname);
+                                       else {
+                                               std::ostringstream res;
+                                               preprocessor_streambuf *buf =
+                                                       new 
preprocessor_streambuf(target_);
+                                               {       std::istream in(buf);
+                                                       new 
preprocessor_file(*buf, nfname);
+                                                       res << in.rdbuf(); }
+                                               delete buf;
+                                               strings_.back() += res.str();
                                        }
-
-                                       value << *i;
-                               }
-
-                               if(i > data.end() - hash_enddef.size()) {
-                                       throw config::error("pre-processing 
condition unterminated " +
-                                                           included_from + ": 
'" + items + "'");
                                }
-
-                               i += hash_enddef.size();
-
-                               
defines_map.insert(std::pair<std::string,preproc_define>(
-                                                   
symbol,preproc_define(value.str(),args)));
-                       }
-
-                       //if this is a pre-processing conditional
-                       static const std::string hash_ifdef("#ifdef");
-                       static const std::string hash_else("#else");
-                       static const std::string hash_endif("#endif");
-
-                       if(size_t(data.end() - i) > hash_ifdef.size() &&
-                          std::equal(hash_ifdef.begin(),hash_ifdef.end(),i)) {
-                               i += hash_ifdef.size();
-                               while(i != data.end() && 
utils::portable_isspace(*i))
-                                       ++i;
-
-                               const std::string::const_iterator end = 
std::find_if(i, data.end(),
-                                                                               
     utils::portable_isspace);
-
-                               if(end == data.end())
-                                       break;
-
-                               //if the symbol is not defined, then we want to 
skip
-                               //to the #endif or #else . Otherwise, continue 
processing
-                               //as normal. The #endif will just be treated as 
a comment
-                               //anyway.
-                               const std::string symbol(i,end);
-                               if(defines_map.count(symbol) == 0) {
-                                       while(size_t(data.end() - i) > 
hash_endif.size() &&
-                                             
!std::equal(hash_endif.begin(),hash_endif.end(),i) &&
-                                             
!std::equal(hash_else.begin(),hash_else.end(),i)) {
-                                               ++i;
-                                       }
-
-                                       i = std::find_if(i,data.end(), 
utils::isnewline);
-                                       if(i == data.end())
-                                               break;
-                               } else {
-                                       i = end;
-                               }
-                       }
-
-                       //if we come across a #else, it must mean that we found 
a #ifdef
-                       //earlier, and we should ignore until #endif
-                       if(size_t(data.end() - i) > hash_else.size() &&
-                          std::equal(hash_else.begin(),hash_else.end(),i)) {
-                               while(size_t(data.end() - i) > 
hash_endif.size() &&
-                                     
!std::equal(hash_endif.begin(),hash_endif.end(),i)) {
-                                       ++i;
-                               }
-
-                               i = std::find_if(i, data.end(), 
utils::isnewline);
-                               if(i == data.end())
-                                       break;
-                       }
-
-                       static const std::string hash_textdomain("#textdomain");
-                       //if we find a #textdomain directive, pass it untouched
-                       if(size_t(data.end() - i) > hash_textdomain.size() &&
-                                       
std::equal(hash_textdomain.begin(),hash_textdomain.end(),i)) {
-
-                               i += hash_textdomain.size();
-                               while(i != data.end() && 
utils::portable_isspace(*i))
-                                       ++i;
-
-                               const std::string::const_iterator end = 
std::find_if(i, data.end(),
-                                               utils::portable_isspace);
-                               if(end == data.end())
-                                       break;
-                               const std::string symbol(i,end);
-                               //put the textdomain to the output stream
-                               out << hash_textdomain << " " << symbol;
-                       }
-
-                       i = std::find_if(i, data.end(), utils::isnewline);
-
-                       if(i == data.end())
-                               break;
-
-                       out.put('\n');
-                       current_line += std::count(begin, i, '\n');
-                       need_linenum = true;
-                       goto linenum_output;
-               } else {
-                       if (c == '\n') {
-                               linenum_output:
-                               if (need_linenum && !in_quotes && 
!included_from.empty()) {
-                                       out << "#line " << current_line << 
included_from;
-                                       need_linenum = false;
-                               } else
-                                       out.put('\n');
-                               ++current_line;
-                               previous_was_newline = true;
                        } else {
-                               out.put(c);
-                               if ((unsigned)c > 32)
-                                       previous_was_newline = false;
+                               ERR_CF << "too much nested preprocessing 
inclusions at "
+                                      << linenum_ << ' ' << target_.location_
+                                      << ". Aborting.\n";
+                               pop_token();
                        }
+               } else {
+                       strings_.back() += c;
+                       token.type = '[';
                }
-       }
+       } else
+               put(c);
+       return true;
 }
 
-static void internal_preprocess_file(const std::string& fname,
-                                     preproc_map& defines_map,
-                                     int depth, std::ostream &out,
-                                     std::string const &included_from,
-                                     bool previous_was_newline)
-{
-       //if it's a directory, we process all files in the directory
-       //that end in .cfg
-       if(is_directory(fname)) {
-
-               std::vector<std::string> files;
-               get_files_in_dir(fname,&files,NULL,ENTIRE_FILE_PATH);
-
-               for(std::vector<std::string>::const_iterator f = files.begin();
-                   f != files.end(); ++f) {
-                       if(is_directory(*f) || f->size() > 4 && 
std::equal(f->end()-4,f->end(),".cfg")) {
-                               internal_preprocess_file(*f, defines_map, 
depth, out, included_from, true);
-                       }
-               }
-
-               return;
-       }
-
-       std::string from;
-       if (!included_from.empty()) {
-               from = ' ' + fname + included_from;
-               if (previous_was_newline)
-                       out << "#line 0" << from;
-       }
-       scoped_istream stream = istream_file(fname);
-       internal_preprocess_data(*stream, defines_map, depth, out, from, 
!previous_was_newline,
-                                directory_name(fname));
-}
+struct preprocessor_deleter: std::istream
+{
+       preprocessor_streambuf *buf_;
+       preprocessor_deleter(preprocessor_streambuf *buf): std::istream(buf), 
buf_(buf) {}
+       ~preprocessor_deleter() { rdbuf(NULL); delete buf_->defines_; delete 
buf_; }
+};
 
 std::istream *preprocess_file(std::string const &fname,
                               preproc_map const *defines)
 {
        log_scope("preprocessing file...");
-       preproc_map defines_copy;
-       if(defines != NULL)
-               defines_copy = *defines;
-
-       std::stringstream *stream = new std::stringstream;
-       internal_preprocess_file(fname, defines_copy, 0, *stream, "\n", true);
-       return stream;
+       preproc_map *defines_copy = new preproc_map;
+       if (defines)
+               *defines_copy = *defines;
+       preprocessor_streambuf *buf = new preprocessor_streambuf(defines_copy);
+       new preprocessor_file(*buf, fname);
+       return new preprocessor_deleter(buf);
 }
Index: wesnoth/src/serialization/preprocessor.hpp
diff -u wesnoth/src/serialization/preprocessor.hpp:1.6 
wesnoth/src/serialization/preprocessor.hpp:1.7
--- wesnoth/src/serialization/preprocessor.hpp:1.6      Wed Apr 27 22:17:37 2005
+++ wesnoth/src/serialization/preprocessor.hpp  Sat May  7 16:11:22 2005
@@ -1,4 +1,4 @@
-/* $Id: preprocessor.hpp,v 1.6 2005/04/27 22:17:37 silene Exp $ */
+/* $Id: preprocessor.hpp,v 1.7 2005/05/07 16:11:22 silene Exp $ */
 /*
    Copyright (C) 2003 by David White <address@hidden>
    Copyright (C) 2005 by Guillaume Melquiond <address@hidden>
@@ -23,10 +23,14 @@
 {
        preproc_define() {}
        explicit preproc_define(std::string const &val) : value(val) {}
-       preproc_define(std::string const &val, std::vector< std::string > const 
&args)
-               : value(val), arguments(args) {}
+       preproc_define(std::string const &val, std::vector< std::string > const 
&args,
+                      std::string const &domain, int line, std::string const 
&loc)
+               : value(val), arguments(args), textdomain(domain), 
linenum(line), location(loc) {}
        std::string value;
        std::vector< std::string > arguments;
+       std::string textdomain;
+       int linenum;
+       std::string location;
        bool operator==(preproc_define const &) const;
        bool operator!=(preproc_define const &v) const { return !operator==(v); 
}
 };




reply via email to

[Prev in Thread] Current Thread [Next in Thread]