Flow123d  JS_before_hm-1003-g4e68d2c
unit_converter_template.hh
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file unit_converter_template.hh
15  * @brief
16  */
17 
18 #ifndef UNIT_CONVERTER_TEMPLATE_HH_
19 #define UNIT_CONVERTER_TEMPLATE_HH_
20 
21 
22 #include <boost/bind.hpp>
23 #include <boost/function.hpp>
24 #include <boost/version.hpp>
25 #include "tools/unit_converter.hh"
26 
27 #if BOOST_VERSION >= 103800
28  #include <boost/spirit/include/classic_core.hpp>
29  #include <boost/spirit/include/classic_confix.hpp>
30  #include <boost/spirit/include/classic_escape_char.hpp>
31  #include <boost/spirit/include/classic_multi_pass.hpp>
32  #include <boost/spirit/include/classic_position_iterator.hpp>
33  #define spirit_namespace boost::spirit::classic
34 #else
35  #include <boost/spirit/core.hpp>
36  #include <boost/spirit/utility/confix.hpp>
37  #include <boost/spirit/utility/escape_char.hpp>
38  #include <boost/spirit/iterator/multi_pass.hpp>
39  #include <boost/spirit/iterator/position_iterator.hpp>
40  #define spirit_namespace boost::spirit
41 #endif
42 
43 
44 namespace units_converter
45 {
46 
47 const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >();
48 const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >();
49 
50 
51 /**
52  * @brief Class manages parsing of user defined field unit.
53  *
54  * Class contains:
55  * - object of type \p UnitData for storing user defined unit
56  * - methods for create UnitData object
57  * - methods managing exceptions, if user defined unit is not in correct format
58  *
59  * For example, unit is defined in format:
60  * "MPa/rho/g_; rho = 990*kg*m^-3; g_ = 9.8*m*s^-2"
61  *
62  * - this unit is composed of three parts separated by semicolons
63  * - first part (MPa/rho/g) defines unit and allows operations multiplication, division and exponentiation of factors
64  * - factor 'MPa' is predefined (MegaPascal), factors 'rho' and 'g_' must be defined in next parts
65  * - first part is always obligatory
66  * - following parts must be in format: '<shortcut> = (<multipicative_coeficient>*)<definition>'
67  * - shotcut must correspond with user defined factor in first part
68  * - multipicative_coeficient is optional
69  * - definition allows operations multiplication, division and exponentiation of factors too
70  * - symbol 'g_' must be defined in this format because 'g' is in conflict with gram
71  */
72 template< class Iter_type >
74 {
75 public:
76  /// Constructor.
78  : unit_data_key_(""), factor_idx_(-1)
79  {
80  unit_data_[""] = Formula();
81  }
82 
83  /// Add new definition of formula
84  void new_shortcut( Iter_type begin, Iter_type end )
85  {
86  std::string key = get_str(begin, end);
87  unit_data_[key] = Formula();
88  unit_data_key_ = key;
89  factor_idx_ = -1;
90  }
91 
92  /// Add new factor of unit (factor is multiplying)
93  void new_mult_factor( Iter_type begin, Iter_type end )
94  {
95  unit_data_[unit_data_key_].factors_.push_back( Factor(get_str(begin, end), 1 ) );
96  ++factor_idx_;
97  }
98 
99  /// Add new factor of unit (factor is dividing)
100  void new_div_factor( Iter_type begin, Iter_type end )
101  {
102  unit_data_[unit_data_key_].factors_.push_back( Factor(get_str(begin, end), -1 ) );
103  ++factor_idx_;
104  }
105 
106  /// Compute exponent to actual factor of unit
107  void new_exp( boost::int64_t i )
108  {
109  unit_data_[unit_data_key_].factors_[factor_idx_].exponent_ *= (int)i;
110  }
111 
112  /// Add multipicative coeficient of unit
113  void new_multipl( double d )
114  {
115  unit_data_[unit_data_key_].coef_ = d;
116  }
117 
118  /// Throw exception if exponent is not in correct format
119  void throw_exp_not_int( Iter_type begin, Iter_type end )
120  {
121  std::stringstream ss;
122  ss << "Value of exponent '" << get_str( begin, end ) << "' is not integer";
123  THROW( ExcInvalidUnit() << EI_UnitError(ss.str()) );
124  }
125 
126  /// Throw exception if shortcut of factor is not in correct format
127  void throw_not_shortcut( Iter_type begin, Iter_type end )
128  {
129  std::stringstream ss;
130  if (begin == end) {
131  ss << "Missing declaration of shortcut '.." << get_str( begin-4, end+4 ) << "..'";
132  } else {
133  ss << "Invalid shortcut of unit '" << get_str( begin, end ) << "'";
134  }
135  THROW( ExcInvalidUnit() << EI_UnitError(ss.str()) );
136  }
137 
138  /// Throw exception if sign '=' missing in definition
139  void throw_not_equating( Iter_type begin, Iter_type end )
140  {
141  std::stringstream ss;
142  ss << "Invalid expression '" << get_str( begin, end ) << "', missing '='";
143  THROW( ExcInvalidUnit() << EI_UnitError(ss.str()) );
144  }
145 
146  /**
147  * @brief Check @p unit_data_ object.
148  *
149  * Method:
150  * - marks factors that are defined as derived unit
151  * - checks undefined factors of unit
152  * - check conflicts in definitions of unit (same shortcut is defined by user and predefined in application)
153  * - checks cyclic definition of unit
154  */
156  {
158  {
159  for(std::vector<struct Factor>::iterator formula_it = it->second.factors_.begin();
160  formula_it != it->second.factors_.end(); ++formula_it)
161  {
162  if (formula_it->factor_ == it->first) { // error - cyclic definition
163  std::stringstream ss;
164  ss << "Cyclic declaration of unit '" << it->first << "'";
165  THROW( ExcInvalidUnit() << EI_UnitError(ss.str()) );
166  } else if (unit_data_.find(formula_it->factor_) != unit_data_.end()) { // formula exists as derived unit
167  if (UnitConverter::basic_factors.units_map_.find(formula_it->factor_) != UnitConverter::basic_factors.units_map_.end()) {
168  // error - conflict with predefined unit
169  std::stringstream ss;
170  ss << "Shortcut '" << formula_it->factor_ << "' is in conflict with predefined unit";
171  THROW( ExcInvalidUnit() << EI_UnitError(ss.str()) );
172  }
173  formula_it->basic_ = false;
174  } else if (UnitConverter::basic_factors.units_map_.find(formula_it->factor_) == UnitConverter::basic_factors.units_map_.end()) {
175  // error - unit not defined
176  std::stringstream ss;
177  ss << "Unit '" << formula_it->factor_ << "' is not defined";
178  THROW( ExcInvalidUnit() << EI_UnitError(ss.str()) );
179  }
180  }
181  }
182  }
183 
184  // Return @p unit_data_
185  inline UnitData unit_data() const
186  { return unit_data_; }
187 private:
188 
190  // to prevent "assignment operator could not be generated" warning
191 
192  inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end ) const
193  {
194  return std::string( begin, end );
195  }
196 
197 
198  UnitData unit_data_; //!< Full parsed data
199  std::string unit_data_key_; //!< key of actual item of unit_data_
200  int factor_idx_; //!< index to actual item of subvector of factors of unit_data_
201 };
202 
203 
204 /**
205  * @brief Definition of unit grammar.
206  *
207  * Allow parse user-defined units.
208  */
209 template< class Iter_type >
210 class UnitSIGrammer : public spirit_namespace::grammar< UnitSIGrammer< Iter_type > >
211 {
212 public:
213 
215 
216  /// Constructor.
217  UnitSIGrammer( Semantic_actions_t& semantic_actions )
218  : actions_( semantic_actions )
219  {
220  }
221 
222 
223  /// Define rules of grammar
224  template< typename ScannerT >
226  {
227  public:
228 
229  definition( const UnitSIGrammer& self )
230  {
231  using namespace spirit_namespace;
232 
233  // first we convert the semantic action class methods to functors with the
234  // parameter signature expected by spirit
235 
236  typedef boost::function< void( Iter_type, Iter_type ) > Str_action;
237  typedef boost::function< void( double ) > Real_action;
238  typedef boost::function< void( boost::int64_t ) > Int_action;
239 
240  Str_action new_shortcut ( boost::bind( &Semantic_actions_t::new_shortcut, &self.actions_, _1, _2 ) );
241  Str_action new_mult_factor ( boost::bind( &Semantic_actions_t::new_mult_factor, &self.actions_, _1, _2 ) );
242  Str_action new_div_factor ( boost::bind( &Semantic_actions_t::new_div_factor, &self.actions_, _1, _2 ) );
243  Real_action new_multipl ( boost::bind( &Semantic_actions_t::new_multipl, &self.actions_, _1 ) );
244  Int_action new_exp ( boost::bind( &Semantic_actions_t::new_exp, &self.actions_, _1 ) );
245  Str_action throw_exp_not_int ( boost::bind( &Semantic_actions_t::throw_exp_not_int, &self.actions_, _1, _2 ) );
246  Str_action throw_not_equating ( boost::bind( &Semantic_actions_t::throw_not_equating, &self.actions_, _1, _2 ) );
247  Str_action throw_not_shortcut ( boost::bind( &Semantic_actions_t::throw_not_shortcut, &self.actions_, _1, _2 ) );
248 
249  // actual grammer
250 
251  unit_
252  = formula_ >> *( ch_p(';') >> *space_p >> constant_ )
253  ;
254 
255  formula_
256  = formula_factor_[ new_mult_factor ] >> !( ch_p('^') >> exp_ )
257  >> *( (ch_p('*') >> formula_factor_[ new_mult_factor ] >> !( ch_p('^') >> exp_ ) )
258  | (ch_p('/') >> formula_factor_[ new_div_factor ] >> !( ch_p('^') >> exp_ ) )
259  )
260  ;
261 
262  formula_factor_
263  = forbidden_char_[ throw_not_shortcut ]
264  | ( alpha_p >> *( anychar_p - ch_p(';') - ch_p('*') - ch_p('/') - ch_p('=') - ch_p('^') - space_p ) )
265  | shortcut_err_[ throw_not_shortcut ]
266  ;
267 
268  constant_
269  = ( formula_factor_[ new_shortcut ] >> *space_p
270  >> ch_p('=') >> *space_p >> constant_value_ )
271  | constant_err_[ throw_not_equating ]
272  ;
273 
274  constant_value_
275  = !(constant_multiplicator_ >> ch_p('*'))
276  >> formula_
277  ;
278 
279  constant_err_
280  = +( anychar_p - ch_p(';') )
281  ;
282 
283  constant_multiplicator_
284  = strict_real_p[ new_multipl ]
285  | int64_p[ new_multipl ]
286  ;
287 
288  exp_
289  = int64_p[ new_exp ]
290  | exp_err_[ throw_exp_not_int ]
291  ;
292 
293  exp_err_
294  = +( anychar_p - ch_p(';') - ch_p('*') - ch_p('/') - space_p )
295  ;
296 
297  shortcut_err_
298  = *( anychar_p - ch_p(';') - ch_p('*') - ch_p('/') - ch_p(';') - ch_p('^') )
299  ;
300 
301  forbidden_char_
302  = *( alnum_p | ch_p('_') )
303  >> ( anychar_p - alnum_p - ch_p('_') - ch_p(';') - ch_p('*') - ch_p('/') - ch_p('=') - ch_p('^') - space_p )
304  >> *( alnum_p | ch_p('_') )
305  ;
306 
307  }
308 
309  spirit_namespace::rule< ScannerT > unit_, formula_, formula_factor_, exp_, exp_err_, constant_,
310  constant_value_, constant_err_, constant_multiplicator_, shortcut_err_, forbidden_char_;
311 
312  const spirit_namespace::rule< ScannerT >& start() const { return unit_; }
313  };
314 
315 private:
316 
317  UnitSIGrammer& operator=( const UnitSIGrammer& ); // to prevent "assignment operator could not be generated" warning
318 
319  Semantic_actions_t& actions_;
320 };
321 
322 
323 } // end namespace units_converter
324 
325 #endif /* UNIT_CONVERTER_TEMPLATE_HH_ */
int factor_idx_
index to actual item of subvector of factors of unit_data_
UnitsMap units_map_
Define all base and derived units given by their symbol.
void throw_not_shortcut(Iter_type begin, Iter_type end)
Throw exception if shortcut of factor is not in correct format.
void throw_not_equating(Iter_type begin, Iter_type end)
Throw exception if sign &#39;=&#39; missing in definition.
#define spirit_namespace
std::string get_str(std::string::const_iterator begin, std::string::const_iterator end) const
void check_unit_data()
Check unit_data_ object.
Definition of unit grammar.
Semantic_actions & operator=(const Semantic_actions &)
void new_shortcut(Iter_type begin, Iter_type end)
Add new definition of formula.
void new_multipl(double d)
Add multipicative coeficient of unit.
const spirit_namespace::uint_parser< boost::uint64_t > uint64_p
Class manages parsing of user defined field unit.
void new_mult_factor(Iter_type begin, Iter_type end)
Add new factor of unit (factor is multiplying)
Semantic_actions< Iter_type > Semantic_actions_t
void new_exp(boost::int64_t i)
Compute exponent to actual factor of unit.
const spirit_namespace::rule< ScannerT > & start() const
const spirit_namespace::int_parser< boost::int64_t > int64_p
void throw_exp_not_int(Iter_type begin, Iter_type end)
Throw exception if exponent is not in correct format.
std::string unit_data_key_
key of actual item of unit_data_
Store structure given by parser.
UnitSIGrammer(Semantic_actions_t &semantic_actions)
Constructor.
void new_div_factor(Iter_type begin, Iter_type end)
Add new factor of unit (factor is dividing)
static const BasicFactors basic_factors
Define all base and derived units given by their symbol.
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53