CMS 3D CMS Logo

PathStatusFilter.cc
Go to the documentation of this file.
1 
45 
46 #include <boost/spirit/include/phoenix_bind.hpp>
47 #include <boost/spirit/include/qi.hpp>
48 
49 #include <functional>
50 #include <memory>
51 #include <ostream>
52 #include <string>
53 #include <sstream>
54 #include <vector>
55 #include <utility>
56 
57 namespace qi = boost::spirit::qi;
58 namespace ascii = boost::spirit::ascii;
59 namespace phoenix = boost::phoenix;
60 
61 namespace edm {
62 
63  class EventSetup;
64  class StreamID;
65 
66  namespace pathStatusExpression {
67  class Evaluator;
68  }
69 
71  public:
72  explicit PathStatusFilter(ParameterSet const&);
74  bool filter(StreamID, Event&, EventSetup const&) const final;
75 
76  private:
78  bool verbose_;
79  };
80 
81  namespace pathStatusExpression {
82 
83  class Evaluator {
84  public:
85  virtual ~Evaluator() {}
86 
87  enum EvaluatorType { Name, Not, And, Or, BeginParen };
88  virtual EvaluatorType type() const = 0;
89 
90  virtual void setLeft(std::unique_ptr<Evaluator>&&) {}
91  virtual void setRight(std::unique_ptr<Evaluator>&&) {}
92 
93  virtual void print(std::ostream& out, unsigned int indentation) const {}
94  virtual void init(ConsumesCollector&) {}
95  virtual bool evaluate(Event const& event) const { return true; };
96  };
97 
98  class Operand : public Evaluator {
99  public:
100  Operand(std::vector<char> const& pathName) : pathName_(pathName.begin(), pathName.end()) {}
101 
102  EvaluatorType type() const override { return Name; }
103 
104  void print(std::ostream& out, unsigned int indentation) const override {
105  out << std::string(indentation, ' ') << pathName_ << "\n";
106  }
107 
108  void init(ConsumesCollector& iC) override { token_ = iC.consumes<PathStatus>(InputTag(pathName_)); }
109 
110  bool evaluate(Event const& event) const override { return event.get(token_).accept(); }
111 
112  private:
115  };
116 
117  class NotOperator : public Evaluator {
118  public:
119  EvaluatorType type() const override { return Not; }
120 
121  void setLeft(std::unique_ptr<Evaluator>&& v) override { operand_ = std::move(v); }
122 
123  void print(std::ostream& out, unsigned int indentation) const override {
124  out << std::string(indentation, ' ') << "not\n";
125  operand_->print(out, indentation + 4);
126  }
127 
128  void init(ConsumesCollector& iC) override { operand_->init(iC); }
129 
130  bool evaluate(Event const& event) const override { return !operand_->evaluate(event); }
131 
132  private:
134  };
135 
136  template <typename T>
137  class BinaryOperator : public Evaluator {
138  public:
139  EvaluatorType type() const override;
140 
141  void setLeft(std::unique_ptr<Evaluator>&& v) override { left_ = std::move(v); }
142  void setRight(std::unique_ptr<Evaluator>&& v) override { right_ = std::move(v); }
143 
144  void print(std::ostream& out, unsigned int indentation) const override;
145 
146  void init(ConsumesCollector& iC) override {
147  left_->init(iC);
148  right_->init(iC);
149  }
150 
151  bool evaluate(Event const& event) const override {
152  T op;
153  return op(left_->evaluate(event), right_->evaluate(event));
154  }
155 
156  private:
159  };
160 
161  template <>
163  return And;
164  }
165 
166  template <>
168  return Or;
169  }
170 
171  template <>
172  void BinaryOperator<std::logical_and<bool>>::print(std::ostream& out, unsigned int indentation) const {
173  out << std::string(indentation, ' ') << "and\n";
174  left_->print(out, indentation + 4);
175  right_->print(out, indentation + 4);
176  }
177  template <>
178  void BinaryOperator<std::logical_or<bool>>::print(std::ostream& out, unsigned int indentation) const {
179  out << std::string(indentation, ' ') << "or\n";
180  left_->print(out, indentation + 4);
181  right_->print(out, indentation + 4);
182  }
183 
186 
187  class BeginParenthesis : public Evaluator {
188  public:
189  EvaluatorType type() const override { return BeginParen; }
190  };
191 
192  // This class exists to properly handle the precedence of the
193  // operators and also handle the order of operations specified
194  // by parentheses. (search for shunting yard algorithm on the
195  // internet for a description of this algorithm)
197  public:
198  void addPathName(std::vector<char> const& s) { operandStack.push_back(std::make_unique<Operand>(s)); }
199 
200  void addOperatorNot() {
201  if (operatorStack.empty() || operatorStack.back()->type() != Evaluator::Not) {
202  operatorStack.push_back(std::make_unique<NotOperator>());
203  } else {
204  // Two Not operations in a row cancel and are the same as no operation at all.
205  operatorStack.pop_back();
206  }
207  }
208 
210  std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
211  backEvaluator->setRight(std::move(operandStack.back()));
212  operandStack.pop_back();
213  backEvaluator->setLeft(std::move(operandStack.back()));
214  operandStack.pop_back();
215  operandStack.push_back(std::move(backEvaluator));
216  operatorStack.pop_back();
217  }
218 
220  std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
221  backEvaluator->setLeft(std::move(operandStack.back()));
222  operandStack.pop_back();
223  operandStack.push_back(std::move(backEvaluator));
224  operatorStack.pop_back();
225  }
226 
227  void addOperatorAnd() {
228  while (!operatorStack.empty()) {
229  std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
230  if (backEvaluator->type() == Evaluator::And) {
231  moveBinaryOperator();
232  } else if (backEvaluator->type() == Evaluator::Not) {
233  moveNotOperator();
234  } else {
235  break;
236  }
237  }
238  operatorStack.push_back(std::make_unique<AndOperator>());
239  }
240 
241  void addOperatorOr() {
242  while (!operatorStack.empty()) {
243  std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
244  if (backEvaluator->type() == Evaluator::And || backEvaluator->type() == Evaluator::Or) {
245  moveBinaryOperator();
246  } else if (backEvaluator->type() == Evaluator::Not) {
247  moveNotOperator();
248  } else {
249  break;
250  }
251  }
252  operatorStack.push_back(std::make_unique<OrOperator>());
253  }
254 
255  void addBeginParenthesis() { operatorStack.push_back(std::make_unique<BeginParenthesis>()); }
256 
258  while (!operatorStack.empty()) {
259  std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
260  if (backEvaluator->type() == Evaluator::BeginParen) {
261  operatorStack.pop_back();
262  break;
263  }
264  if (backEvaluator->type() == Evaluator::And || backEvaluator->type() == Evaluator::Or) {
265  moveBinaryOperator();
266  } else if (backEvaluator->type() == Evaluator::Not) {
267  moveNotOperator();
268  }
269  }
270  }
271 
272  std::unique_ptr<Evaluator> finish() {
273  while (!operatorStack.empty()) {
274  std::unique_ptr<Evaluator>& backEvaluator = operatorStack.back();
275  // Just a sanity check. The grammar defined for the boost Spirit parser
276  // should catch any errors of this type before we get here.
277  if (backEvaluator->type() == Evaluator::BeginParen) {
278  throw cms::Exception("LogicError")
279  << "Should be impossible to get this error. Contact a Framework developer";
280  }
281  if (backEvaluator->type() == Evaluator::And || backEvaluator->type() == Evaluator::Or) {
282  moveBinaryOperator();
283  } else if (backEvaluator->type() == Evaluator::Not) {
284  moveNotOperator();
285  }
286  }
287  // Just a sanity check. The grammar defined for the boost Spirit parser
288  // should catch any errors of this type before we get here.
289  if (!operatorStack.empty() || operandStack.size() != 1U) {
290  throw cms::Exception("LogicError") << "Should be impossible to get this error. Contact a Framework developer";
291  }
292  std::unique_ptr<Evaluator> temp = std::move(operandStack.back());
293  operandStack.pop_back();
294  return temp;
295  }
296 
297  private:
298  std::vector<std::unique_ptr<Evaluator>> operandStack;
299  std::vector<std::unique_ptr<Evaluator>> operatorStack;
300  };
301 
302  // Use boost Spirit to parse the logical expression character string
303  template <typename Iterator>
304  class Grammar : public qi::grammar<Iterator, ascii::space_type> {
305  public:
306  Grammar(ShuntingYardAlgorithm* algorithm) : Grammar::base_type(expression), algorithm_(algorithm) {
307  // setup functors that call into shunting algorithm while parsing the logical expression
308  auto addPathName = phoenix::bind(&ShuntingYardAlgorithm::addPathName, algorithm_, qi::_1);
309  auto addOperatorNot = phoenix::bind(&ShuntingYardAlgorithm::addOperatorNot, algorithm_);
310  auto addOperatorAnd = phoenix::bind(&ShuntingYardAlgorithm::addOperatorAnd, algorithm_);
311  auto addOperatorOr = phoenix::bind(&ShuntingYardAlgorithm::addOperatorOr, algorithm_);
312  auto addBeginParenthesis = phoenix::bind(&ShuntingYardAlgorithm::addBeginParenthesis, algorithm_);
313  auto addEndParenthesis = phoenix::bind(&ShuntingYardAlgorithm::addEndParenthesis, algorithm_);
314 
315  // Define the syntax allowed in the logical expressions
316  pathName = !unaryOperator >> !binaryOperatorTest >> (+qi::char_("a-zA-Z0-9_"))[addPathName];
317  binaryOperand = (qi::lit('(')[addBeginParenthesis] >> expression >> qi::lit(')')[addEndParenthesis]) |
318  (unaryOperator[addOperatorNot] >> binaryOperand) | pathName;
319  afterOperator = ascii::space | &qi::lit('(') | &qi::eoi;
320  unaryOperator = qi::lit("not") >> afterOperator;
321  // The only difference in the next two is that one calls a functor and the other does not
322  binaryOperatorTest = (qi::lit("and") >> afterOperator) | (qi::lit("or") >> afterOperator);
323  binaryOperator =
324  (qi::lit("and") >> afterOperator)[addOperatorAnd] | (qi::lit("or") >> afterOperator)[addOperatorOr];
325  expression = binaryOperand % binaryOperator;
326  }
327 
328  private:
329  qi::rule<Iterator> pathName;
330  qi::rule<Iterator, ascii::space_type> binaryOperand;
331  qi::rule<Iterator> afterOperator;
332  qi::rule<Iterator> unaryOperator;
333  qi::rule<Iterator> binaryOperatorTest;
334  qi::rule<Iterator> binaryOperator;
335  qi::rule<Iterator, ascii::space_type> expression;
336 
338  };
339  } // namespace pathStatusExpression
340 
342  : evaluator_(nullptr), verbose_(pset.getUntrackedParameter<bool>("verbose")) {
343  std::string const logicalExpression = pset.getParameter<std::string>("logicalExpression");
344  if (verbose_) {
345  LogAbsolute("PathStatusFilter") << "PathStatusFilter logicalExpression = " << logicalExpression;
346  }
347 
348  if (logicalExpression.empty()) {
349  return;
350  }
351 
352  pathStatusExpression::ShuntingYardAlgorithm shuntingYardAlgorithm;
353 
354  pathStatusExpression::Grammar<std::string::const_iterator> grammar(&shuntingYardAlgorithm);
355 
356  auto it = logicalExpression.cbegin();
357  if (!qi::phrase_parse(it, logicalExpression.cend(), grammar, ascii::space) || (it != logicalExpression.cend())) {
358  throw cms::Exception("Configuration") << "Syntax error in logical expression. Here is an example of how\n"
359  << "the syntax should look:\n"
360  << " \"path1 and not (path2 or not path3)\"\n"
361  << "The expression must contain alternating appearances of operands\n"
362  << "which are path names and binary operators which can be \'and\'\n"
363  << "or \'or\', with a path name at the beginning and end. There\n"
364  << "must be at least one path name. In addition to the alternating\n"
365  << "path names and binary operators, the unary operator \'not\' can\n"
366  << "be inserted before a path name or a begin parenthesis.\n"
367  << "Parentheses are allowed. Parentheses must come in matching pairs.\n"
368  << "Matching begin and end parentheses must contain a complete and\n"
369  << "syntactically correct logical expression. There must be at least\n"
370  << "one space or parenthesis between operators and path names. Extra\n"
371  << "space is ignored and OK. Path names can only contain upper and\n"
372  << "lower case letters, numbers, and underscores. A path name cannot\n"
373  << "be the same as an operator name.\n";
374  }
375 
376  evaluator_ = shuntingYardAlgorithm.finish();
377  if (verbose_) {
378  std::stringstream out;
379  evaluator_->print(out, 0);
380  LogAbsolute("PathStatusFilter") << out.str();
381  }
383  evaluator_->init(iC);
384  }
385 
388  desc.add<std::string>("logicalExpression", std::string())
389  ->setComment(
390  "Operands are path names. Operators in precedence order "
391  "\'not\', \'and\', and \'or\'. Parentheses allowed.");
392  desc.addUntracked<bool>("verbose", false)->setComment("For debugging only");
393  descriptions.add("pathStatusFilter", desc);
394  }
395 
397  if (evaluator_ == nullptr) {
398  return true;
399  }
400  return evaluator_->evaluate(event);
401  }
402 } // namespace edm
403 
EDGetTokenT< ProductType > consumes(edm::InputTag const &tag)
type
Definition: HCALResponse.h:21
T getParameter(std::string const &) const
void init(ConsumesCollector &iC) override
ParameterDescriptionBase * addUntracked(U const &iLabel, T const &value)
edm::propagate_const< std::unique_ptr< Evaluator > > operand_
void print(std::ostream &out, unsigned int indentation) const override
EvaluatorType type() const override
virtual bool evaluate(Event const &event) const
std::vector< std::unique_ptr< Evaluator > > operatorStack
bool evaluate(Event const &event) const override
#define nullptr
void setLeft(std::unique_ptr< Evaluator > &&v) override
bool evaluate(Event const &event) const override
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
bool evaluate(Event const &event) const override
bool filter(StreamID, Event &, EventSetup const &) const final
EvaluatorType type() const override
edm::propagate_const< std::unique_ptr< Evaluator > > left_
static void fillDescriptions(ConfigurationDescriptions &)
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
Grammar(ShuntingYardAlgorithm *algorithm)
qi::rule< Iterator, ascii::space_type > expression
edm::propagate_const< std::unique_ptr< Evaluator > > right_
ConsumesCollector consumesCollector()
Use a ConsumesCollector to gather consumes information from helper functions.
Operand(std::vector< char > const &pathName)
qi::rule< Iterator, ascii::space_type > binaryOperand
edm::propagate_const< std::unique_ptr< pathStatusExpression::Evaluator > > evaluator_
virtual void setRight(std::unique_ptr< Evaluator > &&)
#define end
Definition: vmac.h:39
void print(std::ostream &out, unsigned int indentation) const override
void init(ConsumesCollector &iC) override
ParameterDescriptionBase * add(U const &iLabel, T const &value)
void setLeft(std::unique_ptr< Evaluator > &&v) override
void init(ConsumesCollector &iC) override
void addPathName(std::vector< char > const &s)
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
void setRight(std::unique_ptr< Evaluator > &&v) override
void add(std::string const &label, ParameterSetDescription const &psetDescription)
ShuntingYardAlgorithm * algorithm_
virtual void init(ConsumesCollector &)
#define begin
Definition: vmac.h:32
HLT enums.
virtual void setLeft(std::unique_ptr< Evaluator > &&)
PathStatusFilter(ParameterSet const &)
std::vector< std::unique_ptr< Evaluator > > operandStack
long double T
EDGetTokenT< PathStatus > token_
def move(src, dest)
Definition: eostools.py:511
virtual void print(std::ostream &out, unsigned int indentation) const
Definition: event.py:1