SXEval 1.0.3
A generic s-expression interpreter library.
Loading...
Searching...
No Matches
SXEval.hpp
Go to the documentation of this file.
1#ifndef SXEVAL_SXEVAL_HPP
2#define SXEVAL_SXEVAL_HPP
3
5#include "sxeval/IOperand.hpp"
7#include "sxeval/Value.hpp"
8#include "sxeval/Variable.hpp"
10#include "sxeval/utils.hpp"
11#include <vector>
12#include <memory>
13#include <string>
14#include <functional>
15#include <iostream>
16#include <cstring>
17#include <sstream>
18
19
20/* DEFINITIONS */
21
22namespace sxeval {
23
24template <typename T>
25using resolveVariable_t = std::function<T&(const std::string&)>;
26template <typename T>
27using resolveEncapsulated_t = std::function<
28 std::function<T(void)>(const std::string&)>;
29
47template <typename T>
48class SXEval {
49public:
53 inline SXEval() {}
54
61 template <typename OP>
62 inline void registerOperation() { _operationsFactory.template add<OP>(); }
63
80 void build(const std::string& expression,
84
109
118 T execute() const;
119
140 T interpret(const std::string& expression,
143 = resolveEncapsulated_t<T>()) const;
144
174
180 std::string toString() const;
181
182private:
183 struct Node {
184 std::unique_ptr<IInstruction<T>> instruct;
185 Node* parent;
186 std::vector<Node> subnodes;
187
188 #ifdef SXEVAL_DEBUG
189 int id = -1;
190 #endif /* SXEVAL_DEBUG */
191 };
192
193 static void _skipChars(const std::string& s, size_t* idx);
194 static std::string _getNextSymbol(const std::string& s, size_t* idx);
195 Node _build(size_t* idx, const resolveVariable_t<T>& resolveVariable,
197 static void _fillParents(Node& parent);
198 static void _buildTreeStr(std::ostream& oss, const Node& node, size_t depth
199 );
200 T _interpret(const std::string& exp, size_t* idx,
203
204 #ifdef SXEVAL_DEBUG
205 int nodeCount = 0;
206 #endif /* SXEVAL_DEBUG */
207
208 operations::OperationsFactory<T> _operationsFactory;
209 std::vector<AOperation<T>*> _operations;
210 std::vector<EncapsulatedVariable<T>*> _encapsulated;
211 Node _lastOperation;
212 std::string _expression;
213
214};
215
224template <typename T>
225inline std::ostream& operator<<(std::ostream& os, const SXEval<T>& obj) {
226 return os << obj.toString();
227}
228
229} /* namespace sxeval */
230
231
232/* IMPLEMENTATIONS */
233
234template <typename T>
235void sxeval::SXEval<T>::build(const std::string& expression,
236 const resolveVariable_t<T>& resolveVariable,
237 const resolveEncapsulated_t<T>& resolveEncapsulated)
238{
239 _operations.clear();
240 _encapsulated.clear();
241 _expression = expression;
242 size_t idx = 0;
243 _lastOperation = _build(&idx, resolveVariable, resolveEncapsulated);
244 _fillParents(_lastOperation);
245}
246
247template <typename T>
249 if (_operations.size() == 0) {
250 throw std::runtime_error("No operations found");
251 }
252 for (const auto& op : _encapsulated) {
253 op->retrieve();
254 }
255 for (const auto& op : _operations) {
256 op->execute();
257 }
258 return _operations.back()->getResult();
259}
260
261template <typename T>
262T sxeval::SXEval<T>::interpret(const std::string& expression,
263 const resolveVariable_t<T>& resolveVariable,
264 const resolveEncapsulated_t<T>& resolveEncapsulated) const
265{
266 size_t idx = 0;
267 return _interpret(expression, &idx, resolveVariable, resolveEncapsulated);
268}
269
270template <typename T>
271T sxeval::SXEval<T>::_interpret(const std::string& exp, size_t* idx,
272 const resolveVariable_t<T>& resolveVariable,
273 const resolveEncapsulated_t<T>& resolveEncapsulated) const
274{
275 _skipChars(exp, idx);
276
277 if (exp[*idx] == '(') {
278 /* ### OPERATION ### */
279 (*idx)++;
280 const auto symbol = _getNextSymbol(exp, idx);
281 std::vector<std::unique_ptr<IInstruction<T>>> args;
282 std::vector<IInstruction<T>*> pargs;
283 _skipChars(exp, idx);
284 while (exp[*idx] != ')') {
285 const auto val = _interpret(exp, idx, resolveVariable,
286 resolveEncapsulated);
287 args.push_back(std::unique_ptr<IInstruction<T>>(new Value<T>(val)));
288 pargs.push_back(args.back().get());
289 _skipChars(exp, idx);
290 }
291 (*idx)++;
292 return _operationsFactory.compute(symbol, pargs);
293
294 } else {
295 /* ### OPERANDS ### */
296 const auto symbol = _getNextSymbol(exp, idx);
297 T res;
298 try {
299 res = StringToType<T>(symbol);
300 } catch (...) {
301 /* as this is not castable, this may be a variable */
302 try {
303 res = resolveVariable(symbol);
304 } catch (...) {
305 /* as it has not been found, this is likely an
306 * encapsulated variable */
307 try {
308 const auto get = resolveEncapsulated(symbol);
309 res = get();
310 } catch (...) {
311 /* last chance, check if it is a true/false keyword
312 */
313 if (symbol == "true") {
314 res = 1;
315 } else if (symbol == "false") {
316 res = 0;
317 } else {
318 throw std::runtime_error("Unknown variable: " + symbol);
319 }
320 }
321 }
322 }
323 return res;
324 }
325}
326
327template <typename T>
328std::string sxeval::SXEval<T>::toString() const {
329 std::ostringstream oss;
330 _buildTreeStr(oss, _lastOperation, 0);
331 return oss.str();
332}
333
334template <typename T>
335void sxeval::SXEval<T>::_skipChars(const std::string& s, size_t* i) {
336 while (s[*i] == ' ' || s[*i] == '\t' || s[*i] == '\n' || s[*i] == '\r' )
337 {
338 (*i)++;
339 }
340}
341
342template <typename T>
343std::string sxeval::SXEval<T>::_getNextSymbol(const std::string& s, size_t* i) {
344 _skipChars(s, i);
345 std::string symbol;
346 char c = s[*i];
347 while (c != ' ' && c != '\t' && c != '\n' && c != '\r' && c != '('
348 && c != ')')
349 {
350 symbol += c;
351 (*i)++;
352 c = s[*i];
353 }
354 return symbol;
355}
356
357template <typename T>
359 const resolveVariable_t<T>& resolveVariable,
360 const resolveEncapsulated_t<T>& resolveEncapsulated)
361{
362 #ifdef SXEVAL_DEBUG
363 {
364 std::ostringstream oss;
365 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
366 << " in " << __func__ << "(): " << "parsing '"
367 << _expression << "'\n";
368 std::cerr << oss.str();
369 }
370 #endif /* SXEVAL_DEBUG */
371 _skipChars(_expression, idx);
372
373 if (_expression[*idx] == '(') {
374 /* ### OPERATION ### */
375 (*idx)++;
376 const auto symbol = _getNextSymbol(_expression, idx);
377 Node node;
378 node.parent = nullptr;
379 #ifdef SXEVAL_DEBUG
380 {
381 node.id = nodeCount++;
382 std::ostringstream oss;
383 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
384 << " in " << __func__ << "(): node " << node.id
385 << ": found operation " << symbol << "\n";
386 std::cerr << oss.str();
387 }
388 #endif /* SXEVAL_DEBUG */
389 _skipChars(_expression, idx);
390 while (_expression[*idx] != ')') {
391 node.subnodes.push_back(_build(idx, resolveVariable,
392 resolveEncapsulated));
393 #ifdef SXEVAL_DEBUG
394 {
395 std::ostringstream oss;
396 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
397 << " in " << __func__ << "(): -> node "
398 << node.subnodes.back().id << " added to node " << node.id
399 << "\n";
400 std::cerr << oss.str();
401 }
402 #endif /* SXEVAL_DEBUG */
403 _skipChars(_expression, idx);
404 }
405 (*idx)++;
406 std::vector<IInstruction<T>*> args;
407 for (auto& subnode : node.subnodes) {
408 args.push_back(subnode.instruct.get());
409 }
410 node.instruct = _operationsFactory.create(symbol, args);
411 _operations.push_back(
412 dynamic_cast<AOperation<T>*>(node.instruct.get()));
413 return std::move(node);
414
415 } else {
416 /* ### OPERANDS ### */
417 const auto symbol = _getNextSymbol(_expression, idx);
418 Node node;
419 try {
420 T val = StringToType<T>(symbol);
421 node = { std::unique_ptr<IInstruction<T>>(new Value<T>(val)),
422 nullptr, {} };
423 #ifdef SXEVAL_DEBUG
424 {
425 node.id = nodeCount++;
426 std::ostringstream oss;
427 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
428 << " in " << __func__ << "(): node " << node.id
429 << ": found value " << symbol << " (="
430 << node.instruct->getResult() << ")\n";
431 std::cerr << oss.str();
432 }
433 #endif /* SXEVAL_DEBUG */
434 } catch (...) {
435 /* as this is not castable, this may be a variable */
436 try {
437 T& var = resolveVariable(symbol);
438 node = {
439 std::unique_ptr<IInstruction<T>>(
440 new Variable<T>(var, symbol)
441 ), nullptr, {} };
442 #ifdef SXEVAL_DEBUG
443 {
444 node.id = nodeCount++;
445 std::ostringstream oss;
446 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
447 << " in " << __func__ << "(): node " << node.id
448 << ": found variable " << symbol << " (="
449 << node.instruct->getResult() << ")\n";
450 std::cerr << oss.str();
451 }
452 #endif /* SXEVAL_DEBUG */
453 } catch (...) {
454 /* as it has not been found, this is likely an
455 * encapsulated variable */
456 try {
457 auto get = resolveEncapsulated(symbol);
458 node = {
459 std::unique_ptr<IInstruction<T>>(
460 new EncapsulatedVariable<T>(get, symbol)
461 ), nullptr, {} };
462 _encapsulated.push_back(
463 dynamic_cast<EncapsulatedVariable<T>*>(
464 node.instruct.get()));
465 #ifdef SXEVAL_DEBUG
466 {
467 node.id = nodeCount++;
468 std::ostringstream oss;
469 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
470 << " in " << __func__ << "(): node " << node.id
471 << ": found encapsulated variable " << symbol
472 << " (=" << node.instruct->getResult() << ")\n";
473 std::cerr << oss.str();
474 }
475 #endif /* SXEVAL_DEBUG */
476 } catch (...) {
477 /* last chance, check if it is a true/false keyword
478 */
479 if (symbol == "true") {
480 node = {
481 std::unique_ptr<IInstruction<T>>(new Value<T>(1)),
482 nullptr, {} };
483 } else if (symbol == "false") {
484 node = {
485 std::unique_ptr<IInstruction<T>>(new Value<T>(0)),
486 nullptr, {} };
487 } else {
488 throw std::runtime_error("Unknown variable: " + symbol);
489 }
490 #ifdef SXEVAL_DEBUG
491 {
492 node.id = nodeCount++;
493 std::ostringstream oss;
494 oss << "[DEBUG] " << __FILE__ << ":" << __LINE__
495 << " in " << __func__ << "(): node " << node.id
496 << ": found boolean value " << symbol
497 << " (=" << node.instruct->getResult() << ")\n";
498 std::cerr << oss.str();
499 }
500 #endif /* SXEVAL_DEBUG */
501 }
502 }
503 }
504 return node;
505 }
506}
507
508template <typename T>
509void sxeval::SXEval<T>::_fillParents(Node& parent) {
510 for (auto& child : parent.subnodes) {
511 child.parent = &parent;
512 _fillParents(child);
513 }
514}
515
516template <typename T>
517void sxeval::SXEval<T>::_buildTreeStr(std::ostream& oss, const Node& node,
518 size_t depth)
519{
520 if (depth > 0) {
521 for (size_t i = 0; i < depth - 1; ++i) {
522 const Node* parent = &node;
523 const Node* child = nullptr;
524 for (size_t j = 0; j < depth - i; ++j) {
525 child = parent;
526 parent = parent->parent;
527 }
528 if (!(parent != nullptr && parent->subnodes.back().instruct.get()
529 == child->instruct.get()))
530 {
531 oss << "│ ";
532 } else {
533 oss << " ";
534 }
535 }
536 if (node.parent != nullptr &&
537 node.parent->subnodes.back().instruct.get() != node.instruct.get())
538 {
539 oss << "├─ ";
540 } else {
541 oss << "└─ ";
542 }
543 }
544 oss << node.instruct->toString() << std::endl;
545 for (const auto& child : node.subnodes) {
546 _buildTreeStr(oss, child, depth + 1);
547 }
548}
549
550#endif /* SXEVAL_SXEVAL_HPP */
The IInstruction class is an interface for any instructions.
Definition IInstruction.hpp:21
The SXEval class is used to evaluate s-expressions.
Definition SXEval.hpp:48
void registerOperation()
Register an operation.
Definition SXEval.hpp:62
T interpret(const std::string &expression, const resolveVariable_t< T > &resolveVariable=resolveVariable_t< T >(), const resolveEncapsulated_t< T > &resolveEncapsulated=resolveEncapsulated_t< T >()) const
Compute the result of an expression without building its tree.
Definition SXEval.hpp:262
T execute() const
Compute the result of the expression tree.
Definition SXEval.hpp:248
T interpret(const std::string &expression, const resolveEncapsulated_t< T > &resolveEncapsulated=resolveEncapsulated_t< T >(), const resolveVariable_t< T > &resolveVariable=resolveVariable_t< T >()) const
Compute the result of an expression without building its tree.
Definition SXEval.hpp:168
std::string toString() const
Convert the expression tree to a string representation.
Definition SXEval.hpp:328
SXEval()
Default constructor.
Definition SXEval.hpp:53
void build(const std::string &expression, const resolveEncapsulated_t< T > &resolveEncapsulated=resolveEncapsulated_t< T >(), const resolveVariable_t< T > &resolveVariable=resolveVariable_t< T >())
Build the expression tree from a s-expression.
Definition SXEval.hpp:104
void build(const std::string &expression, const resolveVariable_t< T > &resolveVariable=resolveVariable_t< T >(), const resolveEncapsulated_t< T > &resolveEncapsulated=resolveEncapsulated_t< T >())
Build the expression tree from a s-expression.
Definition SXEval.hpp:235
The Value class represents a constant value in the SXEval library.
Definition Value.hpp:20
Definition AOperation.hpp:8
std::function< T &(const std::string &)> resolveVariable_t
Definition SXEval.hpp:25
std::ostream & operator<<(std::ostream &os, const IInstruction< T > &obj)
Output stream operator for AInstruction.
Definition IInstruction.hpp:51
std::function< std::function< T(void)>(const std::string &)> resolveEncapsulated_t
Definition SXEval.hpp:28
std::enable_if< std::is_unsigned< T >::value, T >::type Absolute(const T &a)
Perform an absolute value operation on types T.
Definition utils.hpp:262