예제 #1
0
/***********************************************************************
 * block factory - make blocks from JSON object
 **********************************************************************/
static Pothos::Proxy makeBlock(
    const Pothos::Proxy &registry,
    const Pothos::Proxy &evaluator,
    const Poco::JSON::Object::Ptr &blockObj)
{
    const auto id = blockObj->getValue<std::string>("id");

    if (not blockObj->has("path")) throw Pothos::DataFormatException(
        "Pothos::Topology::make()", "blocks["+id+"] missing 'path' field");
    const auto path = blockObj->getValue<std::string>("path");

    //load up the constructor args
    Poco::JSON::Array::Ptr argsArray;
    if (blockObj->isArray("args")) argsArray = blockObj->getArray("args");
    const auto ctorArgs = evalArgsArray(evaluator, argsArray);

    //create the block
    auto block = registry.getHandle()->call(path, ctorArgs.data(), ctorArgs.size());

    //make the calls
    Poco::JSON::Array::Ptr callsArray;
    if (blockObj->isArray("calls")) callsArray = blockObj->getArray("calls");
    if (callsArray) for (size_t i = 0; i < callsArray->size(); i++)
    {
        const auto callArray = callsArray->getArray(i);
        auto name = callArray->getElement<std::string>(0);
        const auto callArgs = evalArgsArray(evaluator, callArray, 1/*offset*/);
        block.getHandle()->call(name, callArgs.data(), callArgs.size());
    }

    return block;
}
예제 #2
0
void ProxyBlockEval::eval(const std::string &id, const Poco::JSON::Object::Ptr &blockDesc)
{
    auto env = Pothos::ProxyEnvironment::make("managed");
    auto registry = env->findProxy("Pothos/BlockRegistry");
    auto path = blockDesc->getValue<std::string>("path");

    //load up the constructor args
    std::vector<Pothos::Proxy> ctorArgs;
    if (blockDesc->isArray("args")) for (auto arg : *blockDesc->getArray("args"))
    {
        const auto obj = this->lookupOrEvalAsType(arg);
        ctorArgs.push_back(env->convertObjectToProxy(obj));
    }

    //create the block
    try
    {
        _proxyBlock = registry.getHandle()->call(path, ctorArgs.data(), ctorArgs.size());
    }
    catch (const Pothos::Exception &ex)
    {
        throw Pothos::Exception("ProxyBlockEval factory("+path+")", ex);
    }
    _proxyBlock.callVoid("setName", id);

    //make the calls
    if (blockDesc->isArray("calls")) for (auto call : *blockDesc->getArray("calls"))
    {
        this->handleCall(call.extract<Poco::JSON::Object::Ptr>());
    }
}
예제 #3
0
void BlockEval::eval(const std::string &id, const Poco::JSON::Object::Ptr &blockDesc)
{
    auto env = Pothos::ProxyEnvironment::make("managed");
    auto registry = env->findProxy("Pothos/BlockRegistry");
    auto path = blockDesc->getValue<std::string>("path");

    //load up the constructor args
    std::vector<Pothos::Proxy> ctorArgs;
    for (auto arg : *blockDesc->getArray("args"))
    {
        const auto propKey = arg.extract<std::string>();
        const auto obj = _properties[propKey];
        ctorArgs.push_back(env->convertObjectToProxy(obj));
    }

    //create the block
    try
    {
        _proxyBlock = registry.getHandle()->call(path, ctorArgs.data(), ctorArgs.size());
    }
    catch (const Pothos::Exception &ex)
    {
        throw Pothos::Exception("BlockEval factory("+path+")", ex);
    }
    _proxyBlock.callVoid("setName", id);

    //inspect before making any calls -- calls may fails
    _portDesc = this->inspectPorts();

    //make the calls
    for (auto call : *blockDesc->getArray("calls"))
    {
        const auto callObj = call.extract<Poco::JSON::Object::Ptr>();
        const auto callName = callObj->get("name").extract<std::string>();
        std::vector<Pothos::Proxy> callArgs;
        for (auto arg : *callObj->getArray("args"))
        {
            const auto propKey = arg.extract<std::string>();
            const auto obj = _properties[propKey];
            callArgs.push_back(env->convertObjectToProxy(obj));
        }
        try
        {
            _proxyBlock.getHandle()->call(callName, callArgs.data(), callArgs.size());
        }
        catch (const Pothos::Exception &ex)
        {
            throw Pothos::Exception("BlockEval call("+callName+")", ex);
        }
    }

    //inspect after making calls -- ports may have changed
    _portDesc = this->inspectPorts();
}
예제 #4
0
void updateTasksList()
{
	printf("updateTasksList()\n");
	tasksNum = task_manager->getTasksNumber();
	if(tasksNum > 0)
	{
		Poco::JSON::Object::Ptr pObj = new Poco::JSON::Object;
		task_manager->getTasks(pObj);
		Poco::DynamicStruct ds = *pObj;
		printf("ds:%s\n", ds.toString().c_str());
		if(pObj->has("tasks"))
			printf("pObj has tasks\n");
		if(pObj->isArray("tasks"))
			printf("pObj is array tasks\n");
		Poco::JSON::Array::Ptr pArray = pObj->getArray("tasks");
		printf("tasksNum:%d, array size:%d\n", tasksNum, pArray->size());
		for(int i = 0; i < tasksNum; i++)
		{
			memset(pTask[i], 0, sizeof(TaskInfo));
			Poco::Dynamic::Var var = pArray->get(i);
			Poco::DynamicStruct dss = var.extract<Poco::DynamicStruct>();
			pTask[i]->id = (Poco::Int64)dss["id"].extract<Poco::Int64>();
			pTask[i]->option = dss["option"].extract<int>();
			pTask[i]->hour = dss["hour"].extract<int>();
			pTask[i]->minute = dss["minute"].extract<int>();
			pTask[i]->weekday = dss["weekday"].extract<int>();
		}
	}
}
예제 #5
0
QString BlockPropertiesPanel::getParamDocString(const Poco::JSON::Object::Ptr &paramDesc)
{
    assert(paramDesc);
    QString output;
    output += QString("<h3>%1</h3>").arg(QString::fromStdString(paramDesc->getValue<std::string>("name")));
    if (paramDesc->isArray("desc")) for (const auto &lineObj : *paramDesc->getArray("desc"))
    {
        const auto line = lineObj.extract<std::string>();
        if (line.empty()) output += "<p /><p>";
        else output += QString::fromStdString(line)+"\n";
    }
    else output += QString("<p>%1</p>").arg(tr("Undocumented"));
    return output;
}
예제 #6
0
bool BlockTreeWidget::blockDescMatchesFilter(const Poco::JSON::Object::Ptr &blockDesc)
{
    if (_filter.isEmpty()) return true;

    const auto path = blockDesc->get("path").extract<std::string>();
    const auto name = blockDesc->get("name").extract<std::string>();

    //construct a candidate string from path, name, categories, and keywords.
    std::string candidate = path+name;
    if (blockDesc->isArray("categories")) for (auto categoryObj : *blockDesc->getArray("categories"))
    {
        candidate += categoryObj.extract<std::string>();
    }
    if(blockDesc->isArray("keywords"))
    {
        const auto keywords = blockDesc->getArray("keywords");
        for(auto keyword : *keywords) candidate += keyword.extract<std::string>();
    }

    //reject if filter string not found in candidate
    candidate = Poco::toLower(candidate);
    const auto searchToken = Poco::toLower(_filter.toStdString());
    return (candidate.find(searchToken) != std::string::npos);
}
예제 #7
0
void AffinityZoneEditor::loadFromConfig(const Poco::JSON::Object::Ptr &config)
{
    if (config->has("color"))
    {
        auto color = QString::fromStdString(config->getValue<std::string>("color"));
        _colorPicker->blockSignals(true);
        _colorPicker->setCurrentColor(QColor(color));
        _colorPicker->blockSignals(false);
    }
    if (config->has("hostUri"))
    {
        auto uri = QString::fromStdString(config->getValue<std::string>("hostUri"));
        this->selectThisUri(uri);
    }
    if (config->has("processName"))
    {
        auto name = QString::fromStdString(config->getValue<std::string>("processName"));
        _processNameEdit->setText(name);
    }
    if (config->has("numThreads"))
    {
        _numThreadsSpin->setValue(config->getValue<int>("numThreads"));
    }
    if (config->has("priority"))
    {
        _prioritySpin->setValue(int(config->getValue<double>("priority")*100));
    }
    if (config->has("affinityMode") and config->has("affinityMask"))
    {
        auto mode = config->getValue<std::string>("affinityMode");
        auto mask = config->getArray("affinityMask");
        std::vector<size_t> selection;
        for (size_t i = 0; i < mask->size(); i++) selection.push_back(mask->getElement<int>(i));
        _cpuSelection->setup(mode, selection);
    }
    if (config->has("yieldMode"))
    {
        auto mode = QString::fromStdString(config->getValue<std::string>("yieldMode"));
        for (int i = 0; i < _yieldModeBox->count(); i++)
        {
            if (_yieldModeBox->itemData(i).toString() == mode) _yieldModeBox->setCurrentIndex(i);
        }
    }
}
예제 #8
0
void ProxyBlockEval::handleCall(const Poco::JSON::Object::Ptr &callObj)
{
    auto env = Pothos::ProxyEnvironment::make("managed");
    const auto callName = callObj->get("name").extract<std::string>();
    std::vector<Pothos::Proxy> callArgs;
    for (auto arg : *callObj->getArray("args"))
    {
        const auto obj = this->lookupOrEvalAsType(arg);
        callArgs.push_back(env->convertObjectToProxy(obj));
    }
    try
    {
        _proxyBlock.getHandle()->call(callName, callArgs.data(), callArgs.size());
    }
    catch (const Pothos::Exception &ex)
    {
        throw Pothos::Exception("ProxyBlockEval call("+callName+")", ex);
    }
}
예제 #9
0
static Poco::JSON::Array::Ptr getConnectedPortInfos(
    const Poco::JSON::Object::Ptr &topObj,
    const std::string &blockId,
    const bool enbFilter,
    const bool isInput)
{
    const auto connsArray = topObj->getArray("connections");
    const auto blocksObj = topObj->getObject("blocks");
    const auto blockObj = blocksObj->getObject(blockId);

    //grab the raw ports info
    Poco::JSON::Array::Ptr portsInfo(new Poco::JSON::Array());
    if (isInput and blockObj->has("inputs")) portsInfo = blockObj->getArray("inputs");
    if (not isInput and blockObj->has("outputs")) portsInfo = blockObj->getArray("outputs");

    //no filtering? return ASAP
    if (not enbFilter) return portsInfo;

    Poco::JSON::Array::Ptr filteredPortsInfo(new Poco::JSON::Array());
    for (size_t i = 0; i < portsInfo->size(); i++)
    {
        const auto portInfo = portsInfo->getObject(i);
        for (size_t c_i = 0; c_i < connsArray->size(); c_i++)
        {
            const auto conn = connsArray->getObject(c_i);
            if (
                (not isInput and blockId == conn->getValue<std::string>("srcId") and portInfo->getValue<std::string>("name") == conn->getValue<std::string>("srcName")) or
                (isInput and blockId == conn->getValue<std::string>("dstId") and portInfo->getValue<std::string>("name") == conn->getValue<std::string>("dstName"))
            )
            {
                filteredPortsInfo->add(portInfo);
                break;
            }
        }
    }
    return filteredPortsInfo;
}
예제 #10
0
static bool flattenDump(Poco::JSON::Object::Ptr &topObj)
{
    assert(topObj);
    bool hierFound = false;

    //create new blocks object that flattens any hierarchy to 1 depth
    //if this block is a hierarchy -- bring its blocks to the top level
    const auto blocksObj = topObj->getObject("blocks");
    assert(blocksObj);
    Poco::JSON::Object::Ptr flatBlocksObj(new Poco::JSON::Object());
    std::vector<std::string> blockUids; blocksObj->getNames(blockUids);
    for (const auto &uid : blockUids)
    {
        const auto blockObj = blocksObj->getObject(uid);
        assert(blockObj);
        if (blockIsHier(blockObj))
        {
            hierFound = true;
            const auto subBlocksObj = blockObj->getObject("blocks");
            assert(subBlocksObj);
            const auto thisName = blockObj->getValue<std::string>("name");
            std::vector<std::string> subBlockUids; subBlocksObj->getNames(subBlockUids);
            for (const auto &subUid : subBlockUids)
            {
                auto subBlockObj = subBlocksObj->getObject(subUid);
                assert(subBlockObj);
                const auto subName = subBlockObj->getValue<std::string>("name");
                subBlockObj->set("name", thisName+"/"+subName); //heritage name
                flatBlocksObj->set(subUid, subBlockObj);
            }
        }
        else flatBlocksObj->set(uid, blockObj);
    }

    //create new connections array folding out depth 1 hierarchies
    const auto connsArray = topObj->getArray("connections");
    assert(connsArray);
    Poco::JSON::Array::Ptr flatConnsArray(new Poco::JSON::Array());
    for (size_t c_i = 0; c_i < connsArray->size(); c_i++)
    {
        const auto connObj = connsArray->getObject(c_i);
        assert(connObj);
        for (const auto & resolvedSrc : resolvePorts(topObj, connObj->getValue<std::string>("srcId"), connObj->getValue<std::string>("srcName"), true))
        {
            for (const auto & resolvedDst : resolvePorts(topObj, connObj->getValue<std::string>("dstId"), connObj->getValue<std::string>("dstName"), false))
            {
                Poco::JSON::Object::Ptr flatConnObj(new Poco::JSON::Object());
                flatConnsArray->add(flatConnObj);
                flatConnObj->set("srcId", resolvedSrc.first);
                flatConnObj->set("srcName", resolvedSrc.second);
                flatConnObj->set("dstId", resolvedDst.first);
                flatConnObj->set("dstName", resolvedDst.second);
            }
        }
    }

    //resolve pass-through connections and totally internal connections
    for (const auto &uid : blockUids)
    {
        const auto blockObj = blocksObj->getObject(uid);
        assert(blockObj);
        if (not blockIsHier(blockObj)) continue;
        const auto subConnsArray = blockObj->getArray("connections");

        for (size_t c_i = 0; c_i < subConnsArray->size(); c_i++)
        {
            const auto subConnObj = subConnsArray->getObject(c_i);
            assert(subConnObj);
            const bool srcIsThis = subConnObj->getValue<std::string>("srcId") == uid;
            const bool dstIsThis = subConnObj->getValue<std::string>("dstId") == uid;

            //totally internal connection
            if (not srcIsThis and not dstIsThis) flatConnsArray->add(subConnObj);

            //otherwise not a pass-through
            if (not srcIsThis or not dstIsThis) continue;

            //find sources where the destination is this pass-through
            for (size_t c_s = 0; c_s < connsArray->size(); c_s++)
            {
                const auto connObj_s = connsArray->getObject(c_s);
                assert(connObj_s);
                if (connObj_s->getValue<std::string>("dstId") != uid) continue;
                if (connObj_s->getValue<std::string>("dstName") != subConnObj->getValue<std::string>("srcName")) continue;

                //find sources where the destination is this pass-through
                for (size_t c_d = 0; c_d < connsArray->size(); c_d++)
                {
                    const auto connObj_d = connsArray->getObject(c_d);
                    assert(connObj_d);
                    if (connObj_d->getValue<std::string>("srcId") != uid) continue;
                    if (connObj_d->getValue<std::string>("srcName") != subConnObj->getValue<std::string>("dstName")) continue;

                    Poco::JSON::Object::Ptr flatConnObj(new Poco::JSON::Object());
                    flatConnsArray->add(flatConnObj);
                    flatConnObj->set("srcId", connObj_s->get("srcId"));
                    flatConnObj->set("srcName", connObj_s->get("srcName"));
                    flatConnObj->set("dstId", connObj_d->get("dstId"));
                    flatConnObj->set("dstName", connObj_d->get("dstName"));
                }
            }
        }
    }

    //set new flat data into the top object
    topObj = new Poco::JSON::Object();
    topObj->set("blocks", flatBlocksObj);
    topObj->set("connections", flatConnsArray);
    return hierFound;
}
예제 #11
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;
}