OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / scope / TypedVariableLink.cc

167 lines · cc ref:
/*
 * opencog/atoms/scope/TypedVariableLink.cc
 *
 * Copyright (C) 2020 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  June 2020
 *
 * 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/base/ClassServer.h>
#include <opencog/atoms/base/hash.h>

#include "TypedVariableLink.h"

using namespace opencog;

void TypedVariableLink::init()
{
	// Must have atom and type specification.
	if (2 != _outgoing.size())
		throw SyntaxException(TRACE_INFO,
			"Expecting atom and type specification; got %s",
			to_short_string().c_str());

	// Type-check. This is ... kind of a pointless restriction,
	// except that pretty much everything else expects variables
	// in this location.
	Type stype = _outgoing[0]->get_type();
	if (VARIABLE_NODE != stype and
	    GLOB_NODE != stype)
		throw SyntaxException(TRACE_INFO,
			"Sorry, we expect type names to be variables! Got=%s",
			to_short_string().c_str());

	// Allow VARIABLE_NODE, although this is a bug in the URE,
	// which should be using a SignatureLink for this case. XXX FIXME.
	Type dtype = _outgoing[1]->get_type();
	if (not nameserver().isA(dtype, TYPE_NODE) and
	    DEFINED_TYPE_NODE != dtype and
	    TYPE_CHOICE != dtype and
	    TYPE_INTERSECTION_LINK != dtype and
	    VARIABLE_NODE != dtype and // XXX FIXME this is wrong; URE-bug
	    SIGNATURE_LINK != dtype and
	    INTERVAL_LINK != dtype and
	    ARROW_LINK != dtype)
		throw SyntaxException(TRACE_INFO,
			"Expecting type definition, got %s in\n%s",
				nameserver().getTypeName(dtype).c_str(),
				to_short_string().c_str());

	_typech = createTypeChoice(HandleSeq({_outgoing[1]}),
		TYPE_CHOICE, GLOB_NODE == _outgoing[0]->get_type());
}

TypedVariableLink::TypedVariableLink(const HandleSeq&& oset, Type t)
	: Link(std::move(oset), t)
{
	init();
}

TypedVariableLink::TypedVariableLink(const Handle& name, const Handle& defn)
	: Link({name, defn}, TYPED_VARIABLE_LINK)
{
	init();
}

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

bool TypedVariableLink::is_untyped() const
{
	return _typech->is_untyped(GLOB_NODE == _outgoing[0]->get_type());
}

const GlobInterval TypedVariableLink::default_interval() const
{
	return _typech->default_interval(GLOB_NODE == _outgoing[0]->get_type());
}

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

/// A specialized hashing function, designed so that all equivalent
/// type specifications get exactly the same hash.  To achieve this,
/// the normalized type specifications are used, rather than the raw
/// user-specified types. (The static analysis is "normalizing").

ContentHash TypedVariableLink::compute_hash() const
{
	ContentHash hsh = get_fvna_offset<sizeof(ContentHash)>();
	fnv1a_hash(hsh, get_type());

	fnv1a_hash(hsh, get_variable()->get_hash());
	fnv1a_hash(hsh, get_typedecl()->get_hash());

	// Links will always have the MSB set.
	ContentHash mask = ((ContentHash) 1UL) << (8*sizeof(ContentHash) - 1);
	hsh |= mask;

	if (Handle::INVALID_HASH == hsh) hsh -= 1;
	return hsh;
}

/// Return true if the other TypedVariable is equal to this one,
/// up to alpha-conversion. This returns `true` if the other
/// TypedVariable has the same type restrictions, even though it
/// might have a different variable name. That is, return `true`
/// if the two variables are alpha-convertable.
///
/// The compare is a semantic compare, not a syntactic compare. That
/// is, the actual type restrictions are compared, and NOT the Atom
/// used to specify the restriction.
///
bool TypedVariableLink::is_equal(const TypedVariableLink& other) const
{
	// If one is a GlobNode, and the other a VariableNode,
	// then its a mismatch.
	if (get_variable()->get_type() != other.get_variable()->get_type())
		return false;

	// Type constraints must match.
	return _typech->is_equal(*other._typech);
}

bool TypedVariableLink::operator==(const Atom& other) const
{
	if (this == &other) return true;
	if (get_hash() != other.get_hash()) return false;
	if (other.get_type() != _type) return false;
	return is_equal(*TypedVariableLinkCast(other.get_handle()));
}

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

std::string TypedVariableLink::to_string(const std::string& indent) const
{
	std::string str = Link::to_string(indent);

	// Avoid printing confusing garbage for the simple case.
	if (not _typech->is_simple())
	{
		str += "\n" + indent;
		str += "; _typech:\n";
		str += _typech->to_string(indent + ";" + OC_TO_STRING_INDENT);
	}

	return str;
}

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

DEFINE_LINK_FACTORY(TypedVariableLink, TYPED_VARIABLE_LINK);

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