/*
* 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 ===================== */