Implement parsing of Advice binding pattern spec

This commit is contained in:
Fischlurch 2010-04-14 07:02:40 +02:00
parent f2269b7e78
commit 85f8035f92
6 changed files with 163 additions and 43 deletions

View file

@ -22,15 +22,27 @@
#include "lib/advice/binding.hpp"
#include "lib/symbol.hpp"
//#include <tr1/functional_hash.h>
#include <boost/functional/hash.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
using lib::Literal;
using boost::algorithm::join;
using boost::lexical_cast;
using boost::regex;
using boost::smatch;
//using boost::regex_search;
using boost::sregex_iterator;
using boost::match_continuous;
using boost::hash_combine;
@ -41,19 +53,44 @@ namespace advice {
Binding::Atom::operator string() const
{
return sym_+"/"+lexical_cast<string> (ari_)
+"("+arg_+")";
/////////////////////TICKET #613 : centralise generally useful RegExps
namespace{ // Implementation details
const string matchSym = "(\\w[\\w_\\.\\-]*)";
const string matchArg = "\\(\\s*"+matchSym+"?\\s*\\)";
regex findPredicate ("\\s*"+matchSym+"("+matchArg+")?\\s*,?"); ///< \c sym(arg), groups: [symbol, parenthesis, argument symbol]
/** detect the \em arity of an predicate, as matched by #findPredicate.
* Currently, we don't really parse predicate logic notation and thus we
* just distinguish nullary predicates (no argument) and predicates with
* one single constant argument. */
inline uint
detectArity (smatch const& match)
{
if (!match[2].matched) return 0; // no parenthesis at all
if (!match[3].matched) return 0; // empty parenthesis
// later we could analyse the argument in detail here...
return 1; // but now we just accept a single constant symbol
}
}
void
Binding::parse_and_append (string def)
{
UNIMPLEMENTED ("do the actual parsing by regexp, create an Atom for each match");
Binding::parse_and_append (Literal lit)
{
string def(lit);
sregex_iterator end;
sregex_iterator pos (def.begin(),def.end(), findPredicate,
match_continuous); // continuous: don't allow garbage *not* matched by the RegExp
while (pos != end)
{
smatch match = *pos;
atoms_.insert (Atom (match[1], detectArity(match), match[3]));
++pos;
}
}
Binding::Binding ()
: atoms_()
@ -61,6 +98,7 @@ namespace advice {
Binding::Binding (Literal spec)
{
REQUIRE (spec);
parse_and_append (spec);
}
@ -68,6 +106,7 @@ namespace advice {
void
Binding::addPredicate (Literal spec)
{
REQUIRE (spec);
parse_and_append (spec);
}
@ -75,7 +114,25 @@ namespace advice {
Binding::operator string() const
{
return "Binding[" + join(atoms_, ", ") + "]";
string repr("Binding[");
typedef NormalisedAtoms::const_iterator AIter;
AIter end = atoms_.end();
AIter pos = atoms_.begin();
for ( ; pos!=end ; ++pos)
repr += string(*pos)+", ";
if (0 < atoms_.size())
repr.resize(repr.size()-2);
repr += "]";
return repr;
}
Binding::Atom::operator string() const
{
return sym_+"/"+lexical_cast<string> (ari_)
+"("+arg_+")";
}
@ -98,5 +155,34 @@ namespace advice {
}
/** bindings are considered equivalent if, after normalisation,
* their respective definitions are identical.
* @note for bindings without variable arguments, equivalence and matching
* always yield the same results. Contrary to this, two bindings with
* some variable arguments could match, without being defined identically.
* For example \c pred(X) matches \c pred(u) or any other binding of the
* form \c pred(<constant_value>)
*/
bool
operator== (Binding const& b1, Binding const& b2)
{
if (b1.atoms_.size() != b2.atoms_.size())
return false;
ASSERT (b1.atoms_.size() == b2.atoms_.size());
typedef Binding::NormalisedAtoms::const_iterator Iter;
Iter end = b1.atoms_.end();
Iter p1 = b1.atoms_.begin();
Iter p2 = b2.atoms_.begin();
for ( ; p1!=end; ++p1, ++p2 )
if (! p1->identical(*p2))
return false;
return true;
}
}} // namespace lib::advice

View file

@ -67,6 +67,7 @@
//#include "lib/hash-indexed.hpp"
//#include "lib/util.hpp"
#include "lib/symbol.hpp"
#include "lib/query.hpp"
//#include <boost/operators.hpp>
//#include <tr1/memory>
@ -103,10 +104,9 @@ namespace advice {
string arg_;
public:
Atom (uint arity, string const& symbol, string const& arg ="")
: ari_(arity)
, sym_(symbol)
, arg_(arg)
explicit
Atom (string const& symbol ="nil", uint arity =0, string const& arg ="")
: ari_(arity), sym_(symbol), arg_(arg)
{ }
string const& sym() const { return sym_; }
@ -115,15 +115,23 @@ namespace advice {
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
compare (Atom const& oa) const ///< @note when #compare returns 0, the corresponding Atom counts as duplicate
{
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!
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)
{
@ -188,10 +196,12 @@ namespace advice {
operator string() const;
friend bool operator== (Binding const&, Binding const&);
private:
/** internal: parse into atoms, and insert them */
void parse_and_append (string def);
void parse_and_append (Literal def);
};
@ -205,7 +215,7 @@ namespace advice {
inline void
Binding::addTypeGuard()
{
UNIMPLEMENTED ("create a new predicate spec to denote that this binding is related to the given advice type");
atoms_.insert (Atom ("advice.type."+lumiera::query::buildTypeID<TY>()));
}
@ -213,19 +223,6 @@ namespace advice {
/* === equality comparison and matching === */
/** bindings are considered equivalent if, after normalisation,
* their respective definitions are identical.
* @note for bindings without variable arguments, equivalence and matching
* always yield the same results. Contrary to this, two bindings with
* some variable arguments could match, without being defined identically.
* For example \c pred(X) matches \c pred(u) or any other binding of the
* form \c pred(<constant_value>)
*/
inline bool
operator== (Binding const& b1, Binding const& b2)
{
UNIMPLEMENTED ("equality of bindings, based on term by term comparison");
}
inline bool
operator!= (Binding const& b1, Binding const& b2)

View file

@ -35,9 +35,6 @@ using boost::algorithm::split;
using boost::algorithm::join;
using boost::algorithm::is_any_of;
using boost::algorithm::token_compress_on;
using boost::regex;
using boost::smatch;
using boost::regex_search;
using boost::regex;
using boost::smatch;

View file

@ -69,7 +69,7 @@ namespace lumiera {
//////////////////////TICKET #613 : centralise generally useful RegExps
namespace{ // Implementation details
map<Symbol, regex> regexTable;

View file

@ -85,5 +85,16 @@ namespace lumiera {
const string removeTerm (Symbol, string& termString);
template<typename TY>
const string
buildTypeID()
{
string typeID (typeid(TY).name());
normaliseID (typeID);
return typeID;
}
}} // namespace lumiera::query
#endif

View file

@ -88,6 +88,7 @@ namespace test {
virtual void
run (Arg)
{
verifyPatternSyntax();
verifyPatternNormalisation();
verifyStaticMatch();
verifyPreparedMatch();
@ -95,12 +96,42 @@ namespace test {
}
void
verifyPatternSyntax()
{
#define _PARSE_AND_SHOW(_STR_) \
cout << _STR_ << "\t--->" << Binding(_STR_) << endl;
_PARSE_AND_SHOW ("");
_PARSE_AND_SHOW ("aSymbol");
_PARSE_AND_SHOW ("aSymbol followed by Garbage : §&Ω%€GΩ%€ar☠☠☠baäääääge");
_PARSE_AND_SHOW ("§&Ω%€GΩ%€ar☠☠☠baäääääge");
_PARSE_AND_SHOW ("a, list , of ,symbols.");
_PARSE_AND_SHOW ("nullary().");
_PARSE_AND_SHOW ("nullary( )");
_PARSE_AND_SHOW ("nothing ()");
_PARSE_AND_SHOW ("predicate( with-argument )");
_PARSE_AND_SHOW ("no( valid definition here)");
Binding testBinding;
testBinding.addTypeGuard<AdviceBindingPattern_test>();
testBinding.addPredicate("one two(), three( four ).");
cout << testBinding << endl;
}
void
verifyPatternNormalisation()
{
Binding b0, b00;
Binding b1 ("cat1(), cat2().");
Binding b2 (" cat2 ( ),cat1 . ");
Binding b2 (" cat2( ) cat1 ****");
cout << "b0==" << b0 << endl;
cout << "b1==" << b1 << endl;
cout << "b2==" << b2 << endl;
CHECK (b0 == b00); CHECK (b00 == b0);
CHECK (b1 == b2); CHECK (b2 == b1);
@ -115,13 +146,11 @@ namespace test {
b1.addTypeGuard<lumiera::Time>();
CHECK (b1 != b2);
b1.addPredicate(" cat3 ( zzz ) ");
b1.addPredicate(" cat3( zzz ) ");
CHECK (b1 != b2);
b2.addTypeGuard<lumiera::Time>();
CHECK (b1 == b2);
cout << "b0==" << b0 << endl;
cout << "b1==" << b1 << endl;
cout << "b2==" << b2 << endl;
}