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;
328 void error(
const std::string&,
int);
329 void warning(
const std::string&,
int);
331 template<
typename T,
typename... A>
405 , old_textdomain_(
t.textdomain_)
406 , old_location_(
t.location_)
407 , old_linenum_(
t.linenum_)
420 if(
char* gp = gptr()) {
442 const int desired_fill_amount = 2000;
444 while(
current() &&
buffer_.rdbuf()->in_avail() < desired_fill_amount) {
461 setg(begin, begin + sz, begin + bs);
467 return static_cast<unsigned char>(*(begin + sz));
497 unsigned nested_level = 0;
517 if(pos.size() <= 2 * nested_level) {
527 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
532 const std::string&
line = *(
i++);
535 res += included_from;
556 std::string position,
error;
557 std::ostringstream pos;
562 error = error_type +
'\n';
563 error +=
"at " + position;
573 std::ostringstream pos;
600 virtual void init()
override;
609 const std::string& name = *(
pos_++);
610 unsigned sz = name.size();
613 if(sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4,
"gfc.")) {
626 std::vector<std::string>::const_iterator
pos_,
end_;
723 void put(
const std::string& );
729 const std::string& history,
730 const std::string& name,
732 const std::string& dir,
733 const std::string& domain,
734 std::unique_ptr<std::map<std::string, std::string>> defines,
735 bool is_define =
false);
752 throw std::logic_error(
"don't compare tokens with characters");
762 return !(lhs == rhs);
786 for(
const std::string& fname :
files_) {
787 std::size_t cpos = fname.rfind(
" ");
789 if(cpos != std::string::npos && cpos >= symbol_index) {
790 std::stringstream ss;
792 <<
"' in included directory '" << name <<
"'.\nThe included symbol probably looks similar to '"
815 if(!file_stream->good()) {
827 const std::string& history,
828 const std::string& name,
830 const std::string& directory,
831 const std::string& domain,
832 std::unique_ptr<std::map<std::string, std::string>> defines,
835 , in_scope_(std::move(
i))
837 , directory_(directory)
839 , local_defines_(std::move(defines))
844 , is_define_(is_define)
846 std::ostringstream
s;
850 if(!history.empty()) {
857 if(!
t.location_.empty()) {
858 s <<
' ' <<
t.linenum_ <<
' ' <<
t.location_;
861 t.location_ =
s.str();
862 t.linenum_ = linenum;
866 if(
t.textdomain_ != domain) {
868 t.textdomain_ = domain;
895 std::ostringstream
s;
907 unsigned stack_pos =
tokens_.back().stack_pos;
921 assert(stack_pos ==
strings_.size());
937 assert(stack_pos + 1 ==
strings_.size());
951 if(
in_.
eof() || (
c !=
' ' &&
c !=
'\t')) {
988 res +=
static_cast<char>(
c);
1009 res +=
static_cast<char>(
c);
1022 res +=
static_cast<char>(
c);
1085 char c =
static_cast<char>(
in_.
get());
1100 s =
"#ifdef or #ifndef";
1103 s =
"Quoted string";
1106 s =
"Verbatim string";
1110 s =
"Macro substitution";
1113 s =
"Macro argument";
1127 std::string buffer(1,
c);
1130 char d =
static_cast<char>(
in_.
get());
1145 if(
c ==
'>' &&
in_.
peek() ==
'>') {
1149 }
else if(
c ==
'<' &&
in_.
peek() ==
'<') {
1154 }
else if(
c ==
'"') {
1166 }
else if(
c ==
'{') {
1173 bool comment =
false;
1175 if(command ==
"define") {
1179 std::map<std::string, std::string> optargs;
1182 parent_.
error(
"No macro name found after #define directive", linenum);
1185 std::string symbol = items.front();
1186 items.erase(items.begin());
1187 int found_arg = 0, found_enddef = 0, found_deprecate = 0;
1188 utils::optional<DEP_LEVEL> deprecation_level;
1189 std::string buffer, deprecation_detail;
1194 char d =
static_cast<char>(
in_.
get());
1201 }
else if(
in_.
peek() ==
'd') {
1202 found_deprecate = 1;
1207 if(found_arg > 0 && ++found_arg == 4) {
1208 if(std::equal(buffer.end() - 3, buffer.end(),
"arg")) {
1209 buffer.erase(buffer.end() - 4, buffer.end());
1215 std::string argbuffer;
1217 int found_endarg = 0;
1223 char e =
static_cast<char>(
in_.
get());
1232 }
else if(found_endarg > 0 && ++found_endarg == 7) {
1233 if(std::equal(argbuffer.end() - 6, argbuffer.end(),
"endarg")) {
1234 argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
1235 optargs[argname] = argbuffer;
1246 if(found_deprecate > 0 && ++found_deprecate == 11) {
1247 if(std::equal(buffer.end() - 10, buffer.end(),
"deprecated")) {
1248 buffer.erase(buffer.end() - 11, buffer.end());
1252 if(deprecation_level) {
1253 deprecation_level = std::max(*deprecation_level,
level);
1255 deprecation_level =
level;
1257 }
catch(
const std::invalid_argument&) {
1267 if(!deprecation_detail.empty()){
1268 deprecation_detail +=
'\n';
1275 if(found_enddef > 0 && ++found_enddef == 7) {
1276 if(std::equal(buffer.end() - 6, buffer.end(),
"enddef")) {
1280 if(std::equal(buffer.end() - 6, buffer.end(),
"define")) {
1284 "Preprocessor error: #define is not allowed inside a #define/#enddef pair",
1292 if(found_enddef != 7) {
1299 std::ostringstream new_pos, old_pos;
1305 WRN_PREPROC <<
"Redefining macro " << symbol <<
" without explicit #undef at "
1307 <<
"previously defined at " <<
lineno_string(old_pos.str());
1310 buffer.erase(buffer.end() - 7, buffer.end());
1313 deprecation_detail, deprecation_level, deprecation_version));
1317 }
else if(command ==
"ifdef" || command ==
"ifndef") {
1318 const bool negate = command[2] ==
'n';
1320 const std::string& symbol =
read_word();
1321 if(symbol.empty()) {
1325 DBG_PREPROC <<
"testing for macro " << symbol <<
": " << (found ?
"defined" :
"not defined");
1327 }
else if(command ==
"ifhave" || command ==
"ifnhave") {
1328 const bool negate = command[2] ==
'n';
1330 const std::string& symbol =
read_word();
1331 if(symbol.empty()) {
1335 DBG_PREPROC <<
"testing for file or directory " << symbol <<
": " << (found ?
"found" :
"not found");
1337 }
else if(command ==
"ifver" || command ==
"ifnver") {
1338 const bool negate = command[2] ==
'n';
1341 const std::string& vsymstr =
read_word();
1343 const std::string& vopstr =
read_word();
1345 const std::string& vverstr =
read_word();
1355 parent_.
error(
"First argument macro in #ifver/#ifnver should not require arguments",
linenum_);
1362 DBG_PREPROC <<
"testing version '" << version1.
str() <<
"' against '" << version2.
str() <<
"' ("
1363 << vopstr <<
"): " << (found ?
"match" :
"no match");
1367 std::string
err =
"Undefined macro in #ifver/#ifnver first argument: '";
1372 }
else if(command ==
"else") {
1384 }
else if(command ==
"endif") {
1396 }
else if(command ==
"textdomain") {
1400 put(
"#textdomain ");
1405 }
else if(command ==
"enddef") {
1407 }
else if(command ==
"undef") {
1409 const std::string& symbol =
read_word();
1414 }
else if(command ==
"error") {
1417 std::ostringstream error;
1422 }
else if(command ==
"warning") {
1425 std::ostringstream warning;
1431 }
else if(command ==
"deprecated") {
1437 }
catch(
const std::invalid_argument&) {
1464 }
else if(
c ==
'}') {
1483 if(
strings_.size() <=
static_cast<std::size_t
>(
token.stack_pos)) {
1488 std::string::size_type pos;
1491 symbol.erase(
b + pos,
b + symbol.find(
'\n', pos + 1) + 1);
1494 std::map<std::string, std::string>::const_iterator arg;
1495 preproc_map::const_iterator macro;
1513 std::ostringstream error;
1514 error <<
"Macro argument '" << symbol <<
"' does not expect any arguments";
1518 std::ostringstream v;
1527 std::size_t optional_arg_num = 0;
1529 auto defines = std::make_unique<std::map<std::string, std::string>>();
1536 for(std::size_t
i = 0;
i < nb_arg; ++
i) {
1545 std::size_t equals_pos = str.find_first_of(
"=");
1547 if(equals_pos != std::string::npos) {
1548 std::size_t argname_pos = str.substr(0, equals_pos).find_last_of(
" \n") + 1;
1550 std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
1553 (*defines)[argname] = str.substr(equals_pos + 1);
1557 DBG_PREPROC <<
"Found override for " << argname <<
" in call to macro " << symbol;
1559 std::ostringstream warning;
1560 warning <<
"Unrecognized optional argument passed to macro '" << symbol <<
"': '"
1573 if(defines->find(argument.first) == defines->end()) {
1577 std::istream
in(buf.get());
1581 auto temp_defines = std::make_unique<std::map<std::string, std::string>>();
1582 temp_defines->insert(defines->begin(), defines->end());
1587 std::ostringstream res;
1590 DBG_PREPROC <<
"Setting default for optional argument " << argument.first <<
" in macro "
1593 (*defines)[argument.first] = res.str();
1598 if(nb_arg - optional_arg_num != val.
arguments.size()) {
1600 const std::string
filename = locations.empty() ?
"<command-line>" :
get_filename(locations[0]);
1601 std::ostringstream error;
1602 error <<
"Preprocessor symbol '" << symbol <<
"' defined at " <<
filename <<
":"
1603 << val.
linenum <<
" expects " << val.
arguments.size() <<
" arguments, but has "
1604 << nb_arg - optional_arg_num <<
" arguments";
1618 DBG_PREPROC <<
"substituting (slow) macro " << symbol;
1626 std::ostringstream res;
1628 std::istream
in(buf.get());
1638 LOG_PREPROC <<
"Macro definition not found for " << symbol <<
", attempting to open as file.";
1649 std::ostringstream res;
1651 std::istream
in(buf.get());
1652 buf->add_preprocessor<
preprocessor_file>(nfname.value(), nfname->size() - symbol.size());
1660 std::ostringstream error;
1661 error <<
"Macro/file '" << symbol <<
"' is missing";
1669 std::ostringstream
s;
1693 : std::basic_istream<char>(nullptr)
1725 clear(std::ios_base::goodbit);
1726 exceptions(std::ios_base::goodbit);
1730 std::unique_ptr<preprocessor_streambuf>
buf_;
1741 log_scope(
"preprocessing file " + fname +
" ...");
1749 log_scope(
"preprocessing string " + contents.substr(0, 10) +
" ...");
1751 std::unique_ptr<preprocessor_streambuf> buf;
1752 std::unique_ptr<preproc_map> local_defines;
1761 defines = local_defines.get();
1768 std::unique_ptr<std::istream>(
new std::istringstream(contents)),
"<string>",
"", 1,
game_config::path, textdomain,
nullptr);
1775 bool write_plain_cfg,
1776 const std::string& parent_directory)
1779 std::vector<std::string> dirs, files;
1785 for(
const std::string& dir : dirs) {
1791 for(
const std::string& file : files) {
1800 LOG_PREPROC <<
"processing resource: " << res_name;
1807 std::stringstream ss;
1813 ss.exceptions(std::ios_base::failbit);
1815 ss << (*stream).rdbuf();
1819 if(write_cfg || write_plain_cfg) {
1820 std::string streamContent = ss.str();
1827 LOG_PREPROC <<
"writing cfg file: " << preproc_res_name;
1834 if(write_plain_cfg) {
1835 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.
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.
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)
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...
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")
std::string preprocess_string(const std::string &contents, preproc_map *defines, const std::string &textdomain)
Function to use the WML preprocessor on a string.
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.
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()
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.