-
-
Save caiorss/2ff870b653b2ec1519bf0423165db1c5 to your computer and use it in GitHub Desktop.
Exprk math-expression library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.9) | |
project(exprk-parser) | |
#========== Global Configurations =============# | |
#----------------------------------------------# | |
set(CMAKE_CXX_STANDARD 17) | |
set(CMAKE_VERBOSE_MAKEFILE ON) | |
set(CMAKE_CXX_EXTENSIONS OFF) | |
#----------- Add dependencies --------------------------# | |
#============= Functions and macros ===========================# | |
macro(Download_Single_Headerlib FILE URL) | |
file(DOWNLOAD ${URL} ${CMAKE_BINARY_DIR}/include/${FILE}) | |
IF(NOT Download_Single_Headerlib_flag) | |
include_directories(${CMAKE_BINARY_DIR}/include) | |
set(Download_Single_Headerlib_flag TRUE) | |
ENDIF() | |
endmacro() | |
Download_Single_Headerlib( exprtk.hpp | |
https://github.com/ArashPartow/exprtk/raw/d81ac1a2ddd9877a7981d32c731fd9a75544ec68/exprtk.hpp ) | |
#----------- Target settings -------------------------------# | |
add_library( mparser mparser.cpp mparser.hpp ) | |
add_executable( main main.cpp ) | |
target_link_libraries( main mparser ) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <cassert> | |
#include "mparser.hpp" | |
double myfun(double a, double b); | |
void test_engine(MathEvaluator& engine, double& x); | |
int main(int argc, char** argv) | |
{ | |
std::puts(" [TRACE] I am up and running Ok. "); | |
MathEvaluator engine; | |
double x = 1.0, y = 2.0, z = 3.0; | |
engine.add_var("x", x) | |
.add_var("y", y) | |
.add_var("z", z) | |
.add_function("myfun", &myfun); | |
assert(argc == 2); | |
auto command = std::string(argv[1]); | |
if(command == "test" ){ test_engine(engine, x); } | |
if(command == "repl" ){ engine.repl(); } | |
std::cerr << " [TRACE] Shutdown engine Ok. " << '\n'; | |
return 0; | |
} | |
// -----------------------------------------// | |
double myfun(double a, double b) | |
{ | |
std::cerr << " [TRACE] a = " << a << "\n"; | |
std::cerr << " [TRACE] b = " << b << "\n"; | |
double r = 3.0 * a + 5.0 * b; | |
std::cerr << " [TRACE] result = " << r << "\n"; | |
std::cerr << "---------------------------------\n"; | |
return r; | |
} | |
void test_engine(MathEvaluator& engine, double& x) | |
{ | |
std::string code = R"( | |
// Define local variables | |
var a := 2.0 / exp(x) * x^2 + y; | |
var b := 10.0 * sqrt(x) + z; | |
// println('\n => x = ', x); | |
// println('\n => y = ', y); | |
// Call custom function | |
var k := myfun(x, y); | |
// Comment: the last expression is returned | |
4.0 * a + 3 * b + 10 * z + k; | |
)"; | |
engine.compile(code); | |
x = 3.0; | |
std::cout << " => x = " << x << " ; engine = " << engine.value() << "\n"; | |
x = 5.0; | |
std::cout << " => x = " << x << " ; engine = " << engine.value() << "\n"; | |
x = 15.0; | |
std::cout << " => x = " << x << " ; engine = " << engine.value() << "\n"; | |
x = -15.0; | |
std::cout << " => x = " << x << " ; engine = " << engine.value() << "\n"; | |
x = 20.0; | |
std::cout << " => x = " << x << " ; engine = " << engine.value() << "\n"; | |
std::string code2 = R"( | |
// Vector/array variable | |
var xs [6] := { 2.0, 10.2, -2.50, 9.256, 100.0, 25.0 }; | |
var ys [6] := { -2.0, 1.225, -5.56, 19.000, 125.0, 125.0 }; | |
println(' => xs =', xs); | |
println(' => ys = ', ys); | |
println(' => 3 * xs + 4 * ys = ', 3 * xs + 4 * ys); | |
println(' => sum(xs) = ', sum(ys) ); | |
println(' => sum(ys) = ', sum(xs) ); | |
)"; | |
engine.compile(code2); | |
engine.value(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mparser.hpp" | |
#include <iostream> | |
#include <string> | |
#include <exprtk.hpp> | |
// --------------------------------------------------// | |
struct MathEvaluator::impl | |
{ | |
exprtk::expression<double> expr; | |
exprtk::symbol_table<double> symbol_table; | |
exprtk::parser<double> parser; | |
std::string code; | |
// Println function | |
exprtk::rtl::io::println<double> println{}; | |
}; | |
// static method | |
void | |
MathEvaluator::dispose(impl* p) | |
{ | |
delete p; | |
} | |
MathEvaluator::MathEvaluator(): m_pimpl(new impl) | |
{ | |
m_pimpl->symbol_table.add_constants(); | |
m_pimpl->expr.register_symbol_table(m_pimpl->symbol_table); | |
m_pimpl->symbol_table.add_function("println", m_pimpl->println); | |
} | |
MathEvaluator& | |
MathEvaluator::add_var(std::string name, double& ref) | |
{ | |
m_pimpl->symbol_table.add_variable(name, ref); | |
return *this; | |
} | |
MathEvaluator& | |
MathEvaluator::add_function(std::string name, double fptr (double) ) | |
{ | |
m_pimpl->symbol_table.add_function(name, fptr); | |
return *this; | |
} | |
MathEvaluator& | |
MathEvaluator::add_function(std::string name, double fptr (double, double) ) | |
{ | |
m_pimpl->symbol_table.add_function(name, fptr); | |
return *this; | |
} | |
bool | |
MathEvaluator::compile(std::string code) | |
{ | |
bool r = m_pimpl->parser.compile(code, m_pimpl->expr); | |
if(!r){ | |
std::string err = "Error: "; | |
err = err + m_pimpl->parser.error(); | |
std::cerr << " Error: " << err << "\n"; | |
throw std::runtime_error(" [PARSER] Unable to parse expression."); | |
} | |
return r; | |
} | |
double | |
MathEvaluator::value() | |
{ | |
return m_pimpl->expr.value(); | |
} | |
void MathEvaluator::repl() | |
{ | |
std::string line; | |
double result; | |
while( std::cin.good() ) | |
{ | |
std::cout << " EXPRTK $ >> "; | |
std::getline(std::cin, line); | |
if(line.empty()) continue; | |
if(line == "exit") return; | |
try { | |
this->compile(line); | |
std::cout << this->value() << '\n'; | |
} catch (std::runtime_error& ex) { | |
std::cerr << "Error: " << ex.what() << '\n'; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _MPARSER_HPP_ | |
#define _MPARSER_HPP_ | |
#include <string> | |
#include <memory> | |
#include <functional> | |
// Unique-ownership smart pointer for implementing PimPl | |
// | |
// Note: It was created because std::uniqe_ptr does | |
// not support 'incomplete types'. | |
// | |
// Note: PimPl (Pointer-To-Implementation) idiom | |
// is a technique for reducing compile-time and | |
// maintaining ABI stability which mitigates the | |
// fragile-ABI problem. | |
// | |
template<typename T, void (*disposer) (T*)> | |
class pimpl_ptr | |
{ | |
T* m_hnd; | |
public: | |
pimpl_ptr() | |
: m_hnd(nullptr) | |
//, m_disp(disp) | |
{ } | |
pimpl_ptr(T* hnd) | |
: m_hnd(hnd) | |
{ } | |
~pimpl_ptr() | |
{ | |
this->release(); | |
} | |
// Disable copy constructor | |
pimpl_ptr(pimpl_ptr const&) = delete; | |
// Disable copy- | |
pimpl_ptr& operator=(pimpl_ptr const&) = delete; | |
// Move Ctor | |
pimpl_ptr(pimpl_ptr&& rhs) | |
{ | |
std::swap(m_hnd, rhs.m_hnd); | |
} | |
// Move assignment operator | |
pimpl_ptr& operator=(pimpl_ptr&& rhs) | |
{ | |
std::swap(m_hnd, rhs.m_hnd); | |
} | |
T* get() const { return m_hnd; } | |
void release() | |
{ | |
// Note: it is not possible to delete incomplete type | |
// in this way: 'delete m_hnd;' | |
disposer(m_hnd); | |
m_hnd = nullptr; | |
} | |
void reset(T* hnd) | |
{ | |
this->release(); | |
m_hnd = hnd; | |
} | |
bool empty() const { return m_hnd == nullptr; } | |
T& operator* () const { return *m_hnd; } | |
T* operator-> () const { return m_hnd; } | |
}; | |
class MathEvaluator | |
{ | |
struct impl; | |
static void dispose(impl* p); | |
pimpl_ptr<impl, MathEvaluator::dispose> m_pimpl; | |
public: | |
MathEvaluator(); | |
~MathEvaluator() = default; | |
MathEvaluator& add_var(std::string name, double& ref); | |
// Register function pointer callback or non-capture lambda | |
MathEvaluator& add_function(std::string, double fptr (double) ); | |
// Register function of two variables | |
MathEvaluator& add_function(std::string, double fptr (double, double) ); | |
double eval_code(std::string code); | |
bool compile(std::string code); | |
double value(); | |
void repl(); | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment