* Add formula support

*  Fix a bug (cannot add new operations due to bug in FormulaEditor)
This commit is contained in:
2010-10-14 20:49:49 +02:00
parent effc630650
commit f33f6ab85a
8 changed files with 703 additions and 155 deletions

449
src/ParseExp.cpp Normal file
View File

@@ -0,0 +1,449 @@
/*
Copyright 2010 Grégory Soutadé
This file is part of KissCount.
KissCount is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
KissCount is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with KissCount. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdio.h>
#include <iostream>
#include "ParseExp.h"
/*
Algorithm
The idea of this parser is very simple : Create a binary tree with an operator, a left and a right operand.
Leafs are constants to be computed. This structure is more flexbile than a simple stack and very easy to parse
(for resultat computation).
A simple expression (3+4) will be represented by :
+
3 4
Another example (3+4+5) :
+
3 +
4 5
If an operator is less prioritary than his father we have to do a transformation.
Example with 3*4+5.
in first instance we may have this tree :
*
3 +
4 5
So we must reverse the tree into something like :
+
* 5
3 4
At each step the parser must read one operand and one operator,
fill a node with the operator and left operand and recurse on right
operand. Finally we stop with a constant (a leaf).
Operations in parenthesis must be computed separatly and filled into
the tree like constants.
*/
/* Less to most prioritary */
enum {CST, ADD, SUB, MUL, DIV, MOD, EXP};
enum {DOUBLE_POINTED=1, INVALID_CHAR, INVALID_PARENTHESIS, INVALID_OPERATION};
#ifdef DEBUG
#define P(x) x
#else
#define P(x)
#endif
double atof(char* s, int size)
{
int neg = 0;
double res = 0;
for (; size--; s++)
{
if (*s == '.')
{
neg = 10;
continue;
}
if (neg > 0)
{
res += (*s - '0') / neg;
neg *= 10;
}
else
{
res *= 10;
res += *s - '0';
}
}
return res;
}
void ParseExp(char** expr, struct parse_opt* root, bool needParenthesis)
{
char* temp;
bool pointed = false;
struct parse_opt* l, *r, *op, *op_tmp;
char type = -1;
bool negative = false, number = false;
l = r = op = op_tmp = NULL;
if (!**expr) return;
for (temp=*expr; **expr; (*expr)++)
{
if (**expr == '(')
{
op_tmp = new struct parse_opt;
op_tmp->type = CST;
op_tmp->root = root;
op_tmp->l = op_tmp->r = NULL;
(*expr)++;
ParseExp(expr, op_tmp, true);
l = op_tmp;
root->l = l;
continue;
}
if (**expr == ')')
{
if (!needParenthesis)
throw INVALID_PARENTHESIS;
if ((*expr-temp) == 0)
throw INVALID_OPERATION;
break;
}
if (**expr >= '0' && **expr <= '9')
continue;
if (**expr == '.')
{
if (!pointed)
pointed = true;
else
throw DOUBLE_POINTED;
continue;
}
if (type != -1)
throw INVALID_OPERATION;
switch(**expr)
{
case '+':
type = ADD;
break;
case '-':
if (temp == *expr)
{
if (negative)
throw INVALID_OPERATION;
temp++;
negative = true;
continue;
}
type = SUB;
break;
case '/':
type = DIV;
break;
case '*':
type = MUL;
break;
// case '%':
// type = MOD;
// break;
default:
throw INVALID_CHAR;
}
number= true;
if (!l)
{
if ((*expr-temp) == 0)
throw INVALID_OPERATION;
l = new struct parse_opt;
l->type = CST;
l->root = root;
l->l = NULL; l->r = NULL;
l->value = atof(temp, *expr-temp);
if (negative)
l->value *= -1.0;
root->l = l;
}
// Here [temp..expr] must be left operand + operator
if (root->root && root->root->type >= type)
{
// Reverse tree
op = new parse_opt;
op->root = root->root;
op->type = root->root->type;
op->r = l;
op->l = root->root->l;
op->l->root = op;
root->root->l = op;
root->root->type = type;
root->type = CST;
root->value = 0.0;
root->l = NULL ; root->r = NULL;
(*expr)++;
ParseExp(expr, root, needParenthesis);
break;
temp=*expr;
}
else
{
// Recurse on right operand
root->type = type;
// atof
r = new struct parse_opt;
r->type = CST;
r->root = root;
r->l = NULL; r->r = NULL;
r->value = 0.0;
root->r = r;
(*expr)++;
ParseExp(expr, r, needParenthesis);
break;
temp=*expr;
}
}
if (needParenthesis && **expr != ')')
throw INVALID_PARENTHESIS;
if (!number)
{
if ((*expr-temp) == 0)
throw INVALID_OPERATION;
if (op_tmp)
*root = *op_tmp;
else
{
root->type = CST;
root->l = NULL; root->r = NULL;
root->value = atof(temp, *expr-temp);
if (negative)
root->value *= -1.0;
}
}
return ;
}
double EvaluateExpr(struct parse_opt* root, bool del)
{
double l, r;
char type;
type = root->type;
if (root->type != CST)
{
l = EvaluateExpr(root->l, del);
r = EvaluateExpr(root->r, del);
}
else
l = root->value;
if (del)
{
if (root->l) delete root->l;
if (root->r) delete root->r;
}
switch (type)
{
case ADD:
P(std::cout << l << " + " << r << std::endl);
return l+r;
case SUB:
P(std::cout << l << " - " << r << std::endl);
return l+r;
case MUL:
P(std::cout << l << " * " << r << std::endl);
return l*r;
case DIV:
P(std::cout << l << " / " << r << std::endl);
return l/r;
// case MOD:
// return EvaluateExpr(root->l) % EvaluateExpr(root->r);
case EXP:
P(std::cout << l << " ^ " << r << std::endl);
return l*r;
case CST:
return l;
default:
return 0.0;
}
}
#ifdef DEBUG
int main()
{
char* e1 = new char[100] ;
char* e = e1;
struct parse_opt root, *r;
try
{
memset(&root, 0, sizeof(root));
r = &root;
strcpy(e1, "4*3");
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "4*3+5");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "3+3+3");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "3+3+3+3");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "4+3*5");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "-4+3*5/2");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "5+-4");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "5--4");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "4*(3+2)");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "(3+2)*4");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "4*(3+2)+5");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "5+(3+2)*4");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "3+(3+(3+3))");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "3+(3+(3+(3+3)))*5");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "5+(3/(6+8--5)*9)*4");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
e1 = e;
memset(e1, 0, 100);
strcpy(e1, "5+(3*(6+8--5)/9)*4");
memset(&root, 0, sizeof(root));
r = &root;
ParseExp(&e1, r, false);
std::cout << e << " = " << EvaluateExpr(&root, true) << "\n";
}
catch (int e)
{
std::cout << "Error " << e << std::endl;
}
return 0;
}
#endif

