Implement parsing of Advice binding pattern spec
This commit is contained in:
parent
f2269b7e78
commit
85f8035f92
6 changed files with 163 additions and 43 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ namespace lumiera {
|
|||
|
||||
|
||||
|
||||
|
||||
//////////////////////TICKET #613 : centralise generally useful RegExps
|
||||
namespace{ // Implementation details
|
||||
|
||||
map<Symbol, regex> regexTable;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue