The source files

We will here follow an example of adding the hypothetical package Squirrel to Porcupine, as they are good old friends. Squirrel is a Python package for conducting some nutty analysis. The first thing we’ll want to do is to create a source-header pair of C++ files in the CodeEditor section. The SquirrelGenerator.hpp goes into the header directory, the SquirrelGenerator.cpp will be placed in the source directory. We will here assume some familiarity with the standards of a C++ class definition. In the header file, it is advised that you just copy an existing generator and change the names from (e.g.) DockerGenerator to SquirrelGenerator.

There is a standard (empty) constructor and destructor method that we will not touch, and there is a variable that states to what language we want to respond, static const QString s_thisLanguage;. Lastly, the important part is that every Generator has a generateCode function:

QString generateCode(
        const QList<NodeTreeItem*>& _nodeList,
        const QVector<const Link*>& _linkList = QVector<const Link*>(0)
        );

The output of this function will be all the code that you generate (in a QString), the input will be the set of all Nodes and Links in the workflow editor. Let’s move to the cpp source file and implement the respective parts.

The source file

We will not discuss the C++ specific includes and immediately go to the Porcupine specific part. A good start would be to define the language that we are programming for:

const QString SquirrelGenerator::s_thisLanguage("Squirrel");

We are going to use this variable to select only the Squirrel nodes from the workflow. The standard constructor and destructor need to be present in the cpp-file, but can be left empty. The only function we need to implement is the generateCode function. If you want, you can make as many helper functions as you want, but we here limit the scope to a single function.

In all likelihood, you want to loop over all nodes in the editor, and add code for each of them:

QString SquirrelGenerator::generateCode(
        const QList<NodeTreeItem*>& _nodeList,
        const QVector<const Link*>& _linkList
        )
{
  QString code;
  foreach(const NodeTreeItem* item, _nodeList)
  {
      code += ...
  }
  return code;
}

What it is that you’d like to add is entirely up to you! But it is important that you can access the variables in the node. Most of the variables are just represented as JSON files. So we can for example take the title of the node in this way:

foreach(const NodeTreeItem* item, _nodeList)
{
  QJsonObject json = _item->getJson();
  Argument title(json["title"].toObject());
  ...
}

The Argument is an internal structure that lets you easily access all variables, so now we can access title.m_name that will yield a QString that states the title of the node (e.g. squirrel.nut_cracker.py).

Almost identically, one can access all input and output ports:

foreach (const PortPair* pair,  _item->getPorts())
{
  Argument argument = pair->getArgument();
  code += argument.m_isInput      // e.g. true
  code += argument.m_isOutput     // e.g. false
  code += argument.m_argumentName // e.g. chestnut
  code += argument.m_isIterator   // e.g. true
}

From here on, it is entirely up to you what type of code you want to write! We do need to cover how to install our SquirrelGenerator to Porcupine. This is explained in the next chapter.