29
src/ParseExp.h Normal file
View File

@@ -0,0 +1,29 @@
/*
Copyright 2010 Grégory Soutadé
This file is part of KissCount.
KissCount is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
KissCount is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with KissCount. If not, see <http://www.gnu.org/licenses/>.
*/
struct parse_opt {
struct parse_opt* root;
char type;
double value;
struct parse_opt* l;
struct parse_opt* r;
};
void ParseExp(char** expr, struct parse_opt* root, bool needParenthesis);
double EvaluateExpr(struct parse_opt* root, bool del);

View File

@@ -47,19 +47,41 @@ bool wxGridCellFormulaEditor::EndEdit (int row, int col, wxGrid *grid/*, const w
{
wxString res = GetValue();
Operation op;
char* str, *str2;
struct parse_opt opt, *r;
bool ret;
res = res.Trim();
if (res.StartsWith(wxT("=")))
{
str = (char*) std::string(res.mb_str()).c_str();
str2 = new char[strlen(str)];
strcpy(str2, str+1);
r = &opt;
str = str2;
try {
ParseExp(&str2, r, false);
}
catch(...)
{
wxMessageBox(_("Invalid formula !"), _("Error"), wxICON_ERROR | wxOK);
delete str;
return false;
}
delete str;
_formula = res;
ret = wxGridCellTextEditor::EndEdit(row, col, grid);
grid->SetCellValue(row, col, wxString::Format(wxT("%.2lf"), EvaluateExpr(&opt, true)));
}
else
{
_formula = wxT("");
ret = wxGridCellTextEditor::EndEdit(row, col, grid);
if (_formula.Length())
grid->SetCellValue(row, col, wxT("0"));
ret = wxGridCellTextEditor::EndEdit(row, col, grid);
}
return ret;
}

View File

@@ -24,7 +24,7 @@
#include <wx/grid.h>
#include <wx/dc.h>
#include "GridAccount.h"
#include "../../ParseExp.h"
class wxGridCellFormulaEditor : public wxGridCellTextEditor
{
public: