OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / flow / CollectionOfLink.cc

169 lines · cc ref:
/*
 * CollectionOfLink.cc
 *
 * Copyright (C) 2015, 2022, 2024 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the
 * exceptions at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/signature/TypeNode.h>
#include <opencog/atoms/value/LinkValue.h>
#include <opencog/atoms/value/ValueFactory.h>

#include "CollectionOfLink.h"

using namespace opencog;

CollectionOfLink::CollectionOfLink(const HandleSeq&& oset, Type t)
	: FunctionLink(std::move(oset), t)
{
	if (not nameserver().isA(t, COLLECTION_OF_LINK))
	{
		const std::string& tname = nameserver().getTypeName(t);
		throw InvalidParamException(TRACE_INFO,
			"Expecting an CollectionOfLink, got %s", tname.c_str());
	}

	_out_type = SET_LINK;
	_out_is_link = true;
	_have_typespec = false;

	check_typespec();
}

// ---------------------------------------------------------------

/// Check for valid form
void CollectionOfLink::check_typespec(void)
{
	if (1 == _outgoing.size())
	{
		_have_typespec = false;
		return;
	}

	// If there are two args, then the first one specifies the
	// output type.
	if (2 != _outgoing.size())
		throw InvalidParamException(TRACE_INFO,
			"Expecting one or two args, got %s",
				to_string().c_str());

	_have_typespec = true;

	// FIXME: _outoging[0] could be executable, in which case
	// is should be executed, first. But I'm lazy. Also:
	// instead of being a simple type, the output could be
	// a complicated signature. Again, I'm lazy.
	if (not _outgoing[0]->is_type(TYPE_NODE))
		throw InvalidParamException(TRACE_INFO,
			"Expecting first arg to be a type, got %s",
				to_string().c_str());
	_out_type = TypeNodeCast(_outgoing[0])->get_kind();

	_out_is_link = nameserver().isLink(_out_type);

	// Normally, we'd re-write only into LinkValues, only.
	// But FormulaStream inherits from FloatValue, and we
	// want to allow FormulaStream rewrites. So support that.
	if ((not _out_is_link and not nameserver().isA(_out_type, LINK_VALUE)) and
	    (not _out_is_link and not nameserver().isA(_out_type, FLOAT_VALUE)))
		throw InvalidParamException(TRACE_INFO,
			"Expecting type to be a Link or LinkValue, got %s for %s",
				nameserver().getTypeName(_out_type).c_str(),
				to_string().c_str());
}

// ---------------------------------------------------------------

ValuePtr CollectionOfLink::rewrap_h(AtomSpace* as, const Handle& base)
{
	if (base->is_node())
	{
		if (_out_is_link)
			return as->add_link(_out_type, {base});
		return valueserver().create(_out_type, ValueSeq({base}));
	}

	if (_out_is_link)
		return as->add_link(_out_type,
			HandleSeq(base->getOutgoingSet()));

	return valueserver().create(_out_type, base->getOutgoingSet());
}

// ---------------------------------------------------------------

ValuePtr CollectionOfLink::rewrap_v(AtomSpace* as, const ValuePtr& vp)
{
	if (not vp->is_type(LINK_VALUE))
		throw InvalidParamException(TRACE_INFO,
			"CollectionOfLink expects a LinkValue, got %s",
			vp->to_string().c_str());

	LinkValuePtr lvp(LinkValueCast(vp));

	if (_out_is_link)
	{
		HandleSeq hs = lvp->to_handle_seq();
		return as->add_link(_out_type, std::move(hs));
	}

	if (vp->get_type() == _out_type)
		return vp;

	return valueserver().create(_out_type, lvp->value());
}

// ---------------------------------------------------------------

/// Return a SetLink vector.
ValuePtr CollectionOfLink::execute(AtomSpace* as, bool silent)
{
	int coff = 0;
	if (_have_typespec) coff = 1;

	// If the atom is not executable, then re-wrap it, as appropriate.
	Handle base(_outgoing[coff]);
	if (not base->is_executable())
		return rewrap_h(as, base);

	// If the given Atom is executable, then execute it, and
	// branch over all the various possibilities.
	ValuePtr vp = base->execute(as, silent);

	// Hmmm. FilterLink returns null pointer when the filter
	// is empty. Is this a bug in FilterLink? Not sure any more.
	if (nullptr == vp) // or VOID_VALUE == vp->get_type()) ???
	{
		if (_out_is_link)
			return createLink(_out_type);
		return valueserver().create(_out_type, ValueSeq({}));
	}

	if (vp->is_atom())
		return rewrap_h(as, HandleCast(vp));

	return rewrap_v(as, vp);
}

DEFINE_LINK_FACTORY(CollectionOfLink, COLLECTION_OF_LINK)

/* ===================== END OF FILE ===================== */