OverviewFilesCommitsIssuesDocsReleasesPeers

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

207 lines · cc ref:
/*
 * ValueOfLink.cc
 *
 * Copyright (C) 2015, 2018 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/core/NumberNode.h>
#include <opencog/atoms/value/VoidValue.h>
#include "ValueOfLink.h"

using namespace opencog;

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

void ValueOfLink::init(void)
{
	size_t ary = _outgoing.size();
	if (0 == ary)
		throw SyntaxException(TRACE_INFO, "Expecting one or more atoms!");

	return;

#if 0
	// Nothing to do, here.
	if (1 == ary and
		 (nameserver().isA(_type, FLOAT_VALUE_OF_LINK)))
	{
		return;
	}

	// Argh. FETCH_VALUE_OF can have 4 args.
	//if (3 < ary)
	//	throw SyntaxException(TRACE_INFO, "Expecting two or three atoms!");
#endif
}

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

// ValueOf must work in the local AtomSpace. This includes
// the evaluation of keys, as needed.
Handle ValueOfLink::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;
}

/// Return the literal value at the indicated key.
ValuePtr ValueOfLink::get_literal(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]));

	return ah->getValue(ak);
}

/// When executed, this will return the value at the indicated key.
ValuePtr ValueOfLink::do_execute(AtomSpace* as, bool silent)
{
	ValuePtr pap(get_literal(as, silent));
	if (pap)
	{
		if (not pap->is_atom())
			return pap;

		Handle aval(HandleCast(pap));
		if (not aval->is_executable())
			return pap;

		// Do it again!
		aval = as->add_atom(aval);
		ValuePtr pval(aval->execute(as, silent));
		if (pval)
			return pval;
		return pap;
	}

	// If we are here, then no Value was found. If there is a
	// third Atom, then it specifies a default to use instead.
	if (2 < _outgoing.size())
		return _outgoing[2];

	// Hmm. If there's no value, it might be because it was deleted,
	// or maybe it was never set. There are many reasons for that.
	//
	// We have two design choices here: return nullptr or VoidValue
	// or throw an exception. Neither is fun. If we return VoidValue,
	// then downstream code has to explicitly check for it, and
	// usually/typically pass it on to the caller. Yuck. This requires
	// lots of if-tests in the codebase. If we throw an exception,
	// then critical blocks will need try-catch blocks. But maybe
	// fewer of these blocks are needed.
	//
	// The historical choice was to throw exceptions, and the various
	// flavors of SilentException in particular. But an audit of the
	// code shows that these are infrequently used: Modern Atomese
	// seems to require SilentExceptions only very rarely. But the
	// few places they are used, they really are needed. So ...
	//
	// The right answer here seems to be "throw", as this will
	// typically be deep inside some processing pipeline, and we
	// want to avoid error checks up and down the call chain.
	if (silent)
		throw SilentException();

	throw InvalidParamException(TRACE_INFO,
	   "No value for %s", to_string().c_str());

	return createVoidValue();
}

/// When executed, this will return the value at the indicated key.
ValuePtr ValueOfLink::execute(AtomSpace* as, bool silent)
{
	// Very special case: If no key is given, *and* its a FloatValueOf,
	// *and* the atom is a NumberNode, then cast the number to a Float.
	size_t ary = _outgoing.size();
	if (1 == ary)
	{
		if (FLOAT_VALUE_OF_LINK == _type and
		    NUMBER_NODE == _outgoing[0]->get_type())
		{
			return createFloatValue(NumberNodeCast(_outgoing[0])->value());
		}

		// Well, we could also be wrapping a unary ValueShim.
		// I think this only happens when there's a bug somewhere
		// else, but ... well, its not totally preposterous, so
		// go ahead and handle it.
		if (VALUE_SHIM_LINK == _outgoing[0]->get_type())
		{
			return _outgoing[0]->execute(as, silent);
		}
		throw InvalidParamException(TRACE_INFO,
			"Expecting an Atom and a key; got %s", to_string().c_str());
	}

	return do_execute(as, silent);
}

// Crazy experimental interface. This breaks the rules, but the rules
// were made to be broken. Amirite?
bool ValueOfLink::bevaluate(AtomSpace* as, bool silent)
{
	if (BOOL_VALUE_OF_LINK != _type)
		throw RuntimeException(TRACE_INFO,
			"Must use a BoolValueOfLink for this to work: got %s\n",
			to_string().c_str());

	ValuePtr boo(execute(as, silent));
	OC_ASSERT(BOOL_VALUE == boo->get_type(), "Unexpected bug!");
	BoolValuePtr bvp(BoolValueCast(boo));
	OC_ASSERT(0 < bvp->size(), "Unexpected size bug!");
	return bvp->value()[0];
}

DEFINE_LINK_FACTORY(ValueOfLink, VALUE_OF_LINK)

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