38 #define ERR_PREPROC LOG_STREAM(err, log_preprocessor)
39 #define WRN_PREPROC LOG_STREAM(warn, log_preprocessor)
40 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor)
41 #define DBG_PREPROC LOG_STREAM(debug, log_preprocessor)
86 static int current_file_number = 0;
90 fnum = ++current_file_number;
93 std::ostringstream shex;
94 shex << std::hex << fnum;
109 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
155 const std::string key =
"argument";
165 const std::string key =
"argument";
176 const std::string key =
"preproc_define";
193 for(
const std::string& arg :
arguments) {
215 value = cfg[
"value"].str();
236 return preproc_map::value_type(cfg[
"name"], second);
241 return stream <<
"value: " << def.
value <<
" arguments: " << def.
location;
244 std::ostream&
operator<<(std::ostream& stream,
const preproc_map::value_type& def)
246 return stream << def.second;
333 void error(
const std::string&,
int);
334 void warning(
const std::string&,
int);
336 template<
typename T,
typename... A>
410 , old_textdomain_(
t.textdomain_)
411 , old_location_(
t.location_)
412 , old_linenum_(
t.linenum_)
425 if(
char* gp = gptr()) {
447 const int desired_fill_amount = 2000;
449 while(
current() &&
buffer_.rdbuf()->in_avail() < desired_fill_amount) {
466 setg(begin, begin + sz, begin + bs);
472 return static_cast<unsigned char>(*(begin + sz));
502 unsigned nested_level = 0;
522 if(pos.size() <= 2 * nested_level) {
532 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
537 const std::string&
line = *(
i++);
540 res += included_from;
561 std::string position,
error;
562 std::ostringstream pos;
567 error = error_type +
'\n';
568 error +=
"at " + position;
578 std::ostringstream pos;
605 virtual void init()
override;
614 const std::string& name = *(
pos_++);
615 unsigned sz = name.size();
618 if(sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4,
"gfc.")) {
631 std::vector<std::string>::const_iterator
pos_,
end_;
728 void put(
const std::string& );
734 const std::string& history,
735 const std::string& name,
737 const std::string& dir,
738 const std::string& domain,
739 std::unique_ptr<std::map<std::string, std::string>> defines,
740 bool is_define =
false);
757 throw std::logic_error(
"don't compare tokens with characters");
767 return !(lhs == rhs);
791 for(
const std::string& fname :
files_) {
792 std::size_t cpos = fname.rfind(
" ");
794 if(cpos != std::string::npos && cpos >= symbol_index) {
795 std::stringstream ss;
797 <<
"' in included directory '" << name <<
"'.\nThe included symbol probably looks similar to '"
820 if(!file_stream->good()) {
832 const std::string& history,
833 const std::string& name,
835 const std::string& directory,
836 const std::string& domain,
837 std::unique_ptr<std::map<std::string, std::string>> defines,
840 , in_scope_(std::move(
i))
842 , directory_(directory)
844 , local_defines_(std::move(defines))
849 , is_define_(is_define)
851 std::ostringstream
s;
855 if(!history.empty()) {
862 if(!
t.location_.empty()) {
863 s <<
' ' <<
t.linenum_ <<
' ' <<
t.location_;
866 t.location_ =
s.str();
867 t.linenum_ = linenum;
871 if(
t.textdomain_ != domain) {
873 t.textdomain_ = domain;
900 std::ostringstream
s;
912 unsigned stack_pos =
tokens_.back().stack_pos;
926 assert(stack_pos ==
strings_.size());
942 assert(stack_pos + 1 ==
strings_.size());
956 if(
in_.
eof() || (
c !=
' ' &&
c !=
'\t')) {
993 res +=
static_cast<char>(
c);
1014 res +=
static_cast<char>(
c);
1027 res +=
static_cast<char>(
c);
1090 char c =
static_cast<char>(
in_.
get());
1105 s =
"#ifdef or #ifndef";
1108 s =
"Quoted string";
1111 s =
"Verbatim string";
1115 s =
"Macro substitution";
1118 s =
"Macro argument";
1132 std::string buffer(1,
c);
1135 char d =
static_cast<char>(
in_.
get());
1150 if(
c ==
'>' &&
in_.
peek() ==
'>') {
1154 }
else if(
c ==
'<' &&
in_.
peek() ==
'<') {
1159 }
else if(
c ==
'"') {
1171 }
else if(
c ==
'{') {
1178 bool comment =
false;
1180 if(command ==
"define") {
1184 std::map<std::string, std::string> optargs;
1187 parent_.
error(
"No macro name found after #define directive", linenum);
1190 std::string symbol = items.front();
1191 items.erase(items.begin());
1192 int found_arg = 0, found_enddef = 0, found_deprecate = 0;
1193 utils::optional<DEP_LEVEL> deprecation_level;
1194 std::string buffer, deprecation_detail;
1199 char d =
static_cast<char>(
in_.
get());
1206 }
else if(
in_.
peek() ==
'd') {
1207 found_deprecate = 1;
1212 if(found_arg > 0 && ++found_arg == 4) {
1213 if(std::equal(buffer.end() - 3, buffer.end(),
"arg")) {
1214 buffer.erase(buffer.end() - 4, buffer.end());
1220 std::string argbuffer;
1222 int found_endarg = 0;
1228 char e =
static_cast<char>(
in_.
get());
1237 }
else if(found_endarg > 0 && ++found_endarg == 7) {
1238 if(std::equal(argbuffer.end() - 6, argbuffer.end(),
"endarg")) {
1239 argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
1240 optargs[argname] = argbuffer;
1251 if(found_deprecate > 0 && ++found_deprecate == 11) {
1252 if(std::equal(buffer.end() - 10, buffer.end(),
"deprecated")) {
1253 buffer.erase(buffer.end() - 11, buffer.end());
1257 if(deprecation_level) {
1258 deprecation_level = std::max(*deprecation_level,
level);
1260 deprecation_level =
level;
1262 }
catch(
const std::invalid_argument&) {
1272 if(!deprecation_detail.empty()){
1273 deprecation_detail +=
'\n';
1280 if(found_enddef > 0 && ++found_enddef == 7) {
1281 if(std::equal(buffer.end() - 6, buffer.end(),
"enddef")) {
1285 if(std::equal(buffer.end() - 6, buffer.end(),
"define")) {
1289 "Preprocessor error: #define is not allowed inside a #define/#enddef pair",
1297 if(found_enddef != 7) {
1304 std::ostringstream new_pos, old_pos;
1310 WRN_PREPROC <<
"Redefining macro " << symbol <<
" without explicit #undef at "
1312 <<
"previously defined at " <<
lineno_string(old_pos.str());
1315 buffer.erase(buffer.end() - 7, buffer.end());
1318 deprecation_detail, deprecation_level, deprecation_version);
1322 }
else if(command ==
"ifdef" || command ==
"ifndef") {
1323 const bool negate = command[2] ==
'n';
1325 const std::string& symbol =
read_word();
1326 if(symbol.empty()) {
1330 DBG_PREPROC <<
"testing for macro " << symbol <<
": " << (found ?
"defined" :
"not defined");
1332 }
else if(command ==
"ifhave" || command ==
"ifnhave") {
1333 const bool negate = command[2] ==
'n';
1335 const std::string& symbol =
read_word();
1336 if(symbol.empty()) {
1340 DBG_PREPROC <<
"testing for file or directory " << symbol <<
": " << (found ?
"found" :
"not found");
1342 }
else if(command ==
"ifver" || command ==
"ifnver") {
1343 const bool negate = command[2] ==
'n';
1346 const std::string& vsymstr =
read_word();
1348 const std::string& vopstr =
read_word();
1350 const std::string& vverstr =
read_word();
1360 parent_.
error(
"First argument macro in #ifver/#ifnver should not require arguments",
linenum_);
1367 DBG_PREPROC <<
"testing version '" << version1.
str() <<
"' against '" << version2.
str() <<
"' ("
1368 << vopstr <<
"): " << (found ?
"match" :
"no match");
1372 std::string
err =
"Undefined macro in #ifver/#ifnver first argument: '";
1377 }
else if(command ==
"else") {
1389 }
else if(command ==
"endif") {
1401 }
else if(command ==
"textdomain") {
1405 put(
"#textdomain ");
1410 }
else if(command ==
"enddef") {
1412 }
else if(command ==
"undef") {
1414 const std::string& symbol =
read_word();
1419 }
else if(command ==
"error") {
1422 std::ostringstream error;
1427 }
else if(command ==
"warning") {
1430 std::ostringstream warning;
1436 }
else if(command ==
"deprecated") {
1442 }
catch(
const std::invalid_argument&) {
1469 }
else if(
c ==
'}') {
1488 if(
strings_.size() <=
static_cast<std::size_t
>(
token.stack_pos)) {
1493 std::string::size_type pos;
1496 symbol.erase(
b + pos,
b + symbol.find(
'\n', pos + 1) + 1);
1499 std::map<std::string, std::string>::const_iterator arg;
1500 preproc_map::const_iterator macro;
1518 std::ostringstream error;
1519 error <<
"Macro argument '" << symbol <<
"' does not expect any arguments";
1523 std::ostringstream v;
1532 std::size_t optional_arg_num = 0;
1534 auto defines = std::make_unique<std::map<std::string, std::string>>();
1541 for(std::size_t
i = 0;
i < nb_arg; ++
i) {
1550 std::size_t equals_pos = str.find_first_of(
"=");
1552 if(equals_pos != std::string::npos) {
1553 std::size_t argname_pos = str.substr(0, equals_pos).find_last_of(
" \n") + 1;
1555 std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
1558 (*defines)[argname] = str.substr(equals_pos + 1);
1562 DBG_PREPROC <<
"Found override for " << argname <<
" in call to macro " << symbol;
1564 std::ostringstream warning;
1565 warning <<
"Unrecognized optional argument passed to macro '" << symbol <<
"': '"
1578 if(defines->find(argument.first) == defines->end()) {
1582 std::istream
in(buf.get());
1586 auto temp_defines = std::make_unique<std::map<std::string, std::string>>();
1587 temp_defines->insert(defines->begin(), defines->end());
1592 std::ostringstream res;
1595 DBG_PREPROC <<
"Setting default for optional argument " << argument.first <<
" in macro "
1598 (*defines)[argument.first] = res.str();
1603 if(nb_arg - optional_arg_num != val.
arguments.size()) {
1605 const std::string
filename = locations.empty() ?
"<command-line>" :
get_filename(locations[0]);
1606 std::ostringstream error;
1607 error <<
"Preprocessor symbol '" << symbol <<
"' defined at " <<
filename <<
":"
1608 << val.
linenum <<
" expects " << val.
arguments.size() <<
" arguments, but has "
1609 << nb_arg - optional_arg_num <<
" arguments";
1623 DBG_PREPROC <<
"substituting (slow) macro " << symbol;
1631 std::ostringstream res;
1633 std::istream
in(buf.get());
1643 LOG_PREPROC <<
"Macro definition not found for " << symbol <<
", attempting to open as file.";
1654 std::ostringstream res;
1656 std::istream
in(buf.get());
1657 buf->add_preprocessor<
preprocessor_file>(nfname.value(), nfname->size() - symbol.size());
1665 std::ostringstream error;
1666 error <<
"Macro/file '" << symbol <<
"' is missing";
1674 std::ostringstream
s;
1698 : std::basic_istream<char>(nullptr)
1730 clear(std::ios_base::goodbit);
1731 exceptions(std::ios_base::goodbit);
1735 std::unique_ptr<preprocessor_streambuf>
buf_;
1746 log_scope(
"preprocessing file " + fname +
" ...");
1755 bool write_plain_cfg,
1756 const std::string& parent_directory)
1759 std::vector<std::string> dirs, files;
1765 for(
const std::string& dir : dirs) {
1771 for(
const std::string& file : files) {
1780 LOG_PREPROC <<
"processing resource: " << res_name;
1787 std::stringstream ss;
1793 ss.exceptions(std::ios_base::failbit);
1795 ss << (*stream).rdbuf();
1799 if(write_cfg || write_plain_cfg) {
1801 std::string streamContent = ss.str();
1803 read(cfg, streamContent);
1809 LOG_PREPROC <<
"writing cfg file: " << preproc_res_name;
1814 write(*outStream, cfg);
1818 if(write_plain_cfg) {
1819 LOG_PREPROC <<
"writing plain cfg file: " << (preproc_res_name +
".plain");
Helper class for buffering a std::istream.
Helper class for buffering a std::istream.
bool eof() const
Is the end of input reached?
int get()
Gets and consumes a character from the buffer.
int peek()
Gets a character from the buffer.
Class for writing a config out to a file in pieces.
void close_child(const std::string &key)
void write_key_val(const std::string &key, const T &value)
This template function will work with any type that can be assigned to an attribute_value.
void open_child(const std::string &key)
A config object defines a single node in a WML file, with access to child nodes.
bool has_attribute(config_key_type key) const
child_itors child_range(config_key_type key)
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Specialized preprocessor for handling any kind of input stream.
std::unique_ptr< std::map< std::string, std::string > > local_defines_
Mapping of macro arguments to their content.
int skipping_
Non-zero when the preprocessor has to skip some input text.
virtual preprocessor::MODE parse_mode() override
Returns the appropriate parsing mode for this preprocessor.
void conditional_skip(bool skip)
int slowpath_
Set to true whenever input tokens cannot be directly sent to the target buffer.
std::string read_rest_of_line()
friend bool operator!=(preprocessor_data::token_desc::token_type, char)
std::vector< std::string > strings_
Buffer for delayed input processing.
filesystem::scoped_istream in_scope_
Manages the lifetime of the std::istream pointer we own.
preprocessor_data(preprocessor_streambuf &, filesystem::scoped_istream, const std::string &history, const std::string &name, int line, const std::string &dir, const std::string &domain, std::unique_ptr< std::map< std::string, std::string >> defines, bool is_define=false)
buffered_istream in_
Input stream.
friend bool operator==(preprocessor_data::token_desc::token_type, char)
bool is_define_
True iff we are currently parsing a macros content, otherwise false.
std::vector< token_desc > tokens_
Stack of nested preprocessing chunks.
virtual bool get_chunk() override
Preprocesses and sends some text to the parent_ buffer.
void push_token(token_desc::token_type)
Specialized preprocessor for handling a file or a set of files.
std::vector< std::string >::const_iterator pos_
std::vector< std::string > files_
virtual bool get_chunk() override
Inserts and processes the next file in the list of included files.
const std::string & name_
std::vector< std::string >::const_iterator end_
virtual void init() override
Allows specifying any actions that need to be called after the constructor completes.
preprocessor_file(preprocessor_streambuf &t, const std::string &name, std::size_t symbol_index=-1)
Constructor.
Target for sending preprocessed output.
std::stringstream buffer_
Buffer filled by the current preprocessor.
preprocessor * current() const
bool quoted_
Set to true if one preprocessor for this target started to read a string.
preprocessor_streambuf(preproc_map *def)
preproc_map default_defines_
virtual int underflow() override
Inherited from basic_streambuf.
void restore_old_preprocessor()
Restores the old preprocessing context.
std::string out_buffer_
Buffer read by the STL stream.
std::string get_current_file()
Decodes the filenames placed in a location.
std::deque< std::unique_ptr< preprocessor > > preprocessor_queue_
Input preprocessor queue.
void add_preprocessor(A &&... args)
void error(const std::string &, int)
preprocessor_streambuf(const preprocessor_streambuf &t)
void warning(const std::string &, int)
Base class for preprocessing an input.
virtual MODE parse_mode()
Returns the appropriate parsing mode for this preprocessor.
std::string old_textdomain_
virtual void init()
Allows specifying any actions that need to be called after the constructor completes.
std::string old_location_
friend class preprocessor_streambuf
preprocessor(preprocessor_streambuf &t)
Sets up a new preprocessor for stream buffer t.
virtual bool get_chunk()=0
Preprocesses and sends some text to the parent_ buffer.
preprocessor_streambuf & parent_
Represents version numbers.
std::string str() const
Serializes the version number into string form.
Definitions for the interface to Wesnoth Markup Language (WML).
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
unsigned in
If equal to search_counter, the node is off the list.
bool do_version_check(const version_info &a, VERSION_COMP_OP op, const version_info &b)
VERSION_COMP_OP parse_version_op(const std::string &op_str)
Interfaces for manipulating version numbers of engine, add-ons, etc.
Standard logging facilities (interface).
#define log_scope(description)
void clear()
Clear the current render target.
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > ¤t_dir)
Returns a translated path to the actual file or directory, if it exists.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
static bool create_directory_if_missing_recursive(const bfs::path &dirpath)
std::unique_ptr< std::istream > scoped_istream
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::unique_ptr< std::ostream > scoped_ostream
const version_info wesnoth_version(VERSION)
struct utils::detail::formula_initer init
std::vector< std::string > quoted_split(const std::string &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
bool portable_isspace(const char c)
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
static lg::log_domain log_preprocessor("preprocessor")
static const std::string left_curly_str
static const std::string current_dir_str
std::string lineno_string(const std::string &lineno)
void preprocess_resource(const std::string &res_name, preproc_map *defines_map, bool write_cfg, bool write_plain_cfg, const std::string &parent_directory)
static std::string get_file_code(const std::string &filename)
static bool encode_filename
static const std::string current_file_str
bool operator==(preprocessor_data::token_desc::token_type, char)
static std::string get_filename(const std::string &file_code)
static std::string get_location(const std::string &loc)
std::ostream & operator<<(std::ostream &stream, const preproc_define &def)
static std::map< std::string, int > file_number_map
static const std::string right_curly_str
bool operator!=(preprocessor_data::token_desc::token_type rhs, char lhs)
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
static const char OUTPUT_SEPARATOR
static std::string preprocessor_error_detail_prefix
std::map< std::string, struct preproc_define > preproc_map
void read(config &cfg, std::istream &in, abstract_validator *validator)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
std::string filename
Filename.
void write(config_writer &, const std::string &) const
version_info deprecation_version
bool operator<(const preproc_define &) const
void write_argument(config_writer &, const std::string &) const
void read(const config &)
void read_argument(const config &)
std::string deprecation_message
std::vector< std::string > arguments
std::map< std::string, std::string > optional_arguments
bool operator==(const preproc_define &) const
utils::optional< DEP_LEVEL > deprecation_level
static preproc_map::value_type read_pair(const config &)
bool is_deprecated() const
Description of a preprocessing chunk.
token_desc(token_type type, const int stack_pos, const int linenum)
int stack_pos
Starting position in strings_ of the delayed text for this chunk.
std::unique_ptr< preprocessor_streambuf > buf_
std::unique_ptr< preproc_map > local_defines_
preprocessor_scope_helper(const std::string &fname, preproc_map *defines)
~preprocessor_scope_helper()
static map_location::DIRECTION n
static map_location::DIRECTION s
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.