Skip to content

Commit 53fee14

Browse files
committed
Fix expansion of function like macroes arguments (issue #13)
1 parent db89af1 commit 53fee14

File tree

2 files changed

+162
-11
lines changed

2 files changed

+162
-11
lines changed

source/tcppLibrary.hpp

Lines changed: 131 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ namespace tcpp
353353
void _removeMacroDefinition(const std::string& macroName) TCPP_NOEXCEPT;
354354

355355
std::vector<TToken> _expandMacroDefinition(const TMacroDesc& macroDesc, const TToken& idToken, const std::function<TToken()>& getNextTokenCallback) const TCPP_NOEXCEPT;
356+
std::vector<TToken> _expandArg(const std::function<TToken()>& getNextTokenCallback) const TCPP_NOEXCEPT;
356357

357358
void _expect(const E_TOKEN_TYPE& expectedType, const E_TOKEN_TYPE& actualType) const TCPP_NOEXCEPT;
358359

@@ -385,6 +386,8 @@ namespace tcpp
385386
///< implementation of the library is placed below
386387
#if defined(TCPP_IMPLEMENTATION)
387388

389+
static const std::string EMPTY_STR_VALUE = "";
390+
388391

389392
std::string ErrorTypeToString(const E_ERROR_TYPE& errorType) TCPP_NOEXCEPT
390393
{
@@ -418,7 +421,7 @@ namespace tcpp
418421
return "Incorrect usage of stringification operation";
419422
}
420423

421-
return "";
424+
return EMPTY_STR_VALUE;
422425
}
423426

424427

@@ -684,7 +687,7 @@ namespace tcpp
684687

685688
static const std::string separators = ",()[]<>\"+-*/&|!=;";
686689

687-
std::string currStr = "";
690+
std::string currStr = EMPTY_STR_VALUE;
688691

689692
while (!inputLine.empty())
690693
{
@@ -782,7 +785,7 @@ namespace tcpp
782785
inputLine.erase(0, currDirectiveStr.length());
783786
mCurrPos += currDirectiveStr.length();
784787

785-
return { std::get<E_TOKEN_TYPE>(currDirective), "", mCurrLineIndex, mCurrPos };
788+
return { std::get<E_TOKEN_TYPE>(currDirective), EMPTY_STR_VALUE, mCurrLineIndex, mCurrPos };
786789
}
787790
}
788791

@@ -807,11 +810,11 @@ namespace tcpp
807810
case '#': // \note concatenation operator
808811
inputLine.erase(0, 1);
809812
++mCurrPos;
810-
return { E_TOKEN_TYPE::CONCAT_OP, "", mCurrLineIndex, mCurrPos };
813+
return { E_TOKEN_TYPE::CONCAT_OP, EMPTY_STR_VALUE, mCurrLineIndex, mCurrPos };
811814
default:
812815
if (nextCh != ' ') // \note stringification operator
813816
{
814-
return { E_TOKEN_TYPE::STRINGIZE_OP, "", mCurrLineIndex, mCurrPos };
817+
return { E_TOKEN_TYPE::STRINGIZE_OP, EMPTY_STR_VALUE, mCurrLineIndex, mCurrPos };
815818
}
816819

817820
return { E_TOKEN_TYPE::BLOB, "#", mCurrLineIndex, mCurrPos };
@@ -973,7 +976,7 @@ namespace tcpp
973976

974977
if (!pCurrInputStream->HasNextLine())
975978
{
976-
return "";
979+
return EMPTY_STR_VALUE;
977980
}
978981

979982
std::string sourceLine = pCurrInputStream->ReadLine();
@@ -1557,6 +1560,11 @@ namespace tcpp
15571560

15581561
std::string replacementValue;
15591562

1563+
std::unordered_map<std::string, std::vector<TToken>> cachedArgsSequences
1564+
{
1565+
{ EMPTY_STR_VALUE, {} }
1566+
};
1567+
15601568
for (size_t currArgIndex = 0; currArgIndex < processingTokens.size(); ++currArgIndex)
15611569
{
15621570
const bool variadics = macroDesc.mVariadic && currArgIndex == macroDesc.mArgsNames.size() - 1;
@@ -1581,14 +1589,50 @@ namespace tcpp
15811589
}
15821590
}
15831591

