Reactions  0.1.1
Handling reaction trees and decays
processes.hpp
Go to the documentation of this file.
1 
5 #pragma once
6 
7 #include <optional>
8 #include <string>
9 #include <utility>
10 #include <variant>
11 #include <vector>
12 
14 #include "reactions/exceptions.hpp"
15 #include "reactions/pow_enum.hpp"
16 #include "reactions/tokens.hpp"
17 
18 namespace reactions {
19 
20  // Forward declarations
21  template <class Element> class element_wrapper;
22  template <class Element> class reaction;
23  template <class Element> class decay;
24 
27  namespace processes {
29  REACTIONS_POW_ENUM_WITH_UNKNOWN(node_type, element, reaction, decay);
30 
43  template <class FillElement, class FillExpression, class ArrowSwitch>
44  void process_expression(std::string::const_iterator &sit,
45  std::string::const_iterator const &end,
46  FillElement fill_element,
47  FillExpression fill_expression,
48  ArrowSwitch arrow_switch) {
49 
50  while (tokens::match_token<tokens::space>(sit))
51  ++sit; // remove leading spaces
52 
53  if (tokens::match_token<tokens::left_bra>(sit))
55  "Expression starts with another expression", end - sit);
56 
57  auto start = sit; // keep track of the beginning of an expression
58  while (sit != end) {
59 
60  if (tokens::match_token<tokens::space>(sit)) {
61 
62  if (sit == start) {
63  // remove consecutive spaces
64  start = ++sit;
65  continue;
66  } else {
67  // add the new element
68  fill_element(start);
69  start = (sit += tokens::space::size);
70  continue;
71  }
72  } else if (tokens::match_token<tokens::left_bra>(sit)) {
73 
74  if (sit == start) {
75 
77 
78  // begin a new expression
79  fill_expression();
80 
81  if (!tokens::match_token<tokens::right_bra>(sit))
83  "Expected closing braces", end - sit);
84 
85  start = (sit += tokens::right_bra::size);
86 
87  continue;
88 
89  } else {
90  // add the new element
91  fill_element(start);
92  start = sit; // end up in the previous conditional
93  continue;
94  }
95  } else if (tokens::match_token<tokens::right_bra>(sit)) {
96 
97  if (sit != start) {
98  fill_element(start);
99  start = sit;
100  }
101 
102  break;
103 
104  } else if (tokens::match_token<tokens::arrow>(sit)) {
105 
106  if (sit != start)
107  fill_element(start);
108 
109  start = (sit += tokens::arrow::size);
110 
111  // switch to the products
112  arrow_switch();
113 
114  continue;
115 
116  } else
117  ++sit;
118  }
119 
120  if (sit != start)
121  fill_element(start);
122  }
123 
130  template <class Process>
131  Process make_process(std::string const &str,
132  typename Process::builder_type builder) {
133 
134  try {
135 
136  auto sit = str.cbegin();
137  auto const send = str.cend();
138 
139  Process p{sit, send, builder};
140 
141  if (sit != send) {
142  if (tokens::match_token<tokens::right_bra>(sit))
143  throw reactions::exceptions::__syntax_error("Mismatching braces",
144  send - sit);
145  else
146  throw reactions::exceptions::__syntax_error("Invalid syntax",
147  send - sit);
148  }
149 
150  return p;
151 
153  throw e.update(str);
154  }
155  }
156  } // namespace processes
157 
158  // Forward declarations
159  template <class Element> class element_wrapper;
160  template <class Element> class reaction;
161  template <class Element> class decay;
162 
168  template <class Element, template <class> class Chain>
169  class node final
170  : protected std::variant<element_wrapper<Element>, Chain<Element>> {
171 
172  public:
175  using chain_type = Chain<Element>;
176 
178  using base_type::base_type;
179 
181  bool is_element() const {
182  return this->type() == processes::node_type::element;
183  }
184 
186  bool is_reaction() const {
187  return type() == processes::node_type::reaction;
188  }
189 
191  bool is_decay() const {
192  return this->type() == processes::node_type::decay;
193  }
194 
196  processes::node_type type() const {
197 
198  if (std::holds_alternative<element_type>(*this))
199  return processes::node_type::element;
200  else {
201  if constexpr (utils::is_template_specialization_v<chain_type, reaction>)
202  return processes::node_type::reaction;
204  decay>)
205  return processes::node_type::decay;
206  else
207  static_assert(utils::dependent_false_v<decltype(*this)>,
208  "Unexpected internal compile-time error");
209  }
210  }
211 
213  Element const &as_element() const { return *as_element_wrapper(); }
214 
216  chain_type const &as_chain() const {
217  return std::get<Chain<Element>>(*this);
218  }
219 
220  protected:
223  return std::get<element_type>(*this);
224  }
225  };
226 
228  namespace processes::detail {
236  template <class Element, template <class> class Chain>
237  inline bool check_nodes(std::vector<node<Element, Chain>> const &first,
238  std::vector<node<Element, Chain>> const &second) {
239 
240  auto size = first.size();
241 
242  if (size != second.size())
243  return false;
244 
245  // two masks to count the elements that are gone
246  std::vector<bool> mask(size, false);
247 
248  for (auto i = 0u; i < size; ++i) {
249 
250  for (auto j = 0u; j < size; ++j) {
251 
252  if (mask[j])
253  // already used
254  continue;
255 
256  if (second[j].type() != first[i].type())
257  // different types
258  continue;
259 
260  switch (first[i].type()) {
261  case (processes::node_type::element):
262  if (first[i].as_element() == second[i].as_element())
263  mask[j] = true;
264  break;
265  case (processes::node_type::reaction):
266  if (first[i].as_chain() == second[i].as_chain())
267  mask[j] = true;
268  break;
269  case (processes::node_type::decay):
270  if (first[i].as_chain() == second[i].as_chain())
271  mask[j] = true;
272  break;
273  case (processes::node_type::unknown_node_type):
275  "A node can not be of unknown type (internal error); please "
276  "report the bug");
277  break;
278  }
279  }
280  // if we reach this point, no match has been found for "first[i]"
281  return false;
282  }
283 
284  // all of them have been matched
285  return true;
286  }
287  } // namespace processes::detail
288 } // namespace reactions
289 
290 namespace reactions {
291 
297  template <class Element> class element_wrapper final {
298 
299  public:
301  element_wrapper(Element &&e) : m_element{std::move(e)} {}
303  ~element_wrapper() = default;
305  element_wrapper(const element_wrapper &) = default;
307  element_wrapper(element_wrapper &&) = default;
309  element_wrapper &operator=(const element_wrapper &) = default;
310  // No empty constructor
311  element_wrapper() = delete;
313  Element const &operator*() const { return m_element; }
315  Element const *operator->() const { return &m_element; }
316 
317  protected:
319  Element m_element;
320  };
321 
326  template <class Element> class reaction final {
327 
328  public:
330  using builder_type =
332  using nodes_type =
335 
337  reaction(reaction &&) = default;
339  reaction(const reaction &) = default;
341  reaction &operator=(const reaction &) = default;
343  ~reaction() = default;
344  // No empty constructor
345  reaction() = delete;
346 
348  nodes_type const &reactants() const { return m_reactants; }
349 
351  nodes_type const &products() const { return m_products; }
352 
360  bool operator==(reaction<Element> const &other) const {
361 
362  if (m_reactants.size() != other.m_reactants.size() ||
363  m_products.size() != other.m_products.size())
364  return false;
365 
366  return processes::detail::check_nodes(m_reactants, other.m_reactants) &&
367  processes::detail::check_nodes(m_products, other.m_products);
368  }
369 
371  bool operator!=(reaction<Element> const &other) const {
372  return !(*this == other);
373  }
374 
375  protected:
383  reaction(std::string::const_iterator &&begin,
384  std::string::const_iterator const &end, builder_type builder)
385  : reaction{begin, end, builder} {}
386 
387  template <class Process>
388  friend Process
390  typename Process::builder_type);
391 
402  reaction(std::string::const_iterator &sit,
403  std::string::const_iterator const &end, builder_type builder) {
404 
405  nodes_type *current_set = &m_reactants;
406 
407  auto fill_element =
408  [&](std::string::const_iterator const &start) -> void {
409  current_set->emplace_back(builder(std::string{start, sit}));
410  };
411  auto fill_reaction = [&]() -> void {
412  current_set->emplace_back(reaction(sit, end, builder));
413  };
414  auto arrow_switch = [&]() -> void {
415  if (!m_reactants.size())
416  throw reactions::exceptions::__syntax_error("Missing reactants",
417  end - sit);
418 
419  if (current_set == &m_products)
420  throw reactions::exceptions::__syntax_error("Duplicated arrow",
421  end - sit);
422 
423  current_set = &m_products;
424  };
425 
426  processes::process_expression(sit, end, fill_element, fill_reaction,
427  arrow_switch);
428 
429  if (!m_reactants.size())
430  throw reactions::exceptions::__syntax_error("Missing reactants",
431  end - sit);
432 
433  if (!m_products.size())
434  throw reactions::exceptions::__syntax_error("Missing products",
435  end - sit);
436  }
437 
442  };
443 
450  template <class Element> class decay final {
451 
452  public:
456 
458  decay(decay &&) = default;
460  decay(const decay &) = default;
462  decay &operator=(const decay &) = default;
464  ~decay() = default;
465 
466  decay() = delete;
467 
469  Element const &head() const { return *(*m_head); }
470 
472  nodes_type const &products() const { return m_products; }
473 
475  bool operator==(decay<Element> const &other) const {
476 
477  if (m_products.size() != other.m_products.size())
478  return false;
479 
480  return (*m_head == *other.m_head) &&
481  processes::detail::check_nodes(m_products, other.m_products);
482  }
483 
485  bool operator!=(decay<Element> const &other) const {
486  return !(*this == other);
487  }
488 
489  protected:
500  decay(std::string::const_iterator &&begin,
501  std::string::const_iterator const &end, builder_type builder)
502  : decay{begin, end, builder} {}
503 
504  template <class Process>
505  friend Process
507  typename Process::builder_type);
508 
519  decay(std::string::const_iterator &sit,
520  std::string::const_iterator const &end, builder_type builder) {
521 
522  bool fill_products = false; // keep track of the elements we are adding
523 
524  auto fill_element =
525  [&](std::string::const_iterator const &start) -> void {
526  if (!m_head) {
527  this->m_head.emplace(builder(std::string{start, sit}));
528  } else if (fill_products) {
529  this->m_products.emplace_back(builder(std::string{start, sit}));
530  } else
531  throw reactions::exceptions::__syntax_error("Missing arrow",
532  end - start);
533  };
534  auto fill_decay = [&] {
535  if (!m_head) {
536  throw reactions::exceptions::__syntax_error("Missing head",
537  end - sit);
538  } else if (fill_products) {
539  this->m_products.emplace_back(decay(sit, end, builder));
540  } else
541  throw reactions::exceptions::__syntax_error("Missing arrow",
542  end - sit);
543  };
544  auto arrow_switch = [&] {
545  if (fill_products)
546  throw reactions::exceptions::__syntax_error("Duplicated arrow",
547  end - sit);
548 
549  else if (!m_head)
550  throw reactions::exceptions::__syntax_error("Missing head particle",
551  end - sit);
552 
553  fill_products = true;
554  };
555 
556  processes::process_expression(sit, end, fill_element, fill_decay,
557  arrow_switch);
558 
559  if (!m_head)
561  "No elements have been parsed", end - sit);
562 
563  if (!m_products.size())
564  throw reactions::exceptions::__syntax_error("Expected products",
565  end - sit);
566  }
567 
572  };
573 
580  template <class Element>
584  return processes::make_process<reaction<Element>>(str, builder);
585  }
586 
592  template <class Element>
594  return make_reaction_for<Element>(str, element_traits::builder<Element>);
595  }
596 
603  template <class Element>
606  return processes::make_process<decay<Element>>(str, builder);
607  }
608 
614  template <class Element> decay<Element> make_decay(std::string const &str) {
615  return make_decay_for<Element>(str, element_traits::builder<Element>);
616  }
617 } // namespace reactions
static constexpr auto dependent_false_v
A false type that can be used with static_assert
Definition: utils.hpp:15
Description of a process where reactants generate a set of products.
Definition: processes.hpp:22
bool is_decay() const
Check if the underlying class is a decay.
Definition: processes.hpp:191
Element m_element
Underlying element.
Definition: processes.hpp:319
Syntax error with an unformatted message.
Definition: exceptions.hpp:69
syntax_error update(std::string const &str)
Definition: exceptions.hpp:77
reaction< Element > make_reaction(std::string const &str)
Create a new reaction.
Definition: processes.hpp:593
element_traits::builder_tpl_t< Element > builder_type
Definition: processes.hpp:454
nodes_type m_products
Products.
Definition: processes.hpp:441
void process_expression(std::string::const_iterator &sit, std::string::const_iterator const &end, FillElement fill_element, FillExpression fill_expression, ArrowSwitch arrow_switch)
Internal function to process an expression.
Definition: processes.hpp:44
Exceptions that can be thrown when running the functions of the package.
Element const & as_element() const
Return the pointer to the underlying object casted to an element.
Definition: processes.hpp:213
T cend(T... args)
Raised when unexpected problems appear, which should be reported as bugs.
Definition: exceptions.hpp:38
Element const & operator*() const
Access the underlying element by reference.
Definition: processes.hpp:313
Description of a process where head particle generate a set of products.
Definition: processes.hpp:23
STL class.
processes::node_type type() const
Get the node type.
Definition: processes.hpp:196
static constexpr auto builder
Default builder for a given kind of element.
Definition: element_traits.hpp:50
nodes_type m_products
Products.
Definition: processes.hpp:571
Contains macros to define smart enumeration types.
nodes_type const & reactants() const
Get the reactants of the reaction.
Definition: processes.hpp:348
bool operator==(decay< Element > const &other) const
Comparison operator.
Definition: processes.hpp:475
Tokens allowed in a reaction or decay.
std::function< T(std::string const &)> const & builder_tpl_t
General builder type for a given kind of element.
Definition: element_traits.hpp:57
Main namespace of the Reactions package.
Definition: all.hpp:22
static constexpr auto is_template_specialization_v
Definition: utils.hpp:54
static const size_t size
Definition: tokens.hpp:16
bool operator!=(decay< Element > const &other) const
Comparison operator.
Definition: processes.hpp:485
bool operator==(reaction< Element > const &other) const
Compare two reactions.
Definition: processes.hpp:360
Base class for a process node.
Definition: processes.hpp:169
Template for elements of a reaction or decay.
Definition: processes.hpp:21
Process make_process(std::string const &str, typename Process::builder_type builder)
Make a new process (a reaction or a decay)
Definition: processes.hpp:131
bool is_element() const
Check if the underlying class is an element.
Definition: processes.hpp:181
T size(T... args)
element_type const & as_element_wrapper() const
Return the underlying object casted to a wrapped.
Definition: processes.hpp:222
reaction< Element > make_reaction_for(std::string const &str, typename reaction< Element >::builder_type builder)
Create a new reaction with a custom builder.
Definition: processes.hpp:582
decay< Element > make_decay_for(std::string const &str, typename decay< Element >::builder_type builder)
Create a new decay with a custom builder.
Definition: processes.hpp:604
Element const * operator->() const
Access the underlying element by pointer.
Definition: processes.hpp:315
T cbegin(T... args)
reaction(std::string::const_iterator &&begin, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:383
nodes_type const & products() const
Get the products of the reaction.
Definition: processes.hpp:351
Element const & head() const
Get the head particle of the decay.
Definition: processes.hpp:469
Utilities to work with element types.
decay(std::string::const_iterator &sit, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:519
std::optional< element_type > m_head
Head particle.
Definition: processes.hpp:569
bool check_nodes(std::vector< node< Element, Chain >> const &first, std::vector< node< Element, Chain >> const &second)
Compare two nodes.
Definition: processes.hpp:237
chain_type const & as_chain() const
Return the pointer to the underlying object casted to a reaction.
Definition: processes.hpp:216
bool operator!=(reaction< Element > const &other) const
Compare two reactions.
Definition: processes.hpp:371
reaction(std::string::const_iterator &sit, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:402
nodes_type const & products() const
Get the products of the decay.
Definition: processes.hpp:472
decay(std::string::const_iterator &&begin, std::string::const_iterator const &end, builder_type builder)
Constructor from the string iterators.
Definition: processes.hpp:500
bool is_reaction() const
Check if the underlying class is a reaction.
Definition: processes.hpp:186
element_traits::builder_tpl_t< Element > builder_type
Underlying element type.
Definition: processes.hpp:331
decay< Element > make_decay(std::string const &str)
Create a new decay.
Definition: processes.hpp:614
nodes_type m_reactants
Reactants.
Definition: processes.hpp:439
element_wrapper(Element &&e)
Constructor given the underlying element.
Definition: processes.hpp:301
REACTIONS_POW_ENUM_WITH_UNKNOWN(node_type, element, reaction, decay)
Node types.
T emplace_back(T... args)