OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / pattern / PatternTerm.cc

496 lines · cc ref:
/*
 * PatternTerm.cc
 *
 * Copyright (C) 2016 OpenCog Foundation
 * All Rights Reserved
 *
 * Created by Nil Geisweiller Oct 2016
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <opencog/util/oc_assert.h>
#include "PatternTerm.h"

namespace opencog {

const PatternTermPtr PatternTerm::UNDEFINED(std::make_shared<PatternTerm>());

PatternTerm::PatternTerm(void)
	: _handle(Handle::UNDEFINED),
	  _quote(Handle::UNDEFINED),
	  _parent(PatternTerm::UNDEFINED),
	  _has_any_bound_var(false),
	  _has_bound_var(false),
	  _is_bound_var(false),
	  _has_any_globby_var(false),
	  _has_globby_var(false),
	  _is_globby_var(false),
	  _has_any_anon_var(false),
	  _has_anon_var(false),
	  _is_anon_var(false),
	  _has_any_evaluatable(false),
	  _has_evaluatable(false),
	  _is_virtual(false),
	  _is_identical(false),
	  _is_exclusive(false),
	  _has_any_unordered_link(false),
	  _has_unordered_below(false),
	  _is_literal(false),
	  _is_present(false),
	  _is_absent(false),
	  _is_choice(false),
	  _has_choice(false),
	  _is_always(false)
{}

PatternTerm::PatternTerm(const PatternTermPtr& parent, const Handle& h)
	: _handle(h), _quote(Handle::UNDEFINED), _parent(parent),
	  _quotation(parent->_quotation.level(),
	             false /* necessarily false since it is local */),
	  _has_any_bound_var(false),
	  _has_bound_var(false),
	  _is_bound_var(false),
	  _has_any_globby_var(false),
	  _has_globby_var(false),
	  _is_globby_var(false),
	  _has_any_anon_var(false),
	  _has_anon_var(false),
	  _is_anon_var(false),
	  _has_any_evaluatable(false),
	  _has_evaluatable(false),
	  _is_virtual(false),
	  _is_identical(false),
	  _is_exclusive(false),
	  _has_any_unordered_link(false),
	  _has_unordered_below(false),
	  _is_literal(false),
	  _is_present(false),
	  _is_absent(false),
	  _is_choice(false),
	  _has_choice(false),
	  _is_always(false)
{
	Type t = h->get_type();

	// Discard the following QuoteLink, UnquoteLink or LocalQuoteLink
	// as it is serving its quoting or unquoting function.
	if (_quotation.consumable(t)) {
		if (1 != h->get_arity())
			throw InvalidParamException(TRACE_INFO,
			                            "QuoteLink/UnquoteLink/LocalQuoteLink has "
			                            "unexpected arity!");
		// Save the quotes, useful for mapping patterns to grounds
		_quote = _handle;
		_handle = h->getOutgoingAtom(0);
	}

	// Update the quotation state
	_quotation.update(t);
}

PatternTermPtr PatternTerm::addOutgoingTerm(const Handle& h)
{
	PatternTermPtr ptm(createPatternTerm(shared_from_this(), h));
	_outgoing.push_back(ptm);
	return ptm;
}

PatternTermSeq PatternTerm::getOutgoingSet() const
{
	PatternTermSeq oset;
	for (const PatternTermWPtr& w : _outgoing)
	{
		PatternTermPtr s(w.lock());
		OC_ASSERT(nullptr != s, "Unexpected corruption of PatternTerm oset!");
		oset.push_back(s);
	}

	return oset;
}

PatternTermPtr PatternTerm::getOutgoingTerm(Arity pos) const
{
	// Checks for a valid position
	if (pos < getArity()) {
		PatternTermPtr s(_outgoing[pos].lock());
		OC_ASSERT(nullptr != s, "Unexpected missing PatternTerm oset entry!");
		return s;
	} else {
		throw RuntimeException(TRACE_INFO,
		                       "invalid outgoing set index %d", pos);
	}
}

/**
 * isDescendant - return true if `this` is a lineal descendant of `ptm`.
 * That is, return true if `ptm` appears as a parent somewhere in the
 * chain of parents of `this`.
 *
 * Mnemonic device: child->isDescendant(parent) == true
 */
bool PatternTerm::isDescendant(const PatternTermPtr& ptm) const
{
	if (PatternTerm::UNDEFINED == _parent) return false;
	if (_parent == ptm) return true;
	if (*_parent == *ptm) return true;
	return _parent->isDescendant(ptm);
}

