From f2269b7e7878b71163ac879a4db225f25160a03c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 13 Apr 2010 06:37:21 +0200 Subject: [PATCH] Implement Advice binding pattern --- src/lib/advice/binding.cpp | 49 ++++- src/lib/advice/binding.hpp | 49 ++++- src/lib/cmdline.cpp | 4 +- src/lib/query.cpp | 8 +- src/lib/query.hpp | 2 +- src/proc/asset.hpp | 8 +- src/proc/mobject/session/defsregistry.hpp | 2 +- tests/42query.tests | 2 +- .../mobject/session/defsregistryimpltest.cpp | 2 +- tests/lib/query/query-utils-test.cpp | 169 +++++++++++++++++ tests/lib/query/queryutilstest.cpp | 172 ------------------ wiki/renderengine.html | 6 +- 12 files changed, 274 insertions(+), 199 deletions(-) create mode 100644 tests/lib/query/query-utils-test.cpp delete mode 100644 tests/lib/query/queryutilstest.cpp diff --git a/src/lib/advice/binding.cpp b/src/lib/advice/binding.cpp index f3429bacc..8944064e4 100644 --- a/src/lib/advice/binding.cpp +++ b/src/lib/advice/binding.cpp @@ -24,6 +24,14 @@ #include "lib/advice/binding.hpp" //#include +#include +#include +#include + +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 (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; } diff --git a/src/lib/advice/binding.hpp b/src/lib/advice/binding.hpp index 79b3face1..84ffcba5e 100644 --- a/src/lib/advice/binding.hpp +++ b/src/lib/advice/binding.hpp @@ -72,6 +72,7 @@ //#include #include #include +#include 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 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); }; diff --git a/src/lib/cmdline.cpp b/src/lib/cmdline.cpp index 89ebc2137..9f4d915ef 100644 --- a/src/lib/cmdline.cpp +++ b/src/lib/cmdline.cpp @@ -47,8 +47,8 @@ using boost::regex_search; #include -namespace util - { +namespace util { + /** create as a tokenised copy of the current commandline. * Note that argv[0] is always ignored. */ diff --git a/src/lib/query.cpp b/src/lib/query.cpp index 931d07ff4..61e376c5e 100644 --- a/src/lib/query.cpp +++ b/src/lib/query.cpp @@ -66,12 +66,12 @@ namespace lumiera { if (is_upper (first)) id[0] = std::tolower (first); } - - namespace // Implementation details - { + + namespace{ // Implementation details + map 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; diff --git a/src/lib/query.hpp b/src/lib/query.hpp index 65eba3869..e43f34724 100644 --- a/src/lib/query.hpp +++ b/src/lib/query.hpp @@ -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); diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index 4fe2aa14c..900877aa4 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -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 inline const PcAsset - pAsset (const shared_ptr& subPtr) + pAsset (shared_ptr const& subPtr) { return static_pointer_cast (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()); diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index f37f08109..0dc6242c5 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -96,7 +96,7 @@ namespace mobject { weak_ptr objRef; Record (const Query& q, const P& obj) - : degree (lumiera::query::countPraed (q)), + : degree (lumiera::query::countPred (q)), query (q), objRef (obj) { } diff --git a/tests/42query.tests b/tests/42query.tests index 664e2912a..5caf37bb1 100644 --- a/tests/42query.tests +++ b/tests/42query.tests @@ -27,6 +27,6 @@ return: 0 END -TEST "count predicates in query" QueryUtils_test countPraed <instanceID); ASSERT ( ps[qx] == (*j)); - d = lumiera::query::countPraed (qx); + d = lumiera::query::countPred (qx); ASSERT ( d_prev <= d ); d_prev = d; } diff --git a/tests/lib/query/query-utils-test.cpp b/tests/lib/query/query-utils-test.cpp new file mode 100644 index 000000000..07c1403dc --- /dev/null +++ b/tests/lib/query/query-utils-test.cpp @@ -0,0 +1,169 @@ +/* + QueryUtils(Test) - checking various utils provided for dealing with config queries + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 +#include + +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 ("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 << " :"< - - 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 -#include - -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 ("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 diff --git a/wiki/renderengine.html b/wiki/renderengine.html index fb64d3a2b..cbe454ed0 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -514,8 +514,8 @@ ColorPalette SiteUrl -
-
{{red{WIP 11/09}}}...//about to explicate a pattern which I'm aiming at within the design almost since the beginning//
+
+
//pattern of collaboration for loosely coupled entities, to be used for various purposes within Proc...//
 Expecting Advice and giving Advice &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 &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//&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'' &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'' &mdash; allowing the advisor to fed the piece of advice into a kind of communication channel (&raquo;advice channel&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 &mdash; thereby developing the advice collaboration into a kind of messaging system. Following this route seems questionable though.