Skip to content

Commit

Permalink
(Rosebud) Code generators are chosen using factories; added new
Browse files Browse the repository at this point in the history
* Added code generators that generate no code. The main backend
  NoneGenerator can be used when all you want is to check that the
  input is valid.  The NoneSerializer is used for nodes that don't
  yet support serialization because they have base classes that
  aren't Rosebud-generated and thus don't implement serialization.

* The main backend code generators ("rosetta", "rose", "yaml", and now
  "none") are selected from the command-line by name with no hard-coded
  values.

* The backend serialization generators ("boost" and now "none") are
  also selected by name with no hard-coded values.

* This is similar to how some of the binary analysis class hierarchies
  work, except here we use "lookup" instead of "forge" since we're not
  forging any new objects.

* These changes make it possible to later add user-defined code
  generators without needing to modify anything in Rosebud other than
  a call to register the generator.

* The "rosetta" backend checks whether a class is serializable.

RPM-357
  • Loading branch information
matzke1 committed Apr 21, 2023
1 parent e2b28fb commit cd09d41
Show file tree
Hide file tree
Showing 22 changed files with 478 additions and 50 deletions.
2 changes: 2 additions & 0 deletions src/Rosebud/BasicTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ enum class When {
};

class Generator;
using GeneratorPtr = std::shared_ptr<Generator>; /**< Shared-ownership pointer to a @ref Generator. */
class Serializer;
using SerializerPtr = std::shared_ptr<Serializer>; /**< Shared-ownership pointer to a @ref Serializer. */

