Eval Expr propre

Description


Eval Expr

Ce projet montre comment fonctionne l'évaluation d'une expression arithmétique en gérant la précédence, les variables, et les fonctions.

C'est une question qui revient souvent sur les forums, et je n'ai pas trouvé un seul projet "propre" qui le traitait correctement (par propre j'entends: commenté, avec de la documentation et des tests unitaires, codé en petites fonctions plutôt qu'une seule monolithique avec des centaines de if dans tous les sens, et sans aucune allocation mémoire).

Projets

Ce projet est découpé en 4 sous projets, ayant chacun leur rôle:


Simple eval: Un évaluateur très basique, qui ne gère pas les espaces, et qui ne détaille pas les erreurs. Les variables et les fonctions ne sont pas gérées, et seul les nombres entiers sont prises en compte. Ce projet est juste là pour montrer qu'on peut créer un interpréteur arithmétique très simplement, en très peu de lignes, en se contentant de suivre une grammaire. C'est un peu la correction type à donner à un débutant qui cherche une solution propre à cet exercice classique.

Eval expr: Le projet principal. C'est un projet qui est plus évolué que le précédent. La class EvalExpr va non seulement gérée les espaces, mais aussi les variables et les fonctions configurées par un utilisateur. Une gestion naïves des erreurs est proposée. Voir l'exemple dans la prochaine section. Le binaire généré prend un seul argument qui est l'expression à interpreter.

Extend example: C'est un exemple montrant comment étendre la classe EvalExpr, afin d'y ajouter des fonctionnalités. Dans cet exemple, est montré comment géré l'association de méthodes plutôt que de fonctions.

Unit test: Comme son nom l'indique, c'est une classe contenant environ 150 tests unitaires, afin de s'assurer que le projet fonctionne correctement.

Example

#include <iostream>
#include "EvalExpr.hh"

double my_abs(double v) { return v >= 0 ? v : v * -1; }
double my_max(doubble a, double b) { return a > b ? a : b; }

int main()
{
  Interpreter::EvalExpr custom_expr;
  double value = 0;
  bool valid = false;

  valid = custom_expr.eval("1 + 1", value);
  std::cout << "Valid: " << valid << ", value: " << value << std::endl; // 2

  valid = custom_expr.eval("4 * ((1 + 2) / 2)", value);
  std::cout << "Valid: " << valid << ", value: " << value << std::endl; // 6

  custom_expr.setVariable("ma_var", 42);
  valid = custom_expr.eval("ma_var * 2", value);
  std::cout << "Valid: " << valid << ", value: " << value << std::endl; // 84

  custom_expr.setUnaryFunction("abs", &my_abs);
  valid = custom_expr.eval("abs(-5.2)", value);
  std::cout << "Valid: " << valid << ", value: " << value << std::endl; // 5.2

  custom_expr.setBinaryFunction("max", &my_max);
  valid = custom_expr.eval("max(5, 10)", value);
  std::cout << "Valid: " << valid << ", value: " << value << std::endl; // 10

  valid = custom_expr.eval("(5 * max(5, abs(-10)) + ma_var)", value);
  std::cout << "Valid: " << valid << ", value: " << value << std::endl; // 92

  return 0;
}

Compilation

Pour compiler, sous Linux, simplement taper: make.

Pour lancer les tests unitaires: make check.

Code coverage

Pour vérifier la couverture de tests, taper: make gcov.

Pour vérifier la couverture de tests avec une interface graphique, taper: make lcov.

Le code coverage doit normalement être à 100%.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.