From 85f8035f928d51e2f7051008108564fb1ddb127e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 14 Apr 2010 07:02:40 +0200 Subject: [PATCH] Implement parsing of Advice binding pattern spec --- src/lib/advice/binding.cpp | 104 ++++++++++++++++-- src/lib/advice/binding.hpp | 49 ++++----- src/lib/cmdline.cpp | 3 - src/lib/query.cpp | 2 +- src/lib/query.hpp | 11 ++ .../advice/advice-binding-pattern-test.cpp | 37 ++++++- 6 files changed, 163 insertions(+), 43 deletions(-) diff --git a/src/lib/advice/binding.cpp b/src/lib/advice/binding.cpp index 8944064e4..94faf762b 100644 --- a/src/lib/advice/binding.cpp +++ b/src/lib/advice/binding.cpp @@ -22,15 +22,27 @@ #include "lib/advice/binding.hpp" +#include "lib/symbol.hpp" //#include #include #include #include +#include + + +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 (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 (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() + */ + 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 diff --git a/src/lib/advice/binding.hpp b/src/lib/advice/binding.hpp index 84ffcba5e..9a487ead1 100644 --- a/src/lib/advice/binding.hpp +++ b/src/lib/advice/binding.hpp @@ -67,6 +67,7 @@ //#include "lib/hash-indexed.hpp" //#include "lib/util.hpp" #include "lib/symbol.hpp" +#include "lib/query.hpp" //#include //#include @@ -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())); } @@ -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() - */ - 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) diff --git a/src/lib/cmdline.cpp b/src/lib/cmdline.cpp index 9f4d915ef..90335230e 100644 --- a/src/lib/cmdline.cpp +++ b/src/lib/cmdline.cpp @@ -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; diff --git a/src/lib/query.cpp b/src/lib/query.cpp index 61e376c5e..ab143cd0b 100644 --- a/src/lib/query.cpp +++ b/src/lib/query.cpp @@ -69,7 +69,7 @@ namespace lumiera { - + //////////////////////TICKET #613 : centralise generally useful RegExps namespace{ // Implementation details map regexTable; diff --git a/src/lib/query.hpp b/src/lib/query.hpp index e43f34724..99bba9ec9 100644 --- a/src/lib/query.hpp +++ b/src/lib/query.hpp @@ -85,5 +85,16 @@ namespace lumiera { const string removeTerm (Symbol, string& termString); + template + const string + buildTypeID() + { + string typeID (typeid(TY).name()); + normaliseID (typeID); + return typeID; + } + + + }} // namespace lumiera::query #endif diff --git a/tests/lib/advice/advice-binding-pattern-test.cpp b/tests/lib/advice/advice-binding-pattern-test.cpp index 827d050db..61f1f06e1 100644 --- a/tests/lib/advice/advice-binding-pattern-test.cpp +++ b/tests/lib/advice/advice-binding-pattern-test.cpp @@ -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(); + 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(); CHECK (b1 != b2); - b1.addPredicate(" cat3 ( zzz ) "); + b1.addPredicate(" cat3( zzz ) "); CHECK (b1 != b2); b2.addTypeGuard(); CHECK (b1 == b2); - cout << "b0==" << b0 << endl; - cout << "b1==" << b1 << endl; cout << "b2==" << b2 << endl; }