Skip to content

Commit

Permalink
(Rosebud) Support for #pragma rosebud ... last resort
Browse files Browse the repository at this point in the history
* If you can't get Rosebud to generate some necessary ROSETTA input,
  you can place a #pragma inside the class definition in the header
  file that's input to Rosebud. This is intended only as a last resort
  while we migrate to Rosebud.

  For instance, this input:

    |class SgFoo: public SgNode {
    |    #pragma rosetta setFunctionSourceCode("SOURCE_FOO", \
    |        "../Grammar/Expression.code")

  Will generate the line:

    |     Foo.setFunctionSourceCode("SOURCE_FOO",
    |        "../Grammar/Expression.code")

RPM-357
  • Loading branch information
matzke1 committed Apr 21, 2023
1 parent d7a7482 commit ddded0e
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/Rosebud/RosettaGenerator.C
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <Sawyer/StaticBuffer.h>

#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
Expand Down Expand Up @@ -504,6 +505,36 @@ RosettaGenerator::genLeafMacros(std::ostream &rosetta, const Ast::Class::Ptr &c)
<<"IS_SERIALIZABLE(" <<shortName(c) <<");\n";
}

void
RosettaGenerator::genRosettaPragmas(std::ostream &rosetta, const std::vector<std::string> &pragmas, const Ast::Class::Ptr &c) {
for (const std::string &pragma: pragmas) {
std::string s = boost::replace_all_copy(pragma, "\\\n", "\n");
boost::trim(s);
if (!boost::ends_with(s, ";"))
s += ";";
rosetta <<"\n"
<<THIS_LOCATION <<"#ifndef DOCUMENTATION\n"
<<" " <<shortName(c) <<"." <<s <<"\n"
<<"#endif // !DOCUMENTATION\n";
}
}

size_t
RosettaGenerator::genRosettaPragmas(std::ostream &rosetta, const Ast::Class::Ptr &c, const Ast::Property::Ptr &p) {
static const std::regex re("^[ \\t]*#[ \\t]*pragma[ \\t]+rosetta[ \\t]+([\\S\\s]*)");
std::vector<std::string> pragmas = extractCpp(p->priorText /*in,out*/, re, 1);
genRosettaPragmas(rosetta, pragmas, c);
return pragmas.size();
}

size_t
RosettaGenerator::genRosettaPragmas(std::ostream &rosetta, const Ast::Class::Ptr &c) {
static const std::regex re("^[ \\t]*#[ \\t]*pragma[ \\t]+rosetta[ \\t]+([\\S\\s]*)");
std::vector<std::string> pragmas = extractCpp(c->endText /*in,out*/, re, 1);
genRosettaPragmas(rosetta, pragmas, c);
return pragmas.size();
}

