Implement Advice binding pattern

This commit is contained in:
Fischlurch 2010-04-13 06:37:21 +02:00
parent f27024172f
commit f2269b7e78
12 changed files with 274 additions and 199 deletions

View file

@ -24,6 +24,14 @@
#include "lib/advice/binding.hpp"
//#include <tr1/functional_hash.h>
#include <boost/functional/hash.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/lexical_cast.hpp>
using boost::algorithm::join;
using boost::lexical_cast;
using boost::hash_combine;
namespace lib {
@ -31,39 +39,62 @@ namespace advice {
// using std::tr1::hash;
// LUMIERA_ERROR_DEFINE (MISSING_INSTANCE, "Existing ID registration without associated instance");
Binding::Atom::operator string() const
{
return sym_+"/"+lexical_cast<string> (ari_)
+"("+arg_+")";
}
void
Binding::parse_and_append (string def)
{
UNIMPLEMENTED ("do the actual parsing by regexp, create an Atom for each match");
}
/* ohlolololohaha */
Binding::Binding ()
{
UNIMPLEMENTED ("create a new empty binding");
}
: atoms_()
{ }
Binding::Binding (Literal spec)
{
UNIMPLEMENTED ("parse the spec and create a new binding");
parse_and_append (spec);
}
void
Binding::addPredicate (Literal spec)
{
UNIMPLEMENTED ("parse the given spec and create an additional predicte, then re-normalise");
parse_and_append (spec);
}
Binding::operator string() const
{
UNIMPLEMENTED ("diagnostic string representation of an advice binding");
return "Binding[" + join(atoms_, ", ") + "]";
}
HashVal
Binding::calculateHash() const
{
UNIMPLEMENTED ("calculate the hash for a normalised advice binding");
HashVal hash=0;
typedef NormalisedAtoms::const_iterator AIter;
AIter pos = atoms_.begin();
AIter end = atoms_.end();
for ( ; pos!=end ; ++pos)
{
hash_combine (hash, pos->sym());
hash_combine (hash, pos->arity());
hash_combine (hash, pos->arg()); //////////////TODO: not in final version with variable arguments
}
return hash;
}

View file

@ -72,6 +72,7 @@
//#include <tr1/memory>
#include <iostream>
#include <string>
#include <set>
namespace lib {
namespace advice {
@ -91,6 +92,51 @@ namespace advice {
class Binding
{
/**
* single predicate
* as part of an advice binding pattern
*/
class Atom
{
uint ari_;
string sym_;
string arg_;
public:
Atom (uint arity, string const& symbol, 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;
int
compare (Atom const& oa) const
{
int res;
if (0 != (res=sym().compare (oi.sym()))) return res;
if (0 != (res=arity() - (oi.arity()))) return res;
return arg().compare (oi.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.
@ -144,7 +190,8 @@ namespace advice {
private:
void normalise(); ////TODO necessary??
/** internal: parse into atoms, and insert them */
void parse_and_append (string def);
};

View file

@ -47,8 +47,8 @@ using boost::regex_search;
#include <iostream>
namespace util
{
namespace util {
/** create as a tokenised <i>copy</i> of the current commandline.
* Note that argv[0] is always ignored. */

View file

@ -66,12 +66,12 @@ namespace lumiera {
if (is_upper (first))
id[0] = std::tolower (first);
}
namespace // Implementation details
{
namespace{ // Implementation details
map<Symbol, regex> regexTable;
Symbol matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\),?\\s*";
@ -128,7 +128,7 @@ namespace lumiera {
* probably get for free when we embed a prolog system)...
*/
uint
countPraed (const string& q)
countPred (const string& q)
{
uint cnt (0);
sregex_iterator end;

View file

@ -77,7 +77,7 @@ namespace lumiera {
* usable for ordering queries, as more predicates usually
* mean more conditions, i.e. more constriction
*/
uint countPraed (const string&);
uint countPred (const string&);
const string extractID (Symbol, const string& termString);

View file

@ -183,7 +183,7 @@ namespace asset {
const uint ver=1);
int compare (const Ident& other) const;
int compare (Ident const& other) const;
/** @note equality ignores version differences */
bool operator== (Ident const& oi) const { return compare (oi) ==0; }
@ -308,7 +308,7 @@ namespace asset {
* forwarded to the Asset comparison operators.
* @note version info is irrelevant */
inline int
Asset::Ident::compare (const Asset::Ident& oi) const
Asset::Ident::compare (Asset::Ident const& oi) const
{
int res;
if (0 != (res=category.compare (oi.category))) return res;
@ -320,7 +320,7 @@ namespace asset {
/** promote subtype-ptr to PAsset, e.g. for comparing */
template<class A>
inline const PcAsset
pAsset (const shared_ptr<A>& subPtr)
pAsset (shared_ptr<A> const& subPtr)
{
return static_pointer_cast<const Asset,A> (subPtr);
}
@ -336,7 +336,7 @@ namespace asset {
/** convenient for debugging */
inline string str (const PcAsset& a)
inline string str (PcAsset const& a)
{
if (a)
return string (*a.get());

View file

@ -96,7 +96,7 @@ namespace mobject {
weak_ptr<TAR> objRef;
Record (const Query<TAR>& q, const P<TAR>& obj)
: degree (lumiera::query::countPraed (q)),
: degree (lumiera::query::countPred (q)),
query (q),
objRef (obj)
{ }

View file

@ -27,6 +27,6 @@ return: 0
END
TEST "count predicates in query" QueryUtils_test countPraed <<END
TEST "count predicates in query" QueryUtils_test countPred <<END
return: 0
END

View file

@ -208,7 +208,7 @@ namespace mobject {
ASSERT ( *j );
Q23 qx ((*j)->instanceID);
ASSERT ( ps[qx] == (*j));
d = lumiera::query::countPraed (qx);
d = lumiera::query::countPred (qx);
ASSERT ( d_prev <= d );
d_prev = d;
}

View file

@ -0,0 +1,169 @@
/*
QueryUtils(Test) - checking various utils provided for dealing with config queries
Copyright (C) Lumiera.org
2008, 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.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/util.hpp"
#include "lib/util-foreach.hpp"
#include "lib/cmdline.hpp"
#include "lib/query.hpp"
#include "query/querydiagnostics.hpp"
#include <tr1/functional>
#include <iostream>
using lumiera::Query;
using util::Cmdline;
using util::isnil;
using util::contains;
using util::for_each;
using std::tr1::placeholders::_1;
using std::tr1::bind;
using std::string;
using std::cout;
using std::endl;
namespace lumiera {
namespace query {
namespace test{
struct Thing
{
virtual ~Thing() {} // add RTTI for Query.asKey();
};
/************************************************************************
* @test check the various small helpers and utilities we utilise
* for dealing with ConfigQuery
*/
class QueryUtils_test : public Test
{
virtual void
run (Arg arg)
{
if (isnil(arg)) arg = Cmdline ("Query normaliseID extractID removeTerm countPred");
if (contains (arg, "Query" )) check_Query ();
if (contains (arg, "normaliseID")) check_normaliseID();
if (contains (arg, "extractID" )) check_extractID ();
if (contains (arg, "removeTerm" )) check_removeTerm ();
if (contains (arg, "countPred" )) check_countPred ();
}
/** @test Query wrapper class basics */
void
check_Query ()
{
cout << Query<Thing> ("I am writing a test sentence.").asKey() << endl;
}
/** @test sanitising and normalising various tokens */
void
check_normaliseID ()
{
Cmdline tokens ("a A AA dufte 1a _1 A_A BÄH");
tokens.push_back ("");
tokens.push_back (" White space ");
tokens.push_back ("§&Ω%€GΩ%€ar ☠☠☠ baäääääge!!!!! ");
cout << "..original : " << tokens << " :"<<endl;
for_each (tokens, normaliseID, _1 );
cout << "normalised : " << tokens << " :"<<endl;
}
/** @test the simple regexp extracting a parameter token */
void
check_extractID ()
{
ASSERT ("tok" == extractID ("pred", "pred(tok)." ));
ASSERT ("tok" == extractID ("pred", " pred( tok )" ));
ASSERT ("tok" == extractID ("pred", "pred(tok), pred(tux)." ));
ASSERT ("tok" == extractID ("pred", "other(xyz) pred(tok) pred(tux)" ));
ASSERT ("tok" == extractID ("pred", "some( pred(tok)" ));
ASSERT (isnil (extractID ("pred", "pred (tok)")));
ASSERT (isnil (extractID ("pred", "pred tok)" )));
ASSERT (isnil (extractID ("pred", "pred(tok " )));
}
/** @test the regexp based cutting of a term with given symbol */
void
check_removeTerm ()
{
// successful------Symbol---input-string----------------------extracted------remaining-------------
ASSERT_removeTerm ("pred", "pred(tok).", "pred(tok)", "." );
ASSERT_removeTerm ("pred", " pred( tok )", "pred(tok)", " " );
ASSERT_removeTerm ("pred", "pred(tok), pred(tux).", "pred(tok)", "pred(tux)." );
ASSERT_removeTerm ("pred", "other(xyz) pred(tok) pred(tux)", "pred(tok)", "other(xyz) pred(tux)" );
ASSERT_removeTerm ("pred", "some( pred(tok)", "pred(tok)", "some( " );
// not successful
ASSERT_removeTerm ("pred", "pred (tok", "", "pred (tok" );
ASSERT_removeTerm ("pred", "pred tok)", "", "pred tok)" );
ASSERT_removeTerm ("pred", "pred(tok", "", "pred(tok" );
}
void
ASSERT_removeTerm (Symbol sym, string input, string extracted, string modified)
{
ASSERT (extracted == removeTerm (sym, input));
ASSERT (modified == input);
}
/** @test counting of predicates in a query
* (currently 4/08 regexp based...)
*/
void
check_countPred ()
{
for (uint i=1; i <= 30; ++i)
ASSERT ( i == countPred (garbage_query (i)));
}
};
/** Register this test class... */
LAUNCHER (QueryUtils_test, "unit query");
}}} // namespace lumiera::query::test

View file

@ -1,172 +0,0 @@
/*
QueryUtils(Test) - checking various utils provided for dealing with config queries
Copyright (C) Lumiera.org
2008, 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.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/util.hpp"
#include "lib/util-foreach.hpp"
#include "lib/cmdline.hpp"
#include "lib/query.hpp"
#include "query/querydiagnostics.hpp"
#include <tr1/functional>
#include <iostream>
using lumiera::Query;
using util::Cmdline;
using util::isnil;
using util::contains;
using util::for_each;
using std::tr1::placeholders::_1;
using std::tr1::bind;
using std::string;
using std::cout;
namespace lumiera {
namespace query {
namespace test{
struct Thing
{
virtual ~Thing() {} // add RTTI for Query.asKey();
};
/************************************************************************
* @test check the various small helpers and utilities we utilise
* for dealing with ConfigQuery
*/
class QueryUtils_test : public Test
{
virtual void
run (Arg arg)
{
if (isnil(arg)) arg = Cmdline ("Query normaliseID extractID removeTerm countPraed");
if (contains (arg, "Query" )) check_Query ();
if (contains (arg, "normaliseID")) check_normaliseID ();
if (contains (arg, "extractID" )) check_extractID ();
if (contains (arg, "removeTerm" )) check_removeTerm ();
if (contains (arg, "countPraed" )) check_countPraed ();
}
/** @test Query wrapper class basics */
void
check_Query ()
{
cout << Query<Thing> ("I am writing a test sentence.").asKey() << "\n";
}
/** @test sanitising and normalising various tokens */
void
check_normaliseID ()
{
Cmdline tokens ("a A AA dufte 1a _1 A_A BÄH");
tokens.push_back ("");
tokens.push_back (" White space ");
tokens.push_back ("§&Ω%€GΩ%€ar Ω baäääääge!!!!! ");
cout << "..original : " << tokens << " :\n";
for_each (tokens, bind ( &normaliseID, _1 ));
cout << "normalised : " << tokens << " :\n";
}
/** @test the simple regexp extracting a parameter token */
void
check_extractID ()
{
ASSERT ("tok" == extractID ("pred", "pred(tok)." ));
ASSERT ("tok" == extractID ("pred", " pred( tok )" ));
ASSERT ("tok" == extractID ("pred", "pred(tok), pred(tux)." ));
ASSERT ("tok" == extractID ("pred", "other(xyz) pred(tok) pred(tux)" ));
ASSERT ("tok" == extractID ("pred", "some( pred(tok)" ));
ASSERT (isnil (extractID ("pred", "pred (tok)")));
ASSERT (isnil (extractID ("pred", "pred tok)" )));
ASSERT (isnil (extractID ("pred", "pred(tok " )));
}
/** @test the regexp based cutting of a term with given symbol */
void
check_removeTerm ()
{
// successful------Symbol---input-string----------------------extracted------remaining-------------
ASSERT_removeTerm ("pred", "pred(tok).", "pred(tok)", "." );
ASSERT_removeTerm ("pred", " pred( tok )", "pred(tok)", " " );
ASSERT_removeTerm ("pred", "pred(tok), pred(tux).", "pred(tok)", "pred(tux)." );
ASSERT_removeTerm ("pred", "other(xyz) pred(tok) pred(tux)", "pred(tok)", "other(xyz) pred(tux)" );
ASSERT_removeTerm ("pred", "some( pred(tok)", "pred(tok)", "some( " );
// not successful
ASSERT_removeTerm ("pred", "pred (tok", "", "pred (tok" );
ASSERT_removeTerm ("pred", "pred tok)", "", "pred tok)" );
ASSERT_removeTerm ("pred", "pred(tok", "", "pred(tok" );
}
void
ASSERT_removeTerm (Symbol sym, string input, string extracted, string modified)
{
ASSERT (extracted == removeTerm (sym, input));
ASSERT (modified == input);
}
/** @test counting of predicates in a query
* (currently 4/08 regexp based...)
*/
void
check_countPraed ()
{
for (uint i=1; i <= 30; ++i)
ASSERT ( i == countPraed (garbage_query (i)));
}
};
/** Register this test class... */
LAUNCHER (QueryUtils_test, "unit query");
} // namespace test
} // namespace query
} // namespace lumiera

View file

@ -514,8 +514,8 @@ ColorPalette
SiteUrl</pre>
</div>
<div title="Advice" modifier="Ichthyostega" modified="201004110126" created="200910311755" tags="Concepts def spec img" changecount="27">
<pre>{{red{WIP 11/09}}}...//about to explicate a pattern which I'm aiming at within the design almost since the beginning//
<div title="Advice" modifier="Ichthyostega" modified="201004130150" created="200910311755" tags="Concepts def spec img" changecount="30">
<pre>//pattern of collaboration for loosely coupled entities, to be used for various purposes within Proc...//
Expecting Advice and giving Advice &amp;mdash; this collaboration ranges somewhere between messaging and dynamic properties, but cross-cutting the primary, often hierarchical relation of dependencies. Always happening at a certain //point of advice,// which creates a distinct, static nature different of being just a convention, on the other hand, Advice is deliberately kept optional and received synchronously, albeit possibly within an continuation.
!Specification
@ -531,7 +531,7 @@ Expecting Advice and giving Advice &amp;mdash; this collaboration ranges somewhe
* ''advice system''
* the ''binding''
* the ''advice''
Usually, the ''advised'' entity opens the collaboration by requesting an advice. The ''advice'' itself is a piece of data of a custom type, which needs to be //copyable.// Obviously, both the advised and the advisor need to share knowledge about the meaning of this advice data. (in a more elaborate version we might allow the advisor to provide a subclass of the advice interface type). The actual advice collaboration happens at a ''point of advice'', which needs to be derived first. To this end, the advised puts up an request by providing his ''binding'', which is a pattern for matching. An entity about to give advice //opens//&amp;nbsp; possible ''advice channels'' by putting up an advisor binding, which similarly is a pattern. The ''advice system'' as mediator resolves both sides, by matching (which in the most general case could be an unification). This process creates an ''advice point solution'' &amp;mdash; allowing the advisor to fed the piece of advice into the advice channel, causing it to be placed into the point of advice. After passing a certain (implementation defined) break point, the advice leaves the influence of the advisor and gets exposed to the advised entities. Especially, this involves copying the advice data into a location managed by the advice system. In the standard case, the advised entity accesses the advice synchronously (an non-blocking). Of course, there could be a status flag to find out if there is new advice. Moreover, typically the advice data type is default constructible and thus there is always a basic form of advice available, thereby completely decoupling the advised entity from the timings related to this collaboration.
Usually, the ''advised'' entity opens the collaboration by requesting an advice. The ''advice'' itself is a piece of data of a custom type, which needs to be //copyable.// Obviously, both the advised and the advisor need to share knowledge about the meaning of this advice data. (in a more elaborate version we might allow the advisor to provide a subclass of the advice interface type). The actual advice collaboration happens at a ''point of advice'', which needs to be derived first. To this end, two prerequisites are to be fulfilled (without fixed sequence): The advised puts up an ''advice request'' by specifying his ''binding'', which is a pattern for matching. An entity about to give advice attaches a possible ''advice provision'', combined with an advisor binding, which similarly is a pattern. The ''advice system'' as mediator resolves both sides, by matching (which in the most general case could be an unification). This process creates an ''advice point solution'' &amp;mdash; allowing the advisor to fed the piece of advice into a kind of communication channel (&amp;raquo;advice channel&amp;laquo;), causing the advice data to be placed into the point of advice. After passing a certain (implementation defined) break point, the advice leaves the influence of the advisor and gets exposed to the advised entities. Especially, this involves copying the advice data into a location managed by the advice system. In the standard case, the advised entity picks up the advice synchronously (and non-blocking). Of course, there could be a status flag to find out if there is new advice. Moreover, typically the advice data type is default constructible and thus there is always a basic form of advice available, thereby completely decoupling the advised entity from the timings related to this collaboration.
!!extensions
In a more elaborate scheme, the advised entity could provide a signal to be invoked either in the thread context of the advisor (being still blocked in the advice providing call), or in a completely separate thread. A third solution would be to allow the advised entity to block until receiving new advice. Both of these more elaborate schemes would also allow to create an advice queue &amp;mdash; thereby developing the advice collaboration into a kind of messaging system. Following this route seems questionable though.