mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-28 07:48:37 -04:00
cppparser: assorted preprocessor improvements:
* Fix function-like macro arguments being expanded even when they were participating in token expansion or stringification * Fix __has_include with comma or closing parenthesis in angle-quoted filename * Don't issue warning if macro is redefined with identical definition * Fixes for extraneous spaces being added to expansions * Assorted refactoring This should resolve #1638.
This commit is contained in:
parent
78f1a5b15a
commit
360216656e
@ -23,7 +23,11 @@ using std::string;
|
||||
*/
|
||||
CPPManifest::ExpansionNode::
|
||||
ExpansionNode(int parm_number, bool stringify, bool paste) :
|
||||
_parm_number(parm_number), _stringify(stringify), _paste(paste), _optional(false)
|
||||
_parm_number(parm_number),
|
||||
_expand(!stringify && !paste),
|
||||
_stringify(stringify),
|
||||
_paste(paste),
|
||||
_optional(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -32,7 +36,12 @@ ExpansionNode(int parm_number, bool stringify, bool paste) :
|
||||
*/
|
||||
CPPManifest::ExpansionNode::
|
||||
ExpansionNode(const string &str, bool paste) :
|
||||
_parm_number(-1), _stringify(false), _paste(paste), _optional(false), _str(str)
|
||||
_parm_number(-1),
|
||||
_expand(!paste),
|
||||
_stringify(false),
|
||||
_paste(paste),
|
||||
_optional(false),
|
||||
_str(str)
|
||||
{
|
||||
}
|
||||
|
||||
@ -41,15 +50,35 @@ ExpansionNode(const string &str, bool paste) :
|
||||
*/
|
||||
CPPManifest::ExpansionNode::
|
||||
ExpansionNode(Expansion nested, bool stringify, bool paste, bool optional) :
|
||||
_parm_number(-1), _stringify(stringify), _paste(paste), _optional(optional), _nested(std::move(nested))
|
||||
_parm_number(-1),
|
||||
_expand(!stringify && !paste),
|
||||
_stringify(stringify),
|
||||
_paste(paste),
|
||||
_optional(optional),
|
||||
_nested(std::move(nested))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool CPPManifest::ExpansionNode::
|
||||
operator ==(const ExpansionNode &other) const {
|
||||
return _parm_number == other._parm_number
|
||||
&& _expand == other._expand
|
||||
&& _stringify == other._stringify
|
||||
&& _paste == other._paste
|
||||
&& _optional == other._optional
|
||||
&& _str == other._str
|
||||
&& _nested == other._nested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a manifest from a preprocessor definition.
|
||||
*/
|
||||
CPPManifest::
|
||||
CPPManifest(const string &args, const cppyyltype &loc) :
|
||||
CPPManifest(const CPPPreprocessor &parser, const string &args, const cppyyltype &loc) :
|
||||
_parser(parser),
|
||||
_variadic_param(-1),
|
||||
_loc(loc),
|
||||
_expr(nullptr),
|
||||
@ -93,7 +122,8 @@ CPPManifest(const string &args, const cppyyltype &loc) :
|
||||
* command-line -D option.
|
||||
*/
|
||||
CPPManifest::
|
||||
CPPManifest(const string ¯o, const string &definition) :
|
||||
CPPManifest(const CPPPreprocessor &parser, const string ¯o, const string &definition) :
|
||||
_parser(parser),
|
||||
_variadic_param(-1),
|
||||
_expr(nullptr),
|
||||
_vis(V_public)
|
||||
@ -190,12 +220,112 @@ stringify(const string &source) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void CPPManifest::
|
||||
extract_args(vector_string &args, const string &expr, size_t &p) const {
|
||||
// Skip whitespace till paren.
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
if (p >= expr.size() || expr[p] != '(') {
|
||||
// No paren, so we have only one arg.
|
||||
size_t q = p;
|
||||
while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) {
|
||||
p++;
|
||||
}
|
||||
args.push_back(expr.substr(q, p - q));
|
||||
}
|
||||
else if (expr[p] == '"' || expr[p] == '\'') {
|
||||
// Quoted string or character.
|
||||
int quote_mark = expr[p];
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote_mark && expr[p] != '\n') {
|
||||
if (expr[p] == '\\') {
|
||||
p++;
|
||||
}
|
||||
if (p < expr.size()) {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else {
|
||||
// Skip paren.
|
||||
p++;
|
||||
int paren_level = 1;
|
||||
size_t q = p;
|
||||
while (p < expr.size()) {
|
||||
if (expr[p] == ',' && paren_level == 1) {
|
||||
// Back up to strip any trailing whitespace.
|
||||
size_t r = p;
|
||||
while (r > q && isspace(expr[r - 1])) {
|
||||
--r;
|
||||
}
|
||||
args.push_back(expr.substr(q, r - q));
|
||||
q = p+1;
|
||||
}
|
||||
else if (expr[p] == '"' || expr[p] == '\'') {
|
||||
// Quoted string or character.
|
||||
int quote_mark = expr[p];
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote_mark && expr[p] != '\n') {
|
||||
if (expr[p] == '\\') {
|
||||
p++;
|
||||
}
|
||||
if (p < expr.size()) {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (expr[p] == '(') {
|
||||
++paren_level;
|
||||
}
|
||||
else if (expr[p] == ')') {
|
||||
--paren_level;
|
||||
if (paren_level == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (isspace(expr[p])) {
|
||||
// Skip whitespace at the beginning.
|
||||
if (q == p) {
|
||||
q++;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
{
|
||||
// Back up to strip any trailing whitespace.
|
||||
size_t r = p;
|
||||
while (r > q && isspace(expr[r - 1])) {
|
||||
--r;
|
||||
}
|
||||
if (!args.empty() || r > q) {
|
||||
args.push_back(expr.substr(q, r - q));
|
||||
}
|
||||
}
|
||||
|
||||
if (p < expr.size() && expr[p] == ')') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)args.size() < _num_parameters) {
|
||||
_parser.warning("Not enough arguments for manifest " + _name);
|
||||
}
|
||||
else if (_variadic_param < 0 && (int)args.size() > _num_parameters) {
|
||||
_parser.warning("Too many arguments for manifest " + _name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
string CPPManifest::
|
||||
expand(const vector_string &args) const {
|
||||
return r_expand(_expansion, args);
|
||||
expand(const vector_string &args, const Manifests &manifests, bool expand_undefined) const {
|
||||
return r_expand(_expansion, args, manifests, expand_undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,6 +340,32 @@ determine_type() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the macro definitions are equal.
|
||||
*/
|
||||
bool CPPManifest::
|
||||
is_equal(const CPPManifest *other) const {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (_name != other->_name) {
|
||||
return false;
|
||||
}
|
||||
if (_has_parameters != other->_has_parameters) {
|
||||
return false;
|
||||
}
|
||||
if (_num_parameters != other->_num_parameters) {
|
||||
return false;
|
||||
}
|
||||
if (_variadic_param != other->_variadic_param) {
|
||||
return false;
|
||||
}
|
||||
if (_expansion != other->_expansion) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -409,6 +565,10 @@ save_expansion(Expansion &expansion, const string &exp, const vector_string &par
|
||||
if (p < exp.size() && exp[p] == '#') {
|
||||
// Woah, this is a token-pasting operator.
|
||||
paste = true;
|
||||
if (!expansion.empty()) {
|
||||
// The previous expansion shouldn't be expanded.
|
||||
expansion.back()._expand = false;
|
||||
}
|
||||
++p;
|
||||
} else {
|
||||
// Mark that the next argument should be stringified.
|
||||
@ -439,7 +599,8 @@ save_expansion(Expansion &expansion, const string &exp, const vector_string &par
|
||||
*
|
||||
*/
|
||||
string CPPManifest::
|
||||
r_expand(const Expansion &expansion, const vector_string &args) const {
|
||||
r_expand(const Expansion &expansion, const vector_string &args,
|
||||
const Manifests &manifests, bool expand_undefined) const {
|
||||
std::string result;
|
||||
|
||||
for (const ExpansionNode &node : expansion) {
|
||||
@ -458,7 +619,8 @@ r_expand(const Expansion &expansion, const vector_string &args) const {
|
||||
if (node._stringify) {
|
||||
subst = stringify(subst);
|
||||
}
|
||||
} else if (i == _variadic_param && node._paste) {
|
||||
}
|
||||
else if (i == _variadic_param && node._paste) {
|
||||
// Special case GCC behavior: if __VA_ARGS__ is pasted to a comma and
|
||||
// no arguments are passed, the comma is removed. MSVC does this
|
||||
// automatically. Not sure if we should allow MSVC behavior as well.
|
||||
@ -467,8 +629,12 @@ r_expand(const Expansion &expansion, const vector_string &args) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (node._expand) {
|
||||
_parser.expand_manifests(subst, manifests, expand_undefined);
|
||||
}
|
||||
|
||||
if (!subst.empty()) {
|
||||
if (result.empty() || node._paste) {
|
||||
if (result.empty() || node._paste || result.back() == '(') {
|
||||
result += subst;
|
||||
} else {
|
||||
result += ' ';
|
||||
@ -477,7 +643,7 @@ r_expand(const Expansion &expansion, const vector_string &args) const {
|
||||
}
|
||||
}
|
||||
if (!node._str.empty()) {
|
||||
if (result.empty() || node._paste) {
|
||||
if (result.empty() || node._paste || node._str[0] == ',' || node._str[0] == ')') {
|
||||
result += node._str;
|
||||
} else {
|
||||
result += ' ';
|
||||
@ -487,7 +653,7 @@ r_expand(const Expansion &expansion, const vector_string &args) const {
|
||||
if (!node._nested.empty()) {
|
||||
string nested_result;
|
||||
if (node._optional && args.size() >= _num_parameters) {
|
||||
nested_result = r_expand(node._nested, args);
|
||||
nested_result = r_expand(node._nested, args, manifests, expand_undefined);
|
||||
}
|
||||
if (node._stringify) {
|
||||
nested_result = stringify(nested_result);
|
||||
|
@ -30,17 +30,25 @@ class CPPType;
|
||||
*/
|
||||
class CPPManifest {
|
||||
public:
|
||||
CPPManifest(const std::string &args, const cppyyltype &loc);
|
||||
CPPManifest(const std::string ¯o, const std::string &definition);
|
||||
typedef std::map<std::string, CPPManifest *> Manifests;
|
||||
|
||||
CPPManifest(const CPPPreprocessor &parser, const std::string &args, const cppyyltype &loc);
|
||||
CPPManifest(const CPPPreprocessor &parser, const std::string ¯o, const std::string &definition);
|
||||
~CPPManifest();
|
||||
|
||||
static std::string stringify(const std::string &source);
|
||||
std::string expand(const vector_string &args = vector_string()) const;
|
||||
void extract_args(vector_string &args, const std::string &expr, size_t &p) const;
|
||||
std::string expand(const vector_string &args = vector_string(),
|
||||
const Manifests &manifests = Manifests(),
|
||||
bool expand_undefined = false) const;
|
||||
|
||||
|
||||
CPPType *determine_type() const;
|
||||
|
||||
bool is_equal(const CPPManifest *other) const;
|
||||
void output(std::ostream &out) const;
|
||||
|
||||
const CPPPreprocessor &_parser;
|
||||
std::string _name;
|
||||
bool _has_parameters;
|
||||
size_t _num_parameters;
|
||||
@ -59,7 +67,11 @@ private:
|
||||
ExpansionNode(int parm_number, bool stringify, bool paste);
|
||||
ExpansionNode(const std::string &str, bool paste = false);
|
||||
ExpansionNode(std::vector<ExpansionNode> nested, bool stringify = false, bool paste = false, bool optional = false);
|
||||
|
||||
bool operator ==(const ExpansionNode &other) const;
|
||||
|
||||
int _parm_number;
|
||||
bool _expand;
|
||||
bool _stringify;
|
||||
bool _paste;
|
||||
bool _optional;
|
||||
@ -73,8 +85,8 @@ private:
|
||||
void save_expansion(Expansion &expansion, const std::string &exp,
|
||||
const vector_string ¶meter_names);
|
||||
|
||||
std::string r_expand(const Expansion &expansion,
|
||||
const vector_string &args = vector_string()) const;
|
||||
std::string r_expand(const Expansion &expansion, const vector_string &args,
|
||||
const Manifests &manifests, bool expand_undefined) const;
|
||||
|
||||
Expansion _expansion;
|
||||
};
|
||||
|
@ -461,7 +461,7 @@ peek_next_token() {
|
||||
*
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
warning(const string &message) {
|
||||
warning(const string &message) const {
|
||||
if (_verbose < 2) {
|
||||
return;
|
||||
}
|
||||
@ -480,7 +480,7 @@ warning(const string &message) {
|
||||
*
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
warning(const string &message, const YYLTYPE &loc) {
|
||||
warning(const string &message, const YYLTYPE &loc) const {
|
||||
if (_verbose >= 2) {
|
||||
if (_verbose >= 3) {
|
||||
indent(cerr, _files.size() * 2);
|
||||
@ -507,7 +507,7 @@ warning(const string &message, const YYLTYPE &loc) {
|
||||
*
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
error(const string &message) {
|
||||
error(const string &message) const {
|
||||
int line = get_line_number();
|
||||
int col = get_col_number();
|
||||
YYLTYPE loc;
|
||||
@ -523,7 +523,7 @@ error(const string &message) {
|
||||
*
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
error(const string &message, const YYLTYPE &loc) {
|
||||
error(const string &message, const YYLTYPE &loc) const {
|
||||
if (_state == S_nested || _state == S_end_nested) {
|
||||
// Don't report or log errors in the nested state. These will be reported
|
||||
// when the nesting level collapses.
|
||||
@ -550,20 +550,20 @@ error(const string &message, const YYLTYPE &loc) {
|
||||
show_line(loc);
|
||||
|
||||
if (!_files.empty() && !loc.file.empty()) {
|
||||
Files::reverse_iterator rit;
|
||||
Files::const_reverse_iterator rit;
|
||||
for (rit = _files.rbegin();
|
||||
rit != _files.rend() && (*rit)._file == loc.file && (*rit)._manifest != nullptr;
|
||||
++rit) {
|
||||
}
|
||||
if (rit != _files.rbegin() && rit != _files.rend()) {
|
||||
--rit;
|
||||
InputFile &infile = *rit;
|
||||
const InputFile &infile = *rit;
|
||||
if (_verbose >= 3) {
|
||||
cerr << "Expansion of " << infile._manifest->_name << ":\n";
|
||||
cerr << " -> " << trim_blanks(infile._input) << "\n";
|
||||
while (rit != _files.rbegin()) {
|
||||
--rit;
|
||||
InputFile &infile = *rit;
|
||||
const InputFile &infile = *rit;
|
||||
cerr << " -> " << trim_blanks(infile._input) << "\n";
|
||||
}
|
||||
}
|
||||
@ -589,7 +589,7 @@ error(const string &message, const YYLTYPE &loc) {
|
||||
* Shows the indicated line, useful for error messages.
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
show_line(const YYLTYPE &loc) {
|
||||
show_line(const YYLTYPE &loc) const {
|
||||
if (loc.file._filename.empty()) {
|
||||
return;
|
||||
}
|
||||
@ -846,18 +846,98 @@ push_expansion(const string &input, const CPPManifest *manifest, const YYLTYPE &
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, expand all manifests within the string and return the new
|
||||
* string.
|
||||
* Given a string, expand all manifests within the string.
|
||||
*/
|
||||
string CPPPreprocessor::
|
||||
expand_manifests(const string &input_expr, bool expand_undefined,
|
||||
const YYLTYPE &loc) {
|
||||
// Get a copy of the expression string we can modify.
|
||||
string expr = input_expr;
|
||||
void CPPPreprocessor::
|
||||
expand_manifests(string &expr, const Manifests &manifests, bool expand_undefined) const {
|
||||
size_t p = 0;
|
||||
while (p < expr.size()) {
|
||||
if (isalpha(expr[p]) || expr[p] == '_') {
|
||||
size_t q = p;
|
||||
while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) {
|
||||
p++;
|
||||
}
|
||||
string ident = expr.substr(q, p - q);
|
||||
|
||||
std::set<const CPPManifest *> expanded;
|
||||
r_expand_manifests(expr, expand_undefined, loc, expanded);
|
||||
return expr;
|
||||
// Here's an identifier. Is it "defined"?
|
||||
if (ident == "defined") {
|
||||
expand_defined_function(expr, q, p);
|
||||
}
|
||||
else if (expand_undefined && ident == "__has_include") {
|
||||
expand_has_include_function(expr, q, p);
|
||||
}
|
||||
else {
|
||||
// Is it a manifest?
|
||||
Manifests::const_iterator mi = manifests.find(ident);
|
||||
if (mi != manifests.end()) {
|
||||
const CPPManifest *manifest = (*mi).second;
|
||||
vector_string args;
|
||||
if (manifest->_has_parameters) {
|
||||
// If it's not followed by a parenthesis, don't expand it.
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
if (p >= expr.size() || expr[p] != '(') {
|
||||
continue;
|
||||
}
|
||||
|
||||
manifest->extract_args(args, expr, p);
|
||||
}
|
||||
|
||||
// Don't consider this manifest when expanding the arguments or
|
||||
// result, to prevent recursion.
|
||||
Manifests nested_manifests(manifests);
|
||||
nested_manifests.erase((*mi).first);
|
||||
|
||||
string result = manifest->expand(args);
|
||||
expand_manifests(result, nested_manifests, expand_undefined);
|
||||
|
||||
expr = expr.substr(0, q) + result + expr.substr(p);
|
||||
p = q + result.size();
|
||||
}
|
||||
else if (ident == "__FILE__") {
|
||||
// Special case: this is a dynamic definition.
|
||||
CPPFile file = get_file();
|
||||
string result = string("\"") + file._filename_as_referenced.get_fullpath() + "\"";
|
||||
expr = expr.substr(0, q) + result + expr.substr(p);
|
||||
p = q + result.size();
|
||||
|
||||
}
|
||||
else if (ident == "__LINE__") {
|
||||
// So is this.
|
||||
string line = format_string(get_line_number());
|
||||
expr = expr.substr(0, q) + line + expr.substr(p);
|
||||
p = q + line.size();
|
||||
}
|
||||
else if (expand_undefined && ident != "true" && ident != "false") {
|
||||
// It is not found. Expand it to 0, but only if we are currently
|
||||
// parsing an #if expression.
|
||||
expr = expr.substr(0, q) + "0" + expr.substr(p);
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (expr[p] == '\'' || expr[p] == '"') {
|
||||
// Skip the next part until we find a closing quotation mark.
|
||||
char quote = expr[p];
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote) {
|
||||
if (expr[p] == '\\') {
|
||||
// This might be an escaped quote. Skip an extra char.
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (p >= expr.size()) {
|
||||
// Unclosed string.
|
||||
warning("missing terminating " + string(1, quote) + " character");
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -870,7 +950,8 @@ expand_manifests(const string &input_expr, bool expand_undefined,
|
||||
CPPExpression *CPPPreprocessor::
|
||||
parse_expr(const string &input_expr, CPPScope *current_scope,
|
||||
CPPScope *global_scope, const YYLTYPE &loc) {
|
||||
string expr = expand_manifests(input_expr, false, loc);
|
||||
string expr = input_expr;
|
||||
expand_manifests(expr, _manifests, false);
|
||||
|
||||
CPPExpressionParser ep(current_scope, global_scope);
|
||||
ep._verbose = 0;
|
||||
@ -1479,7 +1560,7 @@ handle_define_directive(const string &args, const YYLTYPE &loc) {
|
||||
if (args.empty()) {
|
||||
warning("Ignoring empty #define directive", loc);
|
||||
} else {
|
||||
CPPManifest *manifest = new CPPManifest(args, loc);
|
||||
CPPManifest *manifest = new CPPManifest(*this, args, loc);
|
||||
manifest->_vis = preprocessor_vis;
|
||||
if (!manifest->_has_parameters) {
|
||||
string expr_string = manifest->expand();
|
||||
@ -1494,8 +1575,10 @@ handle_define_directive(const string &args, const YYLTYPE &loc) {
|
||||
if (!result.second) {
|
||||
// There was already a macro with this name. Delete the old.
|
||||
CPPManifest *other = result.first->second;
|
||||
warning("redefinition of macro '" + manifest->_name + "'", loc);
|
||||
warning("previous definition is here", other->_loc);
|
||||
if (!manifest->is_equal(other)) {
|
||||
warning("redefinition of macro '" + manifest->_name + "'", loc);
|
||||
warning("previous definition is here", other->_loc);
|
||||
}
|
||||
result.first->second = manifest;
|
||||
}
|
||||
}
|
||||
@ -1544,7 +1627,8 @@ handle_ifndef_directive(const string &args, const YYLTYPE &loc) {
|
||||
void CPPPreprocessor::
|
||||
handle_if_directive(const string &args, const YYLTYPE &loc) {
|
||||
// When expanding manifests, we should replace unknown macros with 0.
|
||||
string expr = expand_manifests(args, true, loc);
|
||||
string expr = args;
|
||||
expand_manifests(expr, _manifests, true);
|
||||
|
||||
int expression_result = 0;
|
||||
CPPExpressionParser ep(current_scope, global_scope);
|
||||
@ -1589,7 +1673,7 @@ handle_include_directive(const string &args, const YYLTYPE &loc) {
|
||||
// filter out quotes and angle brackets properly, we'll only expand
|
||||
// manifests if we don't begin with a quote or bracket.
|
||||
if (!expr.empty() && (expr[0] != '"' && expr[0] != '<')) {
|
||||
expr = expand_manifests(expr, false, loc);
|
||||
expand_manifests(expr, _manifests, false);
|
||||
}
|
||||
|
||||
if (!expr.empty()) {
|
||||
@ -1767,7 +1851,7 @@ skip_false_if_block(bool consider_elifs) {
|
||||
* Returns true if the given manifest is defined.
|
||||
*/
|
||||
bool CPPPreprocessor::
|
||||
is_manifest_defined(const string &manifest_name) {
|
||||
is_manifest_defined(const string &manifest_name) const {
|
||||
Manifests::const_iterator mi = _manifests.find(manifest_name);
|
||||
if (mi != _manifests.end()) {
|
||||
return true;
|
||||
@ -1787,7 +1871,7 @@ is_manifest_defined(const string &manifest_name) {
|
||||
* Locates the given filename. Changes the first argument to the full path.
|
||||
*/
|
||||
bool CPPPreprocessor::
|
||||
find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source) {
|
||||
find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source) const {
|
||||
// Now look for the filename. If we didn't use angle quotes, look first in
|
||||
// the current directory.
|
||||
if (!angle_quotes && filename.exists()) {
|
||||
@ -2180,19 +2264,17 @@ expand_manifest(const CPPManifest *manifest, const YYLTYPE &loc) {
|
||||
manifest->_variadic_param, args);
|
||||
}
|
||||
|
||||
// Perform expansion on the macro arguments.
|
||||
for (string &arg : args) {
|
||||
std::set<const CPPManifest *> expanded;
|
||||
expanded.insert(manifest);
|
||||
for (const InputFile &infile : _files) {
|
||||
if (infile._ignore_manifest) {
|
||||
expanded.insert(infile._manifest);
|
||||
}
|
||||
// Make a copy of the manifests, without the ones we're supposed to ignore.
|
||||
Manifests manifests = _manifests;
|
||||
manifests.erase(manifest->_name);
|
||||
|
||||
for (const InputFile &infile : _files) {
|
||||
if (infile._ignore_manifest) {
|
||||
manifests.erase(infile._manifest->_name);
|
||||
}
|
||||
r_expand_manifests(arg, false, loc, expanded);
|
||||
}
|
||||
|
||||
string expanded = " " + manifest->expand(args) + " ";
|
||||
string expanded = " " + manifest->expand(args, manifests, false) + " ";
|
||||
push_expansion(expanded, manifest, loc);
|
||||
|
||||
#ifdef CPP_VERBOSE_LEX
|
||||
@ -2203,111 +2285,6 @@ expand_manifest(const CPPManifest *manifest, const YYLTYPE &loc) {
|
||||
return internal_get_next_token();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive implementation of expand_manifests().
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
r_expand_manifests(string &expr, bool expand_undefined,
|
||||
const YYLTYPE &loc, std::set<const CPPManifest *> &expanded) {
|
||||
size_t p = 0;
|
||||
while (p < expr.size()) {
|
||||
if (isalpha(expr[p]) || expr[p] == '_') {
|
||||
size_t q = p;
|
||||
while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) {
|
||||
p++;
|
||||
}
|
||||
string ident = expr.substr(q, p - q);
|
||||
|
||||
// Here's an identifier. Is it "defined"?
|
||||
if (ident == "defined") {
|
||||
expand_defined_function(expr, q, p);
|
||||
}
|
||||
else if (expand_undefined && ident == "__has_include") {
|
||||
expand_has_include_function(expr, q, p, loc);
|
||||
}
|
||||
else {
|
||||
// Is it a manifest?
|
||||
Manifests::const_iterator mi = _manifests.find(ident);
|
||||
if (mi != _manifests.end()) {
|
||||
const CPPManifest *manifest = (*mi).second;
|
||||
if (expanded.count(manifest) == 0) {
|
||||
vector_string args;
|
||||
if (manifest->_has_parameters) {
|
||||
// If it's not followed by a parenthesis, don't expand it.
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
if (p >= expr.size() || expr[p] != '(') {
|
||||
continue;
|
||||
}
|
||||
|
||||
extract_manifest_args_inline(manifest->_name, manifest->_num_parameters,
|
||||
manifest->_variadic_param, args, expr, p);
|
||||
}
|
||||
|
||||
// Perform expansion on the macro arguments.
|
||||
for (string &arg : args) {
|
||||
std::set<const CPPManifest *> ignore = expanded;
|
||||
ignore.insert(manifest);
|
||||
r_expand_manifests(arg, expand_undefined, loc, ignore);
|
||||
}
|
||||
|
||||
string result = manifest->expand(args);
|
||||
|
||||
// Recurse, but adding the manifest we just expanded to the list
|
||||
// to be ignored for future expansion, to prevent recursion.
|
||||
std::set<const CPPManifest *> ignore = expanded;
|
||||
ignore.insert(manifest);
|
||||
r_expand_manifests(result, expand_undefined, loc, ignore);
|
||||
|
||||
expr = expr.substr(0, q) + result + expr.substr(p);
|
||||
p = q + result.size();
|
||||
}
|
||||
}
|
||||
else if (ident == "__FILE__") {
|
||||
// Special case: this is a dynamic definition.
|
||||
string file = string("\"") + loc.file._filename_as_referenced.get_fullpath() + "\"";
|
||||
expr = expr.substr(0, q) + file + expr.substr(p);
|
||||
p = q + file.size();
|
||||
|
||||
}
|
||||
else if (ident == "__LINE__") {
|
||||
// So is this.
|
||||
string line = format_string(loc.first_line);
|
||||
expr = expr.substr(0, q) + line + expr.substr(p);
|
||||
p = q + line.size();
|
||||
}
|
||||
else if (expand_undefined && ident != "true" && ident != "false") {
|
||||
// It is not found. Expand it to 0, but only if we are currently
|
||||
// parsing an #if expression.
|
||||
expr = expr.substr(0, q) + "0" + expr.substr(p);
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (expr[p] == '\'' || expr[p] == '"') {
|
||||
// Skip the next part until we find a closing quotation mark.
|
||||
char quote = expr[p];
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote) {
|
||||
if (expr[p] == '\\') {
|
||||
// This might be an escaped quote. Skip an extra char.
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (p >= expr.size()) {
|
||||
// Unclosed string.
|
||||
warning("missing terminating " + string(1, quote) + " character", loc);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -2427,23 +2404,34 @@ extract_manifest_args(const string &name, int num_args, int va_arg,
|
||||
* whether the manifest exists.
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
expand_defined_function(string &expr, size_t q, size_t &p) {
|
||||
string result;
|
||||
expand_defined_function(string &expr, size_t q, size_t &p) const {
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
|
||||
vector_string args;
|
||||
extract_manifest_args_inline("defined", 1, -1, args, expr, p);
|
||||
if (args.size() >= 1) {
|
||||
if (is_manifest_defined(args[0])) {
|
||||
// The macro is defined; the result is "1".
|
||||
result = "1";
|
||||
bool has_paren = false;
|
||||
if (expr[p] == '(') {
|
||||
has_paren = true;
|
||||
p++;
|
||||
}
|
||||
|
||||
size_t r = p;
|
||||
while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (has_paren) {
|
||||
if (expr[p] == ')') {
|
||||
p++;
|
||||
} else {
|
||||
// The macro is undefined; the result is "0".
|
||||
result = "0";
|
||||
error("missing ')' after 'defined'");
|
||||
}
|
||||
}
|
||||
|
||||
string name = expr.substr(r, p - r - 1);
|
||||
char result = is_manifest_defined(name) ? '1' : '0';
|
||||
expr = expr.substr(0, q) + result + expr.substr(p);
|
||||
p = q + result.size();
|
||||
p = q + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2451,153 +2439,109 @@ expand_defined_function(string &expr, size_t q, size_t &p) {
|
||||
* whether the include file exists.
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
expand_has_include_function(string &expr, size_t q, size_t &p, YYLTYPE loc) {
|
||||
expand_has_include_function(string &expr, size_t q, size_t &p) const {
|
||||
bool found_file = false;
|
||||
|
||||
// Skip whitespace till paren.
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
size_t args_begin = p + 1;
|
||||
|
||||
vector_string args;
|
||||
extract_manifest_args_inline("__has_include", 1, -1, args, expr, p);
|
||||
|
||||
if (!args.empty() && args[0].size() >= 2) {
|
||||
Filename filename;
|
||||
bool angle_quotes = false;
|
||||
|
||||
string inc = args[0];
|
||||
|
||||
// Just to play things safe, since our manifest-expansion logic might not
|
||||
// filter out quotes and angle brackets properly, we'll only expand
|
||||
// manifests if we don't begin with a quote or bracket.
|
||||
if (!inc.empty() && (inc[0] != '"' && inc[0] != '<')) {
|
||||
inc = expand_manifests(inc, false, loc);
|
||||
}
|
||||
|
||||
if (inc[0] == '"' && inc[inc.size() - 1] == '"') {
|
||||
filename = inc.substr(1, inc.size() - 2);
|
||||
} else if (inc[0] == '<' && inc[inc.size() - 1] == '>') {
|
||||
filename = inc.substr(1, inc.size() - 2);
|
||||
if (!_noangles) {
|
||||
// If _noangles is true, we don't make a distinction between angle
|
||||
// brackets and quote marks--all #inc statements are treated the
|
||||
// same, as if they used quote marks.
|
||||
angle_quotes = true;
|
||||
}
|
||||
} else {
|
||||
loc.last_column += loc.first_column + p - 2;
|
||||
loc.first_column += args_begin;
|
||||
warning("invalid argument for __has_include() directive", loc);
|
||||
expr = expr.substr(0, q) + "0" + expr.substr(p);
|
||||
p = q + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
filename.set_text();
|
||||
|
||||
CPPFile::Source source = CPPFile::S_none;
|
||||
found_file = find_include(filename, angle_quotes, source);
|
||||
} else {
|
||||
loc.last_column += loc.first_column + p - 2;
|
||||
loc.first_column += args_begin;
|
||||
warning("invalid argument for __has_include() directive", loc);
|
||||
if (expr[p] != '(') {
|
||||
error("expected '(' after '__has_include'");
|
||||
return;
|
||||
}
|
||||
p++;
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
|
||||
int paren_level = 1;
|
||||
bool needs_expansion = false;
|
||||
size_t r = p;
|
||||
while (p < expr.size()) {
|
||||
if (expr[p] == '"' || expr[p] == '\'' || expr[p] == '<') {
|
||||
// Quoted string or angle bracket.
|
||||
int quote_mark = expr[p];
|
||||
if (quote_mark == '<') {
|
||||
quote_mark = '>';
|
||||
}
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote_mark && expr[p] != '\n') {
|
||||
if (expr[p] == '\\') {
|
||||
p++;
|
||||
}
|
||||
if (p < expr.size()) {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (expr[p] == '(') {
|
||||
++paren_level;
|
||||
}
|
||||
else if (expr[p] == ')') {
|
||||
--paren_level;
|
||||
if (paren_level == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (isalnum(expr[p]) || expr[p] == '_') {
|
||||
needs_expansion = true;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p >= expr.size() || expr[p] != ')') {
|
||||
error("missing ')' after '__has_include'");
|
||||
return;
|
||||
}
|
||||
|
||||
// Back up to strip trailing whitespace.
|
||||
size_t t = p;
|
||||
while (t > r && isspace(expr[t - 1])) {
|
||||
--t;
|
||||
}
|
||||
string inc = expr.substr(r, t - r);
|
||||
p++;
|
||||
|
||||
// Only expand if we've encountered unquoted identifier-valid characters,
|
||||
// to be on the safe side.
|
||||
if (needs_expansion) {
|
||||
expand_manifests(inc, _manifests, false);
|
||||
}
|
||||
|
||||
Filename filename;
|
||||
bool angle_quotes = false;
|
||||
|
||||
if (!inc.empty() && inc[0] == '"' && inc[inc.size() - 1] == '"') {
|
||||
filename = inc.substr(1, inc.size() - 2);
|
||||
}
|
||||
else if (!inc.empty() && inc[0] == '<' && inc[inc.size() - 1] == '>') {
|
||||
filename = inc.substr(1, inc.size() - 2);
|
||||
if (!_noangles) {
|
||||
// If _noangles is true, we don't make a distinction between angle
|
||||
// brackets and quote marks--all #inc statements are treated the
|
||||
// same, as if they used quote marks.
|
||||
angle_quotes = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warning("invalid argument for __has_include() directive: " + inc);
|
||||
expr = expr.substr(0, q) + "0" + expr.substr(p);
|
||||
p = q + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
filename.set_text();
|
||||
|
||||
CPPFile::Source source = CPPFile::S_none;
|
||||
found_file = find_include(filename, angle_quotes, source);
|
||||
|
||||
string result = found_file ? "1" : "0";
|
||||
expr = expr.substr(0, q) + result + expr.substr(p);
|
||||
p = q + result.size();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void CPPPreprocessor::
|
||||
extract_manifest_args_inline(const string &name, int num_args,
|
||||
int va_arg, vector_string &args,
|
||||
const string &expr, size_t &p) {
|
||||
// Skip whitespace till paren.
|
||||
while (p < expr.size() && isspace(expr[p])) {
|
||||
p++;
|
||||
}
|
||||
if (p >= expr.size() || expr[p] != '(') {
|
||||
// No paren, so we have only one arg.
|
||||
size_t q = p;
|
||||
while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) {
|
||||
p++;
|
||||
}
|
||||
args.push_back(expr.substr(q, p - q));
|
||||
|
||||
} else if (expr[p] == '"' || expr[p] == '\'') {
|
||||
// Quoted string or character.
|
||||
int quote_mark = expr[p];
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote_mark && expr[p] != '\n') {
|
||||
if (expr[p] == '\\') {
|
||||
p++;
|
||||
}
|
||||
if (p < expr.size()) {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
|
||||
} else {
|
||||
// Skip paren.
|
||||
p++;
|
||||
int paren_level = 1;
|
||||
size_t q = p;
|
||||
while (p < expr.size()) {
|
||||
if (expr[p] == ',' && paren_level == 1) {
|
||||
args.push_back(trim_blanks(expr.substr(q, p - q)));
|
||||
q = p+1;
|
||||
|
||||
} else if (expr[p] == '"' || expr[p] == '\'') {
|
||||
// Quoted string or character.
|
||||
int quote_mark = expr[p];
|
||||
p++;
|
||||
while (p < expr.size() && expr[p] != quote_mark && expr[p] != '\n') {
|
||||
if (expr[p] == '\\') {
|
||||
p++;
|
||||
}
|
||||
if (p < expr.size()) {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (expr[p] == '(') {
|
||||
++paren_level;
|
||||
|
||||
} else if (expr[p] == ')') {
|
||||
--paren_level;
|
||||
if (paren_level == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (isspace(expr[p])) {
|
||||
// Skip whitespace at the beginning.
|
||||
if (q == p) {
|
||||
q++;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
args.push_back(trim_blanks(expr.substr(q, p - q)));
|
||||
|
||||
if (p < expr.size() && expr[p] == ')') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)args.size() < num_args) {
|
||||
warning("Not enough arguments for manifest " + name);
|
||||
|
||||
} else if (va_arg < 0 && (int)args.size() > num_args) {
|
||||
warning("Too many arguments for manifest " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming that we've just read a digit or a period indicating the start of a
|
||||
* number, read the rest.
|
||||
|
@ -57,11 +57,11 @@ public:
|
||||
int _token_index;
|
||||
#endif
|
||||
|
||||
void warning(const std::string &message);
|
||||
void warning(const std::string &message, const YYLTYPE &loc);
|
||||
void error(const std::string &message);
|
||||
void error(const std::string &message, const YYLTYPE &loc);
|
||||
void show_line(const YYLTYPE &loc);
|
||||
void warning(const std::string &message) const;
|
||||
void warning(const std::string &message, const YYLTYPE &loc) const;
|
||||
void error(const std::string &message) const;
|
||||
void error(const std::string &message, const YYLTYPE &loc) const;
|
||||
void show_line(const YYLTYPE &loc) const;
|
||||
|
||||
CPPCommentBlock *get_comment_before(int line, CPPFile file);
|
||||
CPPCommentBlock *get_comment_on(int line, CPPFile file);
|
||||
@ -69,7 +69,7 @@ public:
|
||||
int get_warning_count() const;
|
||||
int get_error_count() const;
|
||||
|
||||
typedef std::map<std::string, CPPManifest *> Manifests;
|
||||
typedef CPPManifest::Manifests Manifests;
|
||||
Manifests _manifests;
|
||||
|
||||
typedef std::vector<CPPManifest *> ManifestStack;
|
||||
@ -116,8 +116,9 @@ protected:
|
||||
bool push_expansion(const std::string &input, const CPPManifest *manifest,
|
||||
const YYLTYPE &loc);
|
||||
|
||||
std::string expand_manifests(const std::string &input_expr, bool expand_undefined,
|
||||
const YYLTYPE &loc);
|
||||
public:
|
||||
void expand_manifests(std::string &expr, const Manifests &manifests,
|
||||
bool expand_undefined = false) const;
|
||||
CPPExpression *parse_expr(const std::string &expr, CPPScope *current_scope,
|
||||
CPPScope *global_scope, const YYLTYPE &loc);
|
||||
|
||||
@ -145,8 +146,8 @@ private:
|
||||
void handle_error_directive(const std::string &args, const YYLTYPE &loc);
|
||||
|
||||
void skip_false_if_block(bool consider_elifs);
|
||||
bool is_manifest_defined(const std::string &manifest_name);
|
||||
bool find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source);
|
||||
bool is_manifest_defined(const std::string &manifest_name) const;
|
||||
bool find_include(Filename &filename, bool angle_quotes, CPPFile::Source &source) const;
|
||||
|
||||
CPPToken get_quoted_char(int c);
|
||||
CPPToken get_quoted_string(int c);
|
||||
@ -158,11 +159,8 @@ private:
|
||||
const YYLTYPE &loc, std::set<const CPPManifest *> &expanded);
|
||||
void extract_manifest_args(const std::string &name, int num_args,
|
||||
int va_arg, vector_string &args);
|
||||
void expand_defined_function(std::string &expr, size_t q, size_t &p);
|
||||
void expand_has_include_function(std::string &expr, size_t q, size_t &p, YYLTYPE loc);
|
||||
void extract_manifest_args_inline(const std::string &name, int num_args,
|
||||
int va_arg, vector_string &args,
|
||||
const std::string &expr, size_t &p);
|
||||
void expand_defined_function(std::string &expr, size_t q, size_t &p) const;
|
||||
void expand_has_include_function(std::string &expr, size_t q, size_t &p) const;
|
||||
|
||||
CPPToken get_number(int c);
|
||||
static int check_keyword(const std::string &name);
|
||||
@ -227,8 +225,8 @@ private:
|
||||
|
||||
std::vector<CPPToken> _saved_tokens;
|
||||
|
||||
int _warning_count;
|
||||
int _error_count;
|
||||
mutable int _warning_count;
|
||||
mutable int _error_count;
|
||||
bool _error_abort;
|
||||
};
|
||||
|
||||
|
@ -302,7 +302,7 @@ predefine_macro(CPPParser& parser, const string& inoption) {
|
||||
macro_name = inoption;
|
||||
}
|
||||
|
||||
CPPManifest *macro = new CPPManifest(macro_name, macro_def);
|
||||
CPPManifest *macro = new CPPManifest(parser, macro_name, macro_def);
|
||||
parser._manifests[macro->_name] = macro;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ predefine_macro(CPPParser &parser, const string &option) {
|
||||
|
||||
cerr << "Predefining " << macro_name << " as " << macro_def << "\n";
|
||||
|
||||
CPPManifest *macro = new CPPManifest(macro_name, macro_def);
|
||||
CPPManifest *macro = new CPPManifest(parser, macro_name, macro_def);
|
||||
parser._manifests[macro->_name] = macro;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user