Example #1
0
/***********************************************************************
 * generate a cpp source that adds the json string to the registry
 **********************************************************************/
static void jsonArrayToCppStaticBlock(const Poco::JSON::Array &arrayOut, std::ostream &os, const std::string &target)
{
    os << "#include <Pothos/Framework.hpp>\n";
    os << "#include <Pothos/Plugin.hpp>\n";
    os << Poco::format("pothos_static_block(registerPothosBlockDocs%s)\n", target);
    os << "{\n";
    for (size_t i = 0; i < arrayOut.size(); i++)
    {
        auto obj = arrayOut.getObject(i);
        assert(obj);

        //create escaped string of json
        std::stringstream ossJsonObj;
        obj->stringify(ossJsonObj);
        std::string escaped;
        for (const auto &ch : ossJsonObj.str())
        {
            escaped += "\\x" + Poco::NumberFormatter::formatHex(int(ch), 2/*width*/, false/*no 0x*/);
        }

        //get a list of all paths including aliases
        std::vector<std::string> paths;
        paths.push_back(obj->getValue<std::string>("path"));
        if (obj->has("aliases")) for (const auto &alias : *obj->getArray("aliases"))
        {
            paths.push_back(alias.toString());
        }

        //register the block description for every path
        for (const auto &path : paths)
        {
            const auto pluginPath = Pothos::PluginPath("/blocks/docs").join(path.substr(1));
            os << Poco::format("    Pothos::PluginRegistry::add(\"%s\", std::string(\"%s\"));\n", pluginPath.toString(), escaped);
        }
    }
    os << "}\n";
}
Example #2
0
/***********************************************************************
 * Parse a single documentation block for markup
 **********************************************************************/