void
RosettaGenerator::genClassDefinition(std::ostream &rosetta, const Ast::Class::Ptr &c, const Hierarchy &h) {
ASSERT_not_null(c);
Expand Down Expand Up @@ -550,7 +581,10 @@ RosettaGenerator::genClassDefinition(std::ostream &rosetta, const Ast::Class::Pt

genClassBegin(rosetta, c);

size_t nPragmas = 0;
for (const auto &p: c->properties) {
nPragmas += genRosettaPragmas(rosetta, c, p());

// Stuff in the input that's prior to the property definition should go in the "other" section of output
header <<locationDirective(p(), p->priorTextToken) <<p->priorText;

Expand All @@ -560,6 +594,7 @@ RosettaGenerator::genClassDefinition(std::ostream &rosetta, const Ast::Class::Pt

// If anything else is in the input class definition after the last property definition, append it to the "other" section of
// output
nPragmas += genRosettaPragmas(rosetta, c);
header <<locationDirective(c, c->endTextToken) <<c->endText;

// Class constructors and destructures emitted quite late in the class definition so we're sure that all the types needed by
Expand All @@ -574,6 +609,16 @@ RosettaGenerator::genClassDefinition(std::ostream &rosetta, const Ast::Class::Pt

c->cppStack->emitClose(rosetta);
genImplFileEnd(impl, c);

// Delayed warnings
if (nPragmas > 0) {
message(WARN, c->findAncestor<Ast::File>(), c->nameToken,
"class " + c->name + " contains " + boost::lexical_cast<std::string>(nPragmas) + " pragma " +
std::string(1 == nPragmas ? "directive" : "directives") + " which should only be used only as a last resort to "
"achieve ROSETTA compatibility. Most of the things done with these pragmas can be done more effectively and "
"with less generated code by using C++ features like dynamic dispatch, template metaprogramming and "
"introspection.");
}
}

std::string
Expand Down
3 changes: 3 additions & 0 deletions src/Rosebud/RosettaGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class RosettaGenerator: public CxxGenerator {
void genRosettaFunctionEnd(std::ostream &rosetta);
void genImplFileBegin(std::ostream &impl, const Ast::ClassPtr&);
void genImplFileEnd(std::ostream &impl, const Ast::ClassPtr&);
void genRosettaPragmas(std::ostream &rosetta, const std::vector<std::string>& pragmas, const Ast::ClassPtr&);
size_t genRosettaPragmas(std::ostream &rosetta, const Ast::ClassPtr&, const Ast::PropertyPtr&);
size_t genRosettaPragmas(std::ostream &rosetta, const Ast::ClassPtr&);
void genClassDefinition(std::ostream&, const Ast::ClassPtr&, const Hierarchy&);
void genNonterminalMacros(std::ostream &rosetta, const Ast::ClassPtr&, const Hierarchy&);
void genNewNonterminalMacro(std::ostream &rosetta, const Ast::ClassPtr&, const Hierarchy&);
Expand Down
33 changes: 33 additions & 0 deletions src/Rosebud/Utility.C
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <Rosebud/Utility.h>

#include <Sawyer/GraphTraversal.h>
#include <Sawyer/Clexer.h>
#include <Sawyer/StaticBuffer.h>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/join.hpp>
Expand Down Expand Up @@ -663,6 +665,37 @@ toCppSymbol(const std::string &s) {
return std::regex_replace(s, std::regex("::|[^_a-zA-Z0-9]"), "_");
}

std::vector<std::string>
extractCpp(std::string &s, const std::regex &re, size_t capture) {
std::vector<std::string> retval;

if (!s.empty()) {
auto buffer = Sawyer::Container::StaticBuffer<size_t, char>::instance(s.c_str(), s.size());
Sawyer::Language::Clexer::TokenStream tokens("-", buffer);
tokens.skipPreprocessorTokens(false);

std::string filtered;
while (true) {
auto token = tokens[0];
const std::string prior = tokens.content().contentAsString(token.prior(), token.begin());
if (token) {
const std::string lexeme = tokens.lexeme(token);
std::smatch found;
if (token.type() == Sawyer::Language::Clexer::TOK_CPP && std::regex_match(lexeme, found, re)) {
filtered += prior;
retval.push_back(found.str(capture));
} else {
filtered += prior + lexeme;
}
tokens.consume();
} else {
filtered += prior; // non-token stuff before EOF
break;
}
}
s = filtered;
}
return retval;
}

} // namespace
7 changes: 7 additions & 0 deletions src/Rosebud/Utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <boost/filesystem.hpp>

#include <regex>
#include <string>
#include <vector>

Expand Down Expand Up @@ -284,5 +285,11 @@ std::string locationDirective(const Ast::NodePtr&, const Token&);
* This is done by replacing all the "::" with "_". */
std::string toCppSymbol(const std::string&);

/** Extract all matching C preprocessor directives from the text.
*
* Modifies the string in place and returns one preprocessor directive per vector element. If capture is non-zero, then it
* refers to a parenthetical capture group in the regular expression, and just that group is saved in the return vector. */
std::vector<std::string> extractCpp(std::string&, const std::regex&, size_t capture);

} // namespace
#endif

0 comments on commit ddded0e

Please sign in to comment.