28#include <unordered_set>
32#include <nlohmann/json.hpp>
34namespace Expressionist {
36using json = nlohmann::json;
41enum class EvalMethod {
55 : _message(std::move(message)) {}
56 const char *what()
const noexcept override {
return _message.c_str(); }
69using Value = std::variant<std::int64_t, double, bool, json>;
71inline bool is_int(
const Value &v) {
72 return std::holds_alternative<std::int64_t>(v);
75inline bool is_bool_value(
const Value &v) {
76 return std::holds_alternative<bool>(v);
79inline bool is_seq(
const Value &v) {
return std::holds_alternative<json>(v); }
81inline double to_double(
const Value &v) {
82 if (std::holds_alternative<std::int64_t>(v))
83 return static_cast<double>(std::get<std::int64_t>(v));
84 if (std::holds_alternative<double>(v))
85 return std::get<double>(v);
86 if (std::holds_alternative<bool>(v))
87 return std::get<bool>(v) ? 1.0 : 0.0;
88 throw ExpressionistException(
"a sequence cannot be used as a number");
91inline bool to_bool(
const Value &v) {
92 if (std::holds_alternative<bool>(v))
93 return std::get<bool>(v);
94 if (std::holds_alternative<std::int64_t>(v))
95 return std::get<std::int64_t>(v) != 0;
96 if (std::holds_alternative<double>(v))
97 return std::get<double>(v) != 0.0;
98 throw ExpressionistException(
"a sequence cannot be used as a boolean");
101inline json to_json(
const Value &v) {
102 if (std::holds_alternative<std::int64_t>(v))
103 return json(std::get<std::int64_t>(v));
104 if (std::holds_alternative<bool>(v))
105 return json(std::get<bool>(v));
106 if (std::holds_alternative<double>(v))
107 return json(std::get<double>(v));
108 return std::get<json>(v);
115using UnaryFn = std::function<double(
double)>;
116using BinaryFn = std::function<double(
double,
double)>;
119 std::map<std::string, double> constants;
120 std::map<std::string, UnaryFn> unary;
121 std::map<std::string, BinaryFn> binary;
124inline Symbols default_symbols() {
128 s.constants[
"pi"] = 3.14159265358979323846;
129 s.constants[
"e"] = 2.71828182845904523536;
130 s.constants[
"tau"] = 6.28318530717958647692;
132 s.unary[
"sin"] = [](
double x) {
return std::sin(x); };
133 s.unary[
"cos"] = [](
double x) {
return std::cos(x); };
134 s.unary[
"tan"] = [](
double x) {
return std::tan(x); };
135 s.unary[
"asin"] = [](
double x) {
return std::asin(x); };
136 s.unary[
"acos"] = [](
double x) {
return std::acos(x); };
137 s.unary[
"atan"] = [](
double x) {
return std::atan(x); };
138 s.unary[
"sinh"] = [](
double x) {
return std::sinh(x); };
139 s.unary[
"cosh"] = [](
double x) {
return std::cosh(x); };
140 s.unary[
"tanh"] = [](
double x) {
return std::tanh(x); };
141 s.unary[
"exp"] = [](
double x) {
return std::exp(x); };
142 s.unary[
"log"] = [](
double x) {
return std::log(x); };
143 s.unary[
"ln"] = [](
double x) {
return std::log(x); };
144 s.unary[
"log10"] = [](
double x) {
return std::log10(x); };
145 s.unary[
"log2"] = [](
double x) {
return std::log2(x); };
146 s.unary[
"sqrt"] = [](
double x) {
return std::sqrt(x); };
147 s.unary[
"cbrt"] = [](
double x) {
return std::cbrt(x); };
148 s.unary[
"abs"] = [](
double x) {
return std::fabs(x); };
149 s.unary[
"floor"] = [](
double x) {
return std::floor(x); };
150 s.unary[
"ceil"] = [](
double x) {
return std::ceil(x); };
151 s.unary[
"round"] = [](
double x) {
return std::round(x); };
152 s.unary[
"trunc"] = [](
double x) {
return std::trunc(x); };
153 s.unary[
"sign"] = [](
double x) {
154 return x > 0.0 ? 1.0 : (x < 0.0 ? -1.0 : 0.0);
157 s.binary[
"pow"] = [](
double a,
double b) {
return std::pow(a, b); };
158 s.binary[
"atan2"] = [](
double a,
double b) {
return std::atan2(a, b); };
159 s.binary[
"hypot"] = [](
double a,
double b) {
return std::hypot(a, b); };
160 s.binary[
"min"] = [](
double a,
double b) {
return std::min(a, b); };
161 s.binary[
"max"] = [](
double a,
double b) {
return std::max(a, b); };
162 s.binary[
"mod"] = [](
double a,
double b) {
return std::fmod(a, b); };
203 explicit Tokenizer(std::string src) : _src(std::move(src)) {}
205 std::vector<Token> tokenize() {
206 std::vector<Token> tokens;
209 if (_i >= _src.size()) {
210 tokens.push_back({TokType::End,
"", Value{}, _i});
215 (c ==
'.' && _i + 1 < _src.size() && is_digit(_src[_i + 1])))
216 tokens.push_back(read_number());
217 else if (std::isalpha(
static_cast<unsigned char>(c)) || c ==
'_')
218 tokens.push_back(read_ident());
220 tokens.push_back(read_op());
226 static bool is_digit(
char c) {
227 return std::isdigit(
static_cast<unsigned char>(c)) != 0;
231 while (_i < _src.size() &&
232 std::isspace(
static_cast<unsigned char>(_src[_i])))
236 Token read_number() {
237 std::size_t start = _i;
238 bool is_float =
false;
239 while (_i < _src.size() && is_digit(_src[_i]))
241 if (_i < _src.size() && _src[_i] ==
'.') {
244 while (_i < _src.size() && is_digit(_src[_i]))
247 if (_i < _src.size() && (_src[_i] ==
'e' || _src[_i] ==
'E')) {
250 if (_i < _src.size() && (_src[_i] ==
'+' || _src[_i] ==
'-'))
252 while (_i < _src.size() && is_digit(_src[_i]))
255 std::string text = _src.substr(start, _i - start);
261 v =
static_cast<std::int64_t
>(std::stoll(text));
262 }
catch (
const std::exception &) {
266 return {TokType::Number, text, v, start};
270 std::size_t start = _i;
273 (std::isalnum(
static_cast<unsigned char>(_src[_i])) || _src[_i] ==
'_'))
275 return {TokType::Ident, _src.substr(start, _i - start), Value{}, start};
279 std::size_t start = _i;
281 auto two = [&](
char a,
char b) {
282 return _i + 1 < _src.size() && _src[_i] == a && _src[_i + 1] == b;
285 return _i += 2, Token{TokType::Le,
"<=", {}, start};
287 return _i += 2, Token{TokType::Ge,
">=", {}, start};
289 return _i += 2, Token{TokType::EqEq,
"==", {}, start};
291 return _i += 2, Token{TokType::NotEq,
"!=", {}, start};
293 return _i += 2, Token{TokType::And,
"&&", {}, start};
295 return _i += 2, Token{TokType::Or,
"||", {}, start};
298 return ++_i, Token{TokType::Plus,
"+", {}, start};
300 return ++_i, Token{TokType::Minus,
"-", {}, start};
302 return ++_i, Token{TokType::Star,
"*", {}, start};
304 return ++_i, Token{TokType::Slash,
"/", {}, start};
306 return ++_i, Token{TokType::Caret,
"^", {}, start};
308 return ++_i, Token{TokType::LParen,
"(", {}, start};
310 return ++_i, Token{TokType::RParen,
")", {}, start};
312 return ++_i, Token{TokType::Comma,
",", {}, start};
314 return ++_i, Token{TokType::Colon,
":", {}, start};
316 return ++_i, Token{TokType::Lt,
"<", {}, start};
318 return ++_i, Token{TokType::Gt,
">", {}, start};
320 return ++_i, Token{TokType::Not,
"!", {}, start};
322 throw ExpressionistException(
"Unexpected character '" +
323 std::string(1, c) +
"' at position " +
324 std::to_string(start));
351 TokType op = TokType::End;
352 std::vector<std::shared_ptr<Node>> children;
355using NodePtr = std::shared_ptr<Node>;
359 Parser(std::vector<Token> tokens, std::string src)
360 : _tokens(std::move(tokens)), _src(std::move(src)) {}
363 NodePtr n = parse_range();
364 if (!check(TokType::End))
365 throw error(
"unexpected trailing token");
370 const Token &peek()
const {
return _tokens[_pos]; }
371 const Token &advance() {
return _tokens[_pos++]; }
372 bool check(TokType t)
const {
return peek().type == t; }
373 bool match(TokType t) {
381 ExpressionistException error(
const std::string &why)
const {
382 return ExpressionistException(
"Parse error in \"" + _src +
"\": " + why +
383 " ('" + peek().text +
"' at position " +
384 std::to_string(peek().pos) +
")");
387 static NodePtr make_binary(TokType op, NodePtr l, NodePtr r) {
388 auto n = std::make_shared<Node>();
389 n->kind = NodeKind::Binary;
391 n->children = {std::move(l), std::move(r)};
398 NodePtr parse_range() {
399 NodePtr first = parse_or();
400 if (!check(TokType::Colon))
402 auto node = std::make_shared<Node>();
403 node->kind = NodeKind::Range;
404 node->children.push_back(first);
405 while (match(TokType::Colon))
406 node->children.push_back(parse_or());
407 if (node->children.size() > 3)
408 throw error(
"a range has at most three parts (start:stop:step)");
413 NodePtr n = parse_and();
414 while (check(TokType::Or)) {
416 n = make_binary(TokType::Or, n, parse_and());
421 NodePtr parse_and() {
422 NodePtr n = parse_equality();
423 while (check(TokType::And)) {
425 n = make_binary(TokType::And, n, parse_equality());
430 NodePtr parse_equality() {
431 NodePtr n = parse_comparison();
432 while (check(TokType::EqEq) || check(TokType::NotEq)) {
433 TokType op = advance().type;
434 n = make_binary(op, n, parse_comparison());
439 NodePtr parse_comparison() {
440 NodePtr n = parse_additive();
441 while (check(TokType::Lt) || check(TokType::Le) || check(TokType::Gt) ||
442 check(TokType::Ge)) {
443 TokType op = advance().type;
444 n = make_binary(op, n, parse_additive());
449 NodePtr parse_additive() {
450 NodePtr n = parse_multiplicative();
451 while (check(TokType::Plus) || check(TokType::Minus)) {
452 TokType op = advance().type;
453 n = make_binary(op, n, parse_multiplicative());
458 NodePtr parse_multiplicative() {
459 NodePtr n = parse_unary();
460 while (check(TokType::Star) || check(TokType::Slash)) {
461 TokType op = advance().type;
462 n = make_binary(op, n, parse_unary());
467 NodePtr parse_unary() {
468 if (check(TokType::Minus) || check(TokType::Not)) {
469 TokType op = advance().type;
470 auto n = std::make_shared<Node>();
471 n->kind = NodeKind::Unary;
473 n->children = {parse_unary()};
476 return parse_power();
481 NodePtr parse_power() {
482 NodePtr base = parse_primary();
483 if (check(TokType::Caret)) {
485 return make_binary(TokType::Caret, base, parse_unary());
490 NodePtr parse_primary() {
491 const Token &t = peek();
492 if (t.type == TokType::Number) {
494 auto n = std::make_shared<Node>();
495 n->kind = is_int(t.value) ? NodeKind::IntLit : NodeKind::FloatLit;
499 if (t.type == TokType::LParen) {
501 NodePtr n = parse_or();
502 if (!match(TokType::RParen))
503 throw error(
"expected ')'");
506 if (t.type == TokType::Ident) {
508 if (t.text ==
"true" || t.text ==
"false") {
509 auto n = std::make_shared<Node>();
510 n->kind = NodeKind::BoolLit;
511 n->value = (t.text ==
"true");
514 if (check(TokType::LParen)) {
516 auto n = std::make_shared<Node>();
517 n->kind = NodeKind::Call;
519 if (!check(TokType::RParen)) {
520 n->children.push_back(parse_or());
521 while (match(TokType::Comma))
522 n->children.push_back(parse_or());
524 if (!match(TokType::RParen))
525 throw error(
"expected ')' in call to '" + t.text +
"'");
528 auto n = std::make_shared<Node>();
529 n->kind = NodeKind::Ident;
533 throw error(
"unexpected token");
536 std::vector<Token> _tokens;
538 std::size_t _pos = 0;
543inline void collect_idents(
const NodePtr &n, std::vector<std::string> &out) {
546 if (n->kind == NodeKind::Ident)
547 out.push_back(n->name);
548 for (
const auto &c : n->children)
549 collect_idents(c, out);
552using Resolver = std::function<std::optional<Value>(
const std::string &)>;
554inline Value num_binary(
const Value &l,
const Value &r, TokType op) {
555 if (op == TokType::Slash)
556 return to_double(l) / to_double(r);
557 if (op == TokType::Caret) {
558 if (is_int(l) && is_int(r) && std::get<std::int64_t>(r) >= 0) {
559 std::int64_t base = std::get<std::int64_t>(l);
560 std::int64_t exp = std::get<std::int64_t>(r);
561 std::int64_t result = 1;
562 for (std::int64_t k = 0; k < exp; ++k)
566 return std::pow(to_double(l), to_double(r));
568 if (is_int(l) && is_int(r)) {
569 std::int64_t a = std::get<std::int64_t>(l);
570 std::int64_t b = std::get<std::int64_t>(r);
582 double a = to_double(l);
583 double b = to_double(r);
596inline Value eval_node(
const NodePtr &n,
const Symbols &sym,
597 const Resolver &resolve) {
599 case NodeKind::IntLit:
600 case NodeKind::FloatLit:
601 case NodeKind::BoolLit:
603 case NodeKind::Ident: {
604 if (
auto v = resolve(n->name))
606 auto it = sym.constants.find(n->name);
607 if (it != sym.constants.end())
609 throw ExpressionistException(
"Undefined variable or constant: '" + n->name +
612 case NodeKind::Unary: {
613 if (n->op == TokType::Not)
614 return !to_bool(eval_node(n->children[0], sym, resolve));
615 Value c = eval_node(n->children[0], sym, resolve);
617 return -std::get<std::int64_t>(c);
618 return -to_double(c);
620 case NodeKind::Binary: {
621 if (n->op == TokType::And) {
622 if (!to_bool(eval_node(n->children[0], sym, resolve)))
624 return to_bool(eval_node(n->children[1], sym, resolve));
626 if (n->op == TokType::Or) {
627 if (to_bool(eval_node(n->children[0], sym, resolve)))
629 return to_bool(eval_node(n->children[1], sym, resolve));
631 Value l = eval_node(n->children[0], sym, resolve);
632 Value r = eval_node(n->children[1], sym, resolve);
639 return num_binary(l, r, n->op);
641 return to_double(l) < to_double(r);
643 return to_double(l) <= to_double(r);
645 return to_double(l) > to_double(r);
647 return to_double(l) >= to_double(r);
649 if (is_bool_value(l) && is_bool_value(r))
650 return std::get<bool>(l) == std::get<bool>(r);
651 return to_double(l) == to_double(r);
653 if (is_bool_value(l) && is_bool_value(r))
654 return std::get<bool>(l) != std::get<bool>(r);
655 return to_double(l) != to_double(r);
657 throw ExpressionistException(
"Internal error: bad binary operator");
660 case NodeKind::Range: {
661 if (n->children.size() == 2) {
662 Value a = eval_node(n->children[0], sym, resolve);
663 Value b = eval_node(n->children[1], sym, resolve);
664 if (!is_int(a) || !is_int(b))
665 throw ExpressionistException(
666 "Binary range 'start:stop' requires integer bounds");
667 std::int64_t start = std::get<std::int64_t>(a);
668 std::int64_t stop = std::get<std::int64_t>(b);
669 json arr = json::array();
670 for (std::int64_t v = start; v <= stop; ++v)
672 return Value(std::in_place_type<json>, std::move(arr));
674 if (n->children.size() == 3) {
675 double start = to_double(eval_node(n->children[0], sym, resolve));
676 double stop = to_double(eval_node(n->children[1], sym, resolve));
677 double step = to_double(eval_node(n->children[2], sym, resolve));
679 throw ExpressionistException(
"Range step must be non-zero");
680 json arr = json::array();
681 double span = stop - start;
686 if (span == 0.0 || (span > 0.0) == (step > 0.0)) {
688 static_cast<std::int64_t
>(std::floor(span / step + 1e-9));
689 for (std::int64_t i = 0; i <= count; ++i)
690 arr.push_back(start +
static_cast<double>(i) * step);
692 return Value(std::in_place_type<json>, std::move(arr));
694 throw ExpressionistException(
695 "A range must have two (start:stop) or three (start:stop:step) parts");
697 case NodeKind::Call: {
698 const std::string &fn = n->name;
699 auto u = sym.unary.find(fn);
700 if (u != sym.unary.end()) {
701 if (n->children.size() != 1)
702 throw ExpressionistException(
"Function '" + fn +
703 "' expects 1 argument, got " +
704 std::to_string(n->children.size()));
705 return u->second(to_double(eval_node(n->children[0], sym, resolve)));
707 auto b = sym.binary.find(fn);
708 if (b != sym.binary.end()) {
709 if (n->children.size() != 2)
710 throw ExpressionistException(
"Function '" + fn +
711 "' expects 2 arguments, got " +
712 std::to_string(n->children.size()));
713 double x = to_double(eval_node(n->children[0], sym, resolve));
714 double y = to_double(eval_node(n->children[1], sym, resolve));
715 return b->second(x, y);
717 throw ExpressionistException(
"Unknown function: '" + fn +
"'");
720 throw ExpressionistException(
"Internal error: bad node");
729 Evaluator(
const Symbols &sym, std::string tag, std::string disableKey,
731 : _sym(sym), _tag(std::move(tag)), _disableKey(std::move(disableKey)),
734 void run(json &root) {
737 walk(root,
nullptr,
"");
738 if (_method == EvalMethod::GRAPH)
742 for (
const Cell &c : _cells)
744 *c.slot = to_json(c.result);
749 Scope *parent =
nullptr;
750 std::map<std::string, std::size_t> keys;
754 json *slot =
nullptr;
755 Scope *scope =
nullptr;
758 bool is_expr =
false;
765 bool starts_with_tag(
const std::string &s)
const {
766 return !_tag.empty() && s.rfind(_tag, 0) == 0;
773 bool is_disabled(
const json &value)
const {
774 if (_disableKey.empty())
776 auto it = value.find(_disableKey);
777 return it != value.end() && *it ==
false;
780 std::size_t add_cell(json &value, Scope *scope,
const std::string &path) {
781 std::size_t idx = _cells.size();
782 _cells.emplace_back();
783 Cell &cell = _cells.back();
787 cell.name = last_component(path);
788 if (value.is_string()) {
789 const std::string &s = value.get_ref<
const std::string &>();
790 if (starts_with_tag(s)) {
792 cell.source = s.substr(_tag.size());
794 Tokenizer tok(cell.source);
795 Parser parser(tok.tokenize(), cell.source);
796 cell.ast = parser.parse();
797 }
catch (
const ExpressionistException &e) {
798 throw ExpressionistException(cell_context(cell) + e.what());
805 void walk(json &value, Scope *enclosing,
const std::string &path) {
806 if (value.is_object()) {
807 if (is_disabled(value))
809 _scopes.emplace_back();
810 Scope *scope = &_scopes.back();
811 scope->parent = enclosing;
814 for (
auto it = value.begin(); it != value.end(); ++it)
815 scope->keys[it.key()] =
816 add_cell(it.value(), scope, join(path, it.key()));
818 for (
auto it = value.begin(); it != value.end(); ++it)
819 if (it.value().is_object() || it.value().is_array())
820 walk(it.value(), scope, join(path, it.key()));
821 }
else if (value.is_array()) {
823 for (std::size_t i = 0; i < value.size(); ++i) {
824 std::string p = path +
"/" + std::to_string(i);
826 if (el.is_object() || el.is_array())
827 walk(el, enclosing, p);
829 add_cell(el, enclosing, p);
835 std::size_t resolve_name(
const std::string &name, Scope *scope,
837 for (Scope *s = scope; s !=
nullptr; s = s->parent) {
838 auto it = s->keys.find(name);
839 if (it != s->keys.end()) {
848 Value literal_value(
const Cell &c)
const {
849 const json &v = *c.slot;
850 if (v.is_number_integer())
851 return static_cast<std::int64_t
>(v.get<std::int64_t>());
852 if (v.is_number_float())
853 return v.get<
double>();
855 return v.get<
bool>();
856 throw ExpressionistException(
"variable '" + c.name +
"' is not numeric");
859 std::string cell_context(
const Cell &c)
const {
860 return "In '" + c.path +
"' (\"" + _tag + c.source +
"\"): ";
866 rethrow_with_context(
const Cell &c,
const ExpressionistException &e)
const {
867 std::string msg = e.what();
868 if (msg.rfind(
"In '", 0) == 0)
870 throw ExpressionistException(cell_context(c) + msg);
875 void eval_recursive() {
876 std::vector<std::size_t> stack;
877 for (std::size_t i = 0; i < _cells.size(); ++i)
878 if (_cells[i].is_expr)
882 Value eval_cell(std::size_t idx, std::vector<std::size_t> &stack) {
883 Cell &cell = _cells[idx];
885 return literal_value(cell);
889 throw ExpressionistException(
"Circular dependency: " +
890 cycle_path(stack, idx));
892 stack.push_back(idx);
893 Scope *scope = cell.scope;
894 Resolver resolver = [&](
const std::string &name) -> std::optional<Value> {
896 std::size_t tgt = resolve_name(name, scope, found);
899 return eval_cell(tgt, stack);
903 v = eval_node(cell.ast, _sym, resolver);
904 }
catch (
const ExpressionistException &e) {
905 rethrow_with_context(cell, e);
913 std::string cycle_path(
const std::vector<std::size_t> &stack,
914 std::size_t idx)
const {
915 std::size_t start = 0;
916 for (std::size_t k = 0; k < stack.size(); ++k)
917 if (stack[k] == idx) {
922 for (std::size_t k = start; k < stack.size(); ++k)
923 m += _cells[stack[k]].name +
" -> ";
924 m += _cells[idx].name;
931 std::size_t n = _cells.size();
932 std::vector<std::vector<std::size_t>> dependents(n);
933 std::vector<int> indeg(n, 0);
934 std::vector<std::size_t> expr_cells;
935 for (std::size_t i = 0; i < n; ++i) {
936 if (!_cells[i].is_expr)
938 expr_cells.push_back(i);
939 std::vector<std::string> idents;
940 collect_idents(_cells[i].ast, idents);
941 std::unordered_set<std::size_t> seen;
942 for (
const std::string &name : idents) {
944 std::size_t tgt = resolve_name(name, _cells[i].scope, found);
945 if (found && _cells[tgt].is_expr && seen.insert(tgt).second) {
946 dependents[tgt].push_back(i);
951 std::queue<std::size_t> q;
952 for (std::size_t i : expr_cells)
955 std::size_t processed = 0;
957 std::size_t i = q.front();
961 for (std::size_t d : dependents[i])
965 if (processed != expr_cells.size()) {
967 for (std::size_t i : expr_cells)
968 if (_cells[i].state != 2)
969 names += _cells[i].name +
" ";
970 throw ExpressionistException(
"Circular dependency detected among: " +
975 void eval_expr_graph(std::size_t idx) {
976 Cell &cell = _cells[idx];
977 Scope *scope = cell.scope;
978 Resolver resolver = [&](
const std::string &name) -> std::optional<Value> {
980 std::size_t tgt = resolve_name(name, scope, found);
983 const Cell &t = _cells[tgt];
986 return literal_value(t);
989 cell.result = eval_node(cell.ast, _sym, resolver);
990 }
catch (
const ExpressionistException &e) {
991 rethrow_with_context(cell, e);
996 static std::string join(
const std::string &path,
const std::string &key) {
997 return path +
"/" + key;
1000 static std::string last_component(
const std::string &path) {
1001 std::size_t p = path.find_last_of(
'/');
1002 return p == std::string::npos ? path : path.substr(p + 1);
1005 const Symbols &_sym;
1007 std::string _disableKey;
1009 std::deque<Scope> _scopes;
1010 std::vector<Cell> _cells;
1051 : _object(std::move(o)), _evalMethod(method),
1052 _symbols(detail::default_symbols()) {}
1056 : _evalMethod(method), _symbols(detail::default_symbols()) {
1058 _object = json::parse(s);
1059 }
catch (
const std::exception &e) {
1061 std::string(e.what()));
1073 : _evalMethod(method), _symbols(detail::default_symbols()) {}
1082 detail::Evaluator ev(_symbols, _tag, _disableKey, _evalMethod);
1097 detail::Evaluator ev(_symbols, _tag, _disableKey, _evalMethod);
1108 json copy = _object;
1109 detail::Evaluator ev(_symbols, _tag, _disableKey, _evalMethod);
1118 detail::Evaluator ev(_symbols, _tag, _disableKey, _evalMethod);
1134 void setTag(
const std::string &tag) { _tag = tag; }
1136 std::string
tag()
const {
return _tag; }
1150 const json &
object()
const {
return _object; }
1155 _symbols.constants[name] = value;
1162 _symbols.unary[name] = std::move(fn);
1170 _symbols.binary[name] = std::move(fn);
1174 json _object = json::object();
1175 EvalMethod _evalMethod = EvalMethod::RECURSIVE;
1176 std::string _tag =
"$";
1177 std::string _disableKey =
"expressionist";
1178 detail::Symbols _symbols;
Definition expressionist.hpp:52
Definition expressionist.hpp:1047
void setTag(const std::string &tag)
Definition expressionist.hpp:1134
void addUnaryFunction(const std::string &name, detail::UnaryFn fn)
Definition expressionist.hpp:1161
EvalMethod getEvalMethod() const
The currently selected resolution strategy.
Definition expressionist.hpp:1127
Expressionist(json o, EvalMethod method=EvalMethod::RECURSIVE)
Construct from a JSON object, to be mutated/copied by evaluate()/produce().
Definition expressionist.hpp:1050
void evaluate(json &object) const
Definition expressionist.hpp:1096
std::string tag() const
The currently configured expression tag.
Definition expressionist.hpp:1136
Expressionist(EvalMethod method=EvalMethod::RECURSIVE)
Definition expressionist.hpp:1072
Expressionist(std::string s, EvalMethod method=EvalMethod::RECURSIVE)
Definition expressionist.hpp:1055
json produce(json object) const
Definition expressionist.hpp:1116
std::string disableKey() const
The currently configured disable key.
Definition expressionist.hpp:1147
void setEvalMethod(EvalMethod method)
Definition expressionist.hpp:1125
void addConstant(const std::string &name, double value)
Definition expressionist.hpp:1154
const json & object() const
Access the (possibly evaluated) stored object.
Definition expressionist.hpp:1150
json produce() const
Definition expressionist.hpp:1107
Expressionist(const char *s, EvalMethod method=EvalMethod::RECURSIVE)
Same as the std::string overload; disambiguates string-literal calls.
Definition expressionist.hpp:1068
void evaluate()
Definition expressionist.hpp:1081
void setDisableKey(const std::string &key)
Definition expressionist.hpp:1145
void addBinaryFunction(const std::string &name, detail::BinaryFn fn)
Definition expressionist.hpp:1169