I hope Daniel is not upset, but Ingo and I have written a new parser:
* it supports white spaces * it supports , and . for the decimal point * it gives parse errors and where the error occured * it supports more functions I also added evaluating an expression given on the command line, it doesn't show the UI then. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17774 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
1fa515a650
commit
19f75b98e6
@ -27,8 +27,8 @@
|
||||
|
||||
#include "CalcApplication.h"
|
||||
#include "CalcOptionsWindow.h"
|
||||
#include "ExpressionParser.h"
|
||||
#include "ExpressionTextView.h"
|
||||
#include "Parser.h"
|
||||
|
||||
|
||||
const uint8 K_COLOR_OFFSET = 32;
|
||||
@ -701,7 +701,6 @@ CalcView::Evaluate()
|
||||
const double EXP_SWITCH_LO = 1e-12;
|
||||
|
||||
BString expression = fExpressionTextView->Text();
|
||||
expression << "\n";
|
||||
|
||||
if (expression.Length() == 0) {
|
||||
beep();
|
||||
@ -719,22 +718,18 @@ CalcView::Evaluate()
|
||||
//printf("evaluate: %s\n", expression.String());
|
||||
|
||||
// evaluate expression
|
||||
Expression parser;
|
||||
|
||||
char* tmpstr = strdup(expression.String());
|
||||
char* start = tmpstr;
|
||||
char* end = start + expression.Length() - 1;
|
||||
double value = 0.0;
|
||||
|
||||
try {
|
||||
value = parser.Evaluate(start, end, true);
|
||||
} catch (const char* error) {
|
||||
fExpressionTextView->SetText(error);
|
||||
ExpressionParser parser;
|
||||
value = parser.Evaluate(expression.String());
|
||||
} catch (ParseException e) {
|
||||
BString error(e.message.String());
|
||||
error << " at " << (e.position + 1);
|
||||
fExpressionTextView->SetText(error.String());
|
||||
return;
|
||||
}
|
||||
|
||||
free(tmpstr);
|
||||
|
||||
//printf(" -> value: %f\n", value);
|
||||
|
||||
// beautify the expression
|
||||
|
@ -8,15 +8,37 @@
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "CalcApplication.h"
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
CalcApplication* app = new CalcApplication();
|
||||
|
||||
app->Run();
|
||||
delete app;
|
||||
if (argc == 1) {
|
||||
// run GUI
|
||||
CalcApplication* app = new CalcApplication();
|
||||
|
||||
app->Run();
|
||||
delete app;
|
||||
} else {
|
||||
// evaluate expression from command line
|
||||
BString expression;
|
||||
int32 i = 1;
|
||||
while (i < argc) {
|
||||
expression << argv[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
try {
|
||||
ExpressionParser parser;
|
||||
printf("%f\n", parser.Evaluate(expression.String()));
|
||||
} catch (ParseException e) {
|
||||
printf("%s at %ld\n", e.message.String(), e.position + 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,281 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2004 Daniel Wallner. All Rights Reserved.
|
||||
* Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Wallner <daniel.wallner@bredband.net>
|
||||
* Timothy Wayper <timmy@wunderbear.com>
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include <iostream.h>
|
||||
#include <fstream.h>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
|
||||
//#include <libxlds/xlds.h>
|
||||
//#include <libdsph/getopt.h>
|
||||
//#include <libdsph/const.h>
|
||||
|
||||
#include "CalcApplication.h"
|
||||
|
||||
using namespace std;
|
||||
//using namespace xlds;
|
||||
|
||||
void Usage()
|
||||
{
|
||||
cerr << "Expression calculator" << endl << endl;
|
||||
cerr << " usage: expcalc [expression] ... [options and arguments]" << endl << endl;
|
||||
cerr << " -h print this message" << endl;
|
||||
cerr << " -e even thousands exponent output" << endl;
|
||||
cerr << " -p SI prefix output" << endl;
|
||||
cerr << " -n newline after output" << endl << endl;
|
||||
cerr << " If no arguments are given, expcalc enters interactive mode." << endl;
|
||||
cerr << " You can combine short flags, so `-e -n' means the same as -en or -ne." << endl;
|
||||
}
|
||||
|
||||
int GetString(string &instring)
|
||||
{
|
||||
instring.assign("");
|
||||
|
||||
char cchar;
|
||||
|
||||
cchar = getc(stdin);
|
||||
|
||||
while (cchar != EOF && cchar != 10)
|
||||
{
|
||||
instring.append(1, cchar);
|
||||
cchar = getc(stdin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc == 1) {
|
||||
CalcApplication* app = new CalcApplication();
|
||||
|
||||
app->Run();
|
||||
delete app;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
char *expression = NULL;
|
||||
char outputMode = 0;
|
||||
bool newline = false;
|
||||
|
||||
while (doptind < argc)
|
||||
{
|
||||
if (argv[doptind][0] != '-' || strlen(argv[doptind]) <= 1)
|
||||
{
|
||||
// File argument
|
||||
if (expression)
|
||||
throw "Multiple expressions not supported";
|
||||
|
||||
expression = argv[doptind];
|
||||
|
||||
doptind++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Option
|
||||
int opt = dgetopt(argc, argv, ":hepn");
|
||||
if (opt != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'h':
|
||||
Usage();
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
outputMode = 1;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
outputMode = 2;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
newline = true;
|
||||
break;
|
||||
|
||||
case ':': // No argument
|
||||
cerr << "Option -" << (char)doptopt << " requires an argument" << endl;
|
||||
throw "";
|
||||
|
||||
case '?':
|
||||
default:
|
||||
cerr << "Unknown option -" << (char)doptopt << endl;
|
||||
throw "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string instring;
|
||||
string laststring;
|
||||
string stored;
|
||||
|
||||
Expression exp;
|
||||
|
||||
if (expression)
|
||||
{
|
||||
double temp = exp.Eval(expression, expression + strlen(expression));
|
||||
|
||||
if (!exp.Error())
|
||||
{
|
||||
cout << exp.dtostr(temp, 14, outputMode).c_str();
|
||||
if (newline)
|
||||
cout << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *i;
|
||||
cout << expression << endl;
|
||||
for (i = expression; i < exp.Error(); i++)
|
||||
cout << " ";
|
||||
cout << "^\nError in input\n\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Expression Calculator\n";
|
||||
cout << "Copyright (c) Daniel Wallner 2004.\n";
|
||||
cout << "Type exit to end program or ? for help.\n\n";
|
||||
|
||||
cout << "ec> ";
|
||||
|
||||
if (GetString(instring))
|
||||
return 0;
|
||||
|
||||
bool rec = false;
|
||||
|
||||
while (strcmp(instring.c_str(), "exit"))
|
||||
{
|
||||
if (!strcmp(instring.c_str(), "?"))
|
||||
{
|
||||
cout << "\nCommands l : repeat last calculation (used with ans)\n";
|
||||
cout << " sto : store last expression\n";
|
||||
cout << " rec : recall stored expression\n";
|
||||
cout << " d : set trigonometric functions to degrees\n";
|
||||
cout << " r : set trigonometric functions to radians (default)\n";
|
||||
cout << " e : toggle exponent output mode\n";
|
||||
cout << " p : set prefix output mode\n";
|
||||
cout << " exit : exit program\n";
|
||||
cout << "\nFunctions sin( : sinus\n";
|
||||
cout << " cos( : cosinus\n";
|
||||
cout << " tan( : tangens\n";
|
||||
cout << " asin( : arcsin\n";
|
||||
cout << " acos( : arccos\n";
|
||||
cout << " atan( : arctan\n";
|
||||
cout << " lg( : logarithm with base 10\n";
|
||||
cout << " ln( : logarithm with base e\n";
|
||||
cout << " sqrt( : sqare root\n";
|
||||
cout << " ans : last answer\n";
|
||||
cout << " ^,*,+,-,/ : as usual :-)\n";
|
||||
cout << "\nConstants e : the constant e\n";
|
||||
cout << " pi : the constant pi\n\n";
|
||||
|
||||
rec=1;
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "l"))
|
||||
{
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "sto"))
|
||||
{
|
||||
stored = laststring;
|
||||
cout << "\nLast expression stored!\n\n";
|
||||
rec = 1;
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "rec"))
|
||||
{
|
||||
instring = stored;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "d"))
|
||||
{
|
||||
exp.SetAglf(M_PI / 180.);
|
||||
rec = 1;
|
||||
cout << "\nDegree mode!\n\n";
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "r"))
|
||||
{
|
||||
exp.SetAglf(1.);
|
||||
rec = 1;
|
||||
cout << "\nRadian mode!\n\n";
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "e"))
|
||||
{
|
||||
outputMode = (outputMode + 1) % 2;
|
||||
cout << "\nExponent output mode" << outputMode << "!\n\n";
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!strcmp(instring.c_str(), "p"))
|
||||
{
|
||||
outputMode = 2;
|
||||
cout << "\nPrefix output mode!\n\n";
|
||||
instring = laststring;
|
||||
}
|
||||
|
||||
if (!instring.size())
|
||||
rec = 1;
|
||||
|
||||
if (rec == 0)
|
||||
{
|
||||
double temp = exp.Eval(instring.c_str(), instring.c_str() + instring.size());
|
||||
|
||||
if (!exp.Error())
|
||||
{
|
||||
cout << "\n" << instring << " = ";
|
||||
cout << exp.dtostr(temp, 14, outputMode).c_str();
|
||||
cout << "\n\n";
|
||||
exp.SetConstant("ans", temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *i;
|
||||
for (i = instring.c_str(); i < exp.Error(); i++)
|
||||
cout << " ";
|
||||
cout << " ";
|
||||
cout << "^\nError in input\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
cout << "ec> ";
|
||||
laststring = instring;
|
||||
if (GetString(instring))
|
||||
return 0;
|
||||
rec = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const char * str)
|
||||
{
|
||||
cerr << str << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
474
src/apps/deskcalc/ExpressionParser.cpp
Normal file
474
src/apps/deskcalc/ExpressionParser.cpp
Normal file
@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright 2006 Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
enum {
|
||||
TOKEN_IDENTIFIER = 'a',
|
||||
|
||||
TOKEN_CONSTANT = '0',
|
||||
|
||||
TOKEN_PLUS = '+',
|
||||
TOKEN_MINUS = '-',
|
||||
|
||||
TOKEN_STAR = '*',
|
||||
TOKEN_SLASH = '/',
|
||||
TOKEN_MODULO = '%',
|
||||
|
||||
TOKEN_POWER = '^',
|
||||
|
||||
TOKEN_OPENING_BRACKET = '(',
|
||||
TOKEN_CLOSING_BRACKET = ')',
|
||||
|
||||
TOKEN_AND = '&',
|
||||
TOKEN_OR = '|',
|
||||
TOKEN_NOT = '~',
|
||||
|
||||
TOKEN_NONE = ' ',
|
||||
TOKEN_END_OF_LINE = '\n',
|
||||
};
|
||||
|
||||
|
||||
struct Token {
|
||||
Token()
|
||||
: string(""),
|
||||
type(TOKEN_NONE),
|
||||
value(0.0),
|
||||
position(0)
|
||||
{
|
||||
}
|
||||
|
||||
Token(const Token& other)
|
||||
: string(other.string),
|
||||
type(other.type),
|
||||
value(other.value),
|
||||
position(other.position)
|
||||
{
|
||||
}
|
||||
|
||||
Token(const char* string, int32 length, int32 position, int32 type)
|
||||
: string(string, length),
|
||||
type(type),
|
||||
value(0.0),
|
||||
position(position)
|
||||
{
|
||||
}
|
||||
|
||||
Token& operator=(const Token& other)
|
||||
{
|
||||
string = other.string;
|
||||
type = other.type;
|
||||
value = other.value;
|
||||
position = other.position;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BString string;
|
||||
int32 type;
|
||||
double value;
|
||||
|
||||
int32 position;
|
||||
};
|
||||
|
||||
|
||||
class Tokenizer {
|
||||
public:
|
||||
Tokenizer()
|
||||
: fString(""),
|
||||
fCurrentChar(NULL),
|
||||
fCurrentToken(),
|
||||
fReuseToken(false)
|
||||
{
|
||||
}
|
||||
|
||||
void SetTo(const char* string)
|
||||
{
|
||||
fString = string;
|
||||
fCurrentChar = fString.String();
|
||||
fCurrentToken = Token();
|
||||
fReuseToken = false;
|
||||
}
|
||||
|
||||
const Token& NextToken()
|
||||
{
|
||||
if (fCurrentToken.type == TOKEN_END_OF_LINE)
|
||||
return fCurrentToken;
|
||||
|
||||
if (fReuseToken) {
|
||||
fReuseToken = false;
|
||||
//printf("next token (recycled): '%s'\n", fCurrentToken.string.String());
|
||||
return fCurrentToken;
|
||||
}
|
||||
|
||||
while (*fCurrentChar != 0 && isspace(*fCurrentChar))
|
||||
fCurrentChar++;
|
||||
|
||||
if (*fCurrentChar == 0)
|
||||
return fCurrentToken = Token("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
|
||||
|
||||
bool decimal = *fCurrentChar == '.' || *fCurrentChar == ',';
|
||||
|
||||
if (decimal || isdigit(*fCurrentChar)) {
|
||||
|
||||
BString temp;
|
||||
|
||||
const char* begin = fCurrentChar;
|
||||
while (*fCurrentChar != 0) {
|
||||
if (!isdigit(*fCurrentChar)) {
|
||||
if (!(*fCurrentChar == '.' || *fCurrentChar == ','
|
||||
|| *fCurrentChar == 'e' || *fCurrentChar == 'E'))
|
||||
break;
|
||||
}
|
||||
if (*fCurrentChar == ',')
|
||||
temp << '.';
|
||||
else
|
||||
temp << *fCurrentChar;
|
||||
fCurrentChar++;
|
||||
}
|
||||
int32 length = fCurrentChar - begin;
|
||||
temp << "&_";
|
||||
double value;
|
||||
char t[2];
|
||||
int32 matches = sscanf(temp.String(), "%lf&%s", &value, t);
|
||||
if (matches != 2)
|
||||
throw ParseException("error in constant", _CurrentPos() - length);
|
||||
|
||||
fCurrentToken = Token(begin, length, _CurrentPos() - length, TOKEN_CONSTANT);
|
||||
fCurrentToken.value = value;;
|
||||
|
||||
} else if (isalpha(*fCurrentChar)) {
|
||||
|
||||
const char* begin = fCurrentChar;
|
||||
while (*fCurrentChar != 0 && (isalpha(*fCurrentChar) || isdigit(*fCurrentChar)))
|
||||
fCurrentChar++;
|
||||
int32 length = fCurrentChar - begin;
|
||||
fCurrentToken = Token(begin, length, _CurrentPos() - length, TOKEN_IDENTIFIER);
|
||||
|
||||
} else {
|
||||
|
||||
switch (*fCurrentChar) {
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '/':
|
||||
|
||||
case '^':
|
||||
case '%':
|
||||
|
||||
case '(':
|
||||
case ')':
|
||||
|
||||
case '&':
|
||||
case '|':
|
||||
case '~':
|
||||
fCurrentToken = Token(fCurrentChar, 1, _CurrentPos(), *fCurrentChar);
|
||||
fCurrentChar++;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ParseException("unexpected character", _CurrentPos());
|
||||
}
|
||||
}
|
||||
|
||||
//printf("next token: '%s'\n", fCurrentToken.string.String());
|
||||
return fCurrentToken;
|
||||
}
|
||||
|
||||
void RewindToken()
|
||||
{
|
||||
fReuseToken = true;
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _CurrentPos() const
|
||||
{
|
||||
return fCurrentChar - fString.String();
|
||||
}
|
||||
|
||||
BString fString;
|
||||
const char* fCurrentChar;
|
||||
Token fCurrentToken;
|
||||
bool fReuseToken;
|
||||
};
|
||||
|
||||
|
||||
ExpressionParser::ExpressionParser()
|
||||
: fTokenizer(new Tokenizer())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ExpressionParser::~ExpressionParser()
|
||||
{
|
||||
delete fTokenizer;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::Evaluate(const char* expressionString)
|
||||
{
|
||||
fTokenizer->SetTo(expressionString);
|
||||
|
||||
double value = _ParseBinary();
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type != TOKEN_END_OF_LINE)
|
||||
throw ParseException("parse error", token.position);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::_ParseBinary()
|
||||
{
|
||||
double value = _ParseSum();
|
||||
|
||||
while (true) {
|
||||
Token token = fTokenizer->NextToken();
|
||||
switch (token.type) {
|
||||
case TOKEN_AND:
|
||||
value = (uint64)value & (uint64)_ParseSum();
|
||||
break;
|
||||
case TOKEN_OR:
|
||||
value = (uint64)value | (uint64)_ParseSum();
|
||||
break;
|
||||
|
||||
default:
|
||||
fTokenizer->RewindToken();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::_ParseSum()
|
||||
{
|
||||
// TODO: check isnan()...
|
||||
double value = _ParseProduct();
|
||||
|
||||
while (true) {
|
||||
Token token = fTokenizer->NextToken();
|
||||
switch (token.type) {
|
||||
case TOKEN_PLUS:
|
||||
value = value + _ParseProduct();
|
||||
break;
|
||||
case TOKEN_MINUS:
|
||||
value = value - _ParseProduct();
|
||||
break;
|
||||
|
||||
default:
|
||||
fTokenizer->RewindToken();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::_ParseProduct()
|
||||
{
|
||||
// TODO: check isnan()...
|
||||
double value = _ParsePower();
|
||||
|
||||
while (true) {
|
||||
Token token = fTokenizer->NextToken();
|
||||
switch (token.type) {
|
||||
case TOKEN_STAR:
|
||||
value = value * _ParsePower();
|
||||
break;
|
||||
case TOKEN_SLASH: {
|
||||
double rhs = _ParsePower();
|
||||
if (rhs == 0.0)
|
||||
throw ParseException("division by zero", token.position);
|
||||
value = value / rhs;
|
||||
break;
|
||||
}
|
||||
case TOKEN_MODULO: {
|
||||
double rhs = _ParsePower();
|
||||
if (rhs == 0.0)
|
||||
throw ParseException("modulo by zero", token.position);
|
||||
value = fmod(value, rhs);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
fTokenizer->RewindToken();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::_ParsePower()
|
||||
{
|
||||
double value = _ParseUnary();
|
||||
|
||||
while (true) {
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type != TOKEN_POWER) {
|
||||
fTokenizer->RewindToken();
|
||||
return value;
|
||||
}
|
||||
value = pow(value, _ParseUnary());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::_ParseUnary()
|
||||
{
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type == TOKEN_END_OF_LINE)
|
||||
throw ParseException("unexpected end of expression", token.position);
|
||||
|
||||
switch (token.type) {
|
||||
case TOKEN_PLUS:
|
||||
return _ParseUnary();
|
||||
case TOKEN_MINUS:
|
||||
return -_ParseUnary();
|
||||
case TOKEN_NOT:
|
||||
return ~(uint64)_ParseUnary();
|
||||
|
||||
case TOKEN_IDENTIFIER:
|
||||
return _ParseFunction(token);
|
||||
|
||||
default:
|
||||
fTokenizer->RewindToken();
|
||||
return _ParseAtom();
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
struct Function {
|
||||
const char* name;
|
||||
int argumentCount;
|
||||
void* function;
|
||||
double value;
|
||||
};
|
||||
|
||||
static const Function kFunctions[] = {
|
||||
{ "e", 0, NULL, M_E },
|
||||
{ "pi", 0, NULL, M_PI },
|
||||
|
||||
{ "abs", 1, &fabs },
|
||||
{ "acos", 1, &acos },
|
||||
{ "asin", 1, &asin },
|
||||
{ "atan", 1, &atan },
|
||||
{ "atan2", 2, &atan2 },
|
||||
{ "ceil", 1, &ceil },
|
||||
{ "cos", 1, &cos },
|
||||
{ "cosh", 1, &cosh },
|
||||
{ "exp", 1, &exp },
|
||||
{ "fabs", 1, &fabs },
|
||||
{ "floor", 1, &floor },
|
||||
{ "fmod", 2, &fmod },
|
||||
{ "log", 1, &log },
|
||||
{ "log10", 1, &log10 },
|
||||
{ "pow", 2, &pow },
|
||||
{ "sin", 1, &sin },
|
||||
{ "sinh", 1, &sinh },
|
||||
{ "sqrt", 1, &sqrt },
|
||||
{ "tan", 1, &tan },
|
||||
{ "tanh", 1, &tanh },
|
||||
{ "hypot", 2, &hypot },
|
||||
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
double
|
||||
ExpressionParser::_ParseFunction(const Token& token)
|
||||
{
|
||||
const Function* function = _FindFunction(token.string.String());
|
||||
if (!function)
|
||||
throw ParseException("unknown identifier", token.position);
|
||||
|
||||
if (function->argumentCount == 0)
|
||||
return function->value;
|
||||
|
||||
_EatToken(TOKEN_OPENING_BRACKET);
|
||||
|
||||
double values[function->argumentCount];
|
||||
for (int32 i = 0; i < function->argumentCount; i++) {
|
||||
values[i] = _ParseBinary();
|
||||
}
|
||||
|
||||
_EatToken(TOKEN_CLOSING_BRACKET);
|
||||
|
||||
// hard coded cases for different count of arguments
|
||||
switch (function->argumentCount) {
|
||||
case 1:
|
||||
return ((double (*)(double))function->function)(values[0]);
|
||||
case 2:
|
||||
return ((double (*)(double, double))function->function)(values[0],
|
||||
values[1]);
|
||||
case 3:
|
||||
return ((double (*)(double, double, double))function->function)(
|
||||
values[0], values[1], values[2]);
|
||||
|
||||
default:
|
||||
throw ParseException("unsupported function argument count",
|
||||
token.position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
ExpressionParser::_ParseAtom()
|
||||
{
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type == TOKEN_END_OF_LINE)
|
||||
throw ParseException("unexpected end of expression", token.position);
|
||||
|
||||
if (token.type == TOKEN_CONSTANT)
|
||||
return token.value;
|
||||
|
||||
fTokenizer->RewindToken();
|
||||
|
||||
_EatToken(TOKEN_OPENING_BRACKET);
|
||||
|
||||
double value = _ParseBinary();
|
||||
|
||||
_EatToken(TOKEN_CLOSING_BRACKET);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ExpressionParser::_EatToken(int32 type)
|
||||
{
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type != type) {
|
||||
BString temp("expected '");
|
||||
temp << (char)type << "' got '" << token.string << "'";
|
||||
throw ParseException(temp.String(), token.position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Function*
|
||||
ExpressionParser::_FindFunction(const char* name) const
|
||||
{
|
||||
for (int32 i = 0; kFunctions[i].name; i++) {
|
||||
if (strcasecmp(kFunctions[i].name, name) == 0)
|
||||
return &kFunctions[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
61
src/apps/deskcalc/ExpressionParser.h
Normal file
61
src/apps/deskcalc/ExpressionParser.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2006 Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#ifndef EXPRESSION_PARSER_H
|
||||
#define EXPRESSION_PARSER_H
|
||||
|
||||
#include <String.h>
|
||||
|
||||
class Tokenizer;
|
||||
|
||||
class ParseException {
|
||||
public:
|
||||
ParseException(const char* message, int32 position)
|
||||
: message(message),
|
||||
position(position)
|
||||
{
|
||||
}
|
||||
|
||||
ParseException(const ParseException& other)
|
||||
: message(other.message),
|
||||
position(other.position)
|
||||
{
|
||||
}
|
||||
|
||||
BString message;
|
||||
int32 position;
|
||||
};
|
||||
|
||||
struct Function;
|
||||
struct Token;
|
||||
|
||||
class ExpressionParser {
|
||||
public:
|
||||
ExpressionParser();
|
||||
~ExpressionParser();
|
||||
|
||||
double Evaluate(const char* expressionString);
|
||||
|
||||
private:
|
||||
|
||||
double _ParseBinary();
|
||||
double _ParseSum();
|
||||
double _ParseProduct();
|
||||
double _ParsePower();
|
||||
double _ParseUnary();
|
||||
double _ParseFunction(const Token& token);
|
||||
double _ParseAtom();
|
||||
|
||||
void _EatToken(int32 type);
|
||||
const Function* _FindFunction(const char* name) const;
|
||||
|
||||
Tokenizer* fTokenizer;
|
||||
};
|
||||
|
||||
#endif // EXPRESSION_PARSER_H
|
@ -10,9 +10,9 @@ Application DeskCalc :
|
||||
CalcView.cpp
|
||||
CalcWindow.cpp
|
||||
DeskCalc.cpp
|
||||
ExpressionParser.cpp
|
||||
ExpressionTextView.cpp
|
||||
InputTextView.cpp
|
||||
Parser.cpp
|
||||
: be $(TARGET_LIBSTDC++) media
|
||||
: DeskCalc.rdef
|
||||
;
|
||||
|
@ -1,919 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2004 Daniel Wallner. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Wallner <daniel.wallner@bredband.net>
|
||||
*/
|
||||
|
||||
#include "Parser.h"
|
||||
|
||||
#include <iostream.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define NOLLF 5e-16
|
||||
|
||||
using namespace std;
|
||||
|
||||
//LineParser::LineParser(istream *stream):
|
||||
// m_stream(stream),
|
||||
// m_currentLine(0)
|
||||
//{
|
||||
//}
|
||||
//
|
||||
//void LineParser::AddSeparator(const char c)
|
||||
//{
|
||||
// m_separators.push_back(c);
|
||||
//}
|
||||
//
|
||||
//size_t LineParser::SetToNext(bool toUpperCase)
|
||||
//{
|
||||
// m_line.erase(m_line.begin(), m_line.end());
|
||||
// m_unparsed.assign("");
|
||||
//
|
||||
// // Skip newlines
|
||||
// while (m_stream->peek() == 0xA || m_stream->peek() == 0xD)
|
||||
// m_stream->get();
|
||||
//
|
||||
// while (!m_stream->eof() && !m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD)
|
||||
// {
|
||||
// string param;
|
||||
//
|
||||
// // Skip whitespaces
|
||||
// while (m_stream->peek() == ' ' || m_stream->peek() == '\t')
|
||||
// m_unparsed.append(1, char(m_stream->get()));
|
||||
//// m_separators.push_back(c); add check for separators !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
// if (m_stream->peek() == '"')
|
||||
// {
|
||||
// // Skip first "
|
||||
// m_unparsed.append(1, char(m_stream->get()));
|
||||
//
|
||||
// int c;
|
||||
// // Read until "
|
||||
// while (!m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD &&
|
||||
// (c = m_stream->get()) != '"' && !m_stream->eof())
|
||||
// {
|
||||
// m_unparsed.append(1, char(c));
|
||||
// if (toUpperCase)
|
||||
// param.append(1, char(toupper(c)));
|
||||
// else
|
||||
// param.append(1, char(c));
|
||||
// }
|
||||
// if (c == '"')
|
||||
// m_unparsed.append(1, char(c));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// int c;
|
||||
// // Read until whitespace
|
||||
//// m_separators.push_back(c); add check for separators !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// while (!m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD &&
|
||||
// (c = m_stream->get()) != ' ' && c != '\t' && !m_stream->eof())
|
||||
// {
|
||||
// m_unparsed.append(1, char(c));
|
||||
// if (toUpperCase)
|
||||
// param.append(1, char(toupper(c)));
|
||||
// else
|
||||
// param.append(1, char(c));
|
||||
// }
|
||||
// if (c == ' ')
|
||||
// m_unparsed.append(1, char(c));
|
||||
// }
|
||||
//
|
||||
// // Store parameter
|
||||
// if (param.size())
|
||||
// m_line.push_back(param);
|
||||
// }
|
||||
//
|
||||
// // Skip newlines
|
||||
// while (m_stream->peek() == 0xA || m_stream->peek() == 0xD)
|
||||
// m_stream->get();
|
||||
//
|
||||
// return m_line.size();
|
||||
//}
|
||||
//
|
||||
//void LineParser::WriteCompressed(string *s) const
|
||||
//{
|
||||
// size_t i;
|
||||
// for (i = 0; i < m_line.size(); i++)
|
||||
// {
|
||||
// if (i != 0)
|
||||
// {
|
||||
// *s += " ";
|
||||
// }
|
||||
// *s += m_line[i];
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//void LineParser::WriteCompressed(ostream *stream) const
|
||||
//{
|
||||
// size_t i;
|
||||
// for (i = 0; i < m_line.size(); i++)
|
||||
// {
|
||||
// if (i != 0)
|
||||
// {
|
||||
// *stream << " ";
|
||||
// }
|
||||
// *stream << m_line[i];
|
||||
// }
|
||||
// *stream << endl;
|
||||
//}
|
||||
//
|
||||
//size_t LineParser::ParsedLines() const
|
||||
//{
|
||||
// return m_currentLine;
|
||||
//}
|
||||
//
|
||||
//const char *LineParser::Param(const size_t param) const
|
||||
//{
|
||||
// return m_line[param].c_str();
|
||||
//}
|
||||
//
|
||||
//const char *LineParser::Line() const
|
||||
//{
|
||||
// return m_unparsed.c_str();
|
||||
//}
|
||||
|
||||
|
||||
// NOTE: commented because unused
|
||||
|
||||
//template<class T>
|
||||
//class MemBlock {
|
||||
// public:
|
||||
// MemBlock()
|
||||
// : fSize(0),
|
||||
// fBuffer(NULL)
|
||||
// {}
|
||||
//
|
||||
// MemBlock(const MemBlock<T>& src)
|
||||
// : fSize(0),
|
||||
// fBuffer(NULL)
|
||||
// {
|
||||
// if (src.fBuffer) {
|
||||
// SetSize(src.fSize);
|
||||
// memcpy(fBuffer, src.fBuffer, src.fSize);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// MemBlock<T>& operator=(const MemBlock<T>& src)
|
||||
// {
|
||||
// if (this != &src) {
|
||||
// SetSize(src.fSize);
|
||||
// if (fBuffer)
|
||||
// memcpy(fBuffer, src.fBuffer, src.fSize);
|
||||
// }
|
||||
// return (*this);
|
||||
// }
|
||||
//
|
||||
// ~MemBlock()
|
||||
// {
|
||||
// free(fBuffer);
|
||||
// }
|
||||
//
|
||||
// void SetSize(const size_t size)
|
||||
// {
|
||||
// size_t oldsize = Size();
|
||||
// if (oldsize != size) {
|
||||
// if (!fBuffer && size) {
|
||||
// fBuffer = malloc(size);
|
||||
// if (!fBuffer)
|
||||
// throw "Out of memory";
|
||||
// } else if (fBuffer && size) {
|
||||
// void *newBuf = realloc(fBuffer, size);
|
||||
// if (newBuf)
|
||||
// fBuffer = newBuf;
|
||||
// else
|
||||
// throw "Out of memory";
|
||||
// } else if (fBuffer && !size) {
|
||||
// free(fBuffer);
|
||||
// fBuffer = 0;
|
||||
// }
|
||||
// fSize = size;
|
||||
// }
|
||||
// }
|
||||
// void* Buffer() const { return fBuffer; }
|
||||
// T* Access() const { return (T*)fBuffer; }
|
||||
// T& operator[](size_t i) const
|
||||
// {
|
||||
// return Access()[i];
|
||||
// }
|
||||
// size_t Size() const { return fSize; }
|
||||
//
|
||||
// protected:
|
||||
// size_t fSize;
|
||||
// void* fBuffer;
|
||||
//};
|
||||
//
|
||||
//
|
||||
//static int
|
||||
//strprintf(string &str, const char * format, ...)
|
||||
//{
|
||||
// va_list argptr;
|
||||
//
|
||||
// char strfix[128];
|
||||
//
|
||||
// va_start(argptr, format);
|
||||
// int ret = vsnprintf(strfix, 128, format, argptr);
|
||||
// va_end(argptr);
|
||||
//
|
||||
// if (ret == -1) {
|
||||
// MemBlock<char> strbuf;
|
||||
// strbuf.SetSize(128);
|
||||
// while (ret == -1) {
|
||||
// strbuf.SetSize(strbuf.Size() * 2);
|
||||
// va_start(argptr, format);
|
||||
// ret = vsnprintf(strbuf.Access(), strbuf.Size(), format, argptr);
|
||||
// va_end(argptr);
|
||||
// }
|
||||
// str.assign(strbuf.Access());
|
||||
// } else if (ret >= 128) {
|
||||
// MemBlock<char> strbuf;
|
||||
// strbuf.SetSize(ret + 1);
|
||||
// va_start(argptr, format);
|
||||
// ret = vsnprintf(strbuf.Access(), ret + 1, format, argptr);
|
||||
// va_end(argptr);
|
||||
// str.assign(strbuf.Access());
|
||||
// } else {
|
||||
// str.assign(strfix);
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
|
||||
static double
|
||||
valtod(const char* exp, const char** end)
|
||||
{
|
||||
// http://en.wikipedia.org/wiki/SI_prefix
|
||||
|
||||
char *endptr;
|
||||
double temp = strtod(exp, &endptr);
|
||||
if (endptr == exp || !*endptr) {
|
||||
if (end)
|
||||
*end = endptr;
|
||||
return temp;
|
||||
}
|
||||
|
||||
if (end)
|
||||
*end = endptr + 1;
|
||||
|
||||
switch (*endptr) {
|
||||
/* case 'Y': // yotta
|
||||
return temp * 1.E24;
|
||||
case 'Z': // zetta
|
||||
return temp * 1.E21;
|
||||
case 'E': // exa, incompatible with exponent!
|
||||
return temp * 1.E18;*/
|
||||
case 'P': // peta
|
||||
return temp * 1.E15;
|
||||
case 'T': // tera
|
||||
return temp * 1.E12;
|
||||
case 'G': // giga
|
||||
return temp * 1.E9;
|
||||
case 'M': // mega
|
||||
return temp * 1.E6;
|
||||
case 'k': // kilo
|
||||
return temp * 1.E3;
|
||||
case 'h': // hecto
|
||||
return temp * 1.E2;
|
||||
case 'd':
|
||||
if (endptr[1] == 'a') {
|
||||
if (end)
|
||||
*end = endptr + 2;
|
||||
return temp * 1.E1; // deca
|
||||
} else
|
||||
return temp * 1.E-1; // deci
|
||||
case 'c': // centi
|
||||
return temp * 1.E-2;
|
||||
case 'm': // milli
|
||||
return temp * 1.E-3;
|
||||
case 'u': // micro
|
||||
return temp * 1.E-6;
|
||||
case 'n': // nano
|
||||
return temp * 1.E-9;
|
||||
case 'p': // pico
|
||||
return temp * 1.E-12;
|
||||
case 'f': // femto
|
||||
return temp * 1.E-15;
|
||||
case 'a': // atto
|
||||
return temp * 1.E-18;
|
||||
case 'z': // zepto
|
||||
return temp * 1.E-21;
|
||||
case 'y': // yocto
|
||||
return temp * 1.E-24;
|
||||
}
|
||||
if (end)
|
||||
*end = endptr;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
// NOTE: commented because unused
|
||||
|
||||
//static double
|
||||
//exptod(const char* str, const char** end)
|
||||
//{
|
||||
// Expression exp;
|
||||
//
|
||||
// const char* evalEnd = str + strlen(str);
|
||||
// double temp = exp.Evaluate(str, evalEnd);
|
||||
//
|
||||
// const char* err = exp.Error();
|
||||
// if (end)
|
||||
// *end = err ? err : evalEnd;
|
||||
//
|
||||
// return temp;
|
||||
//}
|
||||
//
|
||||
//
|
||||
//static string
|
||||
//dtostr(const double value, const unsigned int precision, const char mode)
|
||||
//{
|
||||
// if (value == 0.0)
|
||||
// return "0";
|
||||
//
|
||||
// string str;
|
||||
// char prefix[2];
|
||||
// prefix[0] = 0;
|
||||
// prefix[1] = 0;
|
||||
//
|
||||
// if (mode == 1) {
|
||||
// // exp3
|
||||
// double exp = floor(log10(fabs(value)) / 3.) * 3;
|
||||
// string format;
|
||||
// strprintf(format, "%%.%dGE%%ld", precision);
|
||||
// strprintf(str, format.c_str(), value / pow(10., exp), exp);
|
||||
// } else if (mode == 2) {
|
||||
// // prefix
|
||||
// double exp = floor(log10(fabs(value)) / 3.) * 3;
|
||||
// string format;
|
||||
// strprintf(format, "%%.%dG", precision);
|
||||
// strprintf(str, format.c_str(), value / pow(10., exp));
|
||||
// switch (int(exp)) {
|
||||
// case 24: // yotta
|
||||
// prefix[0] = 'Y';
|
||||
// break;
|
||||
// case 21: // zetta
|
||||
// prefix[0] = 'Z';
|
||||
// break;
|
||||
// case 18: // exa
|
||||
// prefix[0] = 'E';
|
||||
// break;
|
||||
// case 15: // peta
|
||||
// prefix[0] = 'P';
|
||||
// break;
|
||||
// case 12: // tera
|
||||
// prefix[0] = 'T';
|
||||
// break;
|
||||
// case 9: // giga
|
||||
// prefix[0] = 'G';
|
||||
// break;
|
||||
// case 6: // mega
|
||||
// prefix[0] = 'M';
|
||||
// break;
|
||||
// case 3: // kilo
|
||||
// prefix[0] = 'k';
|
||||
// break;
|
||||
// case -3: // milli
|
||||
// prefix[0] = 'm';
|
||||
// break;
|
||||
// case -6: // micro
|
||||
// prefix[0] = 'u';
|
||||
// break;
|
||||
// case -9: // nano
|
||||
// prefix[0] = 'n';
|
||||
// break;
|
||||
// case -12: // pico
|
||||
// prefix[0] = 'p';
|
||||
// break;
|
||||
// case -15: // femto
|
||||
// prefix[0] = 'f';
|
||||
// break;
|
||||
// case -18: // atto
|
||||
// prefix[0] = 'a';
|
||||
// break;
|
||||
// case -21: // zepto
|
||||
// prefix[0] = 'z';
|
||||
// break;
|
||||
// case -24: // yocto
|
||||
// prefix[0] = 'y';
|
||||
// break;
|
||||
// default:
|
||||
// if (exp) {
|
||||
// strprintf(format, "%%.%dGE%%ld", precision);
|
||||
// strprintf(str, format.c_str(), value / pow(10., exp), exp);
|
||||
// } else {
|
||||
// strprintf(format, "%%.%dG", precision);
|
||||
// strprintf(str, format.c_str(), value);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// } else {
|
||||
// // exp
|
||||
// string format;
|
||||
// strprintf(format, "%%.%dG", precision);
|
||||
// strprintf(str, format.c_str(), value);
|
||||
// }
|
||||
//
|
||||
// return string(str) + prefix;
|
||||
//}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Expression::Expression()
|
||||
: fAglf(1.),
|
||||
fErrorPtr(NULL),
|
||||
fTrig(false)
|
||||
{
|
||||
// NOTE: these seem to be hard coded anyways...
|
||||
fFunctionNames.push_back("sin("); // 0
|
||||
fFunctionTypes.push_back(1);
|
||||
fFunctionNames.push_back("asin("); // 1
|
||||
fFunctionTypes.push_back(2);
|
||||
fFunctionNames.push_back("cos("); // 2
|
||||
fFunctionTypes.push_back(1);
|
||||
fFunctionNames.push_back("acos("); // 3
|
||||
fFunctionTypes.push_back(2);
|
||||
fFunctionNames.push_back("tan("); // 4
|
||||
fFunctionTypes.push_back(1);
|
||||
fFunctionNames.push_back("atan("); // 5
|
||||
fFunctionTypes.push_back(2);
|
||||
fFunctionNames.push_back("ln("); // 6
|
||||
fFunctionTypes.push_back(0);
|
||||
fFunctionNames.push_back("lg("); // 7
|
||||
fFunctionTypes.push_back(0);
|
||||
fFunctionNames.push_back("sqrt("); // 8
|
||||
fFunctionTypes.push_back(0);
|
||||
fFunctionNames.push_back("("); // 9
|
||||
fFunctionTypes.push_back(0);
|
||||
|
||||
SetConstant("e", M_E);
|
||||
SetConstant("pi", M_PI);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Expression::SetAglf(const double value)
|
||||
{
|
||||
fAglf = value;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Expression::SetConstant(const char* name, const double value)
|
||||
{
|
||||
for (size_t i = 0; name[i]; i++) {
|
||||
if (!isalnum(name[i])) {
|
||||
throw "Illegal character in constant name";
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < fConstantNames.size(); i++) {
|
||||
if (!strcmp(name, fConstantNames[i].c_str())) {
|
||||
fConstantValues[i] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fConstantNames.push_back(name);
|
||||
fConstantValues.push_back(value);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
Expression::Evaluate(const char* exp)
|
||||
{
|
||||
const char* evalEnd = exp + strlen(exp);
|
||||
return Evaluate(exp, evalEnd);
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
Expression::Evaluate(const char* begin, const char* end, const bool allowWhiteSpaces)
|
||||
{
|
||||
const char* curptr = begin;
|
||||
int state = 1;
|
||||
double operand;
|
||||
double sum = 0.;
|
||||
double prod = 0.0;
|
||||
double power = 0.0;
|
||||
|
||||
if (begin[0] == 0) {
|
||||
fErrorPtr = begin;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
if (fErrorPtr) {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const char *operandEnd;
|
||||
bool minusBeforeOperand = false;
|
||||
bool numberOperand = true;
|
||||
|
||||
if (allowWhiteSpaces) {
|
||||
while (curptr < end && isspace(curptr[0]))
|
||||
curptr++;
|
||||
}
|
||||
|
||||
if (curptr[0] == '-')
|
||||
minusBeforeOperand = true;
|
||||
|
||||
operand = valtod(curptr, &operandEnd);
|
||||
|
||||
if (operandEnd == curptr) {
|
||||
// operand not a number
|
||||
const char *operandStart = curptr;
|
||||
double operandSign = 1.;
|
||||
|
||||
numberOperand = false;
|
||||
|
||||
if (curptr[0] == '+') {
|
||||
++operandStart;
|
||||
} else if (curptr[0] == '-') {
|
||||
++operandStart;
|
||||
operandSign = -1.;
|
||||
}
|
||||
|
||||
// Iterate through functions
|
||||
size_t i;
|
||||
for (i = 0; i < fFunctionNames.size(); i++) {
|
||||
if (!strncmp(operandStart, fFunctionNames[i].c_str(), fFunctionNames[i].size())) {
|
||||
const char *functionArg = operandStart + fFunctionNames[i].size();
|
||||
const char *chptr = functionArg - 1;
|
||||
const char *prptr = functionArg - 1;
|
||||
|
||||
if (fFunctionTypes[i])
|
||||
fTrig = true;
|
||||
|
||||
// Find closing paranthesis
|
||||
while (prptr) {
|
||||
chptr = strchr(chptr + 1, ')');
|
||||
prptr = strchr(prptr + 1, '(');
|
||||
if (prptr > chptr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (chptr) {
|
||||
// Closing paranthesis
|
||||
if (fFunctionTypes[i] == 1) {
|
||||
// Trig
|
||||
operand = operandSign * _ApplyFunction(fAglf * Evaluate(functionArg, chptr), i);
|
||||
if (fabs(operand) < NOLLF)
|
||||
operand = 0;
|
||||
} else if (fFunctionTypes[i] == 2) {
|
||||
// Inv trig
|
||||
operand = operandSign / fAglf * _ApplyFunction(Evaluate(functionArg, chptr), i);
|
||||
} else {
|
||||
// Normal
|
||||
operand = operandSign * _ApplyFunction(Evaluate(functionArg, chptr), i);
|
||||
}
|
||||
operandEnd = chptr + 1;
|
||||
} else {
|
||||
// No closing paranthesis
|
||||
if (fFunctionTypes[i] == 1) {
|
||||
operand = operandSign * _ApplyFunction(fAglf * Evaluate(functionArg, end), i);
|
||||
if (fabs(operand) < NOLLF)
|
||||
operand = 0;
|
||||
} else if (fFunctionTypes[i] == 2) {
|
||||
operand = operandSign / fAglf * _ApplyFunction(Evaluate(functionArg, end), i);
|
||||
} else {
|
||||
operand = operandSign * _ApplyFunction(Evaluate(functionArg, end), i);
|
||||
}
|
||||
operandEnd = end;
|
||||
}
|
||||
|
||||
if (fErrorPtr) {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through constants
|
||||
if (i == fFunctionNames.size()) {
|
||||
// Only search if no function found
|
||||
for (i = 0; i < fConstantNames.size(); i++) {
|
||||
if (!strncmp(operandStart, fConstantNames[i].c_str(), fConstantNames[i].size()) && !isalnum(operandStart[fConstantNames[i].size()])) {
|
||||
operandEnd = operandStart + fConstantNames[i].size();
|
||||
operand = operandSign * fConstantValues[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operandEnd > end) {
|
||||
fErrorPtr = end;
|
||||
return 0.;
|
||||
}
|
||||
|
||||
// Calculate expression in correct order
|
||||
|
||||
// state == 1 => term => sum + operand
|
||||
// state == 2 => product => sum + prod * operand
|
||||
// state == 3 => quotient => sum + prod / operand
|
||||
// state == 4 => plus & power => sum + power ^ operand
|
||||
// state == 5 => minus & power => sum - power ^ operand
|
||||
// state == 6 => product & power => sum + prod * power ^ operand
|
||||
// state == 7 => quotient & power => sum + prod / power ^ operand
|
||||
|
||||
if (curptr != operandEnd) {
|
||||
// number?
|
||||
switch (state) {
|
||||
case 1: // term
|
||||
if (operandEnd == end) {
|
||||
return sum + operand;
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum + operand;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = operand;
|
||||
state = 2;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = operand;
|
||||
state = 3;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = operand;
|
||||
if (minusBeforeOperand) {
|
||||
power = power * -1.;
|
||||
state = 5;
|
||||
} else {
|
||||
state = 4;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (numberOperand && isalpha(operandEnd[0])) {
|
||||
// Assume multiplication of function/constant without *
|
||||
curptr = operandEnd;
|
||||
prod = operand;
|
||||
state = 2;
|
||||
} else {
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // product
|
||||
if (operandEnd == end) {
|
||||
return sum + prod * operand;
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum + prod * operand;
|
||||
state = 1;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod * operand;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod * operand;
|
||||
state = 3;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = operand;
|
||||
state = 6;
|
||||
break;
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // quotient
|
||||
if (operandEnd == end) {
|
||||
return sum + prod / operand;
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum + prod / operand;
|
||||
state = 1;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod / operand;
|
||||
state = 2;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod / operand;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = operand;
|
||||
state = 7;
|
||||
break;
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // plus&power
|
||||
if (operandEnd == end) {
|
||||
return sum + pow(power, operand);
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum + pow(power, operand);
|
||||
state = 1;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = pow(power, operand);
|
||||
state = 2;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = pow(power, operand);
|
||||
state = 3;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = pow(power, operand);
|
||||
break;
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // minus&power
|
||||
if (operandEnd == end) {
|
||||
return sum - pow(power, operand);
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum - pow(power, operand);
|
||||
state = 1;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = pow(power, operand);
|
||||
state = 2;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = pow(power, operand);
|
||||
state = 3;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = pow(power, operand);
|
||||
break;
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6: // product&power
|
||||
if (operandEnd == end) {
|
||||
return sum + prod * pow(power, operand);
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum + prod * pow(power, operand);
|
||||
state = 1;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod * pow(power, operand);
|
||||
state = 2;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod * pow(power, operand);
|
||||
state = 3;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = pow(power, operand);
|
||||
break;
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
break;
|
||||
|
||||
case 7: // quotient&power
|
||||
if (operandEnd == end) {
|
||||
return sum + prod / pow(power, operand);
|
||||
}
|
||||
switch (operandEnd[0]) {
|
||||
// Check next
|
||||
case '+':
|
||||
case '-':
|
||||
curptr = operandEnd;
|
||||
sum = sum + prod / pow(power, operand);
|
||||
state = 1;
|
||||
break;
|
||||
case '*':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod / pow(power, operand);
|
||||
state = 2;
|
||||
break;
|
||||
case '/':
|
||||
curptr = operandEnd + 1;
|
||||
prod = prod / pow(power, operand);
|
||||
state = 3;
|
||||
break;
|
||||
case '^':
|
||||
curptr = operandEnd + 1;
|
||||
power = pow(power, operand);
|
||||
break;
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fErrorPtr = operandEnd;
|
||||
return 0.;
|
||||
}
|
||||
} else {
|
||||
fErrorPtr = curptr;
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Expression::Error()
|
||||
{
|
||||
const char *retval = fErrorPtr;
|
||||
fErrorPtr = NULL;
|
||||
fTrig = false;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
double
|
||||
Expression::_ApplyFunction(const double x, const size_t number) const
|
||||
{
|
||||
switch (number) {
|
||||
case 0:
|
||||
return sin(x);
|
||||
case 1:
|
||||
return asin(x);
|
||||
case 2:
|
||||
return cos(x);
|
||||
case 3:
|
||||
return acos(x);
|
||||
case 4:
|
||||
return tan(x);
|
||||
case 5:
|
||||
return atan(x);
|
||||
case 6:
|
||||
return log(x);
|
||||
case 7:
|
||||
return log10(x);
|
||||
case 8:
|
||||
return sqrt(x);
|
||||
default:
|
||||
return x;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2004 Daniel Wallner. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Daniel Wallner <daniel.wallner@bredband.net>
|
||||
*/
|
||||
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <vector.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
//class LineParser {
|
||||
// public:
|
||||
// LineParser(std::istream *stream);
|
||||
//
|
||||
// void AddSeparator(const char c);
|
||||
//
|
||||
// size_t SetToNext(bool toUpperCase = false);
|
||||
// void WriteCompressed(std::string *s) const;
|
||||
// void WriteCompressed(std::ostream *stream) const;
|
||||
//
|
||||
// size_t ParsedLines() const;
|
||||
// const char *Param(const size_t param) const;
|
||||
// const char *Line() const;
|
||||
//
|
||||
// private:
|
||||
// LineParser();
|
||||
//
|
||||
// std::istream *m_stream;
|
||||
// size_t m_currentLine;
|
||||
// std::string m_unparsed;
|
||||
// std::vector<std::string> m_line;
|
||||
// std::vector<char> m_separators;
|
||||
//};
|
||||
|
||||
class Expression {
|
||||
public:
|
||||
Expression();
|
||||
|
||||
void SetAglf(const double value);
|
||||
bool SetConstant(const char* name,
|
||||
const double value);
|
||||
|
||||
double Evaluate(const char* exp);
|
||||
double Evaluate(const char* begin, const char* end,
|
||||
const bool allowWhiteSpaces = false);
|
||||
const char* Error();
|
||||
|
||||
private:
|
||||
double _ApplyFunction(const double x,
|
||||
const size_t number) const;
|
||||
|
||||
double fAglf;
|
||||
const char* fErrorPtr;
|
||||
bool fTrig;
|
||||
|
||||
std::vector<std::string> fFunctionNames;
|
||||
std::vector<char> fFunctionTypes;
|
||||
|
||||
std::vector<std::string> fConstantNames;
|
||||
std::vector<double> fConstantValues;
|
||||
};
|
||||
|
||||
#endif // PARSER_H
|
Loading…
Reference in New Issue
Block a user