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) {
213 value = cfg[
"value"].str();
215 linenum = cfg[
"linenum"].to_int();
234 return preproc_map::value_type(cfg[
"name"], second);
239 return stream <<
"value: " << def.
value <<
" arguments: " << def.
location;
242 std::ostream&
operator<<(std::ostream& stream,
const preproc_map::value_type& def)
244 return stream << def.second;
331 void error(
const std::string&,
int);
332 void warning(
const std::string&,
int);
334 template<
typename T,
typename... A>
408 , old_textdomain_(
t.textdomain_)
409 , old_location_(
t.location_)
410 , old_linenum_(
t.linenum_)
423 if(
char* gp = gptr()) {
445 const int desired_fill_amount = 2000;
447 while(
current() &&
buffer_.rdbuf()->in_avail() < desired_fill_amount) {
464 setg(begin, begin + sz, begin + bs);
470 return static_cast<unsigned char>(*(begin + sz));
500 unsigned nested_level = 0;
520 if(pos.size() <= 2 * nested_level) {
530 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
535 const std::string&
line = *(
i++);
538 res += included_from;
559 std::string position,
error;
560 std::ostringstream pos;
565 error = error_type +
'\n';
566 error +=
"at " + position;
576 std::ostringstream pos;
603 virtual void init()
override;
612 const std::string& name = *(
pos_++);
613 unsigned sz = name.size();
616 if(sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4,
"gfc.")) {
629 std::vector<std::string>::const_iterator
pos_,
end_;
726 void put(
const std::string& );
732 const std::string& history,
733 const std::string& name,
735 const std::string& dir,
736 const std::string& domain,
737 std::unique_ptr<std::map<std::string, std::string>> defines,
738 bool is_define =
false);
755 throw std::logic_error(
"don't compare tokens with characters");
765 return !(lhs == rhs);
789 for(
const std::string& fname :
files_) {
790 std::size_t cpos = fname.rfind(
" ");
792 if(cpos != std::string::npos && cpos >= symbol_index) {
793 std::stringstream ss;
795 <<
"' in included directory '" << name <<
"'.\nThe included symbol probably looks similar to '"
818 if(!file_stream->good()) {
830 const std::string& history,
831 const std::string& name,
833 const std::string& directory,
834 const std::string& domain,
835 std::unique_ptr<std::map<std::string, std::string>> defines,
838 , in_scope_(std::move(
i))
840 , directory_(directory)
842 , local_defines_(std::move(defines))
847 , is_define_(is_define)
849 std::ostringstream
s;
853 if(!history.empty()) {
860 if(!
t.location_.empty()) {
861 s <<
' ' <<
t.linenum_ <<
' ' <<
t.location_;
864 t.location_ =
s.str();
865 t.linenum_ = linenum;
869 if(
t.textdomain_ != domain) {
871 t.textdomain_ = domain;
898 std::ostringstream
s;
910 unsigned stack_pos =
tokens_.back().stack_pos;
924 assert(stack_pos ==
strings_.size());
940 assert(stack_pos + 1 ==
strings_.size());
954 if(
in_.
eof() || (
c !=
' ' &&
c !=
'\t')) {
991 res +=
static_cast<char>(
c);
1012 res +=
static_cast<char>(
c);
1025 res +=
static_cast<char>(
c);
1088 char c =
static_cast<char>(
in_.
get());
1103 s =
"#ifdef or #ifndef";
1106 s =
"Quoted string";
1109 s =
"Verbatim string";
1113 s =
"Macro substitution";
1116 s =
"Macro argument";
1130 std::string buffer(1,
c);
1133 char d =
static_cast<char>(
in_.
get());
1148 if(
c ==
'>' &&
in_.
peek() ==
'>') {
1152 }
else if(
c ==
'<' &&
in_.
peek() ==
'<') {
1157 }
else if(
c ==
'"') {
1169 }
else if(
c ==
'{') {
1176 bool comment =
false;
1178 if(command ==
"define") {
1182 std::map<std::string, std::string> optargs;
1185 parent_.
error(
"No macro name found after #define directive", linenum);
1188 std::string symbol = items.front();
1189 items.erase(items.begin());
1190 int found_arg = 0, found_enddef = 0, found_deprecate = 0;
1191 utils::optional<DEP_LEVEL> deprecation_level;
1192 std::string buffer, deprecation_detail;
1197 char d =
static_cast<char>(
in_.
get());
1204 }
else if(
in_.
peek() ==
'd') {
1205 found_deprecate = 1;
1210 if(found_arg > 0 && ++found_arg == 4) {
1211 if(std::equal(buffer.end() - 3, buffer.end(),
"arg")) {
1212 buffer.erase(buffer.end() - 4, buffer.end());
1218 std::string argbuffer;
1220 int found_endarg = 0;
1226 char e =
static_cast<char>(
in_.
get());
1235 }
else if(found_endarg > 0 && ++found_endarg == 7) {
1236 if(std::equal(argbuffer.end() - 6, argbuffer.end(),
"endarg")) {
1237 argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
1238 optargs[argname] = argbuffer;
1249 if(found_deprecate > 0 && ++found_deprecate == 11) {
1250 if(std::equal(buffer.end() - 10, buffer.end(),
"deprecated")) {
1251 buffer.erase(buffer.end() - 11, buffer.end());
1255 if(deprecation_level) {
1256 deprecation_level = std::max(*deprecation_level,
level);
1258 deprecation_level =
level;
1260 }
catch(
const std::invalid_argument&) {
1270 if(!deprecation_detail.empty()){
1271 deprecation_detail +=
'\n';
1278 if(found_enddef > 0 && ++found_enddef == 7) {
1279 if(std::equal(buffer.end() - 6, buffer.end(),
"enddef")) {
1283 if(std::equal(buffer.end() - 6, buffer.end(),
"define")) {
1287 "Preprocessor error: #define is not allowed inside a #define/#enddef pair",
1295 if(found_enddef != 7) {
1302 std::ostringstream new_pos, old_pos;
1308 WRN_PREPROC <<
"Redefining macro " << symbol <<
" without explicit #undef at "
1310 <<
"previously defined at " <<
lineno_string(old_pos.str());
1313 buffer.erase(buffer.end() - 7, buffer.end());
1316 deprecation_detail, deprecation_level, deprecation_version);
1320 }
else if(command ==
"ifdef" || command ==
"ifndef") {
1321 const bool negate = command[2] ==
'n';
1323 const std::string& symbol =
read_word();
1324 if(symbol.empty()) {
1328 DBG_PREPROC <<
"testing for macro " << symbol <<
": " << (found ?
"defined" :
"not defined");
1330 }
else if(command ==
"ifhave" || command ==
"ifnhave") {
1331 const bool negate = command[2] ==
'n';
1333 const std::string& symbol =
read_word();
1334 if(symbol.empty()) {
1338 DBG_PREPROC <<
"testing for file or directory " << symbol <<
": " << (found ?
"found" :
"not found");
1340 }
else if(command ==
"ifver" || command ==
"ifnver") {
1341 const bool negate = command[2] ==
'n';
1344 const std::string& vsymstr =
read_word();
1346 const std::string& vopstr =
read_word();
1348 const std::string& vverstr =
read_word();
1358 parent_.
error(
"First argument macro in #ifver/#ifnver should not require arguments",
linenum_);
1365 DBG_PREPROC <<
"testing version '" << version1.
str() <<
"' against '" << version2.
str() <<
"' ("
1366 << vopstr <<
"): " << (found ?
"match" :
"no match");
1370 std::string
err =
"Undefined macro in #ifver/#ifnver first argument: '";
1375 }
else if(command ==
"else") {
1387 }
else if(command ==
"endif") {
1399 }
else if(command ==
"textdomain") {
1403 put(
"#textdomain ");
1408 }
else if(command ==
"enddef") {
1410 }
else if(command ==
"undef") {
1412 const std::string& symbol =
read_word();
1417 }
else if(command ==
"error") {
1420 std::ostringstream error;
1425 }
else if(command ==
"warning") {
1428 std::ostringstream warning;
1434 }
else if(command ==
"deprecated") {
1440 }
catch(
const std::invalid_argument&) {
1467 }
else if(
c ==
'}') {
1486 if(
strings_.size() <=
static_cast<std::size_t
>(
token.stack_pos)) {
1491 std::string::size_type pos;
1494 symbol.erase(
b + pos,
b + symbol.find(
'\n', pos + 1) + 1);
1497 std::map<std::string, std::string>::const_iterator arg;
1498 preproc_map::const_iterator macro;
1516 std::ostringstream error;
1517 error <<
"Macro argument '" << symbol <<
"' does not expect any arguments";
1521 std::ostringstream v;
1530 std::size_t optional_arg_num = 0;
1532 auto defines = std::make_unique<std::map<std::string, std::string>>();
1539 for(std::size_t
i = 0;
i < nb_arg; ++
i) {
1548 std::size_t equals_pos = str.find_first_of(
"=");
1550 if(equals_pos != std::string::npos) {
1551 std::size_t argname_pos = str.substr(0, equals_pos).find_last_of(
" \n") + 1;
1553 std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
1556 (*defines)[argname] = str.substr(equals_pos + 1);
1560 DBG_PREPROC <<
"Found override for " << argname <<
" in call to macro " << symbol;
1562 std::ostringstream warning;
1563 warning <<
"Unrecognized optional argument passed to macro '" << symbol <<
"': '"
1576 if(defines->find(argument.first) == defines->end()) {
1580 std::istream
in(buf.get());
1584 auto temp_defines = std::make_unique<std::map<std::string, std::string>>();
1585 temp_defines->insert(defines->begin(), defines->end());
1590 std::ostringstream res;
1593 DBG_PREPROC <<
"Setting default for optional argument " << argument.first <<
" in macro "
1596 (*defines)[argument.first] = res.str();
1601 if(nb_arg - optional_arg_num != val.
arguments.size()) {
1603 const std::string
filename = locations.empty() ?
"<command-line>" :
get_filename(locations[0]);
1604 std::ostringstream error;
1605 error <<
"Preprocessor symbol '" << symbol <<
"' defined at " <<
filename <<
":"
1606 << val.
linenum <<
" expects " << val.
arguments.size() <<
" arguments, but has "
1607 << nb_arg - optional_arg_num <<
" arguments";
1621 DBG_PREPROC <<
"substituting (slow) macro " << symbol;
1629 std::ostringstream res;
1631 std::istream
in(buf.get());
1641 LOG_PREPROC <<
"Macro definition not found for " << symbol <<
", attempting to open as file.";
1652 std::ostringstream res;
1654 std::istream
in(buf.get());
1655 buf->add_preprocessor<
preprocessor_file>(nfname.value(), nfname->size() - symbol.size());
1663 std::ostringstream error;
1664 error <<
"Macro/file '" << symbol <<
"' is missing";
1672 std::ostringstream
s;
1696 : std::basic_istream<char>(nullptr)
1728 clear(std::ios_base::goodbit);
1729 exceptions(std::ios_base::goodbit);
1733 std::unique_ptr<preprocessor_streambuf>
buf_;
1744 log_scope(
"preprocessing file " + fname +
" ...");
1752 log_scope(
"preprocessing string " + contents.substr(0, 10) +
" ...");
1754 std::unique_ptr<preprocessor_streambuf> buf;
1755 std::unique_ptr<preproc_map> local_defines;
1764 defines = local_defines.get();
1771 std::unique_ptr<std::istream>(
new 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;
1810 std::stringstream ss;
1816 ss.exceptions(std::ios_base::failbit);
1818 ss << (*stream).rdbuf();
1822 if(write_cfg || write_plain_cfg) {
1824 std::string streamContent = ss.str();
1826 read(cfg, streamContent);
1832 LOG_PREPROC <<
"writing cfg file: " << preproc_res_name;
1837 write(*outStream, cfg);
1841 if(write_plain_cfg) {
1842 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...
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
void read(config &cfg, std::istream &in, abstract_validator *validator)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
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
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()
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.