Implemented support for optional macro arguments
This commit is contained in:
parent
cc5071f2e9
commit
255dd6bfc3
3 changed files with 121 additions and 19 deletions
|
@ -227,6 +227,8 @@ Version 1.13.6:
|
|||
supported) keys
|
||||
* Individual [terrain_graphics] rotations can now be skipped entirely by
|
||||
using "skip" in the rotations= list.
|
||||
* WML macros can now include optional arguments with default values that the
|
||||
caller can override.
|
||||
* Miscellaneous and bug fixes:
|
||||
* A new way to make units invulnerable for debugging: select the unit and type
|
||||
";unit invulnerable=yes". This method operates by decreasing the opponent's hit
|
||||
|
|
|
@ -884,12 +884,13 @@ bool preprocessor_data::get_chunk()
|
|||
skip_spaces();
|
||||
int linenum = linenum_;
|
||||
std::vector< std::string > items = utils::split(read_line(), ' ');
|
||||
std::map< std::string, std::string> optargs;
|
||||
if (items.empty()) {
|
||||
target_.error("No macro name found after #define directive", linenum);
|
||||
}
|
||||
std::string symbol = items.front();
|
||||
items.erase(items.begin());
|
||||
int found_enddef = 0;
|
||||
int found_arg = 0, found_enddef = 0;
|
||||
std::string buffer;
|
||||
for(;;) {
|
||||
if (in_.eof())
|
||||
|
@ -898,10 +899,48 @@ bool preprocessor_data::get_chunk()
|
|||
if (d == '\n')
|
||||
++linenum_;
|
||||
buffer += d;
|
||||
if (d == '#')
|
||||
found_enddef = 1;
|
||||
else if (found_enddef > 0)
|
||||
if (++found_enddef == 7) {
|
||||
if (d == '#') {
|
||||
if (in_.peek() == 'a') {
|
||||
found_arg = 1;
|
||||
} else {
|
||||
found_enddef = 1;
|
||||
}
|
||||
} else {
|
||||
if (found_arg > 0 && ++found_arg == 4) {
|
||||
if (std::equal(buffer.end() - 3, buffer.end(), "arg")) {
|
||||
buffer.erase(buffer.end() - 3, buffer.end());
|
||||
|
||||
skip_spaces();
|
||||
std::string argname = read_word();
|
||||
skip_eol();
|
||||
|
||||
std::string argbuffer;
|
||||
|
||||
int found_endarg = 0;
|
||||
for(;;) {
|
||||
if (in_.eof())
|
||||
break;
|
||||
char e = in_.get();
|
||||
if (e == '\n')
|
||||
++linenum_;
|
||||
argbuffer += e;
|
||||
|
||||
if (e == '#') {
|
||||
found_endarg = 1;
|
||||
} else if (found_endarg > 0 && ++found_endarg == 7) {
|
||||
if (std::equal(argbuffer.end() - 6, argbuffer.end(), "endarg")) {
|
||||
argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
|
||||
optargs[argname] = argbuffer;
|
||||
break;
|
||||
} else {
|
||||
target_.error("Unterminated #arg definition", linenum_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_enddef > 0 && ++found_enddef == 7) {
|
||||
if (std::equal(buffer.end() - 6, buffer.end(), "enddef"))
|
||||
break;
|
||||
else {
|
||||
|
@ -911,6 +950,7 @@ bool preprocessor_data::get_chunk()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found_enddef != 7) {
|
||||
target_.error("Unterminated preprocessor definition", linenum_);
|
||||
|
@ -932,7 +972,7 @@ bool preprocessor_data::get_chunk()
|
|||
}
|
||||
|
||||
buffer.erase(buffer.end() - 7, buffer.end());
|
||||
(*target_.defines_)[symbol] = preproc_define(buffer, items, target_.textdomain_,
|
||||
(*target_.defines_)[symbol] = preproc_define(buffer, items, optargs, target_.textdomain_,
|
||||
linenum, target_.location_);
|
||||
LOG_PREPROC << "defining macro " << symbol << " (location " << get_location(target_.location_) << ")\n";
|
||||
}
|
||||
|
@ -1124,26 +1164,85 @@ bool preprocessor_data::get_chunk()
|
|||
}
|
||||
else if (target_.depth_ < 100 && (macro = target_.defines_->find(symbol)) != target_.defines_->end())
|
||||
{
|
||||
preproc_define const &val = macro->second;
|
||||
const preproc_define &val = macro->second;
|
||||
size_t nb_arg = strings_.size() - token.stack_pos - 1;
|
||||
if (nb_arg != val.arguments.size())
|
||||
size_t optional_arg_num = 0;
|
||||
|
||||
std::map<std::string, std::string> *defines = new std::map<std::string, std::string>;
|
||||
const std::string& dir = filesystem::directory_name(val.location.substr(0, val.location.find(' ')));
|
||||
|
||||
for (size_t i = 0; i < nb_arg; ++i) {
|
||||
if (i < val.arguments.size()) {
|
||||
// Normal mandatory arguments
|
||||
|
||||
(*defines)[val.arguments[i]] = strings_[token.stack_pos + i + 1];
|
||||
} else {
|
||||
// These should be optional argument overrides
|
||||
|
||||
std::string str = strings_[token.stack_pos + i + 1];
|
||||
size_t equals_pos = str.find_first_of("=");
|
||||
|
||||
if (equals_pos != std::string::npos) {
|
||||
size_t argname_pos = str.substr(0, equals_pos).find_last_of(" \n") + 1;
|
||||
|
||||
std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
|
||||
|
||||
if (val.optional_arguments.find(argname) != val.optional_arguments.end()) {
|
||||
(*defines)[argname] = str.substr(equals_pos + 1);
|
||||
|
||||
optional_arg_num++;
|
||||
|
||||
DBG_PREPROC << "Found override for " << argname << " in call to macro " << symbol << "\n";
|
||||
} else {
|
||||
std::ostringstream warning;
|
||||
warning << "Unrecognized optional argument passed to macro '" << symbol << "': '" << argname << "'";
|
||||
target_.warning(warning.str(), linenum_);
|
||||
|
||||
optional_arg_num++; // To prevent the argument number check from blowing up
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the macro definition has any optional arguments, insert their defaults
|
||||
if (val.optional_arguments.size() > 0) {
|
||||
for (const auto &arg : val.optional_arguments) {
|
||||
if (defines->find(arg.first) == defines->end()) {
|
||||
std::ostringstream res;
|
||||
preprocessor_streambuf *buf = new preprocessor_streambuf(target_);
|
||||
|
||||
buf->textdomain_ = target_.textdomain_;
|
||||
std::istream in(buf);
|
||||
|
||||
std::istringstream *buffer = new std::istringstream(arg.second);
|
||||
|
||||
std::map<std::string, std::string> *temp_defines = new std::map<std::string, std::string>;
|
||||
temp_defines->insert(defines->begin(), defines->end());
|
||||
|
||||
new preprocessor_data(*buf, buffer, val.location, "", val.linenum, dir, val.textdomain, temp_defines, false);
|
||||
res << in.rdbuf();
|
||||
|
||||
DBG_PREPROC << "Setting default for optional argument " << arg.first << " in macro " << symbol << "\n";
|
||||
|
||||
(*defines)[arg.first] = res.str();
|
||||
|
||||
delete buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_arg - optional_arg_num != val.arguments.size())
|
||||
{
|
||||
const std::vector<std::string>& locations = utils::quoted_split(val.location, ' ');
|
||||
std::ostringstream error;
|
||||
error << "Preprocessor symbol '" << symbol << "' defined at "
|
||||
<< get_filename(locations[0]) << ":" << val.linenum << " expects "
|
||||
<< val.arguments.size() << " arguments, but has "
|
||||
<< nb_arg << " arguments";
|
||||
<< nb_arg - optional_arg_num << " arguments";
|
||||
target_.error(error.str(), linenum_);
|
||||
}
|
||||
std::istringstream *buffer = new std::istringstream(val.value);
|
||||
std::map<std::string, std::string> *defines =
|
||||
new std::map<std::string, std::string>;
|
||||
for (size_t i = 0; i < nb_arg; ++i) {
|
||||
(*defines)[val.arguments[i]] = strings_[token.stack_pos + i + 1];
|
||||
}
|
||||
pop_token();
|
||||
const std::string& dir = filesystem::directory_name(val.location.substr(0, val.location.find(' ')));
|
||||
if (!slowpath_) {
|
||||
DBG_PREPROC << "substituting macro " << symbol << '\n';
|
||||
new preprocessor_data(target_, buffer, val.location, "",
|
||||
|
|
|
@ -32,13 +32,14 @@ typedef std::map< std::string, preproc_define > preproc_map;
|
|||
|
||||
struct preproc_define
|
||||
{
|
||||
preproc_define() : value(), arguments(), textdomain(), linenum(0), location() {}
|
||||
explicit preproc_define(const std::string& val) : value(val), arguments(), textdomain(), linenum(0), location() {}
|
||||
preproc_define(const std::string& val, std::vector< std::string > const &args,
|
||||
preproc_define() : value(), arguments(), optional_arguments(), textdomain(), linenum(0), location() {}
|
||||
explicit preproc_define(const std::string& val) : value(val), arguments(), optional_arguments(), textdomain(), linenum(0), location() {}
|
||||
preproc_define(const std::string& val, const std::vector< std::string > &args, const std::map< std::string, std::string> &optargs,
|
||||
const std::string& domain, int line, const std::string& loc)
|
||||
: value(val), arguments(args), textdomain(domain), linenum(line), location(loc) {}
|
||||
: value(val), arguments(args), optional_arguments(optargs), textdomain(domain), linenum(line), location(loc) {}
|
||||
std::string value;
|
||||
std::vector< std::string > arguments;
|
||||
std::map< std::string, std::string> optional_arguments;
|
||||
std::string textdomain;
|
||||
int linenum;
|
||||
std::string location;
|
||||
|
|
Loading…
Add table
Reference in a new issue