[Top][All Lists]
[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 ¤t_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);
}
};
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Wesnoth-cvs-commits] wesnoth/src/serialization preprocessor.cpp prep...,
Guillaume Melquiond <=