Logo Search packages:      
Sourcecode: tagcoll version File versions  Download package

Expression.cc

/*
 * Embed a tag expression in a class with proper copy semantics
 * Plus some filters based on tag expressions
 *
 * Copyright (C) 2003,2004,2005  Enrico Zini <enrico@debian.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 */

#include <tagcoll/Exception.h>

#include <tagcoll/Expression.h>

#include <tagcoll/tagexpr/TagexprParser.h>

using namespace std;

namespace Tagcoll {

class TagexprTrue : public ExpressionImpl
{
public:
      TagexprTrue() {}
      virtual ~TagexprTrue() {}

      virtual std::string format() const { return "*"; }

      virtual bool eval(const TagexprContext& context) const { return true; }
      virtual bool eval(const OpSet<std::string>& tags) const { return true; }
};

class TagexprTag : public ExpressionImpl
{
protected:
      std::string _tag;
      
public:
      TagexprTag(const std::string& tag) : _tag(tag) {}
      virtual ~TagexprTag() {}

      virtual std::string format() const { return _tag; }

      virtual bool eval(const TagexprContext& context) const
      {
            return context.eval(_tag);
      }
      virtual bool eval(const OpSet<std::string>& tags) const
      {
            if (_tag[0] == '*')
            {
                  string match = _tag.substr(1);
                  for (OpSet<string>::const_iterator i = tags.begin();
                              i != tags.end(); i++)
                        if (i->size() >= match.size() &&
                                    i->substr(i->size() - match.size()) == match)
                              return true;
                  return false;
            }
            else if (_tag[_tag.size() - 1] == '*')
            {
                  string match = _tag.substr(0, _tag.size() - 1);
                  for (OpSet<string>::const_iterator i = tags.begin();
                              i != tags.end(); i++)
                        if (i->size() >= match.size() &&
                                    i->substr(0, match.size()) == match)
                              return true;
                  return false;
            }
            else
                  return tags.find(_tag) != tags.end();
      }
};

class TagexprNot : public ExpressionImpl
{
protected:
      Expression _op;

public:
      TagexprNot(const Expression& operand) : _op(operand) {}

      virtual std::string format() const { return "!" + _op.format(); }

      virtual bool eval(const TagexprContext& context) const
      {
            return ! _op(context);
      }
      virtual bool eval(const OpSet<std::string>& tags) const
      {
            return ! _op(tags);
      }
};

class TagexprAnd : public ExpressionImpl
{
protected:
      Expression _op1;
      Expression _op2;

public:
      TagexprAnd(const Expression& operand1, const Expression& operand2)
            : _op1(operand1), _op2(operand2) {}

      virtual std::string format() const
      {
            return "( " + _op1.format() + " && " + _op2.format() + " )";
      }

      virtual bool eval(const TagexprContext& context) const
      {
            return _op1(context) && _op2(context);
      }
      virtual bool eval(const OpSet<std::string>& tags) const
      {
            return _op1(tags) && _op2(tags);
      }
};

class TagexprOr : public ExpressionImpl
{
protected:
      Expression _op1;
      Expression _op2;

public:
      TagexprOr(const Expression& operand1, const Expression& operand2)
            : _op1(operand1), _op2(operand2) {}
      
      virtual std::string format() const
      {
            return "( " + _op1.format() + " || " + _op2.format() + " )";
      }

      virtual bool eval(const TagexprContext& context) const
      {
            return _op1(context) || _op2(context);
      }
      virtual bool eval(const OpSet<std::string>& tags) const
      {
            return _op1(tags) || _op2(tags);
      }
};


Expression::Expression(const std::string& expr) : m_impl(0)
{
      Expression e = TagexprParser::instance()->parse(expr);
      
      m_impl = e.impl();
      m_impl->ref();
}

Expression::Expression() : m_impl(new TagexprTrue) { m_impl->ref(); }

Expression Expression::matchTag(const std::string& pattern)
{
      return Expression(new TagexprTag(pattern));
}

Expression Expression::operator and (const Expression& e)
{
      return Expression(new TagexprAnd(*this, e));
}

Expression Expression::operator or (const Expression& e)
{
      return Expression(new TagexprOr(*this, e));
}

Expression Expression::operator not ()
{
      return Expression(new TagexprNot(*this));
}


00188 bool TagexprContext::eval(const std::string& tag) const
{
      std::map<std::string, Expression>::const_iterator i = derivedTags.find(tag);
      if (i == derivedTags.end())
            return tags.contains(tag);
      else if (!seen.contains(tag))
      {
            seen += tag;
            bool res = i->second(*this);
            seen -= tag;
            return res;
      }
      else
            // If we got here, we have a circular dependency between derived tags
            // FIXME: find a better way to handle it: throw an exception, maybe?
            return false;
}

}

#ifndef INSTANTIATING_TEMPLATES
#include <string>

namespace Tagcoll {
      template class FilterItemsByExpression<std::string, std::string>;
      template class FilterTagsByExpression<std::string, std::string>;
}
#endif


#ifdef COMPILE_TESTSUITE

#include <tests/test-utils.h>

#include <tagcoll/InputMerger.h>

namespace tut {
using namespace tut_tagcoll;

struct tagcoll_expression_shar {
};
TESTGRP(tagcoll_expression);

template<> template<>
void to::test<1>()
{
      OpSet<string> test;
      test += "coffee";
      test += "tea";
      test += "sugar";
      
      Expression e1("coffee");
      ensure(e1(test));

      Expression e2("coffee && tea");
      ensure(e2(test));

      e1 = Expression("!coffee");
      ensure(!e1(test));

      e1 = Expression("coffee || milk");
      ensure(e1(test));

      e1 = Expression("coffee && !milk");
      ensure(e1(test));

      e1 = Expression("coffee && !tea");
      ensure(!e1(test));

      e1 = Expression("(coffee || milk) && (tea && sugar)");
      ensure(e1(test));

      e1 = Expression("!(coffee && milk) && (tea && sugar)");
      ensure(e1(test));
}

template<> template<>
void to::test<2>()
{
      std::string input_coll(
                  "a: b, c\n"
                  "b:\n"
                  "c: a\n"
                  "d:  c::D, e::F,    f::g\n"
                  );
      std::string output_coll(
                  "a: b, c\n"
                  "d:  c::D, e::F,    f::g\n"
                  );
      InputMerger<string, string> result;
      FilterItemsByExpression<string, string> filter(result, "(*::D && e::F) || c");
      outputCollection(input_coll, filter); 

      InputMerger<string, string> reference;
      outputCollection(output_coll, reference); 

      ensure_coll_equal(reference, result);
}

template<> template<>
void to::test<3>()
{
      std::string input_coll(
                  "a: b, c, a::D\n"
                  "b: D, *::D\n"
                  "c: a\n"
                  "d: c::D, e::F, f::g\n"
                  );
      std::string output_coll(
                  "a: c, a::D\n"
                  "b: *::D\n"
                  "d: c::D, e::F\n"
                  );
      InputMerger<string, string> result;
      FilterTagsByExpression<string, string> filter(result, "*::D || e::F || c");
      outputCollection(input_coll, filter); 

      InputMerger<string, string> reference;
      outputCollection(output_coll, reference); 

      ensure_coll_equal(reference, result);
}

}

#endif

// vim:set ts=4 sw=4:

Generated by  Doxygen 1.6.0   Back to index