A minimal, header-only library for dynamically evaluating C-style integer expressions.
Requires: C++11 or later.
- Header-only: Just copy
TinyExpr.hpp. - C-style syntax: Arithmetic, bitwise, shift, and ternary operators.
- Variable support: Variable names start with
$,_, ora-zA-Z, followed by any combination ofa-zA-Z,0-9,_,$. Case-sensitive. Provide a dictionary (std::map). - Hexadecimal support: Supports
0x1Fliterals. - Whitespace agnostic: Multi-line and indented formulas are supported.
#include "TinyExpr.hpp"
#include <map>
#include <iostream>
int main() {
tinyexpr::TinyExpr eval;
tinyexpr::vars variables = {{"hp", 100}, {"buff", 0x10}};
std::string formula = "(hp + buff) << 1";
tinyexpr::value result = eval.evaluate(formula, variables);
std::cout << result << std::endl; // 232
return 0;
}This header-only evaluator supports the following tokens and operators (precedence from highest to lowest):
- Primary: parentheses
(), numeric literals (decimal,0xhex), variables (first char:$/_/a-zA-Z; subsequent chars:$/_/a-zA-Z/0-9; case-sensitive). - Unary: unary minus
-, bitwise NOT~, logical NOT!. - Multiplicative:
*,/(division throws on divide-by-zero). - Additive:
+,-. - Shift:
<<,>>. - Relational:
<,<=,>,>=. - Equality:
==,!=. - Bitwise:
&,^,|(AND, XOR, OR). - Logical:
&&,||(short-circuiting;&&binds tighter than||). - Ternary conditional:
cond ? expr1 : expr2.
Notes:
- Numeric literals are parsed as
unsigned long longand truncated toT. Literals exceeding 64 bits cause a parse error. See Choosing an evaluator type for details. - Signed integer overflow follows C++ semantics (undefined behavior). Keep values within the signed range of
T, or use an unsigned type (e.g.TinyExprT<uint32_t>) for defined wrap-around.
tinyexpr::TinyExpr (64-bit signed int64_t) is recommended for general use. It covers the full range of typical integer values, and casting the result to a narrower type at the call site is usually all you need:
tinyexpr::TinyExpr eval;
tinyexpr::vars variables = {{"hp", 100}, {"buff", 0x10}};
int32_t result = static_cast<int32_t>(eval.evaluate("hp + buff", variables));All available specializations are listed below. The header includes <cstdint> automatically; value and vars convenience aliases use int64_t.
| Type | Underlying type | Value type (Type) |
|---|---|---|
tinyexpr::TinyExpr (default) |
int64_t (64-bit signed) |
int64_t |
tinyexpr::TinyExpr32 |
int32_t (32-bit signed) |
int32_t |
tinyexpr::TinyExpr16 |
int16_t (16-bit signed) |
int16_t |
tinyexpr::TinyExpr8 |
int8_t (8-bit signed) |
int8_t |
Note on numeric literals: all literals are parsed as unsigned long long via stoull, then truncated to T via static_cast<T>. This means you can freely write values beyond the signed range of T — for example, 0xffffffffffffffff with TinyExpr (64-bit) yields -1, 0xffffffff with TinyExpr32 yields -1, and with TinyExpr8, 255/0xff yield -1 and 128 yields -128. The bit pattern is simply reinterpreted in the target width; there is no restriction on what literals you can write.
For any other integral type, instantiate tinyexpr::TinyExprT<T> directly.