1584-
for (auto& currToken : replacementList)
1592+
for (size_t currTokenIndex = 0; currTokenIndex < replacementList.size(); ++currTokenIndex)
15851593
{
1594+
TToken& currToken = replacementList[currTokenIndex];
1595+
15861596
if ((currToken.mType != E_TOKEN_TYPE::IDENTIFIER) || (currToken.mRawView != currArgName))
15871597
{
15881598
continue;
15891599
}
1600+
1601+
// \note Check whether argument is used as an operand of token-pasting or stringification operators
1602+
auto nextNonSpaceToken = std::find_if(replacementList.cbegin() + currTokenIndex + 1, replacementList.cend(), [](const TToken& token) { return token.mType != E_TOKEN_TYPE::SPACE; });
1603+
auto prevNonSpaceToken = std::find_if(replacementList.rbegin() + (replacementList.size() - currTokenIndex), replacementList.rend(), [](const TToken& token) { return token.mType != E_TOKEN_TYPE::SPACE; });
1604+
1605+
const bool isTokenPastingOperand =
1606+
(nextNonSpaceToken != replacementList.cend() && nextNonSpaceToken->mType == E_TOKEN_TYPE::CONCAT_OP) ||
1607+
(prevNonSpaceToken != replacementList.rend() && prevNonSpaceToken->mType == E_TOKEN_TYPE::CONCAT_OP);
1608+
const bool isStringifyOperand = currTokenIndex > 0 && replacementList[currTokenIndex - 1].mType == E_TOKEN_TYPE::STRINGIZE_OP;
1609+
1610+
if (isTokenPastingOperand || isStringifyOperand)
1611+
{
1612+
currToken.mRawView = replacementValue;
1613+
continue;
1614+
}
15901615

1591-
currToken.mRawView = replacementValue;
1616+
std::vector<TToken>& currExpandedArgTokens = cachedArgsSequences[EMPTY_STR_VALUE];
1617+
1618+
auto prevComputationIt = cachedArgsSequences.find(replacementValue);
1619+
if (prevComputationIt == cachedArgsSequences.cend())
1620+
{
1621+
auto it = processingTokens[currArgIndex].begin();
1622+
currExpandedArgTokens = _expandArg([&processingTokens, &it, currArgIndex] { if (it == processingTokens[currArgIndex].end()) return TToken{ E_TOKEN_TYPE::END }; return *it++; });
1623+
}
1624+
else
1625+
{
1626+
currExpandedArgTokens = prevComputationIt->second;
1627+
}
1628+
1629+
currToken = *currExpandedArgTokens.cbegin();
1630+
1631+
if (currExpandedArgTokens.size() > 1)
1632+
{
1633+
replacementList.insert(replacementList.cbegin() + currTokenIndex + 1, currExpandedArgTokens.cbegin() + 1, currExpandedArgTokens.cend());
1634+
currTokenIndex += currExpandedArgTokens.size() - 1;
1635+
}
15921636
}
15931637

15941638
if (variadics)
@@ -1601,9 +1645,86 @@ namespace tcpp
16011645
return replacementList;
16021646
}
16031647

