The following are examples demonstrating how to use TinyExpr++.
#include "tinyexpr.h"
#include <iostream>
int main(int argc, char *argv[])
{
te_parser tep;
const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)";
double r = tep.evaluate(c);
std::cout << "The expression:\n\t" <<
c << "\nevaluates to:\n\t" << r << "\n";
return EXIT_SUCCESS;
}#include "tinyexpr.h"
#include <iostream>
#include <iomanip>
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cout << "Usage: example \"expression\"\n";
return EXIT_SUCCESS;
}
const char* expression = argv[1];
std::cout << "Evaluating:\n\t" << expression << "\n";
/* This shows an example where the variables
x and y are bound at eval-time. */
double x{ 0 }, y{ 0 };
// Store variable names and pointers.
te_parser tep;
tep.set_variables_and_functions({ {"x", &x}, {"y", &y} });
/* This will compile the expression and check for errors. */
if (tep.compile(expression))
{
/* The variables can be changed here, and eval can be called as many
* times as you like. This is fairly efficient because the parsing has
* already been done. */
x = 3; y = 4;
const double r = tep.evaluate();
std::cout << "Result:\n\t" << r << "\n";
}
else
{
/* Show the user where the error is at. */
std::cout << "\t " << std::setfill(' ') <<
std::setw(tep.get_last_error_position()) << '^' <<
"\tError near here\n";
}
return EXIT_SUCCESS;
}#include "tinyexpr.h"
#include <iostream>
#include <iomanip>
/* An example of calling a free function. */
double my_sum(double a, double b)
{
std::cout << "Called C function with " <<
a << " and " << b << ".\n";
return a + b;
}
int main(int argc, char *argv[])
{
const char *expression = "mysum(5, 6)";
std::cout << "Evaluating:\n\t" << expression << "\n";
te_parser tep;
tep.set_variables_and_functions({{"mysum", my_sum}});
if (tep.compile(expression))
{
const double r = tep.evaluate();
std::cout << "Result:\n\t" << r << "\n";
}
else
{
/* Show the user where the error is at. */
std::cout << "\t " << std::setfill(' ') <<
std::setw(tep.get_last_error_position()) << '^' <<
"\tError near here\n";
}
return EXIT_SUCCESS;
}#include "tinyexpr.h"
#include <iostream>
#include <iomanip>
#include <locale>
#include <clocale>
int main(int argc, char *argv[])
{
/* Set locale to German.
This string is platform dependent. The following works on Windows,
consult your platform's documentation for more details.*/
setlocale(LC_ALL, "de-DE");
std::locale::global(std::locale("de-DE"));
/* After setting your locale to German, functions like strtod() will fail
with values like "3.14" because it expects "3,14" instead.
To fix this, we will tell the parser to use "," as the decimal separator
and ";" as list argument separator.*/
const char *expression = "pow(2,2; 2)"; // instead of "pow(2.2, 2)"
std::cout << "Evaluating:\n\t" << expression << "\n";
te_parser tep;
tep.set_decimal_separator(',');
tep.set_list_separator(';');
if (tep.compile(expression))
{
const double r = tep.evaluate();
std::cout << "Result:\n\t" << r << "\n";
}
else
{
/* Show the user where the error is at. */
std::cout << "\t " << std::setfill(' ') <<
std::setw(tep.get_last_error_position()) << '^' <<
"\tError near here\n";
}
return EXIT_SUCCESS;
}A class derived from te_expr can be bound to custom functions. This enables you to
have full access to an object (via these functions) when parsing an expression.
The following demonstrates creating a te_expr-derived class which contains an array of values:
class te_expr_array : public te_expr
{
public:
explicit te_expr_array(const variable_flags type) noexcept :
te_expr(type) {}
std::array<double, 5> m_data = { 5, 6, 7, 8, 9 };
};Next, create two functions that can accept this object and perform actions on it. (Note that proper error handling is not shown for brevity.):
// Returns the value of a cell from the object's data.
double cell(const te_expr* context, double a)
{
auto* c = dynamic_cast<const te_expr_array*>(context);
return static_cast<double>(c->m_data[static_cast<size_t>(a)]);
}
// Returns the max value of the object's data.
double cell_max(const te_expr* context)
{
auto* c = dynamic_cast<const te_expr_array*>(context);
return static_cast<double>(
*std::max_element(c->m_data.cbegin(), c->m_data.cend()));
}Finally, create an instance of the class and connect the custom functions to it, while also adding them to the parser:
te_expr_array teArray{ TE_DEFAULT };
te_parser tep;
tep.set_variables_and_functions(
{
{"cell", cell, TE_DEFAULT, &teArray},
{"cellmax", cell_max, TE_DEFAULT, &teArray}
});
// change the object's data and evaluate their summation
// (will be 30)
teArray.m_data = { 6, 7, 8, 5, 4 };
auto result = tep.evaluate("SUM(CELL 0, CELL 1, CELL 2, CELL 3, CELL 4)");
// call the other function, getting the object's max value
// (will be 8)
result = tep.evaluate("CellMax()");Functions can accept quoted string literals from a formula.
Such a function takes a std::span of te_arg, where each te_arg is either a number or a std::string_view.
Any argument can be a string or a number in any position, and there is no limit on how many can be passed.
Because of this, these functions have no fixed arity.
The parser therefore does not verify the argument count the way it does for te_fun0–te_fun24.
The function should review args.size() and each argument's type itself:
#include "tinyexpr.h"
#include <iostream>
#include <map>
#include <span>
#include <string>
#include <variant>
// A stand-in for a real database.
const std::map<std::string, double, std::less<>> DB =
{
{ "/Equipment/Temp", 21.5 },
{ "/Equipment/Pressure", 101.3 }
};
double db_query(std::span<const te_arg> args)
{
// One required argument (the path), and an optional default
// for when the path isn't found.
if (args.empty() || args.size() > 2 ||
!std::holds_alternative<std::string_view>(args[0]))
{ return te_parser::te_nan; }
const auto found = DB.find(std::get<std::string_view>(args[0]));
if (found == DB.cend())
{
return (args.size() == 2 && std::holds_alternative<double>(args[1])) ?
std::get<double>(args[1]) : te_parser::te_nan;
}
return found->second;
}
int main()
{
te_parser tep;
tep.set_variables_and_functions(
{
// the cast tells the compiler which function type to bind to
{ "dbquery", static_cast<te_arg_fun>(db_query) }
});
// 21.5
std::cout << tep.evaluate(R"(DBQUERY("/Equipment/Temp"))") << "\n";
// convert to Fahrenheit: 70.7
std::cout << tep.evaluate(R"(DBQUERY("/Equipment/Temp") * 9 / 5 + 32)") << "\n";
// fall back to a default for an unknown path: 0
std::cout << tep.evaluate(R"(DBQUERY("/Equipment/Nope", 0))") << "\n";
return EXIT_SUCCESS;
}To bind such a function to a class instead, use te_arg_confun.
It receives the client object as its first argument, exactly like the functions in Example 5:
class te_database : public te_expr
{
public:
explicit te_database(const te_variable_flags type) noexcept :
te_expr(type) {}
std::map<std::string, double, std::less<>> m_rows =
{ { "voltage", 240 }, { "current", 13 } };
};
double query_db(const te_expr* context, std::span<const te_arg> args)
{
auto* db = dynamic_cast<const te_database*>(context);
if (db == nullptr || args.size() != 1 ||
!std::holds_alternative<std::string_view>(args[0]))
{ return te_parser::te_nan; }
const auto found = db->m_rows.find(std::get<std::string_view>(args[0]));
return (found == db->m_rows.cend()) ? te_parser::te_nan : found->second;
}
te_database db{ TE_DEFAULT };
te_parser tep;
tep.set_variables_and_functions(
{
{ "query", static_cast<te_arg_confun>(query_db), TE_DEFAULT, &db }
});
// will be 3120
const double r = tep.evaluate(R"(QUERY("voltage") * QUERY("current"))");