37 #define ERR_PREPROC LOG_STREAM(err, log_preprocessor)
38 #define WRN_PREPROC LOG_STREAM(warn, log_preprocessor)
39 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor)
40 #define DBG_PREPROC LOG_STREAM(debug, log_preprocessor)
83 static int current_file_number = 0;
87 fnum = ++current_file_number;
90 std::ostringstream shex;
91 shex << std::hex << fnum;
106 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
152 const std::string key =
"argument";
162 const std::string key =
"argument";
173 const std::string key =
"preproc_define";
190 for(
const std::string& arg :
arguments) {
230 map.try_emplace(
cfg[
"name"],
cfg);
235 return stream <<
"value: " << def.
value <<
" arguments: " << def.
location;
238 std::ostream&
operator<<(std::ostream& stream,
const preproc_map::value_type& def)
240 return stream << def.second;
321 void error(
const std::string&,
int);
322 void warning(
const std::string&,
int);
324 template<
typename T,
typename... A>
393 , old_textdomain_(
t.textdomain_)
394 , old_location_(
t.location_)
395 , 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));
475 unsigned nested_level = 0;
495 if(pos.size() <= 2 * nested_level) {
505 std::vector<std::string>::const_iterator
i = pos.begin(), end = pos.end();
510 const std::string&
line = *(
i++);
513 res += included_from;
534 std::string position,
error;
535 std::ostringstream pos;
540 error = error_type +
'\n';
541 error +=
"at " + position;
551 std::ostringstream pos;
578 virtual void init()
override;
587 const std::string& name = *(
pos_++);
589 #ifdef __cpp_lib_starts_ends_with
590 if(!name.ends_with(
".cfg")) {
593 if(name.size() < 5 || !std::equal(name.rbegin(), name.rbegin() + 4,
"gfc.")) {
607 std::vector<std::string>::const_iterator
pos_,
end_;
643 , stack_pos(stack_pos)
695 std::string read_word();
696 std::string read_line();
697 std::string read_rest_of_line();
704 void put(
const std::string& );
705 void conditional_skip(
bool skip);
710 const std::string& history,
711 const std::string& name,
713 const std::string& dir,
714 const std::string& domain,
715 std::unique_ptr<std::map<std::string, std::string>> defines,
716 bool is_define =
false);
733 throw std::logic_error(
"don't compare tokens with characters");
743 return !(lhs == rhs);
767 for(
const std::string& fname :
files_) {
768 std::size_t cpos = fname.rfind(
" ");
770 if(cpos != std::string::npos && cpos >= symbol_index) {
771 std::stringstream ss;
773 <<
"' in included directory '" << name <<
"'.\nThe included symbol probably looks similar to '"
796 if(!file_stream->good()) {
808 const std::string& history,
809 const std::string& name,
811 const std::string& directory,
812 const std::string& domain,
813 std::unique_ptr<std::map<std::string, std::string>> defines,
816 , in_scope_(std::move(
i))
818 , directory_(directory)
820 , local_defines_(std::move(defines))
825 , is_define_(is_define)
827 std::ostringstream
s;
831 if(!history.empty()) {
838 if(!
t.location_.empty()) {
839 s <<
' ' <<
t.linenum_ <<
' ' <<
t.location_;
842 t.location_ =
s.str();
843 t.linenum_ = linenum;
847 if(
t.textdomain_ != domain) {
849 t.textdomain_ = domain;
876 std::ostringstream
s;
888 unsigned stack_pos =
tokens_.back().stack_pos;
902 assert(stack_pos ==
strings_.size());
918 assert(stack_pos + 1 ==
strings_.size());
932 if(
in_.
eof() || (
c !=
' ' &&
c !=
'\t')) {
969 res +=
static_cast<char>(
c);
990 res +=
static_cast<char>(
c);
1003 res +=
static_cast<char>(
c);
1066 char c =
static_cast<char>(
in_.
get());
1081 s =
"#ifdef or #ifndef";
1084 s =
"Quoted string";
1087 s =
"Verbatim string";
1091 s =
"Macro substitution";
1094 s =
"Macro argument";
1108 std::string buffer(1,
c);
1111 char d =
static_cast<char>(
in_.
get());
1126 if(
c ==
'>' &&
in_.
peek() ==
'>') {
1130 }
else if(
c ==
'<' &&
in_.
peek() ==
'<') {
1135 }
else if(
c ==
'"') {
1147 }
else if(
c ==
'{') {
1154 bool comment =
false;
1156 if(command ==
"define") {
1160 std::map<std::string, std::string> optargs;
1163 parent_.
error(
"No macro name found after #define directive", linenum);
1166 std::string symbol = items.front();
1167 items.erase(items.begin());
1168 int found_arg = 0, found_enddef = 0, found_deprecate = 0;
1169 utils::optional<DEP_LEVEL> deprecation_level;
1170 std::string buffer, deprecation_detail;
1175 char d =
static_cast<char>(
in_.
get());
1182 }
else if(
in_.
peek() ==
'd') {
1183 found_deprecate = 1;
1188 if(found_arg > 0 && ++found_arg == 4) {
1189 #ifdef __cpp_lib_starts_ends_with
1190 if(buffer.ends_with(
"arg")) {
1192 if(std::equal(buffer.end() - 3, buffer.end(),
"arg")) {
1194 buffer.erase(buffer.end() - 4, buffer.end());
1200 std::string argbuffer;
1202 int found_endarg = 0;
1208 char e =
static_cast<char>(
in_.
get());
1217 }
else if(found_endarg > 0 && ++found_endarg == 7) {
1218 #ifdef __cpp_lib_starts_ends_with
1219 if(argbuffer.ends_with(
"endarg")) {
1221 if(std::equal(argbuffer.end() - 6, argbuffer.end(),
"endarg")) {
1223 argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
1224 optargs[argname] = argbuffer;
1235 if(found_deprecate > 0 && ++found_deprecate == 11) {
1236 #ifdef __cpp_lib_starts_ends_with
1237 if(buffer.ends_with(
"deprecated")) {
1239 if(std::equal(buffer.end() - 10, buffer.end(),
"deprecated")) {
1241 buffer.erase(buffer.end() - 11, buffer.end());
1245 if(deprecation_level) {
1246 deprecation_level = std::max(*deprecation_level,
level);
1248 deprecation_level =
level;
1250 }
catch(
const std::invalid_argument&) {
1260 if(!deprecation_detail.empty()){
1261 deprecation_detail +=
'\n';
1268 if(found_enddef > 0 && ++found_enddef == 7) {
1269 #ifdef __cpp_lib_starts_ends_with
1270 if(buffer.ends_with(
"enddef")) {
1272 if(std::equal(buffer.end() - 6, buffer.end(),
"enddef")) {
1277 #ifdef __cpp_lib_starts_ends_with
1278 if(buffer.ends_with(
"define")) {
1280 if(std::equal(buffer.end() - 6, buffer.end(),
"define")) {
1285 "Preprocessor error: #define is not allowed inside a #define/#enddef pair",
1293 if(found_enddef != 7) {
1300 std::ostringstream new_pos, old_pos;
1306 WRN_PREPROC <<
"Redefining macro " << symbol <<
" without explicit #undef at "
1308 <<
"previously defined at " <<
lineno_string(old_pos.str());
1311 buffer.erase(buffer.end() - 7, buffer.end());
1314 deprecation_detail, deprecation_level, deprecation_version));
1318 }
else if(command ==
"ifdef" || command ==
"ifndef") {
1319 const bool negate = command[2] ==
'n';
1321 const std::string& symbol =
read_word();
1322 if(symbol.empty()) {
1326 DBG_PREPROC <<
"testing for macro " << symbol <<
": " << (found ?
"defined" :
"not defined");
1328 }
else if(command ==
"ifhave" || command ==
"ifnhave") {
1329 const bool negate = command[2] ==
'n';
1331 const std::string& symbol =
read_word();
1332 if(symbol.empty()) {
1336 DBG_PREPROC <<
"testing for file or directory " << symbol <<
": " << (found ?
"found" :
"not found");
1338 }
else if(command ==
"ifver" || command ==
"ifnver") {
1339 const bool negate = command[2] ==
'n';
1342 const std::string& vsymstr =
read_word();
1344 const std::string& vopstr =
read_word();
1346 const std::string& vverstr =
read_word();
1356 parent_.
error(
"First argument macro in #ifver/#ifnver should not require arguments",
linenum_);
1363 DBG_PREPROC <<
"testing version '" << version1.
str() <<
"' against '" << version2.
str() <<
"' ("
1364 << vopstr <<
"): " << (found ?
"match" :
"no match");
1368 std::string
err =
"Undefined macro in #ifver/#ifnver first argument: '";
1373 }
else if(command ==
"else") {
1385 }
else if(command ==
"endif") {
1397 }
else if(command ==
"textdomain") {
1401 put(
"#textdomain ");
1406 }
else if(command ==
"enddef") {
1408 }
else if(command ==
"undef") {
1410 const std::string& symbol =
read_word();
1415 }
else if(command ==
"error") {
1418 std::ostringstream error;
1423 }
else if(command ==
"warning") {
1426 std::ostringstream warning;
1432 }
else if(command ==
"deprecated") {
1438 }
catch(
const std::invalid_argument&) {
1465 }
else if(
c ==
'}') {
1484 if(
strings_.size() <=
static_cast<std::size_t
>(
token.stack_pos)) {
1489 std::string::size_type pos;
1492 symbol.erase(
b + pos,
b + symbol.find(
'\n', pos + 1) + 1);
1495 std::map<std::string, std::string>::const_iterator arg;
1496 preproc_map::const_iterator macro;
1514 std::ostringstream error;
1515 error <<
"Macro argument '" << symbol <<
"' does not expect any arguments";
1519 std::ostringstream v;
1528 std::size_t optional_arg_num = 0;
1530 auto defines = std::make_unique<std::map<std::string, std::string>>();
1537 for(std::size_t
i = 0;
i < nb_arg; ++
i) {
1546 std::size_t equals_pos = str.find_first_of(
"=");
1548 if(equals_pos != std::string::npos) {
1549 std::size_t argname_pos = str.substr(0, equals_pos).find_last_of(
" \n") + 1;
1551 std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
1554 (*defines)[argname] = str.substr(equals_pos + 1);
1558 DBG_PREPROC <<
"Found override for " << argname <<
" in call to macro " << symbol;
1560 std::ostringstream warning;
1561 warning <<
"Unrecognized optional argument passed to macro '" << symbol <<
"': '"
1574 if(defines->find(argument) == defines->end()) {
1578 std::istream
in(buf.get());
1582 auto temp_defines = std::make_unique<std::map<std::string, std::string>>();
1583 temp_defines->insert(defines->begin(), defines->end());
1588 std::ostringstream res;
1591 DBG_PREPROC <<
"Setting default for optional argument " << argument <<
" in macro "
1594 (*defines)[argument] = res.str();
1599 if(nb_arg - optional_arg_num != val.
arguments.size()) {
1601 const std::string
filename = locations.empty() ?
"<command-line>" :
get_filename(locations[0]);
1602 std::ostringstream error;
1603 error <<
"Preprocessor symbol '" << symbol <<
"' defined at " <<
filename <<
":"
1604 << val.
linenum <<
" expects " << val.
arguments.size() <<
" arguments, but has "
1605 << nb_arg - optional_arg_num <<
" arguments";
1619 DBG_PREPROC <<
"substituting (slow) macro " << symbol;
1627 std::ostringstream res;
1629 std::istream
in(buf.get());
1639 LOG_PREPROC <<
"Macro definition not found for " << symbol <<
", attempting to open as file.";
1650 std::ostringstream res;
1652 std::istream
in(buf.get());
1653 buf->add_preprocessor<
preprocessor_file>(nfname.value(), nfname->size() - symbol.size());
1661 std::ostringstream error;
1662 error <<
"Macro/file '" << symbol <<
"' is missing";
1670 std::ostringstream
s;
1695 : std::istream(nullptr)
1697 , buffer_(owned_defines_)
1702 : std::istream(nullptr)
1710 clear(std::ios_base::goodbit);
1711 exceptions(std::ios_base::goodbit);
1715 template<
typename Preprocessor,
typename... Args>
1722 buffer_.add_preprocessor<Preprocessor>(std::forward<Args>(args)...);
1739 log_scope(
"preprocessing file " + fname +
" ...");
1741 auto stream = std::make_unique<preprocessor_istream>(defines);
1748 log_scope(
"preprocessing file " + fname +
" ...");
1750 auto stream = std::make_unique<preprocessor_istream>();
1756 const std::string& contents,
const std::string& textdomain)
1758 log_scope(
"preprocessing string " + contents.substr(0, 10) +
" ...");
1760 auto stream = std::make_unique<preprocessor_istream>();
1762 std::make_unique<std::istringstream>(contents),
"<string>",
"", 1,
game_config::path, textdomain,
nullptr);
1768 const std::string& contents,
const std::string& textdomain,
preproc_map& defines)
1770 log_scope(
"preprocessing string " + contents.substr(0, 10) +
" ...");
1772 auto stream = std::make_unique<preprocessor_istream>(defines);
1774 std::make_unique<std::istringstream>(contents),
"<string>",
"", 1,
game_config::path, textdomain,
nullptr);
1782 bool write_plain_cfg,
1783 const std::string& parent_directory)
1786 std::vector<std::string> dirs, files;
1792 for(
const std::string& dir : dirs) {
1798 for(
const std::string& file : files) {
1807 LOG_PREPROC <<
"processing resource: " << res_name;
1816 std::stringstream ss;
1822 ss.exceptions(std::ios_base::failbit);
1824 ss << (*stream).rdbuf();
1828 if(write_cfg || write_plain_cfg) {
1829 std::string streamContent = ss.str();
1836 LOG_PREPROC <<
"writing cfg file: " << preproc_res_name;
1843 if(write_plain_cfg) {
1844 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.
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
child_itors child_range(std::string_view key)
bool has_attribute(std::string_view key) const
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()
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.
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.
std::vector< std::unique_ptr< preprocessor > > preprocessor_queue_
Input preprocessor queue.
virtual int underflow() override
Inherited from basic_streambuf.
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)
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_
virtual ~preprocessor()
Restores the old preprocessing context.
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.
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
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, bool strong_quotes)
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 to_string(const Range &range, const Func &op)
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.