Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 27 additions & 29 deletions simplecpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1635,7 +1635,7 @@ namespace simplecpp {
if (output2.cfront() != output2.cback() && macro2tok->str() == this->name())
break;
const MacroMap::const_iterator macro = macros.find(macro2tok->str());
if (macro == macros.end() || !macro->second.functionLike())
if (macro == macros.end() || !macro->second.functionLike() || macro2tok->isExpandedFrom(&macro->second))
break;
TokenList rawtokens2(inputFiles);
const Location loc(macro2tok->location);
Expand Down Expand Up @@ -2089,39 +2089,37 @@ namespace simplecpp {
return functionLike() ? parametertokens2.back()->next : nameTokInst->next;
}

const Token *recursiveExpandToken(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
if (!temp.cback() || !temp.cback()->name || !tok->next || tok->next->op != '(') {
output.takeTokens(temp);
return tok->next;
}

if (!sameline(tok, tok->next)) {
output.takeTokens(temp);
return tok->next;
}

/** Returns the macro to expand when the last token of @p temp is the name of a
* function-like macro and the tokens after @p tok supply its arguments; nullptr otherwise */
static const Macro *rescanMacro(const TokenList &temp, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros) {
if (!temp.cback() || !temp.cback()->name || !sameline(tok, tok->next) || tok->next->op != '(')
return nullptr;
const MacroMap::const_iterator it = macros.find(temp.cback()->str());
if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) {
output.takeTokens(temp);
return tok->next;
}
if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end())
return nullptr;
if (!it->second.functionLike() || temp.cback()->isExpandedFrom(&it->second))
return nullptr;
return &it->second;
}

const Macro &calledMacro = it->second;
if (!calledMacro.functionLike()) {
const Token *recursiveExpandToken(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
// Expand while the expansion result ends with the name of a function-like
// macro whose arguments are supplied by the tokens that follow it. Each round
// consumes that macro call from the token stream, so tok always advances.
while (const Macro * const calledMacro = rescanMacro(temp, tok, macros, expandedmacros)) {
TokenList temp2(files);
temp2.push_back(new Token(temp.cback()->str(), tok->location));

const Token * const tok2 = appendTokens(temp2, loc, tok->next, macros, expandedmacros, parametertokens);
if (!tok2)
break;
output.takeTokens(temp);
return tok->next;
output.deleteToken(output.back());
calledMacro->expand(temp, loc, temp2.cfront(), macros, expandedmacros);
tok = tok2;
}

TokenList temp2(files);
temp2.push_back(new Token(temp.cback()->str(), tok->location));

const Token * const tok2 = appendTokens(temp2, loc, tok->next, macros, expandedmacros, parametertokens);
if (!tok2)
return tok->next;
output.takeTokens(temp);
output.deleteToken(output.back());
calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros);
return tok2->next;
return tok->next;
}

const Token *expandToken(TokenList &output, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
Expand Down
43 changes: 43 additions & 0 deletions test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,46 @@ static void define23() // #40
"unsigned A , B ;", preprocess(code));
}

static void define24()
{
// an expansion result that is a function-like macro name must be rescanned
// repeatedly against the tokens that follow it
const char code[] = "#define a(b, c) c\n"
"#define d() a\n"
"#define g(e) h(e, ) h(e, )\n"
"#define h(e, b) d()(, e)()\n"
"#define i()\n"
"g(i)\n";
ASSERT_EQUALS("", preprocess(code));
}

static void define25()
{
// a macro name that came from expanding that same macro must not be
// re-expanded when rescanned with the tokens that follow it
const char code[] = "#define f() f\n"
"#define wrap(x) x()\n"
"wrap(f())\n";
ASSERT_EQUALS("\n"
"\n"
"f ( )", preprocess(code));
}

static void define26()
{
// a macro name that came from expanding that same macro must not be
// re-expanded with arguments taken from the raw token stream
const char code[] = "#define f() f\n"
"f()()\n";
ASSERT_EQUALS("\n"
"f ( )", preprocess(code));

const char code2[] = "#define f() f\n"
"f()()()\n";
ASSERT_EQUALS("\n"
"f ( ) ( )", preprocess(code2));
}


static void define_invalid_1()
{
Expand Down Expand Up @@ -4096,6 +4136,9 @@ static void runTests(int argc, char **argv, Input input)
TEST_CASE(define21); // #66
TEST_CASE(define22); // #40
TEST_CASE(define23); // #40
TEST_CASE(define24);
TEST_CASE(define25);
TEST_CASE(define26);
TEST_CASE(define_invalid_1);
TEST_CASE(define_invalid_2);
TEST_CASE(define_invalid_3);
Expand Down
Loading