lumiera_documentation/src/common/advice/binding.cpp
Ichthyostega 8ffab2f002 Dependencies: get rid of boost-regexp (see #995)
Mostly, std::regexp can be used as a drop-in replacement.

Note: unfortunately ECMA regexps do not support lookbehind assertions.
This lookbehind is necesary here because we want to allow parsing values
from strings with additional content, which means we need explicitly to
exclude mismatches due to invalid syntax.

We can work around that issue like "either line start, or *not* one of these characters.


Alternatively we could consider to make the match more rigid,
i.e we would require the string to conain *only* the timecode spec to be parsed.
2019-06-24 02:41:02 +02:00

203 lines
6.1 KiB
C++

/*
Binding - pattern defining a specific attachment to the Advice system
Copyright (C) Lumiera.org
2010, 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.
* *****************************************************/
/** @file advice/binding.cpp
** Implementation of a binding record to represent a match between two patterns.
** This is used for the Advice System, to record existing connections between
** advice providers and consumers. But as such, Binding is a generic mechanism
** and looks like it could be of wider use within the Lumiera application.
** This is the reason why Binding got a separate implementation `cpp` file.
*/
#include "lib/util.hpp"
#include "lib/symbol.hpp"
#include "common/advice/binding.hpp"
#include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp>
#include <regex>
using lib::Literal;
using util::isnil;
using std::regex;
using std::smatch;
using std::sregex_iterator;
using std::regex_constants::match_continuous;
using boost::hash_combine;
using boost::lexical_cast;
namespace lumiera{
namespace advice {
LUMIERA_ERROR_DEFINE (BINDING_PATTERN_SYNTAX, "Unable to parse the given binding pattern definition");
/////////////////////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 _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 (Literal lit)
{
string def(lit);
string::const_iterator end_of_last_match = def.begin();
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]));
end_of_last_match = match[0].second;
++pos;
}
if ( end_of_last_match !=def.end()
&& *end_of_last_match !='.'
) // if the match did *not stop at the end of the pattern definition list
throw lumiera::error::Invalid ("Trailing garbage in binding pattern definition" ///////////////TICKET #197 should include the garbage, i.e. where the parsing stops
, LUMIERA_ERROR_BINDING_PATTERN_SYNTAX);
}
Binding::Binding ()
: atoms_()
{ }
Binding::Binding (Literal spec)
{
if (!isnil(spec))
parse_and_append (spec);
}
void
Binding::addPredicate (Literal spec)
{
REQUIRE (spec);
parse_and_append (spec);
}
Binding::operator string() const
{
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_+")";
}
HashVal
Binding::calculateHash() const
{
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()); //////////////TICKET #615 : not in final version with variable arguments
}
return hash;
}
/** 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>) ////TICKET #615 not yet implemented
*/
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 lumiera::advice