diff --git a/dtool/src/cppparser/cppManifest.cxx b/dtool/src/cppparser/cppManifest.cxx index 30964deca5..1b4f2c845d 100644 --- a/dtool/src/cppparser/cppManifest.cxx +++ b/dtool/src/cppparser/cppManifest.cxx @@ -24,8 +24,8 @@ // Description: //////////////////////////////////////////////////////////////////// CPPManifest::ExpansionNode:: -ExpansionNode(int parm_number) : - _parm_number(parm_number) +ExpansionNode(int parm_number, bool stringify, bool paste) : + _parm_number(parm_number), _stringify(stringify), _paste(paste) { } @@ -35,8 +35,8 @@ ExpansionNode(int parm_number) : // Description: //////////////////////////////////////////////////////////////////// CPPManifest::ExpansionNode:: -ExpansionNode(const string &str) : - _parm_number(-1), _str(str) +ExpansionNode(const string &str, bool paste) : + _parm_number(-1), _stringify(false), _paste(paste), _str(str) { } @@ -46,7 +46,11 @@ ExpansionNode(const string &str) : // Description: //////////////////////////////////////////////////////////////////// CPPManifest:: -CPPManifest(const string &args, const CPPFile &file) : _file(file) { +CPPManifest(const string &args, const CPPFile &file) : + _file(file), + _variadic_param(-1), + _expr((CPPExpression *)NULL) +{ assert(!args.empty()); assert(!isspace(args[0])); @@ -69,6 +73,7 @@ CPPManifest(const string &args, const CPPFile &file) : _file(file) { _has_parameters = true; parse_parameters(args, p, parameter_names); _num_parameters = parameter_names.size(); + p++; } else { _has_parameters = false; @@ -83,7 +88,6 @@ CPPManifest(const string &args, const CPPFile &file) : _file(file) { save_expansion(args.substr(p), parameter_names); } - //////////////////////////////////////////////////////////////////// // Function: CPPManifest::Destructor // Access: Public @@ -96,7 +100,58 @@ CPPManifest:: } } +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::stringify +// Access: Public, Static +// Description: This implements the stringification operator, #. +//////////////////////////////////////////////////////////////////// +string CPPManifest:: +stringify(const string &source) { + string result("\""); + enum { + S_escaped = 0x01, + S_single_quoted = 0x02, + S_double_quoted = 0x04, + S_quoted = 0x06, + }; + int state = 0; + + string::const_iterator it; + for (it = source.begin(); it != source.end(); ++it) { + char c = *it; + + if ((state & S_escaped) == 0) { + switch (c) { + case '\\': + if (state & S_quoted) { + state |= S_escaped; + result += '\\'; + } + break; + + case '\'': + state ^= S_single_quoted; + break; + + case '"': + state ^= S_double_quoted; + result += '\\'; + break; + } + } else { + if (c == '\\' || c == '"') { + result += '\\'; + } + state &= ~S_escaped; + } + + result += c; + } + + result += '"'; + return result; +} //////////////////////////////////////////////////////////////////// // Function: CPPManifest::expand @@ -111,14 +166,45 @@ expand(const vector_string &args) const { for (ei = _expansion.begin(); ei != _expansion.end(); ++ei) { if ((*ei)._parm_number >= 0) { int i = (*ei)._parm_number; + + string subst; if (i < (int)args.size()) { - result += " " + args[i] + " "; - } else { - result += " "; + subst = args[i]; + + if (i == _variadic_param) { + for (++i; i < (int)args.size(); ++i) { + subst += ", " + args[i]; + } + } + if ((*ei)._stringify) { + subst = stringify(subst); + } + } else if (i == _variadic_param && (*ei)._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. + if (*result.rbegin() == ',') { + result.resize(result.size() - 1); + } + } + + if (!subst.empty()) { + if (result.empty() || (*ei)._paste) { + result += subst; + } else { + result += ' '; + result += subst; + } } } if (!(*ei)._str.empty()) { - result += (*ei)._str; + if (result.empty() || (*ei)._paste) { + result += (*ei)._str; + } else { + result += ' '; + result += (*ei)._str; + } } } @@ -151,20 +237,40 @@ output(ostream &out) const { if (_has_parameters) { out << "("; if (_num_parameters > 0) { - out << "$1"; + if (_variadic_param == 0) { + out << "..."; + } else { + out << "$1"; + } + for (int i = 1; i < _num_parameters; ++i) { - out << ", $" << i + 1; + if (_variadic_param == i) { + out << ", ..."; + } else { + out << ", $" << i + 1; + } } } out << ")"; } - out << " "; - Expansion::const_iterator ei; for (ei = _expansion.begin(); ei != _expansion.end(); ++ei) { + if ((*ei)._paste) { + out << " ## "; + } else { + out << " "; + } + if ((*ei)._parm_number >= 0) { - out << " $" << (*ei)._parm_number + 1 << " "; + if (stringify) { + out << "#"; + } + if ((*ei)._parm_number == _variadic_param) { + out << "__VA_ARGS__"; + } else { + out << "$" << (*ei)._parm_number + 1; + } } if (!(*ei)._str.empty()) { out << (*ei)._str; @@ -172,8 +278,6 @@ output(ostream &out) const { } } - - //////////////////////////////////////////////////////////////////// // Function: CPPManifest::parse_parameters // Access: Private @@ -197,7 +301,16 @@ parse_parameters(const string &args, size_t &p, args[p] != ')' && args[p] != ',') { p++; } - parameter_names.push_back(args.substr(q, p - q)); + + // Check if it's a variadic parameter by checking if it ends + // with "...". This picks up both C99-style variadic macros + // and GCC-style variadic macros. + if (p - q >= 3 && args.compare(p - 3, 3, "...") == 0) { + _variadic_param = parameter_names.size(); + parameter_names.push_back(args.substr(q, p - q - 3)); + } else { + parameter_names.push_back(args.substr(q, p - q)); + } // Skip whitespace after the parameter name. while (p < args.size() && isspace(args[p])) { @@ -221,16 +334,12 @@ parse_parameters(const string &args, size_t &p, //////////////////////////////////////////////////////////////////// void CPPManifest:: save_expansion(const string &exp, const vector_string ¶meter_names) { - if (parameter_names.empty()) { - // No parameters; this is an easy case. - _expansion.push_back(ExpansionNode(exp)); - return; - } - // Walk through the expansion string. For each substring that is an // identifier, check it against parameter_names. size_t p = 0; size_t last = 0; + bool stringify = false; + bool paste = false; while (p < exp.size()) { if (isalpha(exp[p]) || exp[p] == '_') { // Here's the start of an identifier. Find the end of it. @@ -244,26 +353,67 @@ save_expansion(const string &exp, const vector_string ¶meter_names) { // Is this identifier one of our parameters? int pnum = -1; - for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); ++i) { - if (parameter_names[i] == ident) { - pnum = i; + bool va_args = false; + + if (ident == "__VA_ARGS__") { + va_args = true; + // C99-style variadics, ie. #define macro(...) __VA_ARGS__ + pnum = _variadic_param; + + } else { + for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); ++i) { + const string &pname = parameter_names[i]; + if (pname == ident) { + pnum = i; + } } } if (pnum != -1) { // Yep! if (last != q) { - _expansion.push_back(ExpansionNode(exp.substr(last, q - last))); + _expansion.push_back(ExpansionNode(exp.substr(last, q - last), paste)); + paste = false; } - _expansion.push_back(pnum); + _expansion.push_back(ExpansionNode(pnum, stringify, paste)); + stringify = false; + paste = false; last = p; } + } else if (exp[p] == '#') { + // This may be a stringification operator. + if (last != p) { + _expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste)); + paste = false; + } + + ++p; + + if (p < exp.size() && exp[p] == '#') { + // Woah, this is a token-pasting operator. + paste = true; + ++p; + } else { + // Mark that the next argument should be stringified. + stringify = true; + } + last = p; + + } else if (isspace(exp[p])) { + if (last != p) { + _expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste)); + paste = false; + } + + ++p; + last = p; + } else { - p++; + ++p; } } if (last != p) { - _expansion.push_back(exp.substr(last, p - last)); + _expansion.push_back(ExpansionNode(exp.substr(last, p - last), paste)); } } diff --git a/dtool/src/cppparser/cppManifest.h b/dtool/src/cppparser/cppManifest.h index dc0c6be822..5717ad408a 100644 --- a/dtool/src/cppparser/cppManifest.h +++ b/dtool/src/cppparser/cppManifest.h @@ -33,6 +33,8 @@ class CPPManifest { public: CPPManifest(const string &args, const CPPFile &file = CPPFile()); ~CPPManifest(); + + static string stringify(const string &source); string expand(const vector_string &args = vector_string()) const; CPPType *determine_type() const; @@ -42,6 +44,7 @@ public: string _name; bool _has_parameters; int _num_parameters; + int _variadic_param; CPPFile _file; CPPExpression *_expr; @@ -59,9 +62,11 @@ private: class ExpansionNode { public: - ExpansionNode(int parm_number); - ExpansionNode(const string &str); + ExpansionNode(int parm_number, bool stringify, bool paste); + ExpansionNode(const string &str, bool paste = false); int _parm_number; + bool _stringify; + bool _paste; string _str; }; typedef vector Expansion; diff --git a/dtool/src/cppparser/cppPreprocessor.cxx b/dtool/src/cppparser/cppPreprocessor.cxx index 9715c26b8f..9e0690e952 100644 --- a/dtool/src/cppparser/cppPreprocessor.cxx +++ b/dtool/src/cppparser/cppPreprocessor.cxx @@ -315,23 +315,6 @@ get_next_token0() { int first_col = token._lloc.first_column; CPPFile first_file = token._lloc.file; - if (token._token == '#') { - // Stringify. - token = internal_get_next_token(); - if (token._token == SIMPLE_IDENTIFIER || - token._token == INTEGER || - token._token == REAL || - token._token == STRING) { - token._token = STRING; - } else { - // Stringify nothing. - - _saved_tokens.push_back(token); - token._token = STRING; - token._lval.str = ""; - } - } - if (_resolve_identifiers && (token._token == SIMPLE_IDENTIFIER || token._token == SCOPE)) { // We will be returning a scoped identifier, or a scoping. Keep @@ -366,59 +349,41 @@ get_next_token0() { } } - while (token._token == SCOPE || token._token == TOKENPASTE) { - if (token._token == TOKENPASTE) { - // The token-pasting operator creates one continuous - // identifier across whitespace. - token = internal_get_next_token(); - if (token._token == SIMPLE_IDENTIFIER || - token._token == INTEGER || - token._token == REAL) { - name += token._lval.str; - ident->_names.back().append_name(token._lval.str); - - token = internal_get_next_token(); - - } else { - // Token-paste with nothing. - } - - } else { // token._token == SCOPE - name += "::"; - token = internal_get_next_token(); - string token_prefix; - - if (token._token == '~') { - // A scoping operator followed by a tilde can only be the - // start of a scoped destructor name. Make the tilde be part - // of the name. - name += "~"; - token_prefix = "~"; - token = internal_get_next_token(); - } - - if (token._token != SIMPLE_IDENTIFIER) { - // The last useful token was a SCOPE, thus this is a scoping - // token. - - if (token._token == KW_OPERATOR) { - // Unless the last token we came across was the "operator" - // keyword. We make a special case for this, because it's - // occasionally scoped in normal use. - token._lval = result; - return token; - } - _saved_tokens.push_back(token); - return CPPToken(SCOPING, first_line, first_col, first_file, - name, result); - } - - name += token._lval.str; - ident->_names.push_back(token_prefix + token._lval.str); + while (token._token == SCOPE) { + name += "::"; + token = internal_get_next_token(); + string token_prefix; + if (token._token == '~') { + // A scoping operator followed by a tilde can only be the + // start of a scoped destructor name. Make the tilde be part + // of the name. + name += "~"; + token_prefix = "~"; token = internal_get_next_token(); } + if (token._token != SIMPLE_IDENTIFIER) { + // The last useful token was a SCOPE, thus this is a scoping + // token. + + if (token._token == KW_OPERATOR) { + // Unless the last token we came across was the "operator" + // keyword. We make a special case for this, because it's + // occasionally scoped in normal use. + token._lval = result; + return token; + } + _saved_tokens.push_back(token); + return CPPToken(SCOPING, first_line, first_col, first_file, + name, result); + } + + name += token._lval.str; + ident->_names.push_back(token_prefix + token._lval.str); + + token = internal_get_next_token(); + if (token._token == '<') { // If the next token is an angle bracket and the current // indentifier wants template instantiation, assume the angle @@ -887,11 +852,6 @@ internal_get_next_token() { case '%': if (next_c == '=') return CPPToken(MODEQUAL, first_line, first_col, first_file); break; - - // These are actually preprocessor operators, but it's useful to - // treat them as tokens. - case '#': - if (next_c == '#') return CPPToken(TOKENPASTE, first_line, first_col, first_file); } // It wasn't any of the two- or three-character tokens, so put back @@ -945,11 +905,13 @@ skip_whitespace(int c) { c = skip_comment(c); if (c == '\\') { - // A backslash character is an unusual thing to encounter in the - // middle of unquoted C++ code. But it seems to be legal, and - // it seems to mean the same thing it does within quotes: to - // escape the following character. We simply ignore it. + // This does not usually occur in the middle of unquoted C++ + // code, except before a newline character. c = get(); + if (c != '\n') { + unget(c); + return '\\'; + } } if (!isspace(c)) { @@ -1633,7 +1595,8 @@ expand_manifest(const CPPManifest *manifest) { if (manifest->_has_parameters) { // Hmm, we're expecting arguments. - extract_manifest_args(manifest->_name, manifest->_num_parameters, args); + extract_manifest_args(manifest->_name, manifest->_num_parameters, + manifest->_variadic_param, args); } string expanded = " " + manifest->expand(args) + " "; @@ -1659,7 +1622,7 @@ expand_manifest(const CPPManifest *manifest) { // Description: //////////////////////////////////////////////////////////////////// void CPPPreprocessor:: -extract_manifest_args(const string &name, int num_args, +extract_manifest_args(const string &name, int num_args, int va_arg, vector_string &args) { CPPFile first_file = get_file(); int first_line = get_line_number(); @@ -1683,12 +1646,14 @@ extract_manifest_args(const string &name, int num_args, } else { // Skip paren. - c = get(); + c = skip_whitespace(get()); + int paren_level = 1; string arg; - while (c != EOF && c != ')') { - if (c == ',') { + while (c != EOF) { + if (c == ',' && paren_level == 1) { args.push_back(arg); arg = ""; + c = get(); } else if (c == '"' || c == '\'') { // Quoted string or character. @@ -1706,35 +1671,55 @@ extract_manifest_args(const string &name, int num_args, } } arg += c; + c = get(); } else if (c == '(') { - // Nested parens. - int paren_level = 1; - while (c != EOF && paren_level > 0) { - arg += c; - c = get(); - if (c == '(') { - paren_level++; - } else if (c == ')') { - paren_level--; - } + arg += '('; + ++paren_level; + c = get(); + + } else if (c == ')') { + --paren_level; + if (paren_level == 0) { + break; } - if (c != EOF) { - arg += c; + arg += ')'; + c = get(); + + } else if (isspace(c)) { + // Skip extra whitespace. + c = skip_whitespace(c); + if (!arg.empty()) { + arg += ' '; + } + + } else if (c == '\\') { + // It could be a slash before a newline. + // If so, that's whitespace as well. + c = get(); + if (c != '\n') { + arg += '\\'; + } else if (!arg.empty()) { + arg += ' '; + c = skip_whitespace(get()); } } else { arg += c; + c = get(); } - c = get(); } if (num_args != 0 || !arg.empty()) { args.push_back(arg); } } - if ((int)args.size() != num_args) { - warning("Wrong number of arguments for manifest " + name, + if ((int)args.size() < num_args) { + warning("Not enough arguments for manifest " + name, + first_line, first_col, first_file); + + } else if (va_arg < 0 && (int)args.size() > num_args) { + warning("Too many arguments for manifest " + name, first_line, first_col, first_file); } } @@ -1742,14 +1727,15 @@ extract_manifest_args(const string &name, int num_args, //////////////////////////////////////////////////////////////////// // Function: CPPPreprocessor::expand_defined_function // Access: Private -// Description: +// Description: Expands the defined(manifest) function to either +// 1 or 0, depending on whether the manifest exists. //////////////////////////////////////////////////////////////////// void CPPPreprocessor:: expand_defined_function(string &expr, size_t q, size_t &p) { string result; vector_string args; - extract_manifest_args_inline("defined", 1, args, expr, p); + extract_manifest_args_inline("defined", 1, -1, args, expr, p); if (args.size() >= 1) { const string &manifest_name = args[0]; Manifests::const_iterator mi = _manifests.find(manifest_name); @@ -1777,7 +1763,7 @@ expand_manifest_inline(string &expr, size_t q, size_t &p, vector_string args; if (manifest->_has_parameters) { extract_manifest_args_inline(manifest->_name, manifest->_num_parameters, - args, expr, p); + manifest->_variadic_param, args, expr, p); } string result = manifest->expand(args); @@ -1792,7 +1778,7 @@ expand_manifest_inline(string &expr, size_t q, size_t &p, //////////////////////////////////////////////////////////////////// void CPPPreprocessor:: extract_manifest_args_inline(const string &name, int num_args, - vector_string &args, + int va_arg, vector_string &args, const string &expr, size_t &p) { // Skip whitespace till paren. while (p < expr.size() && isspace(expr[p])) { @@ -1819,7 +1805,7 @@ extract_manifest_args_inline(const string &name, int num_args, } } p++; - + } else { // Skip paren. p++; @@ -1850,8 +1836,11 @@ extract_manifest_args_inline(const string &name, int num_args, } } - if ((int)args.size() != num_args) { - warning("Wrong number of arguments for manifest " + name); + 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); } } @@ -2035,6 +2024,90 @@ check_keyword(const string &name) { return 0; } +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::scan_escape_sequence +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +scan_escape_sequence(int c) { + if (c != '\\') { + return c; + } + + c = get(); + switch (c) { + case 'a': + return '\a'; + + case 'b': + return '\b'; + + case 'f': + return '\f'; + + case 'n': + return '\n'; + + case 'r': + return '\r'; + + case 't': + return '\t'; + + case 'v': + return '\v'; + + case 'e': + // \e is non-standard, buT GCC supports it. + return '\x1B'; + + case 'x': + // hex character. + c = get(); + if (isxdigit(c)) { + int val = hex_val(c); + c = get(); + if (isxdigit(c)) { + val = (val << 4) | hex_val(c); + } else { + unget(c); + } + return val; + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // Octal character. + { + int val = (c - '0'); + c = get(); + if (c >= '0' && c <= '7') { + val = (val << 3) | (c - '0'); + c = get(); + if (c >= '0' && c <= '7') { + val = (val << 3) | (c - '0'); + } else { + unget(c); + } + } else { + unget(c); + } + return val; + } + } + + // Simply output the following character. + return c; +} + //////////////////////////////////////////////////////////////////// // Function: CPPPreprocessor::scan_quoted // Access: Private @@ -2049,62 +2122,7 @@ scan_quoted(int c) { while (c != EOF && c != '\n' && c != quote_mark) { if (c == '\\') { // Backslash means a special character follows. - c = get(); - switch (c) { - case 'n': - c = '\n'; - break; - - case 't': - c = '\t'; - break; - - case 'r': - c = '\r'; - break; - - case 'x': - // hex character. - c = get(); - if (isxdigit(c)) { - int val = hex_val(c); - c = get(); - if (isxdigit(c)) { - val = (val << 4) | hex_val(c); - } else { - unget(c); - } - c = val; - } - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - // Octal character. - { - int val = (c - '0'); - c = get(); - if (c >= '0' && c <= '7') { - val = (val << 3) | (c - '0'); - c = get(); - if (c >= '0' && c <= '7') { - val = (val << 3) | (c - '0'); - } else { - unget(c); - } - } else { - unget(c); - } - c = val; - } - break; - } + c = scan_escape_sequence(c); } str += c; diff --git a/dtool/src/cppparser/cppPreprocessor.h b/dtool/src/cppparser/cppPreprocessor.h index b73953fdf3..48a9dafa0b 100644 --- a/dtool/src/cppparser/cppPreprocessor.h +++ b/dtool/src/cppparser/cppPreprocessor.h @@ -142,16 +142,17 @@ private: CPPToken get_identifier(int c); CPPToken expand_manifest(const CPPManifest *manifest); void extract_manifest_args(const string &name, int num_args, - vector_string &args); + int va_arg, vector_string &args); void expand_defined_function(string &expr, size_t q, size_t &p); void expand_manifest_inline(string &expr, size_t q, size_t &p, const CPPManifest *manifest); void extract_manifest_args_inline(const string &name, int num_args, - vector_string &args, + int va_arg, vector_string &args, const string &expr, size_t &p); CPPToken get_number(int c, int c2 = 0); static int check_keyword(const string &name); + int scan_escape_sequence(int c); string scan_quoted(int c); bool should_ignore_manifest(const CPPManifest *manifest) const;