// ==============================================================
/**
 * Equality operator.  Both the content must match, and the path
 * taken to get to the content must match.
 */
bool PatternTerm::operator==(const PatternTerm& other) const
{
	if (_handle != other._handle) return false;
	if (_parent != other._parent) return false;
	if (PatternTerm::UNDEFINED == _parent) return true;

	return _parent->operator==(*other._parent);
}

// ==============================================================

// Mark recursively, all the way to the root.
void PatternTerm::addAnyBoundVar()
{
	if (not _has_any_bound_var)
	{
		_has_any_bound_var = true;
		if (_parent->_handle)
			_parent->addAnyBoundVar();
	}
}

/// Set two flags: one flag (the "any" flag) is set recursively from
/// a variable, all the way up to the root, indicating that there's
/// a variable on this path.  The other flag gets set only on the
/// variable, and it's immediate parent (i.e. the holder of the
/// variable).
void PatternTerm::addBoundVariable()
{
	if (isQuoted()) return;

	// Mark just this term (the variable itself)
	// and mark the term that holds us.
	_is_bound_var = true;
	if (_parent->_handle)
			_parent->_has_bound_var = true;

	// Mark recursively, all the way to the root.
	addAnyBoundVar();
}

// ==============================================================
// Just like above, but for globs.

void PatternTerm::addAnyGlobbyVar()
{
	if (not _has_any_globby_var)
	{
		_has_any_globby_var = true;
		if (_parent->_handle)
			_parent->addAnyGlobbyVar();
	}
}

void PatternTerm::addGlobbyVar()
{
	if (isQuoted()) return;

	_is_globby_var = true;

	if (_parent->_handle)
		_parent->_has_globby_var = true;

	addAnyGlobbyVar();
}

// ==============================================================
// Just like above, but for anonymous variables (SignatureLinks).

void PatternTerm::addAnyAnonVar()
{
	if (not _has_any_anon_var)
	{
		_has_any_anon_var = true;
		if (_parent->_handle)
			_parent->addAnyAnonVar();
	}
}

void PatternTerm::addAnonVar()
{
	if (isQuoted()) return;

	_is_anon_var = true;

	if (_parent->_handle)
		_parent->_has_anon_var = true;

	addAnyAnonVar();
}

// ==============================================================
// Just like above, but for evaluatables.

void PatternTerm::addAnyEvaluatable()
{
	if (_has_any_evaluatable) return;

	_has_any_evaluatable = true;
	if (_parent->_handle)
		_parent->addAnyEvaluatable();
}

void PatternTerm::addEvaluatable()
{
	_has_evaluatable = true;

	if (_parent->_handle)
		_parent->_has_evaluatable = true;

	addAnyEvaluatable();
}

// ==============================================================

void PatternTerm::markVirtual()
{
	// If quoted, it cannot be evaluated.
	if (isQuoted()) return;

	_is_virtual = true;
}

void PatternTerm::markIdentical()
{
	// If quoted, it cannot be evaluated.
	if (isQuoted()) return;

	_is_identical = true;
}

void PatternTerm::markExclusive()
{
	// If quoted, it cannot be evaluated.
	if (isQuoted()) return;

	_is_exclusive = true;
}

// ==============================================================

void PatternTerm::addUnorderedBelow()
{
	if (_has_unordered_below) return;

	_has_any_unordered_link = true;
	_has_unordered_below = true;
	if (_parent->_handle)
		_parent->addUnorderedBelow();
}

void PatternTerm::addUnorderedLink()
{
	if (_has_any_unordered_link) return;

	_has_any_unordered_link = true;
	if (_parent->_handle)
		_parent->addUnorderedBelow();
}

// ==============================================================

void PatternTerm::markLiteral()
{
	if (_is_literal) return;

	_is_literal = true;
	_has_evaluatable = false;
	_has_any_evaluatable = false;
	for (PatternTermPtr& ptm : getOutgoingSet())
		ptm->markLiteral();
}

// ==============================================================

void PatternTerm::markPresent()
{
	// If its literal, its effectively quoted, so cannot be present.
	if (_is_literal or isQuoted()) return;

	_is_present = true;

	// By definition, everything underneath is literal
	for (PatternTermPtr& ptm : getOutgoingSet())
		ptm->markLiteral();
}

// ==============================================================

void PatternTerm::markAbsent()
{
	// If quoted, it cannot be interpreted as absent.
	if (isQuoted()) return;

	_is_absent = true;
}

// ==============================================================