static Poco::JSON::Object::Ptr parseCommentBlockForMarkup(const CodeBlock &commentBlock)
{
    Poco::JSON::Object::Ptr topObj(new Poco::JSON::Object());
    Poco::JSON::Array calls;
    Poco::JSON::Array keywords;
    Poco::JSON::Array::Ptr aliases(new Poco::JSON::Array());
    Poco::JSON::Array categories;
    Poco::JSON::Array params;
    Poco::JSON::Array::Ptr topDocs(new Poco::JSON::Array());
    Poco::JSON::Object::Ptr currentParam;

    std::string state;
    std::string indent;

    std::string instruction;
    std::string payload;

    //search for the markup begin tag and record the indent
    for (const auto &codeLine : commentBlock)
    {
        std::string line = codeLine.text;
        Poco::RegularExpression::MatchVec matches;

        if (not state.empty())
        {
            if (line.size() >= indent.size() and line.substr(0, indent.size()) != indent)
            {
                if (codeLine.lineNo == commentBlock.back().lineNo) line = "";
                else throw Pothos::SyntaxException("Inconsistent indentation", codeLine.toString());
            }

            if (line.size() >= indent.size()) line = line.substr(indent.size());
            else line = "";

            Poco::RegularExpression("^\\|(\\w+)\\s+(.*)$").match(line, 0, matches);
            if (not matches.empty())
            {
                assert(matches.size() == 3);
                instruction = line.substr(matches[1].offset, matches[1].length);
                payload = line.substr(matches[2].offset, matches[2].length);
            }
        }

        if (state.empty())
        {
            Poco::RegularExpression("^(.*)\\|PothosDoc\\s+(.*)$").match(line, 0, matches);
            if (matches.empty()) continue;
            assert(matches.size() == 3);
            indent = line.substr(matches[1].offset, matches[1].length);
            topObj->set("name", Poco::trim(line.substr(matches[2].offset, matches[2].length)));
            state = "DOC";
        }
        else if (matches.empty() and state == "DOC")
        {
            topDocs->add(line);
        }
        else if (matches.empty() and state == "PARAM")
        {
            auto array = currentParam->getArray("desc");
            array->add(line);
            currentParam->set("desc", stripDocArray(array));
        }
        else if (instruction == "category" and state == "DOC")
        {
            categories.add(Poco::trim(payload));
        }
        else if (instruction == "keywords" and state == "DOC")
        {
            for (const auto &keyword : Poco::StringTokenizer(
                payload, " \t", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY))
            {
                keywords.add(Poco::trim(keyword));
            }
        }
        else if (instruction == "alias" and state == "DOC")
        {
            const std::string alias(Poco::trim(payload));
            try {Pothos::PluginPath(alias);}
            catch (const Pothos::PluginPathError &)
            {
                throw Pothos::SyntaxException("Invalid alias path", codeLine.toString());
            }
            aliases->add(alias);
        }
        else if (instruction == "param" and (state == "DOC" or state == "PARAM"))
        {
            payload = bracketEscapeEncode(payload);
            Poco::RegularExpression::MatchVec fields;
            Poco::RegularExpression("^\\s*(\\w+)(\\s*\\[(.*)\\]\\s*)?(.*)$").match(payload, 0, fields);
            if (fields.empty()) throw Pothos::SyntaxException(
                "Expected |param key[name] description",
                codeLine.toString());

            assert(fields.size() == 5);
            const std::string key = bracketEscapeDecode(Poco::trim(payload.substr(fields[1].offset, fields[1].length)));
            std::string name = titleCase(key);
            if (fields[3].length != 0) name = bracketEscapeDecode(Poco::trim(payload.substr(fields[3].offset, fields[3].length)));
            const std::string desc = bracketEscapeDecode(Poco::trim(payload.substr(fields[4].offset, fields[4].length)));

            currentParam = Poco::JSON::Object::Ptr(new Poco::JSON::Object());
            params.add(currentParam);
            currentParam->set("key", key);
            currentParam->set("name", name);
            Poco::JSON::Array::Ptr descArr(new Poco::JSON::Array());
            descArr->add(desc);
            currentParam->set("desc", descArr);
            state = "PARAM";
        }
        else if (instruction == "default" and state == "PARAM")
        {
            if (currentParam->has("default")) throw Pothos::SyntaxException(
                "Multiple occurrence of |default for param",
                codeLine.toString());
            currentParam->set("default", payload);
        }
        else if (instruction == "units" and state == "PARAM")
        {
            if (currentParam->has("units")) throw Pothos::SyntaxException(
                "Multiple occurrence of |units for param",
                codeLine.toString());
            currentParam->set("units", payload);
        }
        else if (instruction == "widget" and state == "PARAM")
        {
            if (currentParam->has("widgetType")) throw Pothos::SyntaxException(
                "Multiple occurrence of |widget for param",
                codeLine.toString());
            Poco::RegularExpression::MatchVec fields;
            Poco::RegularExpression("^\\s*(\\w+)\\s*\\((.*)\\)$").match(payload, 0, fields);
            if (fields.empty()) throw Pothos::SyntaxException(
                "Expected |widget SpinBox(args...)",
                codeLine.toString());

            assert(fields.size() == 3);
            const std::string widgetType = Poco::trim(payload.substr(fields[1].offset, fields[1].length));
            const std::string argsStr = Poco::trim(payload.substr(fields[2].offset, fields[2].length));

            currentParam->set("widgetType", widgetType);
            loadArgs(codeLine, *currentParam, argsStr, "widgetArgs", "widgetKwargs");
        }
        else if (instruction == "tab" and state == "PARAM")
        {
            if (currentParam->has("tab")) throw Pothos::SyntaxException(
                "Multiple occurrence of |tab for param",
                codeLine.toString());
            currentParam->set("tab", payload);
        }
        else if (instruction == "preview" and state == "PARAM")
        {
            if (currentParam->has("preview")) throw Pothos::SyntaxException(
                "Multiple occurrence of preview for param",
                codeLine.toString());
            Poco::RegularExpression::MatchVec fields;
            Poco::RegularExpression("^\\s*(\\w+)(\\s*\\((.*)\\))?$").match(payload, 0, fields);
            if (fields.empty()) throw Pothos::SyntaxException(
                "Expected |preview previewType(args...)",
                codeLine.toString());

            assert(fields.size() == 2 or fields.size() == 4);
            const std::string previewType = Poco::trim(payload.substr(fields[1].offset, fields[1].length));

            if (previewType != "disable" and
                previewType != "enable" and
                previewType != "valid" and
                previewType != "invalid" and
                previewType != "when"
            ) throw Pothos::SyntaxException(
                "Only supports enable/disable/valid/invalid/when as value for preview option of param",
                codeLine.toString());

            currentParam->set("preview", previewType);
            if (fields.size() == 4)
            {
                const std::string argsStr = Poco::trim(payload.substr(fields[3].offset, fields[3].length));
                loadArgs(codeLine, *currentParam, argsStr, "previewArgs", "previewKwargs");
            }
        }
        else if (instruction == "option" and state == "PARAM")
        {
            payload = bracketEscapeEncode(payload);
            Poco::RegularExpression::MatchVec fields;
            Poco::RegularExpression("^(\\s*\\[(.*)\\]\\s*)?(.*)$").match(payload, 0, fields);
            if (fields.empty()) throw Pothos::SyntaxException(
                "Expected |option [name] value",
                codeLine.toString());

            assert(fields.size() == 4);
            const std::string value = bracketEscapeDecode(Poco::trim(payload.substr(fields[3].offset, fields[3].length)));
            std::string name = titleCase(value);
            if (fields[2].length != 0) name = bracketEscapeDecode(Poco::trim(payload.substr(fields[2].offset, fields[2].length)));

            Poco::JSON::Object option;
            option.set("value", value);
            option.set("name", name);
            if (not currentParam->has("options")) currentParam->set(
                "options", Poco::JSON::Array::Ptr(new Poco::JSON::Array()));
            currentParam->getArray("options")->add(option);
        }
        else if (instruction == "factory" and (state == "DOC" or state == "PARAM"))
        {
            Poco::RegularExpression::MatchVec fields;
            Poco::RegularExpression("^\\s*(/.*)\\s*\\((.*)\\)$").match(payload, 0, fields);
            if (fields.empty()) throw Pothos::SyntaxException(
                "Expected |factory /registry/path(args...)",
                codeLine.toString());

            assert(fields.size() == 3);
            const std::string path = Poco::trim(payload.substr(fields[1].offset, fields[1].length));
            const std::string argsStr = Poco::trim(payload.substr(fields[2].offset, fields[2].length));

            //add the path
            try {Pothos::PluginPath(path);}
            catch (const Pothos::PluginPathError &)
            {
                throw Pothos::SyntaxException("Invalid factory path", codeLine.toString());
            }
            if (topObj->has("path")) throw Pothos::SyntaxException(
                "Multiple occurrence of |factory", codeLine.toString());
            topObj->set("path", path);

            //split and extract args
            loadArgs(codeLine, *topObj, argsStr);

            state = "DOC";
        }
        else if ((instruction == "setter" or instruction == "initializer") and (state == "DOC" or state == "PARAM"))
        {
            Poco::RegularExpression::MatchVec fields;
            Poco::RegularExpression("^\\s*(\\w+)\\s*\\((.*)\\)$").match(payload, 0, fields);
            if (fields.empty()) throw Pothos::SyntaxException(
                "Expected |"+instruction+" setFooBar(args...)",
                codeLine.toString());

            assert(fields.size() == 3);
            const std::string callName = Poco::trim(payload.substr(fields[1].offset, fields[1].length));
            const std::string argsStr = Poco::trim(payload.substr(fields[2].offset, fields[2].length));

            //add to calls
            Poco::JSON::Object call;
            call.set("type", instruction);
            call.set("name", callName);
            loadArgs(codeLine, call, argsStr);
            calls.add(call);

            state = "DOC";
        }
        else if (instruction == "mode" and (state == "DOC" or state == "PARAM"))
        {
            if (topObj->has("mode")) throw Pothos::SyntaxException(
                "Multiple occurrence of |mode",
                codeLine.toString());
            topObj->set("mode", payload);
        }
    }

    //empty state means this was a regular comment block, return null
    if (state.empty()) return Poco::JSON::Object::Ptr();

    topDocs = stripDocArray(topDocs);
    if (topDocs->size() > 0) topObj->set("docs", topDocs);
    if (categories.size() > 0) topObj->set("categories", categories);
    if (keywords.size() > 0) topObj->set("keywords", keywords);
    if (aliases->size() > 0) topObj->set("aliases", aliases);
    if (params.size() > 0) topObj->set("params", params);
    if (calls.size() > 0) topObj->set("calls", calls);

    //sanity check for required stuff
    if (not topObj->has("path"))
    {
        throw Pothos::SyntaxException("missing |factory declaration");
    }

    return topObj;
}
Example #3
0
void GraphEditor::exportToJSONTopology(const QString &fileName)
{
    poco_information_f1(_logger, "Exporting %s", fileName.toStdString());
    Poco::JSON::Object topObj;

    //all graph objects excluding widgets which are not exported
    //we will have to filter out graphical blocks as well
    const auto graphObjects = this->getGraphObjects(~GRAPH_WIDGET);

    //global variables
    Poco::JSON::Array globals;
    for (const auto &name : this->listGlobals())
    {
        const auto &value = this->getGlobalExpression(name);
        Poco::JSON::Object globalObj;
        globalObj.set("name", name.toStdString());
        globalObj.set("value", value.toStdString());
        globals.add(globalObj);
    }
    if (globals.size() > 0) topObj.set("globals", globals);

    //thread pools (filled in by blocks loop)
    Poco::JSON::Object threadPools;
    auto affinityZones = AffinityZonesDock::global();

    //blocks
    Poco::JSON::Array blocks;
    std::map<size_t, const GraphBlock*> uidToBlock;
    for (const auto *obj : graphObjects)
    {
        const auto block = dynamic_cast<const GraphBlock *>(obj);
        if (block == nullptr) continue;
        if (block->isGraphWidget()) continue;
        uidToBlock[block->uid()] = block;

        //copy in the the id and path
        Poco::JSON::Object blockObj;
        blockObj.set("id", block->getId().toStdString());
        blockObj.set("path", block->getBlockDescPath());

        //setup the thread pool when specified
        const auto affinityZone = block->getAffinityZone();
        if (not affinityZone.isEmpty() and affinityZone != "gui")
        {
            const auto config = affinityZones->zoneToConfig(affinityZone);
            threadPools.set(affinityZone.toStdString(), config);
            blockObj.set("threadPool", affinityZone.toStdString());
        }

        //block description args are in the same format
        const auto desc = block->getBlockDesc();
        if (desc->has("args")) blockObj.set("args", desc->get("args"));

        //copy in the named calls in array format
        Poco::JSON::Array calls;
        if (desc->isArray("calls")) for (const auto &elem : *desc->getArray("calls"))
        {
            const auto callObj = elem.extract<Poco::JSON::Object::Ptr>();
            Poco::JSON::Array call;
            call.add(callObj->get("name"));
            for (const auto &arg : *callObj->getArray("args")) call.add(arg);
            calls.add(call);
        }
        blockObj.set("calls", calls);

        //copy in the parameters as local variables
        Poco::JSON::Array locals;
        for (const auto &name : block->getProperties())
        {
            Poco::JSON::Object local;
            local.set("name", name.toStdString());
            local.set("value", block->getPropertyValue(name).toStdString());
            locals.add(local);
        }
        blockObj.set("locals", locals);

        blocks.add(blockObj);
    }
    topObj.set("blocks", blocks);
    if (threadPools.size() > 0) topObj.set("threadPools", threadPools);

    //connections
    Poco::JSON::Array connections;
    for (const auto &connInfo : TopologyEval::getConnectionInfo(graphObjects))
    {
        //the block may have been filtered out
        //check that its found in the mapping
        auto srcIt = uidToBlock.find(connInfo.srcBlockUID);
        if (srcIt == uidToBlock.end()) continue;
        auto dstIt = uidToBlock.find(connInfo.dstBlockUID);
        if (dstIt == uidToBlock.end()) continue;

        //create the connection information in order
        Poco::JSON::Array connArr;
        connArr.add(srcIt->second->getId().toStdString());
        connArr.add(connInfo.srcPort);
        connArr.add(dstIt->second->getId().toStdString());
        connArr.add(connInfo.dstPort);
        connections.add(connArr);
    }
    topObj.set("connections", connections);

    //write to file
    try
    {
        std::ofstream outFile(fileName.toStdString().c_str());
        topObj.stringify(outFile, 4/*indent*/);
    }
    catch (const std::exception &ex)
    {
        poco_error_f2(_logger, "Error exporting %s: %s", fileName, std::string(ex.what()));
    }
}