diff options
-rw-r--r-- | examples/calc_arg.cpp | 22 | ||||
-rw-r--r-- | examples/calc_file.cpp | 20 | ||||
-rw-r--r-- | include/infix_calc/infix_calc.hpp | 77 | ||||
-rw-r--r-- | include/infix_calc/rpn_calc.hpp | 127 | ||||
-rw-r--r-- | src/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/infix_calc.cpp | 583 | ||||
-rw-r--r-- | src/infix_calc_parser.yy | 42 | ||||
-rw-r--r-- | src/infix_calc_scanner1.ll | 2 | ||||
-rw-r--r-- | src/infix_calc_scanner2.ll | 2 | ||||
-rw-r--r-- | src/infix_calc_scanner3.ll | 2 | ||||
-rw-r--r-- | src/rpn_calc.cpp | 682 |
11 files changed, 875 insertions, 690 deletions
diff --git a/examples/calc_arg.cpp b/examples/calc_arg.cpp index 92e522c..e40669f 100644 --- a/examples/calc_arg.cpp +++ b/examples/calc_arg.cpp @@ -19,33 +19,33 @@ int main(int argc, char *argv[]) infix_calc::compiler cc; cc.variables["x"] = 2.0; cc.variables["y"] = 3.0; - printf("Vars: %s\n", infix_calc::to_string(cc.variables).c_str()); + printf("Vars: %s\n", rpn_calc::to_string(cc.variables).c_str()); { - const bool pok = cc.parse (data, ::strlen(data)); - printf("Vanilla RPN: %s\n", infix_calc::to_string(cc.rpn_stack).c_str()); + const bool pok = cc.parse(data, ::strlen(data)); + printf("Vanilla RPN: %s\n", cc.rpn_expr.toString().c_str()); if( !pok ) { std::cerr << "Error occurred @ parsing: " << cc.location() << std::endl; return EXIT_FAILURE; } } double res = 0.0; - infix_calc::RPNStatus estatus = cc.eval(res); - if( infix_calc::RPNStatus::No_Error != estatus ) { - printf("Error occurred @ eval(Vanilla): %s\n", infix_calc::to_string(estatus).c_str()); + rpn_calc::RPNStatus estatus = cc.eval(res); + if( rpn_calc::RPNStatus::No_Error != estatus ) { + printf("Error occurred @ eval(Vanilla): %s\n", rpn_calc::to_string(estatus).c_str()); return EXIT_FAILURE; } printf("Vanilla Result: %f\n", res); estatus = cc.reduce(); - if( infix_calc::RPNStatus::No_Error != estatus ) { - printf("Error occurred @ reduce: %s\n", infix_calc::to_string(estatus).c_str()); + if( rpn_calc::RPNStatus::No_Error != estatus ) { + printf("Error occurred @ reduce: %s\n", rpn_calc::to_string(estatus).c_str()); return EXIT_FAILURE; } - printf("Reduced RPN: %s\n", infix_calc::to_string(cc.rpn_stack).c_str()); + printf("Reduced RPN: %s\n", cc.rpn_expr.toString().c_str()); double res2 = 0.0; estatus = cc.eval(res2); - if( infix_calc::RPNStatus::No_Error != estatus ) { - printf("Error occurred @ eval(Reduced): %s\n", infix_calc::to_string(estatus).c_str()); + if( rpn_calc::RPNStatus::No_Error != estatus ) { + printf("Error occurred @ eval(Reduced): %s\n", rpn_calc::to_string(estatus).c_str()); return EXIT_FAILURE; } printf("Reduced Result: %f\n", res2); diff --git a/examples/calc_file.cpp b/examples/calc_file.cpp index 8c5a8bb..ddd4d6c 100644 --- a/examples/calc_file.cpp +++ b/examples/calc_file.cpp @@ -15,33 +15,33 @@ int main(int argc, char *argv[]) infix_calc::compiler cc; cc.variables["x"] = 2.0; cc.variables["y"] = 3.0; - printf("Vars: %s\n", infix_calc::to_string(cc.variables).c_str()); + printf("Vars: %s\n", rpn_calc::to_string(cc.variables).c_str()); { const bool pok = cc.parse ("-"); // stdin - printf("Vanilla RPN: %s\n", infix_calc::to_string(cc.rpn_stack).c_str()); + printf("Vanilla RPN: %s\n", cc.rpn_expr.toString().c_str()); if( !pok ) { std::cerr << "Error occurred @ parsing: " << cc.location() << std::endl; return EXIT_FAILURE; } } double res = 0.0; - infix_calc::RPNStatus estatus = cc.eval(res); - if( infix_calc::RPNStatus::No_Error != estatus ) { - printf("Error occurred @ eval(Vanilla): %s\n", infix_calc::to_string(estatus).c_str()); + rpn_calc::RPNStatus estatus = cc.eval(res); + if( rpn_calc::RPNStatus::No_Error != estatus ) { + printf("Error occurred @ eval(Vanilla): %s\n", rpn_calc::to_string(estatus).c_str()); return EXIT_FAILURE; } printf("Vanilla Result: %f\n", res); estatus = cc.reduce(); - if( infix_calc::RPNStatus::No_Error != estatus ) { - printf("Error occurred @ reduce: %s\n", infix_calc::to_string(estatus).c_str()); + if( rpn_calc::RPNStatus::No_Error != estatus ) { + printf("Error occurred @ reduce: %s\n", rpn_calc::to_string(estatus).c_str()); return EXIT_FAILURE; } - printf("Reduced RPN: %s\n", infix_calc::to_string(cc.rpn_stack).c_str()); + printf("Reduced RPN: %s\n", cc.rpn_expr.toString().c_str()); double res2 = 0.0; estatus = cc.eval(res2); - if( infix_calc::RPNStatus::No_Error != estatus ) { - printf("Error occurred @ eval(Reduced): %s\n", infix_calc::to_string(estatus).c_str()); + if( rpn_calc::RPNStatus::No_Error != estatus ) { + printf("Error occurred @ eval(Reduced): %s\n", rpn_calc::to_string(estatus).c_str()); return EXIT_FAILURE; } printf("Reduced Result: %f\n", res2); diff --git a/include/infix_calc/infix_calc.hpp b/include/infix_calc/infix_calc.hpp index 26bc9b0..f0f2d14 100644 --- a/include/infix_calc/infix_calc.hpp +++ b/include/infix_calc/infix_calc.hpp @@ -34,6 +34,8 @@ #include <map>
#include <ostream>
+#include <infix_calc/rpn_calc.hpp>
+
namespace infix_calc_yy {
class scanner; // fwd
class parser; // fwd
@@ -42,51 +44,6 @@ namespace infix_calc_yy { namespace infix_calc {
- enum class RPNStatus : unsigned int {
- No_Error,
- /*-----------------------*/
- Too_Complex,
- Unresolved_Variables,
- Division_by_zero,
- Overflow,
- Undefined,
- RPN_Underflow,
- /*-----------------------*/
- };
- std::string to_string(RPNStatus status) noexcept;
-
- enum class rpn_token_t : unsigned int {
- UREAL, VARIABLE,
- SUB, ADD,
- MUL, DIV,
- SIN, COS, TAN, ARCSIN, ARCCOS, ARCTAN,
- POW, LOG, LOG10, EXP,
- SQRT, NEG
- };
- std::string to_string(const rpn_token_t ts) noexcept;
-
- struct rpn_token {
- /** Terminal Symbol (TOKEN) */
- rpn_token_t ts;
- /** Real value */
- double value;
- /** Variable identifier */
- std::string id;
- };
- std::string to_string(const rpn_token& v) noexcept;
-
- std::string to_string(const std::vector<rpn_token>& rpn_stack) noexcept;
-
- typedef std::map<std::string, double> variable_set;
-
- std::string to_string(const variable_set& variables) noexcept;
-
- // Reduce the given source RPN into the new dest
- RPNStatus reduce(std::vector<rpn_token>& result, const std::vector<rpn_token>& source) noexcept;
-
- // Evaluate the given RPN and variables.
- RPNStatus eval(double& result, const variable_set& variables, const std::vector<rpn_token>& rpn_stack) noexcept;
-
class compiler {
private:
friend class infix_calc_yy::parser;
@@ -96,16 +53,16 @@ namespace infix_calc { bool scan_begin(const char* bytes, int len);
void scan_end();
- void put_rpn(rpn_token_t ts);
- void put_rpn(double value);
- void put_rpn(const std::string& variable_name);
- void put_rpn(std::string&& variable_name);
+ void put_rpn(rpn_calc::rpn_token_t ts) { rpn_expr.push_back(ts); }
+ void put_rpn(double value) { rpn_expr.push_back(value); }
+ void put_rpn(const std::string& variable_name) { rpn_expr.push_back(variable_name); }
+ void put_rpn(std::string&& variable_name) { rpn_expr.push_back(std::move(variable_name)); }
void* backend;
public:
- variable_set variables;
- std::vector<rpn_token> rpn_stack;
+ rpn_calc::variable_set variables;
+ rpn_calc::rpn_expression_t rpn_expr;
compiler();
~compiler();
@@ -118,18 +75,14 @@ namespace infix_calc { bool parse (const char* bytes, int len);
// Reduce the RPN, replacing this RPM stack if no error occurs
- RPNStatus reduce() noexcept {
- std::vector<rpn_token> dest;
- RPNStatus s = infix_calc::reduce(dest, rpn_stack);
- if( RPNStatus::No_Error == s ) {
- rpn_stack = dest;
- }
- return s;
- }
+ rpn_calc::RPNStatus reduce() noexcept { return rpn_expr.reduce(); }
+
+ // Checks whether all variables in RPN are resolved with given variables.
+ bool resolved() const noexcept { return rpn_expr.resolved(variables); }
// Evaluate the parsed RPN w/ given variables.
- RPNStatus eval(double& result) const noexcept {
- return infix_calc::eval(result, variables, rpn_stack);
+ rpn_calc::RPNStatus eval(double& result) const noexcept {
+ return rpn_expr.eval(result, variables);
}
infix_calc_yy::location& location();
};
@@ -138,8 +91,6 @@ namespace infix_calc { namespace std {
ostream& operator<<(ostream& os, const infix_calc_yy::location& loc);
- ostream& operator<<(ostream& os, const infix_calc::rpn_token_t rpn);
- ostream& operator<<(ostream& os, const infix_calc::rpn_token& rpn);
}
#endif /* INFIX_CALC_HPP */
diff --git a/include/infix_calc/rpn_calc.hpp b/include/infix_calc/rpn_calc.hpp new file mode 100644 index 0000000..d40f387 --- /dev/null +++ b/include/infix_calc/rpn_calc.hpp @@ -0,0 +1,127 @@ +/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 1993-2023 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef RPN_CALC_HPP
+#define RPN_CALC_HPP
+
+#include <cstdio>
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+#include <vector>
+#include <map>
+#include <ostream>
+
+namespace rpn_calc {
+
+ enum class RPNStatus : unsigned int {
+ No_Error,
+ /*-----------------------*/
+ Too_Complex,
+ Unresolved_Variables,
+ Division_by_zero,
+ Overflow,
+ Undefined,
+ RPN_Underflow,
+ /*-----------------------*/
+ };
+ std::string to_string(RPNStatus status) noexcept;
+
+ enum class rpn_token_t : unsigned int {
+ UREAL, VARIABLE,
+ SUB, ADD,
+ MUL, DIV, MOD,
+ ABS, SIN, COS, TAN, ARCSIN, ARCCOS, ARCTAN,
+ POW, LOG, LOG10, EXP,
+ SQRT, NEG
+ };
+ std::string to_string(const rpn_token_t ts) noexcept;
+
+ struct rpn_token {
+ /** Terminal Symbol (TOKEN) */
+ rpn_token_t ts;
+ /** Real value */
+ double value;
+ /** Variable identifier */
+ std::string id;
+ };
+ typedef std::vector<rpn_token> rpn_stack_t;
+ std::string to_string(const rpn_token& v) noexcept;
+ std::string to_string(const rpn_stack_t& rpn_expr) noexcept;
+
+ // Reduce the given source RPN into destination res RPN
+ RPNStatus reduce(rpn_stack_t& res, const rpn_stack_t& source) noexcept;
+
+ typedef std::map<std::string, double> variable_set;
+ std::string to_string(const variable_set& variables) noexcept;
+
+ struct rpn_expression_t {
+ rpn_stack_t expr;
+
+ void clear() noexcept { expr.clear(); }
+
+ void push_back(rpn_token_t ts) {
+ expr.push_back( { ts, 0.0, "" } );
+ }
+
+ void push_back(double value) {
+ expr.push_back( { rpn_token_t::UREAL, value, "" } );
+ }
+
+ void push_back(const std::string& variable_name) {
+ expr.push_back( { rpn_token_t::VARIABLE, 0.0, variable_name } );
+ }
+
+ void push_back(std::string&& variable_name) {
+ expr.push_back( { rpn_token_t::VARIABLE, 0.0, std::move(variable_name) } );
+ }
+
+ // Reduce this RPN in place
+ RPNStatus reduce() noexcept {
+ rpn_stack_t dest;
+ RPNStatus s = rpn_calc::reduce(dest, expr);
+ if( RPNStatus::No_Error == s ) {
+ expr = dest;
+ }
+ return s;
+ }
+
+ // Checks whether all variables in RPN are resolved with given variables.
+ bool resolved(const variable_set& variables) const noexcept;
+
+ // Evaluate the given RPN and variables.
+ RPNStatus eval(double& result, const variable_set& variables) const noexcept;
+
+ std::string toString() const noexcept { return to_string(expr); }
+ };
+
+} // namespace rpn_calc
+
+namespace std {
+ ostream& operator<<(ostream& os, const rpn_calc::rpn_token_t rpn);
+ ostream& operator<<(ostream& os, const rpn_calc::rpn_token& rpn);
+}
+#endif /* RPN_CALC_HPP */
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 552b7e2..b48a068 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ if(FLEX_FOUND) FLEX_TARGET(InfixCalcScanner1 infix_calc_scanner1.ll ${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner1.cpp COMPILE_FLAGS "--header-file=${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner1.hpp" ) ADD_FLEX_BISON_DEPENDENCY(InfixCalcScanner1 InfixCalcParser) - add_library (infix_calc1 SHARED infix_calc.cpp ${BISON_InfixCalcParser_OUTPUTS} ${FLEX_InfixCalcScanner1_OUTPUTS}) + add_library (infix_calc1 SHARED infix_calc.cpp rpn_calc.cpp ${BISON_InfixCalcParser_OUTPUTS} ${FLEX_InfixCalcScanner1_OUTPUTS}) target_include_directories(infix_calc1 SYSTEM PRIVATE ${FLEX_INCLUDE_DIRS}) target_compile_options(infix_calc1 PRIVATE -DSCANNER_FLEX) #target_link_libraries ( @@ -52,7 +52,7 @@ if(REflex_FOUND) REflex_TARGET(InfixCalcScanner2 infix_calc_scanner2.ll ${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner2.cpp COMPILE_FLAGS "--header-file=${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner2.hpp" ) ADD_REFLEX_BISON_DEPENDENCY(InfixCalcScanner2 InfixCalcParser) - add_library (infix_calc2 SHARED infix_calc.cpp ${BISON_InfixCalcParser_OUTPUTS} ${REflex_InfixCalcScanner2_OUTPUTS}) + add_library (infix_calc2 SHARED infix_calc.cpp rpn_calc.cpp ${BISON_InfixCalcParser_OUTPUTS} ${REflex_InfixCalcScanner2_OUTPUTS}) target_include_directories(infix_calc2 SYSTEM PRIVATE ${REflex_INCLUDE_DIRS}) target_compile_options(infix_calc2 PRIVATE -fPIC -DSCANNER_REFLEX) target_link_options(infix_calc2 PRIVATE -fPIC) @@ -80,7 +80,7 @@ if(USE_FLEXCPP) FLEXCPP_TARGET(InfixCalcScanner3 infix_calc_scanner3.ll ${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner3.cpp COMPILE_FLAGS "--class-header=${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner3.hpp --implementation-header=${CMAKE_CURRENT_BINARY_DIR}/infix_calc_scanner3i.hpp --namespace=infix_calc_yy --class-name=scanner" ) ADD_FLEXCPP_BISON_DEPENDENCY(InfixCalcScanner3 InfixCalcParser) - add_library (infix_calc3 SHARED infix_calc.cpp ${BISON_InfixCalcParser_OUTPUTS} ${Flexcpp_InfixCalcScanner3_OUTPUTS}) + add_library (infix_calc3 SHARED infix_calc.cpp rpn_calc.cpp ${BISON_InfixCalcParser_OUTPUTS} ${Flexcpp_InfixCalcScanner3_OUTPUTS}) target_include_directories(infix_calc3 SYSTEM PRIVATE ${Flexcpp_INCLUDE_DIRS}) target_compile_options(infix_calc3 PRIVATE -DSCANNER_FLEXCPP) set_target_properties( diff --git a/src/infix_calc.cpp b/src/infix_calc.cpp index 1986ca9..5a67cff 100644 --- a/src/infix_calc.cpp +++ b/src/infix_calc.cpp @@ -44,76 +44,6 @@ namespace infix_calc {
-static const char *EvalStatusString[] = {
- "No Error",
- /*-----------------------*/
- "Too complex",
- "Unresolved variables",
- "Division by zero",
- "Overflow",
- "Undefined",
- "RPN Underflow",
- /*-----------------------*/
-};
-
-std::string to_string(RPNStatus status) noexcept {
- return std::string(EvalStatusString[static_cast<unsigned int>(status)]);
-}
-
-std::string to_string(const rpn_token_t ts) noexcept {
- switch(ts) {
- case rpn_token_t::UREAL:
- return "<ureal>";
- case rpn_token_t::VARIABLE:
- return "<var>";
- case rpn_token_t::SUB:
- return "-";
- case rpn_token_t::ADD:
- return "+";
- case rpn_token_t::MUL:
- return "*";
- case rpn_token_t::DIV:
- return "/";
- case rpn_token_t::SIN:
- return "sin";
- case rpn_token_t::COS:
- return "cos";
- case rpn_token_t::TAN:
- return "tan";
- case rpn_token_t::ARCSIN:
- return "asin";
- case rpn_token_t::ARCCOS:
- return "acos";
- case rpn_token_t::ARCTAN:
- return "atan";
- case rpn_token_t::POW:
- return "pow";
- case rpn_token_t::LOG:
- return "log";
- case rpn_token_t::LOG10:
- return "log10";
- case rpn_token_t::EXP:
- return "exp";
- case rpn_token_t::SQRT:
- return "sqrt";
- case rpn_token_t::NEG:
- return "neg";
- }
- return "unknown";
-}
-
-std::string to_string(const rpn_token& v) noexcept {
- switch(v.ts) {
- case rpn_token_t::UREAL:
- return std::to_string(v.value);
- case rpn_token_t::VARIABLE:
- return "'"+v.id+"'";
- default:
- return to_string(v.ts);
- }
- return "unknown";
-}
-
struct backend_t {
#if defined(SCANNER_REFLEX)
infix_calc_yy::scanner scan;
@@ -290,509 +220,6 @@ bool compiler::parse (const char* bytes, int len) { return 0 == res;
}
-void compiler::put_rpn(rpn_token_t ts) {
- rpn_stack.push_back( { ts, 0.0, "" } );
-}
-
-void compiler::put_rpn(double value) {
- rpn_stack.push_back( { rpn_token_t::UREAL, value, "" } );
-}
-
-void compiler::put_rpn(const std::string& variable_name) {
- rpn_stack.push_back( { rpn_token_t::VARIABLE, 0.0, variable_name } );
-}
-
-void compiler::put_rpn(std::string&& variable_name) {
- rpn_stack.push_back( { rpn_token_t::VARIABLE, 0.0, std::move(variable_name) } );
-}
-
-std::string to_string(const std::vector<rpn_token>& rpn_stack) noexcept {
- std::string r;
- for(const rpn_token& t : rpn_stack) {
- r.append(to_string(t));
- if( t.ts == rpn_token_t::UREAL || t.ts == rpn_token_t::VARIABLE ) {
- r.append(", ");
- } else {
- r.append("; ");
- }
- }
- return r;
-}
-
-std::string to_string(const variable_set& variables) noexcept {
- std::string r;
- for (const auto& [key, value] : variables) {
- r.append(key).append(" = ").append(std::to_string(value)).append(", ");
- }
- return r;
-}
-
-RPNStatus reduce(std::vector<rpn_token>& res, const std::vector<rpn_token>& source) noexcept {
- res.clear();
- RPNStatus status = RPNStatus::No_Error;
- double left_operand, right_operand;
-
- auto check_stack_cntr = [&](size_t min) noexcept -> bool {
- if( res.size() < min ) {
- status = RPNStatus::RPN_Underflow;
- return false;
- } else {
- return true;
- }
- };
- auto get_stack_ureal = [&](size_t count) noexcept -> bool {
- const size_t sz = res.size();
- if( 0 == sz ) {
- return 0 == count;
- }
- const size_t hi = sz - 1;
- for(size_t i=0; i<count; ++i) {
- if( hi < i || res[hi-i].ts != rpn_token_t::UREAL ) {
- return false;
- }
- }
- if( count >= 1 ) {
- right_operand = res.back().value;
- res.pop_back();
- }
- if( count >= 2 ) {
- left_operand = res.back().value;
- res.pop_back();
- }
- return true;
- };
-
- for (size_t i=0; i<source.size() && status==RPNStatus::No_Error; i++)
- {
- const rpn_token& t = source[i];
- switch (t.ts)
- {
- case rpn_token_t::UREAL:
- res.push_back(t);
- break;
-
- case rpn_token_t::VARIABLE:
- res.push_back(t);
- break;
-
- case rpn_token_t::ADD:
- if( !check_stack_cntr(2) ) { break; }
- if( get_stack_ureal(2) ) {
- if ( ( ( left_operand > 0 && right_operand > 0 )
- ||
- ( left_operand < 0 && right_operand < 0 )
- ) &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() - std::abs(right_operand)
- )
- {
- status = RPNStatus::Overflow;
- } else {
- res.push_back( { rpn_token_t::UREAL, left_operand + right_operand, "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::SUB:
- if( !check_stack_cntr(2) ) { break; }
- if( get_stack_ureal(2) ) {
- if ( ( ( left_operand > 0 && right_operand < 0 )
- ||
- ( left_operand < 0 && right_operand > 0 )
- ) &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() - std::abs(right_operand)
- )
- {
- status =RPNStatus::Overflow;
- } else {
- res.push_back( { rpn_token_t::UREAL, left_operand - right_operand, "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::MUL:
- if( !check_stack_cntr(2) ) { break; }
- if( get_stack_ureal(2) ) {
- if( std::abs(right_operand) > 1 &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() / std::abs(right_operand) )
- {
- status =RPNStatus::Overflow;
- } else {
- res.push_back( { rpn_token_t::UREAL, left_operand * right_operand, "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::DIV:
- if( !check_stack_cntr(2) ) { break; }
- if( get_stack_ureal(2) ) {
- if (right_operand == 0)
- {
- status = RPNStatus::Division_by_zero;
- break;
- }
- if( std::abs(right_operand) < 1 &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() * std::abs(right_operand) )
- {
- status = RPNStatus::Overflow;
- } else {
- res.push_back( { rpn_token_t::UREAL, left_operand / right_operand, "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::POW:
- if( !check_stack_cntr(2) ) { break; }
- if( get_stack_ureal(2) ) {
- res.push_back( { rpn_token_t::UREAL, std::pow(left_operand, right_operand), "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::SQRT:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- if (right_operand<0)
- {
- status=RPNStatus::Undefined;
- } else {
- res.push_back( { rpn_token_t::UREAL, std::sqrt(right_operand), "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::LOG:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- if (right_operand<=0)
- {
- status=RPNStatus::Undefined;
- } else {
- res.push_back( { rpn_token_t::UREAL, std::log(right_operand), "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::LOG10:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- if (right_operand<=0)
- {
- status=RPNStatus::Undefined;
- } else {
- res.push_back( { rpn_token_t::UREAL, std::log10(right_operand), "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::EXP:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- res.push_back( { rpn_token_t::UREAL, std::exp(right_operand), "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::SIN:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- res.push_back( { rpn_token_t::UREAL, std::sin(right_operand), "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::COS:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- res.push_back( { rpn_token_t::UREAL, std::cos(right_operand), "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::TAN:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- res.push_back( { rpn_token_t::UREAL, std::tan(right_operand), "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::ARCSIN:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- if (std::abs(right_operand)>1)
- {
- status=RPNStatus::Undefined;
- } else {
- res.push_back( { rpn_token_t::UREAL, std::asin(right_operand), "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::ARCCOS:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- if (std::abs(right_operand)>1)
- {
- status=RPNStatus::Undefined;
- } else {
- res.push_back( { rpn_token_t::UREAL, std::acos(right_operand), "" } );
- }
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::ARCTAN:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- res.push_back( { rpn_token_t::UREAL, std::atan(right_operand), "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- case rpn_token_t::NEG:
- if( !check_stack_cntr(1) ) { break; }
- if( get_stack_ureal(1) ) {
- res.push_back( { rpn_token_t::UREAL, 0 - right_operand, "" } );
- } else {
- res.push_back(t);
- }
- break;
-
- default :
- break;
- }
- }
-
- return status;
-}
-
-RPNStatus eval(double& result, const variable_set& variables, const std::vector<rpn_token>& rpn_stack) noexcept {
- RPNStatus status = RPNStatus::No_Error;
- double left_operand, right_operand;
-
- double stack[rpn_stack.size()];
- size_t stack_cntr=0;
- stack[0] = 0.0;
-
- auto get_stack = [&](size_t count) noexcept -> bool {
- if( stack_cntr < count ) {
- status = RPNStatus::RPN_Underflow;
- return false;
- }
- if( count >= 1 ) {
- right_operand = stack[--stack_cntr];
- }
- if( count >= 2 ) {
- left_operand = stack[--stack_cntr];
- }
- return true;
- };
-
- for (size_t i=0; i<rpn_stack.size() && status==RPNStatus::No_Error; i++)
- {
- const rpn_token& t = rpn_stack[i];
- switch (t.ts)
- {
- case rpn_token_t::UREAL:
- stack[stack_cntr++] = t.value;
- break;
-
- case rpn_token_t::VARIABLE:
- {
- if (auto var_iter = variables.find(t.id); var_iter != variables.end()) {
- stack[stack_cntr++] = var_iter->second;
- } else {
- // variable t.id undefined!
- fprintf(stderr, "variable '%s' undefined\n", t.id.c_str());
- status = RPNStatus::Unresolved_Variables; // FIXME: Refine!
- }
- }
- break;
-
- case rpn_token_t::ADD:
- if( !get_stack(2) ) { break; }
- if ( ( ( left_operand > 0 && right_operand > 0 )
- ||
- ( left_operand < 0 && right_operand < 0 )
- ) &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() - std::abs(right_operand)
- )
- {
- status = RPNStatus::Overflow;
- } else {
- stack[stack_cntr++] = left_operand + right_operand;
- }
- break;
-
- case rpn_token_t::SUB:
- if( !get_stack(2) ) { break; }
- if ( ( ( left_operand > 0 && right_operand < 0 )
- ||
- ( left_operand < 0 && right_operand > 0 )
- ) &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() - std::abs(right_operand)
- )
- {
- status =RPNStatus::Overflow;
- } else {
- stack[stack_cntr++] = left_operand - right_operand;
- }
- break;
-
- case rpn_token_t::MUL:
- if( !get_stack(2) ) { break; }
- if( std::abs(right_operand) > 1 &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() / std::abs(right_operand) )
- {
- status =RPNStatus::Overflow;
- } else {
- stack[stack_cntr++] = left_operand * right_operand;
- }
- break;
-
- case rpn_token_t::DIV:
- if( !get_stack(2) ) { break; }
- if (right_operand == 0)
- {
- status = RPNStatus::Division_by_zero;
- break;
- }
- if( std::abs(right_operand) < 1 &&
- std::abs(left_operand) >=
- std::numeric_limits<double>::max() * std::abs(right_operand) )
- {
- status = RPNStatus::Overflow;
- } else {
- stack[stack_cntr++] = left_operand / right_operand;
- }
- break;
-
- case rpn_token_t::POW:
- if( !get_stack(2) ) { break; }
- stack[stack_cntr++] = std::pow(left_operand,right_operand);
- break;
-
- case rpn_token_t::SQRT:
- if( !get_stack(1) ) { break; }
- if (right_operand<0)
- {
- status=RPNStatus::Undefined;
- } else {
- stack[stack_cntr++] = std::sqrt (right_operand);
- }
- break;
-
- case rpn_token_t::LOG:
- if( !get_stack(1) ) { break; }
- if (right_operand<=0)
- {
- status=RPNStatus::Undefined;
- } else {
- stack[stack_cntr++] = std::log (right_operand);
- }
- break;
-
- case rpn_token_t::LOG10:
- if( !get_stack(1) ) { break; }
- if (right_operand<=0)
- {
- status=RPNStatus::Undefined;
- } else {
- stack[stack_cntr++] = std::log10(right_operand);
- }
- break;
-
- case rpn_token_t::EXP:
- if( !get_stack(1) ) { break; }
- stack[stack_cntr++] = std::exp(right_operand);
- break;
-
- case rpn_token_t::SIN:
- if( !get_stack(1) ) { break; }
- stack[stack_cntr++] = std::sin(right_operand);
- break;
-
- case rpn_token_t::COS:
- if( !get_stack(1) ) { break; }
- stack[stack_cntr++] = std::cos(right_operand);
- break;
-
- case rpn_token_t::TAN:
- if( !get_stack(1) ) { break; }
- stack[stack_cntr++] = std::tan(right_operand);
- break;
-
- case rpn_token_t::ARCSIN:
- if( !get_stack(1) ) { break; }
- if (std::abs(right_operand)>1)
- {
- status=RPNStatus::Undefined;
- } else {
- stack[stack_cntr++] = std::asin (right_operand);
- }
- break;
-
- case rpn_token_t::ARCCOS:
- if( !get_stack(1) ) { break; }
- if (std::abs(right_operand)>1)
- {
- status=RPNStatus::Undefined;
- } else {
- stack[stack_cntr++] = std::acos (right_operand);
- }
- break;
-
- case rpn_token_t::ARCTAN:
- if( !get_stack(1) ) { break; }
- stack[stack_cntr++] = std::atan (right_operand);
- break;
-
- case rpn_token_t::NEG:
- if( !get_stack(1) ) { break; }
- stack[stack_cntr++] = 0 - right_operand;
- break;
-
- default :
- break;
- }
- }
-
- if( status == RPNStatus::No_Error ) {
- result = stack[0];
- }
- return (status);
-}
-
static infix_calc_yy::location eof_loc;
infix_calc_yy::parser::symbol_type make_YYEOF() {
@@ -815,13 +242,3 @@ infix_calc_yy::parser::symbol_type make_UREAL(const std::string &s, double f, co std::ostream& std::operator<<(std::ostream& os, const infix_calc_yy::location& loc) {
return infix_calc_yy::operator <<(os, loc);
}
-
-std::ostream& std::operator<<(std::ostream& os, const infix_calc::rpn_token_t ts) {
- os << infix_calc::to_string(ts);
- return os;
-}
-
-std::ostream& std::operator<<(std::ostream& os, const infix_calc::rpn_token& rpn) {
- os << infix_calc::to_string(rpn);
- return os;
-}
diff --git a/src/infix_calc_parser.yy b/src/infix_calc_parser.yy index 6b61fcf..4e5103b 100644 --- a/src/infix_calc_parser.yy +++ b/src/infix_calc_parser.yy @@ -72,13 +72,13 @@ %token <std::string> ERROR
%nterm <double> real_number
-%nterm <rpn_token_t> unary_func
+%nterm <rpn_calc::rpn_token_t> unary_func
%token LPAREN RPAREN EOL
%left SUB ADD
-%left MUL DIV
-%left SIN COS TAN ARCSIN ARCCOS ARCTAN
+%left MUL DIV MOD
+%left ABS SIN COS TAN ARCSIN ARCCOS ARCTAN
%left POW LOG LOG10 EXP
%left SQRT
%left NEG
@@ -86,37 +86,39 @@ %%
expression : product
- | expression ADD product { cc.put_rpn(rpn_token_t::ADD); }
- | expression SUB product { cc.put_rpn(rpn_token_t::SUB); }
+ | expression ADD product { cc.put_rpn(rpn_calc::rpn_token_t::ADD); }
+ | expression SUB product { cc.put_rpn(rpn_calc::rpn_token_t::SUB); }
;
product : operand
- | product MUL operand { cc.put_rpn(rpn_token_t::MUL); }
- | product DIV operand { cc.put_rpn(rpn_token_t::DIV); }
- | product POW operand { cc.put_rpn(rpn_token_t::POW); }
+ | product MUL operand { cc.put_rpn(rpn_calc::rpn_token_t::MUL); }
+ | product DIV operand { cc.put_rpn(rpn_calc::rpn_token_t::DIV); }
+ | product MOD operand { cc.put_rpn(rpn_calc::rpn_token_t::MOD); }
+ | product POW operand { cc.put_rpn(rpn_calc::rpn_token_t::POW); }
;
operand : IDENTIFIER { cc.put_rpn(std::move($1)); }
| ADD IDENTIFIER { cc.put_rpn(std::move($2)); }
| SUB IDENTIFIER { cc.put_rpn(std::move($2));
- cc.put_rpn(rpn_token_t::NEG);
+ cc.put_rpn(rpn_calc::rpn_token_t::NEG);
}
| LPAREN expression RPAREN
| unary_func LPAREN expression RPAREN { cc.put_rpn($1); }
| real_number { cc.put_rpn($1); }
;
-unary_func : SIN { $$=rpn_token_t::SIN; } |
- COS { $$=rpn_token_t::COS; } |
- TAN { $$=rpn_token_t::TAN; } |
- ARCSIN { $$=rpn_token_t::ARCSIN; } |
- ARCCOS { $$=rpn_token_t::ARCCOS; } |
- ARCTAN { $$=rpn_token_t::ARCTAN; } |
- LOG { $$=rpn_token_t::LOG; } |
- LOG10 { $$=rpn_token_t::LOG10; } |
- EXP { $$=rpn_token_t::EXP; } |
- SQRT { $$=rpn_token_t::SQRT; } |
- NEG { $$=rpn_token_t::NEG; } ;
+unary_func : ABS { $$=rpn_calc::rpn_token_t::ABS; } |
+ SIN { $$=rpn_calc::rpn_token_t::SIN; } |
+ COS { $$=rpn_calc::rpn_token_t::COS; } |
+ TAN { $$=rpn_calc::rpn_token_t::TAN; } |
+ ARCSIN { $$=rpn_calc::rpn_token_t::ARCSIN; } |
+ ARCCOS { $$=rpn_calc::rpn_token_t::ARCCOS; } |
+ ARCTAN { $$=rpn_calc::rpn_token_t::ARCTAN; } |
+ LOG { $$=rpn_calc::rpn_token_t::LOG; } |
+ LOG10 { $$=rpn_calc::rpn_token_t::LOG10; } |
+ EXP { $$=rpn_calc::rpn_token_t::EXP; } |
+ SQRT { $$=rpn_calc::rpn_token_t::SQRT; } |
+ NEG { $$=rpn_calc::rpn_token_t::NEG; } ;
real_number : UREAL { $$=$1; } |
ADD UREAL { $$=$2; } |
diff --git a/src/infix_calc_scanner1.ll b/src/infix_calc_scanner1.ll index c77a485..638e651 100644 --- a/src/infix_calc_scanner1.ll +++ b/src/infix_calc_scanner1.ll @@ -69,6 +69,7 @@ blank [ \t\r] \( return infix_calc_yy::parser::make_LPAREN(loc);
\) return infix_calc_yy::parser::make_RPAREN(loc);
+"abs" return infix_calc_yy::parser::make_ABS(loc);
"sin" return infix_calc_yy::parser::make_SIN(loc);
"cos" return infix_calc_yy::parser::make_COS(loc);
"tan" return infix_calc_yy::parser::make_TAN(loc);
@@ -93,6 +94,7 @@ blank [ \t\r] "+" return infix_calc_yy::parser::make_ADD(loc);
\* return infix_calc_yy::parser::make_MUL(loc);
\/ return infix_calc_yy::parser::make_DIV(loc);
+\% return infix_calc_yy::parser::make_MOD(loc);
{id} return infix_calc_yy::parser::make_IDENTIFIER(yytext, loc);
diff --git a/src/infix_calc_scanner2.ll b/src/infix_calc_scanner2.ll index b11d58a..e05c835 100644 --- a/src/infix_calc_scanner2.ll +++ b/src/infix_calc_scanner2.ll @@ -72,6 +72,7 @@ blank [ \t\r] \( return infix_calc_yy::parser::make_LPAREN(loc);
\) return infix_calc_yy::parser::make_RPAREN(loc);
+"abs" return infix_calc_yy::parser::make_ABS(loc);
"sin" return infix_calc_yy::parser::make_SIN(loc);
"cos" return infix_calc_yy::parser::make_COS(loc);
"tan" return infix_calc_yy::parser::make_TAN(loc);
@@ -96,6 +97,7 @@ blank [ \t\r] "+" return infix_calc_yy::parser::make_ADD(loc);
\* return infix_calc_yy::parser::make_MUL(loc);
\/ return infix_calc_yy::parser::make_DIV(loc);
+\% return infix_calc_yy::parser::make_MOD(loc);
{id} return infix_calc_yy::parser::make_IDENTIFIER(yytext, loc);
diff --git a/src/infix_calc_scanner3.ll b/src/infix_calc_scanner3.ll index ade9b77..7adb78c 100644 --- a/src/infix_calc_scanner3.ll +++ b/src/infix_calc_scanner3.ll @@ -34,6 +34,7 @@ \( return infix_calc_yy::parser::make_LPAREN(loc);
\) return infix_calc_yy::parser::make_RPAREN(loc);
+"abs" return infix_calc_yy::parser::make_ABS(loc);
"sin" return infix_calc_yy::parser::make_SIN(loc);
"cos" return infix_calc_yy::parser::make_COS(loc);
"tan" return infix_calc_yy::parser::make_TAN(loc);
@@ -58,6 +59,7 @@ "+" return infix_calc_yy::parser::make_ADD(loc);
\* return infix_calc_yy::parser::make_MUL(loc);
\/ return infix_calc_yy::parser::make_DIV(loc);
+\% return infix_calc_yy::parser::make_MOD(loc);
[a-zA-Z][a-zA-Z_0-9]* return infix_calc_yy::parser::make_IDENTIFIER(yytext, loc);
diff --git a/src/rpn_calc.cpp b/src/rpn_calc.cpp new file mode 100644 index 0000000..fabb0ba --- /dev/null +++ b/src/rpn_calc.cpp @@ -0,0 +1,682 @@ +/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 1993-2023 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstdio>
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+#include <limits>
+#include <functional>
+#include <fstream>
+#include <ostream>
+
+#include <infix_calc/rpn_calc.hpp>
+
+namespace rpn_calc {
+
+static const char *EvalStatusString[] = {
+ "No Error",
+ /*-----------------------*/
+ "Too complex",
+ "Unresolved variables",
+ "Division by zero",
+ "Overflow",
+ "Undefined",
+ "RPN Underflow",
+ /*-----------------------*/
+};
+
+std::string to_string(RPNStatus status) noexcept {
+ return std::string(EvalStatusString[static_cast<unsigned int>(status)]);
+}
+
+std::string to_string(const rpn_token_t ts) noexcept {
+ switch(ts) {
+ case rpn_token_t::UREAL:
+ return "<ureal>";
+ case rpn_token_t::VARIABLE:
+ return "<var>";
+ case rpn_token_t::SUB:
+ return "-";
+ case rpn_token_t::ADD:
+ return "+";
+ case rpn_token_t::MUL:
+ return "*";
+ case rpn_token_t::DIV:
+ return "/";
+ case rpn_token_t::MOD:
+ return "%";
+ case rpn_token_t::ABS:
+ return "abs";
+ case rpn_token_t::SIN:
+ return "sin";
+ case rpn_token_t::COS:
+ return "cos";
+ case rpn_token_t::TAN:
+ return "tan";
+ case rpn_token_t::ARCSIN:
+ return "asin";
+ case rpn_token_t::ARCCOS:
+ return "acos";
+ case rpn_token_t::ARCTAN:
+ return "atan";
+ case rpn_token_t::POW:
+ return "pow";
+ case rpn_token_t::LOG:
+ return "ln";
+ case rpn_token_t::LOG10:
+ return "log";
+ case rpn_token_t::EXP:
+ return "exp";
+ case rpn_token_t::SQRT:
+ return "sqrt";
+ case rpn_token_t::NEG:
+ return "neg";
+ }
+ return "unknown";
+}
+
+std::string to_string(const rpn_token& v) noexcept {
+ switch(v.ts) {
+ case rpn_token_t::UREAL:
+ return std::to_string(v.value);
+ case rpn_token_t::VARIABLE:
+ return "'"+v.id+"'";
+ default:
+ return to_string(v.ts);
+ }
+ return "unknown";
+}
+
+std::string to_string(const rpn_stack_t& rpn_expr) noexcept {
+ std::string r;
+ for(const rpn_token& t : rpn_expr) {
+ r.append(to_string(t));
+ if( t.ts == rpn_token_t::UREAL || t.ts == rpn_token_t::VARIABLE ) {
+ r.append(", ");
+ } else {
+ r.append("; ");
+ }
+ }
+ return r;
+}
+
+std::string to_string(const variable_set& variables) noexcept {
+ std::string r;
+ for (const auto& [key, value] : variables) {
+ r.append(key).append(" = ").append(std::to_string(value)).append(", ");
+ }
+ return r;
+}
+
+RPNStatus reduce(rpn_stack_t& res, const rpn_stack_t& source) noexcept {
+ res.clear();
+ RPNStatus status = RPNStatus::No_Error;
+ double left_operand, right_operand;
+
+ auto check_stack_cntr = [&](size_t min) noexcept -> bool {
+ if( res.size() < min ) {
+ status = RPNStatus::RPN_Underflow;
+ return false;
+ } else {
+ return true;
+ }
+ };
+ auto get_stack_ureal = [&](size_t count) noexcept -> bool {
+ const size_t sz = res.size();
+ if( 0 == sz ) {
+ return 0 == count;
+ }
+ const size_t hi = sz - 1;
+ for(size_t i=0; i<count; ++i) {
+ if( hi < i || res[hi-i].ts != rpn_token_t::UREAL ) {
+ return false;
+ }
+ }
+ if( count >= 1 ) {
+ right_operand = res.back().value;
+ res.pop_back();
+ }
+ if( count >= 2 ) {
+ left_operand = res.back().value;
+ res.pop_back();
+ }
+ return true;
+ };
+
+ for (size_t i=0; i<source.size() && status==RPNStatus::No_Error; i++)
+ {
+ const rpn_token& t = source[i];
+ switch (t.ts)
+ {
+ case rpn_token_t::UREAL:
+ res.push_back(t);
+ break;
+
+ case rpn_token_t::VARIABLE:
+ res.push_back(t);
+ break;
+
+ case rpn_token_t::ADD:
+ if( !check_stack_cntr(2) ) { break; }
+ if( get_stack_ureal(2) ) {
+ if ( ( ( left_operand > 0 && right_operand > 0 )
+ ||
+ ( left_operand < 0 && right_operand < 0 )
+ ) &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() - std::abs(right_operand)
+ )
+ {
+ status = RPNStatus::Overflow;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, left_operand + right_operand, "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::SUB:
+ if( !check_stack_cntr(2) ) { break; }
+ if( get_stack_ureal(2) ) {
+ if ( ( ( left_operand > 0 && right_operand < 0 )
+ ||
+ ( left_operand < 0 && right_operand > 0 )
+ ) &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() - std::abs(right_operand)
+ )
+ {
+ status =RPNStatus::Overflow;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, left_operand - right_operand, "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::MUL:
+ if( !check_stack_cntr(2) ) { break; }
+ if( get_stack_ureal(2) ) {
+ if( std::abs(right_operand) > 1 &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() / std::abs(right_operand) )
+ {
+ status =RPNStatus::Overflow;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, left_operand * right_operand, "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::DIV:
+ if( !check_stack_cntr(2) ) { break; }
+ if( get_stack_ureal(2) ) {
+ if (right_operand == 0)
+ {
+ status = RPNStatus::Division_by_zero;
+ break;
+ }
+ if( std::abs(right_operand) < 1 &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() * std::abs(right_operand) )
+ {
+ status = RPNStatus::Overflow;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, left_operand / right_operand, "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::MOD:
+ if( !check_stack_cntr(2) ) { break; }
+ if( get_stack_ureal(2) ) {
+ if (right_operand == 0)
+ {
+ status = RPNStatus::Division_by_zero;
+ break;
+ }
+ if( std::abs(right_operand) < 1 &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() * std::abs(right_operand) )
+ {
+ status = RPNStatus::Overflow;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, std::fmod(left_operand, right_operand), "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::POW:
+ if( !check_stack_cntr(2) ) { break; }
+ if( get_stack_ureal(2) ) {
+ res.push_back( { rpn_token_t::UREAL, std::pow(left_operand, right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::SQRT:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ if (right_operand<0)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, std::sqrt(right_operand), "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::LOG:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ if (right_operand<=0)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, std::log(right_operand), "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::LOG10:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ if (right_operand<=0)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, std::log10(right_operand), "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::EXP:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, std::exp(right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::ABS:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, std::abs(right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::SIN:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, std::sin(right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::COS:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, std::cos(right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::TAN:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, std::tan(right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::ARCSIN:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ if (std::abs(right_operand)>1)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, std::asin(right_operand), "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::ARCCOS:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ if (std::abs(right_operand)>1)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ res.push_back( { rpn_token_t::UREAL, std::acos(right_operand), "" } );
+ }
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::ARCTAN:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, std::atan(right_operand), "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ case rpn_token_t::NEG:
+ if( !check_stack_cntr(1) ) { break; }
+ if( get_stack_ureal(1) ) {
+ res.push_back( { rpn_token_t::UREAL, 0 - right_operand, "" } );
+ } else {
+ res.push_back(t);
+ }
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ return status;
+}
+
+bool rpn_expression_t::resolved(const variable_set& variables) const noexcept {
+ for( const rpn_token& t : expr ) {
+ switch (t.ts)
+ {
+ case rpn_token_t::VARIABLE:
+ {
+ if (auto var_iter = variables.find(t.id); var_iter == variables.end()) {
+ // variable t.id undefined!
+ fprintf(stderr, "variable '%s' undefined\n", t.id.c_str());
+ return false;
+ }
+ }
+ break;
+
+ default :
+ break;
+ }
+ }
+ return true;
+}
+
+RPNStatus rpn_expression_t::eval(double& result, const variable_set& variables) const noexcept {
+ RPNStatus status = RPNStatus::No_Error;
+ double left_operand, right_operand;
+
+ double stack[expr.size()];
+ size_t stack_cntr=0;
+ stack[0] = 0.0;
+
+ auto get_stack = [&](size_t count) noexcept -> bool {
+ if( stack_cntr < count ) {
+ status = RPNStatus::RPN_Underflow;
+ return false;
+ }
+ if( count >= 1 ) {
+ right_operand = stack[--stack_cntr];
+ }
+ if( count >= 2 ) {
+ left_operand = stack[--stack_cntr];
+ }
+ return true;
+ };
+
+ for (size_t i=0; i<expr.size() && status==RPNStatus::No_Error; i++)
+ {
+ const rpn_token& t = expr[i];
+ switch (t.ts)
+ {
+ case rpn_token_t::UREAL:
+ stack[stack_cntr++] = t.value;
+ break;
+
+ case rpn_token_t::VARIABLE:
+ {
+ if (auto var_iter = variables.find(t.id); var_iter != variables.end()) {
+ stack[stack_cntr++] = var_iter->second;
+ } else {
+ // variable t.id undefined!
+ fprintf(stderr, "variable '%s' undefined\n", t.id.c_str());
+ status = RPNStatus::Unresolved_Variables; // FIXME: Refine!
+ }
+ }
+ break;
+
+ case rpn_token_t::ADD:
+ if( !get_stack(2) ) { break; }
+ if ( ( ( left_operand > 0 && right_operand > 0 )
+ ||
+ ( left_operand < 0 && right_operand < 0 )
+ ) &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() - std::abs(right_operand)
+ )
+ {
+ status = RPNStatus::Overflow;
+ } else {
+ stack[stack_cntr++] = left_operand + right_operand;
+ }
+ break;
+
+ case rpn_token_t::SUB:
+ if( !get_stack(2) ) { break; }
+ if ( ( ( left_operand > 0 && right_operand < 0 )
+ ||
+ ( left_operand < 0 && right_operand > 0 )
+ ) &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() - std::abs(right_operand)
+ )
+ {
+ status =RPNStatus::Overflow;
+ } else {
+ stack[stack_cntr++] = left_operand - right_operand;
+ }
+ break;
+
+ case rpn_token_t::MUL:
+ if( !get_stack(2) ) { break; }
+ if( std::abs(right_operand) > 1 &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() / std::abs(right_operand) )
+ {
+ status =RPNStatus::Overflow;
+ } else {
+ stack[stack_cntr++] = left_operand * right_operand;
+ }
+ break;
+
+ case rpn_token_t::DIV:
+ if( !get_stack(2) ) { break; }
+ if (right_operand == 0)
+ {
+ status = RPNStatus::Division_by_zero;
+ break;
+ }
+ if( std::abs(right_operand) < 1 &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() * std::abs(right_operand) )
+ {
+ status = RPNStatus::Overflow;
+ } else {
+ stack[stack_cntr++] = left_operand / right_operand;
+ }
+ break;
+
+ case rpn_token_t::MOD:
+ if( !get_stack(2) ) { break; }
+ if (right_operand == 0)
+ {
+ status = RPNStatus::Division_by_zero;
+ break;
+ }
+ if( std::abs(right_operand) < 1 &&
+ std::abs(left_operand) >=
+ std::numeric_limits<double>::max() * std::abs(right_operand) )
+ {
+ status = RPNStatus::Overflow;
+ } else {
+ stack[stack_cntr++] = std::fmod(left_operand, right_operand);
+ }
+ break;
+
+ case rpn_token_t::POW:
+ if( !get_stack(2) ) { break; }
+ stack[stack_cntr++] = std::pow(left_operand,right_operand);
+ break;
+
+ case rpn_token_t::SQRT:
+ if( !get_stack(1) ) { break; }
+ if (right_operand<0)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ stack[stack_cntr++] = std::sqrt (right_operand);
+ }
+ break;
+
+ case rpn_token_t::LOG:
+ if( !get_stack(1) ) { break; }
+ if (right_operand<=0)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ stack[stack_cntr++] = std::log (right_operand);
+ }
+ break;
+
+ case rpn_token_t::LOG10:
+ if( !get_stack(1) ) { break; }
+ if (right_operand<=0)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ stack[stack_cntr++] = std::log10(right_operand);
+ }
+ break;
+
+ case rpn_token_t::EXP:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = std::exp(right_operand);
+ break;
+
+ case rpn_token_t::ABS:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = std::abs(right_operand);
+ break;
+
+ case rpn_token_t::SIN:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = std::sin(right_operand);
+ break;
+
+ case rpn_token_t::COS:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = std::cos(right_operand);
+ break;
+
+ case rpn_token_t::TAN:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = std::tan(right_operand);
+ break;
+
+ case rpn_token_t::ARCSIN:
+ if( !get_stack(1) ) { break; }
+ if (std::abs(right_operand)>1)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ stack[stack_cntr++] = std::asin (right_operand);
+ }
+ break;
+
+ case rpn_token_t::ARCCOS:
+ if( !get_stack(1) ) { break; }
+ if (std::abs(right_operand)>1)
+ {
+ status=RPNStatus::Undefined;
+ } else {
+ stack[stack_cntr++] = std::acos (right_operand);
+ }
+ break;
+
+ case rpn_token_t::ARCTAN:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = std::atan (right_operand);
+ break;
+
+ case rpn_token_t::NEG:
+ if( !get_stack(1) ) { break; }
+ stack[stack_cntr++] = 0 - right_operand;
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ if( status == RPNStatus::No_Error ) {
+ result = stack[0];
+ }
+ return (status);
+}
+
+} // namespace rpn_calc
+
+std::ostream& std::operator<<(std::ostream& os, const rpn_calc::rpn_token_t ts) {
+ os << rpn_calc::to_string(ts);
+ return os;
+}
+
+std::ostream& std::operator<<(std::ostream& os, const rpn_calc::rpn_token& rpn) {
+ os << rpn_calc::to_string(rpn);
+ return os;
+}
|