Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@caiorss
Created June 17, 2020 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caiorss/2ff870b653b2ec1519bf0423165db1c5 to your computer and use it in GitHub Desktop.
Save caiorss/2ff870b653b2ec1519bf0423165db1c5 to your computer and use it in GitHub Desktop.
Exprk math-expression library
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 )
#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();
}
#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';
}
}
}
#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