calc.hpp

Go to the documentation of this file.
00001 //////////////////////////////////////////////////////////////////////////
00002 //    This material is provided "as is", with absolutely no warranty
00003 //  expressed or implied. Any use is at your own risk.
00004 //
00005 //    Permission to use or copy this software for any purpose is hereby
00006 //  granted without fee, provided the above notices are retained on all
00007 //  copies. Permission to modify the code and to distribute modified code
00008 //  is granted, provided the above notices are retained, and a notice that
00009 //  the code was modified is included with the above copyright notice.
00010 //////////////////////////////////////////////////////////////////////////
00011 /** @file
00012   @brief Interface of the calculator tools.
00013 
00014 @author Sergey Polichnoy
00015 @see @ref omni_calc
00016 */
00017 #ifndef __OMNI_CALC_HPP_
00018 #define __OMNI_CALC_HPP_
00019 
00020 #include <omni/defs.hpp>
00021 
00022 #include <stdexcept>
00023 #include <iomanip>
00024 #include <istream>
00025 #include <sstream>
00026 #include <limits>
00027 #include <string>
00028 #include <map>
00029 
00030 #include <assert.h>
00031 
00032 namespace omni
00033 {
00034   // forward declarations
00035   namespace calc
00036   {
00037 
00038     // main classes
00039     template<typename T>
00040       class Calculator;
00041     template<typename T>
00042       class FuncTable;
00043 
00044     template<typename T>
00045       class UserFunc;
00046     template<typename T, long>
00047       class Multiplier;
00048     template<typename T, long>
00049       class Divider;
00050 
00051 
00052     // exceptions
00053     namespace err
00054     {
00055       class Failure;
00056         class SyntaxError;
00057           class UnknownFunctionCall;
00058         class CalculationError;
00059           class DivisionByZero;
00060         class InvalidFunctionName;
00061     }
00062 
00063     // implementation
00064     /// @cond details
00065     namespace details
00066     {
00067       template<typename T, typename Ch, typename Tr>
00068         T eval(std::basic_istream<Ch, Tr>&, const Calculator<T>&);
00069       template<typename T, typename Ch, typename Tr, typename Ax>
00070         T eval(const std::basic_string<Ch, Tr, Ax>&, const Calculator<T>&);
00071       template<typename T, typename Ch>
00072         T eval(const Ch*, const Calculator<T>&);
00073       bool check_func_name(const std::wstring &name);
00074     }
00075     /// @endcond
00076 
00077   } // forward declarations
00078 
00079 
00080   // Calculator
00081   namespace calc
00082   {
00083 
00084 //////////////////////////////////////////////////////////////////////////
00085 /// @brief Калькулятор.
00086 /**
00087     Класс содержит две таблицы функций: префиксную и постфиксную.
00088   Используется для смены набора функций при вычислении выражений.
00089 
00090     Калькуляторы можно копировать.
00091 
00092     TODO: detail description
00093 
00094 @param T тип возвращаемого значения, например double, float, int.
00095 
00096 @see @ref omni_calc
00097 */
00098 template<typename T>
00099 class Calculator {
00100 public:
00101   typedef FuncTable<T> TableType;  ///< @brief The table of functions.
00102   typedef           T  ValueType;  ///< @brief The value type.
00103 
00104 
00105 //////////////////////////////////////////////////////////////////////////
00106 /// @name Вычислить выражение
00107 /// @{
00108 public:
00109 
00110 //////////////////////////////////////////////////////////////////////////
00111 /// @brief Вычислить первое выражение из потока ввода.
00112 /**
00113     Метод вычисляет первое выражение из потока ввода @a expression
00114   пока не будет достигнуто окончание выражения (которое может не
00115   совпадать с окончанием потока ввода).
00116 
00117     Если поток ввода не содержит выражения или выражение некорректно,
00118   будет сгенерировано исключение.
00119 
00120 @param[in,out] expression Поток ввода.
00121 @return Значение выражения.
00122 @throw omni::calc::ex::SyntaxError Если выражение некорректно.
00123 @throw omni::calc::ex::CalculationError Если выражение содержит вычислительные ошибки.
00124 */
00125   template<typename Ch, typename Tr>
00126     ValueType operator()(std::basic_istream<Ch, Tr> &expression) const
00127   {
00128     return details::eval(expression, *this);
00129   }
00130 
00131 
00132 //////////////////////////////////////////////////////////////////////////
00133 /// @brief Вычислить выражение из строки.
00134 /**
00135     Метод вычисляет выражение из строки @a expression.
00136 
00137     Если выражение некорректно или строка содержит более
00138   одного выражения будет сгенерировано исключение.
00139 
00140 @param[in] expression Строковое выражение.
00141 @return Значение выражения.
00142 @throw omni::calc::ex::SyntaxError Если выражение некорректно.
00143 @throw omni::calc::ex::CalculationError Если выражение содержит вычислительные ошибки.
00144 */
00145   template<typename Ch, typename Tr, typename Ax>
00146     ValueType operator()(const std::basic_string<Ch, Tr, Ax> &expression) const
00147   {
00148     return details::eval(expression, *this);
00149   }
00150 
00151 
00152 //////////////////////////////////////////////////////////////////////////
00153 /// @brief Вычислить выражение из C-строки.
00154 /**
00155     Метод вычисляет выражение из C-строки @a expression.
00156 
00157     Если выражение некорректно или строка содержит более
00158   одного выражения будет сгенерировано исключение.
00159 
00160 @param[in] expression Строковое выражение.
00161 @return Значение выражения.
00162 @throw omni::calc::ex::SyntaxError Если выражение некорректно.
00163 @throw omni::calc::ex::CalculationError Если выражение содержит вычислительные ошибки.
00164 */
00165   template<typename Ch>
00166     ValueType operator()(const Ch *expression) const
00167   {
00168     return details::eval(expression, *this);
00169   }
00170 
00171 /// @}
00172 //////////////////////////////////////////////////////////////////////////
00173 
00174 //////////////////////////////////////////////////////////////////////////
00175 /// @name Таблицы функций
00176 /// @{
00177 public:
00178 
00179 //////////////////////////////////////////////////////////////////////////
00180 /// @brief Таблица префиксных функций
00181 /**
00182     Метод возвращает таблицу префиксных функций.
00183 */
00184   const TableType& prefix() const
00185   {
00186     return m_prefix;
00187   }
00188 
00189 
00190 //////////////////////////////////////////////////////////////////////////
00191 /// @brief Таблица префиксных функций
00192 /**
00193     Метод возвращает таблицу префиксных функций.
00194 */
00195   TableType& prefix()
00196   {
00197     return m_prefix;
00198   }
00199 
00200 
00201 //////////////////////////////////////////////////////////////////////////
00202 /// @brief Таблица суффиксных функций
00203 /**
00204     Метод возвращает таблицу суфиксных функций.
00205 */
00206   const TableType& suffix() const
00207   {
00208     return m_suffix;
00209   }
00210 
00211 
00212 //////////////////////////////////////////////////////////////////////////
00213 /// @brief Таблица суффиксных функций
00214 /**
00215     Метод возвращает таблицу суфиксных функций.
00216 */
00217   TableType& suffix()
00218   {
00219     return m_suffix;
00220   }
00221 
00222 /// @}
00223 //////////////////////////////////////////////////////////////////////////
00224 
00225 //////////////////////////////////////////////////////////////////////////
00226 /// @name Свойства
00227 /// @{
00228 public:
00229 
00230 //////////////////////////////////////////////////////////////////////////
00231 /// @brief Целочисленная арифметика
00232 /**
00233     Метод возвращает @b true если калькулятор предназначен для вычисления
00234   только целочисленных выражений, иначе @b false (т.е. калькулятор
00235   предназначен для вычисления вещественных выражений).
00236 */
00237   bool is_integer() const
00238   {
00239     assert(std::numeric_limits<ValueType>::is_specialized); // (?) static check
00240     return std::numeric_limits<ValueType>::is_integer;
00241   }
00242 
00243 /// @}
00244 //////////////////////////////////////////////////////////////////////////
00245 
00246 private:
00247   TableType m_prefix;
00248   TableType m_suffix;
00249 };
00250 
00251   } // Calculator
00252 
00253 
00254   // auxiliary functions
00255   namespace calc
00256   {
00257 
00258 //////////////////////////////////////////////////////////////////////////
00259 /// @brief Вычислить вещественное выражение из строки
00260 /**
00261     Функция вычисляет вещественное выражение из строки @a expression.
00262 
00263     Если выражение некорректно или строка содержит более
00264   одного выражения будет сгенерировано исключение.
00265 
00266 @param[in] expression Строковое выражение
00267 @return Значение выражения
00268 @throw omni::calc::SyntaxError Если выражение некорректно
00269 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
00270 @see @ref omni_calc
00271 */
00272 template<typename Ch, typename Tr, typename Ax> inline
00273   double evalf(const std::basic_string<Ch, Tr, Ax> &expression)
00274 {
00275   Calculator<double> x;
00276   return x(expression);
00277 }
00278 
00279 //////////////////////////////////////////////////////////////////////////
00280 /// @brief Вычислить вещественное выражение из C-строки
00281 /**
00282     Функция вычисляет вещественное выражение из C-строки @a expression.
00283 
00284     Если выражение некорректно или строка содержит более
00285   одного выражения будет сгенерировано исключение.
00286 
00287 @param[in] expression Строковое выражение
00288 @return Значение выражения
00289 @throw omni::calc::SyntaxError Если выражение некорректно
00290 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
00291 @see @ref omni_calc
00292 */
00293 template<typename Ch> inline
00294   double evalf(const Ch *expression)
00295 {
00296   Calculator<double> x;
00297   return x(expression);
00298 }
00299 
00300 //////////////////////////////////////////////////////////////////////////
00301 /// @brief Вычислить целое выражение из строки
00302 /**
00303     Функция вычисляет целое выражение из строки @a expression.
00304 
00305     Если выражение некорректно или строка содержит более
00306   одного выражения будет сгенерировано исключение.
00307 
00308 @param[in] expression Строковое выражение
00309 @return Значение выражения
00310 @throw omni::calc::SyntaxError Если выражение некорректно
00311 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
00312 @see @ref omni_calc
00313 */
00314 template<typename Ch, typename Tr, typename Ax> inline
00315   long evali(const std::basic_string<Ch, Tr, Ax> &expression)
00316 {
00317   Calculator<long> x;
00318   return x(expression);
00319 }
00320 
00321 //////////////////////////////////////////////////////////////////////////
00322 /// @brief Вычислить целое выражение из C-строки
00323 /**
00324     Функция вычисляет целое выражение из C-строки @a expression.
00325 
00326     Если выражение некорректно или строка содержит более
00327   одного выражения будет сгенерировано исключение.
00328 
00329 @param[in] expression Строковое выражение
00330 @return Значение выражения
00331 @throw omni::calc::SyntaxError Если выражение некорректно
00332 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
00333 @see @ref omni_calc
00334 */
00335 template<typename Ch> inline
00336   long evali(const Ch *expression)
00337 {
00338   Calculator<long> x;
00339   return x(expression);
00340 }
00341 
00342 // common calculator: sci
00343 const Calculator<double>& sci();
00344 
00345 // common calculator: sci
00346 template<typename Ch, typename Tr, typename Ax> inline
00347   double sci(const std::basic_string<Ch, Tr, Ax> &expression)
00348 {
00349   return sci()(expression);
00350 }
00351 
00352 // common calculator: sci
00353 template<typename Ch> inline
00354   double sci(const Ch *expression)
00355 {
00356   return sci()(expression);
00357 }
00358 
00359 
00360 // common calculator: ratio
00361 const Calculator<double>& ratio();
00362 // common calculator: ratio
00363 template<typename Ch, typename Tr, typename Ax> inline
00364   double ratio(const std::basic_string<Ch, Tr, Ax> &expression)
00365 {
00366   return ratio()(expression);
00367 }
00368 
00369 // common calculator: ratio
00370 template<typename Ch> inline
00371   double ratio(const Ch *expression)
00372 {
00373   return ratio()(expression);
00374 }
00375 
00376 
00377 // common calculator: power
00378 const Calculator<double>& power();
00379 // common calculator: power
00380 template<typename Ch, typename Tr, typename Ax> inline
00381   double power(const std::basic_string<Ch, Tr, Ax> &expression)
00382 {
00383   return power()(expression);
00384 }
00385 
00386 // common calculator: power
00387 template<typename Ch> inline
00388   double power(const Ch *expression)
00389 {
00390   return power()(expression);
00391 }
00392 
00393 
00394 // common calculator: time
00395 const Calculator<double>& time();
00396 // common calculator: time
00397 template<typename Ch, typename Tr, typename Ax> inline
00398   double time(const std::basic_string<Ch, Tr, Ax> &expression)
00399 {
00400   return time()(expression);
00401 }
00402 
00403 // common calculator: time
00404 template<typename Ch> inline
00405   double time(const Ch *expression)
00406 {
00407   return time()(expression);
00408 }
00409 
00410 // common calculator: freq
00411 const Calculator<double>& freq();
00412 // common calculator: freq
00413 template<typename Ch, typename Tr, typename Ax> inline
00414   double freq(const std::basic_string<Ch, Tr, Ax> &expression)
00415 {
00416   return freq()(expression);
00417 }
00418 
00419 // common calculator: freq
00420 template<typename Ch> inline
00421   double freq(const Ch *expression)
00422 {
00423   return freq()(expression);
00424 }
00425 
00426 
00427 // common calculator: bits
00428 const Calculator<double>& bits();
00429 // common calculator: bits
00430 template<typename Ch, typename Tr, typename Ax> inline
00431   double bits(const std::basic_string<Ch, Tr, Ax> &expression)
00432 {
00433   return bits()(expression);
00434 }
00435 
00436 // common calculator: bits
00437 template<typename Ch> inline
00438   double bits(const Ch *expression)
00439 {
00440   return bits()(expression);
00441 }
00442 
00443 
00444 // common calculator: distance
00445 const Calculator<double>& dist();
00446 // common calculator: distance
00447 template<typename Ch, typename Tr, typename Ax> inline
00448   double dist(const std::basic_string<Ch, Tr, Ax> &expression)
00449 {
00450   return dist()(expression);
00451 }
00452 
00453 // common calculator: distance
00454 template<typename Ch> inline
00455   double dist(const Ch *expression)
00456 {
00457   return dist()(expression);
00458 }
00459 
00460   } // auxiliary functions
00461 
00462 
00463 
00464   // user functions...
00465   namespace calc
00466   {
00467 
00468 //////////////////////////////////////////////////////////////////////////
00469 /// @brief Обёртка для пользовательской функции
00470 /**
00471     Класс служит обёрткой для функций вида
00472 @code
00473   T (*f)(T)
00474 @endcode
00475 
00476     Используется для расширения функциональности калькулятора
00477   префиксными или суфиксными функциями.
00478 */
00479 template<typename T>
00480 class UserFunc {
00481 public:
00482   typedef T (*Function)(T);  ///< @brief Пользовательская функция
00483   typedef T Argument;        ///< @brief Тип аргумента пользовательской функции
00484   typedef T Result;          ///< @brief Тип результата пользовательской функции
00485 
00486   friend class FuncTable<T>;
00487 
00488 private:
00489 
00490 //////////////////////////////////////////////////////////////////////////
00491 /// @brief Default constructor.
00492   UserFunc()
00493     : m_func(0)
00494   {}
00495 
00496 public:
00497 
00498 //////////////////////////////////////////////////////////////////////////
00499 /// @brief Конструктор с заданием пользовательской функции
00500 /**
00501     Конструктор сохраняет пользовательскую функцию @a f.
00502 
00503 @param[in] f Пользовательская функция
00504 */
00505 explicit UserFunc(Function f)
00506   : m_func(f)
00507 {}
00508 
00509 
00510 public:
00511 
00512 //////////////////////////////////////////////////////////////////////////
00513 /// @brief Выполнить функцию
00514 /**
00515     Метод вызывает пользовательскую функцию с аргументом @a x.
00516 
00517 @param[in] x Аргумент функции
00518 @return Результат функции
00519 */
00520   Result operator()(Argument x) const
00521   {
00522     return m_func(x);
00523   }
00524 
00525 private:
00526   Function m_func;
00527 };
00528 
00529 
00530 //////////////////////////////////////////////////////////////////////////
00531 /// @brief Таблица пользовательских функций
00532 /**
00533     Класс представляет собой таблицу пользовательских функций.
00534   Позволяет добавлять, удалять и выполнять функцию по строковому имени.
00535 */
00536 template<typename T>
00537 class FuncTable {
00538 public:
00539   typedef UserFunc<T> Function;   ///< @brief Пользовательская функция
00540 
00541 public:
00542 
00543 //////////////////////////////////////////////////////////////////////////
00544 /// @brief Добавить пользовательскую функцию
00545 /**
00546     Метод добавляет в таблицу новую пользовательскую функцию @a f
00547   с именем @a name. Если имя функции некорректно или функция с таким
00548   именем уже существует, то будет сгенерировано исключение.
00549 
00550 @param[in] name Имя пользовательской функции
00551 @param[in] f Пользовательская функция
00552 @throw omni::calc::InvalidFunctionName
00553   Если имя функции не является корректным
00554   или функция с таким именем уже существует
00555 */
00556   void insert(const std::wstring &name, const Function &f)
00557   {
00558     if (!details::check_func_name(name))
00559       throw err::InvalidFunctionName("invalid function name", name); // invalid name
00560 
00561     if (m_table.find(name) != m_table.end())
00562       throw err::InvalidFunctionName("function already exists", name); // already exists
00563 
00564     m_table.insert(std::make_pair(name, f));
00565   }
00566 
00567 
00568 //////////////////////////////////////////////////////////////////////////
00569 /// @brief Удалить пользовательскую функцию
00570 /**
00571     Метод удаляет из таблицы пользовательскую функцию с именем @a name.
00572 
00573 @param[in] name Имя пользовательской функции
00574 @throw omni::calc::InvalidFunctionName
00575   Если функции с таким именем не существует
00576 */
00577   void remove(const std::wstring &name)
00578   {
00579     typename Table::iterator found = m_table.find(name);
00580     if (found == m_table.end())
00581       throw err::InvalidFunctionName("function not exists", name); // not exists
00582 
00583     m_table.erase(found);
00584   }
00585 
00586 
00587 //////////////////////////////////////////////////////////////////////////
00588 /// @brief Проверить задана ли пользовательская функция
00589 /**
00590     Метод проверяет наличие в таблице функции с именем @a name.
00591 
00592 @param[in] name Имя пользовательской функции
00593 @return @b true Если функция существует, иначе @b false
00594 */
00595   bool exists(const std::wstring &name) const
00596   {
00597     return (m_table.find(name) != m_table.end());
00598   }
00599 
00600 
00601 //////////////////////////////////////////////////////////////////////////
00602 /// @brief Выполнить пользовательскую функцию
00603 /**
00604     Метод выполняет пользовательскую функцию с именем @a name. Если функции
00605   с заданным именем нет в таблице, будет сгенерировано исключение.
00606 
00607 @param[in] name Имя пользовательской функции
00608 @param[in] arg Аргумент пользовательской функции
00609 @return Результат выполнения пользовательской функции
00610 @throw omni::calc::UnknownFunctionCall
00611   Если функции с заданным именем нет в таблице
00612 */
00613   T operator()(const std::wstring &name, T arg) const
00614   {
00615     typename Table::const_iterator found = m_table.find(name);
00616     if (found == m_table.end())
00617       throw err::UnknownFunctionCall(name);
00618 
00619     return (*found).second(arg);
00620   }
00621 
00622 private:
00623   typedef std::map<std::wstring, Function> Table;
00624   Table m_table;
00625 };
00626 
00627 
00628 //////////////////////////////////////////////////////////////////////////
00629 /// @brief Функция масштабирования (умножение)
00630 /**
00631   Используется в качестве пользовательской функции масштабирования.
00632 
00633     Например, следующий код добавляет в калькулятор функции "Mb" и "Kb":
00634 
00635 @code
00636 template<typename T>
00637   void f(Calculator<T> &x)
00638   {
00639     x.suffix().insert(L"Mb", Multiplier<T, 1024*1024>());
00640     x.suffix().insert(L"Kb", Multiplier<T, 1024>());
00641   }
00642 @endcode
00643 */
00644 template<typename T, long SCALE>
00645 class Multiplier: public UserFunc<T> {
00646   typedef UserFunc<T> inherited;
00647 
00648 public:
00649   typedef typename inherited::Argument Argument; ///< @brief The argument type.
00650   typedef typename inherited::Result Result;   ///< @brief The result type.
00651 
00652   enum { scale = SCALE };
00653 
00654 public:
00655 
00656 //////////////////////////////////////////////////////////////////////////
00657 /// @brief Default constructor.
00658   Multiplier()
00659     : inherited(func)
00660   {}
00661 
00662 private:
00663 
00664 //////////////////////////////////////////////////////////////////////////
00665 /// @brief Функция масштабирования
00666 /**
00667     Метод выполняет увеличение аргумента @a x в @a SCALE раз.
00668 
00669 @param[in] x Аргумент функции
00670 @return Результат масштабирования
00671 */
00672   static Result func(Argument x)
00673   {
00674     return x * SCALE;
00675   }
00676 };
00677 
00678 
00679 //////////////////////////////////////////////////////////////////////////
00680 /// @brief Функция масштабирования (деление)
00681 /**
00682   Используется в качестве пользовательской функции масштабирования.
00683 
00684     Например, следующий код добавляет в калькулятор функции "ms" и "us":
00685 
00686 @code
00687 template<typename T>
00688   void f(Calculator<T> &x)
00689   {
00690     x.suffix().insert(L"ms", Divider<T, 1000>());
00691     x.suffix().insert(L"us", Divider<T, 1000*1000>());
00692   }
00693 @endcode
00694 */
00695 template<typename T, long SCALE>
00696 class Divider: public UserFunc<T> {
00697   typedef UserFunc<T> inherited;
00698 
00699 public:
00700   typedef typename inherited::Argument Argument; ///< @brief The argument type.
00701   typedef typename inherited::Result Result;   ///< @brief The result type.
00702 
00703   enum { scale = SCALE };
00704 
00705 public:
00706 
00707 //////////////////////////////////////////////////////////////////////////
00708 /// @brief The default constructor.
00709   Divider()
00710     : inherited(func)
00711   {}
00712 
00713 private:
00714 
00715 //////////////////////////////////////////////////////////////////////////
00716 /// @brief Функция масштабирования
00717 /**
00718     Метод выполняет уменьшение аргумента @a x в @a SCALE раз.
00719 
00720 @param[in] x Аргумент функции
00721 @return Результат масштабирования
00722 */
00723   static Result func(Argument x)
00724   {
00725     return x / SCALE;
00726   }
00727 };
00728 
00729   } // calc namespace
00730 
00731 
00732 
00733   // exceptions...
00734   namespace calc
00735   {
00736     namespace err
00737     {
00738 
00739 //////////////////////////////////////////////////////////////////////////
00740 /// @brief Базовое исключение
00741 /**
00742     Класс является базовым для всех исключений калькулятора.
00743 */
00744 class Failure: public std::runtime_error {
00745   typedef std::runtime_error inherited;
00746 
00747 protected:
00748   explicit Failure(const std::string &msg);
00749   explicit Failure(const char *msg);
00750   virtual ~Failure() OMNI_THROW0() {}
00751 };
00752 
00753 
00754 //////////////////////////////////////////////////////////////////////////
00755 /// @brief Синтаксическая ошибка
00756 /**
00757     Исключение генерируется, если в выражении встретилась синтаксическая
00758   ошибка. Это может быть:
00759 
00760     - отсутствие ожидаемого выражения
00761     - неверно расставленные скобки
00762 */
00763 class SyntaxError: public Failure {
00764   typedef Failure inherited;
00765 
00766 public:
00767   explicit SyntaxError(const std::string &msg);
00768   explicit SyntaxError(const char *msg);
00769   virtual ~SyntaxError() OMNI_THROW0() {}
00770 };
00771 
00772 
00773 //////////////////////////////////////////////////////////////////////////
00774 /// @brief Вызов несуществующей функции
00775 /**
00776     Исключение генерируется, если в выражении встретился вызов
00777   несуществующей функции. Имя функции возвращается методом funcName().
00778 */
00779 class UnknownFunctionCall: public SyntaxError {
00780   typedef SyntaxError inherited;
00781 
00782 public:
00783   explicit UnknownFunctionCall(const std::wstring &func_name);
00784   virtual ~UnknownFunctionCall() OMNI_THROW0() {}
00785 
00786 public:
00787   const std::wstring& funcName() const;
00788 
00789 private:
00790   std::wstring m_func_name;
00791 };
00792 
00793 
00794 //////////////////////////////////////////////////////////////////////////
00795 /// @brief Ошибка вычислений
00796 /**
00797     Исключения является базовым для ошибок вычисления.
00798 */
00799 class CalculationError: public Failure {
00800   typedef Failure inherited;
00801 
00802 protected:
00803   explicit CalculationError(const std::string &msg);
00804   explicit CalculationError(const char *msg);
00805   virtual ~CalculationError() OMNI_THROW0() {}
00806 };
00807 
00808 
00809 //////////////////////////////////////////////////////////////////////////
00810 /// @brief Деление на ноль
00811 /**
00812     Исключение генерируется если в выражении встречается
00813   целочисленное деление на ноль.
00814 */
00815 class DivisionByZero: public CalculationError {
00816   typedef CalculationError inherited;
00817 public:
00818   DivisionByZero();
00819   virtual ~DivisionByZero() OMNI_THROW0() {}
00820 };
00821 
00822 
00823 //////////////////////////////////////////////////////////////////////////
00824 /// @brief Некорректное имя функций
00825 /**
00826     Исключение используется при операциях с таблицами функций.
00827   При добавлении функции генерируется, если имя функции некорректно или
00828   функция с таким именем уже существует. При удалении функции генерируется,
00829   если функции с таким именем нет.
00830 */
00831 class InvalidFunctionName: public Failure {
00832   typedef Failure inherited;
00833 
00834 public:
00835   explicit InvalidFunctionName(const std::wstring &func_name);
00836   InvalidFunctionName(const std::string &msg, const std::wstring &func_name);
00837   InvalidFunctionName(const char *msg, const std::wstring &func_name);
00838   virtual ~InvalidFunctionName() OMNI_THROW0() {}
00839 
00840 public:
00841   const std::wstring& funcName() const;
00842 
00843 private:
00844   std::wstring m_func_name;
00845 };
00846 
00847     } // err namespace
00848   } // calc namespace
00849 
00850 
00851   // constants...
00852   namespace calc
00853   {
00854     /// @cond details
00855     namespace details
00856     {
00857 
00858 //////////////////////////////////////////////////////////////////////////
00859 // @brief Константы
00860 /*
00861     Класс содержит константы для обычного (@b char)
00862   и расширенного (@b wchar_t) символьных наборов.
00863 
00864     Константы FLOAT_CHARS и INT_CHARS определяют символы, с которых
00865   могут начинаться вещественные и целые числа соответственно.
00866 
00867     Если целое число начинается с OCT_INDICATOR, то число будет
00868   интерпретироваться в восьмеричной системе. Если сразу за OCT_INDICATOR
00869   расположен символ HEX_INDICATOR1 или HEX_INDICATOR2, то число будет
00870   интерпретироваться в шестнадцатеричной системе.
00871 
00872     Константы BRACE_OPEN и BRACE_CLOSE обозначают открывающуюся
00873   и закрывающуюся скобки соответственно.
00874 
00875     Следующие константы обозначают основные арифметические операции:
00876       - OP_ADD - сложение
00877       - OP_SUB - вычитание
00878       - OP_MUL - умножение
00879       - OP_DIV - деление
00880 
00881 @param Ch Тип символа
00882 */
00883 template<typename Ch>
00884 class CharConst {
00885 public:
00886   typedef Ch Char; ///< @brief The char type.
00887 
00888 public:
00889   static const Char FLOAT_CHARS[]; ///< @brief Цифры вещественного числа
00890   static const Char INT_CHARS[];   ///< @brief Цифры целого числа
00891 
00892 public:
00893   static const Char OCT_INDICATOR;  ///< @brief Индикатор восмеричной или шестнадцатеричной системы счисления
00894   static const Char HEX_INDICATOR1; ///< @brief Первый индикатор шестандцатеричной системы счисления
00895   static const Char HEX_INDICATOR2; ///< @brief Второй индикатор шестандцатеричной системы счисления
00896 
00897   static const Char BRACE_OPEN;  ///< @brief Открывающая скобка
00898   static const Char BRACE_CLOSE; ///< @brief Закрывающая скобка
00899   static const Char OP_ADD; ///< @brief Унарный или бинарный плюс
00900   static const Char OP_SUB; ///< @brief Унарный или бинарный минус
00901   static const Char OP_MUL; ///< @brief Бинарное умножение
00902   static const Char OP_DIV; ///< @brief Бинарное деление
00903 
00904 public:
00905   static bool is_float_digit(Char cx);
00906   static bool is_int_digit(Char cx);
00907   static wchar_t widen(Char cx,
00908     const std::ctype<Char> &fac);
00909 };
00910 
00911     } // details namespace
00912     /// @endcond
00913   } // calc namespace
00914 
00915 
00916   // Evaluating implementation...
00917   namespace calc
00918   {
00919     /// @cond details
00920     namespace details
00921     {
00922 
00923 //////////////////////////////////////////////////////////////////////////
00924 /// @brief Прочитать имя функции из потока ввода
00925 /**
00926     Возвращает имя функции из потока ввода @a is.
00927   Имя функции не должно начинаться с цифры, не должно содержать пробелов.
00928   Может вернуть пустую строку. Для расширенных символов выполняет
00929   преобразование @a char в @a wchar_t.
00930 
00931 @param[in,out] is Поток ввода
00932 @return Имя функции
00933 */
00934 template<typename Ch, typename Tr>
00935   std::wstring get_func_name(std::basic_istream<Ch, Tr> &is)
00936 {
00937   typedef std::basic_istream<Ch, Tr> istream_type;
00938   typedef std::basic_string<Ch, Tr> string_type;
00939   typedef istream_type::traits_type traits_type;
00940   typedef CharConst<Ch> calc_traits;
00941   typedef std::ctype<Ch> facet_type;
00942 
00943   std::ios::iostate state = std::ios::goodbit;
00944   const istream_type::sentry ok(is); // (!) skip white space
00945 
00946   std::wstring func_name;
00947   // func_name.reserve(32);
00948 
00949   if (ok)
00950   {
00951     const facet_type &fac = OMNI_USE_FACET(is.getloc(), facet_type);
00952 
00953     for (bool first_char = true;; first_char = false)
00954     {
00955       typename traits_type::int_type meta = is.peek();
00956 
00957       if (traits_type::eq_int_type(meta, traits_type::eof()))
00958       {
00959         state |= std::ios::eofbit;
00960         break;
00961       }
00962 
00963       typename traits_type::char_type cx = traits_type::to_char_type(meta);
00964       if (first_char) // first char must be "alpha"
00965       {
00966         if (!fac.is(facet_type::alpha, cx))
00967           break;
00968 
00969         if (calc_traits::is_float_digit(cx)
00970           || calc_traits::is_int_digit(cx))
00971             break;
00972       }
00973 
00974       if (!fac.is(facet_type::alnum, cx))
00975         break;
00976 
00977       func_name += calc_traits::widen(cx, fac);
00978       is.ignore();
00979     }
00980   }
00981 
00982   is.setstate(state);
00983   return func_name;
00984 }
00985 
00986 
00987 //////////////////////////////////////////////////////////////////////////
00988 /// @brief Прочитать число из потока ввода
00989 /**
00990     Функция пытается прочитать число из потока ввода @a is.
00991   Если число прочитано, то оно возвращается в переменной @a x.
00992 
00993 @param[in] is Поток ввода
00994 @param[out] x Возврашает число
00995 @param[in] is_integer Is integer calculator?
00996 @return @b true если число успешно прочитано, иначе @b false
00997 */
00998 template<typename T, typename Ch, typename Tr>
00999   bool get_num(std::basic_istream<Ch, Tr> &is, T &x, bool is_integer)
01000 {
01001   typedef std::basic_istream<Ch, Tr> istream_type;
01002   typedef std::basic_string<Ch, Tr> string_type;
01003   typedef istream_type::traits_type traits_type;
01004   typedef CharConst<Ch> calc_traits;
01005 
01006   typename traits_type::int_type meta = is.peek();
01007   if (!traits_type::eq_int_type(meta, traits_type::eof()))
01008   {
01009     typename traits_type::char_type cx = traits_type::to_char_type(meta);
01010 
01011     if (is_integer)
01012     { // integer value
01013       if (calc_traits::is_int_digit(cx))
01014       {
01015         if (traits_type::eq(cx, calc_traits::OCT_INDICATOR)) // check for "oct" or "hex"
01016         {
01017           meta = is.ignore().peek();
01018 
01019           if (!traits_type::eq_int_type(meta, traits_type::eof()))
01020           {
01021             cx = traits_type::to_char_type(meta);
01022 
01023             if (traits_type::eq(cx, calc_traits::HEX_INDICATOR1)
01024               || traits_type::eq(cx, calc_traits::HEX_INDICATOR2))
01025             {
01026               if (is.ignore() >> std::hex >> x)
01027                 return true;
01028             }
01029             else if (calc_traits::is_int_digit(cx))
01030             {
01031               if (is >> std::oct >> x)
01032                 return true;
01033             }
01034             else // (!) simple zero
01035             {
01036               x = T();
01037               return true;
01038             }
01039           }
01040           else
01041           {
01042             is.clear(); // clear "eof" bit
01043             is.putback(cx);
01044           }
01045         }
01046 
01047         // parse "dec" integer
01048         if (is >> std::dec >> x)
01049           return true;
01050       }
01051     }
01052     else
01053     { // float value
01054       if (calc_traits::is_float_digit(cx))
01055       {
01056         if (is >> x)
01057           return true;
01058       }
01059     }
01060   }
01061 
01062   return false;
01063 }
01064 
01065 
01066 //////////////////////////////////////////////////////////////////////////
01067 /// @brief Level 3 expression parsing
01068 /**
01069     Функция пробует прочитать число, унарный плюс или минус, скобки или
01070   вызов префиксной функции. Затем пробует выполнить вызов суффиксной функции.
01071 
01072     Генерирует исключение если нет закрывающей скобки, неправильный
01073   вызов префиксной функции или встретился неправильный символ.
01074 
01075     Параметр @a calculator используется для доступа к таблицам функций.
01076 
01077 @param[in,out] is The input stream.
01078 @param[in] calculator The calculator.
01079 @param[in] enable_suffix Активировать вычисление суффиксных функций
01080 @return Результат
01081 @throw omni::calc::SyntaxError В случае синтаксической ошибки
01082 */
01083 template<typename T, typename Ch, typename Tr>
01084   T level_3(std::basic_istream<Ch, Tr> &is, const Calculator<T> &calculator, bool enable_suffix)
01085 {
01086   T x = T();
01087 
01088   if (!get_num(is >> std::ws, x, calculator.is_integer())) // try to parse number
01089   {
01090     typedef std::basic_istream<Ch, Tr> istream_type;
01091     typedef istream_type::traits_type traits_type;
01092     typedef CharConst<Ch> calc_traits;
01093 
01094     typename traits_type::int_type meta = is.peek();
01095     if (!traits_type::eq_int_type(meta, traits_type::eof()))
01096     {
01097       typename traits_type::char_type cx = traits_type::to_char_type(meta);
01098 
01099       if (traits_type::eq(cx, calc_traits::OP_ADD)) // unary "+"
01100         x = +level_3(is.ignore(), calculator, false);
01101       else if (traits_type::eq(cx, calc_traits::OP_SUB)) // unary "-"
01102         x = -level_3(is.ignore(), calculator, false);
01103       else if (traits_type::eq(cx, calc_traits::BRACE_OPEN)) // braces
01104       {
01105         x = level_1(is.ignore(), calculator);
01106 
01107         // check syntax
01108         meta = (is >> std::ws).peek();
01109         cx = traits_type::to_char_type(meta);
01110         if (traits_type::eq_int_type(meta, traits_type::eof())
01111           || !traits_type::eq(cx, calc_traits::BRACE_CLOSE))
01112             throw err::SyntaxError("expected \")\" char");
01113         else
01114           is.ignore();
01115       }
01116       else // prefix function call
01117       {
01118         std::wstring func_name = get_func_name(is);
01119         if (!func_name.empty())
01120         {
01121           // find open brace
01122           meta = (is >> std::ws).peek();
01123           cx = traits_type::to_char_type(meta);
01124           if (traits_type::eq_int_type(meta, traits_type::eof())
01125             || !traits_type::eq(cx, calc_traits::BRACE_OPEN))
01126               throw err::SyntaxError("expected \"(\" char");
01127           else
01128             is.ignore();
01129 
01130           T z = level_1(is, calculator);
01131 
01132           // check close brace
01133           meta = (is >> std::ws).peek();
01134           cx = traits_type::to_char_type(meta);
01135           if (traits_type::eq_int_type(meta, traits_type::eof())
01136             || !traits_type::eq(cx, calc_traits::BRACE_CLOSE))
01137               throw err::SyntaxError("expected \")\" char");
01138           else
01139             is.ignore();
01140 
01141           x = calculator.prefix()(func_name, z);
01142         }
01143         else
01144           throw err::SyntaxError("expected value or function call");
01145       }
01146     }
01147     else
01148       throw err::SyntaxError("unexpected end of input stream");
01149   }
01150 
01151   // suffix function call
01152   if (enable_suffix)
01153   {
01154     std::wstring func_name = get_func_name(is);
01155     if (!func_name.empty())
01156       x = calculator.suffix()(func_name, x);
01157   }
01158 
01159   return x;
01160 }
01161 
01162 
01163 //////////////////////////////////////////////////////////////////////////
01164 /// @brief Level 2 expression parsing
01165 /**
01166     Функция выполняет бинарное умножение и деление.
01167 
01168     Генерирует исключение если встретилось целочисленное деление на ноль.
01169 
01170 @param[in,out] is The input stream.
01171 @param[in] calculator The calculator.
01172 @return The evaluation result.
01173 @throw omni::calc::DivisionByZero В случае целочисленного деления на ноль
01174 */
01175 template<typename T, typename Ch, typename Tr>
01176   T level_2(std::basic_istream<Ch, Tr> &is, const Calculator<T> &calculator)
01177 {
01178   T x = level_3(is, calculator, true);
01179 
01180   while ((is >> std::ws) && !is.eof())
01181   {
01182     typedef std::basic_istream<Ch, Tr> istream_type;
01183     typedef istream_type::traits_type traits_type;
01184     typedef CharConst<Ch> calc_traits;
01185 
01186     typename traits_type::int_type meta = is.peek();
01187     typename traits_type::char_type cx = traits_type::to_char_type(meta);
01188     if (traits_type::eq(cx, calc_traits::OP_MUL))
01189     {
01190       x *= level_3(is.ignore(1), calculator, true);
01191     }
01192     else if (traits_type::eq(cx, calc_traits::OP_DIV))
01193     {
01194       T z = level_3(is.ignore(1), calculator, true);
01195       if (calculator.is_integer() && (T() == z))
01196         throw err::DivisionByZero();
01197       x /= z;
01198     }
01199     else
01200       break;
01201   }
01202 
01203   return x;
01204 }
01205 
01206 
01207 //////////////////////////////////////////////////////////////////////////
01208 /// @brief Level 1 expression parsing
01209 /**
01210     Функция выполняет бинарное сложение и вычитание.
01211 
01212 @param[in,out] is The input stream.
01213 @param[in] calculator The calculator.
01214 @return The evaluation result.
01215 */
01216 template<typename T, typename Ch, typename Tr>
01217   T level_1(std::basic_istream<Ch, Tr> &is, const Calculator<T> &calculator)
01218 {
01219   T x = level_2(is, calculator);
01220 
01221   while ((is >> std::ws) && !is.eof())
01222   {
01223     typedef std::basic_istream<Ch, Tr> istream_type;
01224     typedef istream_type::traits_type traits_type;
01225     typedef CharConst<Ch> calc_traits;
01226 
01227     typename traits_type::int_type meta = is.peek();
01228     typename traits_type::char_type cx = traits_type::to_char_type(meta);
01229     if (traits_type::eq(cx, calc_traits::OP_ADD))
01230       x += level_2(is.ignore(1), calculator);
01231     else if (traits_type::eq(cx, calc_traits::OP_SUB))
01232       x -= level_2(is.ignore(1), calculator);
01233     else
01234       break;
01235   }
01236 
01237   return x;
01238 }
01239 
01240 
01241 //////////////////////////////////////////////////////////////////////////
01242 /// @brief Evaluate expression.
01243 /**
01244     Функция вычисляет выражение из потока ввода @a is пока не будет достигнуто
01245   окончание выражения (которое может не совпадать с окончанием потока ввода).
01246 
01247     Если поток ввода не содержит выражения или выражение некорректно,
01248   будет сгенерировано исключение.
01249 
01250 @param[in,out] expression Поток ввода
01251 @param[in] calculator Калькулятор
01252 @return Значение выражения
01253 @throw omni::calc::SyntaxError Если выражение некорректно
01254 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
01255 */
01256 template<typename T, typename Ch, typename Tr>
01257   T eval(std::basic_istream<Ch, Tr> &expression, const Calculator<T> &calculator)
01258 {
01259   return level_1(expression, calculator);
01260 }
01261 
01262 
01263 //////////////////////////////////////////////////////////////////////////
01264 /// @brief Evaluate expression.
01265 /**
01266     Функция вычисляет выражение из строки @a expression.
01267 
01268     Если выражение некорректно или строка содержит более
01269   одного выражения будет сгенерировано исключение.
01270 
01271 @param[in] expression Строковое выражение
01272 @param[in] calculator Калькулятор
01273 @return Значение выражения
01274 @throw omni::calc::SyntaxError Если выражение некорректно
01275 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
01276 */
01277 template<typename T, typename Ch, typename Tr, typename Ax>
01278   T eval(const std::basic_string<Ch, Tr, Ax> &expression, const Calculator<T> &calculator)
01279 {
01280   std::basic_istringstream<Ch, Tr> is(expression);
01281   T x = eval(is, calculator);
01282 
01283   if (!(is >> std::ws).eof())
01284     throw err::SyntaxError("expression not fully parsed");
01285 
01286   return x;
01287 }
01288 
01289 //////////////////////////////////////////////////////////////////////////
01290 /// @brief Evaluate expression.
01291 /**
01292     Функция вычисляет выражение из C-строки @a expression.
01293 
01294     Если выражение некорректно или строка содержит более
01295   одного выражения будет сгенерировано исключение.
01296 
01297 @param[in] expression Строковое выражение
01298 @param[in] calculator Калькулятор
01299 @return Значение выражения
01300 @throw omni::calc::SyntaxError Если выражение некорректно
01301 @throw omni::calc::CalculationError Если выражение содержит вычислительные ошибки
01302 */
01303 template<typename T, typename Ch>
01304   T eval(const Ch *expression, const Calculator<T> &calculator)
01305 {
01306   return eval(std::basic_string<Ch>(expression), calculator);
01307 }
01308 
01309     } // details namespace
01310     /// @endcond
01311   } // calc namespace
01312 
01313 
01314 } // omni namespace
01315 
01316 #endif // __OMNI_CALC_HPP_

Generated on Wed Jun 6 17:27:46 2007 for OMNI by  doxygen 1.5.2