301 lines
9.6 KiB
C++
301 lines
9.6 KiB
C++
/*
|
|
BINDING.hpp - pattern defining a specific attachment to the Advice system
|
|
|
|
Copyright (C) Lumiera.org
|
|
2010, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
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 General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
/** @file binding.hpp
|
|
** A pattern to define and identify a specific attachment to the Advice system.
|
|
** This pattern is comprised of a predicate list and intended to be matched or unified
|
|
** against a similar pattern associated with the attachment of a possible collaboration partner.
|
|
** Semantically, this list of atoms forms an conjunction of predicates to be resolved against
|
|
** similar predicates of the partner. Informally, when two entities attach to the Advice system,
|
|
** each specifying a binding, they can be paired up if any condition included into the binding
|
|
** holds true for both sides.
|
|
**
|
|
** Typically, a binding includes a \em type-guard predicate \c advice.type.xx where \c xx is an
|
|
** identifier denoting a type used within an instantiation of the Advice collaboration, i.e. a type
|
|
** used as advice value in a instantiation of the PointOfAdvice<AD> template. Besides the type guard,
|
|
** a binding may narrow down the topic of the advice by providing further predicates. This allows for
|
|
** Advice collaborations targeted at a more specific topic. The goal and intention behind this Advice
|
|
** system is to allow collaboration of entities without requiring them to be tightly coupled. Indeed,
|
|
** the only dependency besides the common type used as advice is to know any specific topic used
|
|
** within the binding. Thus, and advisor entity could put up a piece of advice, i.e. a value of
|
|
** the advice type, and another client entity (the advised entity) could pick up this value
|
|
** without the need to now anything about the advisor.
|
|
**
|
|
** \par implementation notes
|
|
** Any binding will be normalised prior to further processing. This normalisation is based
|
|
** on ordering by predicate symbol and arity. Patterns just comprised of constant symbols
|
|
** (nullary atoms) can even be condensed into a single hash value, which allows for fast
|
|
** match checking. For each pattern, we provide a matcher functor, allowing to check
|
|
** a match against this pattern. In case of the mentioned symbol-only patterns,
|
|
** this matcher will just hold the hash value of the normalised pattern.
|
|
**
|
|
** The advice system uses a binding index datastructure to keep track of any participating
|
|
** patterns and especially of the matching pairs. Actually, this datastructure needs to store
|
|
** only these matcher functors; thus, for a new binding to be used within the advice system,
|
|
** the symbolic definition is parsed, then normalised and finally, after creating the matcher
|
|
** functor, the full pattern definition can be discarded.
|
|
**
|
|
** @note as of 4/2010 this is an experimental setup and implemented just enough to work out
|
|
** the interfaces. Ichthyo expects this collaboration service to play a central role
|
|
** at various places within proc-layer.
|
|
** @todo for now, \em only the case of a completely constant (ground) pattern is implemented.
|
|
** Later we may consider to extend the binding patterns to allow variables, which, on match
|
|
** could be fed as parameters to the bound advice. But this extension requires to extend
|
|
** the simple hash-based match check to an actual unification of the patterns. ///TICKET #615
|
|
**
|
|
** @see advice.hpp
|
|
** @see advice-binding-pattern-test.cpp
|
|
**
|
|
*/
|
|
|
|
|
|
#ifndef LIB_ADVICE_BINDING_H
|
|
#define LIB_ADVICE_BINDING_H
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
#include "lib/symbol.hpp"
|
|
#include "lib/query.hpp"
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <set>
|
|
|
|
namespace lib {
|
|
namespace advice {
|
|
|
|
using std::string;
|
|
|
|
typedef size_t HashVal;
|
|
|
|
LUMIERA_ERROR_DECLARE (BINDING_PATTERN_SYNTAX); ///< Unable to parse the given binding pattern definition
|
|
|
|
|
|
|
|
/**
|
|
* Conjunction of predicates to be matched
|
|
* against a collaboration partner for establishing
|
|
* an Advice connection. The binding is defined by a
|
|
* textual spec in prolog-like syntax. The internal
|
|
* representation is immediately \em normalised.
|
|
* Typically the goal is just to create a #Matcher
|
|
* (Functor) to be stored for later match checks
|
|
* TODO type comment
|
|
*/
|
|
class Binding
|
|
{
|
|
|
|
/**
|
|
* single predicate
|
|
* as part of an advice binding pattern
|
|
*/
|
|
class Atom
|
|
{
|
|
uint ari_;
|
|
string sym_;
|
|
string arg_;
|
|
|
|
public:
|
|
explicit
|
|
Atom (string const& symbol ="nil", uint arity =0, string const& arg ="")
|
|
: ari_(arity), sym_(symbol), arg_(arg)
|
|
{ }
|
|
|
|
string const& sym() const { return sym_; }
|
|
string const& arg() const { return arg_; }
|
|
uint arity() const { return ari_; }
|
|
|
|
operator string() const;
|
|
|
|
bool
|
|
identical (Atom const& oa) const
|
|
{
|
|
return ari_ == oa.ari_
|
|
&& sym_ == oa.sym_
|
|
&& arg_ == oa.arg_;
|
|
}
|
|
|
|
int
|
|
compare (Atom const& oa) const ///< @note when #compare returns 0, the corresponding Atom counts as duplicate
|
|
{
|
|
int res;
|
|
if (0 != (res=sym().compare (oa.sym()))) return res;
|
|
if (0 != (res=arity() - (oa.arity()))) return res;
|
|
return arg().compare (oa.arg()); /////TODO: in the final version, when we'll allow for variable arguments
|
|
} /////////: and unification, then variable arguments must not be part of
|
|
/////////: the comparison, otherwise the matching by hash will break!
|
|
friend bool
|
|
operator< (Atom const& a1, Atom const& a2)
|
|
{
|
|
return a1.compare(a2) < 0;
|
|
}
|
|
};
|
|
|
|
|
|
typedef std::set<Atom> NormalisedAtoms;
|
|
|
|
NormalisedAtoms atoms_;
|
|
|
|
|
|
public:
|
|
/**
|
|
* Functor object for matching against another Binding.
|
|
* Contains precompiled information necessary for
|
|
* determining a match.
|
|
* @note Matcher is POD, copyable, no-throw
|
|
*/
|
|
class Matcher
|
|
{
|
|
HashVal bindingHash_;
|
|
|
|
|
|
Matcher (HashVal ha)
|
|
: bindingHash_(ha)
|
|
{ }
|
|
|
|
friend class Binding;
|
|
|
|
|
|
public:
|
|
bool matches (Binding const& obi) const;
|
|
bool matches (Binding::Matcher const& oma) const;
|
|
|
|
friend HashVal hash_value (Matcher const&);
|
|
};
|
|
|
|
|
|
|
|
|
|
/** create the empty binding, equivalent to \c true */
|
|
Binding();
|
|
|
|
/** create the binding as defined by the given textual definition.
|
|
* @note implicit type conversion deliberately intended */
|
|
Binding (Literal spec);
|
|
|
|
/*-- Binding is default copyable --*/
|
|
|
|
/** extend the definition of this binding
|
|
* by adding a predicate according to the
|
|
* given textual definition */
|
|
void addPredicate (Literal spec);
|
|
|
|
template<typename TY>
|
|
Binding const& addTypeGuard();
|
|
|
|
|
|
Matcher buildMatcher() const;
|
|
HashVal calculateHash() const;
|
|
|
|
operator string() const;
|
|
|
|
friend bool operator== (Binding const&, Binding const&);
|
|
|
|
|
|
private:
|
|
/** internal: parse into atoms, and insert them */
|
|
void parse_and_append (Literal def);
|
|
};
|
|
|
|
|
|
inline std::ostream&
|
|
operator<< (std::ostream& os, Binding const& bi)
|
|
{
|
|
return os << string(bi);
|
|
}
|
|
|
|
template<typename TY>
|
|
inline Binding const&
|
|
Binding::addTypeGuard()
|
|
{
|
|
atoms_.insert (Atom ("advice.type."+lumiera::query::buildTypeID<TY>()));
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* === equality comparison and matching === */
|
|
|
|
|
|
inline bool
|
|
operator!= (Binding const& b1, Binding const& b2)
|
|
{
|
|
return ! (b1 == b2);
|
|
}
|
|
|
|
|
|
inline bool
|
|
matches (Binding const& b1, Binding const& b2)
|
|
{
|
|
return b1.buildMatcher().matches (b2);
|
|
}
|
|
|
|
inline bool
|
|
matches (Binding::Matcher const& m1, Binding::Matcher const& m2)
|
|
{
|
|
return m1.matches (m2);
|
|
}
|
|
|
|
|
|
inline Binding::Matcher
|
|
Binding::buildMatcher() const
|
|
{
|
|
return Matcher (this->calculateHash());
|
|
}
|
|
|
|
|
|
|
|
/* == access hash values used for matching == */
|
|
|
|
inline bool
|
|
Binding::Matcher::matches (Binding const& obi) const
|
|
{
|
|
return bindingHash_ == obi.calculateHash();
|
|
}
|
|
|
|
inline bool
|
|
Binding::Matcher::matches (Binding::Matcher const& oma) const
|
|
{
|
|
return bindingHash_ == hash_value(oma);
|
|
}
|
|
|
|
inline HashVal
|
|
hash_value (Binding::Matcher const& bm)
|
|
{
|
|
return bm.bindingHash_;
|
|
}
|
|
|
|
inline HashVal
|
|
hash_value (Binding const& bi)
|
|
{
|
|
return bi.calculateHash();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::advice
|
|
#endif
|