diff --git a/flang/include/flang/Parser/preprocessor.h b/flang/include/flang/Parser/preprocessor.h index 86528a7e68def..15810a34ee6a5 100644 --- a/flang/include/flang/Parser/preprocessor.h +++ b/flang/include/flang/Parser/preprocessor.h @@ -116,6 +116,7 @@ class Preprocessor { bool IsIfPredicateTrue(const TokenSequence &expr, std::size_t first, std::size_t exprTokens, Prescanner &); void LineDirective(const TokenSequence &, std::size_t, Prescanner &); + TokenSequence TokenizeMacroBody(const std::string &); AllSources &allSources_; std::list names_; diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp index 6e8e3aee19b09..a5de14d864762 100644 --- a/flang/lib/Parser/preprocessor.cpp +++ b/flang/lib/Parser/preprocessor.cpp @@ -301,8 +301,82 @@ void Preprocessor::DefineStandardMacros() { Define("__TIMESTAMP__"s, "__TIMESTAMP__"s); } +static const std::string idChars{ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"s}; + +static std::optional> TokenizeMacroNameAndArgs( + const std::string &str) { + // TODO: variadic macros on the command line (?) + std::vector names; + for (std::string::size_type at{0};;) { + auto nameStart{str.find_first_not_of(" "s, at)}; + if (nameStart == str.npos) { + return std::nullopt; + } + auto nameEnd{str.find_first_not_of(idChars, nameStart)}; + if (nameEnd == str.npos) { + return std::nullopt; + } + auto punc{str.find_first_not_of(" "s, nameEnd)}; + if (punc == str.npos) { + return std::nullopt; + } + if ((at == 0 && str[punc] != '(') || + (at > 0 && str[punc] != ',' && str[punc] != ')')) { + return std::nullopt; + } + names.push_back(str.substr(nameStart, nameEnd - nameStart)); + at = punc + 1; + if (str[punc] == ')') { + if (str.find_first_not_of(" "s, at) != str.npos) { + return std::nullopt; + } else { + return names; + } + } + } +} + +TokenSequence Preprocessor::TokenizeMacroBody(const std::string &str) { + TokenSequence tokens; + Provenance provenance{allSources_.AddCompilerInsertion(str).start()}; + auto end{str.size()}; + for (std::string::size_type at{0}; at < end;) { + // Alternate between tokens that are identifiers (and therefore subject + // to argument replacement) and those that are not. + auto start{str.find_first_of(idChars, at)}; + if (start == str.npos) { + tokens.Put(str.substr(at), provenance + at); + break; + } else if (start > at) { + tokens.Put(str.substr(at, start - at), provenance + at); + } + at = str.find_first_not_of(idChars, start + 1); + if (at == str.npos) { + tokens.Put(str.substr(start), provenance + start); + break; + } else { + tokens.Put(str.substr(start, at - start), provenance + start); + } + } + return tokens; +} + void Preprocessor::Define(const std::string ¯o, const std::string &value) { - definitions_.emplace(SaveTokenAsName(macro), Definition{value, allSources_}); + if (auto lhs{TokenizeMacroNameAndArgs(macro)}) { + // function-like macro + CharBlock macroName{SaveTokenAsName(lhs->front())}; + auto iter{lhs->begin()}; + ++iter; + std::vector argNames{iter, lhs->end()}; + auto rhs{TokenizeMacroBody(value)}; + definitions_.emplace(std::make_pair(macroName, + Definition{ + argNames, rhs, 0, rhs.SizeInTokens(), /*isVariadic=*/false})); + } else { // keyword macro + definitions_.emplace( + SaveTokenAsName(macro), Definition{value, allSources_}); + } } void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); } diff --git a/flang/test/Preprocessing/func-on-command-line.F90 b/flang/test/Preprocessing/func-on-command-line.F90 new file mode 100644 index 0000000000000..cf844e021b371 --- /dev/null +++ b/flang/test/Preprocessing/func-on-command-line.F90 @@ -0,0 +1,4 @@ +! RUN: %flang_fc1 -fdebug-unparse "-Dfoo(a,b)=bar(a+b)" %s | FileCheck %s +! CHECK: CALL bar(3_4) +call foo(1,2) +end