void PatternTerm::markChoice()
{
	// If its literal, its effectively quoted, so cannot be a choice.
	if (_is_literal or isQuoted()) return;

	_is_choice = true;

	// By definition, everything underneath is present, or literal
	for (PatternTermPtr& ptm : getOutgoingSet())
	{
		if (not ptm->isPresent()) ptm->markLiteral();
	}

	// Everything above holds a Choice.
	_has_choice = true;
	PatternTermPtr pnt = _parent;
	while (pnt and pnt->_handle)
	{
		pnt->_has_choice = true;
		pnt = pnt->_parent;
	}
}

// ==============================================================

void PatternTerm::markAlways()
{
	// If its quoted, it cannot be always.
	if (isQuoted()) return;

	_is_always = true;
}

// ==============================================================

std::string PatternTerm::to_short_string() const { return to_string(": "); }

std::string PatternTerm::to_short_string(const std::string& sep) const
{
	// Term is null-terminated at the top.
	// Top term never has a handle in it.
	if (not _handle) return "-";
	std::string str = _parent->to_short_string(sep);
	str += sep + _handle->id_to_string();
	return str;
}

std::string PatternTerm::flag_string() const
{
	std::string str;
	if (isQuoted()) str += "QUOTED!: ";
	if (_has_any_bound_var) str += "HABV: ";
	if (_has_bound_var) str += "HBV: ";
	if (_is_bound_var) str += "BV: ";
	if (_has_any_globby_var) str += "HAGV: ";
	if (_has_globby_var) str += "HGV: ";
	if (_is_globby_var) str += "GV: ";
	if (_has_any_anon_var) str += "HANV: ";
	if (_has_anon_var) str += "HNV: ";
	if (_is_anon_var) str += "NV: ";
	if (_has_any_evaluatable) str += "HE: ";
	if (_has_evaluatable) str += "E: ";
	if (_is_virtual) str += "V: ";
	if (_is_identical) str += "I: ";
	if (_has_any_unordered_link) str += "U: ";
	if (_is_literal) str += "L: ";
	if (_is_present) str += "P: ";
	if (_is_absent) str += "A: ";
	if (_is_choice) str += "C: ";
	if (_has_choice) str += "HC: ";
	if (_is_always) str += "ALW: ";
	str += _handle->id_to_string();
	return str;
}

std::string PatternTerm::to_string() const { return to_string(""); }

std::string PatternTerm::to_string(const std::string& indent) const
{
	// Term is null-terminated at the top.
	// Top term never has a handle in it.
	if (not _handle) return "\n";
	std::string str = _parent->to_string(indent + "   ");
	str += indent;
	str += nameserver().getTypeName(getQuote()->get_type()) + " : ";
	str += flag_string() + "\n";
	return str;
}

std::string PatternTerm::to_full_string() const { return to_full_string(""); }

std::string PatternTerm::to_full_string(const std::string& indent) const
{
	if (getQuote()->is_node())
	{
		std::string str = getQuote()->to_short_string(indent);
		str += "\t; " + flag_string() + "\n";
		return str;
	}

	std::string str = indent;
	std::string more_indent = indent + "  "; // two spaces
	str += "(" + nameserver().getTypeName(getHandle()->get_type());
	str += "\t; " + flag_string() + "\n";
	for (const PatternTermPtr& ptm: getOutgoingSet())
	{
		if (str.back() == ')') str += "\n";
		str += ptm->to_full_string(more_indent);
	}
	if (str.back() != ')') str += indent;
	str += ")";
	return str;
}

std::string oc_to_string(const PatternTerm& pt, const std::string& indent)
{
	return pt.to_string(indent);
}

std::string oc_to_string(const PatternTermPtr& ptr, const std::string& indent)
{
	return ptr->to_string();
}

std::string oc_to_string(const PatternTermSeq& pts, const std::string& indent)
{
	std::string str;
	size_t i=0;
	str += "PatternTermSeq has " + std::to_string(pts.size()) + " terms\n";
	for (const PatternTermPtr& ptm : pts)
	{
		str += indent + "term[" + std::to_string(i) + "]:\n";
		str += ptm->to_full_string(indent + "  ") + "\n";
		i++;
	}
	return str;
}

std::string oc_to_string(const PatternTermSet& pts, const std::string& indent)
{
	std::string str;
	size_t i=0;
	str += "PatternTermSet has " + std::to_string(pts.size()) + " terms\n";
	for (const PatternTermPtr& ptm : pts)
	{
		str += indent + "term[" + std::to_string(i) + "]:\n";
		str += ptm->to_full_string(indent + "  ") + "\n";
		i++;
	}
	return str;
}

} // ~namespace opencog