// AST node types
namespace Ast {
Expand Down
20 changes: 20 additions & 0 deletions src/Rosebud/BoostSerializer.C
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,26 @@

namespace Rosebud {

BoostSerializer::Ptr
BoostSerializer::instance() {
return Ptr(new BoostSerializer);
}

std::string
BoostSerializer::name() const {
return "boost";
}

std::string
BoostSerializer::purpose() const {
return "Generates boost::serialization code.";
}

bool
BoostSerializer::isSerializable(const Ast::Class::Ptr&) const {
return true;
}

void
BoostSerializer::generate(std::ostream &header, std::ostream &impl, const Ast::Class::Ptr &c,
const Generator &generator) const {
Expand Down
10 changes: 10 additions & 0 deletions src/Rosebud/BoostSerializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ namespace Rosebud {
* This serializer generates code for boost::serialization. */
class BoostSerializer: public Serializer {
public:
using Ptr = std::shared_ptr<BoostSerializer>;

protected:
BoostSerializer() {}

public:
static Ptr instance();
virtual std::string name() const override;
virtual std::string purpose() const override;
virtual bool isSerializable(const Ast::ClassPtr&) const override;
virtual void generate(std::ostream &header, std::ostream &impl, const Ast::ClassPtr&, const Generator&) const override;
};

Expand Down
3 changes: 3 additions & 0 deletions src/Rosebud/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ add_executable(rosebud
BoostSerializer.C
CxxGenerator.C
Generator.C
NoneGenerator.C
NoneSerializer.C
RoseGenerator.C
RosettaGenerator.C
Serializer.C
Utility.C
YamlGenerator.C)

Expand Down
51 changes: 51 additions & 0 deletions src/Rosebud/Generator.C
Original file line number Diff line number Diff line change
@@ -1,7 +1,58 @@
#include <Rosebud/Generator.h>

#include <Rosebud/NoneGenerator.h>
#include <Rosebud/RoseGenerator.h>
#include <Rosebud/RosettaGenerator.h>
#include <Rosebud/YamlGenerator.h>

#include <boost/range/adaptor/reversed.hpp>

namespace Rosebud {

std::vector<Generator::Ptr>
Generator::registry_;

void
Generator::initRegistry() {
static bool initialized = false;
if (!initialized) {
registry_.push_back(YamlGenerator::instance());
registry_.push_back(RosettaGenerator::instance());
registry_.push_back(RoseGenerator::instance());
registry_.push_back(NoneGenerator::instance());
initialized = true;
}
}

void
Generator::registerGenerator(const Ptr &generator) {
ASSERT_not_null(generator);
initRegistry();
registry_.push_back(generator);
}

const std::vector<Generator::Ptr>&
Generator::registeredGenerators() {
initRegistry();
return registry_;
}

Generator::Ptr
Generator::lookup(const std::string &name) {
initRegistry();
for (const Ptr &generator: boost::adaptors::reverse(registry_)) {
if (generator->name() == name)
return generator;
}
return {};
}

void
Generator::addAllToParser(Sawyer::CommandLine::Parser &parser) {
for (const Ptr &generator: registry_)
generator->adjustParser(parser);
}

std::string
Generator::propertyDataMemberName(const Ast::Property::Ptr &p) const {
ASSERT_not_null(p);
Expand Down
48 changes: 48 additions & 0 deletions src/Rosebud/Generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,61 @@ namespace Rosebud {

/** Base class for backend code generators. */
class Generator {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Nested types
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public:
using Ptr = GeneratorPtr;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Data members
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private:
static std::vector<Ptr> registry_;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction related stuff
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public:
virtual ~Generator() {}

protected:
Generator() {}

public:
/** Register a backend for use later. */
static void registerGenerator(const Ptr&);

/** Return all registered backends. */
static const std::vector<Ptr>& registeredGenerators();

/** Return the registered generator with the specified name. */
static Ptr lookup(const std::string&);

/** Add command-line switches and documentation to a parser.
*
* Any command-line switches and documentation that is specific to the backend are added to the specified parser. In order to
* not conflict with other backends that might also be adding switch parsers, the backend should create a switch group and give
* it a unique prefix. */
virtual void adjustParser(Sawyer::CommandLine::Parser&) {}

/** Add all known generator switches to parser. */
static void addAllToParser(Sawyer::CommandLine::Parser&);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Properties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public:
/** Every generator should have a unique name. */
virtual std::string name() const = 0;

/** Single-line description for documentation. */
virtual std::string purpose() const = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Code generation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public:
/** Generate code.
*
* The specified project AST is used to generate code. */
Expand All @@ -40,6 +85,9 @@ class Generator {
* Returns a list of zero or more C++ member function names to use as mutator names. If no names are returned, then no mutator
* functions are declared or defined. */
virtual std::vector<std::string> propertyMutatorNames(const Ast::PropertyPtr&) const;

private:
static void initRegistry();
};

} // namespace
Expand Down
5 changes: 5 additions & 0 deletions src/Rosebud/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ rosebud_SOURCES = \
BoostSerializer.C \
CxxGenerator.C \
Generator.C \
NoneGenerator.C \
NoneSerializer.C \
RoseGenerator.C \
RosettaGenerator.C \
Serializer.C \
Utility.C \
YamlGenerator.C
rosebud_LDADD = $(top_builddir)/src/util/Sawyer/libroseSawyer.la $(ROSE_BOOST_LIBS) $(RT_LIBS)
Expand All @@ -28,6 +31,8 @@ headers = \
BoostSerializer.h \
CxxGenerator.h \
Generator.h \
NoneGenerator.h \
NoneSerializer.h \
RoseGenerator.h \
RosettaGenerator.h \
Serializer.h \
Expand Down
23 changes: 23 additions & 0 deletions src/Rosebud/NoneGenerator.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <Rosebud/NoneGenerator.h>

namespace Rosebud {

NoneGenerator::Ptr
NoneGenerator::instance() {
return Ptr(new NoneGenerator);
}

std::string
NoneGenerator::name() const {
return "none";
}

std::string
NoneGenerator::purpose() const {
return "Do not generate code, but only check the input.";
}

void
NoneGenerator::generate(const Ast::Project::Ptr&) {}

} // namespace
25 changes: 25 additions & 0 deletions src/Rosebud/NoneGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef Rosebud_NoneGenerator_H
#define Rosebud_NoneGenerator_H
#include <Rosebud/Generator.h>

#include <Sawyer/CommandLine.h>

namespace Rosebud {

/** Generator that produces a YAML description of the input. */
class NoneGenerator: public Generator {
public:
using Ptr = std::shared_ptr<NoneGenerator>;

protected:
NoneGenerator() {}

public:
static Ptr instance();
virtual std::string name() const override;
virtual std::string purpose() const override;
virtual void generate(const Ast::ProjectPtr&) override;
};

} // namespace
#endif
33 changes: 33 additions & 0 deletions src/Rosebud/NoneSerializer.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <Rosebud/NoneSerializer.h>

#include <Rosebud/Generator.h>

#include <memory>
#include <string>

namespace Rosebud {

NoneSerializer::Ptr
NoneSerializer::instance() {
return Ptr(new NoneSerializer);
}

std::string
NoneSerializer::name() const {
return "none";
}

std::string
NoneSerializer::purpose() const {
return "Generates no serialization code.";
}

bool
NoneSerializer::isSerializable(const Ast::ClassPtr&) const {
return false;
}

void
NoneSerializer::generate(std::ostream&, std::ostream&, const Ast::Class::Ptr&, const Generator&) const {}

} // namespace
26 changes: 26 additions & 0 deletions src/Rosebud/NoneSerializer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef Rosebud_NoneSerializer_H
#define Rosebud_NoneSerializer_H
#include <Rosebud/Serializer.h>

namespace Rosebud {

/** Class serializer using None Serialization.
*
* This serializer generates code for boost::serialization. */
class NoneSerializer: public Serializer {
public:
using Ptr = std::shared_ptr<NoneSerializer>;

protected:
NoneSerializer() {}

public:
static Ptr instance();
virtual std::string name() const override;
virtual std::string purpose() const override;
virtual bool isSerializable(const Ast::ClassPtr&) const override;
virtual void generate(std::ostream &header, std::ostream &impl, const Ast::ClassPtr&, const Generator&) const override;
};

} // namespace
#endif
19 changes: 17 additions & 2 deletions src/Rosebud/RoseGenerator.C
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,27 @@ using namespace Sawyer::Message::Common;

namespace Rosebud {

RoseGenerator::Ptr
RoseGenerator::instance() {
return Ptr(new RoseGenerator);
}

std::string
RoseGenerator::name() const {
return "rose";
}

std::string
RoseGenerator::purpose() const {
return "Experimental backend to generate ROSE code directly without using ROSETTA.";
}

void
RoseGenerator::adjustParser(Sawyer::CommandLine::Parser &parser) {
using namespace Sawyer::CommandLine;

SwitchGroup sg("ROSE backend (--backend=rose)");
sg.name("rose");
SwitchGroup sg("ROSE backend (--backend=" + name() + ")");
sg.name(name());
sg.doc("This backend generates code directly without sending it through ROSETTA. It's goal is to generate a more modern style "
"of tree data structures where node memory is managed and parent pointers are automatic.\n\n"

Expand Down
10 changes: 10 additions & 0 deletions src/Rosebud/RoseGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@ namespace Rosebud {
class RoseGenerator: public CxxGenerator {
using Base = CxxGenerator;

public:
using Ptr = std::shared_ptr<RoseGenerator>;

private:
boost::filesystem::path generatedDir; // directory where generated files are written

protected:
RoseGenerator() {}

public:
static Ptr instance();
virtual std::string name() const override;
virtual std::string purpose() const override;
virtual void adjustParser(Sawyer::CommandLine::Parser&) override;
virtual void generate(const Ast::ProjectPtr&) override;

Expand Down
Loading

0 comments on commit cd09d41

Please sign in to comment.