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)
84 static int current_file_number = 0;
88 fnum = ++current_file_number;
91 std::ostringstream shex;
92 shex << std::hex << fnum;
107 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
153 const std::string key =
"argument";
163 const std::string key =
"argument";
174 const std::string key =
"preproc_define";
191 for(
const std::string& arg :
arguments) {
231 map.try_emplace(
cfg[
"name"],
cfg);
236 return stream <<
"value: " << def.
value <<
" arguments: " << def.
location;
239 std::ostream&
operator<<(std::ostream& stream,
const preproc_map::value_type& def)
241 return stream << def.second;
327 void error(
const std::string&,
int);
328 void warning(
const std::string&,
int);
330 template<
typename T,
typename... A>
402 , old_textdomain_(
t.textdomain_)
403 , old_location_(
t.location_)
404 , old_linenum_(
t.linenum_)
417 if(
char* gp = gptr()) {
439 const int desired_fill_amount = 2000;
441 while(
current() &&
buffer_.rdbuf()->in_avail() < desired_fill_amount) {
458 setg(begin, begin + sz, begin + bs);
464 return static_cast<unsigned char>(*(begin + sz));
494 unsigned nested_level = 0;
514 if(pos.size() <= 2 * nested_level) {
524 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
529 const std::string&
line = *(
i++);
532 res += included_from;
553 std::string position,
error;
554 std::ostringstream pos;
559 error = error_type +
'\n';
560 error +=
"at " + position;
570 std::ostringstream pos;
597 virtual void init()
override;
606 const std::string& name = *(
pos_++);
607 unsigned sz = name.size();
610 if(sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4,
"gfc.")) {
623 std::vector<std::string>::const_iterator
pos_,
end_;
720 void put(
const std::string& );
726 const std::string& history,
727 const std::string& name,
729 const std::string& dir,
730 const std::string& domain,
731 std::unique_ptr<std::map<std::string, std::string>> defines,
732 bool is_define =
false);
749 throw std::logic_error(
"don't compare tokens with characters");
759 return !(lhs == rhs);
783 for(
const std::string& fname :
files_) {
784 std::size_t cpos = fname.rfind(
" ");
786 if(cpos != std::string::npos && cpos >= symbol_index) {
787 std::stringstream ss;
789 <<
"' in included directory '" << name <<
"'.\nThe included symbol probably looks similar to '"
812 if(!file_stream->good()) {
824 const std::string& history,
825 const std::string& name,
827 const std::string& directory,
828 const std::string& domain,
829 std::unique_ptr<std::map<std::string, std::string>> defines,
832 , in_scope_(std::move(
i))
834 , directory_(directory)
836 , local_defines_(std::move(defines))
841 , is_define_(is_define)
843 std::ostringstream
s;
847 if(!history.empty()) {
854 if(!
t.location_.empty()) {
855 s <<
' ' <<
t.linenum_ <<
' ' <<
t.location_;
858 t.location_ =
s.str();
859 t.linenum_ = linenum;
863 if(
t.textdomain_ != domain) {
865 t.textdomain_ = domain;
892 std::ostringstream
s;
904 unsigned stack_pos =
tokens_.back().stack_pos;
918 assert(stack_pos ==
strings_.size());
934 assert(stack_pos + 1 ==
strings_.size());
948 if(
in_.
eof() || (
c !=
' ' &&
c !=
'\t')) {
985 res +=
static_cast<char>(
c);
1006 res +=
static_cast<char>(
c);
1019 res +=
static_cast<char>(
c);
1082 char c =
static_cast<char>(
in_.
get());
1097 s =
"#ifdef or #ifndef";
1100 s =
"Quoted string";
1103 s =
"Verbatim string";
1107 s =
"Macro substitution";
1110 s =
"Macro argument";
1124 std::string buffer(1,
c);
1127 char d =
static_cast<char>(
in_.
get());
1142 if(
c ==
'>' &&
in_.
peek() ==
'>') {
1146 }
else if(
c ==
'<' &&
in_.
peek() ==
'<') {
1151 }
else if(
c ==
'"') {
1163 }
else if(
c ==
'{') {
1170 bool comment =
false;
1172 if(command ==
"define") {
1176 std::map<std::string, std::string> optargs;
1179 parent_.
error(
"No macro name found after #define directive", linenum);
1182 std::string symbol = items.front();
1183 items.erase(items.begin());
1184 int found_arg = 0, found_enddef = 0, found_deprecate = 0;
1185 utils::optional<DEP_LEVEL> deprecation_level;
1186 std::string buffer, deprecation_detail;
1191 char d =
static_cast<char>(
in_.
get());
1198 }
else if(
in_.
peek() ==
'd') {
1199 found_deprecate = 1;
1204 if(found_arg > 0 && ++found_arg == 4) {
1205 if(std::equal(buffer.end() - 3, buffer.end(),
"arg")) {
1206 buffer.erase(buffer.end() - 4, buffer.end());
1212 std::string argbuffer;
1214 int found_endarg = 0;
1220 char e =
static_cast<char>(
in_.
get());
1229 }
else if(found_endarg > 0 && ++found_endarg == 7) {
1230 if(std::equal(argbuffer.end() - 6, argbuffer.end(),
"endarg")) {
1231 argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
1232 optargs[argname] = argbuffer;
1243 if(found_deprecate > 0 && ++found_deprecate == 11) {
1244 if(std::equal(buffer.end() - 10, buffer.end(),
"deprecated")) {
1245 buffer.erase(buffer.end() - 11, buffer.end());
1249 if(deprecation_level) {
1250 deprecation_level = std::max(*deprecation_level,
level);
1252 deprecation_level =
level;
1254 }
catch(
const std::invalid_argument&) {
1264 if(!deprecation_detail.empty()){
1265 deprecation_detail +=
'\n';
1272 if(found_enddef > 0 && ++found_enddef == 7) {
1273 if(std::equal(buffer.end() - 6, buffer.end(),
"enddef")) {
1277 if(std::equal(buffer.end() - 6, buffer.end(),
"define")) {
1281 "Preprocessor error: #define is not allowed inside a #define/#enddef pair",
1289 if(found_enddef != 7) {
1296 std::ostringstream new_pos, old_pos;
1302 WRN_PREPROC <<
"Redefining macro " << symbol <<
" without explicit #undef at "
1304 <<
"previously defined at " <<
lineno_string(old_pos.str());
1307 buffer.erase(buffer.end() - 7, buffer.end());
1310 deprecation_detail, deprecation_level, deprecation_version));
1314 }
else if(command ==
"ifdef" || command ==
"ifndef") {
1315 const bool negate = command[2] ==
'n';
1317 const std::string& symbol =
read_word();
1318 if(symbol.empty()) {
1322 DBG_PREPROC <<
"testing for macro " << symbol <<
": " << (found ?
"defined" :
"not defined");
1324 }
else if(command ==
"ifhave" || command ==
"ifnhave") {
1325 const bool negate = command[2] ==
'n';
1327 const std::string& symbol =
read_word();
1328 if(symbol.empty()) {
1332 DBG_PREPROC <<
"testing for file or directory " << symbol <<
": " << (found ?
"found" :
"not found");
1334 }
else if(command ==
"ifver" || command ==
"ifnver") {
1335 const bool negate = command[2] ==
'n';
1338 const std::string& vsymstr =
read_word();
1340 const std::string& vopstr =
read_word();
1342 const std::string& vverstr =
read_word();
1352 parent_.
error(
"First argument macro in #ifver/#ifnver should not require arguments",
linenum_);
1359 DBG_PREPROC <<
"testing version '" << version1.
str() <<
"' against '" << version2.
str() <<
"' ("
1360 << vopstr <<
"): " << (found ?
"match" :
"no match");
1364 std::string
err =
"Undefined macro in #ifver/#ifnver first argument: '";
1369 }
else if(command ==
"else") {
1381 }
else if(command ==
"endif") {
1393 }
else if(command ==
"textdomain") {
1397 put(
"#textdomain ");
1402 }
else if(command ==
"enddef") {
1404 }
else if(command ==
"undef") {
1406 const std::string& symbol =
read_word();
1411 }
else if(command ==
"error") {
1414 std::ostringstream error;
1419 }
else if(command ==
"warning") {
1422 std::ostringstream warning;
1428 }
else if(command ==
"deprecated") {
1434 }
catch(
const std::invalid_argument&) {
1461 }
else if(
c ==
'}') {
1480 if(
strings_.size() <=
static_cast<std::size_t
>(
token.stack_pos)) {
1485 std::string::size_type pos;
1488 symbol.erase(
b + pos,
b + symbol.find(
'\n', pos + 1) + 1);
1491 std::map<std::string, std::string>::const_iterator arg;
1492 preproc_map::const_iterator macro;
1510 std::ostringstream error;
1511 error <<
"Macro argument '" << symbol <<
"' does not expect any arguments";
1515 std::ostringstream v;
1524 std::size_t optional_arg_num = 0;
1526 auto defines = std::make_unique<std::map<std::string, std::string>>();
1533 for(std::size_t
i = 0;
i < nb_arg; ++
i) {
1542 std::size_t equals_pos = str.find_first_of(
"=");
1544 if(equals_pos != std::string::npos) {
1545 std::size_t argname_pos = str.substr(0, equals_pos).find_last_of(
" \n") + 1;
1547 std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
1550 (*defines)[argname] = str.substr(equals_pos + 1);
1554 DBG_PREPROC <<
"Found override for " << argname <<
" in call to macro " << symbol;
1556 std::ostringstream warning;
1557 warning <<
"Unrecognized optional argument passed to macro '" << symbol <<
"': '"
1570 if(defines->find(argument.first) == defines->end()) {
1574 std::istream
in(buf.get());
1578 auto temp_defines = std::make_unique<std::map<std::string, std::string>>();
1579 temp_defines->insert(defines->begin(), defines->end());
1584 std::ostringstream res;
1587 DBG_PREPROC <<
"Setting default for optional argument " << argument.first <<
" in macro "
1590 (*defines)[argument.first] = res.str();
1595 if(nb_arg - optional_arg_num != val.
arguments.size()) {
1597 const std::string
filename = locations.empty() ?
"<command-line>" :
get_filename(locations[0]);
1598 std::ostringstream error;
1599 error <<
"Preprocessor symbol '" << symbol <<
"' defined at " <<
filename <<
":"
1600 << val.
linenum <<
" expects " << val.
arguments.size() <<
" arguments, but has "
1601 << nb_arg - optional_arg_num <<
" arguments";
1615 DBG_PREPROC <<
"substituting (slow) macro " << symbol;
1623 std::ostringstream res;
1625 std::istream
in(buf.get());
1635 LOG_PREPROC <<
"Macro definition not found for " << symbol <<
", attempting to open as file.";
1646 std::ostringstream res;
1648 std::istream
in(buf.get());
1649 buf->add_preprocessor<
preprocessor_file>(nfname.value(), nfname->size() - symbol.size());
1657 std::ostringstream error;
1658 error <<
"Macro/file '" << symbol <<
"' is missing";
1666 std::ostringstream
s;
1691 : std::istream(nullptr)
1698 : std::istream(nullptr)
1706 clear(std::ios_base::goodbit);
1707 exceptions(std::ios_base::goodbit);
1711 template<
typename Preprocessor,
typename... Args>
1735 log_scope(
"preprocessing file " + fname +
" ...");
1737 auto stream = std::make_unique<preprocessor_istream>(defines);
1744 log_scope(
"preprocessing file " + fname +
" ...");
1746 auto stream = std::make_unique<preprocessor_istream>();
1752 const std::string& contents,
const std::string& textdomain)
1754 log_scope(
"preprocessing string " + contents.substr(0, 10) +
" ...");
1756 auto stream = std::make_unique<preprocessor_istream>();
1758 std::make_unique<std::istringstream>(contents),
"<string>",
"", 1,
game_config::path, textdomain,
nullptr);
1764 const std::string& contents,
const std::string& textdomain,
preproc_map& defines)
1766 log_scope(
"preprocessing string " + contents.substr(0, 10) +
" ...");
1768 auto stream = std::make_unique<preprocessor_istream>(defines);
1770 std::make_unique<std::istringstream>(contents),
"<string>",
"", 1,
game_config::path, textdomain,
nullptr);
1778 bool write_plain_cfg,
1779 const std::string& parent_directory)
1782 std::vector<std::string> dirs, files;
1788 for(
const std::string& dir : dirs) {
1794 for(
const std::string& file : files) {
1803 LOG_PREPROC <<
"processing resource: " << res_name;
1812 std::stringstream ss;
1818 ss.exceptions(std::ios_base::failbit);
1820 ss << (*stream).rdbuf();
1824 if(write_cfg || write_plain_cfg) {
1825 std::string streamContent = ss.str();
1832 LOG_PREPROC <<
"writing cfg file: " << preproc_res_name;
1839 if(write_plain_cfg) {
1840 LOG_PREPROC <<
"writing plain cfg file: " << (preproc_res_name +
".plain");
unsigned in
If equal to search_counter, the node is off the list.
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.
preprocessor_istream(preproc_map &defines)
void process(Args &&... args)
preproc_map owned_defines_
Defines map to be used if no external map is given.
preprocessor_streambuf buffer_
The underlying processing buffer.
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.
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.
preprocessor_streambuf(preproc_map &def)
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.
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.
const version_info wesnoth_version(VERSION)
config read(std::istream &in, abstract_validator *validator)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
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...
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
bool portable_isspace(const char c)
std::string escape(std::string_view 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")
filesystem::scoped_istream preprocess_string(const std::string &contents, const std::string &textdomain)
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 std::string preprocessor_error_detail_prefix
std::map< std::string, struct preproc_define > preproc_map
constexpr unsigned char INLINED_PREPROCESS_DIRECTIVE_CHAR
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
static void insert(preproc_map &, const config &)
std::map< std::string, std::string > optional_arguments
bool operator==(const preproc_define &) const
utils::optional< DEP_LEVEL > deprecation_level
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.
contains the current text being parsed as well as the token_type of what's being parsed.
static map_location::direction n
static map_location::direction s
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.