OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / rule / RuleLink.cc

164 lines · cc ref:
/*
 * RuleLink.cc
 *
 * Copyright (C) 2009, 2014, 2015, 2019, 2022 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/atoms/atom_types/NameServer.h>
#include <opencog/atoms/scope/LambdaLink.h>
#include <opencog/atomspace/AtomSpace.h>

#include "RuleLink.h"

using namespace opencog;

void RuleLink::init(void)
{
	Type t = get_type();

	// If this is a PatternLink, bail out now. They have thier
	// own custom setup.
	if (nameserver().isA(t, PATTERN_LINK)) return;

	if (not nameserver().isA(t, RULE_LINK))
	{
		const std::string& tname = nameserver().getTypeName(t);
		throw InvalidParamException(TRACE_INFO,
			"Expecting a RuleLink, got %s", tname.c_str());
	}

	// If we are quoted, don't bother to try to do anything.
	if (_quoted) return;

	extract_variables();
	if (_body) extract_shadowed_terms(_body);
}

RuleLink::RuleLink(const Handle& vardecl,
                   const Handle& body,
                   const Handle& rewrite)
	: RuleLink(HandleSeq{vardecl, body, rewrite})
{}

RuleLink::RuleLink(const Handle& body, const Handle& rewrite)
	: RuleLink(HandleSeq{body, rewrite})
{}

RuleLink::RuleLink(const HandleSeq&& hseq, Type t)
	: PrenexLink(std::move(hseq), t)
{
	// _quoted is true, that means we are inside a quote,
	// and so nothing to be done. Skip variable extraction.
	// This is what ScopeLink does, too.
	if (_quoted) return;
	init();
}

/* ================================================================= */
///
/// Find and unpack variable declarations, if any; otherwise, just
/// find all free variables in the body (but not the implicand).
///
/// On top of that, initialize _body and _implicand with the
/// clauses and the rewrite rule(s). (Multiple implicands are
/// allowed, this can save some CPU cycles when one search needs to
/// create several rewrites.)
///
void RuleLink::extract_variables(void)
{
	size_t boff = ScopeLink::extract_variables(false);

	for (size_t i=boff+1; i < _outgoing.size(); i++)
		_implicand.push_back(_outgoing[i]);

	// Remove any declared variables that are NOT in the body!
	// This is an "unusual" situation, except that the URE does
	// this regularly, when it constructs rules on the fly.
	// I don't know why.
	if (1 == boff)
	{
		_implicand.push_back(_body),
		trim(_implicand);
		_implicand.pop_back();
	}
}

/* ================================================================= */

// Execute h. If its a lambda, unwrap it.
static Handle maybe_exec(const Handle& h, Variables& redvars)
{
	Handle hred(h);

	// ExecutableLinks can return anything, and not just Atoms.
	// So, here we execute it, and if a non-Atom Value is returned,
	// then pretend the execution never happened. This seems wrong,
	// because the execution could have side-effects. This might
	// be a bug waiting to happen, but I dunno.
	if (h->is_executable())
	{
		ValuePtr vp = h->execute();
		if (vp->is_atom())
			hred = HandleCast(vp);
	}

	if (hred->is_type(LAMBDA_LINK))
	{
		LambdaLinkPtr lmb(LambdaLinkCast(hred));
		redvars.extend_intersect(lmb->get_variables());
		hred = lmb->get_body();
	}
	return hred;
}

/// Reduce the link; i.e. call execute on everything that it wraps.
ValuePtr RuleLink::execute(AtomSpace* as, bool silent)
{
	if (0 == _implicand.size())
		throw SyntaxException(TRACE_INFO,
			"Expecting one or more implicands in %s",
			to_string().c_str());

	Variables redvars = _variables;

	HandleSeq redbody;
	for (const Handle& h : _body->getOutgoingSet())
		redbody.emplace_back(maybe_exec(h, redvars));

	Handle rbdy(as->add_link(_body->get_type(), std::move(redbody)));

	HandleSeq redimpl;
	for (const Handle& h : _implicand)
		redimpl.emplace_back(maybe_exec(h, redvars));

	HandleSeq redset;
	redset.emplace_back(redvars.get_vardecl());
	redset.emplace_back(rbdy);
	redset.insert(redset.end(), redimpl.begin(), redimpl.end());

	return as->add_link(_type, std::move(redset));
}

DEFINE_LINK_FACTORY(RuleLink, RULE_LINK)

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