* Add formula support
* Fix a bug (cannot add new operations due to bug in FormulaEditor)
This commit is contained in:
449
src/ParseExp.cpp
Normal file
449
src/ParseExp.cpp
Normal 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
29
src/ParseExp.h
Normal 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);
|
@@ -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;
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dc.h>
|
||||
#include "GridAccount.h"
|
||||
|
||||
#include "../../ParseExp.h"
|
||||
class wxGridCellFormulaEditor : public wxGridCellTextEditor
|
||||
{
|
||||
public:
|
||||
|
Reference in New Issue
Block a user