1604-
/*std::vector<TToken> Preprocessor::_expandMacroArg(const TMacroDesc& macroDesc, const TToken& idToken, const std::function<TToken()>& getNextTokenCallback) const TCPP_NOEXCEPT
1648+
std::vector<TToken> Preprocessor::_expandArg(const std::function<TToken()>& getNextTokenCallback) const TCPP_NOEXCEPT
16051649
{
1606-
}*/
1650+
std::vector<TToken> outputSequence{};
1651+
1652+
TToken currToken;
1653+
1654+
while ((currToken = getNextTokenCallback()).mType != E_TOKEN_TYPE::END)
1655+
{
1656+
switch (currToken.mType)
1657+
{
1658+
case E_TOKEN_TYPE::IDENTIFIER: // \note try to expand some macro here
1659+
{
1660+
auto iter = std::find_if(mSymTable.cbegin(), mSymTable.cend(), [&currToken](auto&& item)
1661+
{
1662+
return item.mName == currToken.mRawView;
1663+
});
1664+
1665+
auto contextIter = std::find_if(mContextStack.cbegin(), mContextStack.cend(), [&currToken](auto&& item)
1666+
{
1667+
return item == currToken.mRawView;
1668+
});
1669+
1670+
if (iter != mSymTable.cend() && contextIter == mContextStack.cend())
1671+
{
1672+
auto expandedMacroTokens = _expandMacroDefinition(*iter, currToken, getNextTokenCallback);
1673+
outputSequence.insert(outputSequence.cbegin(), expandedMacroTokens.cbegin(), expandedMacroTokens.cend());
1674+
}
1675+
else
1676+
{
1677+
outputSequence.emplace_back(currToken);
1678+
}
1679+
}
1680+
break;
1681+
case E_TOKEN_TYPE::REJECT_MACRO:
1682+
mContextStack.erase(std::remove_if(mContextStack.begin(), mContextStack.end(), [&currToken](auto&& item)
1683+
{
1684+
return item == currToken.mRawView;
1685+
}), mContextStack.end());
1686+
break;
1687+
case E_TOKEN_TYPE::CONCAT_OP:
1688+
#if 0
1689+
while (processedStr.back() == ' ') // \note Remove last character in the processed source if it was a whitespace
1690+
{
1691+
processedStr.erase(processedStr.length() - 1);
1692+
}
1693+
1694+
currToken = TrySkipWhitespaceTokensSequence(getNextTokenCallback(), getNextTokenCallback());
1695+
appendString(currToken.mRawView);
1696+
#endif
1697+
break;
1698+
case E_TOKEN_TYPE::STRINGIZE_OP:
1699+
{
1700+
if (mContextStack.empty())
1701+
{
1702+
mOnErrorCallback({ E_ERROR_TYPE::INCORRECT_OPERATION_USAGE, mpLexer->GetCurrLineIndex() });
1703+
continue;
1704+
}
1705+
1706+
TToken stringLiteralToken = currToken;
1707+
stringLiteralToken.mType = E_TOKEN_TYPE::BLOB;
1708+
stringLiteralToken.mRawView = "\"" + (currToken = mpLexer->GetNextToken()).mRawView + "\"";
1709+
1710+
outputSequence.emplace_back(stringLiteralToken);
1711+
}
1712+
break;
1713+
default:
1714+
if (E_TOKEN_TYPE::COMMENTARY == currToken.mType && mSkipCommentsTokens)
1715+
{
1716+
break;
1717+
}
1718+
1719+
outputSequence.emplace_back(currToken);
1720+
break;
1721+
}
1722+
}
1723+
1724+
TCPP_ASSERT(!outputSequence.empty());
1725+
1726+
return outputSequence;
1727+
}
16071728

16081729
void Preprocessor::_expect(const E_TOKEN_TYPE& expectedType, const E_TOKEN_TYPE& actualType) const TCPP_NOEXCEPT
16091730
{

tests/coreTests.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,4 +1031,34 @@ TEST()
10311031
std::string output = preprocessor.Process();
10321032
REQUIRE((result && output == "int array[4];\n"));
10331033
}
1034-
}
1034+
1035+
SECTION("TestProcess_ArgumentsAreExpandedFirstInFunctionLikeMacroes_Returns4aThen__LINE__a")
1036+
{
1037+
std::string inputSource = R"(
1038+
#define foo(a, b) a ## b
1039+
#define bar(a, b) foo(a,b)
1040+
#define baz bar(__LINE__, a)
1041+
baz
1042+
1043+
#define moo(a, b) a ## b
1044+
#define meow moo(__LINE__, a)
1045+
meow)";
1046+
1047+
Lexer lexer(std::make_unique<StringInputStream>(inputSource));
1048+
1049+
bool result = true;
1050+
1051+
Preprocessor preprocessor(lexer, { [&result](auto&& arg)
1052+
{
1053+
std::cerr << "Error: " << ErrorTypeToString(arg.mType) << std::endl;
1054+
result = false;
1055+
}, [](auto&&, auto&&)
1056+
{
1057+
return nullptr;
1058+
},
1059+
true });
1060+
1061+
std::string output = preprocessor.Process();
1062+
REQUIRE((result && output == "\n4a\n\n__LINE__a"));
1063+
}
1064+
}

0 commit comments

Comments
 (0)