OverviewFilesCommitsIssuesDocsReleasesPeers

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

143 lines · cc ref:
/*
 * SetValueLink.cc
 *
 * Copyright (C) 2015, 2018, 2020 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.
 */

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/execution/ExecutionOutputLink.h>
#include <opencog/atoms/value/FormulaStream.h>
#include <opencog/atoms/value/FutureStream.h>
#include <opencog/atoms/value/VoidValue.h>
#include "SetValueLink.h"

using namespace opencog;

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

	size_t ary = _outgoing.size();
	if (2 > ary or 4 < ary)
		throw SyntaxException(TRACE_INFO,
			"Expecting two, three or four atoms!");
}

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

// SetValue must work in the local AtomSpace. This includes
// the evaluation of keys, as needed.
Handle SetValueLink::localize(AtomSpace* as, bool silent, const Handle& h) const
{
	if (not h->is_executable())
		return as->add_atom(h);

	ValuePtr pap(h->execute(as, silent));
	if (pap and pap->is_atom())
		return as->add_atom(HandleCast(pap));
	return h;
}

/// When executed, this will execute the third argument to obtain
/// a Value, and then set that Value at the indicated key on the
/// first argument. The computed value is returned.
/// The SetValueOn link returns the Atom, not the Value.
ValuePtr SetValueLink::execute(AtomSpace* as, bool silent)
{
	// We cannot know the Value of the Atom unless we are
	// working with the unique version that sits in the
	// AtomSpace! It can happen, during evaluation e.g. of
	// a PutLink, that we are given an Atom that is not in
	// any AtomSpace. In this case, `as` will be a scratch
	// space; we can add the Atom there, and things will
	// trickle out properly in the end.
	//
	// Avoid null-pointer deref due to user error.
	// This can happen with improperly built FilterLinks.
	if (nullptr == as)
		throw RuntimeException(TRACE_INFO,
			"Expecting AtomSpace, got null pointer for %s\n",
			to_string().c_str());

	Handle ah(localize(as, silent, _outgoing[0]));
	Handle ak(localize(as, silent, _outgoing[1]));

	// Default VoidValue
	if (2 == _outgoing.size())
	{
		ValuePtr pap(createVoidValue());
		as->set_value(ah, ak, pap);
		if (SET_VALUE_ON_LINK == get_type())
			return ah;
		return pap;
	}

	// Simple case: just set the Value. Obtain it, as needed.
	if (3 == _outgoing.size())
	{
		ValuePtr pap;
		if (_outgoing[2]->is_executable())
			pap = _outgoing[2]->execute(as, silent);
		else
			pap = _outgoing[2];

		as->set_value(ah, ak, pap);

		if (SET_VALUE_ON_LINK == get_type())
			return ah;
		return pap;
	}

	// XXX Fixme Is this used anywhere ???
	// Its a vaguely spiffy idea, and it is tested in a unit test,
	// but is it actually used in any realistic scenario?
	// It would be coolear to just auto-wrap four or more args in
	// a LinkValue ... then again, this is easy for the user to do,
	// so not that big a deal, either way, I guess ...

	// Complicated Case: There are four arguments. The first two are
	// Atom and key, as before. Then comes a lambda or function in
	// third place, and the arguments to the function in fourth place.
	// Wrap these two in an ExecutionOutput, and then wrap that in a
	// FormulaStream. The user could do this themselves; this is
	// provided as a convenience function.

	const Handle& args(_outgoing[3]);
	Handle exo(createExecutionOutputLink(_outgoing[2], args));
	exo = as->add_atom(exo);

	ValuePtr fsp;
	if (args->is_type(NUMERIC_OUTPUT_SIG))
		fsp = createFormulaStream(exo);
	else
		fsp = createFutureStream(exo);

	as->set_value(ah, ak, fsp);

	if (SET_VALUE_ON_LINK == get_type())
		return ah;
	return fsp;
}

DEFINE_LINK_FACTORY(SetValueLink, SET_VALUE_LINK)

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